iOS tableView详解

释放双眼,带上耳机,听听看~!

1、UITableViewDataSource数据源方法

// 返回第section组中有多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
// 返回多少组,没实现该方法,默认为1
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;

2、UITableViewDelegate代理方法

// 即将显示tableviewcell时调用
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;

// 即将显示header时调用,在cell之后调用
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);

// 即将显示footer时调用,在header之后调用
- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);

// 在删除cell之后调用,停止显示cell的时候调用,界面不显示cell时。
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath NS_AVAILABLE_IOS(6_0);

// 停止显示header的时候调用
- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);

// 停止显示footer的时候调用
- (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);

3、高度代理方法

// 在设置每行cell的高度,header的高度,footer的高度
// 设置某行cell高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
// 设置header高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
// 设置footer高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;

4、设置分组View的方法

// 返回某个section对应的header标题
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
// 返回某个section对应的footer标题
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;
// 设置第section分组的header
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;
// 设置第section分组的footer
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;

5、操作cell时调用的方法

// cell选中时调用
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
// cell取消选中时调用
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(3_0);

6、编辑模式相关的代理方法

// 返回每一行cell的编辑模式, 可以再次设置add或者删除操作。
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
// cell左滑删除时,删除按钮的标题
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(3_0);
// 自定义编辑左滑后出现的界面。  不止只有一个delete按钮, 可以自行定义,返回一个数组。数组中放着UITableviewRowAction
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);
// 未实现 默认为yes,进入编辑时,cell是否缩进。  在开启编辑状态时调用。
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath;

// 右滑准备进行编辑的时候 调用。 将setediting = yes时不调用
- (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath;
// 完成编辑的时候调用
- (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath;

6、索引

//返回要显示的section索引标题
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView;
// return list of section titles to display in section index view (e.g. "ABCD...Z#")
// 点击右侧索引表项时调用
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index;
// 返回指定点所在位置的indexPath
- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point;
// 返回指定cell所在的indexPath
- (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell;
// 返回指定范围rect中的所有cell的indexPath
- (NSArray *)indexPathsForRowsInRect:(CGRect)rect;                              // returns nil if rect not valid
// 返回索引indexPath所指向的cell。
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath;

7、UITableView的属性和构造方法

// cell的构造方法,自定义cell时,如果要初始化设置cell属性时,可以重写该方法,在方法内部设置
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style;


// UITableView的类型: plain类型和group分组类型
@property (nonatomic, readonly) UITableViewStyle           style;
// 数据源
@property (nonatomic, assign)   id <UITableViewDataSource> dataSource;
// 代理
@property (nonatomic, assign)   id <UITableViewDelegate>   delegate;
// 设置所有cell的行高,默认44
@property (nonatomic)          CGFloat                     rowHeight;
// 分组头部高度
@property (nonatomic)          CGFloat                     sectionHeaderHeight;
// 分组尾部高度
@property (nonatomic)          CGFloat                     sectionFooterHeight;
// cell估算高度,默认0
@property (nonatomic)          CGFloat                     estimatedRowHeight NS_AVAILABLE_IOS(7_0);
// 分组头部估算高度
@property (nonatomic)          CGFloat                     estimatedSectionHeaderHeight NS_AVAILABLE_IOS(7_0);
// 分组微博估算高度
@property (nonatomic)          CGFloat                     estimatedSectionFooterHeight NS_AVAILABLE_IOS(7_0);
// 分割线内边距
@property (nonatomic)          UIEdgeInsets                separatorInset NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR; // allows customization of the frame of cell separators

8、cell刷新方法

// 重新载入tableview所有cell  一般是在数据源有改变的时候
- (void)reloadData;
// 重新载入,section的索引标题。
- (void)reloadSectionIndexTitles NS_AVAILABLE_IOS(3_0);   // reloads the index bar.

8、UITableView滚动方法

// 根据传入的indexPath,滚动到相对应的位置,第二个参数是控制对应的cell再滚动后处于tableview的顶部/底部/中部等
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated;
// 滚动到被选中项。  滚动后处于tableview的顶部/底部/中部等
- (void)scrollToNearestSelectedRowAtScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated;

9、插入,删除,刷新,移动section组

// 插入,删除,刷新,移动section组
// 插入section
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
// 删除section
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
// 刷新section
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:
(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0);
// 移动section
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection NS_AVAILABLE_IOS(5_0);

实战演习

Unknown.jpeg

1、多选

在结算购物车的时候,我们需要选中多组cell,我们往往都是写了很多代码,很是麻烦,其实tableView有一个自带属性:删除多个Cell,这个时候我们可以稍作修改,就可以做成我们想要的购物车了,十分简单.

效果图:
多选&删除.gif

注意点:

1、[_tableView setEditing:YES animated:YES];//设为为编辑状态,不然不会出现左侧圆圈

2、设置编辑状态的代理。
编辑状态UITableViewCellEditingStyle有三种模式
UITableViewCellEditingStyleDelete
UITableViewCellEditingStyleInsert
UITableViewCellEditingStyleNone
多选框的风格, 只需要风格同时包含UITableViewCellEditingStyleDelete和UITableViewCellEditingStyleInsert就可以了

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewCellEditingStyleDelete | UITableViewCellEditingStyleInsert;
}

3、选中是会出现蓝色背景,要是感觉不好看,可以修改

cell.multipleSelectionBackgroundView = [UIView new];

4、修改有点击选中图标的颜色

cell.tintColor = [UIColor redColor];

5、不想使用默认图标的话,也可以在自定义

-(void)layoutSubviews
{
    for (UIControl *control in self.subviews){
        if ([control isMemberOfClass:NSClassFromString(@"UITableViewCellEditControl")]){
            for (UIView *v in control.subviews)
            {
                if ([v isKindOfClass: [UIImageView class]]) {
                    UIImageView *img=(UIImageView *)v;
                    if (self.selected) {
                        img.image=[UIImage imageNamed:@"xuanzhong_icon"];
                    }else
                    {
                        img.image=[UIImage imageNamed:@"weixuanzhong_icon"];
                    }
                }
            }
        }
    }
    [super layoutSubviews];
}
//适配第一次图片为空的情况
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
    [super setEditing:editing animated:animated];
    for (UIControl *control in self.subviews){
        if ([control isMemberOfClass:NSClassFromString(@"UITableViewCellEditControl")]){
            for (UIView *v in control.subviews)
            {
                if ([v isKindOfClass: [UIImageView class]]) {
                    UIImageView *img=(UIImageView *)v;
                    if (!self.selected) {
                        img.image=[UIImage imageNamed:@"weixuanzhong_icon"];
                    }
                }
            }
        }
    }

}

6、第一次点击选中,第二次点击删除

//已经选中了某一行
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([_selectArray containsObject:_dataSource[indexPath.row]]) {
        return;
    }
    [_selectArray addObject:_dataSource[indexPath.row]];
}

//不选的时候删除
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [_selectArray removeObject:indexPath];
}

7、全选选中与清空

- (void)SelectButton:(UIButton*)button{
    button.selected = !button.selected;
    if (button.selected) {
        //==================全选================
        //选中tableView中所有的indexPath
        NSArray * array = [_tableView indexPathsForRowsInRect:CGRectMake(0, 0, SCREEN_WIDTH, _tableView.contentSize.height)];
        for (NSIndexPath * indexPath in array) {
            [_tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
        }
        [_selectArray removeAllObjects];
        [_selectArray addObjectsFromArray:_dataSource];
    }else{
        
        //============全选清空====================
        for (NSIndexPath * indexPath in _selectArray) {
            [_tableView deselectRowAtIndexPath:indexPath animated:YES];
        }
        //将选中下标数组清空
        [_selectArray removeAllObjects];
        [_tableView reloadData];
    }  
}

2、索引

tableView 自带有索引条,要是项目对索引没有太大的要求,可以直接使用tableView自带的索引。当我们需要一些特定的索引条的时候,我们可以自定制,自定制思路:1、在屏幕滚动查看当前屏幕中最上面出现的是哪一组,屏幕滚动的时候有一个代理。2、找到最上面出现的是哪一组。
我们可以根据情况定制索引条。这里我只是介绍一下自带索引。

效果图

设置索引栏,常要用的一些方法

//设置索引栏
    //背景色设置
    _tableView.sectionIndexBackgroundColor = [UIColor grayColor];
    //索引颜色
    _tableView.sectionIndexColor = [UIColor redColor];
    //点中时背景色
    _tableView.sectionIndexTrackingBackgroundColor = [UIColor blueColor];

需要遵守的代理方法

#pragma mark - 索引栏显示
- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
    //需要将每一组的title拿出来存放到数组中并且返回
    NSMutableArray * array = [[NSMutableArray alloc] init];
    for (JJHCarGroup * carGroup in _dataSource) {
        [array addObject:carGroup.title];
    }
    [array insertObject:@"#" atIndex:0];
    return array;
}

- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
{
    //默认返回的是index,如果标题前面有其它东西,比如联系人列表最前面有个#号,需要返回index-1,否则和预期的不匹配。
    return index - 1;
}

3、搜索栏

UISearchBar 是苹果自带的原生搜索框,简单好用,如果没有什么特殊的需求,我们完全可以使用这个搜索框

效果图
搜索.gif

1、初始化:UISearchBar继承于UIView,我们可以像创建View那样创建searchBar

 UISearchBar * bar = [[UISearchBar alloc]initWithFrame:CGRectMake(20, 100, 250, 40)];
  [self.view addSubview:bar];

2、这个属性可以设置searchBar的搜索框的风格,枚举如下

@property(nonatomic)        UIBarStyle              barStyle; 

typedef NS_ENUM(NSInteger, UIBarStyle) {
    UIBarStyleDefault          = 0,//默认风格 白色搜索框,多出的背景为灰色
    UIBarStyleBlack            = 1,//黑色风格,黑色的搜索框
    //下面两个枚举已经被禁用,作用和黑色风格一样
    UIBarStyleBlackOpaque      = 1, // Deprecated. Use UIBarStyleBlack
    UIBarStyleBlackTranslucent = 2, // Deprecated. Use UIBarStyleBlack and set the translucent property to YES
};

3、UISearchBar常用属性

// 自适应大小
    [searchBar sizeToFit];
    // 1.设置搜索框的样式
    [searchBar setBarStyle:UIBarStyleDefault];
    // 2.设置背景图片(该方法可以去掉UISearchBar上下的两条线)
    searchBar.backgroundImage = [UIImage imageNamed:@"search_bg_icon"];
    // 3.设置主题颜色
    searchBar.barTintColor = [UIColor redColor];
    // 4.设置外边框颜色
    searchBar.barTintColor = [UIColor greenColor];
    // 5.设置光标颜色
    searchBar.tintColor = [UIColor cyanColor];
    // 6.设置是否透明
    searchBar.translucent = YES;
    // 7.设置占位文字
    searchBar.placeholder = @"占位文字";
    // 8.输入框中间的提示文字
    searchBar.prompt = @"提示文字";
    // 9.显示搜索框右侧的搜索结果按钮
    searchBar.showsSearchResultsButton = YES;
    // 10.搜索框右侧的搜索结果按钮是否选中
    searchBar.searchResultsButtonSelected = YES;
    // 11.设置UISearchBar背景的偏移量
    searchBar.searchFieldBackgroundPositionAdjustment = UIOffsetMake(50, 20);
    // 12.设置UISearchBar开始编辑时文本的偏移量
    searchBar.searchTextPositionAdjustment = UIOffsetMake(50, 20);
    // 13.开始编辑时键盘上方出现一个遮盖视图
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 266)];
    view.backgroundColor = [UIColor yellowColor];
    searchBar.inputAccessoryView = view;
    // 14.设置键盘的样式
    searchBar.keyboardType = UIKeyboardTypeASCIICapable;
    // 15.是否显示搜索框下面的选项条
    searchBar.showsScopeBar = YES;
    // 16.搜索框下面选项条中选项的名称
    searchBar.scopeButtonTitles = @[@"aaaa",@"bbbb",@"cccc"];
    // 17.选项条的背景图片
    searchBar.scopeBarBackgroundImage = [UIImage imageNamed:@"ios_v4_preview_2"];
    // 18.选项条默认选中的按钮下标
    searchBar.selectedScopeButtonIndex = 1;
    // 19.显示输入框右侧的书形图标
    searchBar.showsBookmarkButton = YES;
    // 20.显示右侧的取消按钮(无动画)
//    searchBar.showsCancelButton = YES;
    // 21.显示右侧的取消按钮(有动画)
    [searchBar setShowsCancelButton:YES animated:YES];

4、UISearchBar的代理方法

// 开始编辑时会来到该方法
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
// 结束编辑时会来到该方法
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
// 开始编辑时会来到该方法(可以在该方法判断是否允许用户输入)
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
// 结束编辑时会来到该方法
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar
// 点击取消按钮时会来到该方法
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
// 点击键盘的搜索按钮时会来到该方法
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
// 输入框内容发生改变时,会来到该方法
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText

5、简单项目代码

@interface ViewController6 () <UISearchResultsUpdating>

/**
 *  搜索控制器
 */
@property (nonatomic) UISearchController * searchController;
/**
 *  搜索结果存放数组
 */
@property (nonatomic) NSMutableArray * searchResultsArray;

@end

@implementation LZBViewController6

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.searchResultsArray = [[NSMutableArray alloc] init];
    [self createSearchController];
}

- (void)createSearchController
{
    //创建搜索控制器。参数写nil代表使用当前控制器的view来显示搜索结果
    self.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
    //设置搜索结果更新代理,实现协议中方法,更新搜索结果
    self.searchController.searchResultsUpdater = self;
    
    //两个属性设置
    //显示搜索结果时是否添加半透明覆盖层   默认YES
    self.searchController.dimsBackgroundDuringPresentation = NO;
    //搜索的时候是否隐藏导航栏   默认YES
    self.searchController.hidesNavigationBarDuringPresentation = NO;
    
    //设置搜索栏显示到tableView头部视图上面
    self.tableView.tableHeaderView = self.searchController.searchBar;
}

#pragma mark - 搜索更新结果方法实现
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
    //取出搜索栏里面的内容
    NSString * text = searchController.searchBar.text;
    NSArray * array = [self.dataModel searchWithText:text];
    self.searchResultsArray.array = array;
    
    //让tableView重新刷新数据
    [self.tableView reloadData];
}

#pragma mark - 因为使用的是同一个表格视图,所以需要重写表格视图所有方法
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    //判断当前搜索控制器是否处在活动状态
    if (self.searchController.isActive) {
        return 1;
    }
    return [super numberOfSectionsInTableView:tableView];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (self.searchController.isActive) {
        return self.searchResultsArray.count;
    }
    return [super tableView:tableView numberOfRowsInSection:section];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.searchController.isActive) {
        //取出一个空闲cell,将里面的内容改变
        UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
        cell.textLabel.text = self.searchResultsArray[indexPath.row];
        return cell;
    }
    return [super tableView:tableView cellForRowAtIndexPath:indexPath];
}

4、左右联动

效果图

实现 tableView联动 主要分两种状况(需要创建两个tableView)

1、点击 左侧 cell 让右侧 tableView 滚到对应位置
//MARK: - 点击 cell 的代理方法
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    // 判断是否为 左侧 的 tableView
    if (tableView == self.leftTableView) {

        // 计算出 右侧 tableView 将要 滚动的 位置
        NSIndexPath *moveToIndexPath = [NSIndexPath indexPathForRow:0 inSection:indexPath.row];

        // 将 rightTableView 移动到对应的 位置
        [self.rightTableView scrollToRowAtIndexPath:moveToIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
    }
}
2、滑动 右侧 tableView 让左侧 tableView 滚到对应位置

[self.rightTableView indexPathsForVisibleRows] 返回 所有显示在界面的 cell 的 indexPath

//MARK: - 一个方法就能搞定 右边滑动时跟左边的联动
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    // 如果是 左侧的 tableView 直接return
    if (scrollView == self.leftTableView) return;

    // 取出显示在 视图 且最靠上 的 cell 的 indexPath
    NSIndexPath *topHeaderViewIndexpath = [[self.rightTableView indexPathsForVisibleRows] firstObject];

    // 左侧 talbelView 移动到的位置 indexPath
    NSIndexPath *moveToIndexpath = [NSIndexPath indexPathForRow:topHeaderViewIndexpath.section inSection:0];

    // 移动 左侧 tableView 到 指定 indexPath 居中显示
    [self.leftTableView selectRowAtIndexPath:moveToIndexpath animated:YES scrollPosition:UITableViewScrollPositionMiddle];
}
代码:
#import "ViewController.h"
#import "food.h"
#import "foodGroup.h"
#import "LeftTableViewCell.h"
#import "RightTableViewCell.h"

#import "UIImageView+WebCache.h"

#define leftTableWidth  [UIScreen mainScreen].bounds.size.width * 0.3
#define rightTableWidth [UIScreen mainScreen].bounds.size.width * 0.7
#define ScreenWidth     [UIScreen mainScreen].bounds.size.width
#define ScreenHeight    [UIScreen mainScreen].bounds.size.height

#define leftCellIdentifier  @"leftCellIdentifier"
#define rightCellIdentifier @"rightCellIdentifier"

@interface ViewController ()<UITableViewDataSource, UITableViewDelegate>
{
    //数据源
    NSMutableArray *_dataSource;
    
}
@property (nonatomic, weak) UITableView *leftTableView;

@property (nonatomic, weak) UITableView *rightTableView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _dataSource = [[NSMutableArray alloc]init];
    
    [self.view addSubview:_leftTableView];
    [self.view addSubview:_rightTableView];
    
    [self createleftTableView];
    [self createRightTableView];
    //获取数据源
    [self getDataSource];
    
}
- (void)createleftTableView{
    UITableView *tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 64, leftTableWidth, ScreenHeight-64)];
    [self.view addSubview:tableView];
    _leftTableView = tableView;
    tableView.delegate = self;
    tableView.dataSource = self;
    [tableView registerClass:[LeftTableViewCell class] forCellReuseIdentifier:leftCellIdentifier];
    
}
- (void)createRightTableView{
    UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(leftTableWidth, 64, rightTableWidth, ScreenHeight-64)];
    [self.view addSubview:tableView];
    _rightTableView = tableView;
    tableView.delegate = self;
    tableView.dataSource = self;
    _rightTableView.rowHeight = 80;
    [tableView registerNib:[UINib nibWithNibName:@"RightTableViewCell" bundle:nil] forCellReuseIdentifier:rightCellIdentifier];
}



//获取数据源
- (void)getDataSource{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"meituan" ofType:@"json"];
    NSData *data = [NSData dataWithContentsOfFile:path];
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
    NSArray *foodGroups = dict[@"data"][@"food_spu_tags"];
    
    for (NSDictionary *foodGroupsDic in foodGroups) {
        foodGroup *MyGroup = [[foodGroup alloc]init];
        MyGroup.name = foodGroupsDic[@"name"];
        NSArray * foodArray = foodGroupsDic[@"spus"];
       
        for (NSDictionary *foodDic in foodArray) {
            food *MyFood = [[food alloc]init];
            MyFood.name = foodDic[@"name"];
            MyFood.ImageURL = foodDic[@"picture"];
            MyFood.month_saled_content = foodDic[@"month_saled_content"];
            [MyGroup.foodArray addObject:MyFood];
        }
        
        [_dataSource addObject:MyGroup];
    
    }
    
    [_leftTableView reloadData];
    [_rightTableView reloadData];
}

#pragma mark - 数据源
//返回指定的组数,如果是多组,必须实现这个方法,返回指定组数,如果是一组,这个方法可以不实现,默认是一组
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    if (tableView == _leftTableView) {
        return 1;
    }
    return _dataSource.count;
}
//返回指定组的行数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (tableView == _leftTableView) {
        return _dataSource.count;
    }else{
        foodGroup *Group = _dataSource[section];
        return Group.foodArray.count;
    }
}

- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    if (tableView == _leftTableView) {
        //左边cell
        LeftTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:leftCellIdentifier forIndexPath:indexPath];
        foodGroup *Group = _dataSource[indexPath.row];
        cell.textLabel.text = Group.name;
        cell.textLabel.adjustsFontSizeToFitWidth = YES;
        
        return cell;
    }else{
        //右边cell
        RightTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:rightCellIdentifier forIndexPath:indexPath];
        foodGroup *Group = _dataSource[indexPath.section];
        food *MyFood = Group.foodArray[indexPath.row];
        
        cell.foodNameLabel.text = MyFood.name;
        cell.xiaoLiangLAbel.text = MyFood.month_saled_content;
        [cell.MYHeadImageView sd_setImageWithURL:[NSURL URLWithString:MyFood.ImageURL]];
        
        return cell;
    }
    
    
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
    
    if (tableView == _rightTableView) {
        foodGroup *Group = _dataSource[section];
        return Group.name;
    }else{
        return nil;
    }
}

#pragma mark - 滑动 右侧 tableView 让左侧 tableView 滚到对应位置
//MARK: - 一个方法就能搞定 右边滑动时跟左边的联动
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
    // 如果是 左侧的 tableView 直接return
    if (scrollView == self.leftTableView) return;
    
    // 取出显示在 视图 且最靠上 的 cell 的 indexPath
    NSIndexPath *topHeaderViewIndexpath = [[self.rightTableView indexPathsForVisibleRows] firstObject];
    
    // 左侧 talbelView 移动的 indexPath
    NSIndexPath *moveToIndexpath = [NSIndexPath indexPathForRow:topHeaderViewIndexpath.section inSection:0];
    
    // 移动 左侧 tableView 到 指定 indexPath 居中显示
    [self.leftTableView selectRowAtIndexPath:moveToIndexpath animated:YES scrollPosition:UITableViewScrollPositionMiddle];
    
}

//MARK: - 点击右侧 cell 的代理方法
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    // 选中 左侧 的 tableView
    if (tableView == self.leftTableView) {
        
        NSIndexPath *moveToIndexPath = [NSIndexPath indexPathForRow:0 inSection:indexPath.row];
        
        // 将右侧 tableView 移动到指定位置
        [self.rightTableView selectRowAtIndexPath:moveToIndexPath animated:YES scrollPosition:UITableViewScrollPositionTop];
        
        // 取消选中效果
        [self.rightTableView deselectRowAtIndexPath:moveToIndexPath animated:YES];
    }
}

@end

5、分组的展开收起

展开收起.gif

定义一个section对象,保存一个isExpand标识来判断展开与收起状态。点击的时候设置下isExpand,然后reload下就行了。

核心代码
- (NSInteget)cellForRowAtIndexPath:(NSIndexPath)indexPath{
    if(section.isExpand){
        return dataSource[indexPath.section].count;
    } else {
        return 0;
    }
}

完整代码
- (void)loadData {
    if (!self.dataArray) {
        self.dataArray = [NSMutableArray array];
    }
    if (!self.isExpland) {
       self.isExpland = [NSMutableArray array];
    }

    //这里用一个二维数组来模拟数据。
    self.dataArray = [NSArray arrayWithObjects:@[@"a",@"b",@"c",@"d"],@[@"d",@"e",@"f"],@[@"h",@"i",@"j",@"m",@"n"],nil].mutableCopy;

    //用0代表收起,非0(不一定是1)代表展开,默认都是收起的
    for (int i = 0; i < self.dataArray.count; i++) {
        [self.isExpland addObject:@0];
    }
    [self.tableView reloadData];
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    return self.dataArray.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    //这里是关键,如果选中
    NSArray *array = self.dataArray[section];
    if ([self.isExpland[section] boolValue]) {
        return array.count;
    }
    else {
        return 0;
    }

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:@"cell"];
    }
    cell.textLabel.text = self.dataArray[indexPath.section][indexPath.row];
    return cell;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

    UIButton *headerSection = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 44)];
    headerSection.tag = 666+section;

    //标题
    [headerSection setTitle:[NSString stringWithFormat:@"第%@组",@(section)] forState:UIControlStateNormal];

    [headerSection addTarget:self action:@selector(buttonAction:) forControlEvents:UIControlEventTouchUpInside];
    return headerSection;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 44;
}

- (void)buttonAction:(UIButton *)button {
    NSInteger section = button.tag - 666;
    self.isExpland[section] = [self.isExpland[section] isEqual:@0]?@1:@0;
    NSIndexSet *set = [NSIndexSet indexSetWithIndex:section];
    [self.tableView reloadSections:set withRowAnimation:UITableViewRowAnimationFade];
}

 

人已赞赏
iOS文章

ios开发UI篇--UIScrollView

2019-10-5 9:11:29

iOS文章

iOS NSRange 和 NSString 详解

2019-10-5 10:01:25

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索