前言
? ? 在MyLayout的6大布局中岖免,每種布局都有不同的應(yīng)用場(chǎng)景吧彪。且每種布局的子視圖的約束機(jī)制不一樣:線性布局MyLinearLayout、表格布局MyTableLayout、流式布局MyFlowLayout帆焕、浮動(dòng)布局MyFloatLayout這四種布局的子視圖之間的約束是通過(guò)添加到父布局的先后順序來(lái)決定的;框架布局MyFrameLayout中的子視圖則只跟父布局視圖有關(guān)找田,而跟添加的先后順序無(wú)關(guān)歌憨;相對(duì)布局中MyRelativeLayout的子視圖則是通過(guò)設(shè)置視圖之間的依賴關(guān)系來(lái)建立約束的,而跟添加的先后順序無(wú)關(guān)墩衙。即便如此务嫡,同一種界面功能在一些情況下都可以用任何一種布局來(lái)實(shí)現(xiàn)。在這些布局中相對(duì)布局因?yàn)槭峭ㄟ^(guò)設(shè)定視圖之間的依賴來(lái)建立一種布局約束漆改,因此我們可以用他來(lái)構(gòu)造一些復(fù)雜且無(wú)規(guī)律的界面布局心铃,但其缺點(diǎn)則是太過(guò)于依賴約束,導(dǎo)致當(dāng)界面調(diào)整時(shí)需要重新設(shè)定視圖之間的依賴關(guān)系(iOS的AutoLayout其實(shí)就是一種相對(duì)布局的實(shí)現(xiàn)挫剑,布局時(shí)需要設(shè)置太多的約束去扣,這也是我一直詬病AutoLayout的原因)。那么是否可以有一種方法不設(shè)置視圖之間的依賴而來(lái)實(shí)現(xiàn)一些復(fù)雜的界面布局呢樊破? 這也就是我們推出浮動(dòng)布局MyFloatLayout的原因愉棱。
浮動(dòng)
? ? ?我們的UI界面中總是有一種場(chǎng)景是:某個(gè)容器視圖后續(xù)添加的子視圖的左邊總是緊跟著前面添加的子視圖的右邊,而上邊則跟前面視圖的上邊保持一致進(jìn)行驼芷荩靠顯示奔滑,而當(dāng)容器視圖剩余的寬度空間不夠容納新加入的子視圖時(shí)則新加入的子視圖自動(dòng)的往下移動(dòng)且在不覆蓋已經(jīng)排列好的視圖的前提下尋找出一個(gè)可以容納其寬度的最合適的位置進(jìn)行停靠顺少。我們稱這種機(jī)制為浮動(dòng)朋其。比如下面的場(chǎng)景:
? ? ? 我們的容器視圖的尺寸為500x300,當(dāng)添加視圖A時(shí)脆炎,因?yàn)橐晥DA的寬度是80梅猿,寬度能夠被容器視圖容納,所以我們將視圖A浮動(dòng)到容器視圖的左上角位置腕窥。而當(dāng)添加視圖B時(shí)溜嗜,因?yàn)橐晥DB的寬度是100,仍然能夠被容器視圖的寬度容納(容器視圖剩余寬度為420)却邓,所以將視圖B浮動(dòng)到視圖A的右邊并且上邊對(duì)齊视译。我們也可以按同樣的方式來(lái)處理視圖C的浮動(dòng)。這樣容器視圖剩余的寬度變?yōu)榱?70(500-80-100-150)入蛆。假如這時(shí)候我們想再放入一個(gè)尺寸為200x100的視圖D的時(shí)候响蓉,因?yàn)檫@時(shí)要添加的視圖D的寬度為200,而容器視圖的剩余寬度只有170了哨毁,這時(shí)候視圖D將不能浮動(dòng)到視圖C的右邊了枫甲,我們必須要找一個(gè)合適的位置來(lái)放置視圖D。根據(jù)浮動(dòng)的原則我們算出最合適的浮動(dòng)位置是A視圖的右邊,且為了保持前面視圖不被覆蓋想幻,因此視圖D放置的最合適的坐標(biāo)位置是:(80,130)粱栖。下面就是視圖D放入后的結(jié)果:
? ? ?根據(jù)浮動(dòng)的規(guī)則假如視圖D的寬度不是200而是400的話,那么視圖D將不能浮動(dòng)到視圖A的右邊(視圖A的右邊的剩余的寬度為320脏毯,無(wú)法容納400的寬度)闹究,那么根據(jù)浮動(dòng)的規(guī)則,視圖D將再次往下移動(dòng)食店,并浮動(dòng)到容器視圖的最左邊的(0,180)的位置上渣淤。我們繼續(xù)來(lái)加入一個(gè)新的視圖E,視圖E的尺寸為100x50吉嫩。那么視圖E應(yīng)該是浮動(dòng)到視圖C的右邊還是視圖D的右邊呢价认? 答案是D的右邊,雖然C右邊的空間也可以容納100的寬度自娩,但是卻不符合浮動(dòng)的規(guī)則用踩。我們上面說(shuō)的浮動(dòng)的規(guī)則是在可以容納新加入視圖寬度的情況下新加入的視圖的上邊和前一個(gè)加入的視圖的上邊對(duì)齊,而且新加入的視圖的左邊必須放置在前一個(gè)加入視圖的右邊椒功。因此視圖E加入到容器視圖后的結(jié)果如下:
最后捶箱,我們?cè)賮?lái)考察新視圖F的加入。假如視圖F的尺寸為300x50动漾。那么根據(jù)浮動(dòng)的規(guī)則視圖將無(wú)法浮動(dòng)到E的右邊丁屎,同時(shí)也無(wú)法浮動(dòng)到D的右邊了,這時(shí)候只能繼續(xù)往下移動(dòng)旱眯,而最終的左邊是浮動(dòng)到容器視圖的最左邊晨川,而上邊的位置則是視圖D的下方。最終的布局結(jié)果如下:
? 通過(guò)上面的4張圖片中删豺,我們就可以總結(jié)出浮動(dòng)的規(guī)則共虑,而且上面的浮動(dòng)規(guī)則只是我們的浮動(dòng)布局MyFloatLayout中的一種浮動(dòng)方向:優(yōu)先向左浮動(dòng),再向下浮動(dòng)呀页。我們可以通過(guò)浮動(dòng)布局這種浮動(dòng)機(jī)制妈拌,不再設(shè)置明確設(shè)定每個(gè)子視圖之間的依賴關(guān)系,而只是根據(jù)視圖加入到布局視圖的順序就能夠?qū)崿F(xiàn)一種不規(guī)則的視圖排列的布局場(chǎng)景蓬蝶。下面是我們總結(jié)的優(yōu)先向左浮動(dòng)尘分,再向下浮動(dòng)的浮動(dòng)規(guī)則:
1.加入布局視圖的第一個(gè)子視圖總是浮動(dòng)到布局視圖的左上角。
2.如果新加入的子視圖的寬度能夠被放入到前一個(gè)加入的視圖右邊到布局視圖右邊的剩余寬度空間中的話丸氛,則新加入的視圖的左邊位置是等于前一個(gè)加入視圖的右邊位置培愁,且新加入視圖的上邊位置和前一個(gè)加入的視圖的上邊位置保持一致。
3.如果新加入的子視圖的寬度不能被放入到前一個(gè)加入的視圖的右邊到布局視圖右邊的剩余寬度空間中時(shí)缓窜,則新加入的視圖將繼續(xù)往左往下尋找到一個(gè)能容納其寬度的最小空間定续,并且不能遮擋掉前面加入的所有子視圖的最佳的位置進(jìn)行放置谍咆。
4.如果某個(gè)子視圖的寬度大于等于布局視圖的寬度,則總是浮動(dòng)到布局視圖的最左邊私股,且上邊的位置是前面所有子視圖的最下邊的位置進(jìn)行放置摹察。
5.總是確保任意的子視圖之間是不能被重疊覆蓋。
? ? ? 上面的5條規(guī)則就是浮動(dòng)布局中的一種浮動(dòng)規(guī)則的定義倡鲸,而浮動(dòng)布局的規(guī)則定義其實(shí)是從HTML中的CSS中的一種定位:浮動(dòng)定位的規(guī)則而來(lái)的港粱。CSS中的浮動(dòng)定位最初的設(shè)計(jì)初衷是為實(shí)現(xiàn)圖文混排的效果而設(shè)置的一種浮動(dòng)定位技術(shù), 在CSS中我們可以為某個(gè)元素指定float這個(gè)屬性旦签,而這個(gè)屬性的值可以設(shè)定為left或者right或者none,分別表示元素是向左浮動(dòng)還是向右浮動(dòng)還是不浮動(dòng)寸宏。同時(shí)我們還可以為元素指定clear這個(gè)屬性來(lái)清除浮動(dòng)宁炫,clear這個(gè)屬性可以設(shè)置left, right,both,none這四個(gè)值氮凝。若果您想了解CSS中關(guān)于浮動(dòng)定位的信息可以訪問(wèn):CSS浮動(dòng)定位和CSS浮動(dòng)知識(shí)分享這兩篇文章進(jìn)行詳細(xì)了解羔巢。
? ? ?前面說(shuō)到,在CSS中我們可以通過(guò)float屬性來(lái)指定浮動(dòng)的方向罩阵,以及clear屬性來(lái)指定清除浮動(dòng)的操作竿秆。而我們的浮動(dòng)布局MyFloatLayout也是支持浮動(dòng)方向,以及清除浮動(dòng)這些特性的設(shè)定的稿壁。向右浮動(dòng)我們?cè)诤罄m(xù)會(huì)介紹幽钢,這里先介紹清除浮動(dòng)的作用和意義。
清除浮動(dòng)
? ? 上面的幾個(gè)場(chǎng)景中我們發(fā)現(xiàn)傅是,不管新加入視圖的寬度如何匪燕,只要容器視圖中剩余寬度能夠容納新加入的子視圖,則子視圖總是會(huì)浮動(dòng)到前面一個(gè)視圖的右邊喧笔。但在實(shí)際的應(yīng)用場(chǎng)景中帽驯,我們又希望某個(gè)視圖不遵守這種默認(rèn)的浮動(dòng)規(guī)則,而是讓新加入的子視圖的左邊總是和容器視圖的左邊對(duì)齊书闸,且子視圖的上邊則是放入到前面加入的占用最高空間的視圖的下方尼变。比如下面的情況:
? ? ? ?我們的子視圖C,雖然寬度為150浆劲,并且能浮動(dòng)到視圖B的右邊嫌术,但是實(shí)際中我們則想讓視圖C浮動(dòng)到A的下邊并且左邊和容器視圖對(duì)齊,這時(shí)候我們就需要用到清除浮動(dòng)的概念了梳侨。? 我們可以為視圖C設(shè)置一個(gè)清除浮動(dòng)的屬性蛉威,這樣視圖C就達(dá)到了我們想要的效果。因此我們可以看出走哺,所謂清除浮動(dòng)就是使得視圖的默認(rèn)的浮動(dòng)規(guī)則失效蚯嫌,而總是讓視圖的左邊和容器視圖的左邊對(duì)齊哲虾,而讓視圖的上邊則設(shè)置為前面加入的所有同一個(gè)方向浮動(dòng)的視圖的最高高度的下方。因此我們可以得出浮動(dòng)布局的第6條規(guī)則:
6.如果子視圖設(shè)定了清除浮動(dòng)屬性择示,則視圖在布局時(shí)的左邊界總是和容器視圖的左邊界相等束凑,而上邊界則是在所有前面加入的同一個(gè)方向浮動(dòng)視圖的最高的高度的下方顯示。
通過(guò)視圖的清除浮動(dòng)屬性栅盲,我們可以設(shè)置讓某些視圖不進(jìn)行浮動(dòng)汪诉,而是達(dá)到另起一行的效果。
比重
? ? ?我們?cè)賮?lái)考察一種場(chǎng)景谈秫,就上面的例子來(lái)說(shuō)扒寄,我們先加入了視圖A和B,現(xiàn)在我們想加入一個(gè)視圖C拟烫,并且想讓視圖C浮動(dòng)在視圖B的右邊该编。而且寬度則是已經(jīng)填充的A和B剩余的寬度320(500-80-100)。一個(gè)辦法就是我們手動(dòng)的設(shè)定視圖C的寬度為320,這樣就能達(dá)到想要的效果硕淑,但是在實(shí)際的應(yīng)用中课竣,A和B的寬度可能是不確定的,并且容器視圖的寬度也是不確定的置媳,而不管何種情況我們又總想讓視圖C的寬度總是占用剩余的寬度于樟,就像如下的效果圖一樣:
? ? ? 在上面的場(chǎng)景中,我們希望不需要明確的設(shè)置視圖C的寬度拇囊,而是通過(guò)一種比重的特性來(lái)設(shè)置視圖C總是占用容器視圖的剩余寬度的某個(gè)比例值迂曲。這里的比重的設(shè)置,是在整體布局視圖的浮動(dòng)的方向的設(shè)定上的寥袭,就是說(shuō)當(dāng)整體的布局視圖里面的視圖是支持左邊和右邊浮動(dòng)時(shí)則這個(gè)比重指定的是視圖的寬度的相對(duì)比例值奢米,而當(dāng)布局視圖支持的是上邊和下邊浮動(dòng)時(shí)則這個(gè)比重指的是視圖的高度的相對(duì)比例值。而且我們約定比重值的設(shè)定必須大于0且小于等于1纠永。通過(guò)比重值的設(shè)定鬓长,我們可以不需要對(duì)某個(gè)新加入的視圖設(shè)定具體的寬度或者高度,而只需要指定一個(gè)相對(duì)的值尝江,而由浮動(dòng)布局來(lái)根據(jù)當(dāng)前的浮動(dòng)情況來(lái)自動(dòng)計(jì)算出應(yīng)該有的寬度或者寬度涉波。其中的具體的計(jì)算公式為:
某個(gè)設(shè)置了比重值的視圖的寬度或者高度 = (布局視圖的寬度或者高度 - 前一個(gè)視圖的右邊或者下邊的邊界值)* 視圖的比重值。
? ? ? 就以上面的左右浮動(dòng)的例子來(lái)說(shuō)炭序,假設(shè)我們?cè)O(shè)定視圖C的比重為1啤覆。根據(jù)公式的定義,布局視圖的寬度是500惭聂,而前面一個(gè)視圖B的右邊邊界值是180(100+80)窗声。因此最終視圖C的寬度就是:320 = (500 - 180)*1, 而假如設(shè)定視圖C的比重是0.5的話辜纲,則最終視圖C的寬度就是160了笨觅。又如果我們?cè)僭黾右粋€(gè)視圖D的比重設(shè)置為1的話拦耐。因?yàn)榍懊娴囊晥DC的寬度已經(jīng)算出是160,他的右邊距值是340(180+160)见剩, 因此最終視圖D的寬度就是160 (500 - 340)*1了杀糯。其中的效果圖如下:
? ? ? ?浮動(dòng)布局中的子視圖可以通過(guò)設(shè)定比重來(lái)得到剩余的寬度或者高度,因此浮動(dòng)布局中針對(duì)比重屬性定義新的規(guī)則如下:
7.當(dāng)某個(gè)子視圖設(shè)定了比重屬性時(shí)苍苞,這個(gè)視圖的寬度或者高度將根據(jù)布局視圖的浮動(dòng)方向設(shè)定固翰,以及比重的值的設(shè)定自動(dòng)進(jìn)行計(jì)算,比重的設(shè)置必須大于0小于等于1羹呵,而通過(guò)比重計(jì)算出來(lái)的寬度或高度的公式為:布局視圖的寬度或者高度 - 前一個(gè)視圖的右邊或則下邊的邊界值)* 視圖的比重值
左右浮動(dòng)
? ? ? ?上面我們的介紹的浮動(dòng)的例子中骂际,都總是默認(rèn)是向左浮動(dòng),然后再?gòu)纳系较碌倪M(jìn)行布局冈欢。但前面也有說(shuō)到CSS中的元素的浮動(dòng)定位是同時(shí)支持向左或向右浮動(dòng)的方援。而我們的浮動(dòng)布局也是支持某個(gè)子視圖向左或者向右浮動(dòng)的。當(dāng)某個(gè)子視圖在加入到布局視圖時(shí)涛癌,可以設(shè)定為向左還是向右浮動(dòng),這里的向左和向右是不能同時(shí)支持的送火,視圖要么向左要么向右拳话。對(duì)于視圖向右浮動(dòng)來(lái)說(shuō),他的機(jī)制是和向左浮動(dòng)是一樣的种吸。我們可以看如下的視圖:
? ?可以看出弃衍,當(dāng)A,B,C,D,E,F這幾個(gè)視圖向右浮動(dòng)時(shí),除了方向外坚俗,其他的規(guī)則是跟視圖向左浮動(dòng)的規(guī)則是一樣的镜盯。一個(gè)布局視圖里面的子視圖是可以設(shè)置為向左或者向右浮動(dòng)的,而前面的例子里的所有子視圖要么都向左猖败,要么都向右速缆。但是實(shí)際場(chǎng)景中我們是可以設(shè)置某些視圖向左浮動(dòng),而某些視圖向右浮動(dòng)的恩闻。比如下面的例子:
? ? ? 上面的例子中我們把子視圖添加到布局視圖的順序分別是A,B,C,D,E,F這個(gè)順序艺糜,且設(shè)定C,D,E這三個(gè)子視圖是向左浮動(dòng)的,而A,B,F這三個(gè)子視圖是向右浮動(dòng)的幢尚。在前面的所有向左浮動(dòng)的例子中破停,我們的剩余寬度的比較總是以布局視圖的右邊界為標(biāo)準(zhǔn)的,而前面所有向右浮動(dòng)的例子中我們的剩余寬度的比較總是以布局視圖的左邊界為標(biāo)準(zhǔn)的尉剩。那么當(dāng)我們的布局視圖里面的子視圖又有向左浮動(dòng)的且又有向右浮動(dòng)的情況時(shí)真慢,我們的寬度邊界又是如何考慮的呢?
? ? ?我們來(lái)分析一下上面的左右浮動(dòng)的例子理茎,因?yàn)槲覀兛偸前刺砑拥南群箜樞騺?lái)進(jìn)行浮動(dòng)布局的黑界,所以上面的例子中A,B這兩個(gè)子視圖都向右浮動(dòng)這個(gè)很容易理解管嬉,而視圖C向左浮動(dòng)也比較容易理解。我們來(lái)考察當(dāng)D視圖向左浮動(dòng)要插入到容器視圖時(shí)园爷,我們發(fā)現(xiàn)如果視圖D浮動(dòng)到視圖C的右邊并且上邊和視圖C保持一致時(shí)宠蚂,視圖D的布局寬度將會(huì)覆蓋掉視圖B的部分空間,如果出現(xiàn)了覆蓋則是不符合浮動(dòng)布局規(guī)則5中的定義的童社,因此視圖D必須要往下移動(dòng)求厕,直到到達(dá)視圖B的底部后才不會(huì)出現(xiàn)覆蓋現(xiàn)象,因此視圖D的上邊位置就變?yōu)榱?00扰楼,而左邊的位置則仍然等于視圖A的右邊的位置了呀癣。視圖E也是向左浮動(dòng),這里我們是要求E和最后一個(gè)加入的D必須要保持上邊對(duì)齊弦赖,但是如果保持上邊對(duì)齊的話就無(wú)法容納E的寬度而將產(chǎn)生覆蓋项栏,因此必須要將視圖E往下移動(dòng),直到移到視圖A的下面才能滿足寬度的填充蹬竖,因此視圖E的上邊位置就設(shè)置為視圖A的下邊沼沈,而左邊位置則設(shè)置為D的右邊。最后我們?cè)賮?lái)考察F的情況币厕,雖然前面最后一個(gè)向右浮動(dòng)的視圖是B列另,但是根據(jù)浮動(dòng)規(guī)則2的約定,視圖F的上邊位置必須要和最后一個(gè)加入的視圖E的上邊位置保持一致旦装,但是如果和E的上邊位置保持一致的話页衙,F(xiàn)的長(zhǎng)度將會(huì)覆蓋掉E的位置,因此視圖F必須要往下移動(dòng)到視圖E的下面阴绢,并且右邊要和布局視圖的右邊界保持一致店乐,這樣才能容納視圖F的顯示。通過(guò)上面的例子我們可以看出當(dāng)一個(gè)布局視圖中同時(shí)存在著向左浮動(dòng)和向右浮動(dòng)的子視圖時(shí)呻袭,我們就有浮動(dòng)布局的將新增規(guī)范8的定義如下:
8.當(dāng)浮動(dòng)布局中同時(shí)存在著向左和向右浮動(dòng)的子視圖時(shí)眨八,向左浮動(dòng)的視圖剩余寬度的右邊界是在不覆蓋掉右邊視圖的情況下的最小向右浮動(dòng)的視圖的左邊界,而向右浮動(dòng)的視圖的剩余寬度的左邊界是在不覆蓋掉左邊視圖的情況下的最大向左浮動(dòng)的視圖的右邊界左电。
上下浮動(dòng)
? ? ? ?前面我們介紹向左和向右浮動(dòng)的布局視圖的一些場(chǎng)景踪古。在CSS中也只定義了向左和向右浮動(dòng)的功能,向左向右浮動(dòng)的布局視圖的原則是按視圖添加的順序券腔,以及設(shè)定的浮動(dòng)方向優(yōu)先按左或者按右浮動(dòng)伏穆,然后再整體的從上到下進(jìn)行布局展示。但是在實(shí)際的情況中我們會(huì)要求有某個(gè)子視圖按向上或者向下浮動(dòng)的來(lái)進(jìn)行布局纷纫,并且布局的順序是按添加的子視圖的順序優(yōu)先按向上或者按向下進(jìn)行浮動(dòng)枕扫,然后再整體的從左到右進(jìn)行布局展示,這種浮動(dòng)布局我們稱之為上下浮動(dòng)布局辱魁。上下浮動(dòng)布局里面的子視圖烟瞧,進(jìn)行浮動(dòng)的依據(jù)是根據(jù)子視圖本身的高度诗鸭,以及布局視圖的高度來(lái)決定的(而左右浮動(dòng)布局則是根據(jù)寬度來(lái)決定的)。其中的浮動(dòng)規(guī)范除了方向上不同外参滴,其他的機(jī)制都是跟左右浮動(dòng)是一樣的强岸。我們這里就不再進(jìn)行贅述了,下面我們通過(guò)一張布局來(lái)了解一下上下浮動(dòng)布局的界面:
? ? ? ?上圖可以看出上下浮動(dòng)除了方向上和左右浮動(dòng)不一樣外砾赔,其他的規(guī)則都是一致的蝌箍,上下浮動(dòng)布局是依然支持清除浮動(dòng)的,只不過(guò)清除浮動(dòng)時(shí)方向是變?yōu)榱讼蛴乙苿?dòng)暴心。同時(shí)上下浮動(dòng)布局也是支持子視圖的比重設(shè)置的妓盲,只不過(guò)這里的比重是指子視圖的高度。
MyFloatLayout的方法和屬性的介紹
? ? 說(shuō)了這么多浮動(dòng)布局的實(shí)現(xiàn)原理以及布局的機(jī)制专普,那我們?cè)趺磥?lái)使用和定義浮動(dòng)布局呢悯衬?要實(shí)現(xiàn)和使用浮動(dòng)布局,我們必須要使用浮動(dòng)布局MyFloatLayout這個(gè)類檀夹,這個(gè)類的定義如下:
@interface MyFloatLayout : MyBaseLayout
-(id)initWithOrientation:(MyLayoutViewOrientation)orientation;
+(id)floatLayoutWithOrientation:(MyLayoutViewOrientation)orientation;
@property(nonatomic,assign) MyLayoutViewOrientation orientation;
@property(nonatomic,assign) IBInspectable MyGravity gravity;
@end
? ? ? ?從類的初始化方法中我們可以看出筋粗,在創(chuàng)建一個(gè)浮動(dòng)布局時(shí)必須要指定一個(gè)方向,這個(gè)方向指的是最終子視圖的布局走向炸渡,因?yàn)樽笥腋?dòng)布局我們是先按左右浮動(dòng)最終是一個(gè)從上到下的排列過(guò)程娜亿,而上下浮動(dòng)布局則是先按上下浮動(dòng)最終則是從左到右排列,因此當(dāng)我們指定orientation的值為MyLayoutViewOrientation_Vert表示的是創(chuàng)建一個(gè)左右浮動(dòng)的浮動(dòng)布局偶摔,而當(dāng)值設(shè)定為MyLayoutViewOrientation_Horz時(shí)則表示建立的是一個(gè)上下浮動(dòng)的浮動(dòng)布局,系統(tǒng)默認(rèn)建立的是左右浮動(dòng)的浮動(dòng)布局促脉。而且后續(xù)還可以通過(guò)orientation屬性來(lái)進(jìn)行動(dòng)態(tài)的修改浮動(dòng)的方向辰斋。當(dāng)浮動(dòng)布局的浮動(dòng)方向指定后,接下來(lái)我們就要為某個(gè)要添加到浮動(dòng)布局的子視圖指定浮動(dòng)方向?qū)傩匀澄丁⑶宄?dòng)屬性宫仗、以及比重了,這些則可以通過(guò)視圖的擴(kuò)展分類:
@interface UIView(MyFloatLayoutExt)
@property(nonatomic,assign,getter=isReverseFloat) IBInspectable BOOL reverseFloat;
@property(nonatomic,assign) IBInspectable BOOL clearFloat;
@property(nonatomic, assign) IBInspectable CGFloat weight;
@end
?來(lái)設(shè)置旁仿。 ?在默認(rèn)情況下當(dāng)我們建立的是一個(gè)左右浮動(dòng)布局時(shí)藕夫,我們添加到布局里面的所有子視圖默認(rèn)都是向左浮動(dòng)的,而當(dāng)建立的是一個(gè)上下浮動(dòng)布局時(shí)枯冈,我們添加到布局里面的所有子視圖默認(rèn)都是向上浮動(dòng)的毅贮,因此當(dāng)需要改動(dòng)子視圖浮動(dòng)的方向則可以設(shè)置屬性reverseFloat來(lái)實(shí)現(xiàn),這個(gè)屬性是一個(gè)BOOL類型的值尘奏,當(dāng)設(shè)置為YES時(shí)表示按默認(rèn)方向相反的方向浮動(dòng)滩褥,也就是在左右浮動(dòng)布局中,如果設(shè)置某個(gè)子視圖的reverseFloat為YES的話則表示子視圖是向右浮動(dòng)炫加,而對(duì)于上下浮動(dòng)布局來(lái)數(shù)則表示是向下浮動(dòng)瑰煎。視圖的擴(kuò)展屬性clearFloat也是一個(gè)BOOL類型铺然,表示是否清除浮動(dòng),默認(rèn)值是NO表示不清除浮動(dòng)酒甸,當(dāng)某個(gè)子視圖需要有清除浮動(dòng)的效果時(shí)魄健,請(qǐng)將這個(gè)屬性設(shè)置為YES。最后一個(gè)視圖的擴(kuò)展屬性weight表示視圖的寬度或者高度的比重插勤,這個(gè)值默認(rèn)值是0沽瘦,表示不是按比重來(lái)指定寬度,這時(shí)候你在添加子視圖時(shí)必須明確的指定寬度或者高度饮六,而當(dāng)設(shè)置為非0時(shí)則不需要為子視圖指定寬度和高度其垄,而由布局系統(tǒng)來(lái)自動(dòng)幫你計(jì)算。這里的weight的設(shè)置范圍是:0<=weight <=1.
? ? ? ?上面分別的介紹了浮動(dòng)布局的建立卤橄,以及子視圖的擴(kuò)展的屬性設(shè)置來(lái)實(shí)現(xiàn)視圖在浮動(dòng)布局中的浮動(dòng)方式绿满、是否清除浮動(dòng)、以及比重的設(shè)置方法窟扑。另外對(duì)于浮動(dòng)布局來(lái)說(shuō)喇颁,因?yàn)槭菑腗yBaseLayout中派生的,因此浮動(dòng)布局同樣支持wrapContentWidth以及wrapContentHeight屬性的設(shè)置的嚎货,也就是浮動(dòng)布局的寬高可以由子視圖來(lái)決定的橘霎,需要明確的是一般情況下我們對(duì)于左右浮動(dòng)布局來(lái)說(shuō),只需要設(shè)置wrapContentHeight殖属。當(dāng)然你也可以設(shè)置wrapContentWidth(設(shè)置這個(gè)屬性的前提是布局視圖里面有一個(gè)子視圖特別的寬)姐叁;同樣對(duì)于上下浮動(dòng)布局來(lái)說(shuō),只需要設(shè)置wrapContentWidth洗显。當(dāng)然你也可以設(shè)置wrapContentHeight(設(shè)置這個(gè)屬性的前提是布局視圖里面有一個(gè)子視圖特別的高)外潜。
? ? ? ? 最后,我們看到浮動(dòng)布局視圖里面還有一個(gè)gravity屬性挠唆,這個(gè)屬性在左右浮動(dòng)布局視圖中可以用來(lái)設(shè)置所有子視圖的整體的上处窥,中,下三種托椋靠模式滔驾,而在上下浮動(dòng)布局視圖中則可以用來(lái)設(shè)置所有子視圖的整體的左,中俄讹,右三種投咧拢靠模式。
? ? 下面的效果圖就是我們使用浮動(dòng)布局來(lái)實(shí)現(xiàn)的仿天貓和ZAKER界面布局的效果圖:
? ? ? 上面的gif圖片中有三個(gè)DEMO分別介紹浮動(dòng)布局的患膛,你可以通過(guò)DEMO中的FOLTest1ViewController來(lái)研究和學(xué)習(xí)浮動(dòng)布局的各種屬性的設(shè)置以及效果沽瞭,而FOLTest2ViewController則是介紹的天貓首頁(yè)的布局、FOLTest3ViewController介紹的則是ZAKER的卡片式布局的實(shí)現(xiàn)。通過(guò)DEMO我們可以看出驹溃,當(dāng)我們要實(shí)現(xiàn)一些不規(guī)則的界面布局時(shí)城丧,我們并不需要使用相對(duì)布局來(lái)實(shí)現(xiàn),而只需設(shè)定正確的子視圖的添加順序豌鹤,以及浮動(dòng)屬性的設(shè)置就能達(dá)到我們想要的效果亡哄,而且采用浮動(dòng)布局的優(yōu)點(diǎn)時(shí)不需要再去考慮視圖之間的依賴關(guān)系的設(shè)置了。
智能邊界線
? ? ?為了說(shuō)明智能邊界線我們先來(lái)看這兩個(gè)界面:
? ? ? ?上面的兩個(gè)界面是仿淘寶和天貓首頁(yè)以及ZAKER新聞的界面布疙,我們來(lái)觀察其中的每個(gè)區(qū)塊之間的邊界線蚊惯。我們發(fā)現(xiàn)處在邊緣部分是沒(méi)有顯示邊界線的,而邊界線只會(huì)顯示在區(qū)塊交界的地方顯示一條邊界線灵临。在一般情況下截型,不規(guī)則邊界線的顯示我們有可能需要UI人員提供圖片來(lái)完成,或者不提供圖片我們?cè)诰幊虝r(shí)也需要進(jìn)行條件的判斷以便決定是否需要在特定的位置繪制邊界線儒溉,顯然這樣做將會(huì)增加我們的代碼量宦焦。因此為了解決這個(gè)問(wèn)題,我們的布局系統(tǒng)提供了邊界線以及智能邊界線的功能顿涣。如果您用了MyBaseLayout派生的6大布局的話波闹,我們是可以通過(guò)基類提供的四個(gè)屬性:
@property(nonatomic, strong) MyBorderline *leftBorderline;
@property(nonatomic, strong) MyBorderline *rightBorderline;
@property(nonatomic, strong) MyBorderline *topBorderline;
@property(nonatomic, strong) MyBorderline *bottomBorderline;
來(lái)為布局視圖指定要顯示的四邊的邊界線,我們可以支持設(shè)置邊界線的顏色涛碑,粗細(xì)精堕,縮進(jìn),以及點(diǎn)線等功能蒲障,這樣我們就不再需要單獨(dú)的提供邊界線的切圖了歹篓。要想看邊界線的例子,可以查看LLTest4ViewController和AllTest3ViewController這兩個(gè)DEMO的介紹揉阎。即便如此庄撮,對(duì)于上面的特殊情況,我們還需要進(jìn)行編程以及條件判斷來(lái)完成邊界線的指定余黎,因此為了解決這個(gè)問(wèn)題重窟,我們?cè)诓季种行略黾恿艘粋€(gè)智能邊界線的屬性:
@property(nonatomic, strong) MyBorderline *intelligentBorderline;
@property(nonatomic, assign) BOOL notUseIntelligentBorderline;
如果為某個(gè)布局視圖設(shè)置了智能邊界線的值载萌,那么這個(gè)布局視圖里面的子布局視圖將會(huì)根據(jù)視圖之間的關(guān)系而自動(dòng)智能的生成邊界線惧财。這里需要強(qiáng)調(diào)的是只有布局視圖里面的子布局視圖才會(huì)生成智能的邊界線,對(duì)于布局視圖里面的非布局子視圖是不會(huì)生成邊界線的扭仁。而如果我們的某個(gè)布局視圖里面的子布局視圖不想使用智能的邊界線垮衷,而是仍想自己手動(dòng)設(shè)定,那么只需要將自己的notUseIntelligentBorderline設(shè)置為YES即可乖坠,他表示不使用父布局提供的智能邊界線功能搀突。在當(dāng)前的布局庫(kù)版本中,我們只有線性布局熊泵、浮動(dòng)布局仰迁、表格布局甸昏、流式布局支持智能邊界線的設(shè)定,而框架布局徐许、相對(duì)布局則是不支持的施蜜。正是因?yàn)椴季窒到y(tǒng)里面提供的智能邊界線的功能,就使得我們?cè)谠O(shè)定布局視圖之間的邊界線時(shí)非常的簡(jiǎn)單雌隅,只需要一句話就能搞定翻默。
浮動(dòng)布局的實(shí)踐
? ? ? ?上面就是我們要介紹的關(guān)于浮動(dòng)布局的全部的東西,接下來(lái)我們將借著DEMO中的代碼來(lái)具體的介紹我們?nèi)绾问褂酶?dòng)布局來(lái)實(shí)現(xiàn)上面的功能的恰起。在介紹之前修械,我們這里說(shuō)明一下,我們?nèi)匀皇强梢杂米右晥D的擴(kuò)展屬性myLeft,myRight,myTop,myBottom這4個(gè)屬性來(lái)指定視圖之間的間距的检盼。同時(shí)我們還支持子視圖的寬度擴(kuò)展屬性widthSize的值可以設(shè)置為一個(gè)具體值肯污,也可以等于布局視圖的寬度,以及前面已經(jīng)布好局的子視圖的寬度梯皿,甚至還可以等于子視圖的高度仇箱。 ?
? ? ?因?yàn)樗械年P(guān)于浮動(dòng)布局的代碼我們都能在DEMO中找到,因?yàn)槲覀冎唤榻B幾個(gè)例子东羹,其他的大家可以自己去研究剂桥,我們看下面的圖:
? ? ? ?我們看到上面的界面左上角的區(qū)塊的高度為180,而其余的區(qū)塊都是90属提,并且每個(gè)區(qū)塊的寬度都是屏幕的一半权逗。為了容納上面的界面我們需要先建立左右浮動(dòng)的浮動(dòng)布局:
CGFloat itemHeight = 90;
//品牌特賣
MyFloatLayout *layout1 = [MyFloatLayout floatLayoutWithOrientation:MyLayoutViewOrientation_Vert];
layout1.backgroundColor = [UIColor whiteColor];
layout1.wrapContentHeight = YES;
layout1.intelligentBorderline = [[MyBorderline alloc] initWithColor:[UIColor lightGrayColor]];
我們?cè)O(shè)定的layout1的高度由子視圖決定,并且設(shè)置了智能邊界線冤议。接下來(lái)我們只需要每個(gè)區(qū)塊按順序依次添加進(jìn)來(lái)即可斟薇。且從上面的區(qū)域中我們可以看出一共有3種不同的類型的區(qū)塊分別是A,B, C三種區(qū)塊恕酸,這三種區(qū)塊其實(shí)是用MyFloatLayout來(lái)實(shí)現(xiàn)的堪滨。
? ? ? A區(qū)塊我們也可以用一個(gè)浮動(dòng)布局來(lái)實(shí)現(xiàn),我們只需要建立一個(gè)上下浮動(dòng)布局蕊温,標(biāo)題袱箱,小圖都默認(rèn)往上浮動(dòng)。剩下的大圖寬度和父布局寬度相等义矛,并且設(shè)置weight=1就可以了发笔,這部分代碼的具體實(shí)現(xiàn)就在FOLTest2ViewController中的createItemLayout1_1的方法中實(shí)現(xiàn)。
? ? ? B區(qū)塊我們也可以用浮動(dòng)布局來(lái)實(shí)現(xiàn)凉翻,我們只需要建立一個(gè)左右浮動(dòng)布局了讨,大圖片優(yōu)先向右浮動(dòng),高度和父布局高度相等,接下來(lái)主標(biāo)題向左浮動(dòng)前计,并且weight=1表示占用剩余的寬度胞谭;副標(biāo)題也是向左浮動(dòng),并且設(shè)置清除浮動(dòng)屬性男杈,同時(shí)設(shè)置weight=1表示占用剩余的寬度韭赘;最后的小圖也是設(shè)置為左浮動(dòng),并且設(shè)置清除浮動(dòng)屬性势就。這部分代碼的具體實(shí)現(xiàn)在FOLTest2ViewController中的createItemLayout1_3的方法中實(shí)現(xiàn).
? ? ? ?C區(qū)塊我們也可以用浮動(dòng)布局來(lái)實(shí)現(xiàn)泉瞻,我們只需要建立一個(gè)左右浮動(dòng)布局,主標(biāo)題部分向左浮動(dòng)苞冯,并且寬度和父布局寬度相等袖牙,付標(biāo)題部分向左浮動(dòng),并且寬度和父布局寬度相等舅锄,而圖片部分則向右浮動(dòng)即可鞭达。
最后我們可以依次建立A,B,C三種區(qū)塊然后依次的加入到layout1中去,加入時(shí)只需要設(shè)置A的高度為180皇忿,而寬度則是layout1的一半即可畴蹭,而其他兩種則高度設(shè)置為80,且寬度設(shè)置為layout1的一半即可鳍烁。
小結(jié)
? ? ? 浮動(dòng)布局是一種功能非常強(qiáng)大的布局體系叨襟,從某種程度上來(lái)他甚至是相對(duì)布局的替代方案,而且要比相對(duì)布局要簡(jiǎn)單幔荒,因?yàn)槔锩娴淖右晥D之間是不需要設(shè)置約束和依賴關(guān)系的糊闽,單單憑借加入到布局視圖的順序,以及自身的寬高就能完成我們想要的功能爹梁。而且其提供的能力甚至要比CSS中的浮動(dòng)屬性更加強(qiáng)大右犹。而我們?cè)谶M(jìn)行WEB前端開(kāi)發(fā)時(shí)很多的界面布局其實(shí)都是通過(guò)CSS的浮動(dòng)屬性來(lái)完成的。因此我們也可以借助浮動(dòng)布局來(lái)我們各種復(fù)雜的界面布局姚垃,而且浮動(dòng)布局也能方便的實(shí)現(xiàn)線性布局以及流式布局的能力念链。如果您想了解更多的關(guān)于流式布局的功能請(qǐng)您訪問(wèn)我的github站點(diǎn)來(lái)了解更多:
OC版本:https://github.com/youngsoft/MyLinearLayout
Swift版本: https://github.com/youngsoft/TangramKit
如果您覺(jué)得庫(kù)還不錯(cuò),記得為我點(diǎn)贊哦积糯。