sqliteiOS开发之表视图爱上CoreData

  在触发到CoreData时,感觉就是苹果封装的一个ORM。CoreData负责在Model的实业和sqllite建立关系,数据模型的实体类就一定于Java中的JavaBean,
而CoreData的成效和JavaEE中的Hibernate的效应相近,最中央是五头都有通过对实体的操作来达成对数据库的CURD操作。CoreData中的上下文(managedObjectContext)就相当于Hibernate中的session对象,
CoreData中的save操作就和Hibernate中的commit,还有一对相似之处,在那就不一一列举了。(上边是作者自己为了更好的敞亮CoreData而做的粗略类比,如若学过PHP的ThinkPHP框架的伙伴们也足以和TP中的ORM类比)。

  那么TableView为啥会爱上CoreData呢?下边会通个代码给出他们相爱的缘故。就举一个IOS开发中的经典的demo:通信录来表达问题。

sqlite,    1.在TableView没遇到CoreData的时候大家怎么通过动态表视图来展现大家的通信录的情节呢?也就是说我们通信录的数据结构该怎么协会呢?

    为了在TableView中显示大家的音讯我们那样设计大家的数据结构:

      1.全勤TableView是一个可变的数组tableArray;

      2.tableArray中的每个元素又是一个存放分组的字典sectionDictionary;

      3.在sectionDictionary中大家存放着两个键值对 header和items,
header中存放的时section中的名字,items中存放的时每个section中的用户音信

      4.items中又是一个数组rowsArray,
rowsArray中存放的又是一个字典userInfoDictionary,
在userInfoDictionary中存放着大家要出示的新闻

    千字不如一图,看到上边对我们要设计的数据结构的叙说会有点迷糊,上面来张图吧:

sqlite 1

    2.数据结构大家统筹好了,那么什么样用代码生成我们的测试数据(数据的团队格局如上图所示),下边的代码就是生成大家要在tableView中显得的多寡,生成的数组存储在tableArray中,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*
 *手动创建我们在动态表视图上显示的数据格式
 *整个数据存储在一个数组中
 *数组中每一个元素是一个自动,字典的key是sectionHeader的值,value是该section中以数组形式存的数据
 *section中的每一行对应着一个数组元素,数组元素中又存储着一个字典,字典中存储着用户的具体数据。
 */
 
//为我们的数组分配存储空间, 代表着有20个section
self.telBook = [NSMutableArray arrayWithCapacity:26];
 
//为我们的section设置不同的header
char header = 'A';
 
//计数
static int number = 0;
for (int i = 0; i < 26; i ++) {
    //新建字典来存储我们每个section中的数据, 假设每个section中有1个数组
    NSMutableDictionary *sectionDic = [NSMutableDictionary dictionaryWithCapacity:1];
     
    //创建字典中的数组,数组中以键值对的形式来储存用户的信息
    NSMutableArray *rowArray = [NSMutableArray arrayWithCapacity:3];
    for (int j = 0; j < 3; j ++)
    {
        //创建存储用户信息的字典
        NSMutableDictionary *user = [NSMutableDictionary dictionaryWithCapacity:2];
         
        //生成测试数据
        NSString *name = [NSString stringWithFormat:@"User%03d", number];
        NSString *tel = [NSString stringWithFormat:@"12345%03d", number++];
         
        //加入字典中
        [user setObject:name forKey:@"name"];
        [user setObject:tel forKey:@"tel"];
         
        //把字典加入数组
        [rowArray addObject:user];
    }
     
    //把rowArray添加到section字典中
    NSString *key = [NSString stringWithFormat:@"%c",(header+i)];
    [sectionDic setObject:key forKey:@"header"];
    [sectionDic setObject:rowArray forKey:@"items"];
     
    //把section添加到总的数组中
    [self.telBook addObject:sectionDic];
}

 

    3.把大家用代码创设的东施效颦数据在大家的TableView中展开浮现,在相应的函数中按照大家转移的数据重回相应的值展现在TableView中,突显代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#pragma mark - Table view data source
//返回Section的个数,即我们telBook数组元素的个数
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return self.telBook.count;
}
 
 //返回每个section中的行数,即section中的数组元素的个数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSArray *rowArray = self.telBook[section][@"items"];
    return rowArray.count;
}
 
//给每个分组设置header
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    //获取每个section中的header
    NSString *title = self.telBook[section][@"header"];
    return title;
}
 
 
//获取cell并添加完数据发挥
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
     
    //获取secion中的数据数组
    NSArray *items = self.telBook[indexPath.section][@"items"];
     
    //获取数组中的每一项的一个字典
    NSString *name = items[indexPath.row][@"name"];
    NSString *tel = items[indexPath.row][@"tel"];
     
    //给sel设置值
    cell.textLabel.text = name;
    cell.detailTextLabel.text = tel;
     
    return cell;
}

    4.方面给出的时根本代码,至于怎么布局TableView的Cell模板或者什么把TableViewController和Storyboard中的ViewController绑定,在前边的博客中都有介绍,在那作者就不做赘述。运行结果和上边的图形是一模一样的。

  

  上面的事物只是那篇博文的前奏曲,为了显示上面的数据结构大家那样做是或不是太费事了,而且下边的数据是无法被持久化存储的。假若给大家的数目都要转换成上面的数据社团格局,想必由于所给数据结构的不确定,所以转换起来是一对一的复杂性的。TableView之所以会爱上CoreData,是因为大家的CoreData会简化大家对数码的操作,并且会持久化到sqlite中。CoreData相当于TableView和sqllite的问题,说的规范一些就是炫耀,那么我们CoreData如何利用才会简化大家的操作呢?上面将要介绍的才是那篇博客中的重点:大家如何行使CoreData才会让TableView爱上它吗?

  1.新建一个Empty Application,
在新建工程的时候,不要忘了把Use Core Data给选中,选中Use Core
Data会活动引入Core
Data框架库和在AppDelegate.h和AppDelegate.m中展开对应的陈设,并且还要还自动生成一个以本利用名命名的Data
Model文件,大家得以在Data Model文件中添加大家的数据模型,
添加好的数据模型大家会在变更数据实体类时使用(和JavaBean类似)

    (1)AppDelegata.m中多出的有的代码如下,从多出的局部代码就足以看到,CoreData会把我们的多少实体和sqllite建立起相继对应的关联:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel != nil) {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Demo083101" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}
 
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil) {
        return _persistentStoreCoordinator;
    }
     
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Demo083101.sqlite"];
     
    NSError *error = nil;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }   
     
    return _persistentStoreCoordinator;
}

 

    (2)大家可以经过
projectName.xcdatamodeld中成立大家的数码实体模型,如下图所示

sqlite 2

 

    (3)通过创办好的数量实体模型来创建大家的实体类(和JavaBean类似的东西)创设进度如下图,点击下一步未来,选中创制的实体模型即可:

sqlite 3

 

  2.CoreData备选的大都啦,该我们的TableView出场啦,在Empty
Application中默许的时并未storyboard,
倘诺你又想透过storyboard来简化你的操作,得给使用创建一个storybaord才对,成立进度如下:

    (1)第一步成立一个storyboard文件,命名为Main,如下图所示

sqlite 4

 

    (2)第二步:设置从storyboard来启动, 在Main
InterFace中选中大家制造的storyboard即可

sqlite 5

 

    (3)
第三步修改AppDelegate.m中的函数如下所示,把起先化的做事付出大家创造的storyboard进行:

1
2
3
4
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    return YES;
}

 

  3.安排工作完结接下去就是TableView和CoreData相爱的长河啦,怎么着在storyboard中对TableView的cell进行布署在那时就不赘述了,下面给出大家要经过TableView和CoreData来贯彻怎么样效益。

    (1)大家要兑现对通信录的增删改查,首要须求入下图所示:

  sqlite 6

  (2)已毕增进效果,点击右上角的充足按钮时会跳转到添加页面,在加上页面中有多少个Text菲尔德来经受用户的输入,点击添加按钮举行多少增进。AddViewController.m中的首要代码如下。

    a.须求采取的属性如下,
用NSManagedObejectContext的对象来操作CoreData中的数据,和Hibernate中的session的目的一般

1
2
3
4
5
@property (strong, nonatomic) IBOutlet UITextField *nameTextField;
@property (strong, nonatomic) IBOutlet UITextField *numberTextField;
 
//声明CoreData的上下文
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;

 

    b.获取UIApplication的单例application,
然后再经过application获取delegate,
最终通过delegate来博取上下文,代码如下:

1
2
3
4
//通过application对象的代理对象获取上下文
UIApplication *application = [UIApplication sharedApplication];
id delegate = application.delegate;
self.managedObjectContext = [delegate managedObjectContext];

 

​    c.编辑点击button要回调的法子,在点击添加按钮时首先得经过上下文获取大家的实业对象,获取完实体对象后再给实体对象的属性赋上相应的值,最终调用上下文的save方法来储存一下我们的实业对象。添加完未来还要通过navigationController来重回到上一层视图,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (IBAction)tapAdd:(id)sender {
     
    //获取Person的实体对象
    Person *person = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([Person class]) inManagedObjectContext:self.managedObjectContext];
     
    //给person赋值
    person.name = self.nameTextField.text;
    person.number = self.numberTextField.text;
    person.firstN = [NSString stringWithFormat:@"%c", pinyinFirstLetter([person.name characterAtIndex:0])-32];
     
    //通过上下文存储实体对象
    NSError *error;
    if (![self.managedObjectContext save:&error]) {
        NSLog(@"%@", [error localizedDescription]);
    }
 
    //返回上一层的view
    [self.navigationController popToRootViewControllerAnimated:YES];
     
}

 

​  (3)完毕地点的代码只是通过CoreData往sqlite中添加多少,要想在大家的TableView中显得还索要经过CoreData把大家的存储在sqlite中的数据来查询出来,再用CoreData给大家提供的措施把询问结果做一个更换,转换成适合TableView彰显的数目,上边给出相应的获取数据的代码。

    a.在TableViewController大家须求注明如下八个特性,一个用以获取上下文,一个用于存储重临结果

1
2
3
4
//声明通过CoreData读取数据要用到的变量
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
//用来存储查询并适合TableView来显示的数据
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;

                                          

    b.​在viewDidLoad中获取上下文

1
2
3
4
//通过application对象的代理对象获取上下文
UIApplication *application = [UIApplication sharedApplication];
id delegate = application.delegate;
self.managedObjectContext = [delegate managedObjectContext];

 

    c.在viewDidLoad中经过上下文来询问数据,并储存在fetchedResultsController中,
在获取数据的历程中大家须要定义UIFetchRequest 和排序规则,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*********
 通过CoreData获取sqlite中的数据
 *********/
 
//通过实体名获取请求
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([Person class])];
 
//定义分组和排序规则
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"firstN" ascending:YES];
 
//把排序和分组规则添加到请求中
[request setSortDescriptors:@[sortDescriptor]];
 
//把请求的结果转换成适合tableView显示的数据
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"firstN" cacheName:nil];
 
//执行fetchedResultsController
NSError *error;
if ([self.fetchedResultsController performFetch:&error]) {
    NSLog(@"%@", [error localizedDescription]);
}

 

    d.把询问到的数据体现在TableView中代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#pragma mark - Table view data source
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    //我们的数据中有多少个section, fetchedResultsController中的sections方法可以以数组的形式返回所有的section
    //sections数组中存的是每个section的数据信息
    NSArray *sections = [self.fetchedResultsController sections];
    return sections.count;
}
 
//通过获取section中的信息来获取header和每个secion中有多少数据
 
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    NSArray *sections = [self.fetchedResultsController  sections];
    //获取对应section的sectionInfo
    id<NSFetchedResultsSectionInfo> sectionInfo = sections[section];
     
    //返回header
    return [sectionInfo name];
}
 
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
 
    NSArray *sections = [self.fetchedResultsController sections];
    id<NSFetchedResultsSectionInfo> sectionInfo = sections[section];
     
    //返回每个section中的元素个数
    return [sectionInfo numberOfObjects];
}
 
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
     
    //获取实体对象
    Person *person = [self.fetchedResultsController objectAtIndexPath:indexPath];
     
    cell.textLabel.text = person.name;
    cell.detailTextLabel.text = person.number;
     
    // Configure the cell...
     
    return cell;
}

 

 

  (4) 经上边的代码,我们就可以透过CoreData查询sqlite,
然后把查询测数据结果展现到TableView中,不过上面的代码有个问题,就是当通过CoreData来修改或着充分数码时,TableView上的情节是不跟着CoreData的生成而变更的,接下去要做的就是要绑定TableView和CoreData的涉及。即通过CoreData修改数据的还要TableView也会随之变动。

    a.要想完结TableView和CoreData的一路,我们须要让TableView对应的Controller完毕协议NSFetchedResultsControllerDelegate,
然后再ViewDidLoad中举行登记,在添加上相应的回调代码即可。达成协议的代码如下:

1
2
3
4
5
#import <UIKit/UIKit.h>
 
@interface MyTableViewController : UITableViewController<NSFetchedResultsControllerDelegate>
 
@end


    b.进行委托回调的挂号,在viewDidLoad中添加

1
2
//注册回调,使同步生效
self.fetchedResultsController.delegate = self;

    c.添加相应的嘱托回调的点子,大家可以到Help中的API中去复制,
查询NSFetchedResultsControllerDelegate,找到相应的回调代码复制过来然后再做简单的改动即可,
已毕回调的章程代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/*
 Assume self has a property 'tableView' -- as is the case for an instance of a UITableViewController
 subclass -- and a method configureCell:atIndexPath: which updates the contents of a given cell
 with information from a managed object at the given index path in the fetched results controller.
 */
 
//当CoreData的数据正在发生改变是,FRC产生的回调
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView beginUpdates];
}
 
//分区改变状况
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
     
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                          withRowAnimation:UITableViewRowAnimationFade];
            break;
             
        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                          withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}
 
//数据改变状况
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath {
     
    UITableView *tableView = self.tableView;
     
    switch(type) {
             
        case NSFetchedResultsChangeInsert:
            //让tableView在newIndexPath位置插入一个cell
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                             withRowAnimation:UITableViewRowAnimationFade];
            break;
             
        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                             withRowAnimation:UITableViewRowAnimationFade];
            break;
             
        case NSFetchedResultsChangeUpdate:
            //让tableView刷新indexPath位置上的cell
            [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
             
        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                             withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                             withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}
 
//当CoreData的数据完成改变是,FRC产生的回调
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView endUpdates];
}

 

  (5)经过地方的代码就能够兑现CoreData和TableView的一道啊,到此会感觉到到TableView结合着CoreData是如此的随手,纵然配置起来较为麻烦,但依然比较中规中矩的,只要依据的来,是轻易落成的。因而TableView深爱着CoreData.
上边大家做到了通过CoreData来对数据的插入和询问并联名到TableView中,上面将会介绍到怎么对大家的Cell进行删减。

    a.想通过TableView来删除数据的话得开启大家的TableView的编制功能

1
2
3
4
5
6
7
//开启编辑
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the specified item to be editable.
    return YES;
}

 

​    b.开启编辑成效未来我们就足以在tableView的对应的方法中来落到实处删除功能啦,当点击删除时,大家需呀获取cell对应的目录在CoreData中的实体对象,然后经过上下文举行删减,在save一下即可。因为CoreData和TableView已经举行了合伙,所以删除后TableView会自动更新,删除代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {
        //通过coreData删除对象
        //通过indexPath获取我们要删除的实体
        Person * person = [self.fetchedResultsController objectAtIndexPath:indexPath];
         
        //通过上下文移除实体
        [self.managedObjectContext  deleteObject:person];
         
        //保存
        NSError *error;
        if ([self.managedObjectContext save:&error]) {
            NSLog(@"%@", [error localizedDescription]);
        }
    }
}

​    c.默许的去除按钮上显示的是Delete,
可以透过下边的方法开展改动,代码如下:

1
2
3
4
5
6
//设置删除的名字
 
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return @"删除";
}

   (6)到这一步删除作用算是大功告成了,还有最后一个效率点,就是创新大家的数目。更新数据通过点击相应的cell,把cell上的数码传到UpdateView的页面上,然后举行更新即可。

    a.上面的代码是获取数据大家选中的数目并经过KVC把参数传到目标视图中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#pragma mark - Navigation
//把对应的cell上的值传到修改的页面上
 
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    //参数sender是点击的对应的cell
    //判断sender是否为TableViewCell的对象
    if ([sender isKindOfClass:[UITableViewCell class]]) {
        //做一个类型的转换
        UITableViewCell *cell = (UITableViewCell *)sender;
         
        //通过tableView获取cell对应的索引,然后通过索引获取实体对象
        NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
         
        //用frc通过indexPath来获取Person
        Person *person = [self.fetchedResultsController objectAtIndexPath:indexPath];
         
        //通过segue来获取我们目的视图控制器
        UIViewController *nextView = [segue destinationViewController];
         
        //通过KVC把参数传入目的控制器
        [nextView setValue:person forKey:@"person"];
    }
}

    b.在UpdateViewController中把传过来的实业对象举行翻新,再保存。更新部分的代码和添加一些的代码几乎,在那就不往上贴啦。

  经过地点的劳碌的长河后大家的tableView就会深深的爱上CoreData,
可能上面的内容有些多,有问号的可以留言互换。 

 

 

  上边所做的功力里大家的实在的通信录还有些不一致,看过地点的代码的伴儿会有个问题:添加的页面和立异的页面能无法利用同一个啊?
当然啦,为了听从Don`t Repeat
Yourself的尺度,下边大家就把多个一般的页面合并在协同,同时给大家每条记下加上头像和给整个tableView加上索引。

  1.把创新页面删掉,做如下修改,点击添加和改动都跳转到大家的编制页面,同时加上一个自定义Button,点击Button时,大家会调用ImagePickerController来从手机相册获取图片:

sqlite 7

  2.为了把头像持久化存储,大家还得修改数据模型,从新生成Person类,添加一个存储image的选项,是通过二进制的形式储存的

sqlite 8

  3.在前头封存的ViewController中如若Person为空,说明是举行的增进记录的艺术大家就生成一个新的person,
如果Person不为空则不新建Person对象,直接更新完保存。

    (1)为了取得图片,我们必要添加ImagePickerController对象,并在viewDidLoad中做相应的布置,代码如下

1
2
//声明ImagePicker
@property (strong, nonatomic) UIImagePickerController *picker;

       进行连锁布署

1
2
3
4
5
6
//初始化并配置ImagePicker
self.picker = [[UIImagePickerController alloc] init];
//picker是否可以编辑
self.picker.allowsEditing = YES;
//注册回调
self.picker.delegate = self;

 

    (2)点头像会跳转到大家定义好的ImagePickerController中,大家就可在图片库中甄选相应的相片啦。

1
2
3
4
5
6
7
//点击图片按钮设置图片
- (IBAction)tapImageButton:(id)sender {
 
    //跳转到ImagePickerView来获取按钮
    [self presentViewController:self.picker animated:YES completion:^{}];
 
}

 

    (3)在ImagePickerController中点击取消按钮触发的风浪,跳转到原来编辑的界面

1
2
3
4
5
6
//回调图片选择取消
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    //在ImagePickerView中点击取消时回到原来的界面
    [self dismissViewControllerAnimated:YES completion:^{}];
}

 

      (4)选完图片把头像设置成用户选中的按钮,并dismiss到原来界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//实现图片回调方法,从相册获取图片
-(void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
 
    //获取到编辑好的图片
    UIImage * image = info[UIImagePickerControllerEditedImage];
     
    //把获取的图片设置成用户的头像
    [self.imageButton setImage:image forState:UIControlStateNormal];
     
    //返回到原来View
    [self dismissViewControllerAnimated:YES completion:^{}];
 
}

    (5)把大家点击保存按钮回调的点子作如下修改,如果person为空,大家会新建一个新的person.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- (IBAction)tapSave:(id)sender
{
    //如果person为空则新建,如果已经存在则更新
    if (self.person == nil)
    {
        self.person = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([Person class]) inManagedObjectContext:self.managedObjectContext];
    }
    //赋值
    self.person.name = self.nameTextField.text;
    self.person.tel = self.telTextField.text;
    self.person.firstN = [NSString stringWithFormat:@"%c", pinyinFirstLetter([self.person.name characterAtIndex:0])-32];
     
    //把button上的图片存入对象
    UIImage *buttonImage = [self.imageButton imageView].image;
    self.person.imageData = UIImagePNGRepresentation(buttonImage);
     
    //保存
    NSError *error;
    if (![self.managedObjectContext save:&error]) {
        NSLog(@"%@", [error localizedDescription]);
    }
     
    //保存成功后POP到表视图
    [self.navigationController popToRootViewControllerAnimated:YES];
     
}

​    

    (6)因为是何更新页面公用的所以大家要在viewDidLoad对Text菲尔德和Button的背景展开伊始化,假诺person中的imageData有值大家有用传过来的图片,否则用默许的图纸,添加数据起始化代码如下:

1
2
3
4
5
6
7
8
9
self.nameTextField.text = self.person.name;
self.telTextField.text = self.person.tel;
 
if (self.person.imageData != nil)
{
    UIImage *image = [UIImage imageWithData:self.person.imageData];
    [self.imageButton setImage:image forState:UIControlStateNormal];
 
}

​  

  

  4.方面的代码就可以插入头像了,大家须要在tableView中开展显示即可,在tableView中从person对象中赢得相应的头像,然后突显即可,上面大家要抬高索引。

    (1)在cell中显得头像的代码如下:

1
2
3
4
if (person.imageData != nil) {
    UIImage *image = [UIImage imageWithData:person.imageData];
    cell.imageView.image = image;
}

    (2)完毕添加索引回调的法门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//给我们的通讯录加上索引,下面的方法返回的时一个数组
-(NSArray *) sectionIndexTitlesForTableView:(UITableView *)tableView
{
    //通过fetchedResultsController来获取section数组
    NSArray *sectionArray = [self.fetchedResultsController sections];
     
    //新建可变数组来返回索引数组,大小为sectionArray中元素的多少
    NSMutableArray *index = [NSMutableArray arrayWithCapacity:sectionArray.count];
     
    //通过循环获取每个section的header,存入addObject中
    for (int i = 0; i < sectionArray.count; i ++)
    {
        id <NSFetchedResultsSectionInfo> info = sectionArray[i];
        [index addObject:[info name]];
    }
     
    //返回索引数组
    return index;
}

​  经过地点的步调,大家从前俩个页面能够共用,而且丰裕了头像和目录,运行效果如下:

 

   上边的内容挺多的啊吧,别着急,大家的这几个通信录还没完呢,通讯录中的询问功效是必备的,因为当存的用户多了,为了便利用户查询我们还索要加上一个控件。接下来是我们Search
Bar and Search
出场的时候了。UISearchDisplayController自己有一个TableView用于呈现查询出来的结果,需求在通信录中添加一些代码大家的Seach
Bar就足以行使了。

  1.在storyboard中添加Search Bar and
Search,然后把性能拖入大家相应的TableViewController中即可,新添加属性如下:

//添加Search Display Controller属性
@property (strong, nonatomic) IBOutlet UISearchDisplayController *displayC;

  

  2.编制SearchBar内容改动后调用的法子,大家会由此用户输入的情节开展一个模糊查询,把询问的始末添加到大家事先的fetchResultController中

 1 //当search中的文本变化时就执行下面的方法
 2 - (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
 3 {
 4     //新建查询语句
 5     NSFetchRequest * request = [[NSFetchRequest alloc]initWithEntityName:NSStringFromClass([Person class])];
 6     
 7     //排序规则
 8     NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"firstN" ascending:YES];
 9     [request setSortDescriptors:@[sortDescriptor]];
10     
11     //添加谓词
12     NSPredicate * predicate = [NSPredicate predicateWithFormat:@"name contains %@",searchText];
13     [request setPredicate:predicate];
14     
15     //把查询结果存入fetchedResultsController中
16     self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"firstN" cacheName:nil];
17     
18     NSError *error;
19     if (![self.fetchedResultsController performFetch:&error]) {
20         NSLog(@"%@", [error localizedDescription]);
21     }
22 }

 

  3.因为UISearchDisplayController里的TableView和我们事先的tableView用的是一个FetchedReaultsController,所以在UISearchDisplayController裁撤的时候要重载一下大家事先的TableView,或去通讯录中的FetchedResultsController,
代码如下:

//当在searchView中点击取消按钮时我们重新刷新一下通讯录
-(void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
{
   [self viewDidLoad];
}

 

  4.因为经过search查询的结果集会突显在UISearchDisplayController自己的tableView中,所以加载cell时要开展对应的采取,search中的cell是大家自定义的cell,
接纳代码如下:

 1     //根据不同的tableView来设置不同的cell模板
 2     if ([tableView isEqual:self.tableView])
 3     {
 4         
 5         cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
 6         
 7     }
 8     else
 9     {
10         cell = [tableView dequeueReusableCellWithIdentifier:@"SearchCell" forIndexPath:indexPath];
11         
12     }

 

  5.在大家的询问后的列表中,即使还想点击cell以后跳转到编辑页面,大家该如何做啊?
添加底下的回调方法,用代码举行跳转,代码如下:

 1 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
 2 {
 3     if ([tableView isEqual:self.displayC.searchResultsTableView])
 4     {
 5          Person *person = [self.fetchedResultsController objectAtIndexPath:indexPath];
 6         UIStoryboard * s = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
 7         
 8         //获取要目标视图
 9         UIViewController *destination = [s instantiateViewControllerWithIdentifier:@"EditViewController"];
10         
11         //键值编码传值
12         [destination setValue:person forKeyPath:@"person"];
13         
14         [self.navigationController pushViewController:destination animated:YES];
15     }
16 }

 

  经过地点的手续,大家的查询效用就写好了,下边是最终的运行结果:

sqlite 9

  经上边这么曲折的进度,大家的通讯录的基本成效就基本上了。

  github分享地址:https://github.com/lizelu/TellBook

网站地图xml地图