UITableView的多层点击展开

iOS 码拜 10年前 (2015-05-08) 1387次浏览 0个评论

请问分组的UITableView的多层点击展开怎么实现?最近要做个百度离线地图分省市的下载,类似于百度地图的那种,始终做不到一样的效果,谢谢!

这个效果还是比较好做的,关键在于你的数据源的结构上,假设你有这样一个 Model:

@interface Province : NSObject

@property … NSString *name;
@property … NSArray *cities;
@property … BOOL isOpen;

@end

name用来存显示名称,cities 用来存展开后的数组,isOpen 标识当前是否是打开的状态。
用户展开一个省的时候,你就更新对应 Model 的 isOpen 属性(也可以在 Controller 里记录当前展开的是哪个省,这种 UI 一般只允许展示一个),然后通过 – (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 方法添加或删除 rows,接下来你的 cellForRows 方法就又会调用了,City 和 Province 分别用不同的 Cell Identifier,差不多就是这样。
你先实现一下,有问题的话再把代码发上来。
你也可以参考下这个:http://code4app.com/ios/列表三级展开效果/540ec6be933bf094368b487f

你好,如果用这个方法的话就不能用titleForHeaderInSection方法了,请问有没有相关的Demo,谢谢!
//设置view,将替代titleForHeaderInSection方法
– (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

引用 1 楼 zhangao0086 的回复:

这个效果还是比较好做的,关键在于你的数据源的结构上,假设你有这样一个 Model:

@interface Province : NSObject

@property … NSString *name;
@property … NSArray *cities;
@property … BOOL isOpen;

@end

name用来存显示名称,cities 用来存展开后的数组,isOpen 标识当前是否是打开的状态。
用户展开一个省的时候,你就更新对应 Model 的 isOpen 属性(也可以在 Controller 里记录当前展开的是哪个省,这种 UI 一般只允许展示一个),然后通过 – (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 方法添加或删除 rows,接下来你的 cellForRows 方法就又会调用了,City 和 Province 分别用不同的 Cell Identifier,差不多就是这样。
你先实现一下,有问题的话再把代码发上来。
你也可以参考下这个:http://code4app.com/ios/列表三级展开效果/540ec6be933bf094368b487f

– (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 方法添加或删除 rows这个方法怎么用的呢?

40分
引用 3 楼 qq_24403369 的回复:
Quote: 引用 1 楼 zhangao0086 的回复:

这个效果还是比较好做的,关键在于你的数据源的结构上,假设你有这样一个 Model:

@interface Province : NSObject

@property … NSString *name;
@property … NSArray *cities;
@property … BOOL isOpen;

@end

name用来存显示名称,cities 用来存展开后的数组,isOpen 标识当前是否是打开的状态。
用户展开一个省的时候,你就更新对应 Model 的 isOpen 属性(也可以在 Controller 里记录当前展开的是哪个省,这种 UI 一般只允许展示一个),然后通过 – (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 方法添加或删除 rows,接下来你的 cellForRows 方法就又会调用了,City 和 Province 分别用不同的 Cell Identifier,差不多就是这样。
你先实现一下,有问题的话再把代码发上来。
你也可以参考下这个:http://code4app.com/ios/列表三级展开效果/540ec6be933bf094368b487f

– (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 方法添加或删除 rows这个方法怎么用的呢?

titleForHeaderInSection这个方法还是可以用的,我现写了一个 Demo 给你,你再尝试一下:

@interface Area : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSArray *cities;
@property (nonatomic, assign) BOOL isOpen;

@end

@implementation Area

@end

@interface MasterViewController ()

@property NSMutableArray *objects;

@end

@implementation MasterViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Province"];
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"City"];
    
    self.objects = [[NSMutableArray alloc] init];
    
    Area *province = [Area new];
    province.name = @"福建";
    
    Area *city1 = [Area new];
    city1.name = @"福州";
    Area *city2 = [Area new];
    city2.name = @"厦门";
    Area *city3 = [Area new];
    city3.name = @"泉州";
    Area *city4 = [Area new];
    city4.name = @"莆田";
    
    province.cities = @[city1, city2, city3, city4];
    
    [self.objects insertObject:province atIndex:0];
}

#pragma mark - Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.objects.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell;
    Area *area = self.objects[indexPath.row];
    
    if (area.cities.count > 0) {
        cell = [tableView dequeueReusableCellWithIdentifier:@"Province" forIndexPath:indexPath];
    } else {
        cell = [tableView dequeueReusableCellWithIdentifier:@"City" forIndexPath:indexPath];
    }

    area = self.objects[indexPath.row];
    cell.textLabel.text = [area name];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    Area *area = self.objects[indexPath.row];
    
    if ([cell.reuseIdentifier isEqualToString:@"Province"]) {
        NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:area.cities.count];
        for (NSInteger i = 1; i <= area.cities.count; i++) {
            [indexPaths addObject:[NSIndexPath indexPathForRow:indexPath.row + i inSection:indexPath.section]];
        }
        
        if (area.isOpen) {
            [self.objects removeObjectsInArray:area.cities];
            [tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
        } else {
            [self.objects addObjectsFromArray:area.cities];
            [tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
        }
        area.isOpen = !area.isOpen;
    } else if ([cell.reuseIdentifier isEqualToString:@"City"]) {
        [[[UIAlertView alloc] initWithTitle:@"提示" message:area.name delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show];
    }
}

@end

逻辑基本就是这样了。
insertRowsAtIndexPaths 和 数据源一定要对应的。

你好  谢谢你!你的Demo可以用  但是当你的数据源增加到3组或更多时就会有问题了UITableView的多层点击展开
这是因为- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell;函数的问题吧
引用 4 楼 zhangao0086 的回复:
Quote: 引用 3 楼 qq_24403369 的回复:
Quote: 引用 1 楼 zhangao0086 的回复:

这个效果还是比较好做的,关键在于你的数据源的结构上,假设你有这样一个 Model:

@interface Province : NSObject

@property … NSString *name;
@property … NSArray *cities;
@property … BOOL isOpen;

@end

name用来存显示名称,cities 用来存展开后的数组,isOpen 标识当前是否是打开的状态。
用户展开一个省的时候,你就更新对应 Model 的 isOpen 属性(也可以在 Controller 里记录当前展开的是哪个省,这种 UI 一般只允许展示一个),然后通过 – (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 方法添加或删除 rows,接下来你的 cellForRows 方法就又会调用了,City 和 Province 分别用不同的 Cell Identifier,差不多就是这样。
你先实现一下,有问题的话再把代码发上来。
你也可以参考下这个:http://code4app.com/ios/列表三级展开效果/540ec6be933bf094368b487f

– (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 方法添加或删除 rows这个方法怎么用的呢?

titleForHeaderInSection这个方法还是可以用的,我现写了一个 Demo 给你,你再尝试一下:

@interface Area : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSArray *cities;
@property (nonatomic, assign) BOOL isOpen;

@end

@implementation Area

@end

@interface MasterViewController ()

@property NSMutableArray *objects;

@end

@implementation MasterViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Province"];
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"City"];
    
    self.objects = [[NSMutableArray alloc] init];
    
    Area *province = [Area new];
    province.name = @"福建";
    
    Area *city1 = [Area new];
    city1.name = @"福州";
    Area *city2 = [Area new];
    city2.name = @"厦门";
    Area *city3 = [Area new];
    city3.name = @"泉州";
    Area *city4 = [Area new];
    city4.name = @"莆田";
    
    province.cities = @[city1, city2, city3, city4];
    
    [self.objects insertObject:province atIndex:0];
}

#pragma mark - Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.objects.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell;
    Area *area = self.objects[indexPath.row];
    
    if (area.cities.count > 0) {
        cell = [tableView dequeueReusableCellWithIdentifier:@"Province" forIndexPath:indexPath];
    } else {
        cell = [tableView dequeueReusableCellWithIdentifier:@"City" forIndexPath:indexPath];
    }

    area = self.objects[indexPath.row];
    cell.textLabel.text = [area name];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    Area *area = self.objects[indexPath.row];
    
    if ([cell.reuseIdentifier isEqualToString:@"Province"]) {
        NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:area.cities.count];
        for (NSInteger i = 1; i <= area.cities.count; i++) {
            [indexPaths addObject:[NSIndexPath indexPathForRow:indexPath.row + i inSection:indexPath.section]];
        }
        
        if (area.isOpen) {
            [self.objects removeObjectsInArray:area.cities];
            [tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
        } else {
            [self.objects addObjectsFromArray:area.cities];
            [tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
        }
        area.isOpen = !area.isOpen;
    } else if ([cell.reuseIdentifier isEqualToString:@"City"]) {
        [[[UIAlertView alloc] initWithTitle:@"提示" message:area.name delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show];
    }
}

@end

逻辑基本就是这样了。
insertRowsAtIndexPaths 和 数据源一定要对应的。

你好  谢谢你!你的Demo可以用  但是当你的数据源增加到3组或更多时就会有问题了UITableView的多层点击展开
这是因为- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell;函数的问题吧

引用 6 楼 qq_24403369 的回复:
Quote: 引用 4 楼 zhangao0086 的回复:
Quote: 引用 3 楼 qq_24403369 的回复:
Quote: 引用 1 楼 zhangao0086 的回复:

这个效果还是比较好做的,关键在于你的数据源的结构上,假设你有这样一个 Model:

@interface Province : NSObject

@property … NSString *name;
@property … NSArray *cities;
@property … BOOL isOpen;

@end

name用来存显示名称,cities 用来存展开后的数组,isOpen 标识当前是否是打开的状态。
用户展开一个省的时候,你就更新对应 Model 的 isOpen 属性(也可以在 Controller 里记录当前展开的是哪个省,这种 UI 一般只允许展示一个),然后通过 – (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 方法添加或删除 rows,接下来你的 cellForRows 方法就又会调用了,City 和 Province 分别用不同的 Cell Identifier,差不多就是这样。
你先实现一下,有问题的话再把代码发上来。
你也可以参考下这个:http://code4app.com/ios/列表三级展开效果/540ec6be933bf094368b487f

– (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 方法添加或删除 rows这个方法怎么用的呢?

titleForHeaderInSection这个方法还是可以用的,我现写了一个 Demo 给你,你再尝试一下:

@interface Area : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSArray *cities;
@property (nonatomic, assign) BOOL isOpen;

@end

@implementation Area

@end

@interface MasterViewController ()

@property NSMutableArray *objects;

@end

@implementation MasterViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.navigationItem.leftBarButtonItem = self.editButtonItem;

    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Province"];
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"City"];
    
    self.objects = [[NSMutableArray alloc] init];
    
    Area *province = [Area new];
    province.name = @"福建";
    
    Area *city1 = [Area new];
    city1.name = @"福州";
    Area *city2 = [Area new];
    city2.name = @"厦门";
    Area *city3 = [Area new];
    city3.name = @"泉州";
    Area *city4 = [Area new];
    city4.name = @"莆田";
    
    province.cities = @[city1, city2, city3, city4];
    
    [self.objects insertObject:province atIndex:0];
}

#pragma mark - Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.objects.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell;
    Area *area = self.objects[indexPath.row];
    
    if (area.cities.count > 0) {
        cell = [tableView dequeueReusableCellWithIdentifier:@"Province" forIndexPath:indexPath];
    } else {
        cell = [tableView dequeueReusableCellWithIdentifier:@"City" forIndexPath:indexPath];
    }

    area = self.objects[indexPath.row];
    cell.textLabel.text = [area name];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    Area *area = self.objects[indexPath.row];
    
    if ([cell.reuseIdentifier isEqualToString:@"Province"]) {
        NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:area.cities.count];
        for (NSInteger i = 1; i <= area.cities.count; i++) {
            [indexPaths addObject:[NSIndexPath indexPathForRow:indexPath.row + i inSection:indexPath.section]];
        }
        
        if (area.isOpen) {
            [self.objects removeObjectsInArray:area.cities];
            [tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
        } else {
            [self.objects addObjectsFromArray:area.cities];
            [tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
        }
        area.isOpen = !area.isOpen;
    } else if ([cell.reuseIdentifier isEqualToString:@"City"]) {
        [[[UIAlertView alloc] initWithTitle:@"提示" message:area.name delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil] show];
    }
}

@end

逻辑基本就是这样了。
insertRowsAtIndexPaths 和 数据源一定要对应的。

你好  谢谢你!你的Demo可以用  但是当你的数据源增加到3组或更多时就会有问题了UITableView的多层点击展开
这是因为- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell;函数的问题吧

3组应该也没问题,Area Model的这种结构是采用的组合模式,N 层都可以:

Area *province = [Area new];
province.name = @"福建";

Area *city1 = [Area new];
city1.name = @"福州";
Area *city2 = [Area new];
city2.name = @"厦门";
Area *city3 = [Area new];
city3.name = @"泉州";
Area *city4 = [Area new];
city4.name = @"莆田";

Area *city11 = [Area new];
city11.name = @"北京";

Area *city12 = [Area new];
city12.name = @"上海";

city1.cities = @[city11, city12];

province.cities = @[city1, city2, city3, city4];

[self.objects insertObject:province atIndex:0];

关键是 insert、delete 一定要和数据源同步。


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明UITableView的多层点击展开
喜欢 (0)
[1034331897@qq.com]
分享 (0)

文章评论已关闭!