iOS基于UITableView實現(xiàn)多層展開與收起
本文實例為大家分享了bleView多層展開與收起的具體代碼,供大家參考,具體內(nèi)容如下
規(guī)則要求:
- tableview 有多層,類似于xcode文件目錄的層級關(guān)系,每一個最開始展示的層姑且稱之為根目錄吧,并且,每個根目錄下的層數(shù)不定。
- 與文件目錄類似,每個目錄下可以有不同層級的目錄同時展開,但是同一層次中只有一層是展開的,即要展開B層次的某一層,則需要收起B(yǎng)層次所有其他的層級。
- 最底層是一個個文件,不能再展開(這里在業(yè)務(wù)邏輯上用處是:跳轉(zhuǎn)到不同的頁面)。
想法:
- 整個界面是一個tableview,層級關(guān)系用cell中的label的位置展現(xiàn),而tableview的數(shù)據(jù)源是一個一維數(shù)組_resultArray,其中,展開是在特定位置插入數(shù)據(jù),收起是在特定位置刪除數(shù)據(jù)。
- 每一層目錄存儲著下一層的引用,就是包含了下一層的全部數(shù)據(jù)。展開該層的時候就是將下一層的數(shù)據(jù)加入_resultArray,收起該層時,是將該層的所有下層的數(shù)據(jù)從_resultArray中刪除。
數(shù)據(jù)存儲
//每個目錄的數(shù)據(jù)結(jié)構(gòu)如下: @interface OpenTest : NSObject @property (copy, nonatomic) NSString *title; //非首層展示的標題 @property (assign, nonatomic) NSInteger level; //決定偏移量大小 @property (copy, nonatomic) NSString *openUrl; //最后一層跳轉(zhuǎn)的規(guī)則 @property (copy, nonatomic) NSMutableArray *detailArray; //下一層的數(shù)據(jù) @property (assign, nonatomic) BOOL isOpen; //是否要展開 @property (copy, nonatomic) NSString *imageName; //首層的圖片 @end
其中,因為detailArray中存儲的對象也應(yīng)該是OpenTest, 所以需要在OpenTest.m中借助MJExtension (在github上下載并加入到項目中)進行顯式轉(zhuǎn)化。
#import "OpenTest.h"
@implementation OpenTest
- (instancetype)init {
self = [super init];
if (self) {
[OpenTest mj_setupObjectClassInArray:^NSDictionary *{
return @{
@"detailArray" : [OpenTest class]
};
}];
}
return self;
}
@end
數(shù)據(jù)處理
開始建造源數(shù)據(jù)dataArray,該數(shù)組可明確層級關(guān)系,并且得到展示數(shù)組_resultArray。建造過程如下:
- (void)initData {
_dataArray = [NSMutableArray new];
_resultArray = [NSMutableArray new];
NSMutableArray *secondArray1 = [NSMutableArray new];
NSMutableArray *threeArray1 = [NSMutableArray new];
NSMutableArray *fourArray1 = [NSMutableArray new];
NSArray *FirstTitleArray = @[@"FirstTitle1", @"FirstTitle2", @"FirstTitle3", @"FirstTitle4", @"FirstTitle5", @"FirstTitle6", @"FirstTitle7", @"FirstTitle8", @"FirstTitle9", @"FirstTitle10"];
NSArray *SecondTitleArray = @[@"SecondTitle1", @"SecondTitle2", @"SecondTitle3"];
NSArray *ThreeTitleArray = @[@"ThreeTitle1", @"ThreeTitle2", @"ThreeTitle3", @"ThreeTitle4"];
NSArray *FourTitleArray = @[@"FourTitle1", @"FourTitle2", @"FourTitle3"];
NSArray *imageArray = @[@"scroller1", @"scroller2", @"scroller3", @"scroller4", @"scroller5", @"scroller6", @"scroller7", @"scroller8", @"scroller9", @"scroller10"];
//第四層數(shù)據(jù)
for (int i = 0; i < FourTitleArray.count; i++) {
OpenTest *model = [[OpenTest alloc] init];
model.title = FourTitleArray[i];
model.level = 3;
model.isOpen = NO;
[fourArray1 addObject:model];
}
//第三層數(shù)據(jù)
for (int i = 0; i < ThreeTitleArray.count; i++) {
OpenTest *model = [[OpenTest alloc] init];
model.title = ThreeTitleArray[i];
model.level = 2;
model.isOpen = NO;
model.detailArray = fourArray1;
[threeArray1 addObject:model];
}
//第二層數(shù)據(jù)
for (int i = 0; i < SecondTitleArray.count; i++) {
OpenTest *model = [[OpenTest alloc] init];
model.title = SecondTitleArray[i];
model.level = 1;
model.isOpen = NO;
model.detailArray = [threeArray1 mutableCopy];
[secondArray1 addObject:model];
}
//第一層數(shù)據(jù)
for (int i = 0; i < FirstTitleArray.count; i++) {
OpenTest *model = [[OpenTest alloc] init];
model.title = FirstTitleArray[i];
model.level = 0;
model.isOpen = NO;
model.detailArray = [secondArray1 mutableCopy];
model.imageName = imageArray[i];
[_dataArray addObject:model];
}
//處理源數(shù)據(jù),獲得展示數(shù)組_resultArray
[self dealWithDataArray:_dataArray];
}
/**
將源數(shù)據(jù)數(shù)組處理成要展示的一維數(shù)組,最開始是展示首層的所有的數(shù)據(jù)
@param dataArray 源數(shù)據(jù)數(shù)組
*/
- (void)dealWithDataArray:(NSMutableArray *)dataArray {
for (OpenTest *model in dataArray) {
[_resultArray addObject:model];
if (model.isOpen && model.detailArray.count > 0) {
[self dealWithDataArray:model.detailArray];
}
}
}
當首層沒有展開數(shù)據(jù)時,點擊首層展開第二層數(shù)據(jù),比較容易實現(xiàn),即在_resultArray添加下一層數(shù)據(jù)。添加數(shù)據(jù)方法如下:
/**
在指定位置插入要展示的數(shù)據(jù)
@param dataArray 數(shù)據(jù)數(shù)組
@param row 需要插入的數(shù)組下標
*/
- (void)addObjectWithDataArray:(NSMutableArray *)dataArray row:(NSInteger)row {
for (int i = 0; i < dataArray.count; i++) {
OpenTest *model = dataArray[i];
model.isOpen = NO;
[_resultArray insertObject:model atIndex:row];
row += 1;
}
}
收起方法實現(xiàn)如下:
/**
刪除要收起的數(shù)據(jù)
@param dataArray 數(shù)據(jù)
@param count 統(tǒng)計刪除數(shù)據(jù)的個數(shù)
@return 刪除數(shù)據(jù)的個數(shù)
*/
- (CGFloat)deleteObjectWithDataArray:(NSMutableArray *)dataArray count:(NSInteger)count {
for (OpenTest *model in dataArray) {
count += 1;
if (model.isOpen && model.detailArray.count > 0) {
count = [self deleteObjectWithDataArray:model.detailArray count:count];
}
model.isOpen = NO;
[_resultArray removeObject:model];
}
return count;
}
在已經(jīng)展開的時候點擊另外一個目錄,要先收起再展開。因為每個層次只有一個目錄是展開的,所以收起的時候,只需要跟同層次的目錄數(shù)據(jù)比較,如果是已經(jīng)打開的,則刪除打開目錄的所有子層。方法如下:
/**
與點擊同一層的數(shù)據(jù)比較,然后刪除要收起的數(shù)據(jù)和插入要展開的數(shù)據(jù)
@param model 點擊的cell對應(yīng)的model
@param row 點擊的在tableview的indexPath.row,也對應(yīng)_resultArray的下標
*/
- (void)compareSameLevelWithModel:(OpenTest *)model row:(NSInteger)row {
NSInteger count = 0;
NSInteger index = 0; //需要收起的起始位置
//如果直接用_resultArray,在for循環(huán)為完成之前,_resultArray會發(fā)生改變,使程序崩潰。
NSMutableArray *copyArray = [_resultArray mutableCopy];
for (int i = 0; i < copyArray.count; i++) {
OpenTest *openModel = copyArray[i];
if (openModel.level == model.level) {
//同一個層次的比較
if (openModel.isOpen) {
//刪除openModel所有的下一層
count = [self deleteObjectWithDataArray:openModel.detailArray count:count];
index = i;
openModel.isOpen = NO;
break;
}
}
}
//插入的位置在刪除的位置的后面,則需要減去刪除的數(shù)量。
if (row > index && row > count) {
row -= count;
}
[self addObjectWithDataArray:model.detailArray row:row + 1];
}
界面
系統(tǒng)的tableviewcell 無法修改textLabel的位置,需要修改偏移量有兩種方法。
1、繼承UITableViewCell, 然后重寫父類的方法 - layoutSubviews, 在該方法中修改textLabel的frame。
2、在cell.contentView 中添加一個label。
我這里使用的是第二種方法:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(15, 0, UI_SCREEN_WIDTH / 2, 32)];
label.font = [UIFont systemFontOfSize:14];
label.tag = LabelTag;
[cell.contentView addSubview:label];
}
for (UIView *view in cell.contentView.subviews) {
if (view.tag == LabelTag) {
((UILabel *)view).text = model.title;
((UILabel *)view).frame = CGRectMake(15 + (model.level - 1) * SpaceWidth , 0, UI_SCREEN_WIDTH / 2, 32);
}
}
最后在didSelectRowAtIndexPath方法中實現(xiàn)點擊展開與收起,方法如下:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger row = indexPath.row;
OpenTest *model = _resultArray[row];
if (model.isOpen) {
//原來是展開的,現(xiàn)在要收起,則刪除model.detailArray存儲的數(shù)據(jù)
[self deleteObjectWithDataArray:model.detailArray count:0];
}
else {
if (model.detailArray.count > 0) {
//原來是收起的,現(xiàn)在要展開,則需要將同層次展開的收起,然后再展開
[self compareSameLevelWithModel:model row:row];
}
else {
//點擊的是最后一層數(shù)據(jù),跳轉(zhuǎn)到別的界面
NSLog(@"最后一層");
}
}
model.isOpen = !model.isOpen;
//滑動到屏幕頂部
for (int i = 0; i < _resultArray.count; i++) {
OpenTest *openModel = _resultArray[i];
if (openModel.isOpen && openModel.level == 0) {
//將點擊的cell滑動到屏幕頂部
NSIndexPath *selectedPath = [NSIndexPath indexPathForRow:i inSection:0];
[tableView scrollToRowAtIndexPath:selectedPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
[tableView reloadData];
}
效果

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
iOS微信瀏覽器回退不刷新實例(監(jiān)聽瀏覽器回退事件)
下面小編就為大家?guī)硪黄猧OS微信瀏覽器回退不刷新實例(監(jiān)聽瀏覽器回退事件)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05
iOS應(yīng)用開發(fā)中UIView添加邊框顏色及設(shè)置圓角邊框的方法
這篇文章主要介紹了iOS應(yīng)用開發(fā)中UIView添加邊框顏色及設(shè)置圓角邊框的方法,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2016-02-02

