国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

iOS開發之表視圖愛上CoreData

2019-11-14 20:04:37
字體:
來源:轉載
供稿:網友

  在接觸到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:通訊錄來說明問題。

    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,如下圖所示

 

   ?。?)第二步:設置從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)實現添加功能,點擊右上角的添加按鈕時會跳轉到添加頁面,在添加頁面中有兩個TextField來接受用戶的輸入,點擊添加按鈕進行數據添加。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];
     
}

 

? ?。?)實現上面的代碼只是通過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對象,直接更新完保存。

   ?。?)為了獲取圖片,我們需要添加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對TextField和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;
}

   ?。?)實現添加索引回調的方法

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 }

 

  經過上面的步驟,我們的查詢功能就寫好了,下面是最終的運行結果:

  經上面這么曲折的過程,我們的通訊錄的基本功能就差不多了。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 日喀则市| 潼关县| 延津县| 琼海市| 武陟县| 大渡口区| 宁蒗| 航空| 西乡县| 和硕县| 咸丰县| 通州区| 平邑县| 新沂市| 石渠县| 司法| 西昌市| 昌宁县| 新源县| 呼伦贝尔市| 磐安县| 昌乐县| 榆树市| 苏尼特右旗| 平顶山市| 项城市| 阜南县| 建宁县| 柳河县| 五指山市| 霸州市| 新丰县| 兴隆县| 衡山县| 柞水县| 二手房| 阜新| 翁牛特旗| 澄迈县| 康平县| 乌什县|