在触发到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中存放着大家要出示的新闻
千字不如一图,看到上边对我们要设计的数据结构的叙说会有点迷糊,上面来张图吧:
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中成立大家的数码实体模型,如下图所示
(3)通过创办好的数量实体模型来创建大家的实体类(和JavaBean类似的东西)创设进度如下图,点击下一步未来,选中创制的实体模型即可:
2.CoreData备选的大都啦,该我们的TableView出场啦,在Empty
Application中默许的时并未storyboard,
倘诺你又想透过storyboard来简化你的操作,得给使用创建一个storybaord才对,成立进度如下:
(1)第一步成立一个storyboard文件,命名为Main,如下图所示
(2)第二步:设置从storyboard来启动, 在Main
InterFace中选中大家制造的storyboard即可
(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)大家要兑现对通信录的增删改查,首要须求入下图所示:
(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来从手机相册获取图片:
2.为了把头像持久化存储,大家还得修改数据模型,从新生成Person类,添加一个存储image的选项,是通过二进制的形式储存的
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 }
经过地点的手续,大家的查询效用就写好了,下边是最终的运行结果:
经上边这么曲折的进度,大家的通讯录的基本成效就基本上了。
github分享地址:https://github.com/lizelu/TellBook