一.界面搭建
1.項(xiàng)目需求
主界面能左右滾動(dòng),還能上下滾動(dòng),點(diǎn)擊按鈕跳轉(zhuǎn)界面
2.分析界面
點(diǎn)擊按鈕跳轉(zhuǎn)界面可以自定義UITabBarCotroller來(lái)實(shí)現(xiàn)
左右滾動(dòng),可以利用scrollView來(lái)實(shí)現(xiàn) 或 UICollectionView
上下滾動(dòng),用tableView可以實(shí)現(xiàn)
3.選擇實(shí)現(xiàn)方案
方案一: UITabBarCotroller + scrollView + tableView + titleView(TabBar條)
UIScrollView弊端:沒(méi)有做離屏渲染優(yōu)化
使用scrollView,沒(méi)有顯示的tableView也會(huì)渲染,渲染比較耗內(nèi)存,創(chuàng)建很多對(duì)象
離屏渲染:如果一個(gè)界面,沒(méi)有顯示到屏幕上,不會(huì)渲染,如果在屏幕上,就會(huì)渲染
方案二: UITabBarCotroller + UICollectionView + tableView + scrollView(TabBar條) 能擴(kuò)展標(biāo)題
UICollectionView好處:優(yōu)化離屏渲染,并不是少創(chuàng)建UITableView
tableView添加UICollectionView的cell里面
4.界面搭建實(shí)現(xiàn)步驟
4.1 先創(chuàng)建一個(gè)UIViewContriller
4.2 創(chuàng)建UICollectionView添加到UIViewContriller的View上面
創(chuàng)建UICollectionView,要先設(shè)置布局參數(shù),而且還要設(shè)置滾動(dòng)方向?yàn)樗綕L動(dòng),cell的大小就為屏幕的大小
4.3 添加一個(gè)scrollView(TabBar條)到控制器的View上面
scrollView的y值要從64開始,因?yàn)樯厦孢€有一個(gè)導(dǎo)航條
4.4 添加子控制器(添加給UIViewContriller)
按鈕的內(nèi)容由對(duì)應(yīng)的子控制器決定,創(chuàng)建子控制器的時(shí)候,要確定按鈕的內(nèi)容
4.5 添加標(biāo)題按鈕(添加到scrollView上面)
給按鈕綁定tag,點(diǎn)擊按鈕的時(shí)候,就移除之前按鈕對(duì)應(yīng)的控制器,添加當(dāng)前按鈕對(duì)應(yīng)的控制器,并記錄當(dāng)前選中的按鈕
注意: 如果A控制器的View添加到B控制器的View上,那么A控制器一定要成為B控制器的子控制器
5.展示cell
5.1 設(shè)置cell的尺寸等于屏幕的尺寸,設(shè)置水平滾動(dòng)運(yùn)行報(bào)錯(cuò),垂直滾動(dòng)不報(bào)錯(cuò),為什么?
報(bào)錯(cuò): bug:the item height must be less than the height of the UICollectionView minus the section insets
top and bottom values
分析: 原因:cell高度 必須 要小于等于 colllectionView高度 - (top + bottom)
iOS7之后,導(dǎo)航控制器會(huì)給它里面一個(gè)的UIScrollView頂部添加額外滾動(dòng)區(qū)域
解決方案:取消系統(tǒng)自動(dòng)添加的滾動(dòng)區(qū)域
5.2 左右滾動(dòng)的時(shí)候,cell之間有間距,怎么解決
取消cell的行間距和列間距
layout.minimumInteritemSpacing = 0;
layout.minimumLineSpacing = 0;
5.3 點(diǎn)擊按鈕切換界面的時(shí)候,發(fā)現(xiàn)cell為nill,為什么?
點(diǎn)擊按鈕切換界面不會(huì)去調(diào)用UICollectionView數(shù)據(jù)源方法,只有屏幕滾動(dòng)的時(shí)候才會(huì)調(diào)用數(shù)據(jù)源方法
解決方法:在點(diǎn)擊按鈕的方法里面,改變collectionView的偏移量,讓他偏移到指定的位置
5.4 切換界面,發(fā)現(xiàn)還是不調(diào)用數(shù)據(jù)源方法?
因?yàn)橹疤砑涌刂破鞯膙iew我們寫在 點(diǎn)擊按鈕的方法里面了
點(diǎn)擊按鈕雖然會(huì)滾動(dòng)cell,但會(huì)有延遲,不能及時(shí)去調(diào)用數(shù)據(jù)源方法,
怎么解決?
我們把添加控制器view的代碼寫在創(chuàng)建cell之后就可以了,這樣保證有cell我們?cè)偃ヌ砑涌刂破鞯膙iew
把添加控制器view的代碼寫在數(shù)據(jù)源方法里面
5.5 滾動(dòng)cell有時(shí)候會(huì)顯示兩個(gè)界面,我們想只顯示一個(gè)界面,怎么辦?
開啟UIScrollView的分頁(yè)功能就解決了
UICollectionView繼承UIScrollView,所以UIScrollView的所有屬性和方法都能用
5.6 滾動(dòng)到最后一頁(yè),發(fā)現(xiàn)還能往后滾,只不過(guò)會(huì)彈回來(lái),怎么解決?
關(guān)閉彈簧效果
5.7 發(fā)現(xiàn)tableView上面和下面被擋住了(導(dǎo)航條和TabBar條擋住了)
給tableView上邊和下邊添加一個(gè)內(nèi)邊距
5.8 默認(rèn)選中第0個(gè)按鈕
6.添加下劃線(標(biāo)題指示器)
6.1用什么控件來(lái)作為下劃線
用View就可以了
6.2下劃線添加到哪里?
下劃線是用來(lái)指示按鈕被選中的,和按鈕相關(guān),添加到按鈕里面可以嗎?
不可以,要求下劃線只有一個(gè),按鈕有多個(gè),不符合需求
可以添加到按鈕所在的父控件里面(標(biāo)題欄scrollView)
6.3在哪里添加?
下劃線和按鈕還有scrollView有關(guān)系,那么應(yīng)該在設(shè)置標(biāo)題欄和按鈕的時(shí)候添加
按鈕需要添加多個(gè),下劃線只需要添加一次,
默認(rèn)選中第一個(gè)按鈕方法只會(huì)調(diào)用一次,可以寫在這里面
6.4下劃線的frame怎么設(shè)置?
下劃線的centerX始終和被選中的按鈕的centerX相同
下劃線高度根據(jù)需要我們自己設(shè)定 高度確定,y值也就確定了 = 父控件高度 - 自己高度
下劃線寬度等于按鈕文字的高度
underLineV.xt_width = btn.titleLabel.xt_width;
6.5設(shè)置了下劃線,運(yùn)行不顯示,為什么?
一個(gè)控件沒(méi)有顯示一般有三個(gè)原因:
6.5.1沒(méi)有frame 6.5.2被擋住 6.5.3被隱藏
我們通過(guò)小面包查看,找不到下劃線控件,排除被擋住和被隱藏的可能
6.6打印下劃線frame,發(fā)現(xiàn)寬度為0,為什么?
我們是在viewDidLoad方法里面添加按鈕,雖然給按鈕設(shè)置了frame,但是按鈕里面的子控件還沒(méi)有尺寸,在viewDidLayoutSubviews方法里面才有尺寸
我們要手動(dòng)計(jì)算btn.titleLabel的寬度
6.7怎么計(jì)算btn.titleLabel的寬度
NSDictionary *nameAttr = @{NSFontAttributeName : [UIFont systemFontOfSize:15]};
[btn.titleLabel.text sizeWithAttributes:nameAttr];
過(guò)期的方法
[btn.titleLabel.text sizeWithFont:[UIFont systemFontOfSize:15]];
計(jì)算一段文字高度:constrainedToSize文字的寬度和最大高度
[btn.titleLabel.text sizeWithFont:[UIFont systemFontOfSize:15] constrainedToSize:CGSizeMake(355, MAXFLOAT)];
7.監(jiān)聽collectionView滾動(dòng)完成
7.1 怎么監(jiān)聽?
設(shè)置代理UICollectionView繼承UIScrollView,所以UIScrollView代理方法都能用
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
7.2 滾動(dòng)完成要讓對(duì)應(yīng)的標(biāo)題按鈕成為選中狀態(tài),怎么實(shí)現(xiàn)?
首先要拿到按鈕
根據(jù)控制器就能找到對(duì)應(yīng)的按鈕
7.3怎么拿到當(dāng)前的控制器?
偏移量 / 每個(gè)view的寬度 = 當(dāng)前所在的頁(yè)數(shù)(取整)
頁(yè)數(shù)也就對(duì)應(yīng)著是第幾個(gè)控制器
7.4按鈕怎么拿到?
首先想到從按鈕父控件scrollView的Subviews中去取,但我們有可能會(huì)取到下劃線控件,不可行
我們需要定義一個(gè)數(shù)組,來(lái)保存所有的按鈕,從數(shù)組中就能拿到想要的按鈕
注意:數(shù)組一定要懶加載(初始化),而且用的時(shí)候要用點(diǎn)語(yǔ)法(調(diào)用set方法)
7.5拿到按鈕,調(diào)用按鈕選中狀態(tài)方法,讓按鈕成為選中狀態(tài)
7.6滾動(dòng)完成,怎么讓下劃線(指示器)也跟著移動(dòng)?
滾動(dòng)完成,會(huì)調(diào)用按鈕選中狀態(tài)的方法,在這個(gè)方法設(shè)置下劃線的centerX始終等于當(dāng)前按鈕的centerX就可以了
8.封裝精華框架(類似與百思不得姐的主流界面)
8.1為什么要抽取一個(gè)框架?
這樣的主流界面很多app都需要,封裝起來(lái)方便復(fù)用
8.2怎么封裝?
封裝的前提:復(fù)用性 擴(kuò)展性
抽取一個(gè)基類,把框架特有的方法和實(shí)現(xiàn)封裝到基類(例如:設(shè)置標(biāo)題欄和標(biāo)題按鈕,collectionView等)
我們用的時(shí)候只要繼承自這個(gè)基類,添加我們所需要的控制器就可以了
8.3封裝的簡(jiǎn)單方法
如果實(shí)在不會(huì),就把子類代碼全部拷貝到基類
一些固定的,能確定死的就保留在基類里面
一些不確定的東西(例如:有多少個(gè)子控制器)放到子類實(shí)現(xiàn), 能讓使用者去擴(kuò)展
8.4繼承基類去實(shí)現(xiàn)主流界面的搭建,發(fā)現(xiàn)標(biāo)題欄不能顯示,為什么?
標(biāo)題欄的標(biāo)題按鈕是根據(jù)對(duì)應(yīng)子控制來(lái)設(shè)置的
而我們調(diào)用完父類(基類)的viewDidLoad之后才添加自控制器,也就是說(shuō)先設(shè)置按鈕,在添加自控制器
顯然,標(biāo)題欄按鈕是設(shè)置不成功的
注意:重寫父類的方法,一定要調(diào)用super方法
8.5怎么設(shè)置標(biāo)題欄,才能正常顯示?
我們只需要在添加完子控制器再設(shè)置標(biāo)題欄就可以了
只需要把設(shè)置標(biāo)題欄的按鈕寫在viewWillAppear里面就行了(模仿UITabBarController的實(shí)現(xiàn)原理)
8.6設(shè)置標(biāo)題欄出現(xiàn)一個(gè)bug,再次來(lái)到同一個(gè)界面,標(biāo)題欄又被重新設(shè)置一次,怎么解決?
只需要讓標(biāo)題欄只加載一次就可以了
8.7設(shè)置標(biāo)題欄,為什么不能使用一次代碼?
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
<#code to be executed once#>
});
一次代碼是在整個(gè)工程中只會(huì)被調(diào)用一次,如果在這個(gè)工程中有兩個(gè)界面復(fù)用這個(gè)框架,那么第二次復(fù)用這個(gè)框架的控制器設(shè)置不了標(biāo)題欄
這樣框架就存在漏洞
8.8怎么只讓代碼執(zhí)行一次,還能夠復(fù)用?
在基類中定義一個(gè)BOOL類型的屬性,記錄該方法有沒(méi)有被執(zhí)行
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (_isInitial == NO) {
// 添加標(biāo)題按鈕
[self setupAllTitleButton];
_isInitial = YES;
}
}