HTML/CSS中的float定位在iOS上的实现
CarrollClem
8年前
<p style="text-align:center"><img src="https://simg.open-open.com/show/643555917c097e09ae8c4b2f754d57cc.jpg"></p> <p><strong>CSS中的float属性简介</strong></p> <p>几乎所有会WEB前端开发的同学都知道CSS中有一个float属性用于实现HTML元素的浮动定位展示。float 属性定义元素在哪个方向浮动。以往这个属性总应用于图像,使文本围绕在图像周围。不过在 CSS 中,任何元素都可以浮动,假如在一行之上只有极少的空间可供浮动元素,那么这个元素会跳至下一行,这个过程会持续到某一行拥有足够的空间为止。 浮动布局主要用于那些图文环绕以及实现一些界面不规则排列的场景,并且浮动定位技术在WEB前端开发中应用的非常普遍。iOS中实现不规则排列的方式</p> <p>在iOS中我们可以通过frame以及AutoLayout两种方法来实现界面的布局。如果是通过frame方式在一些不规则界面的场景中就需要进行大量计算来实现布局,而AutoLayout则可以通过设置视图之间的依赖约束来实现布局,这两者的布局方式都和子视图加入到父视图的顺序无关,越是不规则的界面,计算量以及约束依赖的设置就越复杂。我们在开发程序时是会碰到一个概念叫高内聚低耦合,如果对象之间都存在着相互依赖约束关系的话则意味着程序越复杂和难以理解,所以我们要进行对象之间的解耦处理。就以某些不规则的界面来说,表面上看起来杂乱无章实际仍然是有一些规律可循的:视图之间展示的关系是按添加到父视图的先后顺序并且只和自己的尺寸以及父视图的尺寸有关。这样我们就把这种子视图之间的强耦合关联转化为了按排列顺序以及只和自身尺寸有关的弱耦合关联。 怎么来实现这种布局时的弱耦合以便减少约束依赖的复杂性,这就是本文要探讨的浮动实现机制。本文将会先从浮动的原理开始入手介绍浮动的各种特性,然后再介绍淘宝天猫首页的不规则布局以及ZAKER新闻版面的那种不规则的卡片式布局的实现机制,最后再说明怎么去实现这种不规则浮动布局的方法。</p> <p><strong>浮动</strong></p> <p>我们的UI界面中总是有一种场景是:某个容器视图后续添加的子视图的左边总是紧跟着前面添加的子视图的右边,而上边则跟前面视图的上边保持一致进行停靠显示,而当容器视图剩余的宽度空间不够容纳新加入的子视图时则新加入的子视图自动的往下移动且在不覆盖已经排列好的视图的前提下寻找出一个可以容纳其宽度的最合适的位置进行停靠。我们称这种机制为浮动。</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/512a994951ca4fd7841363c771b95faa.png"></p> <p>上面的场景中我们的容器视图的尺寸为500x300,当添加视图A时,因为视图A的宽度是80,宽度能够被容器视图容纳,所以我们将视图A浮动到容器视图的左上角位置。而当添加视图B时,因为视图B的宽度是100,仍然能够被容器视图的宽度容纳(容器视图剩余宽度为420),所以将视图B浮动到视图A的右边并且上边对齐。我们也可以按同样的方式来处理视图C的浮动。这样容器视图剩余的宽度变为了170(500-80-100-150)。假如这时候我们想再放入一个尺寸为200x100的视图D的时候,因为这时要添加的视图D的宽度为200,而容器视图的剩余宽度只有170了,这时候视图D将不能浮动到视图C的右边了,我们必须要找一个合适的位置来放置视图D。根据浮动的原则我们算出最合适的浮动位置是A视图的右边,且为了保持前面视图不被覆盖,因此视图D放置的最合适的坐标位置是:(80,130)。下面就是视图D放入后的结果:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/4d005c1c9c715d0a5ab1fb5ccf98643c.png"></p> <p>根据浮动的规则假如视图D的宽度不是200而是400的话,那么视图D将不能浮动到视图A的右边(视图A的右边的剩余的宽度为320,无法容纳400的宽度),那么根据浮动的规则,视图D将再次往下移动,并浮动到容器视图的最左边的(0,180)的位置上。我们继续来加入一个新的视图E,视图E的尺寸为100x50。那么视图E应该是浮动到视图C的右边还是视图D的右边呢? 答案是D的右边,虽然C右边的空间也可以容纳100的宽度,但是却不符合浮动的规则。我们上面说的浮动的规则是在可以容纳新加入视图宽度的情况下新加入的视图的上边和前一个加入的视图的上边对齐,而且新加入的视图的左边必须放置在前一个加入视图的右边。因此视图E加入到容器视图后的结果如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/e534875396130a9c72713507489aad2f.png"></p> <p>最后,我们再来考察新视图F的加入。假如视图F的尺寸为300x50。那么根据浮动的规则视图将无法浮动到E的右边,同时也无法浮动到D的右边了,这时候只能继续往下移动,而最终的左边是浮动到容器视图的最左边,而上边的位置则是视图D的下方。最终的布局结果如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/7fd231f41eee0fc83f406ac95a4ef5eb.png"></p> <p>通过上面的4张图片我们就可以总结出浮动时优先向左浮动,再向下浮动的浮动规则:</p> <ul> <li> <p>R1:加入布局视图的第一个子视图总是浮动到布局视图的左上角。</p> </li> <li> <p>R2:如果新加入的子视图的宽度能够被放入到前一个加入的视图右边到布局视图右边的剩余宽度空间中的话,则新加入的视图的左边位置是等于前一个加入视图的右边位置,且新加入视图的上边位置和前一个加入的视图的上边位置保持一致。</p> </li> <li> <p>R3:如果新加入的子视图的宽度不能被放入到前一个加入的视图的右边到布局视图右边的剩余宽度空间中时,则新加入的视图将继续往左往下寻找到一个能容纳其宽度的最小空间,并且不能遮挡掉前面加入的所有子视图的最佳的位置进行放置。</p> </li> <li> <p>R4:如果某个子视图的宽度大于等于布局视图的宽度,则总是浮动到布局视图的最左边,且上边的位置是前面所有子视图的最下边的位置进行放置。</p> </li> <li> <p>R5:总是确保任意的子视图之间是不能被重叠覆盖。</p> </li> </ul> <p>上面的5条规则就是一种浮动规则的定义,在CSS中我们可以为某个元素指定float这个属性,而这个属性的值可以设定为left或者right或者none,分别表示元素是向左浮动还是向右浮动还是不浮动。同时我们还可以为元素指定clear这个属性来清除浮动,clear这个属性可以设置left, right,both,none这四个值,下面我再介绍清除浮动的作用和意义。</p> <p>清除浮动</p> <p>上面的几个场景中我们发现,不管新加入视图的宽度如何,只要容器视图中剩余宽度能够容纳新加入的子视图,则子视图总是会浮动到前面一个视图的右边。但在实际的应用场景中,我们又希望某个视图不遵守这种默认的浮动规则,而是让新加入的子视图的左边总是和容器视图的左边对齐,且子视图的上边则是放入到前面加入的占用最高空间的视图的下方。比如下面的情况:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/f3266394340a4fc92742176262e54d07.png"></p> <p>我们的子视图C,虽然宽度为150,并且能浮动到视图B的右边,但是实际中我们则想让视图C浮动到A的下边并且左边和容器视图对齐,这时候我们就需要用到清除浮动的概念了。 我们可以为视图C设置一个清除浮动的属性,这样视图C就达到了我们想要的效果。因此我们可以看出,所谓清除浮动就是使得视图的默认的浮动规则失效,而总是让视图的左边和容器视图的左边对齐,而让视图的上边则设置为前面加入的所有同一个方向浮动的视图的最高高度的下方。因此我们可以得出浮动布局的第6条规则:</p> <ul> <li> <p>R6:如果子视图设定了清除浮动属性,则视图在布局时的左边界总是和容器视图的左边界相等,而上边界则是在所有前面加入的同一个方向浮动视图的最高的高度的下方显示。</p> </li> </ul> <p>通过视图的清除浮动属性,我们可以设置让某些视图不进行浮动,而是达到另起一行的效果。</p> <p><strong>比重</strong></p> <p>我们再来考察一种场景,就上面的例子来说,我们先加入了视图A和B,现在我们想加入一个视图C,并且想让视图C浮动在视图B的右边。而且宽度则是已经填充的A和B剩余的宽度320(500-80-100)。一个办法就是我们手动的设定视图C的宽度为320,这样就能达到想要的效果,但是在实际的应用中,A和B的宽度可能是不确定的,并且容器视图的宽度也是不确定的,而不管何种情况我们又总想让视图C的宽度总是占用剩余的宽度,就像如下的效果图一样:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/c93bca61531319c86258589cd1e70f13.png"></p> <p>在上面的场景中,我们希望不需要明确的设置视图C的宽度,而是通过一种比重的特性来设置视图C总是占用容器视图的剩余宽度的某个比例值。这里的比重的设置,是在整体布局视图的浮动的方向的设定上的,就是说当整体的布局视图里面的视图是支持左边和右边浮动时则这个比重指定的是视图的宽度的相对比例值,而当布局视图支持的是上边和下边浮动时则这个比重指的是视图的高度的相对比例值。而且我们约定比重值的设定必须大于0且小于等于1。通过比重值的设定,我们可以不需要对某个新加入的视图设定具体的宽度或者高度,而只需要指定一个相对的值,而由浮动布局来根据当前的浮动情况来自动计算出应该有的宽度或者宽度。其中的具体的计算公式为:</p> <p>某个设置了比重值的视图的宽度或者高度 = (布局视图的宽度或者高度 - 前一个视图的右边或者下边的边界值)* 视图的比重值。</p> <p>就以上面的左右浮动的例子来说,假设我们设定视图C的比重为1。根据公式的定义,布局视图的宽度是500,而前面一个视图B的右边边界值是180(100+80)。因此最终视图C的宽度就是:320 = (500 - 180)*1, 而假如设定视图C的比重是0.5的话,则最终视图C的宽度就是160了。又如果我们再增加一个视图D的比重设置为1的话。因为前面的视图C的宽度已经算出是160,他的右边距值是340(180+160), 因此最终视图D的宽度就是160 (500 - 340) *1了。其中的效果图如下:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/b1d5eddda604592e575182e10a1a56cb.png"></p> <p>浮动布局中的子视图可以通过设定比重来得到剩余的宽度或者高度,因此浮动布局中针对比重属性定义新的规则如下:</p> <p>R7:当某个子视图设定了比重属性时,这个视图的宽度或者高度将根据布局视图的浮动方向设定,以及比重的值的设定自动进行计算,比重的设置必须大于0小于等于1,而通过比重计算出来的宽度或高度的公式为:布局视图的宽度或者高度 - 前一个视图的右边或则下边的边界值)* 视图的比重值</p> <p><strong>左右浮动</strong></p> <p>上面我们的介绍的浮动的例子中,都总是默认是向左浮动,然后再从上到下的进行布局。但前面也有说到CSS中的元素的浮动定位是同时支持向左或向右浮动的。当某个子视图在加入到布局视图时,可以设定为向左还是向右浮动(float属性的值设置为left或者right),这里的向左和向右是不能同时支持的,视图要么向左要么向右。对于视图向右浮动来说,他的机制是和向左浮动是一样的。我们可以看如下的视图:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/6e810228786b3452a9cdf783daa74bae.png"></p> <p>可以看出,当A,B,C,D,E,F这几个视图向右浮动时,除了方向外,其他的规则是跟视图向左浮动的规则是一样的。一个布局视图里面的子视图是可以设置为向左或者向右浮动的,而前面的例子里的所有子视图要么都向左,要么都向右。但是实际场景中我们是可以设置某些视图向左浮动,而某些视图向右浮动的。比如下面的例子:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/ee8e066c6b786f78abff709a7da86328.png"></p> <p>上面的例子中我们把子视图添加到布局视图的顺序分别是A,B,C,D,E,F这个顺序,且设定C,D,E这三个子视图是向左浮动的,而A,B,F这三个子视图是向右浮动的。在前面的所有向左浮动的例子中,我们的剩余宽度的比较总是以布局视图的右边界为标准的,而前面所有向右浮动的例子中我们的剩余宽度的比较总是以布局视图的左边界为标准的。那么当我们的布局视图里面的子视图又有向左浮动的且又有向右浮动的情况时,我们的宽度边界又是如何考虑的呢?</p> <p>我们来分析一下上面的左右浮动的例子,因为我们总是按添加的先后顺序来进行浮动布局的,所以上面的例子中A,B这两个子视图都向右浮动这个很容易理解,而视图C向左浮动也比较容易理解。我们来考察当D视图向左浮动要插入到容器视图时,我们发现如果视图D浮动到视图C的右边并且上边和视图C保持一致时,视图D的布局宽度将会覆盖掉视图B的部分空间,如果出现了覆盖则是不符合浮动布局规则5中的定义的,因此视图D必须要往下移动,直到到达视图B的底部后才不会出现覆盖现象,因此视图D的上边位置就变为了100,而左边的位置则仍然等于视图A的右边的位置了。视图E也是向左浮动,这里我们是要求E和最后一个加入的D必须要保持上边对齐,但是如果保持上边对齐的话就无法容纳E的宽度而将产生覆盖,因此必须要将视图E往下移动,直到移到视图A的下面才能满足宽度的填充,因此视图E的上边位置就设置为视图A的下边,而左边位置则设置为D的右边。最后我们再来考察F的情况,虽然前面最后一个向右浮动的视图是B,但是根据浮动规则2的约定,视图F的上边位置必须要和最后一个加入的视图E的上边位置保持一致,但是如果和E的上边位置保持一致的话,F的长度将会覆盖掉E的位置,因此视图F必须要往下移动到视图E的下面,并且右边要和布局视图的右边界保持一致,这样才能容纳视图F的显示。通过上面的例子我们可以看出当一个布局视图中同时存在着向左浮动和向右浮动的子视图时,我们就有浮动布局的将新增规范8的定义如下:</p> <ul> <li> <p>R8:当浮动布局中同时存在着向左和向右浮动的子视图时,向左浮动的视图剩余宽度的右边界是在不覆盖掉右边视图的情况下的最小向右浮动的视图的左边界,而向右浮动的视图的剩余宽度的左边界是在不覆盖掉左边视图的情况下的最大向左浮动的视图的右边界。</p> </li> </ul> <p><strong>上下浮动</strong></p> <p>前面我们介绍向左和向右浮动的布局视图的一些场景。在CSS中也只定义了向左和向右浮动的功能,向左向右浮动的布局视图的原则是按视图添加的顺序,以及设定的浮动方向优先按左或者按右浮动,然后再整体的从上到下进行布局展示。但是在实际的情况中我们会要求有某个子视图按向上或者向下浮动的来进行布局,并且布局的顺序是按添加的子视图的顺序优先按向上或者按向下进行浮动,然后再整体的从左到右进行布局展示,这种浮动布局我们称之为上下浮动布局。上下浮动布局里面的子视图,进行浮动的依据是根据子视图本身的高度,以及布局视图的高度来决定的(而左右浮动布局则是根据宽度来决定的)。其中的浮动规范除了方向上不同外,其他的机制都是跟左右浮动是一样的。我们这里就不再进行赘述了,下面我们通过一张布局来了解一下上下浮动布局的界面:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/43af0955d8df2356dbeeb335f28cbb20.png"></p> <p>上图可以看出上下浮动除了方向上和左右浮动不一样外,其他的规则都是一致的,上下浮动布局是依然支持清除浮动的,只不过清除浮动时方向是变为了向右移动。同时上下浮动布局也是支持子视图的比重设置的,只不过这里的比重是指子视图的高度。</p> <p><strong>淘宝天猫首页以及ZAKER新闻的布局机制</strong></p> <p>上面介绍的浮动规则可以看出,通过浮动这种机制,我们将不再通过设置子视图之间的约束来进行不规则布局了,而是通过调整子视图的加入顺序以及设置子视图浮动的方向就能达到不规则布局的效果。就以具体的App例子来说,我们发现淘宝和天猫应用的首页中的每个条目就是以一种看似不规则且无规律的方式依次从上而下的排列,同样类似ZAKER新闻每页中的每个新闻条目也是一种不规则无规律的方式而排列。如果真要实现这种完全不规则排列并且充满整个页面而没有多余的空闲缝隙区域的算法也未尝没有,我们可以通过集装箱算法来实现(集装箱算法的目标就是让某个具有固定尺寸的集装箱,尽可能多的放入一些不规则尺寸的货物,且要尽可能保证最大的容积利用率)。集装箱算法其实是一个动态规划的问题,在实践中我们不大可能利用这种方法来实现完全不规则的布局。而是另辟蹊径采用静态模板的方式来实现。也就是说可以预先提供N种不规则布局的模板,然后每个页面或者每个区域从这N个不同的模板中随机的选取其中几个进行组合排列来达到那种看似不规则的排列效果。而所有的这些都可以通过浮动技术来简单解决。下面就是通过浮动技术来实现的模拟效果图:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/472fea8d0f9af24a089ac3e076b5cc05.jpg"></p> <p><strong>MyFloatLayout的方法和属性的介绍</strong></p> <p>说了这么多浮动布局的实现原理以及布局的机制,那我们怎么来使用和定义浮动布局呢?要实现和使用浮动布局,我们必须要使用浮动布局MyFloatLayout这个类。MyFloatLayout类是 <a href="/misc/goto?guid=4959670321253598717" rel="nofollow,noindex">MyLayout</a> (Swift版本: <a href="/misc/goto?guid=4959733826373016162" rel="nofollow,noindex">TangramKit</a> )布局体系中的一员,它是一个容器视图类,主要目的是为里面的子视图提供浮动的能力,从而实现里面的子视图的不规则的排列。这个类的定义如下:</p> <pre> <code class="language-objectivec">@interface MyFloatLayout : MyBaseLayout -(id)initWithOrientation:(MyLayoutViewOrientation)orientation; +(id)floatLayoutWithOrientation:(MyLayoutViewOrientation)orientation; @property(nonatomic,assign) MyLayoutViewOrientation orientation; @property(nonatomic,assign) MyMarginGravity gravity; @property(nonatomic, assign) CGFloat subviewVertMargin; @property(nonatomic, assign) CGFloat subviewHorzMargin; @property(nonatomic, assign) CGFloat subviewMargin; @property(nonatomic, assign) BOOL noBoundaryLimit; -(void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace; @end</code></pre> <p>从类的初始化方法中我们可以看出,在创建一个浮动布局时必须要指定一个方向,这个方向指的是最终子视图的布局走向,因为左右浮动布局我们是先按左右浮动最终是一个从上到下的排列过程,而上下浮动布局则是先按上下浮动最终则是从左到右排列,因此当我们指定orientation的值为MyLayoutViewOrientation_Vert表示的是创建一个左右浮动的浮动布局,而当值设定为MyLayoutViewOrientation_Horz时则表示建立的是一个上下浮动的浮动布局,系统默认建立的是左右浮动的浮动布局。而且后续还可以通过orientation属性来进行动态的修改浮动的方向。当浮动布局的浮动方向指定后,接下来我们就要为某个要添加到浮动布局的子视图指定浮动方向属性、清除浮动属性、以及比重了,这些则可以通过视图的扩展分类:</p> <pre> <code class="language-objectivec">@interface UIView(MyFloatLayoutExt) @property(nonatomic,assign,getter=isReverseFloat) BOOL reverseFloat; @property(nonatomic,assign) BOOL clearFloat; @property(nonatomic, assign) CGFloat weight; @end</code></pre> <p>来设置。 在默认情况下当我们建立的是一个左右浮动布局时,我们添加到布局里面的所有子视图默认都是向左浮动的,而当建立的是一个上下浮动布局时,我们添加到布局里面的所有子视图默认都是向上浮动的,因此当需要改动子视图浮动的方向则可以设置属性reverseFloat来实现,这个属性是一个BOOL类型的值,当设置为YES时表示按默认方向相反的方向浮动,也就是在左右浮动布局中,如果设置某个子视图的reverseFloat为YES的话则表示子视图是向右浮动,而对于上下浮动布局来数则表示是向下浮动。视图的扩展属性clearFloat也是一个BOOL类型,表示是否清除浮动,默认值是NO表示不清除浮动,当某个子视图需要有清除浮动的效果时,请将这个属性设置为YES。最后一个视图的扩展属性weight表示视图的宽度或者高度的比重,这个值默认值是0,表示不是按比重来指定宽度,这时候你在添加子视图时必须明确的指定宽度或者高度,而当设置为非0时则不需要为子视图指定宽度和高度,而由布局系统来自动帮你计算。这里的weight的设置范围是:0<=weight<=1.</p> <p><strong>浮动布局的包裹属性</strong></p> <p>上面分别的介绍了浮动布局的建立,以及子视图的扩展的属性设置来实现视图在浮动布局中的浮动方式、是否清除浮动、以及比重的设置方法。另外对于浮动布局来说,因为是从MyBaseLayout中派生的,因此浮动布局同样支持wrapContentWidth以及wrapContentHeight属性的设置的,也就是浮动布局的宽高可以由子视图来决定的,需要明确的是一般情况下我们对于左右浮动布局来说,只需要设置wrapContentHeight。当然你也可以设置wrapContentWidth(设置这个属性的前提是布局视图里面有一个子视图特别的宽,或者将布局视图的noBoundaryLimit属性设置为YES);同样对于上下浮动布局来说,只需要设置wrapContentWidth。当然你也可以设置wrapContentHeight(设置这个属性的前提是布局视图里面有一个子视图特别的高,或者将布局视图的noBoundaryLimit属性设置为YES)。</p> <p><strong>浮动布局的停靠属性</strong></p> <p>我们看到浮动布局视图里面还有一个gravity属性,这个属性在左右浮动布局视图中可以用来设置所有子视图的整体的上,中,下三种停靠模式,而在上下浮动布局视图中则可以用来设置所有子视图的整体的左,中,右三种停靠模式。</p> <p>浮动布局的边界限制属性</p> <p>我们再来考察布局视图的noBoundaryLimit属性。在上下浮动布局中,我们要求布局视图要有明确的高度,这样当某个子视图的高度不能被容纳后将会自动的换列。但是在实际中我们可能有一些场景是子视图并不想受到布局视图高度边界的约束,一直往上浮动,而直到某个子视图设置了clearFloat才换列显示,同时布局视图的高度也是包裹的(wrapContentHeight设置为YES的)。比如下面这个场景:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/eb7b91d5f7f3f4f271fd27076f95a26c.png"></p> <p>这个场景是一个上下浮动布局来实现的,但是这里面要求布局视图的高度是动态的(默认上下浮动布局的高度必须明确),因为右边区域的高度是不确定的,因为D子视图的高度会影响整个布局视图的高度。所以为了用上下浮动布局来实现这个场景,我们就必须将布局视图的高度设置为wrapContentHeight,并且将布局视图的noBoundaryLimit属性设置为YES以便表明由我们来控制换列,而不是由布局视图的高度来控制换列。这样在实现时我们就只需要如下伪代码就可以了:</p> <pre> <code class="language-objectivec">MyFloatLayout *S = [MyFloatLayout floatLayoutWithOrientation:MyLayoutViewOrientation_Horz]; S.wrapContentHeight = YES; S.wrapContentWidth = YES; S.noBoundaryLimit = YES; [S addSubview:A]; B.clearFloat = YES; //手动控制换列 [S addSubview:B]; [S addSubview:C]; [S addSubview:D]; [S addSubview:E]; [S addSubview:F]; [S addSubview:G];</code></pre> <p><strong>智能边界线</strong></p> <p>为了说明智能边界线我们先来看这两个界面:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/74230bf80a180c40e911a6a66fd97843.png"></p> <p>上面的两个界面是仿淘宝和天猫首页以及ZAKER新闻的界面,我们来观察其中的每个区块之间的边界线。我们发现处在边缘部分是没有显示边界线的,而边界线只会显示在区块交界的地方显示一条边界线。在一般情况下,不规则边界线的显示我们有可能需要UI人员提供图片来完成,或者不提供图片我们在编程时也需要进行条件的判断以便决定是否需要在特定的位置绘制边界线,显然这样做将会增加我们的代码量。因此为了解决这个问题,我们的布局系统提供了边界线以及智能边界线的功能。如果您用了MyBaseLayout派生的6大布局的话,我们是可以通过基类提供的四个属性:</p> <pre> <code class="language-objectivec">@property(nonatomic, strong) MyBorderLineDraw *leftBorderLine; @property(nonatomic, strong) MyBorderLineDraw *rightBorderLine; @property(nonatomic, strong) MyBorderLineDraw *topBorderLine; @property(nonatomic, strong) MyBorderLineDraw *bottomBorderLine;</code></pre> <p>来为布局视图指定要显示的四边的边界线,我们可以支持设置边界线的颜色,粗细,缩进,以及点线等功能,这样我们就不再需要单独的提供边界线的切图了。要想看边界线的例子,可以查看LLTest4ViewController和AllTest3ViewController这两个DEMO的介绍。即便如此,对于上面的特殊情况,我们还需要进行编程以及条件判断来完成边界线的指定,因此为了解决这个问题,我们在布局中新增加了一个智能边界线的属性:</p> <pre> <code class="language-objectivec">@property(nonatomic, strong) MyBorderLineDraw *IntelligentBorderLine; @property(nonatomic, assign) BOOL notUseIntelligentBorderLine;</code></pre> <p>如果为某个布局视图设置了智能边界线的值,那么这个布局视图里面的子布局视图将会根据视图之间的关系而自动智能的生成边界线。这里需要强调的是只有布局视图里面的子布局视图才会生成智能的边界线,对于布局视图里面的非布局子视图是不会生成边界线的。而如果我们的某个布局视图里面的子布局视图不想使用智能的边界线,而是仍想自己手动设定,那么只需要将自己的notUseIntelligentBorderLine设置为YES即可,他表示不使用父布局提供的智能边界线功能。在当前的布局库版本中,我们只有线性布局、浮动布局、表格布局、流式布局支持智能边界线的设定,而框架布局、相对布局则是不支持的。正是因为布局系统里面提供的智能边界线的功能,就使得我们在设定布局视图之间的边界线时非常的简单,只需要一句话就能搞定。</p> <p><strong>浮动布局的实践</strong></p> <p>上面就是我们要介绍的关于浮动布局的全部的东西,接下来我们将借着DEMO中的代码来具体的介绍我们如何使用浮动布局来实现上面的功能的。在介绍之前,我们这里说明一下,我们仍然是可以用子视图的扩展属性myLeftMargin,myRightMargin,myTopMargin,myBottomMargin这4个属性来指定视图之间的间距的。同时我们还支持子视图的宽度扩展属性widthDime的值可以设置为一个具体值,也可以等于布局视图的宽度,以及前面已经布好局的子视图的宽度,甚至还可以等于子视图的高度。</p> <p>因为所有的关于浮动布局的代码我们都能在DEMO中找到,因为我们只介绍几个例子,其他的大家可以自己去研究,我们看下面的图:</p> <p style="text-align:center"><img src="https://simg.open-open.com/show/546ce6e0d12e1a1b7e87e528e013fd2f.jpg"></p> <p>我们看到上面的界面左上角的区块的高度为180,而其余的区块都是90,并且每个区块的宽度都是屏幕的一半。为了容纳上面的界面我们需要先建立左右浮动的浮动布局:</p> <pre> <code class="language-objectivec">CGFloat itemHeight = 90; MyFloatLayout *layout1 = [MyFloatLayout floatLayoutWithOrientation:MyLayoutViewOrientation_Vert]; layout1.backgroundColor = [UIColor whiteColor]; layout1.wrapContentHeight = YES; layout1.IntelligentBorderLine = [[MyBorderLineDraw alloc] initWithColor:[UIColor lightGrayColor]];</code></pre> <p>我们设定的layout1的高度由子视图决定,并且设置了智能边界线。接下来我们只需要每个区块按顺序依次添加进来即可。且从上面的区域中我们可以看出一共有3种不同的类型的区块分别是A,B, C三种区块,这三种区块其实也是用MyFloatLayout来实现的:</p> <ul> <li> <p>A区块我们也可以用一个浮动布局来实现,我们只需要建立一个上下浮动布局,标题,小图都默认往上浮动。剩下的大图宽度和父布局宽度相等,并且设置weight=1就可以了,这部分代码的具体实现就在FOLTest2ViewController中的createItemLayout1_1的方法中实现。</p> </li> <li> <p>B区块我们也可以用浮动布局来实现,我们只需要建立一个左右浮动布局,大图片优先向右浮动,高度和父布局高度相等,接下来主标题向左浮动,并且weight=1表示占用剩余的宽度;副标题也是向左浮动,并且设置清除浮动属性,同时设置weight=1表示占用剩余的宽度;最后的小图也是设置为左浮动,并且设置清除浮动属性。这部分代码的具体实现在FOLTest2ViewController中的createItemLayout1_3的方法中实现。</p> </li> <li> <p>C区块我们也可以用浮动布局来实现,我们只需要建立一个左右浮动布局,主标题部分向左浮动,并且宽度和父布局宽度相等,付标题部分向左浮动,并且宽度和父布局宽度相等,而图片部分则向右浮动即可。</p> </li> </ul> <p>最后我们可以依次建立A,B,C三种区块然后依次的加入到layout1中去,加入时只需要设置A的高度为180,而宽度则是layout1的一半即可,而其他两种则高度设置为80,且宽度设置为layout1的一半即可。</p> <p><strong>小结</strong></p> <p>浮动布局是一种功能非常强大的布局体系,从某种程度上来他甚至是相对布局的替代方案,而且要比相对布局要简单,因为里面的子视图之间是不需要设置约束和依赖关系的,单单凭借加入到布局视图的顺序,以及自身的宽高就能完成我们想要的功能。而且其提供的能力甚至要比CSS中的浮动属性更加强大。而我们在进行WEB前端开发时很多的界面布局其实都是通过CSS的浮动属性来完成的。因此我们也可以借助浮动布局来我们各种复杂的界面布局,而且浮动布局也能方便的实现线性布局以及流式布局的能力。如果您想了解更多的关于流式布局的功能请您访问我的github站点来了解更多:</p> <p>OC版本: <a href="/misc/goto?guid=4959670321253598717" rel="nofollow,noindex">https://github.com/youngsoft/MyLinearLayout</a></p> <p>Swift版本: <a href="/misc/goto?guid=4959733826373016162" rel="nofollow,noindex">https://github.com/youngsoft/TangramKit</a></p> <p>如果您觉得库还不错,记得给我star哦。</p> <p> </p> <p>来自:http://www.cocoachina.com/ios/20170109/18530.html</p> <p> </p>