iOS開(kāi)發(fā)之級(jí)聯(lián)界面(推薦界面)搭建原理
先看看效果圖:
一.整體布局
1.項(xiàng)目需求
點(diǎn)擊左邊cell,右邊的cell數(shù)據(jù)更新
2.界面搭建
2.1交給兩個(gè)控制器管理比較麻煩,點(diǎn)擊一個(gè)控制器需要通知另外一個(gè)控制器
2. 2因此交給一個(gè)控制器管理比較好
2.3用xib搭建,左右各放一個(gè)tableView就可以了
3.開(kāi)發(fā)順序
先做左邊的tableView,再做右邊的,因?yàn)橛疫叺臄?shù)據(jù)是根據(jù)左邊變化的
二.左邊tableView界面搭建
1.自定義cell
左邊一個(gè)指示器歐一個(gè)view 中間位置用label
2.設(shè)置數(shù)據(jù)源
兩個(gè)tableView設(shè)置同一個(gè)控制器為數(shù)據(jù)源和代理,實(shí)現(xiàn)方法的時(shí)候要先判斷tableView的類(lèi)型
3.請(qǐng)求數(shù)據(jù),查看接口文檔
4.字典轉(zhuǎn)模型
5.顯示數(shù)據(jù)
6.運(yùn)行發(fā)現(xiàn)一個(gè)tableView頂部被擋住,另一個(gè)沒(méi)被擋住,為什么?
蘋(píng)果默認(rèn)只給界面上一個(gè)scrollView設(shè)置額外滾動(dòng)區(qū)域,只需要取消自動(dòng)設(shè)置的額外滾動(dòng)區(qū)域,自己手動(dòng)設(shè)置就可以了
7.選中cell,讓cell的指示器顯示
7.1 怎么實(shí)現(xiàn)?
監(jiān)聽(tīng)cell選中,選中就讓指示器顯示
7.2 但是還要監(jiān)聽(tīng)取消選中,把指示器隱藏
7.3 怎么同時(shí)監(jiān)聽(tīng)一個(gè)cell被選中,另一個(gè)cell取消選中?
cell自己有一個(gè)方法可以同時(shí)監(jiān)聽(tīng)
// 調(diào)用時(shí)刻:當(dāng)一個(gè)cell選中的時(shí)候就會(huì)調(diào)用,并且一個(gè)cell取消選中的時(shí)候也會(huì)調(diào)用 - (void)setSelected:(BOOL)selected animated:(BOOL)animated
7.4 cell不需要選中狀態(tài)
self.selectionStyle = UITableViewCellSelectionStyleNone;
8.點(diǎn)擊左邊cell的時(shí)候,請(qǐng)求右邊tableView的數(shù)據(jù)
監(jiān)聽(tīng)左邊cell的點(diǎn)擊,然后發(fā)送網(wǎng)絡(luò)請(qǐng)求,請(qǐng)求右邊的數(shù)據(jù)
三.右邊tableView界面搭建
1.xib復(fù)用
xib也能復(fù)用,當(dāng)兩個(gè)界面的xib一樣時(shí),可以用同一個(gè)xib,只要給xib傳遞不同的模型即可
2.右邊tableView業(yè)務(wù)邏輯
3.點(diǎn)擊左邊的cell,發(fā)送網(wǎng)絡(luò)請(qǐng)求,請(qǐng)求右邊的數(shù)據(jù)
4.請(qǐng)求數(shù)據(jù),查看接口文檔
4.1發(fā)現(xiàn)有一個(gè)參數(shù)category_id 需要根據(jù)左邊服務(wù)器返回的id 來(lái)加載右邊的數(shù)據(jù),所以,我們?cè)谧筮卼ableView的模型中要再加一個(gè)id屬性
4.2復(fù)用模型,模型也能復(fù)用,只需要在原來(lái)模型中添加需要數(shù)據(jù)的屬性即可
5.展示數(shù)據(jù),點(diǎn)擊左邊cell,右邊就顯示對(duì)應(yīng)的數(shù)據(jù)
四.整體數(shù)據(jù)優(yōu)化
1.默認(rèn)選中左邊的第0個(gè)cell
2.默認(rèn)選中第0個(gè)cell.寫(xiě)在哪里?
2.1寫(xiě)在viewDidLoad里面?
不可以,這個(gè)時(shí)候還沒(méi)有數(shù)據(jù)
2.2要寫(xiě)在數(shù)據(jù)加載成功,而且刷新表格之后
刷新代碼:
[self.categoryTableView reloadData]; // 默認(rèn)選中第0個(gè)cell NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; [self.categoryTableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
3.手動(dòng)選中左邊第0個(gè)cell,發(fā)現(xiàn)右邊數(shù)據(jù)沒(méi)刷新
3.1為什么?
因?yàn)?- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath方法必須用戶手動(dòng)點(diǎn)擊cell才會(huì)觸發(fā)
3.2怎么解決?
自己去調(diào)用這個(gè)方法,寫(xiě)在默認(rèn)選中第0個(gè)cell后邊就可以了
[self tableView:self.categoryTableView didSelectRowAtIndexPath:indexPath];
4.數(shù)據(jù)優(yōu)化
4.1每次點(diǎn)擊左邊cell,右邊cell都需要發(fā)送請(qǐng)求獲得數(shù)據(jù),消耗性能
4.2如果加載過(guò)一次,就保存起來(lái),下次就不用再加載了。
4.3保存到哪里?
保存到對(duì)應(yīng)的分類(lèi)模型的用戶數(shù)組里面,在分類(lèi)tableView的模型中定義一個(gè)用戶數(shù)組,保存左邊cell對(duì)應(yīng)的右邊的tableView的數(shù)據(jù)
4.4 怎么拿到左邊cell對(duì)應(yīng)的一組模型?
請(qǐng)求右邊數(shù)據(jù)的時(shí)候,左邊對(duì)應(yīng)的cell一定是被選中的,通過(guò)記錄選中cell對(duì)應(yīng)的模型,就能拿到這個(gè)模型
4.5 在選中左側(cè)cell的方法中,先判斷模型中user數(shù)組(右邊對(duì)應(yīng)的數(shù)據(jù)數(shù)組)是否有值
如果有值,直接刷新表格,然后return,就不在發(fā)送網(wǎng)絡(luò)請(qǐng)求
如果沒(méi)有,就發(fā)送網(wǎng)絡(luò)請(qǐng)求,請(qǐng)求成功后,保存數(shù)據(jù),刷新表格,展示數(shù)據(jù)
五.上下拉刷新
1.項(xiàng)目需求: 右邊的tableView需要上下拉刷新功能
2.怎么實(shí)現(xiàn)上下拉刷新?
使用 MJRefresh框架
3.下拉刷新,直接加載最新數(shù)據(jù),覆蓋掉原來(lái)的就可以了
我們?cè)揪褪侵苯佑媚P椭械臄?shù)組,覆蓋掉原來(lái)的數(shù)據(jù),所以就不用做移除原來(lái)數(shù)據(jù)的處理了
4.上拉刷新業(yè)務(wù)邏輯
4.1上拉刷新,需要加載更多的數(shù)據(jù),怎么加載更多的數(shù)據(jù)?
需要一個(gè)參數(shù)(page 或 ID),來(lái)獲得更多的數(shù)據(jù)
4.2這個(gè)參數(shù)(page)服務(wù)器會(huì)返回,我們需要記錄一下,記錄到哪里?
應(yīng)該記錄到左邊tableView的模型中,請(qǐng)求更多數(shù)據(jù)的時(shí)候,從模型中取出這個(gè)參數(shù)發(fā)送請(qǐng)求
4.3 下拉刷新的時(shí)候,要對(duì)page參數(shù)還原
把page重置為1,否則下拉刷新,會(huì)加載其它頁(yè)碼的數(shù)據(jù),到值數(shù)據(jù)錯(cuò)亂
4.4 加載更多數(shù)據(jù)成功的時(shí)候,我們就要對(duì)page +1,因?yàn)橛涗浀膒age 會(huì)作為下次請(qǐng)求參數(shù)傳遞
注意:只要請(qǐng)求數(shù)據(jù),請(qǐng)求成功的時(shí)候,就要對(duì)page + 1
4.5 上拉刷新,對(duì)數(shù)據(jù)的處理
上拉刷新,需要把原來(lái)的數(shù)據(jù)和新加載的數(shù)據(jù)一起顯示
4.6 怎么一起顯示?
用數(shù)組保存加載的更多數(shù)據(jù),把這個(gè)數(shù)組中的元素添加到原來(lái)數(shù)據(jù)數(shù)組中
4.7,怎么把一個(gè)數(shù)組中的元素,添加到另一個(gè)數(shù)組中?
通過(guò)- (void)addObject:(ObjectType)anObject;方法?
不可以,這個(gè)方法會(huì)把整個(gè)數(shù)組作為一個(gè)元素,添加到另一個(gè)數(shù)組中[_selectCategoryItem.users addObject:users];
4.8.那用哪個(gè)方法?
- (void)addObjectsFromArray:(NSArray<ObjectType> *)otherArray;
這個(gè)方法會(huì)把數(shù)組中的每一個(gè)元素取出來(lái),添加到另一個(gè)數(shù)組中
5.上拉刷新細(xì)節(jié)處理
5.1 當(dāng)沒(méi)有更多數(shù)據(jù)的時(shí)候,需要隱藏上拉刷新控件
5.2 怎么隱藏?
拿到控件設(shè)置hidden屬性 self.userTableView.mj_footer.hidden
5.3隱藏的條件是什么?
需要判斷當(dāng)前用戶組,有沒(méi)有更多用戶
5.4 怎么判斷?
服務(wù)器返回的數(shù)據(jù)有一個(gè) total_page屬性,如果當(dāng)前頁(yè)>= total_page就沒(méi)有更多數(shù)據(jù)
5.5需要保存 total_page屬性,保存到哪里?
保存到左邊tableView的模型中,每次請(qǐng)求成功,就把 total_page屬性保存到對(duì)應(yīng)的用戶組中
5.6 在刷新表格的時(shí)候,當(dāng)前的page屬性是 當(dāng)前頁(yè)數(shù)+ 1 的值
所以設(shè)置上拉刷新隱藏的條件應(yīng)該是 : page > total_page
5.7 隱藏代碼寫(xiě)在哪里?
寫(xiě)在刷新表格之后,MJ刷新框架每次刷新完數(shù)據(jù),會(huì)自動(dòng)判斷是否隱藏,一定要在刷新方法后設(shè)置才有用
5.8 每次點(diǎn)擊左邊cell的時(shí)候,也要判斷是否隱藏上拉刷新控件,為什么?
有可能數(shù)據(jù)只有一頁(yè),不判斷的話,就會(huì)顯示上拉刷新控件,去刷新的時(shí)候,拿不到更多數(shù)據(jù)
源代碼
- (void)viewDidLoad { [super viewDidLoad]; self.title = @"推薦關(guān)注"; self.automaticallyAdjustsScrollViewInsets = NO; _categoryTableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0); _userTableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0); // 分類(lèi)tableView注冊(cè)cell [_categoryTableView registerNib:[UINib nibWithNibName:@"XMGCategoryCell" bundle:nil] forCellReuseIdentifier:categoryID]; // 用戶tableView注冊(cè)cell [_userTableView registerNib:[UINib nibWithNibName:@"XMGSubTagCell" bundle:nil] forCellReuseIdentifier:userID]; // 請(qǐng)求分類(lèi)數(shù)據(jù) [self loadCategoryData]; // 添加上下拉刷新 [self setupRefreshView]; } - (void)setupRefreshView { // 下拉刷新 // 當(dāng)松手,并且下拉刷新完全顯示的時(shí)候,就會(huì)觸發(fā)下拉刷新 MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewUserData)]; header.automaticallyChangeAlpha = YES; self.userTableView.mj_header = header; // 上拉刷新 MJRefreshAutoNormalFooter *footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreUserData)]; footer.automaticallyHidden = YES; self.userTableView.mj_footer = footer; } - (void)loadCategoryData { AFHTTPSessionManager *mgr = [AFHTTPSessionManager xmg_manager]; NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; parameters[@"a"] = @"category"; parameters[@"c"] = @"subscribe"; [mgr GET:XMGBaseUrl parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary * _Nullable responseObject) { NSArray *dictArr = responseObject[@"list"]; _categorys = [XMGCategoryItem mj_objectArrayWithKeyValuesArray:dictArr]; [self.categoryTableView reloadData]; // 默認(rèn)選中第0個(gè)cell NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; [self.categoryTableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone]; [self tableView:self.categoryTableView didSelectRowAtIndexPath:indexPath]; } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { }]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (tableView == _categoryTableView) { // 顯示分類(lèi)TableView return _categorys.count; } return _selectCategoryItem.users.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (tableView == _categoryTableView) { // 顯示分類(lèi)TableView XMGCategoryCell *cell = [tableView dequeueReusableCellWithIdentifier:categoryID]; cell.item = _categorys[indexPath.row]; return cell; } XMGSubTagCell *cell = [tableView dequeueReusableCellWithIdentifier:userID]; cell.user = _selectCategoryItem.users[indexPath.row]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (tableView == _categoryTableView) { return 44; } return 60 + 1; } // 點(diǎn)擊cell就會(huì)調(diào)用 // 必須用戶手動(dòng)點(diǎn)擊cell才會(huì)觸發(fā) - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (tableView == _categoryTableView) { // 記錄選中分類(lèi)模型 _selectCategoryItem = _categorys[indexPath.row]; // 點(diǎn)擊分類(lèi)cell // 判斷之前有沒(méi)有數(shù)據(jù) if (_selectCategoryItem.users.count) { [self.userTableView reloadData]; self.userTableView.mj_footer.hidden = _selectCategoryItem.page > _selectCategoryItem.total_page; return; } // 請(qǐng)求右邊用戶數(shù)據(jù) [self loadNewUserData]; } } // 沒(méi)有更多數(shù)據(jù)的時(shí)候,隱藏上拉刷新控件 // 判斷當(dāng)前分類(lèi)用戶組 有沒(méi)有更多用戶組 // 加載更多用戶數(shù)據(jù) - (void)loadMoreUserData { [self.mgr.tasks makeObjectsPerformSelector:@selector(cancel)]; NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; parameters[@"a"] = @"list"; parameters[@"c"] = @"subscribe"; parameters[@"category_id"] = _selectCategoryItem.id; parameters[@"page"] = @(_selectCategoryItem.page); [self.mgr GET:XMGBaseUrl parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary * _Nullable responseObject) { [self.userTableView.mj_footer endRefreshing]; _selectCategoryItem.page++; NSArray *dictArr = responseObject[@"list"]; NSArray *users = [XMGUserItem mj_objectArrayWithKeyValuesArray:dictArr]; // 取出數(shù)組中所有元素,添加到新數(shù)組 // [_selectCategoryItem.users addObject:users]; [_selectCategoryItem.users addObjectsFromArray:users]; [self.userTableView reloadData]; // 控制上拉控件是否顯示,一定要在reloadData之后 self.userTableView.mj_footer.hidden = _selectCategoryItem.page > _selectCategoryItem.total_page; } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { }]; } // 加載更新用戶數(shù)據(jù) - (void)loadNewUserData { _selectCategoryItem.page = 1; [self.mgr.tasks makeObjectsPerformSelector:@selector(cancel)]; NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; parameters[@"a"] = @"list"; parameters[@"c"] = @"subscribe"; parameters[@"category_id"] = _selectCategoryItem.id; [self.mgr GET:XMGBaseUrl parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary * _Nullable responseObject) { _selectCategoryItem.page++; // 記錄當(dāng)前分類(lèi)總頁(yè)碼數(shù) _selectCategoryItem.total_page = [responseObject[@"total_page"] integerValue]; // 結(jié)束刷新 [self.userTableView.mj_header endRefreshing]; NSArray *dictArr = responseObject[@"list"]; _selectCategoryItem.users = [XMGUserItem mj_objectArrayWithKeyValuesArray:dictArr]; [self.userTableView reloadData]; self.userTableView.mj_footer.hidden = _selectCategoryItem.page > _selectCategoryItem.total_page; } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { }]; }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 總結(jié)IOS界面間跳轉(zhuǎn)的幾種方法
- iOS圖片界面翻頁(yè)切換效果
- iOS仿微信圖片分享界面實(shí)現(xiàn)代碼
- IOS 聊天界面(自適應(yīng)文字)的實(shí)現(xiàn)
- iOS中使用UItableviewcell實(shí)現(xiàn)團(tuán)購(gòu)和微博界面的示例
- iOS實(shí)現(xiàn)電商購(gòu)物車(chē)界面示例
- IOS實(shí)現(xiàn)微信朋友圈相冊(cè)評(píng)論界面的翻轉(zhuǎn)過(guò)渡動(dòng)畫(huà)
- Unity iOS混合開(kāi)發(fā)界面切換思路解析
- iOS高仿微信相冊(cè)界面翻轉(zhuǎn)過(guò)渡動(dòng)畫(huà)效果
- iOS制作帶彈跳動(dòng)畫(huà)發(fā)布界面
相關(guān)文章
iOS開(kāi)發(fā)中使用屏幕旋轉(zhuǎn)功能的相關(guān)方法
這篇文章主要介紹了iOS開(kāi)發(fā)中使用屏幕旋轉(zhuǎn)功能的相關(guān)方法,包括Transform變化矩陣原理的講解,需要的朋友可以參考下2015-09-09iOS的UI開(kāi)發(fā)中Modal的使用與主流應(yīng)用UI結(jié)構(gòu)介紹
這篇文章主要介紹了iOS的UI開(kāi)發(fā)中Modal的使用與主流應(yīng)用UI結(jié)構(gòu),代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-12-12iOS開(kāi)發(fā)之TextField禁用粘貼、選擇和全選功能
這篇文章主要為大家詳細(xì)介紹了iOS開(kāi)發(fā)之TextField禁用粘貼、選擇和全選功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09詳解iOS 驗(yàn)證碼輸入的實(shí)現(xiàn)思路
這篇文章主要介紹了iOS 驗(yàn)證碼輸入一種實(shí)現(xiàn)思路,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-10-10iOS 將系統(tǒng)自帶的button改裝成上圖片下文字的樣子
這篇文章主要介紹了 iOS 將系統(tǒng)自帶的button改裝成上圖片下文字的樣子,代碼是通過(guò)繼承UIButton,然后再重寫(xiě)layoutSubviews方法,對(duì)自帶的圖片和titleLabel進(jìn)行重新的layout。下面通過(guò)本文給大家分享下實(shí)現(xiàn)代碼2016-12-12iOS中幾種定時(shí)器的實(shí)現(xiàn)小結(jié)
這篇文章主要介紹了iOS中幾種定時(shí)器的實(shí)現(xiàn)小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01