zoukankan      html  css  js  c++  java
  • 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:)

     

  • 相关阅读:
    ruby 二进制转十进制 Integer("0b101") = 5
    开始菜单和我的文档的我的图片及我的音乐变成 my pictrues 正常图标了
    ruby watir 莫名其妙的错误
    Excel SaveAS是去掉提示框
    apache && jboss安装
    ruby require include的区别
    ruby控制鼠标
    This error is raised because the column 'type' is reserved for storing the class in case of inheritance
    用正则表达式限制文本框只能输入数字,小数点,英文字母,汉字等各类代码
    ASP.NET 如何动态修改 Header 属性如添加 Meta 标签 keywords description!
  • 原文地址:https://www.cnblogs.com/easyToCode/p/5330161.html
Copyright © 2011-2022 走看看