D12:UITableView(多行删除_UISearchBar_自定义Section的header)

Inheritance

NSObject
UIResponder
UIView
UIScrollView
UITableView

一、UITableView多行删除

  1. NSArray(NSMutableArray)的2个实用方法
  2. 选中某一行后加入删除数据数组
  3. 点击NavigationBar右侧的Item执行的方法

二、UITableView结合搜索功能

  1. 搜索的控件
  2. 为搜索框添加数据源, 在UISearchResultsUpdating协议实现
  3. 判断是否在搜索,根据属性"isSearch"改写一些方法
  4. 发现问题, 去之前的地方修改
  5. 给表格添加右侧的索引(SectionIndex)

三、自定义UITableView每个Section的header,模拟QQ好友界面分组的点击展开收拢

  1. 当前选中的section, 初始值默认为0
  2. 在创建tableView的时候设置背景视图
  3. 显示表格的每个section的header
  4. 返回每一组有多少行
  5. �点击headView之后去判断点击的Section和之前已选中的Section的关系

一、UITableView多行删除

NSArray(NSMutableArray)的2个实用方法 :

- (void)removeObjectsInArray:(NSArray *)array

- (BOOL)containsObject:(id)anObject

  1. 将要删除的数据

    @property (nonatomic, strong) NSMutableArray *deleteArray;
    
  2. 为视图添加删除按钮

    // 点击进入表格多行选择的状态
    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithTitle:@"删除" style:UIBarButtonItemStyleDone target:self action:@selector(beginDelete:)];
    self.navigationItem.rightBarButtonItem = rightItem;
    
    // 删除数据数组的初始化
    _deleteArray = [NSMutableArray array];        
  1. 默认是删除状态, 我们要修改为可以多选的编辑状态

    - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
    

{
return UITableViewCellEditingStyleDelete|UITableViewCellEditingStyleInsert;
}
```

  1. 选中某一行后加入删除数据数组

    判断是否是在编辑模式下的点击: 没有判断的话, 在非多选状态下选中一条cell, 进入再退出编辑状态就会产生bug删除之前选中过的cell
    判断是否已经加入过删除数据数组

    • (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
      {
      if (tableView.editing == YES) { // 如果没有这句判断的话, 在非多选状态下选中一条cell, 进入再退出编辑状态就会产生bug删除之前选中过的cell
      // 获取选择的对象
      StudentModel *model = self.dataArray[indexPath.row];
      if (![self.deleteArray containsObject:model]) {
      // 添加到删除数据数组
      [self.deleteArray addObject:model];
      }
      }
      }

    // 取消选择, 表示不需要删除该数据

    • (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
      {
      // 获取取消选择的对象
      StudentModel *model = self.dataArray[indexPath.row];

      if ([self.deleteArray containsObject:model]) {
      [self.deleteArray removeObject:model];
      }
      }

  2. 点击NavigationBar右侧的Item执行的方法

    - (void)beginDelete:(id)sender
    {
        // 获取导航按钮上的文字
        NSString *title = self.navigationItem.rightBarButtonItem.title;
        **if ([title isEqualToString:@"删除"])** {
            // 进入表格的多选状态
            [self.tbView setEditing:YES animated:YES];
            // 将导航按钮文字改为"确定"
            self.navigationItem.rightBarButtonItem.title = @"确定";
        } **else { // 文字为"确定"**
            // 执行删除操作
            // 从数据源数组中删除需要删除的数据
            [self.dataArray removeObjectsInArray:self.deleteArray];
            
            // 取消表格的编辑状态
            [self.tbView setEditing:NO];
    
            // 刷新表格
            [self.tbView reloadData];
            
            // 将导航文字恢复成"删除"
            self.navigationItem.rightBarButtonItem.title = @"删除";
        }
    }
    

二、UITableView结合搜索功能

// 创建搜索视图
// iOS7: UISearchDisplayController 已不建议使用
// iOS8: UISearchController

  1. 搜索的控件

@property (nonatomic, strong) UISearchController *searchCtrl;


    * #### 创建搜索控件
    
    ```  
     - (void)createSearch
    {
        // 1. 存nil表示当前搜索结果就保存在当前视图控制器中, 而非是其他视图控制器
        _searchCtrl = [[UISearchController alloc] initWithSearchResultsController:nil];
        // 2. 设置代理
        _searchCtrl.delegate = self;
        // 3. 设置搜索数据更新的代理:怎样显示搜索结果
        _searchCtrl.searchResultsUpdater = self;
        
        // 4. 显示搜索条
        [_searchCtrl.searchBar sizeToFit];
        
        // 5. 添加到父视图
        /*
         tableHeaderView: 表格的表头, 显示在整个表格的上面
         tableFooterView: 表格的表尾, 显示在整个表格的下面
         */
        _tbView.tableHeaderView = _searchCtrl.searchBar;
        
        // 这样实现, 搜索结果也是显示到_tbView中
        // _tbView显示的数据, 就要区分是在搜索时, 还是非搜索的时候
}
  • 为搜索框添加数据源, 在UISearchResultsUpdating协议实现

// 存储符合搜索条件的数据
@property (nonatomic, strong) NSMutableArray *searchResultArray;

* #### 在搜索框里面的文字修改时调用
* #### 在这个方法里面实现搜索的功能
  
```
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
    // 如果搜索结果数组没有初始化, 初始化数组对象
    if (self.searchResultArray == nil) {
        _searchResultArray = [NSMutableArray array];
    }
    
    // 实现搜索的功能
    // 获取搜索的关键字
    NSString *keyword = self.searchCtrl.searchBar.text;
    
    // 从_dataArray数组里面找符合条件的数据
    for (NSArray *sectionArray in self.dataArray) {
        // 遍历每一个section里面的内容
        for (NSString *title in sectionArray) {
            // NSCaseInsensitiveSearch表示大小写不敏感: a与A,a都可以匹配
            NSRange range = [title rangeOfString:keyword options:NSCaseInsensitiveSearch];
            if (range.location != NSNotFound) {
                // 符合条件, 添加到搜索结果数组中
                [self.searchResultArray addObject:title];
            }
        }
    }
    // 显示数据
    [self.tbView reloadData];
}
```    
  • 判断是否在搜索

@property (nonatomic, assign) BOOL isSearch;
* 首先根据当前搜索状态给"isSearch"赋值

        // 进入搜索状态
   - (void)willPresentSearchController:(UISearchController *)searchController
   {
       self.isSearch = 1;
       [self.tbView reloadData];
   }
   
   // 退出搜索状态
   - (void)willDismissSearchController:(UISearchController *)searchController
   {
       // 结束搜索状态
       self.isSearch = 0;
       [self.tbView reloadData];
   }
* #### 根据属性"*isSearch*"改写一些方法    
   // 返回多少组
   - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView  
   {
       **if (self.isSearch) {
           // 搜索时显示一组
           return 1;
       }**
       return self.dataArray.count;
   }
    ```    

   ```  
   // 返回每组多少行
   - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
   {
       **if (self.isSearch) {
           // 搜索时返回的行数
           return self.searchResultArray.count;
       }**
       return [self.dataArray[section] count];
   }
   ```
   ```
   // 返回cell
   - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
   {
       **if (self.isSearch) {
           // 搜索时
           static NSString *SearchCellID = @"Search";
           UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SearchCellID];
           if (nil == cell) {
               cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SearchCellID];
           }
           // 显示数据
           cell.textLabel.text = self.searchResultArray[indexPath.row];
           return cell;
       }**
           原代码
   }
   ```  
   ```  
   // 每个Section的标题
   - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
   {
       if (self.isSearch) {
           return @"搜索结果";
       }
       return [NSString stringWithFormat:@"第%c组", (int)section+'A'];
   }  
   ```    
---
#### 发现问题, 去之前的地方修改
1. #### 修改UISearchController的属性
   
   1. 使搜索结果滑动: 隐藏前面的遮罩视图  
`    _searchCtrl.dimsBackgroundDuringPresentation = NO;`  
   2. 在搜索的时候显示导航栏  
   `_searchCtrl.hidesNavigationBarDuringPresentation = NO;`
 
2. #### 修改搜索词时重新刷新tableView    

// 在搜索框里面的文字修改时调用
// 在这个方法里面实现搜索的功能

  • (void)updateSearchResultsForSearchController:(UISearchController *)searchController
    {
    // 如果搜索结果数组没有初始化, 初始化数组对象
    if (self.searchResultArray == nil) {
    _searchResultArray = [NSMutableArray array];
    }
    else {
    [self.searchResultArray removeAllObjects];
    }
  
 ---
* #### 给表格添加右侧的索引(*SectionIndex*)    
    1. **表格右侧索引的文字**
    
        ```
        //返回索引上面显示的文字内容
        //实现这个方法,系统或默认在右侧添加索引视图
        //点击相应地文字会跳转到对应的section
        //这种对应关系是按照顺序对应的
        //索引视图默认比较小,如果需要的话,可以自己去实现这个功能 
        
        /*
         自己实现功能的思路:
         添加一个视图,放在表格视图的右边
         在上面添加很多按钮
         点击按钮的事件中,设置表格视图的偏移量
         //计算位置
         //header的高度和cell的高度
         //_tbView scrollToRowAtIndexPath:<#(NSIndexPath *)#> atScrollPosition:<#(UITableViewScrollPosition)#> animated:<#(BOOL)#>
         */
         
        - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
        {
            NSMutableArray *array = [NSMutableArray array];
            //26个组对应的索引上面的标题分别是每个字母的大写
            for (int i='A'; i<='Z'; i++) {
                [array addObject:[NSString stringWithFormat:@"%c",i]];
            }
            return array;
        }
        
        ```
    2.  **实现另外一个代理方法,可以修改点击右边index项对应跳转的tableView的section顺序**
    
        ```
        - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
        {
            //参数中的index是索引数组里面的序号
            //返回的值是表格视图的section的序号
            return index;
        }  
        ```

  
* #### 为搜索框在右侧的*"sectionIndexTitles"*增加一个索引    

  • (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
    {
    NSMutableArray *indexArray = [NSMutableArray array];

    // 对应搜索框的索引
    [indexArray addObject:UITableViewIndexSearch];

    for (int i = 0; i < 26; i++) {
    [indexArray addObject:[NSString stringWithFormat:@"%c", 'A'+i]];
    }
    return indexArray;
    }

  • (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
    {
    // 参数中的index是索引数组里面的序号
    // 返回的值是表格视图的section序号

    // 表格的section 索引的序号
    //搜索:-1 0
    //A: 0 1
    //B: 1 2
    if (index == 0) {
    // 搜索的索引
    self.tbView.contentOffset = CGPointZero;
    return -1;
    } else {
    return index-1;
    }
    }

  

---
## 三、自定义UITableView每个Section的header,模拟QQ好友界面分组的点击展开收拢
* #### 当前选中的section, 初始值默认为0
`@property (nonatomic, assign) NSInteger selectSection;`

* #### 在创建tableView的时候设置背景视图 

UIImageView *bgImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"gamebg"]];
bgImageView.frame = _tbView.bounds;
_tbView.backgroundView = bgImageView;


* #### 显示表格的每个section的header      

    ```
    - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
    {
        // section的header的背景视图
        UIImageView *headerImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 40)];
        headerImageView.image = [UIImage imageNamed:@"header"];
        
        // 创建标签, 显示在第几组
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(80, 0, 120, 40)];
        label.text = [NSString stringWithFormat:@"第%ld组", section + 1];
        [headerImageView addSubview:label];
    
        // 添加一个图片, 显示展开的状态
        UIImageView *leftImageView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 0, 40, 40)];
        if (section == self.selectSection) {
            // 选中的图片
            leftImageView.image = [UIImage imageNamed:@"list_ico_d"];
        } else {
            // 未选中的图片
            leftImageView.image = [UIImage imageNamed:@"list_ico"];
        }
        [headerImageView addSubview:leftImageView];
        
        // 点击的功能
        // 添加一个手势
        // 打开用户交互
        UITapGestureRecognizer *g = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)];
        [headerImageView addGestureRecognizer:g];
        headerImageView.userInteractionEnabled = YES;
        headerImageView.tag = 200+section;
        
        return headerImageView;
    }  
    ```

* #### 返回每一组有多少行  
  
    ```  
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        if (section == _selectSection) {
            //选中的组里面的内容全部显示
            NSArray *sectionArray = _dataArray[section];
            return sectionArray.count;
        }else{
            //没有选中的组cell全部不显示,只显示header
            return 0;
        }
    }
    ```  
* #### 点击headView之后去判断点击的Section和之前已选中的Section的关系

    ```
    - (void)tapAction:(UIGestureRecognizer *)g
    {
        //获取点击的section
        UIImageView *headerView = (UIImageView *) g.view;
        NSInteger section = headerView.tag-200;
        NSLog(@"section:%ld",section);
        
        //赋值给选中section的成员变量
        if (section == _selectSection) {
            //点击的是已经展开的组
            //将展开的组关闭
            _selectSection = -1;
            [_tbView reloadData];
        }else{
            
            //点击的是关闭的组
            //将已经展开的组合并,将点击的组展开
            _selectSection = section;
            
            [_tbView reloadData];
        }
        
    }
    ```
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容