• 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:)

     

  • 相关阅读:
    解决 Python 连不上pip库的问题(使用国内镜像地址)
    【原】Coursera—Andrew Ng机器学习—Week 2 习题—Linear Regression with Multiple Variables 多变量线性回归
    【原】Coursera—Andrew Ng机器学习—Week 1 习题—Linear Regression with One Variable 单变量线性回归
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 4_Linear Regression with Multiple Variables 多变量线性回归
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 3_Linear Algebra Review
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 2_Linear regression with one variable 单变量线性回归
    【原】Coursera—Andrew Ng机器学习—课程笔记 Lecture 1_Introduction and Basic Concepts 介绍和基本概念
    vue项目中的父子组件之间的传值。
    精确讲述闭包及内存泄漏
    vue项目富文本编辑器vue-quill-editor之自定义图片上传
  • 原文地址:https://www.cnblogs.com/easyToCode/p/5330161.html
Copyright © 2020-2023  润新知