iOS_CNBlog項目開發 (基於博客園api開發) 下篇

来源:http://www.cnblogs.com/easyToCode/archive/2016/03/28/5330161.html
-Advertisement-
Play Games

這篇博文基於上一篇iOS_CNBlog項目開發 (基於博客園api開發)所寫. 過了剛好兩個星期, 這次基於上一次的1.0版本, 完善了新的功能, 也修複了之前的一些bug, 算是完成1.1版本吧, 一次進步一下點總是好的, 貼上github:)地址, 喜歡的可以玩弄玩弄 https://githu ...


這篇博文基於上一篇iOS_CNBlog項目開發 (基於博客園api開發)所寫.

 

過了剛好兩個星期, 這次基於上一次的1.0版本, 完善了新的功能, 也修複了之前的一些bug, 算是完成1.1版本吧, 一次進步一下點總是好的, 貼上github:)地址, 喜歡的可以玩弄玩弄 https://github.com/samAroundGitHub/CNBlog .

 

然後也貼上這次主要加入的新功能gif吧.

這次主要是修複了一個bug, 新加入了一個博客收藏功能, 一個新聞關註功能, 以及頭像可以更換了(然而並沒有什麼特別的)...

雖然沒有什麼特別但是也介紹一下吧.

 

 

1. 收藏功能實現

存儲方式

這次本地存儲用的是CoreData, 然後使用過後發現, 相比於sqlite, coredata存儲方式的操作感覺更面向對象一點, 然後不用會sql語句也能快速上手吧

 

CoreData使用方法:

a. coredata的創建

方法1. 一開始新建project的時候直接勾選, 這樣, Xcode就會自動在AppDelegate下麵生成的代碼, 其實就是一些操作coredata需要用到的對象初始化, 而且Xcode還會自動生成coredata文件 .xcdatamodeld , 然後你就可以像使用sql圖形界面一樣操作coredata, 其中entity對應sql中的table, attritube對應table中的鍵值, 然後可以添加關係, 跟使用sql差不多.

#pragma mark - Core Data stack

@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;

- (NSURL *)applicationDocumentsDirectory {
    // The directory the application uses to store the Core Data store file. This code uses a directory named "com.easyToCode.CoreDataTest" in the application's documents directory.
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

- (NSManagedObjectModel *)managedObjectModel {
    // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CoreDataTest" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    // The persistent store coordinator for the application. This implementation creates and returns a coordinator, having added the store for the application to it.
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
    
    // Create the coordinator and store
    
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataTest.sqlite"];
    NSError *error = nil;
    NSString *failureReason = @"There was an error creating or loading the application's saved data.";
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        // Report any error we got.
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
        dict[NSLocalizedFailureReasonErrorKey] = failureReason;
        dict[NSUnderlyingErrorKey] = error;
        error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
        // Replace this with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
    
    return _persistentStoreCoordinator;
}


- (NSManagedObjectContext *)managedObjectContext {
    // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }
    
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (!coordinator) {
        return nil;
    }
    _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    return _managedObjectContext;
}

#pragma mark - Core Data Saving support

- (void)saveContext {
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) {
        NSError *error = nil;
        if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
            // Replace this implementation with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
    }
}
View Code

如果一開始創建項目的時候錯過了勾選怎麼辦? 沒關係, 看方法2

 

方法2. 一開始創建項目的時候不清楚要用什麼存儲方法所以沒有勾選use core data, 沒關係, 我們還是可以直接創建coredata文件, 然後添加好自己需要存儲的數據表, 像下麵一樣Create NSManagedObject Subclass, 然後Xcode就會自動生成一個繼承至NSManagedObject的類和coredata表對應, 這樣關聯起來可以使用了.

     

    

 

b. coredata的對象準備

使用coredata需要準備最基本的3個對象

// CoreData實體
@property (nonatomic, strong) NSManagedObjectModel *sm_model;
// 操作實體
@property (nonatomic, strong) NSManagedObjectContext *sm_context;
// 存儲策略
@property (nonatomic, strong) NSPersistentStoreCoordinator *sm_coordinator;

這三個對象有什麼用?

NSManagedObjectModel就好比CoreData對象, 裡面包含著 .xcdatamodeld下所有entities

NSManagedObjectContext就是一個操作CoreData的對象, 你保存數據到哪, 它都管著

NSPersistentStoreCoordinator就是CoreData儲存策略, 它關聯著模型和資料庫持久化

三個對象怎麼創建?

// coradata實體
- (NSManagedObjectModel *)sm_model {
    if (!_sm_model) {
        // nil表示從mainBundle載入
        _sm_model = [NSManagedObjectModel mergedModelFromBundles:nil];
    }
    return _sm_model;
}

// 存儲策略
- (NSPersistentStoreCoordinator *)sm_coordinator {
    if (!_sm_coordinator) {
        
        // 通過模型和資料庫持久化
        _sm_coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.sm_model];
        
        // 持久化到coredata, 預設路徑為 /documents/coredata.db
        NSString *document = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
        document = [document stringByAppendingPathComponent:@"coredata.db"];
        NSURL *url = [NSURL fileURLWithPath:document];
        
        // 錯誤記錄
        NSError *error;
        NSString *failureReason = @"There was an error creating or loading the application's saved data.";
        if (![_sm_coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error]) {
            // Report any error we got.
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            dict[NSLocalizedDescriptionKey] = @"Failed to initialize the application's saved data";
            dict[NSLocalizedFailureReasonErrorKey] = failureReason;
            dict[NSUnderlyingErrorKey] = error;
            error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
            // Replace this with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
            abort();
        }
        
    }
    return _sm_coordinator;
}

// 操作實體
- (NSManagedObjectContext *)sm_context {
    if (!_sm_context) {
        _sm_context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        _sm_context.persistentStoreCoordinator = self.sm_coordinator;
    }
    return _sm_context;
}
View Code

 

c. 操作coredata

其實操作coredata跟操作sql很像, 也是增刪改查, 只是操作coredata用對象加一些方法, 操作sql就是寫sql語句

// 增 刪 改 查

////////////////////////////////////////////////////////////////////////////

// 關聯實體對象和實體上下文 
// entity對應Coredata的entity
// self.m_context對應coredata操作對象NSManagedObjectContext 
// 用kvc對關聯的對象賦值
   NSManagedObject *obj = [NSEntityDescription insertNewObjectForEntityForName:entity inManagedObjectContext:self.sm_context];
    // 綁定數據
    for (int i = 0; i < MIN(names.count, values.count); i++) {
        [obj setValue:values[i] forKey:names[i]];
    }
    // 保存上下文關聯對象
    [self.sm_context save:nil];

////////////////////////////////////////////////////////////////////////////

// 檢索對象
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entity];
    // 設置檢索條件
    request.predicate = [NSPredicate predicateWithFormat:predicate];
    // 刪除操作
    for (NSManagedObject *obj in [self.sm_context executeFetchRequest:request error:nil]) {
        [self.sm_context deleteObject:obj];
    }
    // 保存上下文關聯對象
    [self.sm_context save:nil];

////////////////////////////////////////////////////////////////////////////

// 檢索對象
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entity];
    // 設置檢索條件
    request.predicate = [NSPredicate predicateWithFormat:predicate];
    // 更新操作
    for (NSManagedObject *obj in [self.sm_context executeFetchRequest:request error:nil]) {
        [obj setValue:value forKey:name];
    }
    // 保存上下文關聯對象
    [self.sm_context save:nil];

////////////////////////////////////////////////////////////////////////////

// 檢索對象
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entity];
    // 設置檢索條件
    request.predicate = [NSPredicate predicateWithFormat:predicate];
//    NSLog(@"%@", request.predicate);
    // 查找操作
    return [self.sm_context executeFetchRequest:request error:nil];

////////////////////////////////////////////////////////////////////////////
增 刪 改 查

 

然後為了進一步面向對象, 我也寫了一個工具類 SMCoreDataTool  github:), 一個輕量級的工具, 能夠滿足部分開發要求, 簡化開發

其.h文件如下

@interface SMCoreDataTool : NSObject

/**
 *  mainBundle下所有entity
 */
@property (nonatomic, strong, readonly) NSArray *sm_entitys;

/**
 *  單例
 */
+ (instancetype)shareSMTool;

/**
 *  增刪改查操作
 */
+ (void)sm_toolAddDataWithEntity:(NSString *)entity attributeNames:(NSArray *)names attributeValues:(NSArray *)values;
+ (void)sm_toolDeleteDataWithEntity:(NSString *)entity andPredicate:(NSString *)predicate;
+ (void)sm_toolUpdateDataWithEntity:(NSString *)entity attributeName:(NSString *)name predicate:(NSString *)predicate andUpdateValue:(NSString *)value;
+ (NSArray *)sm_toolSearchDataWithEntity:(NSString *)entity andPredicate:(NSString *)predicate;

/**
 *  運行時 增加數據操作
 */
+ (void)sm_toolAddDataWithEntity:(NSString *)entity attributeModel:(id)model;

/**
 *  清除coredata
 */
+ (void)sm_toolClearCoraDataWithEntiy:(NSString *)entity;

@end

簡單說明一下.

外部暴露類方法, 內部是用單例調用對象方法, 然後提供了增刪改查4個方法, 其中增的方法還額外提供多一個選擇, 可以直接傳入model, 其內部運用了runtime機制會自行判斷能插入的值.

比如coredata如→

model如→

那麼增刪改查操作:

// 添加方法1 
    [SMCoreDataTool sm_toolAddDataWithEntity:@"Entity" attributeNames:@[@"name", @"uri"] attributeValues:@[@"jack", @"www.codedata.com"]];

// 添加方法2
// 這裡coredata沒有age屬性, 所以不會存入該數據
// 只有model與core data同時存在某屬性, 該屬性才會存儲
    SMModel *model = [[SMModel alloc] init];
    model.name = @"中文 亂碼 華盛頓了";
    model.uri = @"www.aaa.ccc";
    model.age = @"11";
    [SMCoreDataTool sm_toolAddDataWithEntity:@"Entity" attributeModel:model];

// 刪除操作
   [SMCoreDataTool sm_toolDeleteDataWithEntity:@"Entity" andPredicate:@"name like 'jack'"];

// 修改操作
    [SMCoreDataTool sm_toolUpdateDataWithEntity:@"Entity" attributeName:@"name" predicate:@"name == 'update'" andUpdateValue:@"hehe"];

// 查找操作
    NSArray *arr = [SMCoreDataTool sm_toolSearchDataWithEntity:@"Entity" andPredicate:nil];
    
    for (NSManagedObject *obj in arr) {
        NSLog(@"%@ - %@", [obj valueForKey:@"name"], [obj valueForKey:@"uri"]);
    }
View Code

有興趣的, (github:)自行玩弄下.

 

2. 頭像修改

頭像修改功能就很簡單啦, 基本是調用了蘋果自帶ImagePicker, 然後加入了相容iOS8

核心代碼如下:

if (iOS8) {
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"獲取圖片" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
        
        // 判斷是否支持相機
        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
            UIAlertAction *defaultActionTakePhoto = [UIAlertAction actionWithTitle:@"拍照" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
                
                imagePicker.delegate = self;
                imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
                imagePicker.allowsEditing = YES;
                
                [self presentViewController:imagePicker animated:YES completion:nil];
            }];
            
            [alertController addAction:defaultActionTakePhoto];
        }
        
        UIAlertAction *defaultActionFromPhotoGraf = [UIAlertAction actionWithTitle:@"從相冊選擇" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
            imagePicker.delegate = self;
            imagePicker.allowsEditing = YES;
            imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
            
            [self presentViewController:imagePicker animated:YES completion:nil];
        }];
        UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            
        }];
        
        [alertController addAction:defaultActionFromPhotoGraf];
        [alertController addAction:cancelAction];
        
        [self presentViewController:alertController animated:YES completion:nil];
        
    } else {
        UIActionSheet *sheet;

        // 判斷是否支持相機
        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
            sheet = [[UIActionSheet alloc] initWithTitle:@"獲取圖片" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"相機", @"從相冊選擇", nil];
        } else {
            sheet = [[UIActionSheet alloc] initWithTitle:@"獲取圖片" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:nil otherButtonTitles:@"從相冊選擇", nil];
        }
        
        [sheet showInView:self.view];
    }
View Code

 

3. SMXMLParserTool

上篇文章有說過這個也是我自行開發的工具類, 使用sax解析xml, 基於NSXMLParser開發, 之前沒有獨立放到github, 現在獨立貼出來, 方便下載使用.

.h 文件

+ (instancetype)sm_toolWithURLString:(NSString *)urlString nodeName:(NSString *)nodeName completeHandler:(void (^)(NSArray *contentArray, NSError *error))completerHandler;
- (instancetype)sm_initWithURLString:(NSString *)urlString nodeName:(NSString *)nodeName completeHandler:(void (^)(NSArray *contentArray, NSError *error))completerHandler;
 
@property (nonatomic, readonly, strong) NSArray *contentArray;
@property (nonatomic, strong) NSString *nodeName;
View Code

使用類方法:

+ (instancetype)sm_toolWithURLString:(NSString *)urlString nodeName:(NSString *)nodeName completeHandler:(void (^)(NSArray *contentArray, NSError *error))completerHandler;

傳入url和xml的大節點名, 然後就會自動解析大節點下各節點, 內部發送非同步網路請求, 然後封裝了block回調方法, 返回內容可以直接在block內部使用, contentArray就是返回的結果. 這是一個小工具, 基本能夠實現功能吧. 喜歡的可以把玩一下 github:)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 模塊化是一個通用的編程最佳實踐。程式的模塊化使我們可以更方便地使用別人的代碼,想要什麼功能,就載入什麼模塊,從而提高代碼的利用效率,增加開發速度。 模塊就像積木,有了它,我們可以搭出各種各種功能樣式的程式。積木有什麼特點?小而簡單。同樣的,我們程式中的模塊也要做到這一點,確保自己創建的函數一次只完成 ...
  • ###關於IE內核的瀏覽器讀取json的問題最近在做一個考試的項目,其中有一個需求是學生意外退出考試後,再次進入考試的時候(考試過程中會自動保存成json存到資料庫)學生上次的做的題目能夠被自動填充。之前測試,一直是用chrome測的,啥問題都沒有,然後前天發現在ie8下麵不可以,然後昨天發現在所有 ...
  • 上一節中我們學會瞭如何通過點擊不同按鈕切換頁面,這節專註於完成反饋頁面的功能以及細節動畫。 導入項目 添加新組件 同步新組件 完成頁面佈局 輸入時加動畫效果 彈出日期選擇 直接引用UI頁面 將要學習的demo效果圖如下所示 1. 導入完整項目 本節示例demo請參考下載地址,可以導入到設計器中學習。 ...
  • 體驗效果:http://hovertree.com/texiao/html5/25/效果圖:代碼如下: 關註微信公眾號 何問起 ,賬號ihewenqi ,或者微信掃描下麵二維碼關註。然後發送"橡皮擦"查看效果。參考:使用CSS實現圖片磨砂玻璃效果 轉自:http://hovertree.com/h/ ...
  • 從github上下載的jquery文件是沒有經過壓縮和合併的,根據jquery README.md 中提供的說明在window構建jquery,進行到最後一步運行grunt時會直接編輯器中打開grunt.js,不能夠完成構建,需要先在命令行執行:DOSKEY grunt=grunt.cmd $*。下 ...
  • 介紹 JavaScript 高漲的人氣帶來了很多變化,以至於如今使用其進行網路開發的形式也變得截然不同了。就如同在瀏覽器中一樣,現在我們也可以在伺服器上運行 JavaScript ,從前端跨越到後端,這樣巨大的反差讓人難以想象,因為僅僅在幾年前 Javascript 還如同 Flash 或者 Jav ...
  • 在以前我們的博客文章中,我們討論了在web設計和開發項目中使用Twitter Bootstrap的好處。Twitter Bootstrap也有很多的缺點。讓我們看看這些主要的問題: 1,它不遵循最佳實踐 我們在使用Twitter Bootstrap時遇到的最大問題之一是你的DOM元素上將擁擠大量的類 ...
  • × 目錄 [1]white-space [2]word-wrap [3]word-break 前面的話 CSS3新增了兩個換行屬性word-wrap和word-break。把空白符和換行放在一起說,是因為實際上空白符是包括換行的,且常用的文本不換行是使用的空白符的屬性white-space: now ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...