iOS_CNBlog项目开发 (基于博客园api开发) 下篇

这篇博文基于上一篇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:)

 

 

 

posted @ 2016-03-28 18:48  自己的心灵猎手  阅读(956)  评论(1编辑  收藏  举报