仿ios通訊錄實現中的細節感悟

仿ios通訊錄實現中的細節感悟

本人選擇實現ios通訊錄的基本功能,主要也是考慮到UITableView在各種應用程序中的普遍使用,同時也包含了UITableView的刪除,移動等編輯功能,同時也包含plist文件解析,UISearchController的基本使用,也比較全面的實現了通訊錄的基本功能。

通訊錄實現的細節:
- 靜態plist文件解析
- tableview根據分組列表顯示
- 刪除聯繫人
- 添加聯繫人
- 分組內移動聯繫人
- 頂部搜索框實現(UISearchController基本使用)
- 導航欄以及searchbar設置

靜態plist文件解析

這裏需要注意的是在解析過程中,首先需要分析清楚plist文件的層級結構,解析的時候按照文件的層級結構,由外到內逐層解析。

這裏是我的plist文件結構

解析代碼實現:
使用字典和數組來存儲數據

@property (nonatomic,retain) NSMutableDictionary *dictionary;
@property (nonatomic,retain) NSMutableArray *array;
//獲取文件路徑
        NSString *path = [[NSBundle mainBundle] pathForResource:@"StudentInformation" ofType:@"plist"];
        //獲取文件內容(字典)
        self.dictionary = [NSMutableDictionary dictionaryWithContentsOfFile:path];
        //獲取分組名
        self.array = [NSMutableArray arrayWithArray:[_dictionary allKeys]];
        //分組名排序
        [_array sortUsingSelector:@selector(compare:)];

tableview分組顯示

這裏需要實現數據顯示,需要實現tableview的數據源和代理,我是使用UITableViewController實現,需要實現分組顯示以及分組快速索引。

這裏需要實現tableview的數據源和代理,有幾個基本的方法是必不可少的。

設置tableview的分組數和分組名

這裏需要根據解析的數據設置分組數和分組名

//返回分組數
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
#warning Potentially incomplete method implementation.
    // Return the number of sections.
    return _array.count;
}
//設置分組名
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
    return _array[section];
}

設置tableview每個分組的行數和行高

需要根據字典中存儲的數據設置

//返回分組的行數
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
#warning Incomplete method implementation.
    // Return the number of rows in the section.
    //獲取每個分組
    NSArray *arr = _dictionary[_array[section]];
    return arr.count;
}
//設置行高
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 80;
}

設置分組的快速索引

特別注意,函數的返回值是數組,因此需要將分組名的數組返回:

//設置快速索引
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{
    return _array;
}

設置cell

tableview自帶重用機制,首先需要註冊相應的cell,然後每次使用時到重用池中取,而不需要每次都創建新的cell,然後cell上顯示的數據使用model傳遞,體現MVC模式。

註冊cell:

//註冊cell
[self.tableView registerClass:[ContactCell class] forCellReuseIdentifier:identifier];
//設置cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    ContactCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];

    // Configure the cell...
    //獲取
    Person *per = [Person new];
    NSArray *arr =  _dictionary[_array[indexPath.section]];
    [per setValuesForKeysWithDictionary:arr[indexPath.row]];
    //設置
    cell.per = per;

    return cell;
}

這裏需要將字典中的內容轉換到model中,同時在model類的實現中,必須重寫- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key方法,因爲在這個方法中可以解決字段名不同的衝突。以防程序崩潰。

tableview動態刪除聯繫人

這裏只是實現了刪除功能,編輯的狀態之用一種。

tableview實現可以編輯,主要分四步完成:
1.設置tableview可以被編輯。
2.指定那些航可以被編輯。
3.制定編輯樣式
4.完成編輯

//1.設置tableview可編輯
- (void)setEditing:(BOOL)editing animated:(BOOL)animated{
    //父類發送消息
    [super setEditing:editing animated:animated];
    //tableview設置狀態
    [self.tableView setEditing:editing animated:animated];
    //title狀態
    self.editButtonItem.title = editing ? @"完成" : @"編輯";
}

//2.指定那些行可以被編輯(默認所有行都可以被編輯)
// 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;
}

//3.設置編輯樣式
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewCellEditingStyleDelete;
}

//4.完成編輯
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
        // Delete the row from the data source
    //處理數據
    //獲取分組
    NSString *key = _array[indexPath.section];
    NSMutableArray *arr = _dictionary[key];
    //判斷分組人數
    if (arr.count == 1) {
        //刪除整個分組
        //從dictionary中刪除分組
        [_dictionary removeObjectForKey:key];
        //刪除快速索引
        [_array removeObjectAtIndex:indexPath.section];
        //UI上處理
        //獲取刪除的分組
        NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:indexPath.section];
        //移除
        [tableView deleteSections:indexSet withRowAnimation:UITableViewRowAnimationFade];
    }else{
        //刪除聯繫人
        //從arr中移除聯繫人
        [arr removeObjectAtIndex:indexPath.row];
        //操作UI
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }

}

需要特別注意的是:
- 編輯樣式,默認只有添加和刪除。
- 完成編輯首先是移除數據,然後纔是操作UI,移除數據也要考慮到對分組的影響。

tableview分組內移動

移動和編輯相比而言,比較簡單,只需要將數據處理好,不需要操作UI。

移動操作大概分三步:
1.設置編輯狀態
2.設置哪些行可以被移動
3.移動完成

//2.設置可以移動的行
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    // Return NO if you do not want the item to be re-orderable.
    return YES;
}

//3.移動完成
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
    //獲取分組
    NSMutableArray *arr = _dictionary[_array[fromIndexPath.section]];
    //獲取刪除的聯繫人
    NSDictionary *dic = arr[fromIndexPath.row];
    //從原位置刪除數據
    [arr removeObjectAtIndex:fromIndexPath.row];
    //插入到目標位置
    [arr insertObject:dic atIndex:toIndexPath.row];

}

這裏需要注意的就是跨行移動,因爲分組內的移動纔是有意義的,tableview提供了檢測跨行移動的方法:

//檢測跨行移動
- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath{
    //判斷是否在同一個分組
    if (sourceIndexPath.section == proposedDestinationIndexPath.section) {
        return proposedDestinationIndexPath;
    }
    //不再在同一個分區,返回源路徑
    return sourceIndexPath;
}

UISearchController使用簡介

之所以使用UISearchController,主要就是爲了實現搜索的效果,我們查看手機中的通訊錄不難發現,搜索框的實現效果還是極好的,這就要歸功於UISearchController,在ios8之後 “UISearchDisplayController has been replaced with UISearchController”,UISearchController本省就有searchbar,因此使用起來更加方便。

查看UISearchController的文檔不難發現,它的屬性值不多,這也比較方便我們學習。

SearchResultController *search = [[SearchResultController alloc] init];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:search];
self.searchController.searchResultsUpdater = search;
self.searchController.delegate = search;

注意:**UISearchController的對象一定要設置成屬性質,不然會沒有效果,即:@property (nonatomic,retain) UISearchController *searchController;
再者,searchController的代理和搜索功能的實現可以具體查看UISearchController的代理協議。

searchbar設置和navigationbar設置

1.細心的朋友會發現,給tableview設置tableHeaderView後,如果按照如下代碼設置,設置完成後會發現searchable頂部的背景和tableview的背景不一樣,這裏就需要換一種設置方式。

self.tableView.tableHeaderView = self.searchController.searchBar;
#warning searchBar上面的背景跟tableview不一樣,怎麼解決
    UIView *myView = [UIView new];
    myView.frame = CGRectMake(0, 0, self.view.frame.size.width, 44);
    [myView addSubview:self.searchController.searchBar];
    self.tableView.tableHeaderView = myView;

2.改變searchbar上取消按鈕的顏色和文字如何設置,這裏就需要實現UISearchController的代理,在UISearchController顯示時改變設置。

#pragma mark - UISearchControllerDelegate代理事件
- (void)willPresentSearchController:(UISearchController *)searchController{
    //設置取消按鈕顯示
    [searchController.searchBar setShowsCancelButton:YES animated:YES];
    //查找searchBar子視圖
    for(id cc in [[[searchController.searchBar subviews] firstObject] subviews])
    {
        //如果找到
        if([cc isKindOfClass:NSClassFromString(@"UINavigationButton")])
        {
            UIButton *btn = (UIButton *)cc;
            //改變顏色
            [btn setTitleColor:searchBarCancelButtonColor forState:UIControlStateNormal];
        }
    }
}

3.關於navigationbar的設置,主要是navigationbar底部的黑線,這裏提供一種去掉黑點的方法

//去掉navigationbar下面的黑線
    if ([self.navigationBar respondsToSelector:@selector( setBackgroundImage:forBarMetrics:)]){
        NSArray *list=self.navigationBar.subviews;
        for (id obj in list) {
            if ([obj isKindOfClass:[UIImageView class]]) {
                UIImageView *imageView=(UIImageView *)obj;
                NSArray *list2=imageView.subviews;
                for (id obj2 in list2) {
                    if ([obj2 isKindOfClass:[UIImageView class]]) {
                        UIImageView *imageView2=(UIImageView *)obj2;
                        imageView2.hidden=YES;
                    }
                }
            }
        }
    }

總結

本文主要是針對通訊錄應用進行了分析,闡釋了主要的實現步驟,需要對tableview的各種代理方法比較清楚,同時tableview也是應用程序中使用頻率最高的控件,希望本文對初學者有所幫助。

項目下載地址

項目下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章