diff --git a/CHANGELOG.md b/CHANGELOG.md index 2584802..2942825 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,195 @@ # 版本变更日志 **MyLayout**的所有版本的变更日志都将会在这里记录. +## [V1.9.9](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.9.9)(2022/12/22) +### Add +1. 扩充了栅格布局MyGridLayout中行列的自适应尺寸计算的能力。在老版本中如果某个格子的行高为自适应,那么这个格子的子格子只能是行。新版本中自适应行高的格子可以拆分为列子格子,同样自适应列宽的格子可以拆分为行子格子。 +2. 视图添加了新的方法:`setLayoutSizeClass:(MySizeClass)sizeClass withValue:(id)value`. 此方法可以用来设置视图整体的布局特性。 +3. 弹性布局MyFlexLayout添加了`AddItem`方法以及添加了宏`MyFlexNew`。通过这两个方法可以使用类似于描述布局的语法糖来构建UI界面。 +4. 添加了对滚动时停靠能力的Demo支持,详细见[LLTest8ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/LLTest8ViewController.m) +5. 添加了一个MyLayout和UICollectionView相结合实现双排瀑布流的Demo:[AllTest9ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/AllTest9ViewController.m) + + +### Update +1. 将布局视图以及视图的一些属性中的 IBInspectable 宏去掉,减少对XIB以及Storyboard的入侵性。 +2. 格式化一些代码布局,以及重命名一些内部的方法。 + +### Fixed +1. 修复布局视图的点击事件有可能不生效的BUG。 +2. 修复了visibility属性可能在不同sizeClass下不生效的BUG。 + + +## [V1.9.8](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.9.8)(2020/08/04) +### Update +1. 重写布局库的内部实现,代码更具可读性,计算性能得到进一步的提升,重写了相对布局的内部实现。 +2. 将布局的属性leftPadding,rightPadding,topPadding,bottomPadding,leadingPadding,trailingPadding改名为paddingLeft,paddingRight,paddingTop,paddingBottom,paddingLeading,paddingTrailing。原来的属性名保留但是会提示过期。 +3. 修复布局套布局时进行尺寸评估可能会不准确的BUG。[issue#116](https://github.com/youngsoft/MyLinearLayout/issues/116) +4. 修复弹性布局MyFlexLayout在单行时子视图不被拉伸的BUG。 +5. 修复相对布局子视图整体水平居中时又设置了padding时的BUG。[issue#120](https://github.com/youngsoft/MyLinearLayout/issues/120) +6. 优化横竖屏不同布局展示的支持能力,由原先的设备方向变为优先考虑接口方向。[issue#123](https://github.com/youngsoft/MyLinearLayout/issues/123) +7. 修复相对布局中子视图的高度等于布局高度,宽度等于自身高度的BUG。[issue#129](https://github.com/youngsoft/MyLinearLayout/issues/129) + +## [V1.9.1](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.9.1)(2019/12/23) +### Added +1. 流式布局和线性布局添加对停靠属性`gravity`的两个新枚举值: `MyGravity_Vert_Among、MyGravity_Horz_Among`的支持。这两个枚举值可以实现间距的等分拉伸功能。between、around、among这三个拉伸的区别在于和父视图之间的间距上。 +2. 流式布局添加最后一行的停靠策略属性`lastlineGravityPolicy`,用来指定流式布局最后行的停靠策略。 +3. 流式布局、浮动布局、线性布局添加对行内浮动间距的支持。我们可使用`setSubviewsSize`函数中的centered参数来指定每一行的头尾视图和父布局视图之间的间距也是浮动的。 + + +--- +## [V1.9.0](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.9.0)(2019/11/30) + +### Added + +1. 添加新的布局:**MyFlexLayout**。 MyFlexLayout布局是从MyFlowLayout派生的布局,它兼容flexbox的所有特性,以及提供了和flexbox相同的设置语法。您可以通过MyFlexLayout中提供的flex属性并用链式语法的形式来设置布局特性,同时还可以通过视图提供的扩展分类flexItem属性来设置布局内子视图的布局特性。具体可以参考:[FLXTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/FLXTest1ViewController.m) 中的介绍和使用方法 + +2. 添加尺寸对象MyLayoutSize和位置对象MyLayoutPos的最值约束的设置。您可以将视图的尺寸设置为某一个尺寸集合中的最大值或者最小值。在相对布局中您也可以将视图的位置设置为某一个位置集合中的最大值和最小值。比如A的宽度是B,C,D三个视图宽度中的最大值,则可以设置为:`A.widthSize.equalTo(@[B.widthSize,C.widthSize,D.widthSize].myMaxSize)` 具体实例请参考:[RLTest6ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/RLTest6ViewController.m) + +3. 添加对视图尺寸和位置进行压缩的设置。在线性布局和流式布局以及框架布局中当子视图的尺寸和间距总和大于布局视图的尺寸时,您可以通过MyLayoutSize以及MyLayoutPos中提供的新属性**shrink**来设置压缩的比重,默认情况下压缩比重的值是0表示不压缩,比重越大压缩度越大。具体的使用方法可以参考:[AllTest7ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/AllTest7ViewController.m) 中的第12个demo。 + + +4. 针对线性布局、流式布局中的gravity属性添加了Around和Stretch两种停靠和对齐能力,其中的Around和Between的区别是前者的首行和尾行的拉伸间距是其他行之间间距的一半,而后者则首行和尾行的间距不会被拉伸。Stretch和Fill的区别是前者对明确设置了尺寸的视图不进行尺寸拉伸,而后者则是对所有视图都进行尺寸拉伸 + + +5. 添加和抽象对拖动的支持,系统提供了一个新的类**MyLayoutDragger**,你可以用这个类中提供的方法就可以很轻易的实现对布局视图中的子视图进行拖放操作。具体使用方法可以参考:[FLLTest3ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/FLLTest3ViewController.m) + +6. 添加对iOS13的黑白模式适配的支持,解决了边界线的黑白模式适配的问题。 + +7. 流式布局MyFlowLayout中添加单独设置行内停靠对齐的支持属性:**lineGravity**,这个属性是一个block类型的对象,我们可以借助这个属性来实现对布局内每行的停靠对齐操作。具体实例可以参考:[FLLTest4ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/FLLTest4ViewController.m) + + + +8. 流式布局MyFlowLayout添加属性**isFlex**,这个属性默认是NO,当设置为YES时则表明让流式布局来兼容flexbox的特性。具体兼容可以参考属性中的注释。 + + +9. 流式布局和浮动布局添加了行内的基线对齐的功能。您可以将gravity或者arrangedGravity的值设置为MyGravity_Baseline。 + +10. 浮动布局添加对尺寸自适应的支持。您可以在垂直浮动布局中将布局视图的宽度设置为自适应,同时可以在水平浮动布局中将布局视图的高度设置为自适应。 + +11. 在线性布局、流式布局、浮动布局中实现了可以在尺寸自适应的模式下gravity的停靠属性生效的能力。解决了[issue#102](https://github.com/youngsoft/MyLinearLayout/issues/102) +12. 在线性布局、流式布局、浮动布局中实现了设置尺寸自适应并且设置了最大最小值约束的情况下子视图被正确布局的功能。 +13. 扩充的流式布局中的pagedCount的能力,由原先的必须是在滚动视图的子视图下生效改为可以在任意时刻生效。 +14. 改变对布局动画方法:`layoutAnimationWithDuration:`的实现逻辑,并新增加了动画的选项以及动画完成后的处理block机制,具体使用请参见方法:`layoutAnimationWithDuration:options:completion:` + +### Update +1. 更新了对尺寸自适应属性的设置方式。不再建议使用wrapContentWidth进行宽度自适应设置,而是改为`myWidth = MyLayoutSize.wrap`或者`widthSize.equalTo(@(MyLayoutSize.wrap))`进行宽度自适应设置。不再建议使用wrapContentHeight进行高度尺寸自适应设置,而是改为`myHeight= MyLayoutSize.wrap`或者`heightSize.equalTo(@(MyLayoutSize.wrap))`进行高度自适应设置。 对于宽度是否自适应的判断则可以用`myWidth == MyLayoutSize.wrap`或widthSize.iswrap进行判断,高度也是一样的。 +2. 在框架布局、线性布局、相对布局中子视图如果同时设置了上下边距或者左右边距,并且同时设置了高度或者宽度约束时则子视图的高度或者宽度不再是由布局视图决定而是由自身设置的高度或者宽度约束决定。解决了[issue#100](https://github.com/youngsoft/MyLinearLayout/issues/100) + +2. 将浮动布局的noBoundaryLimit属性设置为过期,这个属性将不再生效,而是直接将布局的宽度或者高度设置为自适应即可实现相同的功能。 + +3. 线性布局中的shrinkType属性用于控制所有子视图的压缩特性,如果子视图的尺寸设置shrink属性值则以子视图的设置优先,布局视图的shrinkType将不再起作用。 +4. 修改布局视图监听子布局视图frame和center变化的实现,由原来的KVO形式变化为直接调用。这样就可以减少布局计算的耗时处理。 +5. 重构了流式布局、相对布局,加快了布局计算时间,尤其是相对布局的重构。 + + + +### Fixed + +1. 修复在使用sizeclass时对位置对象进行克隆的一个BUG。这个BUG可能导致在不同sizeclass下位置设置被覆盖的问题。 +2. 修复相对布局下一组视图在高度或者宽度自适应下无法垂直居中或者水平居中的[BUG#103](https://github.com/youngsoft/MyLinearLayout/issues/103) +3. 修复框架布局MyFrameLayout设置高度自适应时,当其中的子视图隐藏或者恢复隐藏时自适应高度无法更新的[BUG#99](https://github.com/youngsoft/MyLinearLayout/issues/99) +4. 修复了流式布局在设置了子视图水平间距和垂直间距的情况下右对齐和底部对齐的BUG。 + + + + + --- +## [V1.8.0](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.8.0)(2019/06/25) + +#### Fixed + +1. 重构线性布局,精简实现逻辑和代码。 +2. 针对各种布局下的子视图的尺寸约束能力做了扩展。原先版本中只有相对布局中的子视图可以设置尺寸依赖任意其他子视图,而其他布局中的子视图只有有限的约束依赖能力。新版本中这些约束依赖得到增强,几乎所有布局下的子视图都可以支持自身宽度等于自身高度,以及尺寸依赖其他子视图的情况。[issue#94](https://github.com/youngsoft/MyLinearLayout/issues/94) +3. 修复当将布局视图作为UIScrollView下的容器视图同时UIScrollView支持放大缩小能力时,在屏幕旋转时可能会产生的BUG。[issue#93](https://github.com/youngsoft/MyLinearLayout/issues/93) +4. 修复了布局视图的尺寸自适应能力和AutoLayout结合后,布局视图添加删除子视图后要手动代码进行布局视图尺寸自适应的刷新问题。[issue#95](https://github.com/youngsoft/MyLinearLayout/issues/95) +5. 修复相对布局子视图隐藏时,其他依赖这个子视图的子视图的位置和尺寸不正确更新的问题。 +6. 修复UIImageView的宽度固定高度自适应的问题。 +7. 支持通过对isMyLayouting进行KVO监听来实现布局的通知处理机制,我们可以通过KVO布局视图的isMyLayouting属性来或者布局完成后子视图的frame以及布局视图的frame值了。 +8. 下列方法或属性名被列为过期,并推荐使用新的方法名和属性名: + +``` ++[MyBaseLayout myUpArabicUI:(BOOL)isArabicUI inWindow:(UIWindow *)window] ==> +[MyBaseLayout updateRTL:(BOOL)isRTL inWindow:(UIWindow *)window] + +-[UIView myVisibility] ==> -[UIView visibility] + +-[UIView myAlignment] == > -[UIView alignment] + +``` + + +## [V1.7.0](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.7.0)(2019/05/16) +#### Added +1. 添加了对MyLayout的尺寸自适应和AutoLayout结合的能力。AutoLayout能使用和UILabel一样的MyLayout布局视图中的高度和宽度自适应的设置。具体需求见[issue#79](https://github.com/youngsoft/MyLinearLayout/issues/79)。这个问题的解决得到简化处理。新版本的能力让UITableViewCell的高度自适应的能力得到简化。具体的代码演示见[AllTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/AllTest1ViewController.m),以及[AllTest12ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/AllTest12ViewController.m)中的详细介绍。 + + 2. 添加了对RTL设置的即时生效的能力,您可通过方法`+[MyBaseLayout myUpArabicUI:(BOOL)isArabicUI inWindow:(UIWindow *)window +`来设置,具体的功能实现要感谢[LAnqxpp](https://github.com/LAnqxpp)的贡献。 +#### Fixed +1. 修复了在完成布局后再对子视图设置约束时子视图约束不起作用的[BUG#90](https://github.com/youngsoft/MyLinearLayout/issues/90)。当代码中不对布局视图中的子视图设置任何约束时就会出现这个问题,这个问题涉及到所有布局。 +2. 修复了相对布局可能会产生尺寸无限大的问题,尤其是当相对布局的高度为自适应,并且相对布局中同样存在着具有高度自适应的子布局视图的情况。 + + +## [V1.6.1](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.6.1)(2018/09/29) +#### Fixed +1. 修复对所有iPhoneX系列的设备的布局视图在设置padding的值为safeAreaMargin时的BUG。 +2. 添加对Application Extension上使用布局库视图的支持。[BUG#81](https://github.com/youngsoft/MyLinearLayout/issues/81) + + +## [V1.6.0](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.6.0)(2018/08/04) + +#### Added +1. 添加布局属性`layoutTransform`,用来实现对布局内子视图的整体位置变换,可以通过这个属性来实现一般常见的平移,缩放,水平翻转,垂直翻转等功能。具体的DEMO在新增加的[AllTest11ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/AllTest11ViewController.m)中可以查看。 +2. 为流式布局`MyFlowLayout`支持子视图固定尺寸并且间距动态拉伸调整的能力,你可以通过设置流式布局的方法:`setSubviewsSize:minSpace:maxSpace:`来实现,这个方法原先只支持内容约束流式布局,现在新版本对数量约束流式布局也同样支持了。具体的DEMO在新增加的[FLLTest8ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/FLLTest8ViewController.m)中可以查看。 + +#### Fixed +1. 修复了UILabel等控件的尺寸设置了wrapContentHeight或者wrapContentWidth为YES并且同时又设置了最大最小尺寸时,在相对布局内进行尺寸计算内可能会出现的问题。 +2. 修复了通过Carthage引入库时出现符合以及文件无法找到的问题。 + + +## [V1.5.3](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.5.3)(2018/05/11) + +#### Added +1.添加了流式布局`MyFlowLayout`对瀑布流的支持,主要是数量约束流式布局来实现,通过设置`autoArrange`为YES或者设置`arrangedGravity`属性为`MyGravity_Horz_Between或者MyGravity_Vert_Between`来实现两种不同策略的瀑布流模式,瀑布流模式其实就是一种紧凑的流式布局排列方式。具体的DEMO在新增加的[FLLTest7ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/FLLTest7ViewController.m)中可以查看。 + +#### Fixed +1. 修复了流式布局`MyFlowLayout`中的arrangedGravity属性设置和子视图的myAlignment属性同时设置时有可能对齐方式不正确的问题,以及gravity属性设置后停靠有可能不正确的问题。 +2. 优化和修复了对UIScrollView+布局视图时,设置UIScrollView的高度或者宽度由布局视图的尺寸进行自适应的问题。新版本中UIScrollView的尺寸可以依赖于布局视图的尺寸,同时布局视图的最大最小尺寸可以设置为UIScrollView的尺寸。具体例子参考:[FLLTest7ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/FLLTest7ViewController.m) +3. 添加了对`myTop,myBottom,myLeft,myRight,myLeading,myTrailing,mySize,myWidth,myHeight,myMargin, myHorzMargin, myVertMargin`的读取使用的告警处理!一般情况下这些属性只能用于设置具体的数值,而不能用来获取某个尺寸,get方法也并不代表着视图的约束尺寸,这个点切记,切记。 +4. 添加了对布局视图的`cacheEstimatedRect`属性的使用限制说明,这个属性只能用于那些需要高度自适应的UITableViewCell的根布局视图中使用,其他地方如果使用则有可能会出现计算不正确的问题。 +5. 恢复了对XCODE8.0以前的编译器版本使用MyLayout的支持。 + + + +## [V1.5.2](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.5.2)(2018/05/06) + +#### Fixed +1. 修复路径布局`MyPathLayout`中的方法`getSubviewPathPoint`可能产生数组越界的问题。 +2. 修复路径布局中的`MyPathSpace`的初始化方法和蓝牙框架一起使用时可能会出现编译时错误的问题。[BUG#70](https://github.com/youngsoft/MyLinearLayout/issues/70) +3. 修复表格布局`MyTableLayout`的行高是MyLayoutSize.wrap时,并且又设置了智能边界线时,列子视图的边界线显示不完整的问题。[BUG#71](https://github.com/youngsoft/MyLinearLayout/issues/71) +4. 修复了一些编译和分析有可能会产生报警的代码。 +5. 提供了新的关于表格布局的DEMO:[TLTest4ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/TLTest4ViewController.m) + + +## [V1.5.1](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.5.1)(2018/04/23) + +#### Added +1. 添加了对浮动布局MyFloatLayout中的子视图的行或者列内对齐方式的设置,您可以借助子视图的myAlignment属性来设置行或者列内的对齐方式,具体的DEMO请参考:[FOLTest7ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/FOLTest7ViewController.m) 中的介绍。 +2. 将所有设置为过期的方法删除。 +3. 修正一些编译上的告警以及一些和Masonry公用时的一些问题。 +4. 修复方向旋转时有可能不调用`rotationToDeviceOrientationBlock`的问题。 +5. 修正一些注释上的提示。 +6. 将网友提交合并的代码一起发布新的版本。 + + ## [V1.5.0](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.5.0)(2017/10/9) #### Added -1. 添加新的布局种类:**栅格布局(MyGridLayout)**。栅格布局是一种将矩形区域划分为多个子矩形区域,并将这个划分一直持续下去的机制,然后再将子视图填充到对应的栅格区域里面的一种布局视图。栅格布局特别适合于动态布局,布局样式可以从服务器动态下发,并且可以用JSON格式的语言来描述这种布局结构,具体请参考新增加的栅格布局和对应的DEMO。以及对应的说明文档:[栅格布局介绍](http://bicyclering.com/2017/09/01/IOS-UIViewLayout-MyLinearLayout/#more) +1. 添加新的布局种类:**栅格布局(MyGridLayout)**。栅格布局是一种将矩形区域划分为多个子矩形区域,并将这个划分一直持续下去的机制,然后再将子视图填充到对应的栅格区域里面的一种布局视图。栅格布局特别适合于动态布局,布局样式可以从服务器动态下发,并且可以用JSON格式的语言来描述这种布局结构,具体请参考新增加的栅格布局和对应的DEMO。以及对应的说明文档:[栅格布局介绍](http://bicyclering.com/2017/09/01/IOS-UIViewLayout-%E5%B8%83%E5%B1%80-MyLinearLayout/#more) 2. 添加了对 **基线对齐baseline**的支持[issue:#43](https://github.com/youngsoft/MyLinearLayout/issues/43),目前只有**水平线性布局(MyLinearLayout)**和**相对布局(MyRelativeLayout)**支持基线对齐。 - 1. 在**MyGravity**中添加了`MyGravity_Vert_Baseline`的枚举定义来支持线性布局的基线对齐,并且在线性布局中添加了一个属性:`baselineBaseView`来指定某个基线基准视图。同时在布局视图的gravity属性中支持对`MyGravity_Vert_Baseline`的设置。具体例子参考:[LLTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayout/LLTest1ViewController.m) + 1. 在**MyGravity**中添加了`MyGravity_Vert_Baseline`的枚举定义来支持线性布局的基线对齐,并且在线性布局中添加了一个属性:`baselineBaseView`来指定某个基线基准视图。同时在布局视图的gravity属性中支持对`MyGravity_Vert_Baseline`的设置。具体例子参考:[LLTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/LLTest1ViewController.m) - 2. 在UIView的扩展属性中增加了一个扩展属性:`baselinePos`。你可以在相对布局中的子视图使用这个属性来进行基线对齐的设置。具体例子请参考:[RLTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayout/RLTest1ViewController.m) + 2. 在UIView的扩展属性中增加了一个扩展属性:`baselinePos`。你可以在相对布局中的子视图使用这个属性来进行基线对齐的设置。具体例子请参考:[RLTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/RLTest1ViewController.m) 3. 添加对Apple TV_OS 的支持,您可以用MyLayout来开发apple TV方面的应用。 4. **MyLayoutPos**中增加了一个特殊的值`safeAreaMargin`用来支持对iOS11应用的适配。 @@ -34,7 +213,7 @@ #### Added 1. 添加适配iOS11的能力以及**iPhoneX**的方法。基本不需要改动当前代码。如果需要改动只需要设置根布局视图的一些属性即可。 1. 新增布局视图属性:`insetsPaddingFromSafeArea`用来设置在哪个方向缩进对应方向的安全区域。 - 2. 新增布局视图属性:`insetLandscapeFringePadding`用来设置当支持横屏时,并且insetsPaddingFromSafeArea设置为左右缩进时,是否只缩进有刘海的那一边。这个属性默认设置为NO,表示两边都缩进。您可以在特殊需要时将这个属性设置为YES表示只缩进刘海那一边,非刘海那一边则不缩进。具体参考使用DEMO:[LLTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayout/LLTest1ViewController.m) + 2. 新增布局视图属性:`insetLandscapeFringePadding`用来设置当支持横屏时,并且insetsPaddingFromSafeArea设置为左右缩进时,是否只缩进有刘海的那一边。这个属性默认设置为NO,表示两边都缩进。您可以在特殊需要时将这个属性设置为YES表示只缩进刘海那一边,非刘海那一边则不缩进。具体参考使用DEMO:[LLTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/LLTest1ViewController.m) #### Changed 1. 布局方法:`estimateLayoutRect`设置为过期,请用`sizeThatFits`方法来代替,方法换名字的原因是iOS的默认sizeThatFits本来就是用来进行尺寸评估的,所以没有必要用新方法。 @@ -45,7 +224,7 @@ ## [V1.4.2](https://github.com/youngsoft/MyLinearLayout/releases/tag/1.4.2)(2017/8/31) #### Added -1. 表格布局MyTableLayout添加了`addRow:colCount:`方法,目的是为了支持那些列数固定并且宽度固定的需求,具体例子见DEMO:[TLTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayout/TLTest1ViewController.m)中的第五行的代码。 +1. 表格布局MyTableLayout添加了`addRow:colCount:`方法,目的是为了支持那些列数固定并且宽度固定的需求,具体例子见DEMO:[TLTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/TLTest1ViewController.m)中的第五行的代码。 2. 添加了布局视图的高度等于非布局父视图宽度以及布局视图宽度等于非布局父视图高度的支持,目的是为了支持对布局视图进行旋转`transform`的支持。 3. 添加了框架布局MyFrameLayout中子视图的高度等于另外视图宽度以及宽度等于另外视图高度的支持。 4. 下一个版本将会有重大功能的添加:栅格布局的支持、基线对齐的支持、均分的再次优化等等功能,敬请期待吧。。 @@ -104,7 +283,7 @@ 3. 将原先线性布局、流式布局、浮动布局中的`gravity`属性提升到了布局基类中,目前线性布局、流式布局、浮动布局、和框架布局都支持`gravity`的设置。 4. 进一步优化了布局视图的性能,表现为对KVO监听的延迟处理和优化。 5. 进一步优化了布局视图的内存占用尺寸,将布局视图中对触摸事件处理的变量变为按需要才创建,以及布局视图的边界线对象也改为了按需要才建立,这两部分按需处理机制将有效的减少了布局视图的内存占用。 -6. 为了更进一步的优化和简化MyLayout对UITableviewCell高度自适应的处理,新版本中对实现的解决方案进行优化处理,具体详情请参考:[AllTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayout/AllTest1ViewController.m)中的介绍 +6. 为了更进一步的优化和简化MyLayout对UITableviewCell高度自适应的处理,新版本中对实现的解决方案进行优化处理,具体详情请参考:[AllTest1ViewController](https://github.com/youngsoft/MyLinearLayout/blob/master/MyLayoutDemo/AllTest1ViewController.m)中的介绍 7. 优化工程目录结构。 diff --git a/MyLayout.podspec b/MyLayout.podspec index c820741..6af7531 100644 --- a/MyLayout.podspec +++ b/MyLayout.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "MyLayout" - s.version = "1.5.0" + s.version = "1.9.10" s.summary = "MyLayout is an iOS UI framework integrates the functions with Android,AutoLayout,SizeClass,HTML CSS float and flexbox,UIView UITableView." s.description = <<-DESC @@ -61,10 +61,10 @@ Pod::Spec.new do |s| # # s.platform = :ios - s.platform = :ios, "7.0" + s.platform = :ios, "9.0" # When using multiple platforms - s.ios.deployment_target = "7.0" + s.ios.deployment_target = "9.0" # s.osx.deployment_target = "10.7" # s.watchos.deployment_target = "2.0" @@ -90,6 +90,7 @@ Pod::Spec.new do |s| #s.exclude_files = "Classes/Exclude" s.public_header_files = "MyLayout/Lib/*.h" + s.private_header_files = "MyLayout/Lib/{*Inner.h,MyLayoutDelegate.h,MyLayoutMath.h,MyGridNode.h,MyLayoutSizeClass.h}" end diff --git a/MyLayout.xcodeproj/project.pbxproj b/MyLayout.xcodeproj/project.pbxproj index 5ea6427..ffe2c57 100644 --- a/MyLayout.xcodeproj/project.pbxproj +++ b/MyLayout.xcodeproj/project.pbxproj @@ -25,14 +25,14 @@ 182225D31E77E5930081AA4B /* MyTableLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225C31E77E5930081AA4B /* MyTableLayout.m */; }; 182225D41E77E5930081AA4B /* MyDimeScale.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225C41E77E5930081AA4B /* MyDimeScale.m */; }; 182225D51E77E5930081AA4B /* MyLayoutSize.m in Sources */ = {isa = PBXBuildFile; fileRef = 182225C51E77E5930081AA4B /* MyLayoutSize.m */; }; - 182DB6421EF7876200E2D65D /* MyGridLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 182DB6401EF7876200E2D65D /* MyGridLayout.h */; }; + 182DB6421EF7876200E2D65D /* MyGridLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 182DB6401EF7876200E2D65D /* MyGridLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; 182DB6431EF7876200E2D65D /* MyGridLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 182DB6411EF7876200E2D65D /* MyGridLayout.m */; }; 182DB6441EF7876200E2D65D /* MyGridLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 182DB6411EF7876200E2D65D /* MyGridLayout.m */; }; 1897384F1E1631E3004F80D6 /* MyRelativeLayoutTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 1897384E1E1631E3004F80D6 /* MyRelativeLayoutTestCase.m */; }; 189738511E16330B004F80D6 /* MyLayoutTestCaseBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 189738501E16330B004F80D6 /* MyLayoutTestCaseBase.m */; }; 18B152341DEDDE5500AD7A1C /* MyLayoutUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B152331DEDDE5500AD7A1C /* MyLayoutUITests.m */; }; 18B2CCE81EAF5066001AE0E1 /* MyLinearLayoutTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B2CCE71EAF5066001AE0E1 /* MyLinearLayoutTestCase.m */; }; - 18B2CCEA1EB046AC001AE0E1 /* MyFloatLayoutTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B2CCE91EB046AB001AE0E1 /* MyFloatLayoutTestCase.m */; }; + 18B2CCEA1EB046AC001AE0E1 /* MyFlowLayoutTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B2CCE91EB046AB001AE0E1 /* MyFlowLayoutTestCase.m */; }; 18C15AEC1EDF13D700AADEAC /* MyLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 18D684001C4F421400A48BB4 /* MyLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18C15AED1EDF13DE00AADEAC /* MyFlowLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225AB1E77E5930081AA4B /* MyFlowLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18C15AEE1EDF13E500AADEAC /* MyLinearLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225AC1E77E5930081AA4B /* MyLinearLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -44,7 +44,7 @@ 18C15AF41EDF140400AADEAC /* MyBaseLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225AF1E77E5930081AA4B /* MyBaseLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18C15AF51EDF140D00AADEAC /* MyLayoutPos.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225B01E77E5930081AA4B /* MyLayoutPos.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18C15AF61EDF141000AADEAC /* MyLayoutSize.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225BC1E77E5930081AA4B /* MyLayoutSize.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 18C15AF71EDF141600AADEAC /* MyLayoutSizeClass.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225C01E77E5930081AA4B /* MyLayoutSizeClass.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 18C15AF71EDF141600AADEAC /* MyLayoutSizeClass.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225C01E77E5930081AA4B /* MyLayoutSizeClass.h */; }; 18C15AF81EDF141A00AADEAC /* MyMaker.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225C21E77E5930081AA4B /* MyMaker.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18C15AF91EDF142000AADEAC /* MyDimeScale.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225C71E77E5930081AA4B /* MyDimeScale.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18C15AFA1EDF142300AADEAC /* MyLayoutDef.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225B31E77E5930081AA4B /* MyLayoutDef.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -73,8 +73,8 @@ 18D3C9281EDF07A700D3DE43 /* RLTest1ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3C91F1EDF07A700D3DE43 /* RLTest1ViewController.m */; }; 18D3C9291EDF07A700D3DE43 /* RLTest2ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3C9211EDF07A700D3DE43 /* RLTest2ViewController.m */; }; 18D3C92A1EDF07A700D3DE43 /* RLTest3ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3C9231EDF07A700D3DE43 /* RLTest3ViewController.m */; }; - 18D3C92B1EDF07A700D3DE43 /* RLTest4ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3C9251EDF07A700D3DE43 /* RLTest4ViewController.m */; }; - 18D3C92C1EDF07A700D3DE43 /* RLTest5ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3C9271EDF07A700D3DE43 /* RLTest5ViewController.m */; }; + 18D3C92B1EDF07A700D3DE43 /* LLTest8ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3C9251EDF07A700D3DE43 /* LLTest8ViewController.m */; }; + 18D3C92C1EDF07A700D3DE43 /* RLTest4ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3C9271EDF07A700D3DE43 /* RLTest4ViewController.m */; }; 18D3C9391EDF07C000D3DE43 /* FLLTest1ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3C92E1EDF07C000D3DE43 /* FLLTest1ViewController.m */; }; 18D3C93A1EDF07C000D3DE43 /* FLLTest2ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3C9301EDF07C000D3DE43 /* FLLTest2ViewController.m */; }; 18D3C93B1EDF07C000D3DE43 /* FLLTest3ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D3C9321EDF07C000D3DE43 /* FLLTest3ViewController.m */; }; @@ -138,7 +138,9 @@ 18D684591C4F423400A48BB4 /* MyLayoutTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 18D684551C4F423400A48BB4 /* MyLayoutTests.m */; }; 200A26D91FA8A90300B06E7B /* AllTest9ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 200A26D81FA8A90300B06E7B /* AllTest9ViewController.m */; }; 200A26DC1FA96B2400B06E7B /* AllTest9CollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 200A26DB1FA96B2400B06E7B /* AllTest9CollectionViewCell.m */; }; - 205642861F4CFF9B00E8BDDE /* MyBorderline.h in Headers */ = {isa = PBXBuildFile; fileRef = 205642841F4CFF9B00E8BDDE /* MyBorderline.h */; }; + 202A5A0D2950A1E60076DD27 /* AllTest9WaterFlowLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 202A5A0C2950A1E60076DD27 /* AllTest9WaterFlowLayout.m */; }; + 204DC321209D5A8700F6CB57 /* TLTest4ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 204DC320209D5A8600F6CB57 /* TLTest4ViewController.m */; }; + 205642861F4CFF9B00E8BDDE /* MyBorderline.h in Headers */ = {isa = PBXBuildFile; fileRef = 205642841F4CFF9B00E8BDDE /* MyBorderline.h */; settings = {ATTRIBUTES = (Public, ); }; }; 205642871F4CFF9B00E8BDDE /* MyBorderline.m in Sources */ = {isa = PBXBuildFile; fileRef = 205642851F4CFF9B00E8BDDE /* MyBorderline.m */; }; 205642881F4CFFB600E8BDDE /* MyBorderline.m in Sources */ = {isa = PBXBuildFile; fileRef = 205642851F4CFF9B00E8BDDE /* MyBorderline.m */; }; 2056430A1F4E53CD00E8BDDE /* MyGridNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 205643081F4E53CD00E8BDDE /* MyGridNode.h */; }; @@ -148,19 +150,43 @@ 205643161F55A03000E8BDDE /* MyLayoutMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 205643141F55A03000E8BDDE /* MyLayoutMath.h */; }; 205643171F55A03000E8BDDE /* MyLayoutMath.m in Sources */ = {isa = PBXBuildFile; fileRef = 205643151F55A03000E8BDDE /* MyLayoutMath.m */; }; 205643181F55A16400E8BDDE /* MyLayoutMath.m in Sources */ = {isa = PBXBuildFile; fileRef = 205643151F55A03000E8BDDE /* MyLayoutMath.m */; }; + 205AE95E22BC4C2900BE8E76 /* MyFloatLayoutTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 205AE95D22BC4C2900BE8E76 /* MyFloatLayoutTestCase.m */; }; 205CFA8E1F49BD1C00355489 /* GLTest1ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 205CFA8D1F49BD1C00355489 /* GLTest1ViewController.m */; }; + 205E0C5F20B90347001A8B99 /* MyGrid.h in Headers */ = {isa = PBXBuildFile; fileRef = 205642891F4D013D00E8BDDE /* MyGrid.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 205E0C6020B907CC001A8B99 /* MyLayoutPosInner.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225C61E77E5930081AA4B /* MyLayoutPosInner.h */; }; + 205E0C6120B907D1001A8B99 /* MyLayoutSizeInner.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225C81E77E5930081AA4B /* MyLayoutSizeInner.h */; }; + 205E0C6220B907DB001A8B99 /* MyLayoutInner.h in Headers */ = {isa = PBXBuildFile; fileRef = 182225B81E77E5930081AA4B /* MyLayoutInner.h */; }; + 205E0C6320B907EC001A8B99 /* MyLayoutDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 208157C01F5A886300E945B6 /* MyLayoutDelegate.h */; }; 205F9EBA1F7AA5710034732C /* gl.png in Resources */ = {isa = PBXBuildFile; fileRef = 205F9EB91F7AA5710034732C /* gl.png */; }; 205F9EBC1F7AA6510034732C /* all.png in Resources */ = {isa = PBXBuildFile; fileRef = 205F9EBB1F7AA6510034732C /* all.png */; }; + 2075CC3220A0B2A400BA6F65 /* FLLTest7ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2075CC3120A0B2A400BA6F65 /* FLLTest7ViewController.m */; }; 208157BD1F586AA700E945B6 /* DetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 208157BC1F586AA700E945B6 /* DetailViewController.m */; }; - 208157C21F5A886300E945B6 /* MyLayoutDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 208157C01F5A886300E945B6 /* MyLayoutDelegate.h */; }; 208157C31F5A886400E945B6 /* MyLayoutDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 208157C11F5A886300E945B6 /* MyLayoutDelegate.m */; }; 208157C41F5A886400E945B6 /* MyLayoutDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 208157C11F5A886300E945B6 /* MyLayoutDelegate.m */; }; + 2094619E2341FD6F005F5C8E /* AllTestExampleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2094619D2341FD6F005F5C8E /* AllTestExampleViewController.m */; }; + 20A45EC52318FCA50026A18C /* MyFlexLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 20DF42402318F4EA0078CAA0 /* MyFlexLayout.m */; }; 20B46A8D1F8A79C300826372 /* layoutdemo8.gif in Resources */ = {isa = PBXBuildFile; fileRef = 20B46A8C1F8A79C300826372 /* layoutdemo8.gif */; }; + 20DCF333208C3BFB007A879B /* FOLTest7ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 20DCF332208C3BFB007A879B /* FOLTest7ViewController.m */; }; + 20DF423E2318A6000078CAA0 /* FLXTest1ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 20DF423D2318A6000078CAA0 /* FLXTest1ViewController.m */; }; + 20DF42412318F4EA0078CAA0 /* MyFlexLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 20DF423F2318F4EA0078CAA0 /* MyFlexLayout.h */; }; + 20DF42422318F4EA0078CAA0 /* MyFlexLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 20DF42402318F4EA0078CAA0 /* MyFlexLayout.m */; }; + 20F0DFFD2111749A00CFCE8C /* AllTest11ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 20F0DFFC2111749A00CFCE8C /* AllTest11ViewController.m */; }; + 20F0E00021120A8D00CFCE8C /* FLLTest8ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 20F0DFFF21120A8D00CFCE8C /* FLLTest8ViewController.m */; }; 20F783561F60399700BE5B31 /* GLTest3ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 20F783551F60399700BE5B31 /* GLTest3ViewController.m */; }; 4411977B1F6AE78E00C22557 /* GridLayoutDemo5.json in Resources */ = {isa = PBXBuildFile; fileRef = 4411977A1F6AE78E00C22557 /* GridLayoutDemo5.json */; }; 444B71911F6A3E3F00331872 /* GLTest5ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 444B71901F6A3E3F00331872 /* GLTest5ViewController.m */; }; 44EBDA8C1F619A6300B47CBD /* GLTest4ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 44EBDA8B1F619A6300B47CBD /* GLTest4ViewController.m */; }; 44EBDA8E1F619C9000B47CBD /* GridLayoutDemo4.json in Resources */ = {isa = PBXBuildFile; fileRef = 44EBDA8D1F619C9000B47CBD /* GridLayoutDemo4.json */; }; + 467E63CC228C4D6F0065D080 /* AllTest12ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 467E63CB228C4D6F0065D080 /* AllTest12ViewController.m */; }; + 467E63CF228CDA660065D080 /* AllTest1TableViewCellForAutoLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 467E63CE228CDA660065D080 /* AllTest1TableViewCellForAutoLayout.m */; }; + 4689881D23430A8400BFE829 /* MyPathLayoutTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 4689881C23430A8400BFE829 /* MyPathLayoutTestCase.m */; }; + 4689881F23430AA000BFE829 /* MyGridLayoutTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 4689881E23430AA000BFE829 /* MyGridLayoutTestCase.m */; }; + 4689882223449E8300BFE829 /* RLTest5ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4689882123449E8300BFE829 /* RLTest5ViewController.m */; }; + 46898847236733AC00BFE829 /* FLLTest9ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 46898846236733AC00BFE829 /* FLLTest9ViewController.m */; }; + 6740E1F820A52D9E00AFBC5A /* AllTest10ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6740E1F620A52D9E00AFBC5A /* AllTest10ViewController.m */; }; + 6740E1FB20A52DA900AFBC5A /* AllTest10Model.m in Sources */ = {isa = PBXBuildFile; fileRef = 6740E1F920A52DA900AFBC5A /* AllTest10Model.m */; }; + 6740E1FE20A52DB100AFBC5A /* AllTest10HeaderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6740E1FC20A52DB000AFBC5A /* AllTest10HeaderView.m */; }; + 6740E20120A52DC100AFBC5A /* AllTest10Cell.m in Sources */ = {isa = PBXBuildFile; fileRef = 6740E1FF20A52DC100AFBC5A /* AllTest10Cell.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -195,13 +221,13 @@ 182225AE1E77E5930081AA4B /* MyTableLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = MyTableLayout.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 182225AF1E77E5930081AA4B /* MyBaseLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = MyBaseLayout.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 182225B01E77E5930081AA4B /* MyLayoutPos.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyLayoutPos.h; sourceTree = ""; }; - 182225B11E77E5930081AA4B /* MyBaseLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MyBaseLayout.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 182225B11E77E5930081AA4B /* MyBaseLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MyBaseLayout.m; sourceTree = ""; }; 182225B21E77E5930081AA4B /* MyLinearLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MyLinearLayout.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 182225B31E77E5930081AA4B /* MyLayoutDef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyLayoutDef.h; sourceTree = ""; }; 182225B41E77E5930081AA4B /* MyFlowLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MyFlowLayout.m; sourceTree = ""; }; 182225B51E77E5930081AA4B /* MyRelativeLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MyRelativeLayout.m; sourceTree = ""; }; 182225B61E77E5930081AA4B /* MyPathLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MyPathLayout.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - 182225B71E77E5930081AA4B /* MyFrameLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MyFrameLayout.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 182225B71E77E5930081AA4B /* MyFrameLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MyFrameLayout.m; sourceTree = ""; }; 182225B81E77E5930081AA4B /* MyLayoutInner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyLayoutInner.h; sourceTree = ""; }; 182225B91E77E5930081AA4B /* MyFloatLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MyFloatLayout.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 182225BA1E77E5930081AA4B /* MyFrameLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyFrameLayout.h; sourceTree = ""; }; @@ -230,7 +256,7 @@ 18B152331DEDDE5500AD7A1C /* MyLayoutUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyLayoutUITests.m; sourceTree = ""; }; 18B152351DEDDE5500AD7A1C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 18B2CCE71EAF5066001AE0E1 /* MyLinearLayoutTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyLinearLayoutTestCase.m; sourceTree = ""; }; - 18B2CCE91EB046AB001AE0E1 /* MyFloatLayoutTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyFloatLayoutTestCase.m; sourceTree = ""; }; + 18B2CCE91EB046AB001AE0E1 /* MyFlowLayoutTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyFlowLayoutTestCase.m; sourceTree = ""; }; 18C15AEA1EDF133200AADEAC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 18D3C9031EDF074900D3DE43 /* LLTest1ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLTest1ViewController.h; sourceTree = ""; }; 18D3C9041EDF074900D3DE43 /* LLTest1ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLTest1ViewController.m; sourceTree = ""; }; @@ -256,10 +282,10 @@ 18D3C9211EDF07A700D3DE43 /* RLTest2ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLTest2ViewController.m; sourceTree = ""; }; 18D3C9221EDF07A700D3DE43 /* RLTest3ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLTest3ViewController.h; sourceTree = ""; }; 18D3C9231EDF07A700D3DE43 /* RLTest3ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLTest3ViewController.m; sourceTree = ""; }; - 18D3C9241EDF07A700D3DE43 /* RLTest4ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLTest4ViewController.h; sourceTree = ""; }; - 18D3C9251EDF07A700D3DE43 /* RLTest4ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLTest4ViewController.m; sourceTree = ""; }; - 18D3C9261EDF07A700D3DE43 /* RLTest5ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLTest5ViewController.h; sourceTree = ""; }; - 18D3C9271EDF07A700D3DE43 /* RLTest5ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLTest5ViewController.m; sourceTree = ""; }; + 18D3C9241EDF07A700D3DE43 /* LLTest8ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLTest8ViewController.h; sourceTree = ""; }; + 18D3C9251EDF07A700D3DE43 /* LLTest8ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LLTest8ViewController.m; sourceTree = ""; }; + 18D3C9261EDF07A700D3DE43 /* RLTest4ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLTest4ViewController.h; sourceTree = ""; }; + 18D3C9271EDF07A700D3DE43 /* RLTest4ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLTest4ViewController.m; sourceTree = ""; }; 18D3C92D1EDF07C000D3DE43 /* FLLTest1ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLLTest1ViewController.h; sourceTree = ""; }; 18D3C92E1EDF07C000D3DE43 /* FLLTest1ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLLTest1ViewController.m; sourceTree = ""; }; 18D3C92F1EDF07C000D3DE43 /* FLLTest2ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLLTest2ViewController.h; sourceTree = ""; }; @@ -365,6 +391,11 @@ 200A26D81FA8A90300B06E7B /* AllTest9ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AllTest9ViewController.m; sourceTree = ""; }; 200A26DA1FA96B2400B06E7B /* AllTest9CollectionViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AllTest9CollectionViewCell.h; sourceTree = ""; }; 200A26DB1FA96B2400B06E7B /* AllTest9CollectionViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AllTest9CollectionViewCell.m; sourceTree = ""; }; + 202A5A0B2950A1E60076DD27 /* AllTest9WaterFlowLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AllTest9WaterFlowLayout.h; sourceTree = ""; }; + 202A5A0C2950A1E60076DD27 /* AllTest9WaterFlowLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AllTest9WaterFlowLayout.m; sourceTree = ""; }; + 2043D5DB20995DF800276C68 /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; }; + 204DC31F209D5A7C00F6CB57 /* TLTest4ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TLTest4ViewController.h; sourceTree = ""; }; + 204DC320209D5A8600F6CB57 /* TLTest4ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TLTest4ViewController.m; sourceTree = ""; }; 205642841F4CFF9B00E8BDDE /* MyBorderline.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyBorderline.h; sourceTree = ""; }; 205642851F4CFF9B00E8BDDE /* MyBorderline.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyBorderline.m; sourceTree = ""; }; 205642891F4D013D00E8BDDE /* MyGrid.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyGrid.h; sourceTree = ""; }; @@ -374,15 +405,30 @@ 205643121F51C7EB00E8BDDE /* GLTest2ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = GLTest2ViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 205643141F55A03000E8BDDE /* MyLayoutMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyLayoutMath.h; sourceTree = ""; }; 205643151F55A03000E8BDDE /* MyLayoutMath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyLayoutMath.m; sourceTree = ""; }; + 205AE95D22BC4C2900BE8E76 /* MyFloatLayoutTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyFloatLayoutTestCase.m; sourceTree = ""; }; 205CFA8C1F49BD1C00355489 /* GLTest1ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLTest1ViewController.h; sourceTree = ""; }; 205CFA8D1F49BD1C00355489 /* GLTest1ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = GLTest1ViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 205F9EB91F7AA5710034732C /* gl.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = gl.png; sourceTree = ""; }; 205F9EBB1F7AA6510034732C /* all.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = all.png; sourceTree = ""; }; + 2075CC3020A0B29700BA6F65 /* FLLTest7ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLLTest7ViewController.h; sourceTree = ""; }; + 2075CC3120A0B2A400BA6F65 /* FLLTest7ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLLTest7ViewController.m; sourceTree = ""; }; 208157BB1F586A9900E945B6 /* DetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DetailViewController.h; sourceTree = ""; }; 208157BC1F586AA700E945B6 /* DetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DetailViewController.m; sourceTree = ""; }; 208157C01F5A886300E945B6 /* MyLayoutDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyLayoutDelegate.h; sourceTree = ""; }; 208157C11F5A886300E945B6 /* MyLayoutDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyLayoutDelegate.m; sourceTree = ""; }; + 2094619C2341FD5E005F5C8E /* AllTestExampleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllTestExampleViewController.h; sourceTree = ""; }; + 2094619D2341FD6F005F5C8E /* AllTestExampleViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AllTestExampleViewController.m; sourceTree = ""; }; 20B46A8C1F8A79C300826372 /* layoutdemo8.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = layoutdemo8.gif; sourceTree = ""; }; + 20DCF331208C3BEF007A879B /* FOLTest7ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FOLTest7ViewController.h; sourceTree = ""; }; + 20DCF332208C3BFB007A879B /* FOLTest7ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FOLTest7ViewController.m; sourceTree = ""; }; + 20DF423C2318A5EE0078CAA0 /* FLXTest1ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLXTest1ViewController.h; sourceTree = ""; }; + 20DF423D2318A6000078CAA0 /* FLXTest1ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLXTest1ViewController.m; sourceTree = ""; }; + 20DF423F2318F4EA0078CAA0 /* MyFlexLayout.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyFlexLayout.h; sourceTree = ""; }; + 20DF42402318F4EA0078CAA0 /* MyFlexLayout.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyFlexLayout.m; sourceTree = ""; }; + 20F0DFFB2111749A00CFCE8C /* AllTest11ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AllTest11ViewController.h; sourceTree = ""; }; + 20F0DFFC2111749A00CFCE8C /* AllTest11ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AllTest11ViewController.m; sourceTree = ""; }; + 20F0DFFE21120A8D00CFCE8C /* FLLTest8ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FLLTest8ViewController.h; sourceTree = ""; }; + 20F0DFFF21120A8D00CFCE8C /* FLLTest8ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FLLTest8ViewController.m; sourceTree = ""; }; 20F783541F60398C00BE5B31 /* GLTest3ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLTest3ViewController.h; sourceTree = ""; }; 20F783551F60399700BE5B31 /* GLTest3ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GLTest3ViewController.m; sourceTree = ""; }; 4411977A1F6AE78E00C22557 /* GridLayoutDemo5.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = GridLayoutDemo5.json; sourceTree = ""; }; @@ -391,6 +437,24 @@ 44EBDA8A1F619A6300B47CBD /* GLTest4ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLTest4ViewController.h; sourceTree = ""; }; 44EBDA8B1F619A6300B47CBD /* GLTest4ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GLTest4ViewController.m; sourceTree = ""; }; 44EBDA8D1F619C9000B47CBD /* GridLayoutDemo4.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = GridLayoutDemo4.json; sourceTree = ""; }; + 467E63CA228C4D630065D080 /* AllTest12ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllTest12ViewController.h; sourceTree = ""; }; + 467E63CB228C4D6F0065D080 /* AllTest12ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AllTest12ViewController.m; sourceTree = ""; }; + 467E63CD228CDA550065D080 /* AllTest1TableViewCellForAutoLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllTest1TableViewCellForAutoLayout.h; sourceTree = ""; }; + 467E63CE228CDA660065D080 /* AllTest1TableViewCellForAutoLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AllTest1TableViewCellForAutoLayout.m; sourceTree = ""; }; + 4689881C23430A8400BFE829 /* MyPathLayoutTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyPathLayoutTestCase.m; sourceTree = ""; }; + 4689881E23430AA000BFE829 /* MyGridLayoutTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyGridLayoutTestCase.m; sourceTree = ""; }; + 4689882023449E7900BFE829 /* RLTest5ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLTest5ViewController.h; sourceTree = ""; }; + 4689882123449E8300BFE829 /* RLTest5ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RLTest5ViewController.m; sourceTree = ""; }; + 468988452367339C00BFE829 /* FLLTest9ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FLLTest9ViewController.h; sourceTree = ""; }; + 46898846236733AC00BFE829 /* FLLTest9ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FLLTest9ViewController.m; sourceTree = ""; }; + 6740E1F520A52D9D00AFBC5A /* AllTest10ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllTest10ViewController.h; sourceTree = ""; }; + 6740E1F620A52D9E00AFBC5A /* AllTest10ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AllTest10ViewController.m; sourceTree = ""; }; + 6740E1F920A52DA900AFBC5A /* AllTest10Model.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AllTest10Model.m; sourceTree = ""; }; + 6740E1FA20A52DA900AFBC5A /* AllTest10Model.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllTest10Model.h; sourceTree = ""; }; + 6740E1FC20A52DB000AFBC5A /* AllTest10HeaderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AllTest10HeaderView.m; sourceTree = ""; }; + 6740E1FD20A52DB000AFBC5A /* AllTest10HeaderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllTest10HeaderView.h; sourceTree = ""; }; + 6740E1FF20A52DC100AFBC5A /* AllTest10Cell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AllTest10Cell.m; sourceTree = ""; }; + 6740E20020A52DC100AFBC5A /* AllTest10Cell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AllTest10Cell.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -433,6 +497,7 @@ 18D684531C4F423400A48BB4 /* MyLayoutTests */, 18B152321DEDDE5500AD7A1C /* MyLayoutUITests */, 1840828E1B2C46E8003F378B /* Products */, + 2043D5DA20995DF800276C68 /* Frameworks */, ); sourceTree = ""; }; @@ -477,6 +542,7 @@ 18D3C8FC1EDF053000D3DE43 /* RelativeLayoutDemo */, 18D3C9001EDF056900D3DE43 /* TableLayoutDemo */, 18D3C8FD1EDF053D00D3DE43 /* FlowLayoutDemo */, + 20BA39E0234DD6BF003F22D2 /* FlexLayoutDemo */, 18D3C8FF1EDF055C00D3DE43 /* FloatLayoutDemo */, 205CFA8B1F49BCAF00355489 /* GridLayoutDemo */, 18D3C8FE1EDF055000D3DE43 /* PathLayoutDemo */, @@ -502,6 +568,8 @@ 18D3C90E1EDF074900D3DE43 /* LLTest6ViewController.m */, 18D3C90F1EDF074900D3DE43 /* LLTest7ViewController.h */, 18D3C9101EDF074900D3DE43 /* LLTest7ViewController.m */, + 18D3C9241EDF07A700D3DE43 /* LLTest8ViewController.h */, + 18D3C9251EDF07A700D3DE43 /* LLTest8ViewController.m */, ); name = LinearLayoutDemo; sourceTree = ""; @@ -526,10 +594,10 @@ 18D3C9211EDF07A700D3DE43 /* RLTest2ViewController.m */, 18D3C9221EDF07A700D3DE43 /* RLTest3ViewController.h */, 18D3C9231EDF07A700D3DE43 /* RLTest3ViewController.m */, - 18D3C9241EDF07A700D3DE43 /* RLTest4ViewController.h */, - 18D3C9251EDF07A700D3DE43 /* RLTest4ViewController.m */, - 18D3C9261EDF07A700D3DE43 /* RLTest5ViewController.h */, - 18D3C9271EDF07A700D3DE43 /* RLTest5ViewController.m */, + 18D3C9261EDF07A700D3DE43 /* RLTest4ViewController.h */, + 18D3C9271EDF07A700D3DE43 /* RLTest4ViewController.m */, + 4689882023449E7900BFE829 /* RLTest5ViewController.h */, + 4689882123449E8300BFE829 /* RLTest5ViewController.m */, ); name = RelativeLayoutDemo; sourceTree = ""; @@ -550,6 +618,12 @@ 18D3C9361EDF07C000D3DE43 /* FLLTest5ViewController.m */, 18D3C9371EDF07C000D3DE43 /* FLLTest6ViewController.h */, 18D3C9381EDF07C000D3DE43 /* FLLTest6ViewController.m */, + 2075CC3020A0B29700BA6F65 /* FLLTest7ViewController.h */, + 2075CC3120A0B2A400BA6F65 /* FLLTest7ViewController.m */, + 20F0DFFE21120A8D00CFCE8C /* FLLTest8ViewController.h */, + 20F0DFFF21120A8D00CFCE8C /* FLLTest8ViewController.m */, + 468988452367339C00BFE829 /* FLLTest9ViewController.h */, + 46898846236733AC00BFE829 /* FLLTest9ViewController.m */, ); name = FlowLayoutDemo; sourceTree = ""; @@ -587,6 +661,8 @@ 18D3C9571EDF07F800D3DE43 /* FOLTest5ViewController.m */, 18D3C9581EDF07F800D3DE43 /* FOLTest6ViewController.h */, 18D3C9591EDF07F800D3DE43 /* FOLTest6ViewController.m */, + 20DCF331208C3BEF007A879B /* FOLTest7ViewController.h */, + 20DCF332208C3BFB007A879B /* FOLTest7ViewController.m */, ); name = FloatLayoutDemo; sourceTree = ""; @@ -600,6 +676,8 @@ 18D3C9631EDF080800D3DE43 /* TLTest2ViewController.m */, 18D3C9641EDF080800D3DE43 /* TLTest3ViewController.h */, 18D3C9651EDF080800D3DE43 /* TLTest3ViewController.m */, + 204DC31F209D5A7C00F6CB57 /* TLTest4ViewController.h */, + 204DC320209D5A8600F6CB57 /* TLTest4ViewController.m */, ); name = TableLayoutDemo; sourceTree = ""; @@ -625,6 +703,14 @@ 18D3C9841EDF084000D3DE43 /* AllTest8ViewController.m */, 200A26D71FA8A90300B06E7B /* AllTest9ViewController.h */, 200A26D81FA8A90300B06E7B /* AllTest9ViewController.m */, + 6740E1F520A52D9D00AFBC5A /* AllTest10ViewController.h */, + 6740E1F620A52D9E00AFBC5A /* AllTest10ViewController.m */, + 20F0DFFB2111749A00CFCE8C /* AllTest11ViewController.h */, + 20F0DFFC2111749A00CFCE8C /* AllTest11ViewController.m */, + 467E63CA228C4D630065D080 /* AllTest12ViewController.h */, + 467E63CB228C4D6F0065D080 /* AllTest12ViewController.m */, + 2094619C2341FD5E005F5C8E /* AllTestExampleViewController.h */, + 2094619D2341FD6F005F5C8E /* AllTestExampleViewController.m */, 18D3C9021EDF068200D3DE43 /* AllTestModel&View */, ); name = IntegratedDemo; @@ -635,6 +721,8 @@ children = ( 18D3C9691EDF082800D3DE43 /* AllTest1TableViewCell.h */, 18D3C96A1EDF082800D3DE43 /* AllTest1TableViewCell.m */, + 467E63CD228CDA550065D080 /* AllTest1TableViewCellForAutoLayout.h */, + 467E63CE228CDA660065D080 /* AllTest1TableViewCellForAutoLayout.m */, 18D3C96B1EDF082800D3DE43 /* AllTest1TableViewHeaderFooterView.h */, 18D3C96C1EDF082800D3DE43 /* AllTest1TableViewHeaderFooterView.m */, 18D3C96D1EDF082800D3DE43 /* AllTest2TableViewCell.h */, @@ -643,6 +731,14 @@ 18D3C9701EDF082800D3DE43 /* AllTestDataModel.m */, 200A26DA1FA96B2400B06E7B /* AllTest9CollectionViewCell.h */, 200A26DB1FA96B2400B06E7B /* AllTest9CollectionViewCell.m */, + 202A5A0B2950A1E60076DD27 /* AllTest9WaterFlowLayout.h */, + 202A5A0C2950A1E60076DD27 /* AllTest9WaterFlowLayout.m */, + 6740E1FA20A52DA900AFBC5A /* AllTest10Model.h */, + 6740E1F920A52DA900AFBC5A /* AllTest10Model.m */, + 6740E1FD20A52DB000AFBC5A /* AllTest10HeaderView.h */, + 6740E1FC20A52DB000AFBC5A /* AllTest10HeaderView.m */, + 6740E20020A52DC100AFBC5A /* AllTest10Cell.h */, + 6740E1FF20A52DC100AFBC5A /* AllTest10Cell.m */, ); name = "AllTestModel&View"; sourceTree = ""; @@ -722,6 +818,8 @@ 18D684001C4F421400A48BB4 /* MyLayout.h */, 182225AB1E77E5930081AA4B /* MyFlowLayout.h */, 182225B41E77E5930081AA4B /* MyFlowLayout.m */, + 20DF423F2318F4EA0078CAA0 /* MyFlexLayout.h */, + 20DF42402318F4EA0078CAA0 /* MyFlexLayout.m */, 182225AC1E77E5930081AA4B /* MyLinearLayout.h */, 182225B21E77E5930081AA4B /* MyLinearLayout.m */, 182225AD1E77E5930081AA4B /* MyFloatLayout.h */, @@ -776,12 +874,23 @@ 180BD8A01EA9EF9A00C6980B /* MyFrameLayoutTestCase.m */, 181836941EAB1CB10031F337 /* MyNoLayoutSuperviewTestCase.m */, 18B2CCE71EAF5066001AE0E1 /* MyLinearLayoutTestCase.m */, - 18B2CCE91EB046AB001AE0E1 /* MyFloatLayoutTestCase.m */, + 18B2CCE91EB046AB001AE0E1 /* MyFlowLayoutTestCase.m */, + 205AE95D22BC4C2900BE8E76 /* MyFloatLayoutTestCase.m */, + 4689881C23430A8400BFE829 /* MyPathLayoutTestCase.m */, + 4689881E23430AA000BFE829 /* MyGridLayoutTestCase.m */, 1810AC211EB966D30022CBFF /* MyLayoutPerformanceTestCase.m */, ); path = MyLayoutTests; sourceTree = ""; }; + 2043D5DA20995DF800276C68 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 2043D5DB20995DF800276C68 /* CoreBluetooth.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 205CFA8B1F49BCAF00355489 /* GridLayoutDemo */ = { isa = PBXGroup; children = ( @@ -799,6 +908,15 @@ name = GridLayoutDemo; sourceTree = ""; }; + 20BA39E0234DD6BF003F22D2 /* FlexLayoutDemo */ = { + isa = PBXGroup; + children = ( + 20DF423C2318A5EE0078CAA0 /* FLXTest1ViewController.h */, + 20DF423D2318A6000078CAA0 /* FLXTest1ViewController.m */, + ); + name = FlexLayoutDemo; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -809,7 +927,6 @@ 2056430A1F4E53CD00E8BDDE /* MyGridNode.h in Headers */, 18C15AEC1EDF13D700AADEAC /* MyLayout.h in Headers */, 18C15AED1EDF13DE00AADEAC /* MyFlowLayout.h in Headers */, - 208157C21F5A886300E945B6 /* MyLayoutDelegate.h in Headers */, 18C15AEE1EDF13E500AADEAC /* MyLinearLayout.h in Headers */, 18C15AEF1EDF13E900AADEAC /* MyFloatLayout.h in Headers */, 18C15AF01EDF13EF00AADEAC /* MyTableLayout.h in Headers */, @@ -820,12 +937,18 @@ 18C15AF41EDF140400AADEAC /* MyBaseLayout.h in Headers */, 18C15AF51EDF140D00AADEAC /* MyLayoutPos.h in Headers */, 18C15AF61EDF141000AADEAC /* MyLayoutSize.h in Headers */, - 18C15AF71EDF141600AADEAC /* MyLayoutSizeClass.h in Headers */, + 205E0C5F20B90347001A8B99 /* MyGrid.h in Headers */, + 182DB6421EF7876200E2D65D /* MyGridLayout.h in Headers */, + 205642861F4CFF9B00E8BDDE /* MyBorderline.h in Headers */, + 20DF42412318F4EA0078CAA0 /* MyFlexLayout.h in Headers */, 18C15AF81EDF141A00AADEAC /* MyMaker.h in Headers */, + 18C15AF71EDF141600AADEAC /* MyLayoutSizeClass.h in Headers */, 18C15AF91EDF142000AADEAC /* MyDimeScale.h in Headers */, - 205642861F4CFF9B00E8BDDE /* MyBorderline.h in Headers */, - 182DB6421EF7876200E2D65D /* MyGridLayout.h in Headers */, 18C15AFA1EDF142300AADEAC /* MyLayoutDef.h in Headers */, + 205E0C6020B907CC001A8B99 /* MyLayoutPosInner.h in Headers */, + 205E0C6120B907D1001A8B99 /* MyLayoutSizeInner.h in Headers */, + 205E0C6220B907DB001A8B99 /* MyLayoutInner.h in Headers */, + 205E0C6320B907EC001A8B99 /* MyLayoutDelegate.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -909,11 +1032,12 @@ 184082851B2C46E8003F378B /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0820; + LastUpgradeCheck = 1250; ORGANIZATIONNAME = YoungSoft; TargetAttributes = { 181A10B41EDF12CF00996203 = { CreatedOnToolsVersion = 8.3.2; + DevelopmentTeam = BGQ8WB468K; ProvisioningStyle = Automatic; }; 1840828C1B2C46E8003F378B = { @@ -923,11 +1047,12 @@ }; 184082A51B2C46E8003F378B = { CreatedOnToolsVersion = 6.3.2; - DevelopmentTeam = Q5MFLFRY64; + DevelopmentTeam = BGQ8WB468K; TestTargetID = 1840828C1B2C46E8003F378B; }; 18B152301DEDDE5500AD7A1C = { CreatedOnToolsVersion = 8.1; + DevelopmentTeam = BGQ8WB468K; ProvisioningStyle = Automatic; TestTargetID = 1840828C1B2C46E8003F378B; }; @@ -935,11 +1060,12 @@ }; buildConfigurationList = 184082881B2C46E8003F378B /* Build configuration list for PBXProject "MyLayout" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( Base, "zh-Hans", + en, ); mainGroup = 184082841B2C46E8003F378B; productRefGroup = 1840828E1B2C46E8003F378B /* Products */; @@ -1027,6 +1153,7 @@ 2056430B1F4E53CD00E8BDDE /* MyGridNode.m in Sources */, 205643171F55A03000E8BDDE /* MyLayoutMath.m in Sources */, 18C15AFF1EDF144B00AADEAC /* MyRelativeLayout.m in Sources */, + 20DF42422318F4EA0078CAA0 /* MyFlexLayout.m in Sources */, 182DB6441EF7876200E2D65D /* MyGridLayout.m in Sources */, 18C15B001EDF144B00AADEAC /* MyPathLayout.m in Sources */, 18C15B011EDF144B00AADEAC /* MyFrameLayout.m in Sources */, @@ -1060,26 +1187,36 @@ 18D3C9731EDF082800D3DE43 /* AllTest2TableViewCell.m in Sources */, 18D3C95F1EDF07F800D3DE43 /* FOLTest6ViewController.m in Sources */, 182225D51E77E5930081AA4B /* MyLayoutSize.m in Sources */, + 6740E20120A52DC100AFBC5A /* AllTest10Cell.m in Sources */, 18D3C93A1EDF07C000D3DE43 /* FLLTest2ViewController.m in Sources */, + 467E63CF228CDA660065D080 /* AllTest1TableViewCellForAutoLayout.m in Sources */, 18D3C95A1EDF07F800D3DE43 /* FOLTest1ViewController.m in Sources */, 182225D01E77E5930081AA4B /* MyLayoutPos.m in Sources */, + 2094619E2341FD6F005F5C8E /* AllTestExampleViewController.m in Sources */, 18D3C98B1EDF084000D3DE43 /* AllTest7ViewController.m in Sources */, + 20F0E00021120A8D00CFCE8C /* FLLTest8ViewController.m in Sources */, 200A26DC1FA96B2400B06E7B /* AllTest9CollectionViewCell.m in Sources */, + 4689882223449E8300BFE829 /* RLTest5ViewController.m in Sources */, 18D3C9881EDF084000D3DE43 /* AllTest4ViewController.m in Sources */, 18D3C9131EDF074900D3DE43 /* LLTest3ViewController.m in Sources */, 18D3C95E1EDF07F800D3DE43 /* FOLTest5ViewController.m in Sources */, 18D3C99C1EDF09BE00D3DE43 /* CFTool.m in Sources */, 18D3C9141EDF074900D3DE43 /* LLTest4ViewController.m in Sources */, + 20DF423E2318A6000078CAA0 /* FLXTest1ViewController.m in Sources */, 18D3C9931EDF08F800D3DE43 /* ViewController.m in Sources */, 18D3C93B1EDF07C000D3DE43 /* FLLTest3ViewController.m in Sources */, 205643181F55A16400E8BDDE /* MyLayoutMath.m in Sources */, 18D3C95C1EDF07F800D3DE43 /* FOLTest3ViewController.m in Sources */, 18D3C9491EDF07E700D3DE43 /* PLTest1ViewController.m in Sources */, 18D3C95D1EDF07F800D3DE43 /* FOLTest4ViewController.m in Sources */, + 2075CC3220A0B2A400BA6F65 /* FLLTest7ViewController.m in Sources */, 182225CE1E77E5930081AA4B /* MyFrameLayout.m in Sources */, 18D3C9941EDF08F800D3DE43 /* YYFPSLabel.m in Sources */, 18D3C91D1EDF078200D3DE43 /* FLTest2ViewController.m in Sources */, 18D3C9661EDF080800D3DE43 /* TLTest1ViewController.m in Sources */, + 46898847236733AC00BFE829 /* FLLTest9ViewController.m in Sources */, + 467E63CC228C4D6F0065D080 /* AllTest12ViewController.m in Sources */, + 6740E1FE20A52DB100AFBC5A /* AllTest10HeaderView.m in Sources */, 18D3C94A1EDF07E700D3DE43 /* PLTest2ViewController.m in Sources */, 205642881F4CFFB600E8BDDE /* MyBorderline.m in Sources */, 18D3C93C1EDF07C000D3DE43 /* FLLTest4ViewController.m in Sources */, @@ -1092,6 +1229,8 @@ 18D3C9891EDF084000D3DE43 /* AllTest5ViewController.m in Sources */, 182225CC1E77E5930081AA4B /* MyRelativeLayout.m in Sources */, 18D3C94C1EDF07E700D3DE43 /* PLTest4ViewController.m in Sources */, + 202A5A0D2950A1E60076DD27 /* AllTest9WaterFlowLayout.m in Sources */, + 20A45EC52318FCA50026A18C /* MyFlexLayout.m in Sources */, 2056430C1F4E8B1E00E8BDDE /* MyGridNode.m in Sources */, 182225D11E77E5930081AA4B /* MyLayoutSizeClass.m in Sources */, 444B71911F6A3E3F00331872 /* GLTest5ViewController.m in Sources */, @@ -1100,24 +1239,29 @@ 18D3C9171EDF074900D3DE43 /* LLTest7ViewController.m in Sources */, 18D3C98A1EDF084000D3DE43 /* AllTest6ViewController.m in Sources */, 18D3C9111EDF074900D3DE43 /* LLTest1ViewController.m in Sources */, + 20DCF333208C3BFB007A879B /* FOLTest7ViewController.m in Sources */, 18D3C93E1EDF07C000D3DE43 /* FLLTest6ViewController.m in Sources */, 200A26D91FA8A90300B06E7B /* AllTest9ViewController.m in Sources */, 18D3C9281EDF07A700D3DE43 /* RLTest1ViewController.m in Sources */, 182225CA1E77E5930081AA4B /* MyLinearLayout.m in Sources */, 18D3C91C1EDF078200D3DE43 /* FLTest1ViewController.m in Sources */, 18D3C9951EDF08F800D3DE43 /* YYWeakProxy.m in Sources */, + 20F0DFFD2111749A00CFCE8C /* AllTest11ViewController.m in Sources */, 20F783561F60399700BE5B31 /* GLTest3ViewController.m in Sources */, 44EBDA8C1F619A6300B47CBD /* GLTest4ViewController.m in Sources */, 18D3C9711EDF082800D3DE43 /* AllTest1TableViewCell.m in Sources */, 182225CF1E77E5930081AA4B /* MyFloatLayout.m in Sources */, - 18D3C92B1EDF07A700D3DE43 /* RLTest4ViewController.m in Sources */, - 18D3C92C1EDF07A700D3DE43 /* RLTest5ViewController.m in Sources */, + 18D3C92B1EDF07A700D3DE43 /* LLTest8ViewController.m in Sources */, + 6740E1FB20A52DA900AFBC5A /* AllTest10Model.m in Sources */, + 18D3C92C1EDF07A700D3DE43 /* RLTest4ViewController.m in Sources */, + 204DC321209D5A8700F6CB57 /* TLTest4ViewController.m in Sources */, 182DB6431EF7876200E2D65D /* MyGridLayout.m in Sources */, 18D3C9871EDF084000D3DE43 /* AllTest3ViewController.m in Sources */, 18D3C9161EDF074900D3DE43 /* LLTest6ViewController.m in Sources */, 18D3C9721EDF082800D3DE43 /* AllTest1TableViewHeaderFooterView.m in Sources */, 208157C31F5A886400E945B6 /* MyLayoutDelegate.m in Sources */, 18D3C94D1EDF07E700D3DE43 /* PLTest5ViewController.m in Sources */, + 6740E1F820A52D9E00AFBC5A /* AllTest10ViewController.m in Sources */, 18D3C9851EDF084000D3DE43 /* AllTest1ViewController.m in Sources */, 182225D21E77E5930081AA4B /* MyMaker.m in Sources */, 182225C91E77E5930081AA4B /* MyBaseLayout.m in Sources */, @@ -1129,14 +1273,17 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4689881D23430A8400BFE829 /* MyPathLayoutTestCase.m in Sources */, 18B2CCE81EAF5066001AE0E1 /* MyLinearLayoutTestCase.m in Sources */, + 205AE95E22BC4C2900BE8E76 /* MyFloatLayoutTestCase.m in Sources */, 18D684591C4F423400A48BB4 /* MyLayoutTests.m in Sources */, 1810AC221EB966D30022CBFF /* MyLayoutPerformanceTestCase.m in Sources */, + 4689881F23430AA000BFE829 /* MyGridLayoutTestCase.m in Sources */, 181836951EAB1CB10031F337 /* MyNoLayoutSuperviewTestCase.m in Sources */, 180BD8A11EA9EF9A00C6980B /* MyFrameLayoutTestCase.m in Sources */, 1897384F1E1631E3004F80D6 /* MyRelativeLayoutTestCase.m in Sources */, 189738511E16330B004F80D6 /* MyLayoutTestCaseBase.m in Sources */, - 18B2CCEA1EB046AC001AE0E1 /* MyFloatLayoutTestCase.m in Sources */, + 18B2CCEA1EB046AC001AE0E1 /* MyFlowLayoutTestCase.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1223,16 +1370,19 @@ CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = BGQ8WB468K; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = MyLayout/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.youngsoft.MyLayout; @@ -1250,15 +1400,17 @@ CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CODE_SIGN_IDENTITY = ""; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = BGQ8WB468K; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = MyLayout/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; OTHER_LDFLAGS = ""; PRODUCT_BUNDLE_IDENTIFIER = com.youngsoft.MyLayout; @@ -1274,18 +1426,28 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -1309,7 +1471,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = ""; @@ -1321,18 +1483,28 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -1349,7 +1521,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = ""; SDKROOT = iphoneos; @@ -1364,14 +1536,17 @@ CLANG_ENABLE_CODE_COVERAGE = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = BGQ8WB468K; GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; INFOPLIST_FILE = "$(SRCROOT)/MyLayoutDemo/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.youngsoft.mylayout; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = com.meituan.MyLayoutDemo; PRODUCT_NAME = MyLayoutDemo; PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -1383,14 +1558,17 @@ CLANG_ENABLE_CODE_COVERAGE = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = BGQ8WB468K; GCC_PREPROCESSOR_DEFINITIONS = ""; INFOPLIST_FILE = "$(SRCROOT)/MyLayoutDemo/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.youngsoft.mylayout; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_BUNDLE_IDENTIFIER = com.meituan.MyLayoutDemo; PRODUCT_NAME = MyLayoutDemo; PROVISIONING_PROFILE = ""; + PROVISIONING_PROFILE_SPECIFIER = ""; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; @@ -1399,7 +1577,7 @@ isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - DEVELOPMENT_TEAM = Q5MFLFRY64; + DEVELOPMENT_TEAM = BGQ8WB468K; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", @@ -1420,7 +1598,7 @@ isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - DEVELOPMENT_TEAM = Q5MFLFRY64; + DEVELOPMENT_TEAM = BGQ8WB468K; FRAMEWORK_SEARCH_PATHS = ( "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", @@ -1441,8 +1619,9 @@ CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES; DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = BGQ8WB468K; INFOPLIST_FILE = MyLayoutUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.youngsoft.MyLayoutUITests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1458,8 +1637,9 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES; + DEVELOPMENT_TEAM = BGQ8WB468K; INFOPLIST_FILE = MyLayoutUITests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.youngsoft.MyLayoutUITests; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/MyLayout.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/MyLayout.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/MyLayout.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/MyLayout.xcodeproj/xcshareddata/xcbaselines/184082A51B2C46E8003F378B.xcbaseline/0B2E8D10-2561-4DF8-8F44-0650622D8DB9.plist b/MyLayout.xcodeproj/xcshareddata/xcbaselines/184082A51B2C46E8003F378B.xcbaseline/0B2E8D10-2561-4DF8-8F44-0650622D8DB9.plist new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/MyLayout.xcodeproj/xcshareddata/xcbaselines/184082A51B2C46E8003F378B.xcbaseline/0B2E8D10-2561-4DF8-8F44-0650622D8DB9.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/MyLayout.xcodeproj/xcshareddata/xcschemes/MyLayout.xcscheme b/MyLayout.xcodeproj/xcshareddata/xcschemes/MyLayout.xcscheme index df29ad5..96dcb2b 100644 --- a/MyLayout.xcodeproj/xcshareddata/xcschemes/MyLayout.xcscheme +++ b/MyLayout.xcodeproj/xcshareddata/xcschemes/MyLayout.xcscheme @@ -1,6 +1,6 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MyLayout/Info.plist b/MyLayout/Info.plist index ad93595..47276bf 100644 --- a/MyLayout/Info.plist +++ b/MyLayout/Info.plist @@ -2,23 +2,23 @@ - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.5.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.8.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + diff --git a/MyLayout/Lib/MyBaseLayout.h b/MyLayout/Lib/MyBaseLayout.h index 1bba083..db54adf 100644 --- a/MyLayout/Lib/MyBaseLayout.h +++ b/MyLayout/Lib/MyBaseLayout.h @@ -6,15 +6,14 @@ // Copyright (c) 2015年 YoungSoft. All rights reserved. // +#import "MyBorderline.h" #import "MyLayoutDef.h" #import "MyLayoutPos.h" #import "MyLayoutSize.h" -#import "MyLayoutSizeClass.h" -#import "MyBorderline.h" /* 几种视图类型的定义: -1.布局视图: 就是从MyBaseLayout派生而出的视图,目前MyLayout中一共有:线性布局、框架布局、相对布局、表格布局、流式布局、浮动布局、路径布局7种布局。 布局视图也是一个视图。 +1.布局视图: 就是从MyBaseLayout派生而出的视图,目前MyLayout中一共有:线性布局、框架布局、相对布局、表格布局、流式布局、浮动布局、路径布局、栅格布局、弹性布局9种布局。 布局视图也是一个视图。 2.非布局视图: 除上面说的7种布局视图外的所有视图和控件。 3.布局父视图: 如果某个视图的父视图是一个布局视图,那么这个父视图就是布局父视图。 4.非布局父视图:如果某个视图的父视图不是一个布局视图,那么这个父视图就是非布局父视图。 @@ -22,11 +21,9 @@ 6.非布局子视图:如果某个视图的子视图不是一个布局视图,那么这个子视图就是非布局子视图。 */ - @class MyBaseLayout; -@interface UIView(MyLayoutExt) - +@interface UIView (MyLayoutExt) /* 视图的布局位置对象属性,用来指定视图水平布局位置和垂直布局位置。对于一个视图来说我们可以使用frame属性中的origin部分来确定一个视图的左上方位在父视图的位置。这种方法的缺点是需要明确的指定一个常数值,以及需要进行位置的计算,缺乏可扩展性以及可维护性。因此对于布局视图里面的子视图来说我们将不再通过设置frame属性中的origin部分来确定位置,而是通过视图扩展的布局位置对象属性来设置视图的布局位置。子视图的布局位置对象在不同类型的布局视图里和不同的场景下所表达的意义以及能设置的值类型将会有一定的差异性。下面的表格将列出这些差异性: @@ -40,7 +37,7 @@ |--------------------+------------------+----------------------+---------------+-----------+--------------------+-------+-------| |Vert MyTableLayout | T,B,CY | L,R | L,R,T,B,CY | - | - | - |T,B | |--------------------+------------------+----------------------+---------------+-----------+--------------------+-------+-------| - |Horz MyTableLayout | L,R,CX | T,B | L,R,CX,T,B | - | - | L,R | - | + |Horz MyTableLayout | L,R,CX | T,B | L,R,CX,T,B | - | - | L,R | - | |--------------------+------------------+----------------------+---------------+-----------+--------------------+-------+-------| |MyFrameLayout | ALL | - | L,R,T,B,CX,CY | - | - | - | - | |--------------------+------------------+----------------------+---------------+-----------+--------------------+-------+-------| @@ -87,59 +84,50 @@ */ - /** *视图的上边布局位置对象。(top layout position of the view.) */ -@property(nonatomic, readonly) MyLayoutPos *topPos; +@property (nonatomic, readonly) MyLayoutPos *topPos; /** *视图的头部布局位置对象,对于非阿拉伯国家就是左边,对于阿拉伯国家就是右边(leading layout position of the view.) */ -@property(nonatomic, readonly) MyLayoutPos *leadingPos; - +@property (nonatomic, readonly) MyLayoutPos *leadingPos; /** *视图的下边布局位置对象。(bottom layout position of the view.) */ -@property(nonatomic, readonly) MyLayoutPos *bottomPos; - +@property (nonatomic, readonly) MyLayoutPos *bottomPos; /** *视图的尾部布局位置对象,对于非阿拉伯国家就是右边,对于阿拉伯国家就是左边。(trailing layout position of the view.) */ -@property(nonatomic, readonly) MyLayoutPos *trailingPos; - +@property (nonatomic, readonly) MyLayoutPos *trailingPos; /** *视图的水平中心布局位置对象。(horizontal center layout position of the view.) */ -@property(nonatomic, readonly) MyLayoutPos *centerXPos; +@property (nonatomic, readonly) MyLayoutPos *centerXPos; /** *视图的垂直中心布局位置对象。(vertical center layout position of the view.) */ -@property(nonatomic, readonly) MyLayoutPos *centerYPos; - - +@property (nonatomic, readonly) MyLayoutPos *centerYPos; /** *视图的左边布局位置对象。如果您不考虑国际化布局就用这个属性,如果考虑国际化则请用leadingPos(left layout position of the view.) */ -@property(nonatomic, readonly) MyLayoutPos *leftPos; +@property (nonatomic, readonly) MyLayoutPos *leftPos; /** *视图的右边布局位置对象。如果您不考虑国际化布局就用这个属性,如果考虑国际化则请用trailingPos(right layout position of the view.) */ -@property(nonatomic, readonly) MyLayoutPos *rightPos; - - +@property (nonatomic, readonly) MyLayoutPos *rightPos; /** *视图的基线位置对象。目前只支持相对布局里面的视图的设置并且调用视图或者被调用视图都只能是UILabel和UITextField和UITextView三个只支持单行文本的视图。 */ -@property(nonatomic, readonly) MyLayoutPos *baselinePos; - +@property (nonatomic, readonly) MyLayoutPos *baselinePos; /* 下面定义的八个属性是上面定义的leftPos,topPos,rightPos,bottomPos,centerXPos,centerYPos的equalTo设置NSNumber类型的值时的简化版本。 @@ -169,107 +157,95 @@ 2. 如果同时设置了上下边距就能决定自己的高度,同时设置上下间距不能决定自己的高度! */ - - /** - *视图上边的布局位置, 是topPos.equalTo方法的简化版本 + *视图上边的布局位置, 是topPos.equalTo方法的简化版本。此属性只用于赋值不用于读取! */ /** - *Top layout position of the view. Equivalent to topPos.equalTo(NSNumber). + *Top layout position of the view. Equivalent to topPos.equalTo(NSNumber). Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGFloat myTop; - +@property (nonatomic, assign) CGFloat myTop; /** - *视图头部的布局位置, 是leadingPos.equalTo方法的简化版本 + *视图头部的布局位置, 是leadingPos.equalTo方法的简化版本。此属性只用于赋值不用于读取! */ /** - *Leading layout position of the view. Equivalent to leadingPos.equalTo(NSNumber). + *Leading layout position of the view. Equivalent to leadingPos.equalTo(NSNumber).Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGFloat myLeading; - +@property (nonatomic, assign) CGFloat myLeading; /** - *视图下边的布局位置, 是bottomPos.equalTo方法的简化版本 + *视图下边的布局位置, 是bottomPos.equalTo方法的简化版本。此属性只用于赋值不用于读取! */ /** - *Bottom layout position of the view. Equivalent to bottomPos.equalTo(NSNumber). + *Bottom layout position of the view. Equivalent to bottomPos.equalTo(NSNumber).Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGFloat myBottom; - +@property (nonatomic, assign) CGFloat myBottom; /** - *视图尾部的布局位置, 是trailingPos.equalTo方法的简化版本 + *视图尾部的布局位置, 是trailingPos.equalTo方法的简化版本。此属性只用于赋值不用于读取! */ /** - *Trailing layout position of the view. Equivalent to trailingPos.equalTo(NSNumber). + *Trailing layout position of the view. Equivalent to trailingPos.equalTo(NSNumber).Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGFloat myTrailing; - +@property (nonatomic, assign) CGFloat myTrailing; /** - *视图水平中心布局位置, 是centerXPos.equalTo方法的简化版本 + *视图水平中心布局位置, 是centerXPos.equalTo方法的简化版本。此属性只用于赋值不用于读取! */ /** - *Horizontal center layout position of the view. Equivalent to centerXPos.equalTo(NSNumber). + *Horizontal center layout position of the view. Equivalent to centerXPos.equalTo(NSNumber).Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGFloat myCenterX; +@property (nonatomic, assign) CGFloat myCenterX; /** - *视图垂直中心布局位置, 是centerYPos.equalTo方法的简化版本 + *视图垂直中心布局位置, 是centerYPos.equalTo方法的简化版本。此属性只用于赋值不用于读取! */ /** - *Vertical center layout position of the view. Equivalent to centerYPos.equalTo(NSNumber). + *Vertical center layout position of the view. Equivalent to centerYPos.equalTo(NSNumber).Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGFloat myCenterY; +@property (nonatomic, assign) CGFloat myCenterY; /** - *视图中心布局位置, 是myCenterX,myCenterY方法的简化版本 + *视图中心布局位置, 是myCenterX,myCenterY方法的简化版本。此属性只用于赋值不用于读取! */ /** - *Center layout position of the view. Equivalent to set myCenterX and myCenterY . + *Center layout position of the view. Equivalent to set myCenterX and myCenterY .Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGPoint myCenter; - +@property (nonatomic, assign) CGPoint myCenter; /** - *视图左边的布局位置, 是leftPos.equalTo方法的简化版本 + *视图左边的布局位置, 是leftPos.equalTo方法的简化版本。此属性只用于赋值不用于读取! */ /** - *Left layout position of the view. Equivalent to leftPos.equalTo(NSNumber). + *Left layout position of the view. Equivalent to leftPos.equalTo(NSNumber).Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGFloat myLeft; - +@property (nonatomic, assign) CGFloat myLeft; /** - *视图右边的布局位置, 是rightPos.equalTo方法的简化版本 + *视图右边的布局位置, 是rightPos.equalTo方法的简化版本。此属性只用于赋值不用于读取! */ /** - *Right layout position of the view. Equivalent to rightPos.equalTo(NSNumber). + *Right layout position of the view. Equivalent to rightPos.equalTo(NSNumber).Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGFloat myRight; - - +@property (nonatomic, assign) CGFloat myRight; /** - *视图四边的布局位置, 是myLeading,myTop,myTrailing,myBottom的简化版本 + *视图四边的布局位置, 是myLeading,myTop,myTrailing,myBottom的简化版本。此属性只用于赋值不用于读取! */ /** - *Boundary layout position of the view. Equivalent to myLeading,myTop,myTrailing,myBottom set to the same number. + *Boundary layout position of the view. Equivalent to myLeading,myTop,myTrailing,myBottom set to the same number.Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGFloat myMargin; +@property (nonatomic, assign) CGFloat myMargin; /** - *leading and trailing margin of the view to the superview. Equivalent to myLeading,myTrailing set to the same number + *leading and trailing margin of the view to the superview. Equivalent to myLeading,myTrailing set to the same number.Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGFloat myHorzMargin; +@property (nonatomic, assign) CGFloat myHorzMargin; /** - *top and bottom margin of the view to the superview. Equivalent to myTop,myBottom set to the same number + *top and bottom margin of the view to the superview. Equivalent to myTop,myBottom set to the same number.Only for setting, not for getting. */ -@property(nonatomic, assign) IBInspectable CGFloat myVertMargin; - - +@property (nonatomic, assign) CGFloat myVertMargin; /* 视图的布局尺寸对象MyLayoutSize,用于设置视图的宽度和高度尺寸。视图可以通过设置frame值来设置自身的尺寸,也可以通过设置widthSize和heightSize来 @@ -295,11 +271,11 @@ 定义A为操作的视图本身,B为A的兄弟视图,P为A的父视图。 +-------------+----------+--------------+---------------+------------+--------------+---------------+--------------+----------------------+ - | 对象 \ 值 | NSNumber |A.widthSize |A.heightSize |B.widthSize | B.heightSize | P.widthSize |P.heightSize |NSArray| + | 对象 \ 值 | NSNumber |A.widthSize |A.heightSize |B.widthSize | B.heightSize | P.widthSize |P.heightSize |NSArray| +-------------+----------+--------------+---------------+------------+--------------+---------------+--------------+----------------------+ - |A.widthSize | ALL |ALL |FR/R/FL-H/FO |FR/R/FO/P | R |L/FR/R/FL/FO/P | R |R | + |A.widthSize | ALL |ALL |ALL |ALL | ALL |ALL |ALL |R | +-------------+----------+--------------+---------------+------------+--------------+---------------+--------------+----------------------+ - |A.heightSize | ALL |FR/R/FL-V/FO/L|ALL |R |FR/R/FO/P |R |L/FR/R/FL/FO/P|R | + |A.heightSize | ALL |ALL |ALL |ALL |ALL |ALL |ALL |R | +-------------+----------+--------------+---------------+------------+--------------+---------------+--------------+----------------------+ 上表中所有的布局尺寸的值都支持设置为数值,而且所有布局下的子视图的宽度和高度尺寸都可以设置为等于自身的宽度和高度尺寸,布局库这里做了特殊处理,是不会造成循环引用的。比如: @@ -312,19 +288,15 @@ the MyLayoutSize is layout dimension object object of the UIView. used to set the width and height of the View which is in Layout View */ - - /** - *视图的宽度布局尺寸对象,可以通过其中的euqalTo方法来设置NSNumber,MyLayoutSize,NSArray,nil这四种值 + *视图的宽度布局尺寸对象,可以通过其中的euqalTo方法来设置NSNumber,MyLayoutSize,NSArray,MyLayoutMostSize,UIView,nil这六种值 */ -@property(nonatomic, readonly) MyLayoutSize *widthSize; +@property (nonatomic, readonly) MyLayoutSize *widthSize; /** - *视图的高度布局尺寸对象,可以通过其中的euqalTo方法来设置NSNumber,MyLayoutSize,NSArray,nil这四种值 + *视图的高度布局尺寸对象,可以通过其中的euqalTo方法来设置NSNumber,MyLayoutSize,NSArray,MyLayoutMostSize,UIView,nil这六种值 */ -@property(nonatomic, readonly) MyLayoutSize *heightSize; - - +@property (nonatomic, readonly) MyLayoutSize *heightSize; /* 下面三个属性是上面widthSize,heightSize的equalTo设置NSNumber类型值的简化版本,表示设置视图的高度和宽度的布局尺寸。 @@ -337,40 +309,25 @@ */ /** - *视图的宽度布局尺寸,是widthSize.equalTo方法的简化版本 - */ -@property(nonatomic,assign) IBInspectable CGFloat myWidth; - -/** - *视图的高度布局尺寸,是heightSize.equalTo方法的简化版本 - */ -@property(nonatomic,assign) IBInspectable CGFloat myHeight; - -/** - *视图的宽度高度布局尺寸,是myWidth,myHeight方法的简化版本 + *视图的宽度布局尺寸,是widthSize.equalTo方法的简化版本。此属性只用于赋值不用于读取!除了设置常规的数字外您还可以设置一些特殊的值: + MyLayoutSize.wrap 表示宽度自适应 + MyLayoutSize.fill 表明宽度填充父视图剩余宽度 + MyLayoutSize.empty 表明清除宽度约束。 */ -@property(nonatomic,assign) IBInspectable CGSize mySize; - - +@property (nonatomic, assign) CGFloat myWidth; /** - *视图的宽度包裹属性,表示视图的宽度由所有子视图的整体宽度来确定或者根据视图内容来宽度自适应。默认值是NO(水平线性布局默认这个属性是YES),表示必须要明确指定视图的宽度,而当设置为YES时则不需要明确的指定视图的宽度了。这个属性不能和widthSize(或者设置了左右边距)同时设置否则可能会产生约束冲突,因为前者表明宽度由子视图或者内容来决定而后者则表示宽度是一个明确的值。如果同时设置了宽度包裹属性又同时设置了明确的宽度则系统会出现约束冲突告警,虽然如此,系统在布局时做了一些优化,如果同时设置了明确的宽度和宽度包裹则会在布局前将宽度包裹属性置为NO。 + *视图的高度布局尺寸,是heightSize.equalTo方法的简化版本。此属性只用于赋值不用于读取!除了设置常规的数字外您还可以设置一些特殊的值: + MyLayoutSize.wrap 表示高度自适应 + MyLayoutSize.fill 表明高度填充父视图剩余高度 + MyLayoutSize.empty 表明清除高度约束。 */ -@property(nonatomic,assign) IBInspectable BOOL wrapContentWidth; +@property (nonatomic, assign) CGFloat myHeight; /** - *视图的高度包裹属性,表示视图的高度由所有子视图的整体高度来确定或者根据视图内容来高度自适应。默认值是NO(垂直线性布局默认这个属性是YES),表示必须要明确指定布局的高度,而当设置为YES时则不需要明确的指定视图的高度了。这个属性不能和heightSize(或者设置了上下边距)同时设置否则可能会产生约束冲突,因为前者表明高度由子视图或者内容来决定而后者则表示高度是一个明确的值。如果同时设置了高度包裹属性又同时设置了明确的高度则系统会出现约束冲突告警,虽然如此,系统在布局时做了一些优化,如果同时设置了明确的高度和高度包裹则会在布局前将高度包裹属性置为NO。如果某个非布局视图指定了明确的宽度,而又将这个属性设置为了YES的话就能实现在固定宽度的情况下视图的高度根据内容自适应的效果,这个特性主要用于UILabel,UITextView以及实现了sizeThatFits方法的视图来实现高度根据内容自适应的场景。UILabel在使用这个属性时会自动设置numberOfLines为0,因此如果您要修改numberOfLines则需要在设置这个属性后进行;UITextView可以用这个属性以及heightSize中的max方法来实现到达指定的高度后若继续输入则产生滚动的效果;UIImageView可以用这个属性来在实现在确定宽度的情况下高度根据宽度的缩放情况进行等比例的缩放。 + *视图的宽度高度布局尺寸,是myWidth,myHeight方法的简化版本。此属性只用于赋值不用于读取! */ -@property(nonatomic,assign) IBInspectable BOOL wrapContentHeight; - - -/** - *视图的尺寸根据内容来决定,也就是视图的尺寸由内容包裹。这个属性是: A.wrapContentWidth = YES; A.wrapContentHeight = YES;的简化版本。 - *在历史版本中对UILabel进行text赋值后总要手动调用sizeToFit来重新更新布局,在1.3.6以后的新版本中,如果你希望视图的尺寸根据内容而确定则请将这个属性 - *设置为YES。这样就可以在设置完毕text后系统会自动激发布局处理。 - */ -@property(nonatomic, assign) IBInspectable BOOL wrapContentSize; - +@property (nonatomic, assign) CGSize mySize; /** 设置子视图的相对比重尺寸。也就是子视图的尺寸并不是一个绝对值而是一个相对值,在最终布局时系统会根据布局视图的剩余尺寸来将这个相对的值转化为绝对的值。通过对子视图设置相对的尺寸可以很方便的适配各种屏幕尺寸的设备,以便达到完美的布局效果。这个属性默认值是0表示不使用相对尺寸。weight属性的引入是参考android中的weight的概念而来的。 @@ -380,66 +337,66 @@ @note 1.在线性布局和表格布局下: - - 1.1. 当线性布局是垂直线性布局时这个属性用来设置子视图高度占用父线性布局剩余高度的比重,这样子视图就不需要明确的设定高度值。 - 1.2. 当线性布局是水平线性布局时这个属性用来设置子视图宽度占用父线性布局剩余宽度的比重,这样子视图就不需要明确的设定宽度值。 + 1.1. 当线性布局是垂直线性布局时这个属性用来设置子视图高度占用父线性布局剩余高度的比重,这样子视图就不需要明确的设定高度值。 - @code - 视图的真实尺寸值 = 布局视图剩余尺寸 * 当前视图的weight比重/(布局视图内所有设置了weight比重值的子视图比重之和)。 - @endcode + 1.2. 当线性布局是水平线性布局时这个属性用来设置子视图宽度占用父线性布局剩余宽度的比重,这样子视图就不需要明确的设定宽度值。 - 对一个垂直线性布局举例来说:假设布局视图的高度是100,A子视图占据了20的固定高度,B子视图的weight设置为0.4,C子视图的weight设置为0.6 那么: - @code - A子视图的高度 = 20 - B子视图的高度 = (100-20)*0.4/(0.4+0.6) = 32 - C子视图的高度 = (100-20)*0.6/(0.4+0.6) = 48 - @endcode + @code + 视图的真实尺寸值 = 布局视图剩余尺寸 * 当前视图的weight比重/(布局视图内所有设置了weight比重值的子视图比重之和)。 + @endcode + + 对一个垂直线性布局举例来说:假设布局视图的高度是100,A子视图占据了20的固定高度,B子视图的weight设置为0.4,C子视图的weight设置为0.6 那么: + @code + A子视图的高度 = 20 + B子视图的高度 = (100-20)*0.4/(0.4+0.6) = 32 + C子视图的高度 = (100-20)*0.6/(0.4+0.6) = 48 + @endcode 子视图设置weight属性时要注意如下几点: - - 垂直线性布局必须指定明确的高度,而不能使用wrapContentHeight属性;水平线性布局必须指定明确的宽度,而不能使用wrapContentWidth属性。 + - 垂直线性布局必须指定明确的高度,而高度不能设置为自适应;水平线性布局必须指定明确的宽度,而不能使用wrapContentWidth属性。 - - 如果比重属性设置为0的话则表示视图的尺寸必须要明确的被指定,这也是默认值。 + - 如果比重属性设置为0的话则表示视图的尺寸必须要明确的被指定,这也是默认值。 + + - 线性布局里面的所有子视图的比重值的和不一定要求是1,比如线性布局里面两个视图的比重都设置为1和都设置为0.5以及都设置为0.1的意义是一样的,都是占用50%。 - - 线性布局里面的所有子视图的比重值的和不一定要求是1,比如线性布局里面两个视图的比重都设置为1和都设置为0.5以及都设置为0.1的意义是一样的,都是占用50%。 - @note 2.在流式布局下: - 2.1. 当流式布局是垂直流式布局时这个属性用来设置子视图宽度占用父流式布局当前行的剩余宽度的比重,这样子视图就不需要明确的设定宽度值。 - - 2.2. 当流式布局是水平流式布局时这个属性用来设置子视图高度占用父流式布局当前列的剩余高度的比重,这样子视图就不需要明确的设定高度值。 + 2.1. 当流式布局是垂直流式布局时这个属性用来设置子视图宽度占用父流式布局当前行的剩余宽度的比重,这样子视图就不需要明确的设定宽度值。 - 同时在数量约束和内容约束两种布局中视图设置的比重值在最终计算出真实尺寸时也是有差异的: + 2.2. 当流式布局是水平流式布局时这个属性用来设置子视图高度占用父流式布局当前列的剩余高度的比重,这样子视图就不需要明确的设定高度值。 - @code - 数量约束流式布局内的视图的真实尺寸值 = 布局视图剩余尺寸 * 当前视图的weight比重/(视图所在行的所有设置了weight比重值的视图比重之和)。 + 同时在数量约束和内容约束两种布局中视图设置的比重值在最终计算出真实尺寸时也是有差异的: - 内容约束流式布局内的视图的真实尺寸值 = 布局视图剩余尺寸 * 当前视图的weight比重。 + @code + 数量约束流式布局内的视图的真实尺寸值 = 布局视图剩余尺寸 * 当前视图的weight比重/(视图所在行的所有设置了weight比重值的视图比重之和)。 + + 内容约束流式布局内的视图的真实尺寸值 = 布局视图剩余尺寸 * 当前视图的weight比重。 - 对一个垂直数量约束流式布局举例来说:假设布局视图的宽度是100,A子视图占据了20的固定宽度,B子视图的weight设置为0.4,C子视图的weight设置为0.6 那么: - A子视图的宽度 = 20 - B子视图的宽度 = (100-20)*0.4/(0.4+0.6) = 32 - C子视图的宽度 = (100-20)*0.6/(0.4+0.6) = 48 + 对一个垂直数量约束流式布局举例来说:假设布局视图的宽度是100,A子视图占据了20的固定宽度,B子视图的weight设置为0.4,C子视图的weight设置为0.6 那么: + A子视图的宽度 = 20 + B子视图的宽度 = (100-20)*0.4/(0.4+0.6) = 32 + C子视图的宽度 = (100-20)*0.6/(0.4+0.6) = 48 - 对一个垂直内容约束流式布局举例来说:假设布局视图的宽度是100,A子视图占据了20的固定宽度,B子视图的weight设置为0.4,C子视图的weight设置为0.6 那么: - A子视图的宽度 = 20 - B子视图的宽度 = (100-20)*0.4 = 32 - C子视图的宽度 = (100-20-32)*0.6 = 28.8 + 对一个垂直内容约束流式布局举例来说:假设布局视图的宽度是100,A子视图占据了20的固定宽度,B子视图的weight设置为0.4,C子视图的weight设置为0.6 那么: + A子视图的宽度 = 20 + B子视图的宽度 = (100-20)*0.4 = 32 + C子视图的宽度 = (100-20-32)*0.6 = 28.8 @endcode - + @note 3. 在浮动布局下: - 3.1. 当浮动布局的方向是垂直布局时,这个属性用来设置视图宽度占用当前浮动布局父视图剩余宽度的比重,这样视图就不需要明确的设定宽度值。 - - 3.2. 当浮动布局的方向是水平布局时,这个属性用来设置视图高度占用当前浮动布局父视图剩余高度的比重,这样子视图就不需要明确的设定高度值。 + 3.1. 当浮动布局的方向是垂直布局时,这个属性用来设置视图宽度占用当前浮动布局父视图剩余宽度的比重,这样视图就不需要明确的设定宽度值。 - @code + 3.2. 当浮动布局的方向是水平布局时,这个属性用来设置视图高度占用当前浮动布局父视图剩余高度的比重,这样子视图就不需要明确的设定高度值。 + + @code 视图的真实尺寸 = 布局父视图的剩余尺寸 * 视图的weight比重值。 - @endcode + @endcode 举例来说:假设一个垂直浮动布局视图的宽度是100,A子视图占据了20的固定宽度,B子视图的weight设置为0.4,C子视图的weight设置为0.6 那么: @@ -450,9 +407,7 @@ @endcode */ -@property(nonatomic, assign) IBInspectable CGFloat weight; - - +@property (nonatomic, assign) CGFloat weight; /** 设置视图不受布局父视图的布局约束控制和不再参与视图的布局,所有设置的其他扩展属性都将失效而必须用frame来设置视图的位置和尺寸,默认值是NO。这个属性主要用于某些视图希望在布局视图中进行特殊处理和进行自定义的设置的场景。比如一个垂直线性布局下有A,B,C三个子视图设置如下: @@ -486,8 +441,7 @@ useFrame的应用场景是某个视图虽然是布局视图的子视图但不想受到父布局视图的约束,而是可以通过frame进行自由位置和尺寸调整的场景。 */ -@property(nonatomic, assign) IBInspectable BOOL useFrame; - +@property (nonatomic, assign) BOOL useFrame; /** 设置视图在进行布局时只会参与布局但不会真实的调整位置和尺寸,默认值是NO。当设置为YES时会在布局时保留出视图的布局位置和布局尺寸的空间,但不会更新视图的位置和尺寸,也就是说只会占位但不会更新。因此你可以通过frame值来进行位置和尺寸的任意设置,而不会受到你的布局视图的影响。这个属性主要用于某些视图希望在布局视图中进行特殊处理和进行自定义的设置的场景。比如一个垂直线性布局下有A,B,C三个子视图设置如下: @@ -528,14 +482,12 @@ noLayout的应用场景是那些想在运行时动态调整某个视图的位置和尺寸,但是又不想破坏布局视图中其他子视图的布局结构的场景,也就是调整了视图的位置和尺寸,但是不会调整其他的兄弟子视图的位置和尺寸。 */ -@property(nonatomic, assign) IBInspectable BOOL noLayout; - +@property (nonatomic, assign) BOOL noLayout; /** 指定视图的可见性,默认是visible。这个属性是对视图hidden属性的扩展,布局系统对视图的hidden属性设置后,视图将不再参与布局。但在实际中有些场景我们希望视图隐藏后 仍然会占用空间仍然会参与布局。因此我们可以用这个属性来设置当视图隐藏后是否继续参与布局。 如果您使用了这个属性来对视图进行隐藏和取消隐藏操作则请不要再去操作hidden属性,否则可能出现二者效果不一致的情况。因此建议视图的隐藏和显示用这个属性进行设置。 - 在老版本中布局中的子视图隐藏时要么都参与布局,要么都不参与布局,这是通过布局属性hideSubviewReLayout来设置,新版本中这个属性将会设置为无效了! 属性可以设置的值如下: @@ -550,46 +502,44 @@ 建议您用这个属性设置视图的隐藏的显示而不用系统默认的hidden来进行设置。 */ -@property(nonatomic, assign) MyVisibility myVisibility; - +@property (nonatomic, assign) MyVisibility visibility; /** - 指定子在布局视图上的对齐方式,默认是None表示未指定,这个属性目前只支持框架布局,线性布局,流式布局下的属性设置。 + 指定子在布局视图上的对齐方式,默认是None表示未指定,这个属性目前只支持框架布局,线性布局,流式布局,浮动布局下的属性设置。 1. 在框架布局中支持上、中、下、垂直拉伸和左、中、右、水平拉伸8个设置 2. 在垂直线性布局中只支持左、中、右、水平拉伸对齐。(如果父布局视图设置了gravity,子视图设置了这个属性则这个属性优先级最高) - 3. 在水平线性布局中只支持上、中、下、垂直拉伸对齐。(如果父布局视图设置了gravity,子视图设置了这个属性则这个属性优先级最高) + 3. 在水平线性布局中只支持上、中、下、垂直拉伸、基线对齐。(如果父布局视图设置了gravity,子视图设置了这个属性则这个属性优先级最高) - 4. 在垂直流式布局中用来设置一行内的上、中、下、垂直拉伸对齐。(如果父布局视图设置了arrangedGravity,子视图设置了这个属性则这个属性优先级最高) + 4. 在垂直流式布局和垂直浮动布局中用来设置一行内的上、中、下、垂直拉伸、基线对齐。(如果流式父布局视图设置了arrangedGravity,子视图设置了这个属性则这个属性优先级最高) - 5. 在水平流式布局中用来设置一列内的左、中、右、水平拉伸对齐。(如果父布局视图设置了arrangedGravity,子视图时设置了这个属性则这个属性优先级最高) + 5. 在水平流式布局和水平浮动布局中用来设置一列内的左、中、右、水平拉伸对齐。(如果流式父布局视图设置了arrangedGravity,子视图时设置了这个属性则这个属性优先级最高) */ -@property(nonatomic, assign) MyGravity myAlignment; - - +@property (nonatomic, assign) MyGravity alignment; /** 视图在父布局视图中布局完成后也就是视图的frame更新完成后执行的block,执行完block后会被重置为nil。通过在viewLayoutCompleteBlock中我们可以得到这个视图真实的frame值,当然您也可以在里面进行其他业务逻辑的操作和属性的获取和更新。block方法中layout参数就是父布局视图,而v就是视图本身,block中这两个参数目的是为了防止循环引用的问题。 + + 不再建议使用这个block进行获取子视图最终frame值,因为这个block只会执行一次,所以得到的frame值可能不准确。 + 建议通过KVO来观察布局视图的isMyLayouting属性来获取布局完成后子视图的frame值。 + */ -@property(nonatomic,copy) void (^viewLayoutCompleteBlock)(MyBaseLayout* layout, UIView *v); - +@property (nonatomic, copy) void (^viewLayoutCompleteBlock)(MyBaseLayout *layout, UIView *v); /** 视图的在父布局视图调用完评估尺寸的方法后,可以通过这个方法来获取评估的CGRect值。评估的CGRect值是在布局前评估计算的值,而frame则是视图真正完成布局后的真实的CGRect值。在调用这个方法前请先调用父布局视图的-(CGSize)sizeThatFits:方法进行布局视图的尺寸评估,否则此方法返回的值未可知。这个方法主要用于在视图布局前而想得到其在父布局视图中的位置和尺寸的场景。 @return 返回被子视图的评估的CGRect值。 */ --(CGRect)estimatedRect; - +- (CGRect)estimatedRect; /** *清除视图所有为布局而设置的扩展属性值。如果是布局视图调用这个方法则同时会清除布局视图中所有关于布局设置的属性值。 */ --(void)resetMyLayoutSetting; --(void)resetMyLayoutSettingInSizeClass:(MySizeClass)sizeClass; - +- (void)resetMyLayoutSetting; +- (void)resetMyLayoutSettingInSizeClass:(MySizeClass)sizeClass; /** 获取视图在某个Size Classes下的MyLayoutSizeClass对象。视图可以通过得到的MyLayoutSizeClass对象来设置视图在对应Size Classes下的各种布局约束属性。 @@ -597,8 +547,7 @@ @param sizeClass 指定获取某种SizeClass @return 返回指定的SizeClass */ --(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass; - +- (instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass; /** 获取视图在某个Size Classes下的MyLayoutSizeClass对象,如果对象不存在则会新建立一个MyLayoutSizeClass对象,并且其布局约束属性都拷贝自srcSizeClass中的属性,如果对象已经存在则srcSizeClass不起作用,如果srcSizeClass本来就不存在则也不会起作用。 @@ -606,121 +555,62 @@ @param srcSizeClass 指定从哪种SizeClass中拷贝 @return 返回指定的SizeClass */ --(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass; - - -@end - - -@interface UIView(MyLayoutExtDeprecated) - -/** - * 过期的位置和尺寸设置方法,老版本中带Margin后缀就明确为了边距的概念,但是这个和属性定义的概念是不一致的,位置即可表示边距也可以表示间距。所以这些方法将设置为过期。您可以在相应的位置定义宏:#define MY_USEOLDMETHODNOWARNING = 1 则不会出现老方法告警,不过不建议这么做。 - */ - - -/** - *过期属性,请用myLeft - */ -@property(nonatomic, assign) CGFloat myLeftMargin MYMETHODDEPRECATED("use myLeft to instead"); - -/** - *过期属性,请用myTop - */ -@property(nonatomic, assign) CGFloat myTopMargin MYMETHODDEPRECATED("use myTop to instead"); - -/** - *过期属性,请用myRight - */ -@property(nonatomic, assign) CGFloat myRightMargin MYMETHODDEPRECATED("use myRight to instead"); - -/** - *过期属性,请用myBottom - */ -@property(nonatomic, assign) CGFloat myBottomMargin MYMETHODDEPRECATED("use myBottom to instead"); - -/** - *过期属性,请用myCenterX - */ -@property(nonatomic, assign) CGFloat myCenterXOffset MYMETHODDEPRECATED("use myCenterX to instead"); - -/** - *过期属性,请用myCenterY - */ -@property(nonatomic, assign) CGFloat myCenterYOffset MYMETHODDEPRECATED("use myCenterY to instead"); - -/** - *过期属性,请用myCenter - */ -@property(nonatomic, assign) CGPoint myCenterOffset MYMETHODDEPRECATED("use myCenter to instead"); - -/** - *过期属性,请用widthSize - */ -@property(nonatomic, readonly) MyLayoutSize *widthDime MYMETHODDEPRECATED("use widthSize to instead"); - -/** - *过期属性,请用heightSize - */ -@property(nonatomic, readonly) MyLayoutSize *heightDime MYMETHODDEPRECATED("use heightSize to instead"); - - -/** - *过期属性,请用wrapContentHeight - */ -@property(nonatomic, assign, getter=isFlexedHeight) BOOL flexedHeight MYMETHODDEPRECATED("use wrapContentHeight to instead"); +- (instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass; +/// 为某个sizeClass设置新的布局特性值,新的布局特性值一般来源于其他视图或者其他sizeClass,从而实现布局特征属性的拷贝功能。 +/// @param sizeClass 具体的sizeClass类型 +/// @param value 新的值,这个值必须是上述两个fetchLayoutSizeClass方法返回值的copy结果。 +- (void)setLayoutSizeClass:(MySizeClass)sizeClass withValue:(id)value; @end - - /** 布局视图基类,基类不支持实例化对象。在编程时我们经常会用到一些视图,这种视图只是负责将里面的子视图按照某种规则进行排列和布局,而别无其他的作用。因此我们称这种视图为容器视图或者称为布局视图。 布局视图通过重载layoutSubviews方法来完成子视图的布局和排列的工作。对于每个加入到布局视图中的子视图,都会在加入时通过KVO机制监控子视图的center和bounds以及frame值的变化,每当子视图的这些属性一变化时就又会重新引发布局视图的布局动作。同时对每个视图的布局扩展属性的设置以及对布局视图的布局属性的设置都会引发布局视图的布局动作。布局视图在添加到非布局父视图时也会通过KVO机制来监控非布局父视图的frame值和bounds值,这样每当非布局父视图的尺寸变更时也会引发布局视图的布局动作。前面说的引起变动的方法就是会在KVO处理逻辑以及布局扩展属性和布局属性设置完毕后通过调用setNeedLayout来实现的,当布局视图收到setNeedLayout的请求后,会在下一个runloop中对布局视图进行重新布局而这就是通过调用layoutSubviews方法来实现的。布局视图基类只提供了更新所有子视图的位置和尺寸以及一些基础的设置,而至于如何排列和布局这些子视图则要根据应用的场景和需求来确定,因此布局基类视图提供了一个: - -(CGSize)calcLayoutRect:(CGSize)size isEstimate:(BOOL)isEstimate pHasSubLayout:(BOOL*)pHasSubLayout sizeClass:(MySizeClass)sizeClass sbs:(NSMutableArray*)sbs + -(CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context 的方法,要求派生类去重载这个方法,这样不同的派生类就可以实现不同的应用场景,这就是布局视图的核心实现机制。 - MyLayout布局库根据实际中常见的场景实现了7种不同的布局视图派生类他们分别是:线性布局、表格布局、相对布局、框架布局、流式布局、浮动布局、路径布局。 + MyLayout布局库根据实际中常见的场景实现了8种不同的布局视图派生类他们分别是:线性布局、表格布局、相对布局、框架布局、流式布局、浮动布局、路径布局、栅格布局。 */ @interface MyBaseLayout : UIView -#if UIKIT_DEFINE_AS_PROPERTIES - /** 用于实现对阿拉伯国家的布局适配。对于非阿拉伯国家来说,界面布局都是默认从左到右排列。而对于阿拉伯国家来说界面布局则默认是从右往左排列。默认这个属性是NO,您可以将这个属性设置为YES,这样布局里面的所有视图都将从右到左进行排列布局。如果您需要考虑国际化布局的问题,那么您应该用leadingPos来表示头部的位置,而用trailingPos来表示尾部的位置,这样当布局方向是LTR时那么leadingPos就表示的是左边而trailingPos则表示的是右边;而当布局方向是RTL时那么leadingPos表示的是右边而trailingPos则表示的是左边。如果您的界面布局不会考虑到国际化以及不需要考虑RTL时那么您可以用leftPos和rightPos来表示左右而不需要用leadingPos和trailingPos。 */ -@property(class, nonatomic, assign) BOOL isRTL; -#else -+(BOOL)isRTL; -+(void)setIsRTL:(BOOL)isRTL; -#endif +@property (class, nonatomic, assign) BOOL isRTL; + + +/** + 直接更新window下所有布局视图的RTL特性。 + */ ++ (void)updateRTL:(BOOL)isRTL inWindow:(UIWindow *)window; +//+ (void)myUpArabicUI:(BOOL)isArabicUI inWindow:(UIWindow *)window MYDEPRECATED("use updateRTL:inWindow: instead"); /* 布局视图里面的padding属性用来设置布局视图的内边距。内边距是指布局视图里面的子视图离自己距离。外边距则是视图与父视图之间的距离。 内边距是在自己的尺寸内离子视图的距离,而外边距则不是自己尺寸内离其他视图的距离。下面是内边距和外边距的效果图: ^ - | topMargin + | margin-top | width +------------------------------+ | |------------> - | l r | rightMargin - | e topPadding i | - | f g | - | t +---------------+ h | - <----------| P | | t | - leftMargin| a | | P | - | d | subviews | a | height - | d | content | d | - | i | | d | - | n | | i | - | g +---------------+ n | - | g | - | bottomPadding | + | p p | margin-right + | a padding-top a | + | d d | + | d +---------------+ d | + <----------| i | | i | + margin-left| n | | n | + | g | subview's | g | height + | | | content | | | + | l | | r | + | e | | i | + | f +---------------+ g | + | t h | + | padding-bottom t | +------------------------------+ - |bottomMargin + |margin-bottom | V @@ -732,74 +622,69 @@ /** * 设置布局视图四周的内边距值。所谓内边距是指布局视图内的所有子视图离布局视图四周的边距。通过为布局视图设置内边距可以减少为所有子视图设置外边距的工作,而外边距则是指视图离父视图四周的距离。 */ -@property(nonatomic,assign) UIEdgeInsets padding; +@property (nonatomic, assign) UIEdgeInsets padding; /** * 顶部内边距,用来设置子视图离自身顶部的边距值。 */ -@property(nonatomic, assign) IBInspectable CGFloat topPadding; +@property (nonatomic, assign) CGFloat paddingTop; /** *头部内边距,用来设置子视图离自身头部的边距值。对于LTR方向的布局来说就是指的左边内边距,而对于RTL方向的布局来说就是指的右边内边距 */ -@property(nonatomic, assign) IBInspectable CGFloat leadingPadding; +@property (nonatomic, assign) CGFloat paddingLeading; /** *底部内边距,用来设置子视图离自身底部的边距值。 */ -@property(nonatomic, assign) IBInspectable CGFloat bottomPadding; +@property (nonatomic, assign) CGFloat paddingBottom; /** *尾部内边距,用来设置子视图离自身尾部的边距值。对于LTR方向的布局来说就是指的右边内边距,而对于RTL方向的布局来说就是指的左边内边距 */ -@property(nonatomic, assign) IBInspectable CGFloat trailingPadding; +@property (nonatomic, assign) CGFloat paddingTrailing; /** - *左边内边距,用来设置子视图离自身左边的边距值。如果您不需要考虑布局的方向,那么请用这个属性来设置左边的内部边距,如果您需要考虑国际化则请用leadingPadding + *左边内边距,用来设置子视图离自身左边的边距值。如果您不需要考虑布局的方向,那么请用这个属性来设置左边的内部边距,如果您需要考虑国际化则请用paddingLeading */ -@property(nonatomic, assign) IBInspectable CGFloat leftPadding; +@property (nonatomic, assign) CGFloat paddingLeft; /** - *右边内边距,用来设置子视图离自身右边的边距值。如果您不需要考虑布局的方向,那么请用这个属性来设置右边的内部边距,如果您需要考虑国际化则请用trailingPadding + *右边内边距,用来设置子视图离自身右边的边距值。如果您不需要考虑布局的方向,那么请用这个属性来设置右边的内部边距,如果您需要考虑国际化则请用paddingTrailing */ -@property(nonatomic, assign) IBInspectable CGFloat rightPadding; - +@property (nonatomic, assign) CGFloat paddingRight; /** * 设置当布局的尺寸由子视图决定并且在没有子视图加入的情况下padding的设置值是否会加入到布局的尺寸值里面。默认是YES,表示当布局视图没有子视图时padding值也会加入到尺寸里面。 - * 举例来说假设某个布局视图的高度是wrapContentHeight,并且设置了topPadding为10,bottomPadding为20。那么默认情况下当没有任何子视图时布局视图的高度是30;而当我们将这个属性设置为NO时,那么在没有任何子视图时布局视图的高度就是0,也就是说topPadding和bottomPadding不会参与高度的计算了。 + * 举例来说假设某个布局视图的高度是自适应,并且设置了paddingTop为10,paddingBottom为20。那么默认情况下当没有任何子视图时布局视图的高度是30;而当我们将这个属性设置为NO时,那么在没有任何子视图时布局视图的高度就是0,也就是说paddingTop和paddingBottom不会参与高度的计算了。 */ -@property(nonatomic, assign) BOOL zeroPadding; - +@property (nonatomic, assign) BOOL zeroPadding; /** 指定padding内边距的缩进是在SafeArea基础之上进行的。默认是UIRectEdgeLeft|UIRectEdgeRight 表示缩进SafeArea所指定的水平区域。你也可以设置只缩进某一个或则几个方向,或者不缩进任何一个方向。这个属性是为了支持iPoneX而设置的。为了支持iPhoneX的全屏幕适配。我们只需要对根布局视图设置这个扩展属性,默认情况下是不需要进行特殊设置的,MyLayout自动会对iPhoneX进行适配。我们知道iOS11中引入了安全区域的概念,MyLayout中的根布局视图会自动将安全区域叠加到设置的padding中去。默认情况下四周的安全区域都会叠加到padding中去,因此您可以根据特殊情况来设置只需要叠加哪一个方向的安全区域。 */ -@property(nonatomic, assign) UIRectEdge insetsPaddingFromSafeArea; - +@property (nonatomic, assign) UIRectEdge insetsPaddingFromSafeArea; /** *当insetsPaddingFromSafeArea同时设置有左右方向同时缩进并且在横屏时是否只缩进有刘海方向的内边距。默认是NO,表示两边都会缩进。如果你想让没有刘海的那一边延伸到屏幕的安全区外,请将这个属性设置为YES。iPhoneX设备中具有一个尺寸为44的刘海区域。当您横屏时为了对齐,左右两边的安全缩进区域都是44。但是有些时候我们希望没有刘海的那一边不需要缩进对齐而是延伸到安全区域以外。这时候您可以通过给根布局视图设置这个属性来达到效果。注意这个属性只有insetsPaddingFromSafeArea设置了左右都缩进时才有效。 */ -@property(nonatomic, assign) BOOL insetLandscapeFringePadding; - +@property (nonatomic, assign) BOOL insetLandscapeFringePadding; /** *设置布局视图里面子视图之间的垂直间距。如果布局内每个子视图的垂直间距都相等则可以用这个属性来设置。这个属性不支持框架布局、相对布局、路径布局。 */ -@property(nonatomic ,assign) IBInspectable CGFloat subviewVSpace; +@property (nonatomic, assign) CGFloat subviewVSpace; /** *设置布局视图里面子视图之间的水平间距。如果布局内每个子视图的水平间距都相等则可以用这个属性来设置。这个属性不支持框架布局、相对布局、路径布局。 */ -@property(nonatomic, assign) IBInspectable CGFloat subviewHSpace; +@property (nonatomic, assign) CGFloat subviewHSpace; /** *设置布局视图里面子视图之间的间距。如果布局内每个子视图的间距都相等则可以用这个属性来设置。这个属性不支持框架布局、相对布局、路径布局。 */ -@property(nonatomic, assign) IBInspectable CGFloat subviewSpace; - +@property (nonatomic, assign) CGFloat subviewSpace; /** 布局里面的所有子视图的整体停靠方向以及填充,所谓停靠是指布局视图里面的所有子视图整体在布局视图中的位置,系统默认的停靠是在布局视图的左上角。 @note - 只有框架布局、线性布局、表格布局、流式布局、浮动布局支持gravity属性,相对布局和路径布局不支持。 + 只有框架布局、线性布局、表格布局、流式布局、浮动布局、栅格布局支持gravity属性,相对布局和路径布局不支持。 1. MyGravity_Vert_Top,MyGravity_Vert_Center,MyGravity_Vert_Bottom 表示整体垂直居上,居中,居下 (支持:框架布局,线性布局,表格布局,流式布局,垂直浮动布局) @@ -809,78 +694,74 @@ 4. MyGravity_Horz_Between 表示每列之间的列间距都被拉伸,以便使里面的子视图水平方向填充满整个布局视图。 (支持:水平线性布局,水平表格布局,流式布局) - 5. MyGravity_Vert_Fill 表示布局会拉伸子视图的高度,以便使里面的子视图垂直方向填充满整个布局视图的高度或者子视图平分布局视图的高度。(支持:框架布局,水平线性布局,水平表格布局,流式布局) + 5. MyGravity_Vert_Around 表示每行之间的行间距都被拉伸,以便使里面的子视图垂直方向填充满整个布局视图,首尾子视图和父视图的间距是子视图兄弟之间的一半。 (支持:垂直线性布局,垂直表格布局,流式布局) + + 6. MyGravity_Horz_Around 表示每列之间的列间距都被拉伸,以便使里面的子视图水平方向填充满整个布局视图,首尾子视图和父视图的间距是子视图兄弟之间的一半。 (支持:水平线性布局,水平表格布局,流式布局) + + 7. MyGravity_Vert_Among 表示每行之间的行间距都被拉伸,以便使里面的子视图垂直方向填充满整个布局视图,首尾子视图和父视图的间距和子视图兄弟之间的间距相等。 (支持:垂直线性布局,垂直表格布局,流式布局) + + 8. MyGravity_Horz_Among 表示每列之间的列间距都被拉伸,以便使里面的子视图水平方向填充满整个布局视图,首尾子视图和父视图的间距和子视图兄弟之间的间距相等。 (支持:水平线性布局,水平表格布局,流式布局) - 6. MyGravity_Horz_Fill 表示布局会拉伸子视图的宽度,以便使里面的子视图水平方向填充满整个布局视图的宽度或者子视图平分布局视图的宽度。 (支持:框架布局,垂直线性布局,垂直表格布局,流式布局) - 7. MyGravity_Vert_Baseline 表示布局里面的子视图都基线对齐,目前只支持水平线性布局。 + 9. MyGravity_Vert_Fill 表示布局会拉伸子视图的高度,以便使里面的子视图垂直方向填充满整个布局视图的高度或者子视图平分布局视图的高度。(支持:框架布局,水平线性布局,水平表格布局,流式布局) + + 10. MyGravity_Horz_Fill 表示布局会拉伸子视图的宽度,以便使里面的子视图水平方向填充满整个布局视图的宽度或者子视图平分布局视图的宽度。 (支持:框架布局,垂直线性布局,垂直表格布局,流式布局) + 11. MyGravity_Vert_Baseline 表示布局里面的子视图都基线对齐,目前只支持水平线性布局。 + 12.MyGravity_Vert_Stretch 表示当布局中的子视图没有设置高度约束时或者非布局子视图高度自适应时,会拉伸子视图的高度。(支持:线性布局、流式布局) + 13.MyGravity_Horz_Stretch 表示当布局中的子视图没有设置宽度约束或者非布局子视图宽度自适应时,会拉伸子视图的宽度。(支持:线性布局、流式布局) */ -@property(nonatomic, assign) IBInspectable MyGravity gravity; - +@property (nonatomic, assign) MyGravity gravity; /** *设置是否布局里面的所有子视图按添加的顺序逆序进行布局。默认是NO,表示按子视图添加的顺序排列。比如一个垂直线性布局依次添加A,B,C三个子视图,那么在正常布局时A,B,C从上到下依次排列,而当这个属性设置为YES时,则布局时就会变成按C,B,A依次从上到下排列。这个属性的设置对框架布局和相对布局无效。 */ -@property(nonatomic, assign) BOOL reverseLayout; +@property (nonatomic, assign) BOOL reverseLayout; +/** + 设置布局内所有子视图的位置的坐标变换,这个坐标变换不会影响到子视图的尺寸。默认值为CGAffineTransformIdentity,表示对子视图不进行任何位置变换。 + 这个属性会对布局视图内所有的子视图进行单独的位置变换,而不是进行整体的变换。在对子视图进行变换时的坐标原点是布局视图的中心点,并且是以子视图自身的中心点作为坐标变换计算的依据。 + + @note + 我们可以设置如下的值进行一些常见的位置变化: + 1.CGAffineTransformIdentity 表示恢复正常,不进行任何位置变换。 + 2.CGAffineTransformMakeTranslation(tx, ty) 表示布局视图内所有子视图的水平位置都将偏移tx, 垂直位置都将偏移ty。 + 3.CGAffineTransformMakeScale(sx,sy) 表示布局视图内所有子视图的水平位置都将缩放sx倍,所有子视图的垂直位置都将缩放sy倍。 + 4.CGAffineTransformMake(-1,0,0,1,0,0) 表示布局视图内所有子视图的位置都将水平翻转,形成水平镜像效果。 + 5.CGAffineTransformMake(1,0,0,-1,0,0) 表示布局视图内所有子视图的位置都将垂直翻转,形成垂直镜像效果。 + 6.CGAffineTransformMake(-1,0,0,-1,0,0) 表示布局视图内所有子视图的位置都将旋转180度。 + 7. 当然我们还可以进行任意的坐标变换,只要您熟悉CGAffineTransform的使用和设置方法。 + */ +@property (nonatomic, assign) CGAffineTransform layoutTransform; /** *把一个布局视图放入到UIScrollView(UITableView和UICollectionView除外)内时是否自动调整UIScrollView的contentSize值。默认是MyAdjustScrollViewContentSizeModeAuto表示布局视图会自动接管UIScrollView的contentSize的值。 你可以将这个属性设置MyAdjustScrollViewContentSizeModeNo而不调整和控制contentSize的值,设置为MyAdjustScrollViewContentSizeModeYes则一定会调整contentSize. */ -@property(nonatomic, assign) MyAdjustScrollViewContentSizeMode adjustScrollViewContentSizeMode; - +@property (nonatomic, assign) MyAdjustScrollViewContentSizeMode adjustScrollViewContentSizeMode; /** *在布局视图进行布局时是否调用基类的layoutSubviews方法,默认设置为NO。 */ -@property(nonatomic, assign) BOOL priorAutoresizingMask; - +@property (nonatomic, assign) BOOL priorAutoresizingMask; /** *设置是否选中状态。您可以用这个状态来记录布局的扩展属性。 */ -@property(nonatomic,getter=isSelected) BOOL selected; // default is NO may be used by some subclasses or by application - - +@property (nonatomic, getter=isSelected) BOOL selected; // default is NO may be used by some subclasses or by application /** *设置布局视图在布局开始之前的处理块。系统会在每次布局完成前调用对应的处理块后将处理块清空为nil。系统会在调用layoutSubviews方法前执行beginLayoutBlock。 */ -@property(nonatomic,copy) void (^beginLayoutBlock)(); +@property (nonatomic, copy) void (^beginLayoutBlock)(void); /** *设置布局视图在布局完成之后的处理块。系统会在每次布局完成后调用对应的处理块后将处理块清空为nil。您也可以在endLayoutBlock块内取到所有子视图真实布局后的frame值。系统会在调用layoutSubviews方法后执行endLayoutBlock。 */ -@property(nonatomic,copy) void (^endLayoutBlock)(); - - - +@property (nonatomic, copy) void (^endLayoutBlock)(void); /** - 删除所有子视图的方便方法 - */ --(void)removeAllSubviews; - - -/** - *设置布局时的动画。并指定时间。这个函数是下面方法的简单实现: - - @code - self.beginLayoutBlock = ^{ - - [UIView beginAnimations:nil context:nil]; - [UIView setAnimationDuration:duration]; - }; - - self.endLayoutBlock = ^{ - - [UIView commitAnimations]; - }; - -@endcode - + *设置布局时的动画。并指定时间,选项,和完成时的处理,这个动画只会在调用后的下次布局时执行一次。 @param duration 指定动画的时间间隔 */ --(void)layoutAnimationWithDuration:(NSTimeInterval)duration; - +- (void)layoutAnimationWithDuration:(NSTimeInterval)duration; +- (void)layoutAnimationWithDuration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion; /** 设置布局视图在第一次布局完成之后或者有横竖屏切换时进行处理的block。这个block不像beginLayoutBlock以及endLayoutBlock那样只会执行一次,而是会一直存在 @@ -893,82 +774,81 @@ isPortrait 表明当前是横屏还是竖屏。 */ -@property(nonatomic,copy) void (^rotationToDeviceOrientationBlock)(MyBaseLayout *layout, BOOL isFirst, BOOL isPortrait); - +@property (nonatomic, copy) void (^rotationToDeviceOrientationBlock)(MyBaseLayout *layout, BOOL isFirst, BOOL isPortrait); /** - *判断当前是否正在布局中,如果正在布局中则返回YES,否则返回NO + 删除所有子视图的方便方法 */ -@property(nonatomic,assign,readonly) BOOL isMyLayouting; +- (void)removeAllSubviews; +/** + *判断当前是否正在布局中,如果正在布局中则返回YES,否则返回NO。 我们可以通过KVO的方式来监听这个属性的变化,以便获取布局完成后的真实frame值。 + */ +@property (nonatomic, assign, readonly) BOOL isMyLayouting; /** *设置布局视图的四周的边界线对象。boundBorderLine是同时设置四周的边界线。您也可以单独设置布局视图某些边的边界线。边界线对象默认是nil,您必须通过建立边界线类MyBorderline的对象实例并赋值给下面的属性来实现边界线的指定。 */ /**顶部边界线*/ -@property(nonatomic, strong) MyBorderline *topBorderline; +@property (nonatomic, strong) MyBorderline *topBorderline; /**头部边界线*/ -@property(nonatomic, strong) MyBorderline *leadingBorderline; +@property (nonatomic, strong) MyBorderline *leadingBorderline; /**底部边界线*/ -@property(nonatomic, strong) MyBorderline *bottomBorderline; +@property (nonatomic, strong) MyBorderline *bottomBorderline; /**尾部边界线*/ -@property(nonatomic, strong) MyBorderline *trailingBorderline; +@property (nonatomic, strong) MyBorderline *trailingBorderline; /**四周边界线*/ -@property(nonatomic, strong) MyBorderline *boundBorderline; +@property (nonatomic, strong) MyBorderline *boundBorderline; /** *如果您不需要考虑国际化的问题则请用这个属性设置左边边界线,否则用leadingBorderline */ -@property(nonatomic, strong) MyBorderline *leftBorderline; +@property (nonatomic, strong) MyBorderline *leftBorderline; /** *如果您不需要考虑国际化的问题则请用这个属性设置右边边界线,否则用trailingBorderline */ -@property(nonatomic, strong) MyBorderline *rightBorderline; - +@property (nonatomic, strong) MyBorderline *rightBorderline; /** *智能边界线,智能边界线不是设置布局自身的边界线而是对添加到布局视图里面的子布局视图根据子视图之间的关系智能的生成边界线,对于布局视图里面的非布局子视图则不会生成边界线。目前的版本只支持线性布局,表格布局,流式布局和浮动布局这四种布局。 *举例来说如果为某个垂直线性布局设置了智能边界线,那么当这垂直线性布局里面添加了A和B两个子布局视图时,系统会智能的在A和B之间绘制一条边界线。 */ -@property(nonatomic, strong) MyBorderline *intelligentBorderline; +@property (nonatomic, strong) MyBorderline *intelligentBorderline; /** *不使用父布局视图提供的智能边界线功能。当某个布局视图的父布局视图设置了intelligentBorderline时但是这个布局视图又想自己定义边界线或者不想要边界线时则将这个属性设置为YES即可。 */ -@property(nonatomic, assign) BOOL notUseIntelligentBorderline; - - +@property (nonatomic, assign) BOOL notUseIntelligentBorderline; /** *在有布局触摸事件时布局按下时背景的高亮的颜色。只有设置了setTarget:action方法此属性才生效。 */ -@property(nonatomic,strong) UIColor *highlightedBackgroundColor; +@property (nonatomic, strong) UIColor *highlightedBackgroundColor; /** *在有布局触摸事件时布局按下时的高亮不透明度。值的范围是[0,1],默认是0表示完全不透明,为1表示完全透明。只有设置了setTarget:action方法此属性才生效。 */ -@property(nonatomic,assign) CGFloat highlightedOpacity; +@property (nonatomic, assign) CGFloat highlightedOpacity; /** *设置布局的背景图片。这个属性的设置就是设置了布局的layer.contents的值,因此如果要实现背景图的局部拉伸请用layer的其他contentsXXX属性进行调整和处理 */ -@property(nonatomic,strong) UIImage *backgroundImage; +@property (nonatomic, strong) UIImage *backgroundImage; /** *在有布局触摸事件时布局按下时的高亮背景图片。只有设置了setTarget:action方法此属性才生效。 */ -@property(nonatomic,strong) UIImage *highlightedBackgroundImage; +@property (nonatomic, strong) UIImage *highlightedBackgroundImage; /** 设置布局的touch up 、touch down、touch cancel事件的处理动作,后两个事件的处理必须依赖于第一个事件的处理。请不要在这些处理动作中修改背景色,不透明度,以及背景图片。如果您只想要高亮效果但是不想处理事件则将action设置为nil即可了。 @param target 事件的处理对象,如果设置为nil则表示取消事件。 @param action 事件的处理动作,格式为:-(void)handleAction:(MyBaseLayout*)sender。 */ --(void)setTarget:(id)target action:(SEL)action; --(void)setTouchDownTarget:(id)target action:(SEL)action; --(void)setTouchCancelTarget:(id)target action:(SEL)action; - +- (void)setTarget:(id)target action:(SEL)action; +- (void)setTouchDownTarget:(id)target action:(SEL)action; +- (void)setTouchCancelTarget:(id)target action:(SEL)action; /** 评估布局视图的尺寸。这个方法并不会让布局视图进行真正的布局,只是对布局的尺寸进行评估,主要用于在布局完成前想预先知道布局尺寸的场景。通过对布局进行尺寸的评估,可以在不进行布局的情况下动态的计算出布局的位置和大小,但需要注意的是这个评估值有可能不是真实显示的实际位置和尺寸。 @@ -978,9 +858,8 @@ 3.sizeThatFits:CGSizeMake(0,100) 表示布局的高度固定为100,而宽度则根据布局的子视图来进行动态评估。 @return 返回评估的尺寸。 */ --(CGSize)sizeThatFits:(CGSize)size; --(CGSize)sizeThatFits:(CGSize)size inSizeClass:(MySizeClass)sizeClass; - +- (CGSize)sizeThatFits:(CGSize)size; +- (CGSize)sizeThatFits:(CGSize)size inSizeClass:(MySizeClass)sizeClass; /** 是否缓存经过sizeThatFits方法评估后的所有子视图的位置和尺寸一次!,默认设置为NO不缓存。当我们用sizeThatFits方法评估布局视图的尺寸后,所有子视图都会生成评估的位置和尺寸,因为此时并没有执行布局所以子视图并没有真实的更新frame值。而当布局视图要进行真实布局时又会重新计算所有子视图的位置和尺寸,因此为了优化性能当我们对布局进行评估后在下次真实布局时我们可以不再重新计算子视图的位置和尺寸而是用前面评估的值来设置位置和尺寸。这个属性设置为YES时则每次评估后到下一次布局时不会再重新计算子视图的布局了,而是用评估值来布局子视图的位置和尺寸。而当这个属性设置为NO时则每次布局都会重新计算子视图的位置和布局。 @@ -990,7 +869,7 @@ MyXXXLayout *rootLayout= [MyXXXLayout new]; rootLayout.cacheEstimatedRect = YES; //设置缓存评估的rect,如果您的cell是高度自适应的话,强烈建立打开这个属性,这会大大的增强您的tableview的性能!! rootLayout.myHorzMargin = 0; //宽度和父视图相等 - rootLayout.wrapContentHeight = YES; //高度动态包裹。 + rootLayout.myHeight = MyLayoutSize.wrap; //高度动态包裹。 [self.contentView addSubview:rootLayout]; self.rootLayout = rootLayout; @@ -1007,9 +886,11 @@ } @endcode + + @note + 这个属性有可能会造成动态高度计算不正确,请只在UITableviewCell的高度为自适应时使用,其他地方不建议设置这个属性!!。如果出现高度计算异常也请勿设置这个属性为YES。 */ -@property(nonatomic, assign) BOOL cacheEstimatedRect; - +@property (nonatomic, assign) BOOL cacheEstimatedRect; /** 评估计算一个未加入到布局视图中的子视图subview在加入后的frame值。在实践中我们希望得到某个未加入的子视图在添加到布局视图后的应该具有的frame值,这时候就可以用这个方法来获取。比如我们希望把一个子视图从一个布局视图里面移到另外一个布局视图的末尾时希望能够提供动画效果,这时候就可以通过这个方法来得到加入后的子视图的位置和尺寸。 @@ -1022,7 +903,7 @@ //得到将A加入到L2后的评估的frame值,注意这时候A还没有加入到L2。 CGRect rectNew = [L2 subview:A estimatedRectInLayoutSize:CGSizeZero]; //将新位置的评估的frame值,这里需要进行坐标转换为S在中的frame。 - rectNew = [L2 convertRect:rectNew toView:self.view]; + rectNew = [L2 convertRect:rectNew toView:S]; //动画的过程是先将A作为S的子视图进行位置的调整后再加入到L2中去 [A removeFromSuperview]; @@ -1048,88 +929,109 @@ @param size 指定布局视图期望的宽度或者高度,一般请将这个值设置为CGSizeZero。 具体请参考sizeThatFits方法中的size的说明。 @return 子视图在布局视图最后一个位置(假如加入后)的frame值。 */ --(CGRect)subview:(UIView*)subview estimatedRectInLayoutSize:(CGSize)size; - +- (CGRect)subview:(UIView *)subview estimatedRectInLayoutSize:(CGSize)size; @end - -@interface MyBaseLayout(MyBaseLayoutDeprecated) +#pragma mark-- MyLayoutDragger +@class MyLayoutDragger; /** - *过期方法,对于间距统一用space来描述,而边距用margin来描述。 - *原先子视图之间的间距属性的命名规范不合理,所以这里将原先的设置间距的属性设置为过期。这里也和TangramKit中的命名统一。 - *您可以在相应的位置定义宏:#define MY_USEOLDMETHODNOWARNING = 1 则不会出现老方法告警,不过不建议这么做。 + 布局视图拖动器类,用来实现布局内的视图的拖动封装。用于实现布局子视图的拖放处理。 +布局视图的拖动器类,只支持那些按顺序添加的布局视图,不支持相对布局、框架布局、栅格布局、路径布局。 + 一般情况下我们要实现布局内子视图的: + 1.UIControlEventTouchDown 事件来处理拖动开始 + 2.UIControlEventTouchDragInside UIControlEventTouchDragOutside 事件来处理拖动进行中 + 3. UIControlEventTouchUpInside UIControlEventTouchCancel 事件来处理拖动结束。 + + 如果您的布局下的子视图不是UIControl类的派生类的话,请分别重载如下事件: + + - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 在这个方法中调用dragView:withEvent: + - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; 在这个方法中调用dragginView:withEvent + - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; + - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 在上述两个方法中调用dropView:withEvent + + 无论何种方式,最终都是在调用完dropView:withEvent方法后,通过读取currentIndex和oldIndex的值来获取位置信息的改变。 + */ +@interface MyLayoutDragger : NSObject -/** -*过期属性,请用subviewVSpace -*/ -@property(nonatomic ,assign, getter=subviewVSpace, setter=setSubviewVSpace:) CGFloat subviewVertMargin MYMETHODDEPRECATED("use subviewVSpace to instead"); +//子视图拖动时,拖动区域当前所归属的子视图位置索引。 +@property (nonatomic, assign, readonly) NSUInteger currentIndex; -/** - *过期属性,请用subviewHSpace - */ -@property(nonatomic, assign, getter=subviewHSpace, setter=setSubviewHSpace:) CGFloat subviewHorzMargin MYMETHODDEPRECATED("use subviewHSpace to instead"); +//子视图拖动时,拖动视图的老的位置索引。 +@property (nonatomic, assign, readonly) NSUInteger oldIndex; -/** - *过期属性,请用subviewSpace - */ -@property(nonatomic, assign, getter=subviewSpace, setter=setSubviewSpace:) CGFloat subviewMargin MYMETHODDEPRECATED("use subviewSpace to instead"); +//当前是否正在拖动中。 +@property (nonatomic, assign, readonly) BOOL hasDragging; -/** - *过期方法, 原先对边界线命名BorderLine不符合规则,Borderline是一个单词不是一个词组。这里也和TangramKit中的命名统一。 - */ +//设置拖动时不会调整的子视图列表。也就是说数组中指定的子视图在拖动时不会被移动而是总是固定在原有的位置。 +@property (nonatomic, strong) NSArray *exclusiveViews; -/** - *过期属性,请用leftBorderline - */ -@property(nonatomic, strong, getter=leftBorderline, setter=setLeftBorderline:) MyBorderline *leftBorderLine MYMETHODDEPRECATED("use leftBorderline to instead"); +//设置拖动时位置调整的动画时长,默认是0秒,设置为0时拖动不产生动画效果。设置为0.2效果较好。 +@property (nonatomic, assign) NSTimeInterval animateDuration; -/** - *过期属性,请用rightBorderline - */ -@property(nonatomic, strong, getter=rightBorderline, setter=setRightBorderline:) MyBorderline *rightBorderLine MYMETHODDEPRECATED("use rightBorderline to instead"); +//当拖动的视图和现有视图重叠时是否支持悬停功能,默认为NO。当开启开关后,并且oldIndex和currentIndex相等时则处于悬停状态。开启悬停功能的目的是为了支持一些替换或者更新的能力。 +@property (nonatomic, assign) BOOL canHover; -/** - *过期属性,请用topBorderline - */ -@property(nonatomic, strong, getter=topBorderline, setter=setTopBorderline:) MyBorderline *topBorderLine MYMETHODDEPRECATED("use topBorderline to instead"); +//当前是否正在悬停中。我们可以借助这个属性值判断来做一些操作处理。 +@property (nonatomic, assign, readonly) BOOL isHovering; -/** - *过期属性,请用bottomBorderline - */ -@property(nonatomic, strong, getter=bottomBorderline, setter=setBottomBorderline:) MyBorderline *bottomBorderLine MYMETHODDEPRECATED("use bottomBorderline to instead"); -/** - *过期属性,请用boundBorderline - */ -@property(nonatomic, strong, getter=boundBorderline, setter=setBoundBorderline:) MyBorderline *boundBorderLine MYMETHODDEPRECATED("use boundBorderline to instead"); +//下列方法请在子视图的相应事件处理中调用。 -/** - *过期属性,请用intelligentBorderline - */ -@property(nonatomic, strong, getter=intelligentBorderline, setter=setIntelligentBorderline:) MyBorderline *IntelligentBorderLine MYMETHODDEPRECATED("use intelligentBorderline to instead"); +//开始拖动,请在子视图view的拖动开始事件处调用这个方法,其中view指定要开始拖动的视图。 +- (void)dragView:(UIView *)view withEvent:(UIEvent *)event; +//拖动中,请在子视图view的拖动过程事件中调用这个方法,其中的view指定拖动中的视图。 +- (void)dragginView:(UIView *)view withEvent:(UIEvent *)event; +//结束拖动,请在子视图view的结束拖动事件中调用这个方法,其中的view指定要结束拖动的视图。 +- (void)dropView:(UIView *)view withEvent:(UIEvent *)event; -/** - *过期属性,请用notUseIntelligentBorderline - */ -@property(nonatomic, assign, getter=notUseIntelligentBorderline, setter=setNotUseIntelligentBorderline:) BOOL notUseIntelligentBorderLine MYMETHODDEPRECATED("use notUseIntelligentBorderline to instead"); +@end + +@interface MyBaseLayout (MyLayoutDragger) + +//创建一个布局视图拖动器。要求拖动的视图必须是布局视图中的子视图。 +- (MyLayoutDragger *)createLayoutDragger; + +@end +#pragma mark-- Deprecated + +@interface UIView (MyDeprecated) /** - *这个属性在新版本将失效并且无任何意义了。如果想让子视图隐藏时是否继续占据位置则请参考使用子视图的myVisibility属性来进行单独设置。 + *!!!不建议使用这个属性了,而是直接设置视图的宽度为MyLayoutSize.wrap。 比如:myWidth = MyLayoutSize.wrap 或者 widthSize.equalTo(@(MyLayoutSize.wrap))。 + *获取和判断的时候可以通过 myWidth == MyLayoutSize.wrap 或者 widthSize.isWrap 来比较判断 + *视图的宽度包裹属性,表示视图的宽度由所有子视图的整体宽度来确定或者根据视图内容来宽度自适应。默认值是NO(水平线性布局默认这个属性是YES),表示必须要明确指定视图的宽度,而当设置为YES时则不需要明确的指定视图的宽度了。这个属性不能和widthSize(或者设置了左右边距)同时设置否则可能会产生约束冲突,因为前者表明宽度由子视图或者内容来决定而后者则表示宽度是一个明确的值。如果同时设置了宽度包裹属性又同时设置了明确的宽度则系统会出现约束冲突告警,虽然如此,系统在布局时做了一些优化,如果同时设置了明确的宽度和宽度包裹则会在布局前将宽度包裹属性置为NO。 */ -@property(nonatomic, assign) BOOL hideSubviewReLayout MYMETHODDEPRECATED("this property was invalid, please use subview's myVisibility to instead"); - +@property (nonatomic, assign) BOOL wrapContentWidth; /** - *过期方法。请用sizeThatFits方法来代替。 + *!!!不建议使用这个属性了,而是直接设置视图的高度为MyLayoutSize.wrap。 比如:myHeight = MyLayoutSize.wrap 或者 heightSize.equalTo(@(MyLayoutSize.wrap))。 + *获取和判断的时候可以通过 myHeight == MyLayoutSize.wrap 或者 heightSize.isWrap 来比较判断 + + *视图的高度包裹属性,表示视图的高度由所有子视图的整体高度来确定或者根据视图内容来高度自适应。默认值是NO(垂直线性布局默认这个属性是YES),表示必须要明确指定布局的高度,而当设置为YES时则不需要明确的指定视图的高度了。这个属性不能和heightSize(或者设置了上下边距)同时设置否则可能会产生约束冲突,因为前者表明高度由子视图或者内容来决定而后者则表示高度是一个明确的值。如果同时设置了高度包裹属性又同时设置了明确的高度则系统会出现约束冲突告警,虽然如此,系统在布局时做了一些优化,如果同时设置了明确的高度和高度包裹则会在布局前将高度包裹属性置为NO。如果某个非布局视图指定了明确的宽度,而又将这个属性设置为了YES的话就能实现在固定宽度的情况下视图的高度根据内容自适应的效果,这个特性主要用于UILabel,UITextView以及实现了sizeThatFits方法的视图来实现高度根据内容自适应的场景。UILabel在使用这个属性时会自动设置numberOfLines为0,因此如果您要修改numberOfLines则需要在设置这个属性后进行;UITextView可以用这个属性以及heightSize中的max方法来实现到达指定的高度后若继续输入则产生滚动的效果;UIImageView可以用这个属性来在实现在确定宽度的情况下高度根据宽度的缩放情况进行等比例的缩放。 */ --(CGRect)estimateLayoutRect:(CGSize)size MYMETHODDEPRECATED("this method was invalid, please use sizeThatFits: to instead"); --(CGRect)estimateLayoutRect:(CGSize)size inSizeClass:(MySizeClass)sizeClass MYMETHODDEPRECATED("this method was invalid, please use sizeThatFits:inSizeClass: to instead"); +@property (nonatomic, assign) BOOL wrapContentHeight; +/** + *不建议使用这个属性了!直接设置尺寸为MyLayoutSize.wrap即可。 + *视图的尺寸根据内容来决定,也就是视图的尺寸由内容包裹。这个属性是: A.wrapContentWidth = YES; A.wrapContentHeight = YES;的简化版本。 + *在历史版本中对UILabel进行text赋值后总要手动调用sizeToFit来重新更新布局,在1.3.6以后的新版本中,如果你希望视图的尺寸根据内容而确定则请将这个属性 + *设置为YES。这样就可以在设置完毕text后系统会自动激发布局处理。 + */ +@property (nonatomic, assign) BOOL wrapContentSize; @end - +//@interface MyBaseLayout(MyDeprecated) +// +//@property (nonatomic, assign) CGFloat topPadding MYDEPRECATED("use paddingTop instead"); +//@property (nonatomic, assign) CGFloat leadingPadding MYDEPRECATED("use paddingLeading instead"); +//@property (nonatomic, assign) CGFloat bottomPadding MYDEPRECATED("use paddingBottom instead"); +//@property (nonatomic, assign) CGFloat trailingPadding MYDEPRECATED("use paddingTrailing instead"); +//@property (nonatomic, assign) CGFloat leftPadding MYDEPRECATED("use paddingLeft instead"); +//@property (nonatomic, assign) CGFloat rightPadding MYDEPRECATED("use paddingRight instead"); +// +//@end diff --git a/MyLayout/Lib/MyBaseLayout.m b/MyLayout/Lib/MyBaseLayout.m index 5c0d021..93f9b6a 100644 --- a/MyLayout/Lib/MyBaseLayout.m +++ b/MyLayout/Lib/MyBaseLayout.m @@ -7,3506 +7,2985 @@ // #import "MyBaseLayout.h" -#import "MyLayoutInner.h" #import "MyLayoutDelegate.h" +#import "MyLayoutInner.h" #import +const char *const ASSOCIATEDOBJECT_KEY_MYLAYOUT_ENGINE = "ASSOCIATEDOBJECT_KEY_MYLAYOUT_ENGINE"; -const char * const ASSOCIATEDOBJECT_KEY_MYLAYOUT_FRAME = "ASSOCIATEDOBJECT_KEY_MYLAYOUT_FRAME"; +void *_myObserverContextA = (void *)20175281; +void *_myObserverContextB = (void *)20175282; +void *_myObserverContextC = (void *)20175283; -void* _myObserverContextA = (void*)20175281; -void* _myObserverContextB = (void*)20175282; -void* _myObserverContextC = (void*)20175283; +@interface MyLayoutDragger () +@property (nonatomic, assign) NSUInteger currentIndex; +@property (nonatomic, assign) NSUInteger oldIndex; +@property (nonatomic, assign) BOOL hasDragging; +@property (nonatomic, weak) MyBaseLayout *layout; -@implementation UIView(MyLayoutExt) - --(MyLayoutPos*)topPos -{ - return self.myCurrentSizeClass.topPos; -} +@end --(MyLayoutPos*)leadingPos -{ - return self.myCurrentSizeClass.leadingPos; -} +/** + 窗口对RTL的支持。 + */ +@interface UIWindow (MyLayoutExt) +- (void)myUpdateRTL:(BOOL)isRTL; --(MyLayoutPos*)bottomPos -{ - return self.myCurrentSizeClass.bottomPos; -} +@end +@implementation UIView (MyLayoutExt) --(MyLayoutPos*)trailingPos -{ - return self.myCurrentSizeClass.trailingPos; +- (MyLayoutPos *)topPos { + return self.myDefaultSizeClass.topPos; } - - --(MyLayoutPos*)centerXPos -{ - return self.myCurrentSizeClass.centerXPos; +- (MyLayoutPos *)leadingPos { + return self.myDefaultSizeClass.leadingPos; } - --(MyLayoutPos*)centerYPos -{ - return self.myCurrentSizeClass.centerYPos; +- (MyLayoutPos *)bottomPos { + return self.myDefaultSizeClass.bottomPos; } - --(MyLayoutPos*)leftPos -{ - return self.myCurrentSizeClass.leftPos; +- (MyLayoutPos *)trailingPos { + return self.myDefaultSizeClass.trailingPos; } --(MyLayoutPos*)rightPos -{ - return self.myCurrentSizeClass.rightPos; +- (MyLayoutPos *)centerXPos { + return self.myDefaultSizeClass.centerXPos; } --(MyLayoutPos*)baselinePos -{ - return self.myCurrentSizeClass.baselinePos; +- (MyLayoutPos *)centerYPos { + return self.myDefaultSizeClass.centerYPos; } - - --(CGFloat)myTop -{ - return self.myCurrentSizeClass.myTop; +- (MyLayoutPos *)leftPos { + return self.myDefaultSizeClass.leftPos; } --(void)setMyTop:(CGFloat)myTop -{ - self.myCurrentSizeClass.myTop = myTop; +- (MyLayoutPos *)rightPos { + return self.myDefaultSizeClass.rightPos; } --(CGFloat)myLeading -{ - return self.myCurrentSizeClass.myLeading; +- (MyLayoutPos *)baselinePos { + return self.myDefaultSizeClass.baselinePos; } --(void)setMyLeading:(CGFloat)myLeading -{ - self.myCurrentSizeClass.myLeading = myLeading; +- (CGFloat)myTop { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myTop; } - --(CGFloat)myBottom -{ - return self.myCurrentSizeClass.myBottom; +- (void)setMyTop:(CGFloat)myTop { + self.myDefaultSizeClass.myTop = myTop; } --(void)setMyBottom:(CGFloat)myBottom -{ - self.myCurrentSizeClass.myBottom = myBottom; +- (CGFloat)myLeading { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myLeading; } - - --(CGFloat)myTrailing -{ - return self.myCurrentSizeClass.myTrailing; +- (void)setMyLeading:(CGFloat)myLeading { + self.myDefaultSizeClass.myLeading = myLeading; } --(void)setMyTrailing:(CGFloat)myTrailing -{ - self.myCurrentSizeClass.myTrailing = myTrailing; +- (CGFloat)myBottom { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myBottom; } - --(CGFloat)myCenterX -{ - return self.myCurrentSizeClass.myCenterX; +- (void)setMyBottom:(CGFloat)myBottom { + self.myDefaultSizeClass.myBottom = myBottom; } --(void)setMyCenterX:(CGFloat)myCenterX -{ - self.myCurrentSizeClass.myCenterX = myCenterX; +- (CGFloat)myTrailing { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myTrailing; } --(CGFloat)myCenterY -{ - return self.myCurrentSizeClass.myCenterY; +- (void)setMyTrailing:(CGFloat)myTrailing { + self.myDefaultSizeClass.myTrailing = myTrailing; } --(void)setMyCenterY:(CGFloat)myCenterY -{ - self.myCurrentSizeClass.myCenterY = myCenterY; +- (CGFloat)myCenterX { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myCenterX; } - --(CGPoint)myCenter -{ - return self.myCurrentSizeClass.myCenter; +- (void)setMyCenterX:(CGFloat)myCenterX { + self.myDefaultSizeClass.myCenterX = myCenterX; } --(void)setMyCenter:(CGPoint)myCenter -{ - self.myCurrentSizeClass.myCenter = myCenter; +- (CGFloat)myCenterY { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myCenterY; } - --(CGFloat)myLeft -{ - return self.myCurrentSizeClass.myLeft; +- (void)setMyCenterY:(CGFloat)myCenterY { + self.myDefaultSizeClass.myCenterY = myCenterY; } --(void)setMyLeft:(CGFloat)myLeft -{ - self.myCurrentSizeClass.myLeft = myLeft; +- (CGPoint)myCenter { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myCenter; } --(CGFloat)myRight -{ - return self.myCurrentSizeClass.myRight; +- (void)setMyCenter:(CGPoint)myCenter { + self.myDefaultSizeClass.myCenter = myCenter; } --(void)setMyRight:(CGFloat)myRight -{ - self.myCurrentSizeClass.myRight = myRight; +- (CGFloat)myLeft { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myLeft; } - - --(CGFloat)myMargin -{ - return self.myCurrentSizeClass.myMargin; +- (void)setMyLeft:(CGFloat)myLeft { + self.myDefaultSizeClass.myLeft = myLeft; } --(void)setMyMargin:(CGFloat)myMargin -{ - self.myCurrentSizeClass.myMargin = myMargin; +- (CGFloat)myRight { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myRight; } --(CGFloat)myHorzMargin -{ - return self.myCurrentSizeClass.myHorzMargin; +- (void)setMyRight:(CGFloat)myRight { + self.myDefaultSizeClass.myRight = myRight; } --(void)setMyHorzMargin:(CGFloat)myHorzMargin -{ - self.myCurrentSizeClass.myHorzMargin = myHorzMargin; +- (CGFloat)myMargin { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myMargin; } - --(CGFloat)myVertMargin -{ - return self.myCurrentSizeClass.myVertMargin; +- (void)setMyMargin:(CGFloat)myMargin { + self.myDefaultSizeClass.myMargin = myMargin; } --(void)setMyVertMargin:(CGFloat)myVertMargin -{ - self.myCurrentSizeClass.myVertMargin = myVertMargin; +- (CGFloat)myHorzMargin { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myHorzMargin; } - --(MyLayoutSize*)widthSize -{ - return self.myCurrentSizeClass.widthSize; +- (void)setMyHorzMargin:(CGFloat)myHorzMargin { + self.myDefaultSizeClass.myHorzMargin = myHorzMargin; } - - --(MyLayoutSize*)heightSize -{ - return self.myCurrentSizeClass.heightSize; +- (CGFloat)myVertMargin { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.myVertMargin; } +- (void)setMyVertMargin:(CGFloat)myVertMargin { + self.myDefaultSizeClass.myVertMargin = myVertMargin; +} --(CGFloat)myWidth -{ - return self.myCurrentSizeClass.myWidth; +- (MyLayoutSize *)widthSize { + return self.myDefaultSizeClass.widthSize; } +- (MyLayoutSize *)heightSize { + return self.myDefaultSizeClass.heightSize; +} --(void)setMyWidth:(CGFloat)myWidth -{ - self.myCurrentSizeClass.myWidth = myWidth; +- (CGFloat)myWidth { + return self.myDefaultSizeClassInner.myWidth; } --(CGFloat)myHeight -{ - return self.myCurrentSizeClass.myHeight; +- (void)setMyWidth:(CGFloat)myWidth { + self.myDefaultSizeClass.myWidth = myWidth; } --(void)setMyHeight:(CGFloat)myHeight -{ - self.myCurrentSizeClass.myHeight = myHeight; +- (CGFloat)myHeight { + return self.myDefaultSizeClassInner.myHeight; } --(CGSize)mySize -{ - return self.myCurrentSizeClass.mySize; +- (void)setMyHeight:(CGFloat)myHeight { + self.myDefaultSizeClass.myHeight = myHeight; } --(void)setMySize:(CGSize)mySize -{ - self.myCurrentSizeClass.mySize = mySize; +- (CGSize)mySize { +#if DEBUG + NSLog(@"%s 一般只用于设置,而不能用于获取!!", sel_getName(_cmd)); +#endif + return self.myDefaultSizeClassInner.mySize; } +- (void)setMySize:(CGSize)mySize { + self.myDefaultSizeClass.mySize = mySize; +} --(void)setWrapContentHeight:(BOOL)wrapContentHeight -{ - UIView *sc = self.myCurrentSizeClass; - if (sc.wrapContentHeight != wrapContentHeight) - { - sc.wrapContentHeight = wrapContentHeight; - if (self.superview != nil) +- (void)setWrapContentHeight:(BOOL)wrapContentHeight { + MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; + if (viewTraits.wrapContentHeight != wrapContentHeight) { + viewTraits.wrapContentHeight = wrapContentHeight; + if (self.superview != nil) { [self.superview setNeedsLayout]; + } } } --(BOOL)wrapContentHeight -{ +- (BOOL)wrapContentHeight { //特殊处理,减少不必要的对象创建 - return self.myCurrentSizeClassInner.wrapContentHeight; + return self.myDefaultSizeClassInner.wrapContentHeight; } --(void)setWrapContentWidth:(BOOL)wrapContentWidth -{ - UIView *sc = self.myCurrentSizeClass; - if (sc.wrapContentWidth != wrapContentWidth) - { - sc.wrapContentWidth = wrapContentWidth; - if (self.superview != nil) +- (void)setWrapContentWidth:(BOOL)wrapContentWidth { + MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; + if (viewTraits.wrapContentWidth != wrapContentWidth) { + viewTraits.wrapContentWidth = wrapContentWidth; + if (self.superview != nil) { [self.superview setNeedsLayout]; + } } - } --(BOOL)wrapContentWidth -{ +- (BOOL)wrapContentWidth { //特殊处理,减少不必要的对象创建 - return self.myCurrentSizeClassInner.wrapContentWidth; + return self.myDefaultSizeClassInner.wrapContentWidth; } - --(BOOL)wrapContentSize -{ - return self.myCurrentSizeClassInner.wrapContentSize; +- (BOOL)wrapContentSize { + return self.myDefaultSizeClassInner.wrapContentSize; } --(void)setWrapContentSize:(BOOL)wrapContentSize -{ - UIView *sc = self.myCurrentSizeClass; - if (sc.wrapContentSize != wrapContentSize) - { - sc.wrapContentSize = wrapContentSize; - if (self.superview != nil) - [self.superview setNeedsLayout]; +- (void)setWrapContentSize:(BOOL)wrapContentSize { + MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; + viewTraits.wrapContentSize = wrapContentSize; + if (self.superview != nil) { + [self.superview setNeedsLayout]; } } --(CGFloat)weight -{ - return self.myCurrentSizeClass.weight; +- (CGFloat)weight { + return self.myDefaultSizeClassInner.weight; } --(void)setWeight:(CGFloat)weight -{ - UIView *sc = self.myCurrentSizeClass; - if (sc.weight != weight) - { - sc.weight = weight; - if (self.superview != nil) +- (void)setWeight:(CGFloat)weight { + MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; + if (viewTraits.weight != weight) { + viewTraits.weight = weight; + if (self.superview != nil) { [self.superview setNeedsLayout]; + } } } - - --(BOOL)useFrame -{ - return self.myCurrentSizeClass.useFrame; +- (BOOL)useFrame { + return self.myDefaultSizeClassInner.useFrame; } --(void)setUseFrame:(BOOL)useFrame -{ - UIView *sc = self.myCurrentSizeClass; - if (sc.useFrame != useFrame) - { - sc.useFrame = useFrame; - if (self.superview != nil) - [ self.superview setNeedsLayout]; +- (void)setUseFrame:(BOOL)useFrame { + MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; + if (viewTraits.useFrame != useFrame) { + viewTraits.useFrame = useFrame; + if (self.superview != nil) { + [self.superview setNeedsLayout]; + } } - } --(BOOL)noLayout -{ - return self.myCurrentSizeClass.noLayout; +- (BOOL)noLayout { + return self.myDefaultSizeClassInner.noLayout; } --(void)setNoLayout:(BOOL)noLayout -{ - UIView *sc = self.myCurrentSizeClass; - if (sc.noLayout != noLayout) - { - sc.noLayout = noLayout; - if (self.superview != nil) - [ self.superview setNeedsLayout]; +- (void)setNoLayout:(BOOL)noLayout { + MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; + if (viewTraits.noLayout != noLayout) { + viewTraits.noLayout = noLayout; + if (self.superview != nil) { + [self.superview setNeedsLayout]; + } } - } --(MyVisibility)myVisibility -{ - return self.myCurrentSizeClass.myVisibility; +- (MyVisibility)visibility { + return self.myDefaultSizeClassInner.visibility; } --(void)setMyVisibility:(MyVisibility)myVisibility -{ - UIView *sc = self.myCurrentSizeClass; - if (sc.myVisibility != myVisibility) - { - sc.myVisibility = myVisibility; - if (myVisibility == MyVisibility_Visible) - self.hidden = NO; - else - self.hidden = YES; - - if (self.superview != nil) - [ self.superview setNeedsLayout]; +- (void)setVisibility:(MyVisibility)visibility { + MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; + if (viewTraits.visibility != visibility) { + viewTraits.visibility = visibility; + self.hidden = (visibility != MyVisibility_Visible); + if (self.superview != nil) { + //修复布局视图在从隐藏转到不隐藏并且有尺寸自适应时,位置和尺寸不会重新计算的BUG。 + if (!self.isHidden && + [self isKindOfClass:MyBaseLayout.class] && (viewTraits.widthSizeInner.isWrap || viewTraits.heightSizeInner.isWrap)) { + [self setNeedsLayout]; + } + [self.superview setNeedsLayout]; + } } - } --(MyGravity)myAlignment -{ - return self.myCurrentSizeClass.myAlignment; +- (MyGravity)alignment { + return self.myDefaultSizeClassInner.alignment; } --(void)setMyAlignment:(MyGravity)myAlignment -{ - UIView *sc = self.myCurrentSizeClass; - if (sc.myAlignment != myAlignment) - { - sc.myAlignment = myAlignment; - if (self.superview != nil) - [ self.superview setNeedsLayout]; +- (void)setAlignment:(MyGravity)alignment { + MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; + if (viewTraits.alignment != alignment) { + viewTraits.alignment = alignment; + if (self.superview != nil) { + [self.superview setNeedsLayout]; + } } - } - --(void (^)(MyBaseLayout*, UIView*))viewLayoutCompleteBlock -{ - return self.myCurrentSizeClass.viewLayoutCompleteBlock; +- (void (^)(MyBaseLayout *, UIView *))viewLayoutCompleteBlock { + return self.myDefaultSizeClassInner.viewLayoutCompleteBlock; } --(void)setViewLayoutCompleteBlock:(void (^)(MyBaseLayout *, UIView *))viewLayoutCompleteBlock -{ - self.myCurrentSizeClass.viewLayoutCompleteBlock = viewLayoutCompleteBlock; +- (void)setViewLayoutCompleteBlock:(void (^)(MyBaseLayout *, UIView *))viewLayoutCompleteBlock { + self.myDefaultSizeClass.viewLayoutCompleteBlock = viewLayoutCompleteBlock; } - - - - --(CGRect)estimatedRect -{ - CGRect rect = self.myFrame.frame; - if (rect.size.width == CGFLOAT_MAX || rect.size.height == CGFLOAT_MAX) +- (CGRect)estimatedRect { + CGRect rect = self.myEngine.frame; + if (rect.size.width == CGFLOAT_MAX || rect.size.height == CGFLOAT_MAX) { return self.frame; + } return rect; } - - --(void)resetMyLayoutSetting -{ +- (void)resetMyLayoutSetting { [self resetMyLayoutSettingInSizeClass:MySizeClass_wAny | MySizeClass_hAny]; } --(void)resetMyLayoutSettingInSizeClass:(MySizeClass)sizeClass -{ - [self.myFrame.sizeClasses removeObjectForKey:@(sizeClass)]; +- (void)resetMyLayoutSettingInSizeClass:(MySizeClass)sizeClass { + [self.myEngine.sizeClasses removeObjectForKey:@(sizeClass)]; } - - - - - --(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass -{ +- (instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass { return [self fetchLayoutSizeClass:sizeClass copyFrom:0xFF]; } --(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass -{ - MyFrame *myFrame = self.myFrame; - if (myFrame.sizeClasses == nil) - myFrame.sizeClasses = [NSMutableDictionary new]; - - MyViewSizeClass *myLayoutSizeClass = (MyViewSizeClass*)[myFrame.sizeClasses objectForKey:@(sizeClass)]; - if (myLayoutSizeClass == nil) - { - MyViewSizeClass *srcLayoutSizeClass = (MyViewSizeClass*)[myFrame.sizeClasses objectForKey:@(srcSizeClass)]; - if (srcLayoutSizeClass == nil) - myLayoutSizeClass = [self createSizeClassInstance]; - else - myLayoutSizeClass = [srcLayoutSizeClass copy]; - myLayoutSizeClass.view = self; - [myFrame.sizeClasses setObject:myLayoutSizeClass forKey:@(sizeClass)]; - } - - return (UIView*)myLayoutSizeClass; - -} - - - -@end - -@implementation UIView(MyLayoutExtDeprecated) - - - - --(CGFloat)myLeftMargin -{ - return self.myLeft; +- (instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass { + return (UIView *)[self.myEngine fetchView:self layoutSizeClass:sizeClass copyFrom:srcSizeClass]; } --(void)setMyLeftMargin:(CGFloat)myLeftMargin -{ - self.myLeft = myLeftMargin; +- (void)setLayoutSizeClass:(MySizeClass)sizeClass withValue:(id)value { + [self.myEngine setView:self layoutSizeClass:sizeClass withTraits:value]; } --(CGFloat)myTopMargin -{ - return self.myTop; -} +@end --(void)setMyTopMargin:(CGFloat)myTopMargin -{ - self.myTop = myTopMargin; -} +@implementation UIView (MyLayoutExtInner) --(CGFloat)myRightMargin -{ - return self.myRight; +- (instancetype)myDefaultSizeClass { + MyLayoutEngine *viewEngine = self.myEngine; + if (viewEngine.defaultSizeClass == nil) { + viewEngine.defaultSizeClass = [viewEngine fetchView:self layoutSizeClass:MySizeClass_wAny | MySizeClass_hAny copyFrom:0xFF]; + } + return (UIView *)viewEngine.defaultSizeClass; } --(void)setMyRightMargin:(CGFloat)myRightMargin -{ - self.myRight = myRightMargin; +- (instancetype)myDefaultSizeClassInner { + return (UIView *)self.myEngineInner.defaultSizeClass; } --(CGFloat)myBottomMargin -{ - return self.myBottom; +- (instancetype)myCurrentSizeClass { + MyLayoutEngine *viewEngine = self.myEngine; + if (viewEngine.currentSizeClass == nil) { + viewEngine.currentSizeClass = (MyViewTraits *)[self myDefaultSizeClass]; + } + return (UIView *)viewEngine.currentSizeClass; } --(void)setMyBottomMargin:(CGFloat)myBottomMargin -{ - self.myBottom = myBottomMargin; +- (instancetype)myCurrentSizeClassInner { + //如果没有则不会建立,为了优化减少不必要的建立。 + return (UIView *)self.myEngineInner.currentSizeClass; } --(MyLayoutSize*)widthDime -{ - return self.widthSize; +- (MyLayoutEngine *)myEngine { + MyLayoutEngine *obj = objc_getAssociatedObject(self, ASSOCIATEDOBJECT_KEY_MYLAYOUT_ENGINE); + if (obj == nil) { + obj = [MyLayoutEngine new]; + objc_setAssociatedObject(self, ASSOCIATEDOBJECT_KEY_MYLAYOUT_ENGINE, obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + } + return obj; } - - --(MyLayoutSize*)heightDime -{ - return self.heightSize; +- (MyLayoutEngine *)myEngineInner { + return (MyLayoutEngine*)objc_getAssociatedObject(self, ASSOCIATEDOBJECT_KEY_MYLAYOUT_ENGINE); } - --(CGFloat)myCenterXOffset -{ - return self.myCenterX; +- (id)createSizeClassInstance { + return [MyViewTraits new]; } --(void)setMyCenterXOffset:(CGFloat)myCenterXOffset -{ - self.myCenterX = myCenterXOffset; +- (MyLayoutPos *)topPosInner { + return self.myDefaultSizeClassInner.topPosInner; } --(CGFloat)myCenterYOffset -{ - return self.myCenterY; +- (MyLayoutPos *)leadingPosInner { + return self.myDefaultSizeClassInner.leadingPosInner; } --(void)setMyCenterYOffset:(CGFloat)myCenterYOffset -{ - self.myCenterY = myCenterYOffset; +- (MyLayoutPos *)bottomPosInner { + return self.myDefaultSizeClassInner.bottomPosInner; } - --(CGPoint)myCenterOffset -{ - return self.myCenter; +- (MyLayoutPos *)trailingPosInner { + return self.myDefaultSizeClassInner.trailingPosInner; } --(void)setMyCenterOffset:(CGPoint)myCenterOffset -{ - self.myCenter = myCenterOffset; +- (MyLayoutPos *)centerXPosInner { + return self.myDefaultSizeClassInner.centerXPosInner; } - - --(void)setFlexedHeight:(BOOL)flexedHeight -{ - self.wrapContentHeight = flexedHeight; +- (MyLayoutPos *)centerYPosInner { + return self.myDefaultSizeClassInner.centerYPosInner; } --(BOOL)isFlexedHeight -{ - return self.wrapContentHeight; +- (MyLayoutPos *)leftPosInner { + return self.myDefaultSizeClassInner.leftPosInner; } - - -@end - - -@implementation UIView(MyLayoutExtInner) - - --(instancetype)myDefaultSizeClass -{ - return [self fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hAny]; +- (MyLayoutPos *)rightPosInner { + return self.myDefaultSizeClassInner.rightPosInner; } - --(instancetype)myCurrentSizeClass -{ - MyFrame *myFrame = self.myFrame; //减少多次访问,增加性能。 - if (myFrame.sizeClass == nil) - myFrame.sizeClass = [self myDefaultSizeClass]; - - return myFrame.sizeClass; +- (MyLayoutPos *)baselinePosInner { + return self.myDefaultSizeClassInner.baselinePosInner; } --(instancetype)myCurrentSizeClassInner -{ - //如果没有则不会建立,为了优化减少不必要的建立。 - MyFrame *obj = objc_getAssociatedObject(self, ASSOCIATEDOBJECT_KEY_MYLAYOUT_FRAME); - return obj.sizeClass; +- (MyLayoutSize *)widthSizeInner { + return self.myDefaultSizeClassInner.widthSizeInner; } --(instancetype)myCurrentSizeClassFrom:(MyFrame*)myFrame -{ - if (myFrame.sizeClass == nil) - myFrame.sizeClass = [self myDefaultSizeClass]; - - return myFrame.sizeClass; +- (MyLayoutSize *)heightSizeInner { + return self.myDefaultSizeClassInner.heightSizeInner; } - - - --(instancetype)myBestSizeClass:(MySizeClass)sizeClass -{ - - MySizeClass wsc = sizeClass & 0x03; - MySizeClass hsc = sizeClass & 0x0C; - MySizeClass ori = sizeClass & 0xC0; - - MyFrame *myFrame = self.myFrame; - - if (myFrame.sizeClasses == nil) - myFrame.sizeClasses = [NSMutableDictionary new]; - - - MySizeClass searchSizeClass; - MyViewSizeClass *myClass = nil; - if (myFrame.multiple) - { - //first search the most exact SizeClass - searchSizeClass = wsc | hsc | ori; - myClass = (MyViewSizeClass*)[myFrame.sizeClasses objectForKey:@(searchSizeClass)]; - if (myClass != nil) - return (UIView*)myClass; - - - searchSizeClass = wsc | hsc; - if (searchSizeClass != sizeClass) - { - MyViewSizeClass *myClass = (MyViewSizeClass*)[myFrame.sizeClasses objectForKey:@(searchSizeClass)]; - if (myClass != nil) - return (UIView*)myClass; - } - - - searchSizeClass = MySizeClass_wAny | hsc | ori; - if (ori != 0 && searchSizeClass != sizeClass) - { - myClass = (MyViewSizeClass*)[myFrame.sizeClasses objectForKey:@(searchSizeClass)]; - if (myClass != nil) - return (UIView*)myClass; - - } - - searchSizeClass = MySizeClass_wAny | hsc; - if (searchSizeClass != sizeClass) - { - myClass = (MyViewSizeClass*)[myFrame.sizeClasses objectForKey:@(searchSizeClass)]; - if (myClass != nil) - return (UIView*)myClass; - } - - searchSizeClass = wsc | MySizeClass_hAny | ori; - if (ori != 0 && searchSizeClass != sizeClass) - { - myClass = (MyViewSizeClass*)[myFrame.sizeClasses objectForKey:@(searchSizeClass)]; - if (myClass != nil) - return (UIView*)myClass; - } - - searchSizeClass = wsc | MySizeClass_hAny; - if (searchSizeClass != sizeClass) - { - myClass = (MyViewSizeClass*)[myFrame.sizeClasses objectForKey:@(searchSizeClass)]; - if (myClass != nil) - return (UIView*)myClass; - } - - searchSizeClass = MySizeClass_wAny | MySizeClass_hAny | ori; - if (ori != 0 && searchSizeClass != sizeClass) - { - myClass = (MyViewSizeClass*)[myFrame.sizeClasses objectForKey:@(searchSizeClass)]; - if (myClass != nil) - return (UIView*)myClass; +- (CGFloat)myEstimatedWidth { + //如果视图的父视图不是布局视图则直接返回宽度值。 + if (![self.superview isKindOfClass:[MyBaseLayout class]]) { + return CGRectGetWidth(self.bounds); + } else { + MyLayoutEngine *viewEngine = self.myEngine; + if (viewEngine.width == CGFLOAT_MAX) { + return CGRectGetWidth(self.bounds); } - + return viewEngine.width; } - - searchSizeClass = MySizeClass_wAny | MySizeClass_hAny; - myClass = (MyViewSizeClass*)[myFrame.sizeClasses objectForKey:@(searchSizeClass)]; - if (myClass == nil) - { - myClass = [self createSizeClassInstance]; - myClass.view = self; - [myFrame.sizeClasses setObject:myClass forKey:@(searchSizeClass)]; - } - - return (UIView*)myClass; - - } - --(MyFrame*)myFrame -{ - - MyFrame *obj = objc_getAssociatedObject(self, ASSOCIATEDOBJECT_KEY_MYLAYOUT_FRAME); - if (obj == nil) - { - obj = [MyFrame new]; - objc_setAssociatedObject(self, ASSOCIATEDOBJECT_KEY_MYLAYOUT_FRAME, obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); - +- (CGFloat)myEstimatedHeight { + if (![self.superview isKindOfClass:[MyBaseLayout class]]) { + return CGRectGetHeight(self.bounds); + } else { + MyLayoutEngine *viewEngine = self.myEngine; + if (viewEngine.height == CGFLOAT_MAX) { + return CGRectGetHeight(self.bounds); + } + return viewEngine.height; } - - return obj; -} - --(id)createSizeClassInstance -{ - return [MyViewSizeClass new]; -} - - --(MyLayoutPos*)topPosInner -{ - return self.myCurrentSizeClass.topPosInner; -} - --(MyLayoutPos*)leadingPosInner -{ - return self.myCurrentSizeClass.leadingPosInner; -} - --(MyLayoutPos*)bottomPosInner -{ - return self.myCurrentSizeClass.bottomPosInner; -} - --(MyLayoutPos*)trailingPosInner -{ - return self.myCurrentSizeClass.trailingPosInner; -} - - - --(MyLayoutPos*)centerXPosInner -{ - return self.myCurrentSizeClass.centerXPosInner; -} - - --(MyLayoutPos*)centerYPosInner -{ - return self.myCurrentSizeClass.centerYPosInner; -} - - --(MyLayoutPos*)leftPosInner -{ - return self.myCurrentSizeClass.leftPosInner; -} - --(MyLayoutPos*)rightPosInner -{ - return self.myCurrentSizeClass.rightPosInner; -} - - --(MyLayoutPos*)baselinePosInner -{ - return self.myCurrentSizeClass.baselinePosInner; -} - - --(MyLayoutSize*)widthSizeInner -{ - return self.myCurrentSizeClass.widthSizeInner; -} - - --(MyLayoutSize*)heightSizeInner -{ - return self.myCurrentSizeClass.heightSizeInner; } @end - - -@implementation MyBaseLayout -{ - MyLayoutTouchEventDelegate *_touchEventDelegate; - - MyBorderlineLayerDelegate *_borderlineLayerDelegate; - +@implementation MyBaseLayout { BOOL _isAddSuperviewKVO; - - int _lastScreenOrientation; //为0为初始状态,为1为竖屏,为2为横屏。内部使用。 - BOOL _useCacheRects; + MyLayoutTouchEventDelegate *_touchEventDelegate; + MyBorderlineLayerDelegate *_borderlineLayerDelegate; + MyBaseLayoutOptionalData *_optionalData; + MyLayoutEngine *_myEngine; } - --(void)dealloc -{ +- (void)dealloc { //如果您在使用时出现了KVO的异常崩溃,原因是您将这个视图被多次加入为子视图,请检查您的代码,是否这个视图被多次加入!! - _endLayoutBlock = nil; - _beginLayoutBlock = nil; - _rotationToDeviceOrientationBlock = nil; + _optionalData = nil; } -+(BOOL)isRTL -{ - return [MyViewSizeClass isRTL]; -} +#pragma mark-- Public Methods -+(void)setIsRTL:(BOOL)isRTL -{ - [MyViewSizeClass setIsRTL:isRTL]; ++ (BOOL)isRTL { + return [MyViewTraits isRTL]; } -#pragma mark -- Public Method ++ (void)setIsRTL:(BOOL)isRTL { + [MyViewTraits setIsRTL:isRTL]; +} ++ (void)updateRTL:(BOOL)isRTL inWindow:(UIWindow *)window { + [window myUpdateRTL:isRTL]; +} +//+ (void)myUpArabicUI:(BOOL)isArabicUI inWindow:(UIWindow *)window { +// [self updateRTL:isArabicUI inWindow:window]; +//} --(CGFloat)topPadding -{ - return self.myCurrentSizeClass.topPadding; +- (CGFloat)paddingTop { + return self.myDefaultSizeClassInner.paddingTop; } --(void)setTopPadding:(CGFloat)topPadding -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.topPadding != topPadding) - { - lsc.topPadding = topPadding; +- (void)setPaddingTop:(CGFloat)paddingTop { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.paddingTop != paddingTop) { + layoutTraits.paddingTop = paddingTop; [self setNeedsLayout]; } } --(CGFloat)leadingPadding -{ - return self.myCurrentSizeClass.leadingPadding; +- (CGFloat)paddingLeading { + return self.myDefaultSizeClassInner.paddingLeading; } --(void)setLeadingPadding:(CGFloat)leadingPadding -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.leadingPadding != leadingPadding) - { - lsc.leadingPadding = leadingPadding; +- (void)setPaddingLeading:(CGFloat)paddingLeading { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.paddingLeading != paddingLeading) { + layoutTraits.paddingLeading = paddingLeading; [self setNeedsLayout]; } } - --(CGFloat)bottomPadding -{ - return self.myCurrentSizeClass.bottomPadding; +- (CGFloat)paddingBottom { + return self.myDefaultSizeClassInner.paddingBottom; } --(void)setBottomPadding:(CGFloat)bottomPadding -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.bottomPadding != bottomPadding) - { - lsc.bottomPadding = bottomPadding; +- (void)setPaddingBottom:(CGFloat)paddingBottom { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.paddingBottom != paddingBottom) { + layoutTraits.paddingBottom = paddingBottom; [self setNeedsLayout]; } } - --(CGFloat)trailingPadding -{ - return self.myCurrentSizeClass.trailingPadding; +- (CGFloat)paddingTrailing { + return self.myDefaultSizeClassInner.paddingTrailing; } --(void)setTrailingPadding:(CGFloat)trailingPadding -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.trailingPadding != trailingPadding) - { - lsc.trailingPadding = trailingPadding; +- (void)setPaddingTrailing:(CGFloat)paddingTrailing { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.paddingTrailing != paddingTrailing) { + layoutTraits.paddingTrailing = paddingTrailing; [self setNeedsLayout]; } } - --(UIEdgeInsets)padding -{ - return self.myCurrentSizeClass.padding; +- (UIEdgeInsets)padding { + return self.myDefaultSizeClassInner.padding; } --(void)setPadding:(UIEdgeInsets)padding -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (!UIEdgeInsetsEqualToEdgeInsets(lsc.padding, padding)) - { - lsc.padding = padding; +- (void)setPadding:(UIEdgeInsets)padding { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (!UIEdgeInsetsEqualToEdgeInsets(layoutTraits.padding, padding)) { + layoutTraits.padding = padding; [self setNeedsLayout]; } } - --(CGFloat)leftPadding -{ - return self.myCurrentSizeClass.leftPadding; +- (CGFloat)paddingLeft { + return self.myDefaultSizeClassInner.paddingLeft; } --(void)setLeftPadding:(CGFloat)leftPadding -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.leftPadding != leftPadding) - { - lsc.leftPadding = leftPadding; +- (void)setPaddingLeft:(CGFloat)paddingLeft { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.paddingLeft != paddingLeft) { + layoutTraits.paddingLeft = paddingLeft; [self setNeedsLayout]; } } - --(CGFloat)rightPadding -{ - return self.myCurrentSizeClass.rightPadding; +- (CGFloat)paddingRight { + return self.myDefaultSizeClassInner.paddingRight; } --(void)setRightPadding:(CGFloat)rightPadding -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.rightPadding != rightPadding) - { - lsc.rightPadding = rightPadding; +- (void)setPaddingRight:(CGFloat)paddingRight { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.paddingRight != paddingRight) { + layoutTraits.paddingRight = paddingRight; [self setNeedsLayout]; } } --(BOOL)zeroPadding -{ - return self.myCurrentSizeClass.zeroPadding; +- (BOOL)zeroPadding { + //这里不用myDefaultSizeClassInner的原因是这个属性默认是YES! + return self.myDefaultSizeClass.zeroPadding; } --(void)setZeroPadding:(BOOL)zeroPadding -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.zeroPadding != zeroPadding) - { - lsc.zeroPadding = zeroPadding; +- (void)setZeroPadding:(BOOL)zeroPadding { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.zeroPadding != zeroPadding) { + layoutTraits.zeroPadding = zeroPadding; [self setNeedsLayout]; } } --(UIRectEdge)insetsPaddingFromSafeArea -{ - return self.myCurrentSizeClass.insetsPaddingFromSafeArea; +- (UIRectEdge)insetsPaddingFromSafeArea { + //这里不用myDefaultSizeClassInner的原因是这个属性默认是UIRectEdgeLeft | UIRectEdgeRight ! + return self.myDefaultSizeClass.insetsPaddingFromSafeArea; } --(void)setInsetsPaddingFromSafeArea:(UIRectEdge)insetsPaddingFromSafeArea -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.insetsPaddingFromSafeArea != insetsPaddingFromSafeArea) - { - lsc.insetsPaddingFromSafeArea = insetsPaddingFromSafeArea; +- (void)setInsetsPaddingFromSafeArea:(UIRectEdge)insetsPaddingFromSafeArea { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.insetsPaddingFromSafeArea != insetsPaddingFromSafeArea) { + layoutTraits.insetsPaddingFromSafeArea = insetsPaddingFromSafeArea; [self setNeedsLayout]; } } --(BOOL)insetLandscapeFringePadding -{ - return self.myCurrentSizeClass.insetLandscapeFringePadding; +- (BOOL)insetLandscapeFringePadding { + return self.myDefaultSizeClassInner.insetLandscapeFringePadding; } --(void)setInsetLandscapeFringePadding:(BOOL)insetLandscapeFringePadding -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.insetLandscapeFringePadding != insetLandscapeFringePadding) - { - lsc.insetLandscapeFringePadding = insetLandscapeFringePadding; +- (void)setInsetLandscapeFringePadding:(BOOL)insetLandscapeFringePadding { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.insetLandscapeFringePadding != insetLandscapeFringePadding) { + layoutTraits.insetLandscapeFringePadding = insetLandscapeFringePadding; [self setNeedsLayout]; } } --(void)setSubviewHSpace:(CGFloat)subviewHSpace -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - - if (lsc.subviewHSpace != subviewHSpace) - { - lsc.subviewHSpace = subviewHSpace; +- (void)setSubviewHSpace:(CGFloat)subviewHSpace { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.subviewHSpace != subviewHSpace) { + layoutTraits.subviewHSpace = subviewHSpace; [self setNeedsLayout]; } } --(CGFloat)subviewHSpace -{ - return self.myCurrentSizeClass.subviewHSpace; +- (CGFloat)subviewHSpace { + return self.myDefaultSizeClassInner.subviewHSpace; } --(void)setSubviewVSpace:(CGFloat)subviewVSpace -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.subviewVSpace != subviewVSpace) - { - lsc.subviewVSpace = subviewVSpace; +- (void)setSubviewVSpace:(CGFloat)subviewVSpace { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.subviewVSpace != subviewVSpace) { + layoutTraits.subviewVSpace = subviewVSpace; [self setNeedsLayout]; } } --(CGFloat)subviewVSpace -{ - return self.myCurrentSizeClass.subviewVSpace; +- (CGFloat)subviewVSpace { + return self.myDefaultSizeClassInner.subviewVSpace; } --(void)setSubviewSpace:(CGFloat)subviewSpace -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - - if (lsc.subviewSpace != subviewSpace) - { - lsc.subviewSpace = subviewSpace; +- (void)setSubviewSpace:(CGFloat)subviewSpace { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.subviewSpace != subviewSpace) { + layoutTraits.subviewSpace = subviewSpace; [self setNeedsLayout]; } } --(CGFloat)subviewSpace -{ - return self.myCurrentSizeClass.subviewSpace; +- (CGFloat)subviewSpace { + return self.myDefaultSizeClassInner.subviewSpace; } --(void)setGravity:(MyGravity)gravity -{ - - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.gravity != gravity) - { - lsc.gravity = gravity; +- (void)setGravity:(MyGravity)gravity { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.gravity != gravity) { + layoutTraits.gravity = gravity; [self setNeedsLayout]; } } --(MyGravity)gravity -{ - return self.myCurrentSizeClass.gravity; +- (MyGravity)gravity { + return self.myDefaultSizeClassInner.gravity; } - - --(void)setReverseLayout:(BOOL)reverseLayout -{ - - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.reverseLayout != reverseLayout) - { - lsc.reverseLayout = reverseLayout; +- (void)setReverseLayout:(BOOL)reverseLayout { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.reverseLayout != reverseLayout) { + layoutTraits.reverseLayout = reverseLayout; [self setNeedsLayout]; } } --(BOOL)reverseLayout -{ - return self.myCurrentSizeClass.reverseLayout; +- (BOOL)reverseLayout { + return self.myDefaultSizeClassInner.reverseLayout; } - - --(void)setHideSubviewReLayout:(BOOL)hideSubviewReLayout -{ - //这个属性已经无效了,请单独设置子视图的myVisibility属性来控制视图的显示与否。 - NSAssert(0, @"oops!, hideSubviewReLayout is invalid please use subview's myVisibility to instead!!!"); +- (CGAffineTransform)layoutTransform { + //这里不用myDefaultSizeClassInner的原因是这个属性默认是CGAffineTransformIdentity + return self.myDefaultSizeClass.layoutTransform; } --(BOOL)hideSubviewReLayout -{ - return NO; +- (void)setLayoutTransform:(CGAffineTransform)layoutTransform { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (!CGAffineTransformEqualToTransform(layoutTraits.layoutTransform, layoutTransform)) { + layoutTraits.layoutTransform = layoutTransform; + [self setNeedsLayout]; + } } --(void)removeAllSubviews -{ +- (void)removeAllSubviews { [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; } - --(void)layoutAnimationWithDuration:(NSTimeInterval)duration -{ - self.beginLayoutBlock = ^{ - - [UIView beginAnimations:nil context:nil]; - [UIView setAnimationDuration:duration]; - }; - - self.endLayoutBlock = ^{ - - [UIView commitAnimations]; - }; -} - --(MyBorderline*)topBorderline -{ +- (MyBorderline *)topBorderline { return _borderlineLayerDelegate.topBorderline; } --(void)setTopBorderline:(MyBorderline *)topBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setTopBorderline:(MyBorderline *)topBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:self.layer]; } - _borderlineLayerDelegate.topBorderline = topBorderline; } --(MyBorderline*)leadingBorderline -{ +- (MyBorderline *)leadingBorderline { return _borderlineLayerDelegate.leadingBorderline; } --(void)setLeadingBorderline:(MyBorderline *)leadingBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setLeadingBorderline:(MyBorderline *)leadingBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:self.layer]; } - _borderlineLayerDelegate.leadingBorderline = leadingBorderline; } --(MyBorderline*)bottomBorderline -{ +- (MyBorderline *)bottomBorderline { return _borderlineLayerDelegate.bottomBorderline; } --(void)setBottomBorderline:(MyBorderline *)bottomBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setBottomBorderline:(MyBorderline *)bottomBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:self.layer]; } - _borderlineLayerDelegate.bottomBorderline = bottomBorderline; } - --(MyBorderline*)trailingBorderline -{ +- (MyBorderline *)trailingBorderline { return _borderlineLayerDelegate.trailingBorderline; } --(void)setTrailingBorderline:(MyBorderline *)trailingBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setTrailingBorderline:(MyBorderline *)trailingBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:self.layer]; } - _borderlineLayerDelegate.trailingBorderline = trailingBorderline; } - - --(MyBorderline*)leftBorderline -{ +- (MyBorderline *)leftBorderline { return _borderlineLayerDelegate.leftBorderline; } --(void)setLeftBorderline:(MyBorderline *)leftBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setLeftBorderline:(MyBorderline *)leftBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:self.layer]; } - _borderlineLayerDelegate.leftBorderline = leftBorderline; } - --(MyBorderline*)rightBorderline -{ +- (MyBorderline *)rightBorderline { return _borderlineLayerDelegate.rightBorderline; } --(void)setRightBorderline:(MyBorderline *)rightBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setRightBorderline:(MyBorderline *)rightBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:self.layer]; } - _borderlineLayerDelegate.rightBorderline = rightBorderline; } - --(void)setBoundBorderline:(MyBorderline *)boundBorderline -{ +- (void)setBoundBorderline:(MyBorderline *)boundBorderline { self.leadingBorderline = boundBorderline; self.trailingBorderline = boundBorderline; self.topBorderline = boundBorderline; self.bottomBorderline = boundBorderline; } --(MyBorderline*)boundBorderline -{ +- (MyBorderline *)boundBorderline { return self.bottomBorderline; } --(void)setBackgroundImage:(UIImage *)backgroundImage -{ - if (_backgroundImage != backgroundImage) - { +- (void)setBackgroundImage:(UIImage *)backgroundImage { + if (_backgroundImage != backgroundImage) { _backgroundImage = backgroundImage; self.layer.contents = (id)_backgroundImage.CGImage; } } - - --(CGSize)myEstimateLayoutRect:(CGSize)size inSizeClass:(MySizeClass)sizeClass sbs:(NSMutableArray*)sbs -{ - MyFrame *selfMyFrame = self.myFrame; - - if (selfMyFrame.multiple) - selfMyFrame.sizeClass = [self myBestSizeClass:sizeClass]; - - for (UIView *sbv in self.subviews) - { - MyFrame *sbvmyFrame = sbv.myFrame; - if (sbvmyFrame.multiple) - sbvmyFrame.sizeClass = [sbv myBestSizeClass:sizeClass]; - } - - BOOL hasSubLayout = NO; - CGSize selfSize= [self calcLayoutRect:size isEstimate:NO pHasSubLayout:&hasSubLayout sizeClass:sizeClass sbs:sbs]; - - if (hasSubLayout) - { - selfMyFrame.width = selfSize.width; - selfMyFrame.height = selfSize.height; - - selfSize = [self calcLayoutRect:CGSizeZero isEstimate:YES pHasSubLayout:&hasSubLayout sizeClass:sizeClass sbs:sbs]; - } - - selfMyFrame.width = selfSize.width; - selfMyFrame.height = selfSize.height; - - - - //计算后还原为默认sizeClass - for (UIView *sbv in self.subviews) - { - MyFrame *sbvmyFrame = sbv.myFrame; - if (sbvmyFrame.multiple) - sbvmyFrame.sizeClass = self.myDefaultSizeClass; - } - - if (selfMyFrame.multiple) - selfMyFrame.sizeClass = self.myDefaultSizeClass; - - if (self.cacheEstimatedRect) - _useCacheRects = YES; - - return CGSizeMake(_myCGFloatRound(selfSize.width), _myCGFloatRound(selfSize.height)); -} - --(void)setCacheEstimatedRect:(BOOL)cacheEstimatedRect -{ +- (void)setCacheEstimatedRect:(BOOL)cacheEstimatedRect { _cacheEstimatedRect = cacheEstimatedRect; _useCacheRects = NO; } - --(CGRect)subview:(UIView*)subview estimatedRectInLayoutSize:(CGSize)size -{ - if (subview.superview == self) +- (CGRect)subview:(UIView *)subview estimatedRectInLayoutSize:(CGSize)size { + if (subview.superview == self) { return subview.frame; - - NSMutableArray *sbs = [self myGetLayoutSubviews]; - [sbs addObject:subview]; - - [self myEstimateLayoutRect:size inSizeClass:MySizeClass_wAny | MySizeClass_hAny sbs:sbs]; - + } + NSMutableArray *subviews = [self.subviews mutableCopy]; + [subviews addObject:subview]; + + [self myEstimateLayoutSize:size inSizeClass:MySizeClass_wAny | MySizeClass_hAny subviews:subviews]; + return [subview estimatedRect]; } - - --(void)setHighlightedOpacity:(CGFloat)highlightedOpacity -{ - if (_touchEventDelegate == nil) - { +- (void)setHighlightedOpacity:(CGFloat)highlightedOpacity { + if (_touchEventDelegate == nil) { _touchEventDelegate = [[MyLayoutTouchEventDelegate alloc] initWithLayout:self]; } - _touchEventDelegate.highlightedOpacity = highlightedOpacity; } --(CGFloat)highlightedOpacity -{ +- (CGFloat)highlightedOpacity { return _touchEventDelegate.highlightedOpacity; } --(void)setHighlightedBackgroundColor:(UIColor *)highlightedBackgroundColor -{ - if (_touchEventDelegate == nil) - { +- (void)setHighlightedBackgroundColor:(UIColor *)highlightedBackgroundColor { + if (_touchEventDelegate == nil) { _touchEventDelegate = [[MyLayoutTouchEventDelegate alloc] initWithLayout:self]; } - _touchEventDelegate.highlightedBackgroundColor = highlightedBackgroundColor; } --(UIColor*)highlightedBackgroundColor -{ +- (UIColor *)highlightedBackgroundColor { return _touchEventDelegate.highlightedBackgroundColor; } --(void)setHighlightedBackgroundImage:(UIImage *)highlightedBackgroundImage -{ - if (_touchEventDelegate == nil) - { +- (void)setHighlightedBackgroundImage:(UIImage *)highlightedBackgroundImage { + if (_touchEventDelegate == nil) { _touchEventDelegate = [[MyLayoutTouchEventDelegate alloc] initWithLayout:self]; } - _touchEventDelegate.highlightedBackgroundImage = highlightedBackgroundImage; } --(UIImage*)highlightedBackgroundImage -{ +- (UIImage *)highlightedBackgroundImage { return _touchEventDelegate.highlightedBackgroundImage; } - --(void)setTarget:(id)target action:(SEL)action -{ - if (_touchEventDelegate == nil) - { +- (void)setTarget:(id)target action:(SEL)action { + if (_touchEventDelegate == nil) { _touchEventDelegate = [[MyLayoutTouchEventDelegate alloc] initWithLayout:self]; } - [_touchEventDelegate setTarget:target action:action]; } - --(void)setTouchDownTarget:(id)target action:(SEL)action -{ - if (_touchEventDelegate == nil) - { +- (void)setTouchDownTarget:(id)target action:(SEL)action { + if (_touchEventDelegate == nil) { _touchEventDelegate = [[MyLayoutTouchEventDelegate alloc] initWithLayout:self]; } - [_touchEventDelegate setTouchDownTarget:target action:action]; } --(void)setTouchCancelTarget:(id)target action:(SEL)action -{ - if (_touchEventDelegate == nil) - { +- (void)setTouchCancelTarget:(id)target action:(SEL)action { + if (_touchEventDelegate == nil) { _touchEventDelegate = [[MyLayoutTouchEventDelegate alloc] initWithLayout:self]; } - [_touchEventDelegate setTouchCancelTarget:target action:action]; - } +- (MyLayoutDragger *)createLayoutDragger { + MyLayoutDragger *dragger = [MyLayoutDragger new]; + dragger.currentIndex = -1; + dragger.oldIndex = -1; + dragger.hasDragging = NO; + dragger.layout = self; + return dragger; +} + +- (void)setBeginLayoutBlock:(void (^)(void))beginLayoutBlock { + if (_optionalData == nil) { + _optionalData = [MyBaseLayoutOptionalData new]; + } + _optionalData.beginLayoutBlock = beginLayoutBlock; +} + +- (void (^)(void))beginLayoutBlock { + return _optionalData.beginLayoutBlock; +} + +- (void)setEndLayoutBlock:(void (^)(void))endLayoutBlock { + if (_optionalData == nil) { + _optionalData = [MyBaseLayoutOptionalData new]; + } + _optionalData.endLayoutBlock = endLayoutBlock; +} +- (void (^)(void))endLayoutBlock { + return _optionalData.endLayoutBlock; +} +- (void)setRotationToDeviceOrientationBlock:(void (^)(MyBaseLayout *, BOOL, BOOL))rotationToDeviceOrientationBlock { + if (_optionalData == nil) { + _optionalData = [MyBaseLayoutOptionalData new]; + } + _optionalData.rotationToDeviceOrientationBlock = rotationToDeviceOrientationBlock; +} +- (void (^)(MyBaseLayout *, BOOL, BOOL))rotationToDeviceOrientationBlock { + return _optionalData.rotationToDeviceOrientationBlock; +} -#pragma mark -- Touches Event +- (void)layoutAnimationWithDuration:(NSTimeInterval)duration { + [self layoutAnimationWithDuration:duration options:0 completion:nil]; +} +- (void)layoutAnimationWithDuration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^__nullable)(BOOL finished))completion { + if (_optionalData == nil) { + _optionalData = [MyBaseLayoutOptionalData new]; + } + _optionalData.aniDuration = duration; + _optionalData.aniOptions = options; + _optionalData.aniCompletion = completion; +} +#pragma mark-- Touches Event -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -{ - +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [_touchEventDelegate touchesBegan:touches withEvent:event]; - [super touchesBegan:touches withEvent:event]; } -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event -{ +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { [_touchEventDelegate touchesMoved:touches withEvent:event]; [super touchesMoved:touches withEvent:event]; } - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event -{ +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [_touchEventDelegate touchesEnded:touches withEvent:event]; [super touchesEnded:touches withEvent:event]; } -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event -{ +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [_touchEventDelegate touchesCancelled:touches withEvent:event]; [super touchesCancelled:touches withEvent:event]; } +#pragma mark-- KVO - -#pragma mark -- KVO - - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(UIView*)object change:(NSDictionary *)change context:(void *)context -{ - +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(UIView *)object change:(NSDictionary *)change context:(void *)context { //监控非布局父视图的frame的变化,而改变自身的位置和尺寸 - if (context == _myObserverContextC) - { + if (context == _myObserverContextC) { //只监控父视图的尺寸变换 CGRect rcOld = [change[NSKeyValueChangeOldKey] CGRectValue]; CGRect rcNew = [change[NSKeyValueChangeNewKey] CGRectValue]; - if (!_myCGSizeEqual(rcOld.size, rcNew.size)) - { + if (!_myCGSizeEqual(rcOld.size, rcNew.size)) { [self myUpdateLayoutRectInNoLayoutSuperview:object]; } return; } - //监控子视图的frame的变化以便重新进行布局 - if (!_isMyLayouting) - { - - if (context == _myObserverContextA) - { - [self setNeedsLayout]; - //这里添加的原因是有可能子视图取消隐藏后不会绘制自身,所以这里要求子视图重新绘制自身 - if ([keyPath isEqualToString:@"hidden"] && ![change[NSKeyValueChangeNewKey] boolValue]) - { - [(UIView*)object setNeedsDisplay]; - } - - } - else if (context == _myObserverContextB) - {//针对UILabel特殊处理。。 - - UIView *sbvsc = object.myCurrentSizeClass; - - if (sbvsc.widthSizeInner.dimeSelfVal != nil && sbvsc.heightSizeInner.dimeSelfVal != nil) - { + if (!self.isMyLayouting) { + if (context == _myObserverContextA) { + if (!object.myCurrentSizeClassInner.useFrame) { [self setNeedsLayout]; + //这里添加的原因是有可能子视图取消隐藏后不会绘制自身,所以这里要求子视图重新绘制自身 + if ([keyPath isEqualToString:@"hidden"] && ![change[NSKeyValueChangeNewKey] boolValue]) { + [(UIView *)object setNeedsDisplay]; + } } - else if (sbvsc.wrapContentWidth || - sbvsc.wrapContentHeight || - sbvsc.widthSizeInner.dimeSelfVal != nil || - sbvsc.heightSizeInner.dimeSelfVal != nil) - { - [object sizeToFit]; + } else if (context == _myObserverContextB) { //针对UILabel特殊处理。。 + MyViewTraits *subviewTraits = (MyViewTraits*)object.myDefaultSizeClass; + if (subviewTraits.widthSizeInner.wrapVal || subviewTraits.heightSizeInner.wrapVal) { + [self setNeedsLayout]; } } } } +#pragma mark-- Override Methods -#pragma mark -- Override Method - - - --(void)setWrapContentHeight:(BOOL)wrapContentHeight -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.wrapContentHeight != wrapContentHeight) - { - lsc.wrapContentHeight = wrapContentHeight; +- (void)setWrapContentHeight:(BOOL)wrapContentHeight { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.wrapContentHeight != wrapContentHeight) { + layoutTraits.wrapContentHeight = wrapContentHeight; [self setNeedsLayout]; } } - --(void)setWrapContentWidth:(BOOL)wrapContentWidth -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.wrapContentWidth != wrapContentWidth) - { - lsc.wrapContentWidth = wrapContentWidth; +- (void)setWrapContentWidth:(BOOL)wrapContentWidth { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.wrapContentWidth != wrapContentWidth) { + layoutTraits.wrapContentWidth = wrapContentWidth; [self setNeedsLayout]; } - } --(void)setWrapContentSize:(BOOL)wrapContentSize -{ - MyBaseLayout *lsc = self.myCurrentSizeClass; - if (lsc.wrapContentSize != wrapContentSize) - { - lsc.wrapContentSize = wrapContentSize; - [self setNeedsLayout]; - } +- (void)setWrapContentSize:(BOOL)wrapContentSize { + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myDefaultSizeClass; + layoutTraits.wrapContentSize = wrapContentSize; + [self setNeedsLayout]; } - - --(CGSize)calcLayoutRect:(CGSize)size isEstimate:(BOOL)isEstimate pHasSubLayout:(BOOL*)pHasSubLayout sizeClass:(MySizeClass)sizeClass sbs:(NSMutableArray*)sbs -{ - CGSize selfSize; - if (isEstimate) - selfSize = self.myFrame.frame.size; - else - { - selfSize = self.bounds.size; - if (size.width != 0) - selfSize.width = size.width; - if (size.height != 0) - selfSize.height = size.height; +- (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { + if (context->isEstimate) { + context->selfSize = size; + } else { + context->selfSize = self.bounds.size; + if (size.width != 0) { + context->selfSize.width = size.width; + } + if (size.height != 0) { + context->selfSize.height = size.height; + } } - - if (pHasSubLayout != nil) - *pHasSubLayout = NO; - - return selfSize; - -} - --(id)createSizeClassInstance -{ - return [MyLayoutViewSizeClass new]; + return context->selfSize; } +- (id)createSizeClassInstance { + return [MyLayoutTraits new]; +} --(CGSize)sizeThatFits:(CGSize)size -{ +- (CGSize)sizeThatFits:(CGSize)size { return [self sizeThatFits:size inSizeClass:MySizeClass_wAny | MySizeClass_hAny]; } --(CGSize)sizeThatFits:(CGSize)size inSizeClass:(MySizeClass)sizeClass -{ - return [self myEstimateLayoutRect:size inSizeClass:sizeClass sbs:nil]; +- (CGSize)sizeThatFits:(CGSize)size inSizeClass:(MySizeClass)sizeClass { + return [self myEstimateLayoutSize:size inSizeClass:sizeClass subviews:nil]; } - --(void)setHidden:(BOOL)hidden -{ - if (self.isHidden == hidden) +- (void)setHidden:(BOOL)hidden { + if (self.isHidden == hidden) { return; - + } [super setHidden:hidden]; - if (hidden == NO) - { - + UIView *superview = self.superview; + if ([superview isKindOfClass:[MyBaseLayout class]]) { + if (!((MyBaseLayout *)superview).isMyLayouting) { + [superview setNeedsLayout]; + } + } + if (hidden == NO) { [_borderlineLayerDelegate setNeedsLayoutIn:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height) withLayer:self.layer]; - - if ([self.superview isKindOfClass:[MyBaseLayout class]]) - { - [self setNeedsLayout]; + } +} + +- (void)setCenter:(CGPoint)center { + CGPoint oldCenter = self.center; + [super setCenter:center]; + UIView *superview = self.superview; + if (!CGPointEqualToPoint(oldCenter, center) && [superview isKindOfClass:[MyBaseLayout class]]) { + if (!((MyBaseLayout *)superview).isMyLayouting) { + [superview setNeedsLayout]; } - } - } +- (void)setFrame:(CGRect)frame { + CGRect oldFrame = self.frame; + [super setFrame:frame]; + UIView *superview = self.superview; + if (!CGRectEqualToRect(oldFrame, frame) && [superview isKindOfClass:[MyBaseLayout class]]) { + if (!((MyBaseLayout *)superview).isMyLayouting) { + [superview setNeedsLayout]; + } + } +} +- (void)didAddSubview:(UIView *)subview { + [super didAddSubview:subview]; -- (void)didAddSubview:(UIView *)subview -{ - [super didAddSubview:subview]; //只要加入进来后就修改其默认的实现,而改用我们的实现,这里包括隐藏,调整大小, - - if ([subview isKindOfClass:[MyBaseLayout class]]) - { - ((MyBaseLayout*)subview).cacheEstimatedRect = self.cacheEstimatedRect; + if ([subview isKindOfClass:[MyBaseLayout class]]) { + ((MyBaseLayout *)subview).cacheEstimatedRect = self.cacheEstimatedRect; } - + + [self myInvalidateIntrinsicContentSize]; } -- (void)willRemoveSubview:(UIView *)subview -{ - [super willRemoveSubview:subview]; //删除后恢复其原来的实现。 +- (void)willRemoveSubview:(UIView *)subview { + [super willRemoveSubview:subview]; [self myRemoveSubviewObserver:subview]; + + [self myInvalidateIntrinsicContentSize]; } --(void)willMoveToWindow:(UIWindow *)newWindow -{ +- (void)willMoveToWindow:(UIWindow *)newWindow { [super willMoveToWindow:newWindow]; - if (newWindow == nil) - { + if (newWindow == nil) { //这里处理可能因为触摸事件被强行终止而导致的背景色无法恢复的问题。 [_touchEventDelegate myResetTouchHighlighted2]; } } -- (void)willMoveToSuperview:(UIView*)newSuperview -{ +- (void)willMoveToSuperview:(UIView *)newSuperview { [super willMoveToSuperview:newSuperview]; - - MyBaseLayout *lsc = self.myCurrentSizeClass; - - //特殊处理如果视图是控制器根视图则取消wrapContentWidth, wrapContentHeight,以及adjustScrollViewContentSizeMode的设置。 + + MyBaseLayout *layoutTraits = self.myDefaultSizeClass; + + //特殊处理如果视图是控制器根视图则取消高度或者宽度包裹,安全区padding的缩进, 以及adjustScrollViewContentSizeMode的设置。 + //但是有一种特殊情况是控制器是子视图控制器。因此还需要添加判断父视图是否是非布局父视图,只有是非布局父视图下才将自适应设置清除 @try { - - if (newSuperview != nil) - { + + if (newSuperview != nil) { UIRectEdge defRectEdge = UIRectEdgeLeft | UIRectEdgeRight; id vc = [self valueForKey:@"viewDelegate"]; - if (vc != nil) - { - lsc.wrapContentWidth = NO; - lsc.wrapContentHeight = NO; - if (lsc.insetsPaddingFromSafeArea == defRectEdge) - lsc.insetsPaddingFromSafeArea = ~UIRectEdgeTop; + if (vc != nil) { + if (![newSuperview isKindOfClass:[MyBaseLayout class]]) { + if (layoutTraits.widthSizeInner.wrapVal) { + [layoutTraits.widthSizeInner _myEqualTo:nil]; + } + if (layoutTraits.heightSizeInner.wrapVal) { + [layoutTraits.heightSizeInner _myEqualTo:nil]; + } + } + + if (layoutTraits.insetsPaddingFromSafeArea == defRectEdge) { + layoutTraits.insetsPaddingFromSafeArea = ~UIRectEdgeTop; + } self.adjustScrollViewContentSizeMode = MyAdjustScrollViewContentSizeModeNo; } - + //如果布局视图的父视图是滚动视图并且是非UITableView和UICollectionView的话。将默认叠加除顶部外的安全区域。 - if ([newSuperview isKindOfClass:[UIScrollView class]] && ![newSuperview isKindOfClass:[UITableView class]] && ![newSuperview isKindOfClass:[UICollectionView class]]) - { - if (lsc.insetsPaddingFromSafeArea == defRectEdge) - lsc.insetsPaddingFromSafeArea = ~UIRectEdgeTop; + if ([newSuperview isKindOfClass:[UIScrollView class]] && ![newSuperview isKindOfClass:[UITableView class]] && ![newSuperview isKindOfClass:[UICollectionView class]]) { + if (layoutTraits.insetsPaddingFromSafeArea == defRectEdge) { + layoutTraits.insetsPaddingFromSafeArea = ~UIRectEdgeTop; + } } } - + } @catch (NSException *exception) { - - } - - -#ifdef DEBUG - - if (lsc.wrapContentHeight && lsc.heightSizeInner.dimeVal != nil) - { - //约束警告:wrapContentHeight和设置的heightSize可能有约束冲突 - NSLog(@"Constraint warning!%@'s wrapContentHeight and heightSize setting may be constraint.",self); - } - - if (lsc.wrapContentWidth && lsc.widthSizeInner.dimeVal != nil) - { - //约束警告:wrapContentWidth和设置的widthSize可能有约束冲突 - NSLog(@"Constraint warning!%@'s wrapContentWidth and widthSize setting may be constraint.",self); } - -#endif - - - - + //将要添加到父视图时,如果不是MyLayout派生则则跟需要根据父视图的frame的变化而调整自身的位置和尺寸 - if (newSuperview != nil && ![newSuperview isKindOfClass:[MyBaseLayout class]]) - { + if (newSuperview != nil && ![newSuperview isKindOfClass:[MyBaseLayout class]]) { #ifdef DEBUG - - if (lsc.leadingPosInner.posRelaVal != nil) - { + + if (layoutTraits.leadingPosInner.anchorVal != nil) { //约束冲突:左边距依赖的视图不是父视图 - NSCAssert(lsc.leadingPosInner.posRelaVal.view == newSuperview, @"Constraint exception!! %@leading pos dependent on:%@is not superview",self, lsc.leadingPosInner.posRelaVal.view); + NSCAssert(layoutTraits.leadingPosInner.anchorVal.view == newSuperview, @"Constraint exception!! %@leading pos dependent on:%@is not superview", self, layoutTraits.leadingPosInner.anchorVal.view); } - - if (lsc.trailingPosInner.posRelaVal != nil) - { + + if (layoutTraits.trailingPosInner.anchorVal != nil) { //约束冲突:右边距依赖的视图不是父视图 - NSCAssert(lsc.trailingPosInner.posRelaVal.view == newSuperview, @"Constraint exception!! %@trailing pos dependent on:%@is not superview",self,lsc.trailingPosInner.posRelaVal.view); + NSCAssert(layoutTraits.trailingPosInner.anchorVal.view == newSuperview, @"Constraint exception!! %@trailing pos dependent on:%@is not superview", self, layoutTraits.trailingPosInner.anchorVal.view); } - - if (lsc.centerXPosInner.posRelaVal != nil) - { + + if (layoutTraits.centerXPosInner.anchorVal != nil) { //约束冲突:水平中心点依赖的视图不是父视图 - NSCAssert(lsc.centerXPosInner.posRelaVal.view == newSuperview, @"Constraint exception!! %@horizontal center pos dependent on:%@is not superview",self, lsc.centerXPosInner.posRelaVal.view); + NSCAssert(layoutTraits.centerXPosInner.anchorVal.view == newSuperview, @"Constraint exception!! %@horizontal center pos dependent on:%@is not superview", self, layoutTraits.centerXPosInner.anchorVal.view); } - - if (lsc.topPosInner.posRelaVal != nil) - { + + if (layoutTraits.topPosInner.anchorVal != nil) { //约束冲突:上边距依赖的视图不是父视图 - NSCAssert(lsc.topPosInner.posRelaVal.view == newSuperview, @"Constraint exception!! %@top pos dependent on:%@is not superview",self, lsc.topPosInner.posRelaVal.view); + NSCAssert(layoutTraits.topPosInner.anchorVal.view == newSuperview, @"Constraint exception!! %@top pos dependent on:%@is not superview", self, layoutTraits.topPosInner.anchorVal.view); } - - if (lsc.bottomPosInner.posRelaVal != nil) - { + + if (layoutTraits.bottomPosInner.anchorVal != nil) { //约束冲突:下边距依赖的视图不是父视图 - NSCAssert(lsc.bottomPosInner.posRelaVal.view == newSuperview, @"Constraint exception!! %@bottom pos dependent on:%@is not superview",self, lsc.bottomPosInner.posRelaVal.view); - + NSCAssert(layoutTraits.bottomPosInner.anchorVal.view == newSuperview, @"Constraint exception!! %@bottom pos dependent on:%@is not superview", self, layoutTraits.bottomPosInner.anchorVal.view); } - - if (lsc.centerYPosInner.posRelaVal != nil) - { + + if (layoutTraits.centerYPosInner.anchorVal != nil) { //约束冲突:垂直中心点依赖的视图不是父视图 - NSCAssert(lsc.centerYPosInner.posRelaVal.view == newSuperview, @"Constraint exception!! vertical center pos dependent on:%@is not superview",lsc.centerYPosInner.posRelaVal.view); + NSCAssert(layoutTraits.centerYPosInner.anchorVal.view == newSuperview, @"Constraint exception!! vertical center pos dependent on:%@is not superview", layoutTraits.centerYPosInner.anchorVal.view); } - - if (lsc.widthSizeInner.dimeRelaVal != nil) - { + + if (layoutTraits.widthSizeInner.anchorVal != nil) { //约束冲突:宽度依赖的视图不是父视图 - NSCAssert(lsc.widthSizeInner.dimeRelaVal.view == newSuperview, @"Constraint exception!! %@width dependent on:%@is not superview",self, lsc.widthSizeInner.dimeRelaVal.view); + NSCAssert(layoutTraits.widthSizeInner.anchorVal.view == newSuperview, @"Constraint exception!! %@width dependent on:%@is not superview", self, layoutTraits.widthSizeInner.anchorVal.view); } - - if (lsc.heightSizeInner.dimeRelaVal != nil) - { + + if (layoutTraits.heightSizeInner.anchorVal != nil) { //约束冲突:高度依赖的视图不是父视图 - NSCAssert(lsc.heightSizeInner.dimeRelaVal.view == newSuperview, @"Constraint exception!! %@height dependent on:%@is not superview",self,lsc.heightSizeInner.dimeRelaVal.view); + NSCAssert(layoutTraits.heightSizeInner.anchorVal.view == newSuperview, @"Constraint exception!! %@height dependent on:%@is not superview", self, layoutTraits.heightSizeInner.anchorVal.view); } - + #endif - - if ([self myUpdateLayoutRectInNoLayoutSuperview:newSuperview]) - { + if ([self myUpdateLayoutRectInNoLayoutSuperview:newSuperview]) { //有可能父视图不为空,所以这里先把以前父视图的KVO删除。否则会导致程序崩溃 - + //如果您在这里出现了崩溃时,不要惊慌,是因为您开启了异常断点调试的原因。这个在release下是不会出现的,要想清除异常断点调试功能,请按下CMD+7键 //然后在左边将异常断点清除即可 - - if (_isAddSuperviewKVO && self.superview != nil && ![self.superview isKindOfClass:[MyBaseLayout class]]) - { + + if (_isAddSuperviewKVO && self.superview != nil && ![self.superview isKindOfClass:[MyBaseLayout class]]) { @try { [self.superview removeObserver:self forKeyPath:@"frame"]; - - } - @catch (NSException *exception) { - - } - @finally { - + }@catch (NSException *exception) { } @try { [self.superview removeObserver:self forKeyPath:@"bounds"]; - - } - @catch (NSException *exception) { - + }@catch (NSException *exception) { } - @finally { - - } - - } - + [newSuperview addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:_myObserverContextC]; [newSuperview addObserver:self forKeyPath:@"bounds" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:_myObserverContextC]; _isAddSuperviewKVO = YES; } } - - - if (_isAddSuperviewKVO && newSuperview == nil && self.superview != nil && ![self.superview isKindOfClass:[MyBaseLayout class]]) - { - + + if (_isAddSuperviewKVO && newSuperview == nil && self.superview != nil && ![self.superview isKindOfClass:[MyBaseLayout class]]) { //如果您在这里出现了崩溃时,不要惊慌,是因为您开启了异常断点调试的原因。这个在release下是不会出现的,要想清除异常断点调试功能,请按下CMD+7键 //然后在左边将异常断点清除即可 - _isAddSuperviewKVO = NO; @try { [self.superview removeObserver:self forKeyPath:@"frame"]; - - } - @catch (NSException *exception) { - - } - @finally { - + }@catch (NSException *exception) { } @try { [self.superview removeObserver:self forKeyPath:@"bounds"]; - - } - @catch (NSException *exception) { - - } - @finally { - + }@catch (NSException *exception) { } - - } - - - if (newSuperview != nil) - { + + if (newSuperview != nil) { //不支持放在UITableView和UICollectionView下,因为有肯能是tableheaderView或者section下。 - if ([newSuperview isKindOfClass:[UIScrollView class]] && ![newSuperview isKindOfClass:[UITableView class]] && ![newSuperview isKindOfClass:[UICollectionView class]]) - { - if (self.adjustScrollViewContentSizeMode == MyAdjustScrollViewContentSizeModeAuto) - { + if ([newSuperview isKindOfClass:[UIScrollView class]] && ![newSuperview isKindOfClass:[UITableView class]] && ![newSuperview isKindOfClass:[UICollectionView class]]) { + if (self.adjustScrollViewContentSizeMode == MyAdjustScrollViewContentSizeModeAuto) { //这里预先设置一下contentSize主要是为了解决contentOffset在后续计算contentSize的偏移错误的问题。 [UIView performWithoutAnimation:^{ - UIScrollView *scrollSuperView = (UIScrollView*)newSuperview; - if (CGSizeEqualToSize(scrollSuperView.contentSize, CGSizeZero)) - { + UIScrollView *scrollSuperView = (UIScrollView *)newSuperview; + if (CGSizeEqualToSize(scrollSuperView.contentSize, CGSizeZero)) { CGSize screenSize = [UIScreen mainScreen].bounds.size; - scrollSuperView.contentSize = CGSizeMake(0, screenSize.height + 0.1); + scrollSuperView.contentSize = CGSizeMake(0, screenSize.height + 0.1); } }]; - + self.adjustScrollViewContentSizeMode = MyAdjustScrollViewContentSizeModeYes; } } + } else { + _optionalData.aniDuration = 0.0; + _optionalData.beginLayoutBlock = nil; + _optionalData.endLayoutBlock = nil; + if (_optionalData.rotationToDeviceOrientationBlock == nil) { + _optionalData = nil; + } } - else - { - self.beginLayoutBlock = nil; - self.endLayoutBlock = nil; - } - - } - --(void)awakeFromNib -{ +- (void)awakeFromNib { [super awakeFromNib]; - - if (self.superview != nil && ![self.superview isKindOfClass:[MyBaseLayout class]]) + + if (self.superview != nil && ![self.superview isKindOfClass:[MyBaseLayout class]]) { [self myUpdateLayoutRectInNoLayoutSuperview:self.superview]; + } } --(void)safeAreaInsetsDidChange -{ +- (void)safeAreaInsetsDidChange { +#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) + [super safeAreaInsetsDidChange]; - - - if (self.superview != nil && ![self.superview isKindOfClass:[MyBaseLayout class]] && +#endif + + if (self.superview != nil && + ![self.superview isKindOfClass:[MyBaseLayout class]] && (self.leadingPosInner.isSafeAreaPos || self.trailingPosInner.isSafeAreaPos || self.topPosInner.isSafeAreaPos || - self.bottomPosInner.isSafeAreaPos) - ) - { - if (!_isMyLayouting) - { - _isMyLayouting = YES; + self.bottomPosInner.isSafeAreaPos)) { + if (!self.isMyLayouting) { + self.isMyLayouting = YES; [self myUpdateLayoutRectInNoLayoutSuperview:self.superview]; - _isMyLayouting = NO; + self.isMyLayouting = NO; } } } --(void)layoutSubviews -{ - - if (!self.autoresizesSubviews) - return; - - if (self.beginLayoutBlock != nil) - self.beginLayoutBlock(); - self.beginLayoutBlock = nil; +- (void)setNeedsLayout { + [super setNeedsLayout]; + [self myInvalidateIntrinsicContentSize]; +} - int currentScreenOrientation = 0; - - - if (!self.isMyLayouting) - { +- (CGSize)intrinsicContentSize { + CGSize size = [super intrinsicContentSize]; + if (self.translatesAutoresizingMaskIntoConstraints == NO && (self.widthSizeInner.wrapVal || self.heightSizeInner.wrapVal)) { + if (self.widthSizeInner.wrapVal && self.heightSizeInner.wrapVal) { + size = [self sizeThatFits:CGSizeZero]; + } else if (self.widthSizeInner.wrapVal) { + //动态宽度 + NSLayoutConstraint *heightConstraint = nil; + for (NSLayoutConstraint *constraint in self.constraints) { + if (constraint.firstItem == self && constraint.firstAttribute == NSLayoutAttributeHeight) { + heightConstraint = constraint; + break; + } + } + + if (heightConstraint == nil) { + for (NSLayoutConstraint *constraint in self.superview.constraints) { + if (constraint.firstItem == self && constraint.firstAttribute == NSLayoutAttributeHeight) { + heightConstraint = constraint; + break; + } + } + } + + if (heightConstraint != nil) { + CGFloat dependHeight = UIViewNoIntrinsicMetric; + if ([heightConstraint.secondItem isKindOfClass:[UIView class]]) { + UIView *dependView = (UIView *)heightConstraint.secondItem; + CGRect dependViewRect = dependView.bounds; + if (heightConstraint.secondAttribute == NSLayoutAttributeHeight) { + dependHeight = CGRectGetHeight(dependViewRect); + } + + else if (heightConstraint.secondAttribute == NSLayoutAttributeWidth) { + dependHeight = CGRectGetWidth(dependViewRect); + } + + else { + dependHeight = UIViewNoIntrinsicMetric; + } + } else if (heightConstraint.secondItem == nil) { + dependHeight = 0; + } + if (dependHeight != UIViewNoIntrinsicMetric) { + dependHeight *= heightConstraint.multiplier; + dependHeight += heightConstraint.constant; + size.width = [self sizeThatFits:CGSizeMake(0, dependHeight)].width; + } + } + } else { + //动态高度 + NSLayoutConstraint *widthConstraint = nil; + for (NSLayoutConstraint *constraint in self.constraints) { + if (constraint.firstItem == self && constraint.firstAttribute == NSLayoutAttributeWidth) { + widthConstraint = constraint; + break; + } + } + + if (widthConstraint == nil) { + for (NSLayoutConstraint *constraint in self.superview.constraints) { + if (constraint.firstItem == self && constraint.firstAttribute == NSLayoutAttributeWidth) { + widthConstraint = constraint; + break; + } + } + } + + CGFloat dependWidth = UIViewNoIntrinsicMetric; + if (widthConstraint != nil) { + if ([widthConstraint.secondItem isKindOfClass:[UIView class]]) { + UIView *dependView = (UIView *)widthConstraint.secondItem; + CGRect dependViewRect = dependView.bounds; + if (widthConstraint.secondAttribute == NSLayoutAttributeWidth) { + dependWidth = CGRectGetWidth(dependViewRect); + } + + else if (widthConstraint.secondAttribute == NSLayoutAttributeHeight) { + dependWidth = CGRectGetHeight(dependViewRect); + } else { + dependWidth = UIViewNoIntrinsicMetric; + } + } else if (widthConstraint.secondItem == nil) { + dependWidth = 0; + } + if (dependWidth != UIViewNoIntrinsicMetric) { + dependWidth *= widthConstraint.multiplier; + dependWidth += widthConstraint.constant; + size.height = [self sizeThatFits:CGSizeMake(dependWidth, 0)].height; + } + } + } + } + return size; +} + +- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority { + return [self sizeThatFits:targetSize]; +} + +- (void)doLayoutSubviews { + int currentScreenOrientation = 0; - _isMyLayouting = YES; + if (!self.isMyLayouting) { + self.isMyLayouting = YES; - if (self.priorAutoresizingMask) + if (self.priorAutoresizingMask) { [super layoutSubviews]; - + } //减少每次调用就计算设备方向以及sizeclass的次数。 MySizeClass sizeClass = [self myGetGlobalSizeClass]; - if ((sizeClass & 0xF0) == MySizeClass_Portrait) + if ((sizeClass & 0xF0) == MySizeClass_Portrait) { currentScreenOrientation = 1; - else if ((sizeClass & 0xF0) == MySizeClass_Landscape) + } else if ((sizeClass & 0xF0) == MySizeClass_Landscape) { currentScreenOrientation = 2; + } + //得到当前布局视图和子视图的最佳的sizeClass + MyLayoutEngine *layoutViewEngine = self.myEngine; + layoutViewEngine.currentSizeClass = [layoutViewEngine fetchView:self bestLayoutSizeClass:sizeClass]; - MyFrame *selfMyFrame = self.myFrame; - if (selfMyFrame.multiple) - selfMyFrame.sizeClass = [self myBestSizeClass:sizeClass]; - for (UIView *sbv in self.subviews) - { - MyFrame *sbvmyFrame = sbv.myFrame; - if (sbvmyFrame.multiple) - sbv.myFrame.sizeClass = [sbv myBestSizeClass:sizeClass]; + NSMutableArray *subviewEngines = [NSMutableArray arrayWithCapacity:self.subviews.count]; + for (UIView *subview in self.subviews) { + MyLayoutEngine *subviewEngine = subview.myEngine; + subviewEngine.currentSizeClass = [subviewEngine fetchView:subview bestLayoutSizeClass:sizeClass]; - if (!sbvmyFrame.hasObserver && sbvmyFrame.sizeClass != nil && !sbvmyFrame.sizeClass.useFrame) - { - [self myAddSubviewObserver:sbv sbvmyFrame:sbvmyFrame]; + if (!subviewEngine.hasObserver && subviewEngine.currentSizeClass != nil && !subviewEngine.currentSizeClass.useFrame) { + subviewEngine.hasObserver = YES; + [self myAddSubviewObserver:subview]; } + + [subviewEngines addObject:subviewEngine]; } - - MyBaseLayout *lsc = (MyBaseLayout*)selfMyFrame.sizeClass; + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)layoutViewEngine.currentSizeClass; //计算布局 CGSize oldSelfSize = self.bounds.size; CGSize newSelfSize; - if (_useCacheRects && selfMyFrame.width != CGFLOAT_MAX && selfMyFrame.height != CGFLOAT_MAX) - { - newSelfSize = CGSizeMake(selfMyFrame.width, selfMyFrame.height); - } - else - { - newSelfSize = [self calcLayoutRect:[self myCalcSizeInNoLayoutSuperview:self.superview currentSize:oldSelfSize] isEstimate:NO pHasSubLayout:nil sizeClass:sizeClass sbs:nil]; + if (_useCacheRects && layoutViewEngine.width != CGFLOAT_MAX && layoutViewEngine.height != CGFLOAT_MAX) { + newSelfSize = CGSizeMake(layoutViewEngine.width, layoutViewEngine.height); + } else { + + MyLayoutContext context; + context.isEstimate = NO; + context.sizeClass = sizeClass; + context.layoutViewEngine = layoutViewEngine; + context.selfSize = oldSelfSize; + + newSelfSize = [self calcLayoutSize:[self myCalcSizeInNoLayoutSuperview:self.superview currentSize:oldSelfSize] subviewEngines:subviewEngines context:&context]; } + newSelfSize = _myCGSizeRound(newSelfSize); _useCacheRects = NO; - - static CGFloat sSizeError = 0; - if (sSizeError == 0) - sSizeError = 1 / [UIScreen mainScreen].scale + 0.0001; //误差量。 + static CGFloat sSizeError = 0; + if (sSizeError == 0) { + sSizeError = 1 / [UIScreen mainScreen].scale + 0.0001; //误差量。 + } //设置子视图的frame并还原 - for (UIView *sbv in self.subviews) - { - CGRect sbvOldBounds = sbv.bounds; - CGPoint sbvOldCenter = sbv.center; + for (UIView *subview in self.subviews) { + CGRect sbvOldBounds = subview.bounds; + CGPoint sbvOldCenter = subview.center; - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (sbvmyFrame.leading != CGFLOAT_MAX && sbvmyFrame.top != CGFLOAT_MAX && !sbvsc.noLayout && !sbvsc.useFrame) - { - if (sbvmyFrame.width < 0) - { - sbvmyFrame.width = 0; + MyLayoutEngine *subviewEngine = subview.myEngine; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + + if (subviewEngine.leading != CGFLOAT_MAX && subviewEngine.top != CGFLOAT_MAX && !subviewTraits.noLayout && !subviewTraits.useFrame) { + if (subviewEngine.width < 0) { + subviewEngine.width = 0; } - if (sbvmyFrame.height < 0) - { - sbvmyFrame.height = 0; + if (subviewEngine.height < 0) { + subviewEngine.height = 0; } - //这里的位置需要进行有效像素的舍入处理,否则可能出现文本框模糊,以及视图显示可能多出一条黑线的问题。 //原因是当frame中的值不能有效的转化为最小可绘制的物理像素时就会出现模糊,虚化,多出黑线,以及layer处理圆角不圆的情况。 //所以这里要将frame中的点转化为有效的点。 - //这里之所以讲布局子视图的转化方法和一般子视图的转化方法区分开来是因为。我们要保证布局子视图不能出现细微的重叠,因为布局子视图有边界线 + //这里之所以将布局子视图的转化方法和一般子视图的转化方法区分开来是因为我们要保证布局子视图不能出现细微的重叠,因为布局子视图有边界线 //如果有边界线而又出现细微重叠的话,那么边界线将无法正常显示,因此这里做了一个特殊的处理。 - CGRect rc; - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - rc = _myLayoutCGRectRound(sbvmyFrame.frame); - - - CGRect sbvTempBounds = CGRectMake(sbvOldBounds.origin.x, sbvOldBounds.origin.y, rc.size.width, rc.size.height); - - if (_myCGFloatErrorEqual(sbvTempBounds.size.width, sbvOldBounds.size.width, sSizeError)) - sbvTempBounds.size.width = sbvOldBounds.size.width; - - if (_myCGFloatErrorEqual(sbvTempBounds.size.height, sbvOldBounds.size.height, sSizeError)) - sbvTempBounds.size.height = sbvOldBounds.size.height; - - - if (_myCGFloatErrorNotEqual(sbvTempBounds.size.width, sbvOldBounds.size.width, sSizeError)|| - _myCGFloatErrorNotEqual(sbvTempBounds.size.height, sbvOldBounds.size.height, sSizeError)) - { - sbv.bounds = sbvTempBounds; + CGRect rect; + if ([subview isKindOfClass:[MyBaseLayout class]]) { + rect = _myLayoutCGRectRound(subviewEngine.frame); + + CGRect subviewTempBounds = CGRectMake(sbvOldBounds.origin.x, sbvOldBounds.origin.y, rect.size.width, rect.size.height); + + if (_myCGFloatErrorEqual(subviewTempBounds.size.width, sbvOldBounds.size.width, sSizeError)) { + subviewTempBounds.size.width = sbvOldBounds.size.width; } - - CGPoint sbvTempCenter = CGPointMake(rc.origin.x + sbv.layer.anchorPoint.x * sbvTempBounds.size.width, rc.origin.y + sbv.layer.anchorPoint.y * sbvTempBounds.size.height); - - if (_myCGFloatErrorEqual(sbvTempCenter.x, sbvOldCenter.x, sSizeError)) - sbvTempCenter.x = sbvOldCenter.x; - - if (_myCGFloatErrorEqual(sbvTempCenter.y, sbvOldCenter.y, sSizeError)) - sbvTempCenter.y = sbvOldCenter.y; - - - if (_myCGFloatErrorNotEqual(sbvTempCenter.x, sbvOldCenter.x, sSizeError)|| - _myCGFloatErrorNotEqual(sbvTempCenter.y, sbvOldCenter.y, sSizeError)) - { - sbv.center = sbvTempCenter; + if (_myCGFloatErrorEqual(subviewTempBounds.size.height, sbvOldBounds.size.height, sSizeError)) { + subviewTempBounds.size.height = sbvOldBounds.size.height; } + if (_myCGFloatErrorNotEqual(subviewTempBounds.size.width, sbvOldBounds.size.width, sSizeError) || + _myCGFloatErrorNotEqual(subviewTempBounds.size.height, sbvOldBounds.size.height, sSizeError)) { + subview.bounds = subviewTempBounds; + } + CGPoint subviewTempCenter = CGPointMake(rect.origin.x + subview.layer.anchorPoint.x * subviewTempBounds.size.width, rect.origin.y + subview.layer.anchorPoint.y * subviewTempBounds.size.height); - - } - else - { - rc = _myCGRectRound(sbvmyFrame.frame); - - sbv.center = CGPointMake(rc.origin.x + sbv.layer.anchorPoint.x * rc.size.width, rc.origin.y + sbv.layer.anchorPoint.y * rc.size.height); - sbv.bounds = CGRectMake(sbvOldBounds.origin.x, sbvOldBounds.origin.y, rc.size.width, rc.size.height); - + if (_myCGFloatErrorEqual(subviewTempCenter.x, sbvOldCenter.x, sSizeError)) { + subviewTempCenter.x = sbvOldCenter.x; + } + if (_myCGFloatErrorEqual(subviewTempCenter.y, sbvOldCenter.y, sSizeError)) { + subviewTempCenter.y = sbvOldCenter.y; + } + if (_myCGFloatErrorNotEqual(subviewTempCenter.x, sbvOldCenter.x, sSizeError) || + _myCGFloatErrorNotEqual(subviewTempCenter.y, sbvOldCenter.y, sSizeError)) { + subview.center = subviewTempCenter; + } + } else { + rect = _myCGRectRound(subviewEngine.frame); + subview.center = CGPointMake(rect.origin.x + subview.layer.anchorPoint.x * rect.size.width, rect.origin.y + subview.layer.anchorPoint.y * rect.size.height); + subview.bounds = CGRectMake(sbvOldBounds.origin.x, sbvOldBounds.origin.y, rect.size.width, rect.size.height); } - } - - if (sbvsc.myVisibility == MyVisibility_Gone && !sbv.isHidden) - { - sbv.bounds = CGRectMake(sbvOldBounds.origin.x, sbvOldBounds.origin.y, 0, 0); + + if (subviewTraits.visibility == MyVisibility_Gone && !subview.isHidden) { + subview.bounds = CGRectMake(sbvOldBounds.origin.x, sbvOldBounds.origin.y, 0, 0); } - - if (sbvmyFrame.sizeClass.viewLayoutCompleteBlock != nil) - { - sbvmyFrame.sizeClass.viewLayoutCompleteBlock(self, sbv); - sbvmyFrame.sizeClass.viewLayoutCompleteBlock = nil; + if (subviewEngine.currentSizeClass.viewLayoutCompleteBlock != nil) { + subviewEngine.currentSizeClass.viewLayoutCompleteBlock(self, subview); + subviewEngine.currentSizeClass.viewLayoutCompleteBlock = nil; } - - - if (sbvmyFrame.multiple) - sbvmyFrame.sizeClass = [sbv myDefaultSizeClass]; - [sbvmyFrame reset]; + + [subviewEngine reset]; } - - - if (newSelfSize.width != CGFLOAT_MAX && (lsc.wrapContentWidth || lsc.wrapContentHeight)) - { - - + + if (newSelfSize.width != CGFLOAT_MAX && (layoutTraits.widthSizeInner.wrapVal || layoutTraits.heightSizeInner.wrapVal)) { //因为布局子视图的新老尺寸计算在上面有两种不同的方法,因此这里需要考虑两种计算的误差值,而这两种计算的误差值是不超过1/屏幕精度的。 //因此我们认为当二者的值超过误差时我们才认为有尺寸变化。 - BOOL isWidthAlter = _myCGFloatErrorNotEqual(newSelfSize.width, oldSelfSize.width, sSizeError); + BOOL isWidthAlter = _myCGFloatErrorNotEqual(newSelfSize.width, oldSelfSize.width, sSizeError); BOOL isHeightAlter = _myCGFloatErrorNotEqual(newSelfSize.height, oldSelfSize.height, sSizeError); - + //如果父视图也是布局视图,并且自己隐藏则不调整自身的尺寸和位置。 BOOL isAdjustSelf = YES; - if (self.superview != nil && [self.superview isKindOfClass:[MyBaseLayout class]]) - { - MyBaseLayout *supl = (MyBaseLayout*)self.superview; - if ([supl myIsNoLayoutSubview:self]) + if (self.superview != nil && [self.superview isKindOfClass:[MyBaseLayout class]]) { + if ([(MyViewTraits*)self.myCurrentSizeClass invalid]) { isAdjustSelf = NO; + } } - if (isAdjustSelf && (isWidthAlter || isHeightAlter)) - { - - if (newSelfSize.width < 0) - { + if (isAdjustSelf && (isWidthAlter || isHeightAlter)) { + if (newSelfSize.width < 0) { newSelfSize.width = 0; } - - if (newSelfSize.height < 0) - { + + if (newSelfSize.height < 0) { newSelfSize.height = 0; } - - if (CGAffineTransformIsIdentity(self.transform)) - { + if (CGAffineTransformIsIdentity(self.transform)) { CGRect currentFrame = self.frame; - if (isWidthAlter && lsc.wrapContentWidth) + if (isWidthAlter && layoutTraits.widthSizeInner.wrapVal) { currentFrame.size.width = newSelfSize.width; - - if (isHeightAlter && lsc.wrapContentHeight) + } + if (isHeightAlter && layoutTraits.heightSizeInner.wrapVal) { currentFrame.size.height = newSelfSize.height; - + } self.frame = currentFrame; - } - else - { + } else { CGRect currentBounds = self.bounds; CGPoint currentCenter = self.center; - - if (isWidthAlter && lsc.wrapContentWidth) - { + + //针对滚动父视图做特殊处理,如果父视图是滚动视图,而且当前的缩放比例不为1时系统会调整中心点的位置,因此这里需要特殊处理。 + CGFloat superViewZoomScale = 1.0; + if ([self.superview isKindOfClass:[UIScrollView class]]) { + superViewZoomScale = ((UIScrollView *)self.superview).zoomScale; + } + if (isWidthAlter && layoutTraits.widthSizeInner.wrapVal) { currentBounds.size.width = newSelfSize.width; - currentCenter.x += (newSelfSize.width - oldSelfSize.width) * self.layer.anchorPoint.x; + currentCenter.x += (newSelfSize.width - oldSelfSize.width) * self.layer.anchorPoint.x * superViewZoomScale; } - - if (isHeightAlter && lsc.wrapContentHeight) - { + if (isHeightAlter && layoutTraits.heightSizeInner.wrapVal) { currentBounds.size.height = newSelfSize.height; - currentCenter.y += (newSelfSize.height - oldSelfSize.height) * self.layer.anchorPoint.y; + currentCenter.y += (newSelfSize.height - oldSelfSize.height) * self.layer.anchorPoint.y * superViewZoomScale; } - self.bounds = currentBounds; self.center = currentCenter; - - } + } } } - - [_borderlineLayerDelegate setNeedsLayoutIn:CGRectMake(0, 0, newSelfSize.width, newSelfSize.height) withLayer:self.layer]; - - //这里只用width判断的原因是如果newSelfSize被计算成功则size中的所有值都不是CGFLOAT_MAX,所以这里选width只是其中一个代表。 - if (newSelfSize.width != CGFLOAT_MAX) - { - UIView *supv = self.superview; + //这里只用width判断的原因是如果newSelfSize被计算成功则size中的所有值都不是CGFLOAT_MAX,所以这里选width只是其中一个代表。 + if (newSelfSize.width != CGFLOAT_MAX) { + UIView *superview = self.superview; + //更新边界线。 + if (_borderlineLayerDelegate != nil) { + CGRect borderlineRect = CGRectMake(0, 0, newSelfSize.width, newSelfSize.height); + if ([superview isKindOfClass:[MyBaseLayout class]]) { + //这里给父布局视图一个机会来可以改变当前布局的borderlineRect的值,也就是显示的边界线有可能会超出当前布局视图本身的区域。 + //比如一些表格或者其他的情况。默认情况下这个函数什么也不做。 + [((MyBaseLayout *)superview) myHookSublayout:self borderlineRect:&borderlineRect]; + } - //如果自己的父视图是非UIScrollView以及非布局视图。以及自己是wrapContentWidth或者wrapContentHeight时,并且如果设置了在父视图居中或者居下或者居右时要在父视图中更新自己的位置。 - if (supv != nil && ![supv isKindOfClass:[MyBaseLayout class]]) - { + [_borderlineLayerDelegate setNeedsLayoutIn:borderlineRect withLayer:self.layer]; + } + //如果自己的父视图是非UIScrollView以及非布局视图。以及自己的宽度或者高度是包裹的,并且如果设置了在父视图居中或者居下或者居右时要在父视图中更新自己的位置。 + if (superview != nil && ![superview isKindOfClass:[MyBaseLayout class]]) { CGPoint centerPonintSelf = self.center; CGRect rectSelf = self.bounds; - CGRect rectSuper = supv.bounds; + CGRect rectSuper = superview.bounds; //特殊处理低版本下的top和bottom的两种安全区域的场景。 - if ((lsc.topPosInner.isSafeAreaPos || lsc.bottomPosInner.isSafeAreaPos) && [UIDevice currentDevice].systemVersion.doubleValue < 11 ) - { - if (lsc.topPosInner.isSafeAreaPos) - { - centerPonintSelf.y = [lsc.topPosInner realPosIn:rectSuper.size.height] + self.layer.anchorPoint.y * rectSelf.size.height; - } - else - { - centerPonintSelf.y = rectSuper.size.height - rectSelf.size.height - [lsc.bottomPosInner realPosIn:rectSuper.size.height] + self.layer.anchorPoint.y * rectSelf.size.height; + if ((layoutTraits.topPosInner.isSafeAreaPos || layoutTraits.bottomPosInner.isSafeAreaPos) && [UIDevice currentDevice].systemVersion.doubleValue < 11) { + if (layoutTraits.topPosInner.isSafeAreaPos) { + centerPonintSelf.y = [layoutTraits.topPosInner measureWith:rectSuper.size.height] + self.layer.anchorPoint.y * rectSelf.size.height; + } else { + centerPonintSelf.y = rectSuper.size.height - rectSelf.size.height - [layoutTraits.bottomPosInner measureWith:rectSuper.size.height] + self.layer.anchorPoint.y * rectSelf.size.height; } } - //如果自己的父视图是非UIScrollView以及非布局视图。以及自己是wrapContentWidth或者wrapContentHeight时,并且如果设置了在父视图居中或者居下或者居右时要在父视图中更新自己的位置。 - if (![supv isKindOfClass:[UIScrollView class]] && (lsc.wrapContentWidth || lsc.wrapContentHeight)) - { - - if ([MyBaseLayout isRTL]) + //如果自己的父视图是非UIScrollView以及非布局视图。以及自己的宽度或者高度是包裹的时,并且如果设置了在父视图居中或者居下或者居右时要在父视图中更新自己的位置。 + if (![superview isKindOfClass:[UIScrollView class]] && (layoutTraits.widthSizeInner.wrapVal || layoutTraits.heightSizeInner.wrapVal)) { + + if ([MyBaseLayout isRTL]) { centerPonintSelf.x = rectSuper.size.width - centerPonintSelf.x; - - if (lsc.wrapContentWidth) - { + } + if (layoutTraits.widthSizeInner.wrapVal) { //如果只设置了右边,或者只设置了居中则更新位置。。 - if (lsc.centerXPosInner.posVal != nil) - { - centerPonintSelf.x = (rectSuper.size.width - rectSelf.size.width)/2 + self.layer.anchorPoint.x * rectSelf.size.width; - - centerPonintSelf.x += [lsc.centerXPosInner realPosIn:rectSuper.size.width]; - } - else if (lsc.trailingPosInner.posVal != nil && lsc.leadingPosInner.posVal == nil) - { - centerPonintSelf.x = rectSuper.size.width - rectSelf.size.width - [lsc.trailingPosInner realPosIn:rectSuper.size.width] + self.layer.anchorPoint.x * rectSelf.size.width; + if (layoutTraits.centerXPosInner.val != nil) { + centerPonintSelf.x = (rectSuper.size.width - rectSelf.size.width) / 2 + self.layer.anchorPoint.x * rectSelf.size.width; + + centerPonintSelf.x += [layoutTraits.centerXPosInner measureWith:rectSuper.size.width]; + } else if (layoutTraits.trailingPosInner.val != nil && layoutTraits.leadingPosInner.val == nil) { + centerPonintSelf.x = rectSuper.size.width - rectSelf.size.width - [layoutTraits.trailingPosInner measureWith:rectSuper.size.width] + self.layer.anchorPoint.x * rectSelf.size.width; } - } - - if (lsc.wrapContentHeight) - { - if (lsc.centerYPosInner.posVal != nil) - { - centerPonintSelf.y = (rectSuper.size.height - rectSelf.size.height)/2 + [lsc.centerYPosInner realPosIn:rectSuper.size.height] + self.layer.anchorPoint.y * rectSelf.size.height; - } - else if (lsc.bottomPosInner.posVal != nil && lsc.topPosInner.posVal == nil) - { + + if (layoutTraits.heightSizeInner.wrapVal) { + if (layoutTraits.centerYPosInner.val != nil) { + centerPonintSelf.y = (rectSuper.size.height - rectSelf.size.height) / 2 + [layoutTraits.centerYPosInner measureWith:rectSuper.size.height] + self.layer.anchorPoint.y * rectSelf.size.height; + } else if (layoutTraits.bottomPosInner.val != nil && layoutTraits.topPosInner.val == nil) { //这里可能有坑,在有安全区时。但是先不处理了。 - centerPonintSelf.y = rectSuper.size.height - rectSelf.size.height - [lsc.bottomPosInner realPosIn:rectSuper.size.height] + self.layer.anchorPoint.y * rectSelf.size.height; + centerPonintSelf.y = rectSuper.size.height - rectSelf.size.height - [layoutTraits.bottomPosInner measureWith:rectSuper.size.height] + self.layer.anchorPoint.y * rectSelf.size.height; } } - - if ([MyBaseLayout isRTL]) + + if ([MyBaseLayout isRTL]) { centerPonintSelf.x = rectSuper.size.width - centerPonintSelf.x; - + } } - + //如果有变化则只调整自己的center。而不变化 - if (!_myCGPointEqual(self.center, centerPonintSelf)) - { + if (!_myCGPointEqual(self.center, centerPonintSelf)) { self.center = centerPonintSelf; } - } - - + //这里处理当布局视图的父视图是非布局父视图,且父视图具有wrap属性时需要调整父视图的尺寸。 - if (supv != nil && ![supv isKindOfClass:[MyBaseLayout class]]) - { - if (supv.wrapContentHeight || supv.wrapContentWidth) - { + if (superview != nil && ![superview isKindOfClass:[MyBaseLayout class]]) { + if (superview.heightSizeInner.wrapVal || superview.widthSizeInner.wrapVal) { //调整父视图的高度和宽度。frame值。 - CGRect superBounds = supv.bounds; - CGPoint superCenter = supv.center; - - if (supv.wrapContentHeight) - { - superBounds.size.height = [self myValidMeasure:supv.heightSizeInner sbv:supv calcSize:lsc.myTop + newSelfSize.height + lsc.myBottom sbvSize:superBounds.size selfLayoutSize:newSelfSize]; - superCenter.y += (superBounds.size.height - supv.bounds.size.height) * supv.layer.anchorPoint.y; + CGRect superBounds = superview.bounds; + CGPoint superCenter = superview.center; + + if (superview.heightSizeInner.wrapVal) { + superBounds.size.height = [self myValidMeasure:superview.heightSizeInner subview:superview calcSize:layoutTraits.myTop + newSelfSize.height + layoutTraits.myBottom subviewSize:superBounds.size selfLayoutSize:newSelfSize]; + superCenter.y += (superBounds.size.height - superview.bounds.size.height) * superview.layer.anchorPoint.y; } - - if (supv.wrapContentWidth) - { - superBounds.size.width = [self myValidMeasure:supv.widthSizeInner sbv:supv calcSize:lsc.myLeading + newSelfSize.width + lsc.myTrailing sbvSize:superBounds.size selfLayoutSize:newSelfSize]; - superCenter.x += (superBounds.size.width - supv.bounds.size.width) * supv.layer.anchorPoint.x; + + if (superview.widthSizeInner.wrapVal) { + superBounds.size.width = [self myValidMeasure:superview.widthSizeInner subview:superview calcSize:layoutTraits.myLeading + newSelfSize.width + layoutTraits.myTrailing subviewSize:superBounds.size selfLayoutSize:newSelfSize]; + superCenter.x += (superBounds.size.width - superview.bounds.size.width) * superview.layer.anchorPoint.x; } - - if (!_myCGRectEqual(supv.bounds, superBounds)) - { - supv.center = superCenter; - supv.bounds = superBounds; + + if (!_myCGRectEqual(superview.bounds, superBounds)) { + superview.center = superCenter; + superview.bounds = superBounds; } - } } - + //处理父视图是滚动视图时动态调整滚动视图的contentSize - [self myAlterScrollViewContentSize:newSelfSize lsc:lsc]; + [self myLayout:layoutTraits adjustScrollViewContentWithSize:newSelfSize]; } - - if (selfMyFrame.multiple) - selfMyFrame.sizeClass = [self myDefaultSizeClass]; - _isMyLayouting = NO; - + self.isMyLayouting = NO; } - - if (self.endLayoutBlock != nil) - self.endLayoutBlock(); - self.endLayoutBlock = nil; //执行屏幕旋转的处理逻辑。 - if (currentScreenOrientation != 0 && self.rotationToDeviceOrientationBlock != nil) - { - if (_lastScreenOrientation == 0) - { - _lastScreenOrientation = currentScreenOrientation; - self.rotationToDeviceOrientationBlock(self,YES, currentScreenOrientation == 1); - } - else - { - if (_lastScreenOrientation != currentScreenOrientation) - { - _lastScreenOrientation = currentScreenOrientation; - self.rotationToDeviceOrientationBlock(self, NO, currentScreenOrientation == 1); + if (currentScreenOrientation != 0 && _optionalData.rotationToDeviceOrientationBlock != nil) { + if (_optionalData.lastScreenOrientation == 0) { + _optionalData.lastScreenOrientation = currentScreenOrientation; + _optionalData.rotationToDeviceOrientationBlock(self, YES, currentScreenOrientation == 1); + } else { + if (_optionalData.lastScreenOrientation != currentScreenOrientation) { + _optionalData.lastScreenOrientation = currentScreenOrientation; + _optionalData.rotationToDeviceOrientationBlock(self, NO, currentScreenOrientation == 1); } } - - _lastScreenOrientation = currentScreenOrientation; + _optionalData.lastScreenOrientation = currentScreenOrientation; } - - -} - - -#pragma mark -- Deprecated Method - --(CGRect)estimateLayoutRect:(CGSize)size -{ - CGRect rect = CGRectZero; - rect.size = [self sizeThatFits:size]; - return rect; -} - --(CGRect)estimateLayoutRect:(CGSize)size inSizeClass:(MySizeClass)sizeClass -{ - CGRect rect = CGRectZero; - rect.size = [self sizeThatFits:size inSizeClass:sizeClass]; - return rect; } +- (void)layoutSubviews { + if (!self.autoresizesSubviews) { + return; + } + if (_optionalData.beginLayoutBlock != nil) { + _optionalData.beginLayoutBlock(); + _optionalData.beginLayoutBlock = nil; + } -#pragma mark -- Private Method + if (_optionalData == nil || _optionalData.aniDuration <= 0) { + [self doLayoutSubviews]; + } else { + [UIView animateWithDuration:_optionalData.aniDuration + delay:0 + options:_optionalData.aniOptions + animations:^{ + [self doLayoutSubviews]; + } + completion:_optionalData.aniCompletion]; + _optionalData.aniDuration = 0.0; + _optionalData.aniCompletion = nil; + } + if (_optionalData.endLayoutBlock != nil) { + _optionalData.endLayoutBlock(); + _optionalData.endLayoutBlock = nil; + } --(BOOL)myIsRelativePos:(CGFloat)margin -{ - return margin > 0 && margin < 1; + //因为rotationToDeviceOrientationBlock设置后不会在内部被清除,而其他的都会被清除。 + //所有只要rotationToDeviceOrientationBlock为空就可以将可选的多余数据给清除掉了。 + if (_optionalData != nil && _optionalData.rotationToDeviceOrientationBlock == nil) { + _optionalData = nil; + } } - --(MyGravity)myGetSubviewVertGravity:(UIView*)sbv sbvsc:(UIView*)sbvsc vertGravity:(MyGravity)vertGravity -{ - MyGravity sbvVertAligement = sbvsc.myAlignment & MyGravity_Horz_Mask; - MyGravity sbvVertGravity = MyGravity_Vert_Top; +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection { + [super traitCollectionDidChange:previousTraitCollection]; + BOOL canUpdateBorderlineColor = NO; + if (@available(iOS 13.0, *)) { + canUpdateBorderlineColor = [self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]; + } else if (@available(iOS 12.0, *)) { + canUpdateBorderlineColor = (self.traitCollection.userInterfaceStyle != previousTraitCollection.userInterfaceStyle); + } else { + // Fallback on earlier versions + } - if (vertGravity != MyGravity_None) - { - sbvVertGravity = vertGravity; - if (sbvVertAligement != MyGravity_None) - { - sbvVertGravity = sbvVertAligement; - } + if (canUpdateBorderlineColor) { + [_borderlineLayerDelegate updateAllBorderlineColor]; } - else - { - - if (sbvVertAligement != MyGravity_None) - { - sbvVertGravity = sbvVertAligement; - } - - if (sbvsc.topPosInner.posVal != nil && sbvsc.bottomPosInner.posVal != nil) - { - sbvVertGravity = MyGravity_Vert_Fill; - } - else if (sbvsc.centerYPosInner.posVal != nil) - { - sbvVertGravity = MyGravity_Vert_Center; - } - else if (sbvsc.topPosInner.posVal != nil) - { - sbvVertGravity = MyGravity_Vert_Top; - } - else if (sbvsc.bottomPosInner.posVal != nil) - { - sbvVertGravity = MyGravity_Vert_Bottom; - } +} + +#pragma mark-- Private Methods + +- (MyLayoutEngine *)myEngine { + if (_myEngine == nil) { + _myEngine = [MyLayoutEngine new]; } - - return sbvVertGravity; + return _myEngine; } +- (MyLayoutEngine *)myEngineInner { + return _myEngine; +} --(void)myCalcVertGravity:(MyGravity)vertGravity - sbv:(UIView *)sbv - sbvsc:(UIView*)sbvsc - paddingTop:(CGFloat)paddingTop - paddingBottom:(CGFloat)paddingBottom - baselinePos:(CGFloat)baselinePos - selfSize:(CGSize)selfSize - pRect:(CGRect*)pRect -{ +- (CGSize)myEstimateLayoutSize:(CGSize)size inSizeClass:(MySizeClass)sizeClass subviews:(NSMutableArray *)subviews { + NSArray *tuple = [self myUpdateCurrentSizeClass:sizeClass subviews:subviews]; + MyLayoutEngine *layoutViewEngine = tuple.firstObject; + NSMutableArray *subviewEngines = tuple.lastObject; - CGFloat topMargin = [self myValidMargin:sbvsc.topPosInner sbv:sbv calcPos:[sbvsc.topPosInner realPosIn:selfSize.height - paddingTop - paddingBottom] selfLayoutSize:selfSize]; - CGFloat centerMargin = [self myValidMargin:sbvsc.centerYPosInner sbv:sbv calcPos:[sbvsc.centerYPosInner realPosIn:selfSize.height - paddingTop - paddingBottom] selfLayoutSize:selfSize]; - CGFloat bottomMargin = [self myValidMargin:sbvsc.bottomPosInner sbv:sbv calcPos:[sbvsc.bottomPosInner realPosIn:selfSize.height - paddingTop - paddingBottom] selfLayoutSize:selfSize]; - - //确保设置基线对齐的视图都是UILabel,UITextField,UITextView - if (baselinePos == CGFLOAT_MAX && vertGravity == MyGravity_Vert_Baseline) - vertGravity = MyGravity_Vert_Top; + MyLayoutContext context; + context.isEstimate = YES; + context.sizeClass = sizeClass; + context.layoutViewEngine = layoutViewEngine; - UIFont *sbvFont = nil; - if (vertGravity == MyGravity_Vert_Baseline) - { - sbvFont = [self myGetSubviewFont:sbv]; + if (layoutViewEngine.currentSizeClass.widthSizeInner.numberVal != nil) { + size.width = MAX(layoutViewEngine.currentSizeClass.widthSizeInner.measure, size.width); } - if (sbvFont == nil && vertGravity == MyGravity_Vert_Baseline) - vertGravity = MyGravity_Vert_Top; + if (layoutViewEngine.currentSizeClass.heightSizeInner.numberVal != nil) { + size.height = MAX(layoutViewEngine.currentSizeClass.heightSizeInner.measure, size.height); + } + CGSize selfSize = [self calcLayoutSize:size subviewEngines:subviewEngines context:&context]; - if (vertGravity == MyGravity_Vert_Top) - { - pRect->origin.y = paddingTop + topMargin; - } - else if (vertGravity == MyGravity_Vert_Bottom) - { - pRect->origin.y = selfSize.height - paddingBottom - bottomMargin - pRect->size.height; + layoutViewEngine.width = selfSize.width; + layoutViewEngine.height = selfSize.height; + + if (self.cacheEstimatedRect) { + _useCacheRects = YES; } - else if (vertGravity == MyGravity_Vert_Baseline) - { - //得到基线位置。 - pRect->origin.y = baselinePos - sbvFont.ascender - (pRect->size.height - sbvFont.lineHeight) / 2; + return _myCGSizeRound(selfSize); +} - } - else if (vertGravity == MyGravity_Vert_Fill) - { - pRect->origin.y = paddingTop + topMargin; - pRect->size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:selfSize.height - paddingTop - paddingBottom - topMargin - bottomMargin sbvSize:pRect->size selfLayoutSize:selfSize]; - } - else if (vertGravity == MyGravity_Vert_Center) - { - pRect->origin.y = (selfSize.height - paddingTop - paddingBottom - topMargin - bottomMargin - pRect->size.height)/2 + paddingTop + topMargin + centerMargin; - } - else if (vertGravity == MyGravity_Vert_Window_Center) - { - if (self.window != nil) - { - pRect->origin.y = (CGRectGetHeight(self.window.bounds) - topMargin - bottomMargin - pRect->size.height)/2 + topMargin + centerMargin; - pRect->origin.y = [self.window convertPoint:pRect->origin toView:self].y; - } - } - else - { - ; - } - +- (CGFloat)myCalcSubview:(MyLayoutEngine *)subviewEngine + vertGravity:(MyGravity)vertGravity + baselinePos:(CGFloat)baselinePos + withContext:(MyLayoutContext *)context { -} + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + UIView *subview = subviewTraits.view; + CGFloat layoutViewContentHeight = context->selfSize.height - context->paddingTop - context->paddingBottom; + CGFloat marginTop = [self myValidMargin:subviewTraits.topPosInner subview:subview calcPos:[subviewTraits.topPosInner measureWith:layoutViewContentHeight] selfLayoutSize:context->selfSize]; + CGFloat marginCenter = [self myValidMargin:subviewTraits.centerYPosInner subview:subview calcPos:[subviewTraits.centerYPosInner measureWith:layoutViewContentHeight] selfLayoutSize:context->selfSize]; + CGFloat marginBottom = [self myValidMargin:subviewTraits.bottomPosInner subview:subview calcPos:[subviewTraits.bottomPosInner measureWith:layoutViewContentHeight] selfLayoutSize:context->selfSize]; --(MyGravity)myGetSubviewHorzGravity:(UIView*)sbv sbvsc:(UIView*)sbvsc horzGravity:(MyGravity)horzGravity -{ - MyGravity sbvHorzAligement = [self myConvertLeftRightGravityToLeadingTrailing:sbvsc.myAlignment & MyGravity_Vert_Mask]; - MyGravity sbvHorzGravity = MyGravity_Horz_Leading; - - if (horzGravity != MyGravity_None) - { - sbvHorzGravity = horzGravity; - if (sbvHorzAligement != MyGravity_None) - { - sbvHorzGravity = sbvHorzAligement; + //垂直压缩。 + CGFloat fixedHeight = marginTop + marginCenter + marginBottom + subviewEngine.height; + if (fixedHeight > context->selfSize.height) { + CGFloat spareHeight = context->selfSize.height - fixedHeight; + CGFloat totalShrink = subviewTraits.topPosInner.shrink + subviewTraits.centerYPosInner.shrink + subviewTraits.bottomPosInner.shrink + subviewTraits.heightSizeInner.shrink; + if (totalShrink != 0.0) { + marginTop += (subviewTraits.topPosInner.shrink / totalShrink) * spareHeight; + marginCenter += (subviewTraits.centerYPosInner.shrink / totalShrink) * spareHeight; + marginBottom += (subviewTraits.bottomPosInner.shrink / totalShrink) * spareHeight; + subviewEngine.height += (subviewTraits.heightSizeInner.shrink / totalShrink) * spareHeight; } } - else - { - - if (sbvHorzAligement != MyGravity_None) - { - sbvHorzGravity = sbvHorzAligement; - } - - if (sbvsc.leadingPosInner.posVal != nil && sbvsc.trailingPosInner.posVal != nil) - { - sbvHorzGravity = MyGravity_Horz_Fill; - } - else if (sbvsc.centerXPosInner.posVal != nil) - { - sbvHorzGravity = MyGravity_Horz_Center; - } - else if (sbvsc.leadingPosInner.posVal != nil) - { - sbvHorzGravity = MyGravity_Horz_Leading; - } - else if (sbvsc.trailingPosInner.posVal != nil) - { - sbvHorzGravity = MyGravity_Horz_Trailing; + + //如果是设置垂直拉伸则,如果子视图有约束则不受影响,否则就变为和填充一个意思。 + if (vertGravity == MyGravity_Vert_Stretch) { + if (subviewTraits.heightSizeInner.val != nil && subviewTraits.heightSizeInner.priority != MyPriority_Low) { + vertGravity = MyGravity_Vert_Top; + } else { + vertGravity = MyGravity_Vert_Fill; } } - - return sbvHorzGravity; -} - - --(void)myCalcHorzGravity:(MyGravity)horzGravity - sbv:(UIView *)sbv - sbvsc:(UIView*)sbvsc - paddingLeading:(CGFloat)paddingLeading - paddingTrailing:(CGFloat)paddingTrailing - selfSize:(CGSize)selfSize - pRect:(CGRect*)pRect -{ - CGFloat paddingHorz = paddingLeading + paddingTrailing; - - CGFloat leadingMargin = [self myValidMargin:sbvsc.leadingPosInner sbv:sbv calcPos:[sbvsc.leadingPosInner realPosIn:selfSize.width - paddingHorz] selfLayoutSize:selfSize]; - - CGFloat centerMargin = [self myValidMargin:sbvsc.centerXPosInner sbv:sbv calcPos:[sbvsc.centerXPosInner realPosIn:selfSize.width - paddingHorz] selfLayoutSize:selfSize]; - - CGFloat trailingMargin = [self myValidMargin:sbvsc.trailingPosInner sbv:sbv calcPos:[sbvsc.trailingPosInner realPosIn:selfSize.width - paddingHorz] selfLayoutSize:selfSize]; - - if (horzGravity == MyGravity_Horz_Leading) - { - pRect->origin.x = paddingLeading + leadingMargin; - } - else if (horzGravity == MyGravity_Horz_Trailing) - { - pRect->origin.x = selfSize.width - paddingTrailing - trailingMargin - pRect->size.width; + //确保设置基线对齐的视图都是UILabel,UITextField,UITextView + if (baselinePos == CGFLOAT_MAX && vertGravity == MyGravity_Vert_Baseline) { + vertGravity = MyGravity_Vert_Top; } - if (horzGravity == MyGravity_Horz_Fill) - { - - pRect->origin.x = paddingLeading + leadingMargin; - pRect->size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:selfSize.width - paddingHorz - leadingMargin - trailingMargin sbvSize:pRect->size selfLayoutSize:selfSize]; - + UIFont *subviewFont = nil; + if (vertGravity == MyGravity_Vert_Baseline) { + subviewFont = [self myGetSubviewFont:subview]; } - else if (horzGravity == MyGravity_Horz_Center) - { - pRect->origin.x = (selfSize.width - paddingHorz - leadingMargin - trailingMargin - pRect->size.width)/2 + paddingLeading + leadingMargin + centerMargin; + if (subviewFont == nil && vertGravity == MyGravity_Vert_Baseline) { + vertGravity = MyGravity_Vert_Top; } - else if (horzGravity == MyGravity_Horz_Window_Center) - { - if (self.window != nil) - { - pRect->origin.x = (CGRectGetWidth(self.window.bounds) - leadingMargin - trailingMargin - pRect->size.width)/2 + leadingMargin + centerMargin; - pRect->origin.x = [self.window convertPoint:pRect->origin toView:self].x; - - //因为从右到左布局最后统一进行了转换,但是窗口居中是不按布局来控制的,所以这里为了保持不变需要进行特殊处理。 - if ([MyBaseLayout isRTL]) - { - pRect->origin.x = selfSize.width - pRect->origin.x - pRect->size.width; - } - } + if (vertGravity == MyGravity_Vert_Top) { + subviewEngine.top = context->paddingTop + marginTop; + } else if (vertGravity == MyGravity_Vert_Bottom) { + subviewEngine.top = context->selfSize.height - context->paddingBottom - marginBottom - subviewEngine.height; + } else if (vertGravity == MyGravity_Vert_Baseline) { + //得到基线位置。 + subviewEngine.top = baselinePos - subviewFont.ascender - (subviewEngine.height - subviewFont.lineHeight) / 2; + } else if (vertGravity == MyGravity_Vert_Fill) { + subviewEngine.top = context->paddingTop + marginTop; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subview calcSize:layoutViewContentHeight - marginTop - marginBottom subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } else if (vertGravity == MyGravity_Vert_Center) { + subviewEngine.top = (layoutViewContentHeight - marginTop - marginBottom - subviewEngine.height) / 2 + context->paddingTop + marginTop + marginCenter; + } else if (vertGravity == MyGravity_Vert_Window_Center) { + if (self.window != nil) { + subviewEngine.top = (CGRectGetHeight(self.window.bounds) - marginTop - marginBottom - subviewEngine.height) / 2 + marginTop + marginCenter; + subviewEngine.top = [self.window convertPoint:subviewEngine.origin toView:self].y; + } } - else - { - ; - } + return marginTop + marginCenter + marginBottom + subviewEngine.height; } +- (CGFloat)myCalcSubview:(MyLayoutEngine *)subviewEngine + horzGravity:(MyGravity)horzGravity + withContext:(MyLayoutContext *)context { + + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + UIView *subview = subviewTraits.view; + CGFloat layoutViewContentWidth = context->selfSize.width - context->paddingLeading - context->paddingTrailing; + CGFloat marginLeading = [self myValidMargin:subviewTraits.leadingPosInner subview:subview calcPos:[subviewTraits.leadingPosInner measureWith:layoutViewContentWidth] selfLayoutSize:context->selfSize]; + CGFloat marginCenter = [self myValidMargin:subviewTraits.centerXPosInner subview:subview calcPos:[subviewTraits.centerXPosInner measureWith:layoutViewContentWidth] selfLayoutSize:context->selfSize]; + CGFloat marginTrailing = [self myValidMargin:subviewTraits.trailingPosInner subview:subview calcPos:[subviewTraits.trailingPosInner measureWith:layoutViewContentWidth] selfLayoutSize:context->selfSize]; + + //水平压缩。 + CGFloat fixedWidth = marginLeading + marginCenter + marginTrailing + subviewEngine.width; + if (fixedWidth > context->selfSize.width) { + CGFloat spareWidth = context->selfSize.width - fixedWidth; + CGFloat totalShrink = subviewTraits.leadingPosInner.shrink + subviewTraits.centerXPosInner.shrink + subviewTraits.trailingPosInner.shrink + subviewTraits.widthSizeInner.shrink; + if (totalShrink != 0.0) { + marginLeading += (subviewTraits.leadingPosInner.shrink / totalShrink) * spareWidth; + marginCenter += (subviewTraits.centerXPosInner.shrink / totalShrink) * spareWidth; + marginTrailing += (subviewTraits.trailingPosInner.shrink / totalShrink) * spareWidth; + subviewEngine.width += (subviewTraits.widthSizeInner.shrink / totalShrink) * spareWidth; + } + } + + //如果是设置水平拉伸则,如果子视图有约束则不受影响,否则就变为和填充一个意思。 + if (horzGravity == MyGravity_Horz_Stretch) { + if (subviewTraits.widthSizeInner.val != nil && subviewTraits.widthSizeInner.priority != MyPriority_Low) { + horzGravity = MyGravity_Horz_Leading; + } else { + horzGravity = MyGravity_Horz_Fill; + } + } + + if (horzGravity == MyGravity_Horz_Leading) { + subviewEngine.leading = context->paddingLeading + marginLeading; + } else if (horzGravity == MyGravity_Horz_Trailing) { + subviewEngine.leading = context->selfSize.width - context->paddingTrailing - marginTrailing - subviewEngine.width; + } + if (horzGravity == MyGravity_Horz_Fill) { + subviewEngine.leading = context->paddingLeading + marginLeading; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subview calcSize:layoutViewContentWidth - marginLeading - marginTrailing subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } else if (horzGravity == MyGravity_Horz_Center) { + subviewEngine.leading = (layoutViewContentWidth - marginLeading - marginTrailing - subviewEngine.width) / 2 + context->paddingLeading + marginLeading + marginCenter; + } else if (horzGravity == MyGravity_Horz_Window_Center) { + if (self.window != nil) { + subviewEngine.leading = (CGRectGetWidth(self.window.bounds) - marginLeading - marginTrailing - subviewEngine.width) / 2 + marginLeading + marginCenter; + subviewEngine.leading = [self.window convertPoint:subviewEngine.origin toView:self].x; --(void)myCalcSizeOfWrapContentSubview:(UIView*)sbv sbvsc:(UIView*)sbvsc sbvmyFrame:(MyFrame*)sbvmyFrame -{ - BOOL isLayoutView = [sbv isKindOfClass:[MyBaseLayout class]]; - BOOL isWrapWidth = (sbvsc.widthSizeInner.dimeSelfVal != nil) || (!isLayoutView && sbvsc.wrapContentWidth); //宽度包裹特殊处理 - BOOL isWrapHeight = (sbvsc.heightSizeInner.dimeSelfVal != nil) || (!isLayoutView && sbvsc.wrapContentSize);//高度包裹也特殊处理 - - - if (isWrapWidth || isWrapHeight) - { - CGSize fitSize = [sbv sizeThatFits:CGSizeZero]; - if (isWrapWidth) - { - if (sbvsc.wrapContentWidth) - sbvmyFrame.width = fitSize.width; - else - sbvmyFrame.width = [sbvsc.widthSizeInner measureWith:fitSize.width]; - } - - if (isWrapHeight) - { - if (sbvsc.wrapContentHeight) - sbvmyFrame.height = fitSize.height; - else - sbvmyFrame.height = [sbvsc.heightSizeInner measureWith:fitSize.height]; + //因为从右到左布局最后统一进行了转换,但是窗口居中是不按布局来控制的,所以这里为了保持不变需要进行特殊处理。 + if ([MyBaseLayout isRTL]) { + subviewEngine.leading = context->selfSize.width - subviewEngine.leading - subviewEngine.width; + } } } + return marginLeading + marginCenter + marginTrailing + subviewEngine.width; } - --(CGSize)myCalcSizeInNoLayoutSuperview:(UIView*)newSuperview currentSize:(CGSize)size -{ - if (newSuperview == nil || [newSuperview isKindOfClass:[MyBaseLayout class]]) +- (CGSize)myCalcSizeInNoLayoutSuperview:(UIView *)newSuperview currentSize:(CGSize)size { + if (newSuperview == nil || [newSuperview isKindOfClass:[MyBaseLayout class]]) { return size; - + } CGRect rectSuper = newSuperview.bounds; - UIView *ssc = newSuperview.myCurrentSizeClassInner; - UIView *lsc = self.myCurrentSizeClass; - - if (!ssc.wrapContentWidth) - { - if (lsc.widthSizeInner.dimeRelaVal.view == newSuperview) - { - if (lsc.widthSizeInner.dimeRelaVal.dime == MyGravity_Horz_Fill) - size.width = [lsc.widthSizeInner measureWith:rectSuper.size.width]; - else - size.width = [lsc.widthSizeInner measureWith:rectSuper.size.height]; - - size.width = [self myValidMeasure:lsc.widthSizeInner sbv:self calcSize:size.width sbvSize:size selfLayoutSize:rectSuper.size]; + MyViewTraits *superviewTraits = (MyViewTraits*)newSuperview.myDefaultSizeClassInner; //非布局父视图只有默认布局样式 + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)self.myCurrentSizeClass; + + if (!superviewTraits.widthSizeInner.wrapVal) { + if (layoutTraits.widthSizeInner.anchorVal.view == newSuperview) { + if (layoutTraits.widthSizeInner.anchorVal.anchorType == MyLayoutAnchorType_Width) { + size.width = [layoutTraits.widthSizeInner measureWith:rectSuper.size.width]; + } else { + size.width = [layoutTraits.widthSizeInner measureWith:rectSuper.size.height]; + } + size.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:size.width subviewSize:size selfLayoutSize:rectSuper.size]; + } else if (layoutTraits.widthSizeInner.fillVal) { + size.width = [layoutTraits.widthSizeInner measureWith:rectSuper.size.width]; } - - if (lsc.leadingPosInner.posVal != nil && lsc.trailingPosInner.posVal != nil) - { - CGFloat leadingMargin = [lsc.leadingPosInner realPosIn:rectSuper.size.width]; - CGFloat trailingMargin = [lsc.trailingPosInner realPosIn:rectSuper.size.width]; - size.width = rectSuper.size.width - leadingMargin - trailingMargin; - size.width = [self myValidMeasure:lsc.widthSizeInner sbv:self calcSize:size.width sbvSize:size selfLayoutSize:rectSuper.size]; - + + if (layoutTraits.leadingPosInner.val != nil && layoutTraits.trailingPosInner.val != nil) { + if (layoutTraits.widthSizeInner.val == nil || layoutTraits.widthSizeInner.priority == MyPriority_Low) { + CGFloat marginLeading = [layoutTraits.leadingPosInner measureWith:rectSuper.size.width]; + CGFloat marginTrailing = [layoutTraits.trailingPosInner measureWith:rectSuper.size.width]; + size.width = rectSuper.size.width - marginLeading - marginTrailing; + size.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:size.width subviewSize:size selfLayoutSize:rectSuper.size]; + } } - - if (size.width < 0) - { + if (size.width < 0) { size.width = 0; } } - if (!ssc.wrapContentHeight) - { - if (lsc.heightSizeInner.dimeRelaVal.view == newSuperview) - { - if (lsc.heightSizeInner.dimeRelaVal.dime == MyGravity_Vert_Fill) - size.height = [lsc.heightSizeInner measureWith:rectSuper.size.height]; - else - size.height = [lsc.heightSizeInner measureWith:rectSuper.size.width]; - - size.height = [self myValidMeasure:lsc.heightSizeInner sbv:self calcSize:size.height sbvSize:size selfLayoutSize:rectSuper.size]; - + if (!superviewTraits.heightSizeInner.wrapVal) { + if (layoutTraits.heightSizeInner.anchorVal.view == newSuperview) { + if (layoutTraits.heightSizeInner.anchorVal.anchorType == MyLayoutAnchorType_Height) { + size.height = [layoutTraits.heightSizeInner measureWith:rectSuper.size.height]; + } else { + size.height = [layoutTraits.heightSizeInner measureWith:rectSuper.size.width]; + } + size.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:size.height subviewSize:size selfLayoutSize:rectSuper.size]; + } else if (layoutTraits.heightSizeInner.fillVal) { + size.height = [layoutTraits.heightSizeInner measureWith:rectSuper.size.height]; } - - if (lsc.topPosInner.posVal != nil && lsc.bottomPosInner.posVal != nil) - { - CGFloat topMargin = [lsc.topPosInner realPosIn:rectSuper.size.height]; - CGFloat bottomMargin = [lsc.bottomPosInner realPosIn:rectSuper.size.height]; - size.height = rectSuper.size.height - topMargin - bottomMargin; - size.height = [self myValidMeasure:lsc.heightSizeInner sbv:self calcSize:size.height sbvSize:size selfLayoutSize:rectSuper.size]; - + + if (layoutTraits.topPosInner.val != nil && layoutTraits.bottomPosInner.val != nil) { + if (layoutTraits.heightSizeInner.val == nil || layoutTraits.heightSizeInner.priority == MyPriority_Low) { + CGFloat marginTop = [layoutTraits.topPosInner measureWith:rectSuper.size.height]; + CGFloat marginBottom = [layoutTraits.bottomPosInner measureWith:rectSuper.size.height]; + size.height = rectSuper.size.height - marginTop - marginBottom; + size.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:size.height subviewSize:size selfLayoutSize:rectSuper.size]; + } } - - if (size.height < 0) - { + if (size.height < 0) { size.height = 0; } - } - - return size; } +- (BOOL)myUpdateLayoutRectInNoLayoutSuperview:(UIView *)newSuperview { + BOOL isAdjust = NO; //这个变量表明是否后续父视图尺寸变化后需要调整更新布局视图的尺寸。 --(BOOL)myUpdateLayoutRectInNoLayoutSuperview:(UIView*)newSuperview -{ - BOOL isAdjust = NO; - CGRect rectSuper = newSuperview.bounds; - - UIView *lsc = self.myCurrentSizeClass; - - CGFloat leadingMargin = [lsc.leadingPosInner realPosIn:rectSuper.size.width]; - CGFloat trailingMargin = [lsc.trailingPosInner realPosIn:rectSuper.size.width]; - CGFloat topMargin = [lsc.topPosInner realPosIn:rectSuper.size.height]; - CGFloat bottomMargin = [lsc.bottomPosInner realPosIn:rectSuper.size.height]; + MyLayoutTraits *layoutTraits = (MyLayoutTraits *)self.myCurrentSizeClass; + + CGFloat marginLeading = [layoutTraits.leadingPosInner measureWith:rectSuper.size.width]; + CGFloat marginTrailing = [layoutTraits.trailingPosInner measureWith:rectSuper.size.width]; + CGFloat marginTop = [layoutTraits.topPosInner measureWith:rectSuper.size.height]; + CGFloat marginBottom = [layoutTraits.bottomPosInner measureWith:rectSuper.size.height]; CGRect rectSelf = self.bounds; - + + //针对滚动父视图做特殊处理,如果父视图是滚动视图,而且当前的缩放比例不为1时系统会调整中心点的位置,因此这里需要特殊处理。 + CGFloat superViewZoomScale = 1.0; + if ([newSuperview isKindOfClass:[UIScrollView class]]) { + superViewZoomScale = ((UIScrollView *)newSuperview).zoomScale; + } //得到在设置center后的原始值。 - rectSelf.origin.y = self.center.y - rectSelf.size.height * self.layer.anchorPoint.y; - rectSelf.origin.x = self.center.x - rectSelf.size.width * self.layer.anchorPoint.x; + rectSelf.origin.x = self.center.x - rectSelf.size.width * self.layer.anchorPoint.x * superViewZoomScale; + rectSelf.origin.y = self.center.y - rectSelf.size.height * self.layer.anchorPoint.y * superViewZoomScale; CGRect oldRectSelf = rectSelf; - - //确定左右边距和宽度。 - if (lsc.widthSizeInner.dimeVal != nil) - { - lsc.wrapContentWidth = NO; - - if (lsc.widthSizeInner.dimeRelaVal != nil) - { - if (lsc.widthSizeInner.dimeRelaVal.view == newSuperview) - { - if (lsc.widthSizeInner.dimeRelaVal.dime == MyGravity_Horz_Fill) - rectSelf.size.width = [lsc.widthSizeInner measureWith:rectSuper.size.width]; - else - rectSelf.size.width = [lsc.widthSizeInner measureWith:rectSuper.size.height]; + //确定左右边距和宽度。 + if (layoutTraits.widthSizeInner.val != nil) { + if (layoutTraits.widthSizeInner.anchorVal != nil) { + if (layoutTraits.widthSizeInner.anchorVal.view == newSuperview) { + isAdjust = YES; + + if (layoutTraits.widthSizeInner.anchorVal.anchorType == MyLayoutAnchorType_Width) { + rectSelf.size.width = [layoutTraits.widthSizeInner measureWith:rectSuper.size.width]; + } else { + rectSelf.size.width = [layoutTraits.widthSizeInner measureWith:rectSuper.size.height]; + } + } else { + rectSelf.size.width = [layoutTraits.widthSizeInner measureWith:layoutTraits.widthSizeInner.anchorVal.view.myEstimatedWidth]; } - else - { - rectSelf.size.width = [lsc.widthSizeInner measureWith:lsc.widthSizeInner.dimeRelaVal.view.estimatedRect.size.width]; - } + } else if (layoutTraits.widthSizeInner.numberVal != nil) { + rectSelf.size.width = layoutTraits.widthSizeInner.measure; + } else if (layoutTraits.widthSizeInner.fillVal) { isAdjust = YES; + rectSelf.size.width = [layoutTraits.widthSizeInner measureWith:rectSuper.size.width]; } - else - rectSelf.size.width = lsc.widthSizeInner.measure; - - } - - rectSelf.size.width = [self myValidMeasure:lsc.widthSizeInner sbv:self calcSize:rectSelf.size.width sbvSize:rectSelf.size selfLayoutSize:rectSuper.size]; - - if ([MyBaseLayout isRTL]) - rectSelf.origin.x = rectSuper.size.width - rectSelf.origin.x - rectSelf.size.width; + } - - if (lsc.leadingPosInner.posVal != nil && lsc.trailingPosInner.posVal != nil) - { + //这里要判断自己的宽度设置了最小和最大宽度依赖于父视图的情况。如果有这种情况,则父视图在变化时也需要调整自身。 + if (layoutTraits.widthSizeInner.lBoundValInner.anchorVal.view == newSuperview || layoutTraits.widthSizeInner.uBoundValInner.anchorVal.view == newSuperview) { isAdjust = YES; - lsc.wrapContentWidth = NO; - rectSelf.size.width = rectSuper.size.width - leadingMargin - trailingMargin; - rectSelf.size.width = [self myValidMeasure:lsc.widthSizeInner sbv:self calcSize:rectSelf.size.width sbvSize:rectSelf.size selfLayoutSize:rectSuper.size]; - + } + + rectSelf.size.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:rectSelf.size.width subviewSize:rectSelf.size selfLayoutSize:rectSuper.size]; + + if ([MyBaseLayout isRTL]) { + rectSelf.origin.x = rectSuper.size.width - rectSelf.origin.x - rectSelf.size.width; + } + if (layoutTraits.leadingPosInner.val != nil && layoutTraits.trailingPosInner.val != nil) { + isAdjust = YES; + //如果宽度约束的优先级很低都按左右边距来决定布局视图的宽度。 + if (layoutTraits.widthSizeInner.priority == MyPriority_Low) { + [layoutTraits.widthSizeInner _myEqualTo:nil]; + } + if (layoutTraits.widthSizeInner.val == nil) { + rectSelf.size.width = rectSuper.size.width - marginLeading - marginTrailing; + rectSelf.size.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:rectSelf.size.width subviewSize:rectSelf.size selfLayoutSize:rectSuper.size]; + } + #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - + if (@available(iOS 11.0, *)) { - + //在ios11后如果是滚动视图的contentInsetAdjustmentBehavior设置为UIScrollViewContentInsetAdjustmentAlways //那么系统不管contentSize如何总是会将安全区域叠加到contentInsets所以这里的边距不应该是偏移的边距而是0 UIScrollView *scrollSuperView = nil; - if ([newSuperview isKindOfClass:[UIScrollView class]]) - scrollSuperView = (UIScrollView*)newSuperview; - if (scrollSuperView != nil && lsc.leadingPosInner.isSafeAreaPos) - { - leadingMargin = lsc.leadingPosInner.offsetVal + ([MyBaseLayout isRTL] ? scrollSuperView.safeAreaInsets.right : scrollSuperView.safeAreaInsets.left) - ([MyBaseLayout isRTL] ? scrollSuperView.adjustedContentInset.right : scrollSuperView.adjustedContentInset.left); + if ([newSuperview isKindOfClass:[UIScrollView class]]) + scrollSuperView = (UIScrollView *)newSuperview; + if (scrollSuperView != nil && layoutTraits.leadingPosInner.isSafeAreaPos) { + marginLeading = layoutTraits.leadingPosInner.offsetVal + ([MyBaseLayout isRTL] ? scrollSuperView.safeAreaInsets.right : scrollSuperView.safeAreaInsets.left) - ([MyBaseLayout isRTL] ? scrollSuperView.adjustedContentInset.right : scrollSuperView.adjustedContentInset.left); } } #endif - - rectSelf.origin.x = leadingMargin; - } - else if (lsc.centerXPosInner.posVal != nil) - { + + rectSelf.origin.x = marginLeading; + } else if (layoutTraits.centerXPosInner.val != nil) { isAdjust = YES; - rectSelf.origin.x = (rectSuper.size.width - rectSelf.size.width)/2; - rectSelf.origin.x += [lsc.centerXPosInner realPosIn:rectSuper.size.width]; - } - else if (lsc.leadingPosInner.posVal != nil) - { + rectSelf.origin.x = (rectSuper.size.width - rectSelf.size.width) / 2; + rectSelf.origin.x += [layoutTraits.centerXPosInner measureWith:rectSuper.size.width]; + } else if (layoutTraits.leadingPosInner.val != nil) { #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - + if (@available(iOS 11.0, *)) { - //iOS11中的滚动条的安全区会叠加到contentInset里面。因此这里要特殊处理,让x轴的开始位置不应该算偏移。 UIScrollView *scrollSuperView = nil; - if ([newSuperview isKindOfClass:[UIScrollView class]]) - scrollSuperView = (UIScrollView*)newSuperview; - if (scrollSuperView != nil && lsc.leadingPosInner.isSafeAreaPos) - { - leadingMargin = lsc.leadingPosInner.offsetVal + ([MyBaseLayout isRTL] ? scrollSuperView.safeAreaInsets.right : scrollSuperView.safeAreaInsets.left) - ([MyBaseLayout isRTL] ? scrollSuperView.adjustedContentInset.right : scrollSuperView.adjustedContentInset.left); + if ([newSuperview isKindOfClass:[UIScrollView class]]) { + scrollSuperView = (UIScrollView *)newSuperview; + } + if (scrollSuperView != nil && layoutTraits.leadingPosInner.isSafeAreaPos) { + marginLeading = layoutTraits.leadingPosInner.offsetVal + ([MyBaseLayout isRTL] ? scrollSuperView.safeAreaInsets.right : scrollSuperView.safeAreaInsets.left) - ([MyBaseLayout isRTL] ? scrollSuperView.adjustedContentInset.right : scrollSuperView.adjustedContentInset.left); } } #endif - rectSelf.origin.x = leadingMargin; - } - else if (lsc.trailingPosInner.posVal != nil) - { + rectSelf.origin.x = marginLeading; + } else if (layoutTraits.trailingPosInner.val != nil) { isAdjust = YES; - rectSelf.origin.x = rectSuper.size.width - rectSelf.size.width - trailingMargin; + rectSelf.origin.x = rectSuper.size.width - rectSelf.size.width - marginTrailing; } - else; - - - if (lsc.heightSizeInner.dimeVal != nil) - { - lsc.wrapContentHeight = NO; - - if (lsc.heightSizeInner.dimeRelaVal != nil) - { - if (lsc.heightSizeInner.dimeRelaVal.view == newSuperview) - { - if (lsc.heightSizeInner.dimeRelaVal.dime == MyGravity_Vert_Fill) - rectSelf.size.height = [lsc.heightSizeInner measureWith:rectSuper.size.height]; - else - rectSelf.size.height = [lsc.heightSizeInner measureWith:rectSuper.size.width]; - } - else - { - rectSelf.size.height = [lsc.heightSizeInner measureWith:lsc.heightSizeInner.dimeRelaVal.view.estimatedRect.size.height]; + + if (layoutTraits.heightSizeInner.val != nil) { + if (layoutTraits.heightSizeInner.anchorVal != nil) { + if (layoutTraits.heightSizeInner.anchorVal.view == newSuperview) { + isAdjust = YES; + if (layoutTraits.heightSizeInner.anchorVal.anchorType == MyLayoutAnchorType_Height) { + rectSelf.size.height = [layoutTraits.heightSizeInner measureWith:rectSuper.size.height]; + } else { + rectSelf.size.height = [layoutTraits.heightSizeInner measureWith:rectSuper.size.width]; + } + } else { + rectSelf.size.height = [layoutTraits.heightSizeInner measureWith:layoutTraits.heightSizeInner.anchorVal.view.myEstimatedHeight]; } + } else if (layoutTraits.heightSizeInner.numberVal != nil) { + rectSelf.size.height = layoutTraits.heightSizeInner.measure; + } else if (layoutTraits.heightSizeInner.fillVal) { isAdjust = YES; + rectSelf.size.height = [layoutTraits.heightSizeInner measureWith:rectSuper.size.height]; } - else - rectSelf.size.height = lsc.heightSizeInner.measure; } - - rectSelf.size.height = [self myValidMeasure:lsc.heightSizeInner sbv:self calcSize:rectSelf.size.height sbvSize:rectSelf.size selfLayoutSize:rectSuper.size]; - - if (lsc.topPosInner.posVal != nil && lsc.bottomPosInner.posVal != nil) - { + + //这里要判断自己的高度设置了最小和最大高度依赖于父视图的情况。如果有这种情况,则父视图在变化时也需要调整自身。 + if (layoutTraits.heightSizeInner.lBoundValInner.anchorVal.view == newSuperview || layoutTraits.heightSizeInner.uBoundValInner.anchorVal.view == newSuperview) { isAdjust = YES; - lsc.wrapContentHeight = NO; - rectSelf.size.height = rectSuper.size.height - topMargin - bottomMargin; - rectSelf.size.height = [self myValidMeasure:lsc.heightSizeInner sbv:self calcSize:rectSelf.size.height sbvSize:rectSelf.size selfLayoutSize:rectSuper.size]; - + } + + rectSelf.size.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:rectSelf.size.height subviewSize:rectSelf.size selfLayoutSize:rectSuper.size]; + + if (layoutTraits.topPosInner.val != nil && layoutTraits.bottomPosInner.val != nil) { + isAdjust = YES; + //如果高度约束优先级很低则按上下边距来决定布局视图高度。 + if (layoutTraits.heightSizeInner.priority == MyPriority_Low) { + [layoutTraits.heightSizeInner _myEqualTo:nil]; + } + + if (layoutTraits.heightSizeInner.val == nil) { + rectSelf.size.height = rectSuper.size.height - marginTop - marginBottom; + rectSelf.size.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:rectSelf.size.height subviewSize:rectSelf.size selfLayoutSize:rectSuper.size]; + } + #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - + if (@available(iOS 11.0, *)) { - + //在ios11后如果是滚动视图的contentInsetAdjustmentBehavior设置为UIScrollViewContentInsetAdjustmentAlways //那么系统不管contentSize如何总是会将安全区域叠加到contentInsets所以这里的边距不应该是偏移的边距而是0 UIScrollView *scrollSuperView = nil; if ([newSuperview isKindOfClass:[UIScrollView class]]) - scrollSuperView = (UIScrollView*)newSuperview; - if (scrollSuperView != nil && lsc.topPosInner.isSafeAreaPos) - { - topMargin = lsc.topPosInner.offsetVal + scrollSuperView.safeAreaInsets.top - scrollSuperView.adjustedContentInset.top; + scrollSuperView = (UIScrollView *)newSuperview; + if (scrollSuperView != nil && layoutTraits.topPosInner.isSafeAreaPos) { + marginTop = layoutTraits.topPosInner.offsetVal + scrollSuperView.safeAreaInsets.top - scrollSuperView.adjustedContentInset.top; } } #endif - - rectSelf.origin.y = topMargin; - } - else if (lsc.centerYPosInner.posVal != nil) - { + + rectSelf.origin.y = marginTop; + } else if (layoutTraits.centerYPosInner.val != nil) { isAdjust = YES; - rectSelf.origin.y = (rectSuper.size.height - rectSelf.size.height)/2 + [lsc.centerYPosInner realPosIn:rectSuper.size.height]; - } - else if (lsc.topPosInner.posVal != nil) - { + rectSelf.origin.y = (rectSuper.size.height - rectSelf.size.height) / 2 + [layoutTraits.centerYPosInner measureWith:rectSuper.size.height]; + } else if (layoutTraits.topPosInner.val != nil) { #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - + if (@available(iOS 11.0, *)) { - //在ios11后如果是滚动视图的contentInsetAdjustmentBehavior设置为UIScrollViewContentInsetAdjustmentAlways //那么系统不管contentSize如何总是会将安全区域叠加到contentInsets所以这里的边距不应该是偏移的边距而是0 UIScrollView *scrollSuperView = nil; - if ([newSuperview isKindOfClass:[UIScrollView class]]) - scrollSuperView = (UIScrollView*)newSuperview; - if (scrollSuperView != nil && lsc.topPosInner.isSafeAreaPos) - { - topMargin = lsc.topPosInner.offsetVal + scrollSuperView.safeAreaInsets.top - scrollSuperView.adjustedContentInset.top; + if ([newSuperview isKindOfClass:[UIScrollView class]]) { + scrollSuperView = (UIScrollView *)newSuperview; + } + if (scrollSuperView != nil && layoutTraits.topPosInner.isSafeAreaPos) { + marginTop = layoutTraits.topPosInner.offsetVal + scrollSuperView.safeAreaInsets.top - scrollSuperView.adjustedContentInset.top; } } #endif - rectSelf.origin.y = topMargin; - } - else if (lsc.bottomPosInner.posVal != nil) - { + rectSelf.origin.y = marginTop; + } else if (layoutTraits.bottomPosInner.val != nil) { isAdjust = YES; - rectSelf.origin.y = rectSuper.size.height - rectSelf.size.height - bottomMargin; + rectSelf.origin.y = rectSuper.size.height - rectSelf.size.height - marginBottom; } - else; - - if ([MyBaseLayout isRTL]) + + if ([MyBaseLayout isRTL]) { rectSelf.origin.x = rectSuper.size.width - rectSelf.origin.x - rectSelf.size.width; - + } rectSelf = _myCGRectRound(rectSelf); - if (!_myCGRectEqual(rectSelf, oldRectSelf)) - { - if (rectSelf.size.width < 0) - { + if (!_myCGRectEqual(rectSelf, oldRectSelf)) { + if (rectSelf.size.width < 0) { rectSelf.size.width = 0; } - if (rectSelf.size.height < 0) - { + if (rectSelf.size.height < 0) { rectSelf.size.height = 0; } - - if (CGAffineTransformIsIdentity(self.transform)) - { + if (CGAffineTransformIsIdentity(self.transform)) { self.frame = rectSelf; + } else { + self.bounds = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, rectSelf.size.width, rectSelf.size.height); + self.center = CGPointMake(rectSelf.origin.x + self.layer.anchorPoint.x * rectSelf.size.width * superViewZoomScale, rectSelf.origin.y + self.layer.anchorPoint.y * rectSelf.size.height * superViewZoomScale); } - else - { - self.bounds = CGRectMake(self.bounds.origin.x, self.bounds.origin.y,rectSelf.size.width, rectSelf.size.height); - self.center = CGPointMake(rectSelf.origin.x + self.layer.anchorPoint.x * rectSelf.size.width, rectSelf.origin.y + self.layer.anchorPoint.y * rectSelf.size.height); - } - } - else if (lsc.wrapContentWidth || lsc.wrapContentHeight) - { - [self setNeedsLayout]; + } else if (layoutTraits.widthSizeInner.wrapVal || layoutTraits.heightSizeInner.wrapVal) { + [self setNeedsLayout]; } - - - return isAdjust; - } --(CGFloat)myHeightFromFlexedHeightView:(UIView*)sbv sbvsc:(UIView*)sbvsc inWidth:(CGFloat)width -{ - CGFloat h = [sbv sizeThatFits:CGSizeMake(width, 0)].height; - if ([sbv isKindOfClass:[UIImageView class]]) - { - //根据图片的尺寸进行等比缩放得到合适的高度。 - UIImage *img = ((UIImageView*)sbv).image; - if (img != nil && img.size.width != 0) - { - h = img.size.height * (width / img.size.width); - } - } - else if ([sbv isKindOfClass:[UIButton class]]) - { - //按钮特殊处理多行的。。 - UIButton *button = (UIButton*)sbv; +- (CGFloat)mySubview:(MyViewTraits *)subviewTraits wrapHeightSizeFits:(CGSize)size withContext:(MyLayoutContext *)context { + UIView *subview = subviewTraits.view; + + CGFloat height = 0.0; + + if (![subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { - if (button.titleLabel != nil) - { - //得到按钮本身的高度,以及单行文本的高度,这样就能算出按钮和文本的间距 - CGSize buttonSize = [button sizeThatFits:CGSizeMake(0, 0)]; - CGSize buttonTitleSize = [button.titleLabel sizeThatFits:CGSizeMake(0, 0)]; - CGSize sz = [button.titleLabel sizeThatFits:CGSizeMake(width, 0)]; - h = sz.height + buttonSize.height - buttonTitleSize.height; //这个sz只是纯文本的高度,所以要加上原先按钮和文本的高度差。。 + height = [subview sizeThatFits:CGSizeMake(size.width, 0)].height; + if ([subview isKindOfClass:[UIImageView class]]) { + //根据图片的尺寸进行等比缩放得到合适的高度。 + if (!subviewTraits.widthSizeInner.wrapVal) { + UIImage *img = ((UIImageView *)subview).image; + if (img != nil && img.size.width != 0) { + height = img.size.height * (size.width / img.size.width); + } + } + } else if ([subview isKindOfClass:[UIButton class]]) { + //按钮特殊处理多行的。。 + UIButton *button = (UIButton *)subview; + if (button.titleLabel != nil) { + //得到按钮本身的高度,以及单行文本的高度,这样就能算出按钮和文本的间距 + CGSize buttonSize = [button sizeThatFits:CGSizeMake(0, 0)]; + CGSize buttonTitleSize = [button.titleLabel sizeThatFits:CGSizeMake(0, 0)]; + CGSize sz = [button.titleLabel sizeThatFits:CGSizeMake(size.width, 0)]; + height = sz.height + buttonSize.height - buttonTitleSize.height; //这个sz只是纯文本的高度,所以要加上原先按钮和文本的高度差。。 + } } + + } else if (context->isEstimate) { + //只有在评估时才这么处理。 + MyBaseLayout *sublayout = (MyBaseLayout*)subviewTraits.view; + height = [sublayout sizeThatFits:CGSizeMake(size.width, 0) inSizeClass:context->sizeClass].height; + } else { + height = size.height; } - else - ; - - if (sbvsc.heightSizeInner == nil) - return h; - else - return [sbvsc.heightSizeInner measureWith:h]; + return [subviewTraits.heightSizeInner measureWith:height]; } - --(CGFloat)myGetBoundLimitMeasure:(MyLayoutSize*)boundDime sbv:(UIView*)sbv dimeType:(MyGravity)dimeType sbvSize:(CGSize)sbvSize selfLayoutSize:(CGSize)selfLayoutSize isUBound:(BOOL)isUBound -{ +- (CGFloat)myGetBoundLimitMeasure:(MyLayoutSize *)anchor subview:(UIView *)subview anchorType:(MyLayoutAnchorType)anchorType subviewSize:(CGSize)subviewSize selfLayoutSize:(CGSize)selfLayoutSize isUBound:(BOOL)isUBound { CGFloat value = isUBound ? CGFLOAT_MAX : -CGFLOAT_MAX; - if (boundDime == nil) + if (anchor == nil || !anchor.isActive || anchor.valType == MyLayoutValType_Nil) { return value; - - MyLayoutValueType lValueType = boundDime.dimeValType; - if (lValueType == MyLayoutValueType_NSNumber) - { - value = boundDime.dimeNumVal.doubleValue; - } - else if (lValueType == MyLayoutValueType_LayoutDime) - { - if (boundDime.dimeRelaVal.view == self) - { - if (boundDime.dimeRelaVal.dime == MyGravity_Horz_Fill) - value = selfLayoutSize.width - (boundDime.dimeRelaVal.view == self ? (self.myLayoutLeadingPadding + self.myLayoutTrailingPadding) : 0); - else - value = selfLayoutSize.height - (boundDime.dimeRelaVal.view == self ? (self.myLayoutTopPadding + self.myLayoutBottomPadding) :0); - } - else if (boundDime.dimeRelaVal.view == sbv) - { - if (boundDime.dimeRelaVal.dime == dimeType) - { + } + MyLayoutValType valType = anchor.valType; + if (valType == MyLayoutValType_Number || valType == MyLayoutValType_Most) { + value = anchor.numberVal.doubleValue; + } else if (valType == MyLayoutValType_LayoutSize) { + if (anchor.anchorVal.view == self) { + if (anchor.anchorVal.anchorType == MyLayoutAnchorType_Width) { + value = selfLayoutSize.width - (self.myLayoutPaddingLeading + self.myLayoutPaddingTrailing); + } else { + value = selfLayoutSize.height - (self.myLayoutPaddingTop + self.myLayoutPaddingBottom); + } + } else if (anchor.anchorVal.view == subview) { + if (anchor.anchorVal.anchorType == anchorType) { //约束冲突:无效的边界设置方法 - NSCAssert(0, @"Constraint exception!! %@ has invalid lBound or uBound setting",sbv); + NSCAssert(0, @"Constraint exception!! %@ has invalid lBound or uBound setting", subview); + } else { + if (anchor.anchorVal.anchorType == MyLayoutAnchorType_Width) { + value = subviewSize.width; + } else { + value = subviewSize.height; + } } - else - { - if (boundDime.dimeRelaVal.dime == MyGravity_Horz_Fill) - value = sbvSize.width; - else - value = sbvSize.height; + } else { + if (anchor.anchorVal.anchorType == MyLayoutAnchorType_Width) { + value = anchor.anchorVal.view.myEstimatedWidth; + } else { + value = anchor.anchorVal.view.myEstimatedHeight; } } - else if (boundDime.dimeSelfVal != nil) - { - if (dimeType == MyGravity_Horz_Fill) - value = sbvSize.width; - else - value = sbvSize.height; - } - else - { - if (boundDime.dimeRelaVal.dime == MyGravity_Horz_Fill) - { - value = boundDime.dimeRelaVal.view.estimatedRect.size.width; - } - else - { - value = boundDime.dimeRelaVal.view.estimatedRect.size.height; - } + + } else if (valType == MyLayoutValType_Wrap) { + if (anchorType == MyLayoutAnchorType_Width) { + value = subviewSize.width; + } else { + value = subviewSize.height; } - - } - else - { + } else { //约束冲突:无效的边界设置方法 - NSCAssert(0, @"Constraint exception!! %@ has invalid lBound or uBound setting",sbv); + NSCAssert(0, @"Constraint exception!! %@ has invalid lBound or uBound setting", subview); } - - if (value == CGFLOAT_MAX || value == -CGFLOAT_MAX) - return value; - - return [boundDime measureWith:value]; + if (value == CGFLOAT_MAX || value == -CGFLOAT_MAX) { + return value; + } + return [anchor measureWith:value]; } - - --(CGFloat)myValidMeasure:(MyLayoutSize*)dime sbv:(UIView*)sbv calcSize:(CGFloat)calcSize sbvSize:(CGSize)sbvSize selfLayoutSize:(CGSize)selfLayoutSize -{ - if (dime == nil) +- (CGFloat)myValidMeasure:(MyLayoutSize *)anchor subview:(UIView *)subview calcSize:(CGFloat)calcSize subviewSize:(CGSize)subviewSize selfLayoutSize:(CGSize)selfLayoutSize { + if (calcSize < 0.0) { + calcSize = 0.0; + } + if (anchor == nil) { return calcSize; - + } //算出最大最小值。 - CGFloat min = dime.isActive? [self myGetBoundLimitMeasure:dime.lBoundValInner sbv:sbv dimeType:dime.dime sbvSize:sbvSize selfLayoutSize:selfLayoutSize isUBound:NO] : -CGFLOAT_MAX; - CGFloat max = dime.isActive ? [self myGetBoundLimitMeasure:dime.uBoundValInner sbv:sbv dimeType:dime.dime sbvSize:sbvSize selfLayoutSize:selfLayoutSize isUBound:YES] : CGFLOAT_MAX; - - calcSize = _myCGFloatMax(min, calcSize); - calcSize = _myCGFloatMin(max, calcSize); - + if (anchor.isActive) { + if (anchor.lBoundValInner != nil || anchor.uBoundValInner != nil) { + CGFloat min = [self myGetBoundLimitMeasure:anchor.lBoundValInner subview:subview anchorType:anchor.anchorType subviewSize:subviewSize selfLayoutSize:selfLayoutSize isUBound:NO]; + CGFloat max = [self myGetBoundLimitMeasure:anchor.uBoundValInner subview:subview anchorType:anchor.anchorType subviewSize:subviewSize selfLayoutSize:selfLayoutSize isUBound:YES]; + + calcSize = _myCGFloatMax(min, calcSize); + calcSize = _myCGFloatMin(max, calcSize); + } + } return calcSize; } - --(CGFloat)myGetBoundLimitMargin:(MyLayoutPos*)boundPos sbv:(UIView*)sbv selfLayoutSize:(CGSize)selfLayoutSize -{ +- (CGFloat)myGetBound:(MyLayoutPos *)anchor limitMarginOfSubview:(UIView *)subview { CGFloat value = 0; - if (boundPos == nil) + if (anchor == nil) { return value; - - MyLayoutValueType lValueType = boundPos.posValType; - if (lValueType == MyLayoutValueType_NSNumber) - value = boundPos.posNumVal.doubleValue; - else if (lValueType == MyLayoutValueType_LayoutPos) - { - CGRect rect = boundPos.posRelaVal.view.myFrame.frame; - - MyGravity pos = boundPos.posRelaVal.pos; - if (pos == MyGravity_Horz_Leading) - { - if (rect.origin.x != CGFLOAT_MAX) + } + MyLayoutValType valType = anchor.valType; + if (valType == MyLayoutValType_Number) { + value = anchor.numberVal.doubleValue; + } else if (valType == MyLayoutValType_LayoutPos) { + CGRect rect = anchor.anchorVal.view.myEngine.frame; + MyLayoutAnchorType anchorType = anchor.anchorVal.anchorType; + if (anchorType == MyLayoutAnchorType_Leading) { + if (rect.origin.x != CGFLOAT_MAX) { value = CGRectGetMinX(rect); - } - else if (pos == MyGravity_Horz_Center) - { - if (rect.origin.x != CGFLOAT_MAX) + } + } else if (anchorType == MyLayoutAnchorType_CenterX) { + if (rect.origin.x != CGFLOAT_MAX) { value = CGRectGetMidX(rect); - } - else if (pos == MyGravity_Horz_Trailing) - { - if (rect.origin.x != CGFLOAT_MAX) + } + } else if (anchorType == MyLayoutAnchorType_Trailing) { + if (rect.origin.x != CGFLOAT_MAX) { value = CGRectGetMaxX(rect); - } - else if (pos == MyGravity_Vert_Top) - { - if (rect.origin.y != CGFLOAT_MAX) + } + } else if (anchorType == MyLayoutAnchorType_Top) { + if (rect.origin.y != CGFLOAT_MAX) { value = CGRectGetMinY(rect); - } - else if (pos == MyGravity_Vert_Center) - { - if (rect.origin.y != CGFLOAT_MAX) + } + } else if (anchorType == MyLayoutAnchorType_CenterY) { + if (rect.origin.y != CGFLOAT_MAX) { value = CGRectGetMidY(rect); - } - else if (pos == MyGravity_Vert_Bottom) - { - if (rect.origin.y != CGFLOAT_MAX) + } + } else if (anchorType == MyLayoutAnchorType_Bottom) { + if (rect.origin.y != CGFLOAT_MAX) { value = CGRectGetMaxY(rect); + } } - } - else - { + } else { //约束冲突:无效的边界设置方法 - NSCAssert(0, @"Constraint exception!! %@ has invalid lBound or uBound setting",sbv); + NSCAssert(0, @"Constraint exception!! %@ has invalid lBound or uBound setting", subview); } - - return value + boundPos.offsetVal; + return value + anchor.offsetVal; } - --(CGFloat)myValidMargin:(MyLayoutPos*)pos sbv:(UIView*)sbv calcPos:(CGFloat)calcPos selfLayoutSize:(CGSize)selfLayoutSize -{ - if (pos == nil) +- (CGFloat)myValidMargin:(MyLayoutPos *)anchor subview:(UIView *)subview calcPos:(CGFloat)calcPos selfLayoutSize:(CGSize)selfLayoutSize { + if (anchor == nil) { return calcPos; - + } //算出最大最小值 - CGFloat min = (pos.isActive && pos.lBoundValInner != nil) ? [self myGetBoundLimitMargin:pos.lBoundValInner sbv:sbv selfLayoutSize:selfLayoutSize] : -CGFLOAT_MAX; - CGFloat max = (pos.isActive && pos.uBoundValInner != nil) ? [self myGetBoundLimitMargin:pos.uBoundValInner sbv:sbv selfLayoutSize:selfLayoutSize] : CGFLOAT_MAX; - - calcPos = _myCGFloatMax(min, calcPos); - calcPos = _myCGFloatMin(max, calcPos); + if (anchor.isActive) { + if (anchor.lBoundValInner != nil || anchor.uBoundValInner != nil) { + CGFloat min = (anchor.lBoundValInner != nil) ? [self myGetBound:anchor.lBoundValInner limitMarginOfSubview:subview] : -CGFLOAT_MAX; + CGFloat max = (anchor.uBoundValInner != nil) ? [self myGetBound:anchor.uBoundValInner limitMarginOfSubview:subview] : CGFLOAT_MAX; + + calcPos = _myCGFloatMax(min, calcPos); + calcPos = _myCGFloatMin(max, calcPos); + } + } return calcPos; } --(BOOL)myIsNoLayoutSubview:(UIView*)sbv -{ - UIView *sbvsc = sbv.myCurrentSizeClass; +- (NSArray *)myUpdateCurrentSizeClass:(MySizeClass)sizeClass subviews:(NSArray *)subviews { - if (sbvsc.useFrame) - return YES; + MyLayoutEngine *layoutViewEngine = self.myEngine; + layoutViewEngine.currentSizeClass = (MyViewTraits*)[self fetchLayoutSizeClass:sizeClass]; - if (sbv.isHidden) - { - return sbvsc.myVisibility != MyVisibility_Invisible; + if (subviews == nil) { + subviews = self.subviews; } - else - { - return sbvsc.myVisibility == MyVisibility_Gone; + NSMutableArray *subviewEngines = [NSMutableArray arrayWithCapacity:subviews.count]; + for (UIView *subview in subviews) { + MyLayoutEngine *subviewEngine = subview.myEngine; + subviewEngine.currentSizeClass = (MyViewTraits*)[subview fetchLayoutSizeClass:sizeClass]; + [subviewEngines addObject:subviewEngine]; } + return @[layoutViewEngine, subviewEngines]; } --(NSMutableArray*)myGetLayoutSubviews -{ - return [self myGetLayoutSubviewsFrom:self.subviews]; -} - --(NSMutableArray*)myGetLayoutSubviewsFrom:(NSArray*)sbsFrom -{ - NSMutableArray *sbs = [NSMutableArray arrayWithCapacity:sbsFrom.count]; - BOOL isReverseLayout = self.reverseLayout; +- (CGSize)myAdjustLayoutViewSizeWithContext:(MyLayoutContext *)context { - if (isReverseLayout) - { - [sbs addObjectsFromArray:[sbsFrom reverseObjectEnumerator].allObjects]; - } - else - { - [sbs addObjectsFromArray:sbsFrom]; - - } - - for (NSInteger i = sbs.count - 1; i >=0; i--) - { - UIView *sbv = sbs[i]; - if ([self myIsNoLayoutSubview:sbv]) - { - [sbs removeObjectAtIndex:i]; - } - } + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)context->layoutViewEngine.currentSizeClass; - return sbs; + //调整布局视图自己的尺寸。 + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:context->selfSize.height subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; -} + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:context->selfSize.width subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; --(void)mySetSubviewRelativeDimeSize:(MyLayoutSize*)dime selfSize:(CGSize)selfSize lsc:(MyBaseLayout*)lsc pRect:(CGRect*)pRect -{ - if (dime.dimeRelaVal == nil) - return; - - if (dime.dime == MyGravity_Horz_Fill) - { - - if (dime.dimeRelaVal == lsc.widthSizeInner && !lsc.wrapContentWidth) - pRect->size.width = [dime measureWith:(selfSize.width - lsc.myLayoutLeadingPadding - lsc.myLayoutTrailingPadding)]; - else if (dime.dimeRelaVal == lsc.heightSizeInner) - pRect->size.width = [dime measureWith:(selfSize.height - lsc.myLayoutTopPadding - lsc.myLayoutBottomPadding)]; - else if (dime.dimeRelaVal == dime.view.heightSizeInner) - pRect->size.width = [dime measureWith:pRect->size.height]; - else if (dime.dimeRelaVal.dime == MyGravity_Horz_Fill) - pRect->size.width = [dime measureWith:dime.dimeRelaVal.view.estimatedRect.size.width]; - else - pRect->size.width = [dime measureWith:dime.dimeRelaVal.view.estimatedRect.size.height]; - } - else - { - if (dime.dimeRelaVal == lsc.heightSizeInner && !lsc.wrapContentHeight) - pRect->size.height = [dime measureWith:(selfSize.height - lsc.myLayoutTopPadding - lsc.myLayoutBottomPadding)]; - else if (dime.dimeRelaVal == lsc.widthSizeInner) - pRect->size.height = [dime measureWith:(selfSize.width - lsc.myLayoutLeadingPadding - lsc.myLayoutTrailingPadding)]; - else if (dime.dimeRelaVal == dime.view.widthSizeInner) - pRect->size.height = [dime measureWith:pRect->size.width]; - else if (dime.dimeRelaVal.dime == MyGravity_Horz_Fill) - pRect->size.height = [dime measureWith:dime.dimeRelaVal.view.estimatedRect.size.width]; - else - pRect->size.height = [dime measureWith:dime.dimeRelaVal.view.estimatedRect.size.height]; - } -} - --(CGSize)myAdjustSizeWhenNoSubviews:(CGSize)size sbs:(NSArray *)sbs lsc:(MyBaseLayout*)lsc -{ - //如果没有子视图,并且padding不参与空子视图尺寸计算则尺寸应该扣除padding的值。 - if (sbs.count == 0 && !lsc.zeroPadding) - { - if (lsc.wrapContentWidth) - size.width -= (lsc.myLayoutLeadingPadding + lsc.myLayoutTrailingPadding); - if (lsc.wrapContentHeight) - size.height -= (lsc.myLayoutTopPadding + lsc.myLayoutBottomPadding); - } - - return size; -} + //对所有子视图进行布局变换 + CGAffineTransform layoutTransform = layoutTraits.layoutTransform; + if (!CGAffineTransformIsIdentity(layoutTransform)) { + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { -- (void)myAdjustLayoutSelfSize:(CGSize *)pSelfSize lsc:(MyBaseLayout*)lsc -{ - //调整自己的尺寸。 - pSelfSize->height = [self myValidMeasure:lsc.heightSizeInner sbv:self calcSize:pSelfSize->height sbvSize:*pSelfSize selfLayoutSize:self.superview.bounds.size]; - - pSelfSize->width = [self myValidMeasure:lsc.widthSizeInner sbv:self calcSize:pSelfSize->width sbvSize:*pSelfSize selfLayoutSize:self.superview.bounds.size]; -} + //取子视图中心点坐标。因为这个坐标系的原点是布局视图的左上角,所以要转化为数学坐标系的原点坐标, 才能应用坐标变换。 + CGPoint centerPoint = CGPointMake(subviewEngine.leading + subviewEngine.width / 2 - context->selfSize.width / 2, + subviewEngine.top + subviewEngine.height / 2 - context->selfSize.height / 2); --(void)myAdjustSubviewsRTLPos:(NSArray*)sbs selfWidth:(CGFloat)selfWidth -{ - if ([MyBaseLayout isRTL]) - { - for (UIView *sbv in sbs) - { - MyFrame *myFrame = sbv.myFrame; - - myFrame.leading = selfWidth - myFrame.leading - myFrame.width; - myFrame.trailing = myFrame.leading + myFrame.width; + //应用坐标变换 + centerPoint = CGPointApplyAffineTransform(centerPoint, layoutTransform); + + //还原为左上角坐标系。 + centerPoint.x += context->selfSize.width / 2; + centerPoint.y += context->selfSize.height / 2; + + //根据中心点的变化调整开始和结束位置。 + subviewEngine.leading = centerPoint.x - subviewEngine.width / 2; + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width; + subviewEngine.top = centerPoint.y - subviewEngine.height / 2; + subviewEngine.bottom = subviewEngine.top + subviewEngine.height; } } -} --(MyGravity)myConvertLeftRightGravityToLeadingTrailing:(MyGravity)horzGravity -{ - if (horzGravity == MyGravity_Horz_Left) - { - if ([MyBaseLayout isRTL]) - return MyGravity_Horz_Trailing; - else - return MyGravity_Horz_Leading; + //对所有子视图进行RTL设置 + if ([MyBaseLayout isRTL]) { + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + subviewEngine.leading = context->selfSize.width - subviewEngine.leading - subviewEngine.width; + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width; + } } - else if (horzGravity == MyGravity_Horz_Right) - { - if ([MyBaseLayout isRTL]) - return MyGravity_Horz_Leading; - else - return MyGravity_Horz_Trailing; + + //如果没有子视图,并且padding不参与空子视图尺寸计算则尺寸应该扣除padding的值。 + if (context->subviewEngines.count == 0 && !layoutTraits.zeroPadding) { + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width -= (context->paddingLeading + context->paddingTrailing); + } + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height -= (context->paddingTop + context->paddingBottom); + } } - else - return horzGravity; - + return context->selfSize; } --(UIFont*)myGetSubviewFont:(UIView*)sbv -{ - UIFont *sbvFont = nil; - if ([sbv isKindOfClass:[UILabel class]] || - [sbv isKindOfClass:[UITextField class]] || - [sbv isKindOfClass:[UITextView class]] || - [sbv isKindOfClass:[UIButton class]]) - { - sbvFont = [sbv valueForKey:@"font"]; +- (UIFont *)myGetSubviewFont:(UIView *)subview { + UIFont *subviewFont = nil; + if ([subview isKindOfClass:[UILabel class]] || + [subview isKindOfClass:[UITextField class]] || + [subview isKindOfClass:[UITextView class]] || + [subview isKindOfClass:[UIButton class]]) { + subviewFont = [subview valueForKey:@"font"]; } - - return sbvFont; + return subviewFont; } --(CGFloat)myLayoutTopPadding -{ - return self.myCurrentSizeClass.myLayoutTopPadding; +- (CGFloat)myLayoutPaddingTop { + return self.myCurrentSizeClass.myLayoutPaddingTop; } --(CGFloat)myLayoutBottomPadding -{ - return self.myCurrentSizeClass.myLayoutBottomPadding; +- (CGFloat)myLayoutPaddingBottom { + return self.myCurrentSizeClass.myLayoutPaddingBottom; } --(CGFloat)myLayoutLeftPadding -{ - return self.myCurrentSizeClass.myLayoutLeftPadding; +- (CGFloat)myLayoutPaddingLeft { + return self.myCurrentSizeClass.myLayoutPaddingLeft; } --(CGFloat)myLayoutRightPadding -{ - return self.myCurrentSizeClass.myLayoutRightPadding; +- (CGFloat)myLayoutPaddingRight { + return self.myCurrentSizeClass.myLayoutPaddingRight; } --(CGFloat)myLayoutLeadingPadding -{ - return self.myCurrentSizeClass.myLayoutLeadingPadding; +- (CGFloat)myLayoutPaddingLeading { + return self.myCurrentSizeClass.myLayoutPaddingLeading; } --(CGFloat)myLayoutTrailingPadding -{ - return self.myCurrentSizeClass.myLayoutTrailingPadding; +- (CGFloat)myLayoutPaddingTrailing { + return self.myCurrentSizeClass.myLayoutPaddingTrailing; } +- (void)myLayout:(MyLayoutTraits *)layoutTraits adjustScrollViewContentWithSize:(CGSize)newSize { + if (self.adjustScrollViewContentSizeMode == MyAdjustScrollViewContentSizeModeYes && self.superview != nil && [self.superview isKindOfClass:[UIScrollView class]]) { + UIScrollView *scrollView = (UIScrollView *)self.superview; + CGSize contsize = scrollView.contentSize; + CGRect rectSuper = scrollView.bounds; -- (void)myAlterScrollViewContentSize:(CGSize)newSize lsc:(MyBaseLayout*)lsc -{ - if (self.adjustScrollViewContentSizeMode == MyAdjustScrollViewContentSizeModeYes && self.superview != nil && [self.superview isKindOfClass:[UIScrollView class]]) - { - UIScrollView *scrolv = (UIScrollView*)self.superview; - CGSize contsize = scrolv.contentSize; - CGRect rectSuper = scrolv.bounds; - //这里把自己在父视图中的上下左右边距也算在contentSize的包容范围内。 - CGFloat leadingMargin = [lsc.leadingPosInner realPosIn:rectSuper.size.width]; - CGFloat trailingMargin = [lsc.trailingPosInner realPosIn:rectSuper.size.width]; - CGFloat topMargin = [lsc.topPosInner realPosIn:rectSuper.size.height]; - CGFloat bottomMargin = [lsc.bottomPosInner realPosIn:rectSuper.size.height]; - + CGFloat marginLeading = [layoutTraits.leadingPosInner measureWith:rectSuper.size.width]; + CGFloat marginTrailing = [layoutTraits.trailingPosInner measureWith:rectSuper.size.width]; + CGFloat marginTop = [layoutTraits.topPosInner measureWith:rectSuper.size.height]; + CGFloat marginBottom = [layoutTraits.bottomPosInner measureWith:rectSuper.size.height]; + #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) if (@available(iOS 11.0, *)) { - if (/*scrolv.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAlways*/ 1) - { - if (lsc.leadingPosInner.isSafeAreaPos) - leadingMargin = lsc.leadingPosInner.offsetVal;// + scrolv.safeAreaInsets.left - scrolv.adjustedContentInset.left; - - if (lsc.trailingPosInner.isSafeAreaPos) - trailingMargin = lsc.trailingPosInner.offsetVal;// + scrolv.safeAreaInsets.right - scrolv.adjustedContentInset.right; - - if (lsc.topPosInner.isSafeAreaPos) - topMargin = lsc.topPosInner.offsetVal; - - if (lsc.bottomPosInner.isSafeAreaPos) - bottomMargin = lsc.bottomPosInner.offsetVal; + if (1) { + if (layoutTraits.leadingPosInner.isSafeAreaPos) { + marginLeading = layoutTraits.leadingPosInner.offsetVal; + } + if (layoutTraits.trailingPosInner.isSafeAreaPos) { + marginTrailing = layoutTraits.trailingPosInner.offsetVal; + } + if (layoutTraits.topPosInner.isSafeAreaPos) { + marginTop = layoutTraits.topPosInner.offsetVal; + } + if (layoutTraits.bottomPosInner.isSafeAreaPos) { + marginBottom = layoutTraits.bottomPosInner.offsetVal; + } } } #endif - - - - if (contsize.height != newSize.height + topMargin + bottomMargin) - contsize.height = newSize.height + topMargin + bottomMargin; - if (contsize.width != newSize.width + leadingMargin + trailingMargin) - contsize.width = newSize.width + leadingMargin + trailingMargin; - - //因为调整contentsize可能会调整contentOffset,所以为了保持一致性这里要还原掉原来的contentOffset - CGPoint oldOffset = scrolv.contentOffset; - if (!CGSizeEqualToSize(scrolv.contentSize, contsize)) - scrolv.contentSize = contsize; - - if ((oldOffset.x <= 0 || oldOffset.x <= contsize.width - rectSuper.size.width) && - (oldOffset.y <= 0 || oldOffset.y <= contsize.height - rectSuper.size.height)) - { - if (!CGPointEqualToPoint(scrolv.contentOffset, oldOffset)) - { - scrolv.contentOffset = oldOffset; - } + + if (contsize.height != newSize.height + marginTop + marginBottom) { + contsize.height = newSize.height + marginTop + marginBottom; + } + if (contsize.width != newSize.width + marginLeading + marginTrailing) { + contsize.width = newSize.width + marginLeading + marginTrailing; + } + + contsize.width *= scrollView.zoomScale; + contsize.height *= scrollView.zoomScale; + if (!CGSizeEqualToSize(scrollView.contentSize, contsize)) { + scrollView.contentSize = contsize; } } } MySizeClass _myGlobalSizeClass = 0xFF; +#if TARGET_OS_IOS +- (UIDeviceOrientation)myGetDeviceOrientation { + UIDeviceOrientation devOri = UIDeviceOrientationPortrait; + UIInterfaceOrientation intOri = UIInterfaceOrientationUnknown; + + //先用界面方向,再用设备方向。这样可以处理一些强制屏幕旋转的问题。 +#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 130000) + if (@available(iOS 13.0, *)) { + intOri = self.window.windowScene.interfaceOrientation; + } else +#endif + { //因为App Extension 中不支持 sharedApplication 所以这里要特殊处理。 + BOOL isApp = [UIApplication respondsToSelector:@selector(sharedApplication)]; + if (isApp) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)]; +#pragma clang diagnostic pop + intOri = application.statusBarOrientation; + } + } + + if (intOri != UIInterfaceOrientationUnknown) { + devOri = (UIDeviceOrientation)intOri; + } else { + devOri = [UIDevice currentDevice].orientation; + } + + return devOri; +} +#endif + //获取全局的当前的SizeClass,减少获取次数的调用。 --(MySizeClass)myGetGlobalSizeClass -{ +- (MySizeClass)myGetGlobalSizeClass { //找到最根部的父视图。 - if (_myGlobalSizeClass == 0xFF || ![self.superview isKindOfClass:[MyBaseLayout class]]) - { - MySizeClass sizeClass; - if ([UIDevice currentDevice].systemVersion.floatValue < 8) - sizeClass = MySizeClass_hAny | MySizeClass_wAny; - else - sizeClass = (MySizeClass)((self.traitCollection.verticalSizeClass << 2) | self.traitCollection.horizontalSizeClass); + if (_myGlobalSizeClass == 0xFF || ![self.superview isKindOfClass:[MyBaseLayout class]]) { + MySizeClass sizeClass = (MySizeClass)((self.traitCollection.verticalSizeClass << 2) | self.traitCollection.horizontalSizeClass); #if TARGET_OS_IOS - UIDeviceOrientation ori = [UIDevice currentDevice].orientation; - if (UIDeviceOrientationIsPortrait(ori)) - { + UIDeviceOrientation ori = [self myGetDeviceOrientation]; + if (UIDeviceOrientationIsPortrait(ori)) { sizeClass |= MySizeClass_Portrait; - } - else if (UIDeviceOrientationIsLandscape(ori)) - { + } else if (UIDeviceOrientationIsLandscape(ori)) { sizeClass |= MySizeClass_Landscape; + } else { //如果 ori == UIDeviceOrientationUnknown 的话, 默认给竖屏设置 + //当方向为未知时,尝试得到VC的方向,而不是设置默认方向。 + sizeClass |= MySizeClass_Portrait; } - else; #endif _myGlobalSizeClass = sizeClass; } - else - { - ; - } - return _myGlobalSizeClass; } --(void)myRemoveSubviewObserver:(UIView*)subview -{ - - MyFrame *sbvmyFrame = objc_getAssociatedObject(subview, ASSOCIATEDOBJECT_KEY_MYLAYOUT_FRAME); - if (sbvmyFrame != nil) - { - sbvmyFrame.sizeClass.viewLayoutCompleteBlock = nil; - if (sbvmyFrame.hasObserver) - { - [subview removeObserver:self forKeyPath:@"hidden"]; - [subview removeObserver:self forKeyPath:@"frame"]; - - if ([subview isKindOfClass:[MyBaseLayout class]]) - { - [subview removeObserver:self forKeyPath:@"center"]; - } - else if ([subview isKindOfClass:[UILabel class]]) - { - [subview removeObserver:self forKeyPath:@"text"]; - [subview removeObserver:self forKeyPath:@"attributedText"]; +- (void)myRemoveSubviewObserver:(UIView *)subview { + MyLayoutEngine *subviewEngine = subview.myEngineInner; + if (subviewEngine != nil) { + subviewEngine.currentSizeClass.viewLayoutCompleteBlock = nil; + if (subviewEngine.hasObserver) { + subviewEngine.hasObserver = NO; + if (![subview isKindOfClass:[MyBaseLayout class]]) { + [subview removeObserver:self forKeyPath:@"hidden"]; + [subview removeObserver:self forKeyPath:@"frame"]; + + if ([subview isKindOfClass:[UIScrollView class]]) { + [subview removeObserver:self forKeyPath:@"center"]; + } else if ([subview isKindOfClass:[UILabel class]]) { + [subview removeObserver:self forKeyPath:@"text"]; + [subview removeObserver:self forKeyPath:@"attributedText"]; + } } - else; - - sbvmyFrame.hasObserver = NO; } } } --(void)myAddSubviewObserver:(UIView*)subview sbvmyFrame:(MyFrame*)sbvmyFrame -{ - - if (!sbvmyFrame.hasObserver) - { - //添加hidden, frame,center的属性通知。 +- (void)myAddSubviewObserver:(UIView *)subview { + //非布局子视图添加hidden, frame的属性变化通知。 + if (![subview isKindOfClass:[MyBaseLayout class]]) { [subview addObserver:self forKeyPath:@"hidden" options:NSKeyValueObservingOptionNew context:_myObserverContextA]; [subview addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:_myObserverContextA]; - if ([subview isKindOfClass:[MyBaseLayout class]]) - { + if ([subview isKindOfClass:[UIScrollView class]]) { //如果是UIScrollView则需要特殊监听center属性 + //有时候我们可能会把滚动视图加入到布局视图中去,滚动视图的尺寸有可能设置为高度自适应,这样就会调整center。从而需要重新激发滚动视图的布局 + //这也就是为什么只监听center的原因了。 [subview addObserver:self forKeyPath:@"center" options:NSKeyValueObservingOptionNew context:_myObserverContextA]; - } - else if ([subview isKindOfClass:[UILabel class]]) - {//如果是UILabel则一旦设置了text和attributedText则 - + } else if ([subview isKindOfClass:[UILabel class]]) { //如果是UILabel则监听text和attributedText属性 [subview addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:_myObserverContextB]; [subview addObserver:self forKeyPath:@"attributedText" options:NSKeyValueObservingOptionNew context:_myObserverContextB]; } - else; - - sbvmyFrame.hasObserver = YES; - } } +- (void)myAdjustSizeSettingOfSubviewEngine:(MyLayoutEngine *)subviewEngine withContext:(MyLayoutContext *)context { + + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + UIView *subview = subviewTraits.view; --(void)myAdjustSubviewWrapContentSet:(UIView*)sbv isEstimate:(BOOL)isEstimate sbvmyFrame:(MyFrame*)sbvmyFrame sbvsc:(UIView*)sbvsc selfSize:(CGSize)selfSize sizeClass:(MySizeClass)sizeClass pHasSubLayout:(BOOL*)pHasSubLayout -{ - if (!isEstimate) - { - sbvmyFrame.frame = sbv.bounds; - [self myCalcSizeOfWrapContentSubview:sbv sbvsc:sbvsc sbvmyFrame:sbvmyFrame]; + if (!context->isEstimate) { + subviewEngine.frame = CGRectMake(0, 0, CGRectGetWidth(subview.bounds), CGRectGetHeight(subview.bounds)); } - - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - - if (sbvsc.wrapContentHeight && (sbvsc.heightSizeInner.dimeVal != nil || (sbvsc.topPosInner.posVal != nil && sbvsc.bottomPosInner.posVal != nil))) - { - sbvsc.wrapContentHeight = NO; - } - - if (sbvsc.wrapContentWidth && (sbvsc.widthSizeInner.dimeVal != nil || (sbvsc.leadingPosInner.posVal != nil && sbvsc.trailingPosInner.posVal != nil))) - { - sbvsc.wrapContentWidth = NO; - } - - - if (pHasSubLayout != nil && (sbvsc.wrapContentHeight || sbvsc.wrapContentWidth)) - *pHasSubLayout = YES; + //只要子视图是包裹并且布局视图是fill填充,都应该清除子视图的包裹设置。 + if (subviewTraits.widthSizeInner.wrapVal && context->horzGravity == MyGravity_Horz_Fill) { + [subviewTraits.widthSizeInner _myEqualTo:nil]; + } + if (subviewTraits.heightSizeInner.wrapVal && context->vertGravity == MyGravity_Vert_Fill) { + [subviewTraits.heightSizeInner _myEqualTo:nil]; + } + if (subviewTraits.leadingPosInner.val != nil && + subviewTraits.trailingPosInner.val != nil && + (subviewTraits.widthSizeInner.priority == MyPriority_Low || !layoutTraits.widthSizeInner.wrapVal)) { + [subviewTraits.widthSizeInner _myEqualTo:nil]; + } + if (subviewTraits.topPosInner.val != nil && + subviewTraits.bottomPosInner.val != nil && + (subviewTraits.heightSizeInner.priority == MyPriority_Low || !layoutTraits.heightSizeInner.wrapVal)) { + [subviewTraits.heightSizeInner _myEqualTo:nil]; + } + if ([subview isKindOfClass:[MyBaseLayout class]]) { - if (isEstimate && (sbvsc.wrapContentHeight || sbvsc.wrapContentWidth)) - { - [(MyBaseLayout*)sbv sizeThatFits:sbvmyFrame.frame.size inSizeClass:sizeClass]; - if (sbvmyFrame.multiple) - { - sbvmyFrame.sizeClass = [sbv myBestSizeClass:sizeClass]; //因为estimateLayoutRect执行后会还原,所以这里要重新设置 - sbvsc = sbvmyFrame.sizeClass; - } + if (context->isEstimate && (subviewTraits.heightSizeInner.wrapVal || subviewTraits.widthSizeInner.wrapVal)) { + [(MyBaseLayout *)subview sizeThatFits:subviewEngine.size inSizeClass:context->sizeClass]; } } - } - - --(void)myCalcSubViewRect:(UIView*)sbv - sbvsc:(UIView*)sbvsc - sbvmyFrame:(MyFrame*)sbvmyFrame - lsc:(MyBaseLayout*)lsc - vertGravity:(MyGravity)vertGravity - horzGravity:(MyGravity)horzGravity - inSelfSize:(CGSize)selfSize - paddingTop:(CGFloat)paddingTop - paddingLeading:(CGFloat)paddingLeading - paddingBottom:(CGFloat)paddingBottom - paddingTrailing:(CGFloat)paddingTrailing - pMaxWrapSize:(CGSize*)pMaxWrapSize -{ - +- (CGFloat)myWidthSizeValueOfSubviewEngine:(MyLayoutEngine *)subviewEngine + withContext:(MyLayoutContext *)context { - CGRect rect = sbvmyFrame.frame; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - {//宽度等于固定的值。 - - rect.size.width = sbvsc.widthSizeInner.measure; - } - else if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal.view != sbv) - {//宽度等于其他的依赖的视图。 - - if (sbvsc.widthSizeInner.dimeRelaVal == self.widthSizeInner) - rect.size.width = [sbvsc.widthSizeInner measureWith:selfSize.width - paddingLeading - paddingTrailing]; - else if (sbvsc.widthSizeInner.dimeRelaVal == self.heightSizeInner) - { - rect.size.width = [sbvsc.widthSizeInner measureWith:selfSize.height - paddingTop - paddingBottom]; + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + CGFloat retVal = subviewEngine.width; + MyLayoutSize *sbvWidthSizeInner = subviewTraits.widthSizeInner; + if (sbvWidthSizeInner.numberVal != nil) { //宽度等于固定的值。 + retVal = sbvWidthSizeInner.measure; + } else if (sbvWidthSizeInner.anchorVal != nil && sbvWidthSizeInner.anchorVal.view != subviewTraits.view) { //宽度等于其他的依赖的视图。 + if (sbvWidthSizeInner.anchorVal == layoutTraits.widthSizeInner) { + retVal = [sbvWidthSizeInner measureWith:context->selfSize.width - context->paddingLeading - context->paddingTrailing]; + } else if (sbvWidthSizeInner.anchorVal == layoutTraits.heightSizeInner) { + retVal = [sbvWidthSizeInner measureWith:context->selfSize.height - context->paddingTop - context->paddingBottom]; + } else { + if (sbvWidthSizeInner.anchorVal.anchorType == MyLayoutAnchorType_Width) { + retVal = [sbvWidthSizeInner measureWith:sbvWidthSizeInner.anchorVal.view.myEstimatedWidth]; + } else { + retVal = [sbvWidthSizeInner measureWith:sbvWidthSizeInner.anchorVal.view.myEstimatedHeight]; + } } - else - rect.size.width = [sbvsc.widthSizeInner measureWith:sbvsc.widthSizeInner.dimeRelaVal.view.estimatedRect.size.width]; - } - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - [self myCalcHorzGravity:[self myGetSubviewHorzGravity:sbv sbvsc:sbvsc horzGravity:horzGravity] sbv:sbv sbvsc:sbvsc paddingLeading:paddingLeading paddingTrailing:paddingTrailing selfSize:selfSize pRect:&rect]; - - - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - {//高度等于固定的值。 - rect.size.height = sbvsc.heightSizeInner.measure; - } - else if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal.view != sbv) - {//高度等于其他依赖的视图 - if (sbvsc.heightSizeInner.dimeRelaVal == self.heightSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:selfSize.height - paddingTop - paddingBottom]; - else if (sbvsc.heightSizeInner.dimeRelaVal == self.widthSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:selfSize.width - paddingLeading - paddingTrailing]; - else - rect.size.height = [sbvsc.heightSizeInner measureWith:sbvsc.heightSizeInner.dimeRelaVal.view.estimatedRect.size.height]; - } - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - {//高度等于内容的高度 - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; + } else if (sbvWidthSizeInner.fillVal) { + retVal = [sbvWidthSizeInner measureWith:context->selfSize.width - context->paddingLeading - context->paddingTrailing]; + } else if (sbvWidthSizeInner.wrapVal) { + if (![subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + retVal = [sbvWidthSizeInner measureWith:[subviewTraits.view sizeThatFits:CGSizeMake(0, subviewEngine.height)].width]; + } else if (context->isEstimate) { + MyBaseLayout *sublayout = (MyBaseLayout*)subviewTraits.view; + retVal = [sublayout sizeThatFits:subviewEngine.size inSizeClass:context->sizeClass].width; + retVal = [sbvWidthSizeInner measureWith:retVal]; + } + } + return retVal; +} + +- (CGFloat)myHeightSizeValueOfSubviewEngine:(MyLayoutEngine *)subviewEngine + withContext:(MyLayoutContext *)context { + + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyLayoutSize *subviewHeightSizeInner = subviewTraits.heightSizeInner; + + CGFloat contentWidth = context->selfSize.width - context->paddingLeading - context->paddingTrailing; + CGFloat contentHeight = context->selfSize.height - context->paddingTop - context->paddingBottom; + + CGFloat retVal = subviewEngine.height; + if (subviewHeightSizeInner.numberVal != nil) { //高度等于固定的值。 + retVal = subviewHeightSizeInner.measure; + } else if (subviewHeightSizeInner.anchorVal != nil && subviewHeightSizeInner.anchorVal.view != subviewTraits.view) { //高度等于其他依赖的视图 + if (subviewHeightSizeInner.anchorVal == layoutTraits.heightSizeInner) { + retVal = [subviewHeightSizeInner measureWith:contentHeight]; + } else if (subviewHeightSizeInner.anchorVal == layoutTraits.widthSizeInner) { + retVal = [subviewHeightSizeInner measureWith:contentWidth]; + } else { + if (subviewHeightSizeInner.anchorVal.anchorType == MyLayoutAnchorType_Width) { + retVal = [subviewHeightSizeInner measureWith:subviewHeightSizeInner.anchorVal.view.myEstimatedWidth]; + } else { + retVal = [subviewHeightSizeInner measureWith:subviewHeightSizeInner.anchorVal.view.myEstimatedHeight]; + } + } + } else if (subviewHeightSizeInner.fillVal) { + retVal = [subviewHeightSizeInner measureWith:contentHeight]; + } else if (subviewHeightSizeInner.wrapVal) { + retVal = [self mySubview:subviewTraits wrapHeightSizeFits:subviewEngine.size withContext:context]; } + return retVal; +} + +- (void)myCalcRectOfSubviewEngine:(MyLayoutEngine *)subviewEngine + pMaxWrapSize:(CGSize *)pMaxWrapSize + withContext:(MyLayoutContext *)context { - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - [self myCalcVertGravity:[self myGetSubviewVertGravity:sbv sbvsc:sbvsc vertGravity:vertGravity] sbv:sbv sbvsc:sbvsc paddingTop:paddingTop paddingBottom:paddingBottom baselinePos:CGFLOAT_MAX selfSize:selfSize pRect:&rect]; - + MyLayoutTraits *layoutTraits = (MyLayoutTraits*)context->layoutViewEngine.currentSizeClass; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + [self myCalcSubview:subviewEngine horzGravity:[subviewTraits finalHorzGravityFrom:context->horzGravity] withContext:context]; + + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; + + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + [self myCalcSubview:subviewEngine vertGravity:[subviewTraits finalVertGravityFrom:context->vertGravity] baselinePos:CGFLOAT_MAX withContext:context]; + //特殊处理宽度等于高度 - if (sbvsc.widthSizeInner.dimeRelaVal.view == sbv && sbvsc.widthSizeInner.dimeRelaVal.dime == MyGravity_Vert_Fill) - { - rect.size.width = [sbvsc.widthSizeInner measureWith:rect.size.height]; - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - [self myCalcHorzGravity:[self myGetSubviewHorzGravity:sbv sbvsc:sbvsc horzGravity:horzGravity] sbv:sbv sbvsc:sbvsc paddingLeading:paddingLeading paddingTrailing:paddingTrailing selfSize:selfSize pRect:&rect]; + if (subviewTraits.widthSizeInner.anchorVal.view == subviewTraits.view && subviewTraits.widthSizeInner.anchorVal.anchorType == MyLayoutAnchorType_Height) { + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + [self myCalcSubview:subviewEngine horzGravity:[subviewTraits finalHorzGravityFrom:context->horzGravity] withContext:context]; } - + //特殊处理高度等于宽度。 - if (sbvsc.heightSizeInner.dimeRelaVal.view == sbv && sbvsc.heightSizeInner.dimeRelaVal.dime == MyGravity_Horz_Fill) - { - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width]; - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - { - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; + if (subviewTraits.heightSizeInner.anchorVal.view == subviewTraits.view && subviewTraits.heightSizeInner.anchorVal.anchorType == MyLayoutAnchorType_Width) { + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + + if (subviewTraits.heightSizeInner.wrapVal) { + subviewEngine.height = [self mySubview:subviewTraits wrapHeightSizeFits:subviewEngine.size withContext:context]; } - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - [self myCalcVertGravity:[self myGetSubviewVertGravity:sbv sbvsc:sbvsc vertGravity:vertGravity] sbv:sbv sbvsc:sbvsc paddingTop:paddingTop paddingBottom:paddingBottom baselinePos:CGFLOAT_MAX selfSize:selfSize pRect:&rect]; - + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + [self myCalcSubview:subviewEngine vertGravity:[subviewTraits finalVertGravityFrom:context->vertGravity] baselinePos:CGFLOAT_MAX withContext:context]; } - sbvmyFrame.frame = rect; - - if (pMaxWrapSize != NULL) - { - if (lsc.wrapContentWidth) - { + subviewEngine.bottom = subviewEngine.top + subviewEngine.height; + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width; + + if (pMaxWrapSize != NULL) { + if (layoutTraits.widthSizeInner.wrapVal) { //如果同时设置左右边界则左右边界为最小的宽度 - if (sbvsc.leadingPosInner.posVal != nil && sbvsc.trailingPosInner.posVal != nil) - { - if (_myCGFloatLess(pMaxWrapSize->width, sbvsc.leadingPosInner.absVal + sbvsc.trailingPosInner.absVal + paddingLeading + paddingTrailing)) - pMaxWrapSize->width = sbvsc.leadingPosInner.absVal + sbvsc.trailingPosInner.absVal + paddingLeading + paddingTrailing; + if (subviewTraits.leadingPosInner.val != nil && subviewTraits.trailingPosInner.val != nil) { + if (_myCGFloatLess(pMaxWrapSize->width, subviewTraits.leadingPosInner.measure + subviewTraits.trailingPosInner.measure + context->paddingLeading + context->paddingTrailing)) { + pMaxWrapSize->width = subviewTraits.leadingPosInner.measure + subviewTraits.trailingPosInner.measure + context->paddingLeading + context->paddingTrailing; + } } - - //宽度不依赖布局并且没有同时设置左右边距则参与最大宽度计算。 - if ((sbvsc.widthSizeInner.dimeRelaVal.view != self) && - (sbvsc.leadingPosInner.posVal == nil || sbvsc.trailingPosInner.posVal == nil)) - { - - if (_myCGFloatLess(pMaxWrapSize->width, sbvmyFrame.width + sbvsc.leadingPosInner.absVal + sbvsc.centerXPosInner.absVal + sbvsc.trailingPosInner.absVal + paddingLeading + paddingTrailing)) - pMaxWrapSize->width = sbvmyFrame.width + sbvsc.leadingPosInner.absVal + sbvsc.centerXPosInner.absVal + sbvsc.trailingPosInner.absVal + paddingLeading + paddingTrailing; - - if (_myCGFloatLess(pMaxWrapSize->width,sbvmyFrame.trailing + sbvsc.trailingPosInner.absVal + paddingTrailing)) - pMaxWrapSize->width = sbvmyFrame.trailing + sbvsc.trailingPosInner.absVal + paddingTrailing; - + + //宽度不依赖布局则参与最大宽度计算。 + if ((subviewTraits.widthSizeInner.anchorVal == nil || subviewTraits.widthSizeInner.anchorVal != layoutTraits.widthSizeInner) && !subviewTraits.widthSizeInner.fillVal) { + if (_myCGFloatLess(pMaxWrapSize->width, subviewEngine.width + subviewTraits.leadingPosInner.measure + subviewTraits.centerXPosInner.measure + subviewTraits.trailingPosInner.measure + context->paddingLeading + context->paddingTrailing)) { + pMaxWrapSize->width = subviewEngine.width + subviewTraits.leadingPosInner.measure + subviewTraits.centerXPosInner.measure + subviewTraits.trailingPosInner.measure + context->paddingLeading + context->paddingTrailing; + } + //只有不居中和底部对齐才比较底部。 + if (subviewTraits.centerXPosInner.val == nil && + subviewTraits.trailingPosInner.val == nil && + _myCGFloatLess(pMaxWrapSize->width, subviewEngine.trailing + subviewTraits.trailingPosInner.measure + context->paddingTrailing)) { + pMaxWrapSize->width = subviewEngine.trailing + subviewTraits.trailingPosInner.measure + context->paddingTrailing; + } } } - - if (lsc.wrapContentHeight) - { + + if (layoutTraits.heightSizeInner.wrapVal) { //如果同时设置上下边界则上下边界为最小的高度 - if (sbvsc.topPosInner.posVal != nil && sbvsc.bottomPosInner.posVal != nil) - { - if (_myCGFloatLess(pMaxWrapSize->height, sbvsc.topPosInner.absVal + sbvsc.bottomPosInner.absVal + paddingTop + paddingBottom)) - pMaxWrapSize->height = sbvsc.topPosInner.absVal + sbvsc.bottomPosInner.absVal + paddingTop + paddingBottom; + if (subviewTraits.topPosInner.val != nil && subviewTraits.bottomPosInner.val != nil) { + if (_myCGFloatLess(pMaxWrapSize->height, subviewTraits.topPosInner.measure + subviewTraits.bottomPosInner.measure + context->paddingTop + context->paddingBottom)) { + pMaxWrapSize->height = subviewTraits.topPosInner.measure + subviewTraits.bottomPosInner.measure + context->paddingTop + context->paddingBottom; + } } - - //高度不依赖布局并且没有同时设置上下边距则参与最大高度计算。 - if ((sbvsc.heightSizeInner.dimeRelaVal.view != self) && - (sbvsc.topPosInner.posVal == nil || sbvsc.bottomPosInner.posVal == nil)) - { - if (_myCGFloatLess(pMaxWrapSize->height, sbvmyFrame.height + sbvsc.topPosInner.absVal + sbvsc.centerYPosInner.absVal + sbvsc.bottomPosInner.absVal + paddingTop + paddingBottom)) - pMaxWrapSize->height = sbvmyFrame.height + sbvsc.topPosInner.absVal + sbvsc.centerYPosInner.absVal + sbvsc.bottomPosInner.absVal + paddingTop + paddingBottom; - - if (_myCGFloatLess(pMaxWrapSize->height, sbvmyFrame.bottom + sbvsc.bottomPosInner.absVal + paddingBottom)) - pMaxWrapSize->height = sbvmyFrame.bottom + sbvsc.bottomPosInner.absVal + paddingBottom; + + //高度不依赖布局则参与最大高度计算。 + if ((subviewTraits.heightSizeInner.anchorVal == nil || subviewTraits.heightSizeInner.anchorVal != layoutTraits.heightSizeInner) && !subviewTraits.heightSizeInner.fillVal) { + if (_myCGFloatLess(pMaxWrapSize->height, subviewEngine.height + subviewTraits.topPosInner.measure + subviewTraits.centerYPosInner.measure + subviewTraits.bottomPosInner.measure + context->paddingTop + context->paddingBottom)) { + pMaxWrapSize->height = subviewEngine.height + subviewTraits.topPosInner.measure + subviewTraits.centerYPosInner.measure + subviewTraits.bottomPosInner.measure + context->paddingTop + context->paddingBottom; + } + + //只有在不居中对齐和底部对齐时才比较底部。 + if (subviewTraits.centerYPosInner.val == nil && + subviewTraits.bottomPosInner.val == nil && + _myCGFloatLess(pMaxWrapSize->height, subviewEngine.bottom + subviewTraits.bottomPosInner.measure + context->paddingBottom)) { + pMaxWrapSize->height = subviewEngine.bottom + subviewTraits.bottomPosInner.measure + context->paddingBottom; + } } } } - - } +- (void)myHookSublayout:(MyBaseLayout *)sublayout borderlineRect:(CGRect *)pRect { + //do nothing... +} + +- (void)myInvalidateIntrinsicContentSize { + if (!self.translatesAutoresizingMaskIntoConstraints) { + if (self.widthSizeInner.wrapVal || self.heightSizeInner.wrapVal) { + [self invalidateIntrinsicContentSize]; + } + } +} + +- (void)myCalcSubviewsWrapContentSize:(MyLayoutContext *)context withCustomSetting:(void (^)(MyViewTraits *subviewTraits))customSetting { + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + UIView *subview = subviewTraits.view; + if (customSetting != nil) { + customSetting(subviewTraits); + } + CGSize size = subview.bounds.size; + subviewEngine.width = size.width; + subviewEngine.height = size.height; + } +} + +#pragma mark -- Deprecated methods +// +// +//- (CGFloat)topPadding { +// return self.paddingTop; +//} +// +//- (void)setTopPadding:(CGFloat)topPadding { +// self.paddingTop = topPadding; +//} +// +//- (CGFloat)leadingPadding { +// return self.paddingLeading; +//} +// +//- (void)setLeadingPadding:(CGFloat)leadingPadding { +// self.paddingLeading = leadingPadding; +//} +// +//- (CGFloat)bottomPadding { +// return self.paddingBottom; +//} +// +//- (void)setBottomPadding:(CGFloat)bottomPadding { +// self.paddingBottom = bottomPadding; +//} +// +//- (CGFloat)trailingPadding { +// return self.paddingTrailing; +//} +// +//- (void)setTrailingPadding:(CGFloat)trailingPadding { +// self.paddingTrailing = trailingPadding; +//} +// +//- (CGFloat)leftPadding { +// return self.paddingLeft; +//} +// +//- (void)setLeftPadding:(CGFloat)leftPadding { +// self.paddingLeft = leftPadding; +//} +// +//- (CGFloat)rightPadding { +// return self.paddingRight; +//} +// +//- (void)setRightPadding:(CGFloat)rightPadding { +// self.paddingRight = rightPadding; +//} @end +@implementation MyBaseLayoutOptionalData -@implementation MyFrame +@end --(id)init -{ - self = [super init]; - if (self != nil) - { - _leading = CGFLOAT_MAX; - _trailing = CGFLOAT_MAX; - _top = CGFLOAT_MAX; - _bottom = CGFLOAT_MAX; - _width = CGFLOAT_MAX; - _height = CGFLOAT_MAX; +@implementation UIWindow (MyLayoutExt) + +- (void)myUpdateRTL:(BOOL)isRTL { + BOOL oldRTL = [MyBaseLayout isRTL]; + if (oldRTL != isRTL) { + [MyBaseLayout setIsRTL:isRTL]; + [self mySetNeedLayoutAllSubviews:self]; } - - return self; } --(void)reset -{ - _leading = CGFLOAT_MAX; - _trailing = CGFLOAT_MAX; - _top = CGFLOAT_MAX; - _bottom = CGFLOAT_MAX; - _width = CGFLOAT_MAX; - _height = CGFLOAT_MAX; +- (void)mySetNeedLayoutAllSubviews:(UIView *)view { + NSArray *subviews = view.subviews; + for (UIView *subview in subviews) { + if ([subview isKindOfClass:[MyBaseLayout class]]) { + [subview setNeedsLayout]; + } + [self mySetNeedLayoutAllSubviews:subview]; + } } +@end +#pragma mark-- MyLayoutDragger --(CGRect)frame -{ - return CGRectMake(_leading, _top,_width, _height); -} +@interface MyLayoutDragger() + +@property(nonatomic, assign) CGPoint centerPointOffset; --(void)setFrame:(CGRect)frame -{ - _leading = frame.origin.x; - _top = frame.origin.y; - _width = frame.size.width; - _height = frame.size.height; - _trailing = _leading + _width; - _bottom = _top + _height; +@end + +@implementation MyLayoutDragger + +//开始拖动 +- (void)dragView:(UIView *)view withEvent:(UIEvent *)event { + if (self.layout == nil) { + return; + } + self.oldIndex = [self.layout.subviews indexOfObject:view]; + self.currentIndex = self.oldIndex; + self.hasDragging = NO; + + CGPoint point = [[event touchesForView:view].anyObject locationInView:self.layout]; + CGPoint center = view.center; + self.centerPointOffset = CGPointMake(center.x - point.x, center.y - point.y); } --(BOOL)multiple -{ - return self.sizeClasses.count > 1; +//拖动中,在拖动时要排除移动的子视图序列,动画的时长。返回是否拖动成功。 +- (void)dragginView:(UIView *)view withEvent:(UIEvent *)event { + if (self.layout == nil) { + return; + } + self.hasDragging = YES; + + //取出拖动时当前的位置点。 + CGPoint point = [[event touchesForView:view].anyObject locationInView:self.layout]; + + UIView *hoverView = nil; //保存拖动时手指所在的视图。 + //判断当前手指在具体视图的位置。这里要排除exclusiveViews中指定的所有视图的位置,因为这些视图固定不会调整。 + for (UIView *subview in self.layout.subviews) { + if (subview != view && view.useFrame && ![self.exclusiveViews containsObject:subview]) { + if (CGRectContainsPoint(subview.frame, point)) { + hoverView = subview; + break; + } + } + } + + //如果拖动的view和手指下当前其他的兄弟视图有重合时则意味着需要将当前视图view插入到手指下其他视图所在的位置,并且调整其他视图的位置。 + if (hoverView != nil) { + if (self.animateDuration > 0) { + [self.layout layoutAnimationWithDuration:self.animateDuration]; + } + //得到要移动的视图的位置索引。 + self.currentIndex = [self.layout.subviews indexOfObjectIdenticalTo:hoverView]; + if (self.oldIndex != self.currentIndex) { + self.oldIndex = self.currentIndex; + } else { + if (!self.canHover) { + if (hoverView.center.x > view.center.x) { + self.currentIndex = self.oldIndex + 1; + } + } + } + + for (NSInteger i = self.layout.subviews.count - 1; i > self.currentIndex; i--) { + [self.layout exchangeSubviewAtIndex:i withSubviewAtIndex:i - 1]; + } + + //因为view在bringSubviewToFront后变为了最后一个子视图,因此要调整正确的位置。 + //经过上面的view的位置调整完成后,需要重新激发布局视图的布局,因此这里要设置autoresizesSubviews为YES。 + self.layout.autoresizesSubviews = YES; + view.useFrame = NO; + view.noLayout = YES; + //这里设置为YES表示布局时不会改变view的真实位置而只是在布局视图中占用一个位置和尺寸,正是因为只是占用位置,因此会调整其他视图的位置。 + [self.layout layoutIfNeeded]; + } + //在进行view的位置调整时,要把view移动到最顶端,也就子视图数组的的最后。同时布局视图不能激发子视图布局,因此要把autoresizesSubviews设置为NO,同时因为要自定义view的位置,因此要把useFrame设置为YES,并且恢复noLayout为NO。 + [self.layout bringSubviewToFront:view]; //把拖动的子视图放在最后,这样这个子视图在移动时就会在所有兄弟视图的上面。 + self.layout.autoresizesSubviews = NO; //在拖动时不要让布局视图激发布局 + view.useFrame = YES; //因为拖动时,拖动的控件需要自己确定位置,不能被布局约束,因此必须要将useFrame设置为YES下面的center设置才会有效。 + view.center = CGPointMake(point.x + self.centerPointOffset.x, point.y + self.centerPointOffset.y); //因为useFrame设置为了YES所有这里可以直接调整center,从而实现了位置的自定义设置。 + view.noLayout = NO; //恢复noLayout为NO。 } --(NSString*)description -{ - return [NSString stringWithFormat:@"leading:%g, top:%g, width:%g, height:%g, trailing:%g, bottom:%g",_leading,_top,_width,_height,_trailing,_bottom]; +//结束拖动 +- (void)dropView:(UIView *)view withEvent:(UIEvent *)event { + if (self.layout == nil) { + return; + } + if (!self.hasDragging) { + return; + } + self.hasDragging = NO; + + //当抬起时,需要让拖动的子视图调整到正确的顺序,并重新参与布局,因此这里要把拖动的子视图的useFrame设置为NO,同时把布局视图的autoresizesSubviews还原为YES。 + for (NSInteger i = self.layout.subviews.count - 1; i > self.currentIndex; i--) { + [self.layout exchangeSubviewAtIndex:i withSubviewAtIndex:i - 1]; + } + + view.useFrame = NO; //让拖动的子视图重新参与布局,将useFrame设置为NO + self.layout.autoresizesSubviews = YES; //让布局视图可以重新激发布局,这里还原为YES。 } +- (BOOL)isHovering { + return self.hasDragging && self.canHover && self.oldIndex == self.currentIndex; +} @end - diff --git a/MyLayout/Lib/MyBorderline.h b/MyLayout/Lib/MyBorderline.h index db04365..886fe03 100644 --- a/MyLayout/Lib/MyBorderline.h +++ b/MyLayout/Lib/MyBorderline.h @@ -5,60 +5,50 @@ // Created by oubaiquan on 2017/8/23. // Copyright © 2017年 YoungSoft. All rights reserved. // -#import #import "MyLayoutDef.h" - +#import /** *布局的边界画线类,用于实现绘制布局的四周的边界线的功能。一个布局视图中提供了上下左右4个方向的边界画线类对象。 */ @interface MyBorderline : NSObject - /** *边界线的颜色 */ -@property(nonatomic,strong) UIColor *color; +@property (nonatomic, strong) UIColor *color; /** *边界线的厚度,默认是1,设置的值不能小于1. 单位是像素。 */ -@property(nonatomic,assign) CGFloat thick; +@property (nonatomic, assign) CGFloat thick; /** *边界线的头部缩进单位。比如某个布局视图宽度是100,头部缩进单位是20,那么边界线将从20的位置开始绘制。 */ -@property(nonatomic,assign) CGFloat headIndent; +@property (nonatomic, assign) CGFloat headIndent; /** *边界线的尾部缩进单位。比如某个布局视图宽度是100,尾部缩进单位是20,那么边界线将绘制到80的位置结束。 */ -@property(nonatomic, assign) CGFloat tailIndent; +@property (nonatomic, assign) CGFloat tailIndent; /** *设置边界线为点划线,如果是0则边界线是实线 */ -@property(nonatomic, assign) CGFloat dash; +@property (nonatomic, assign) CGFloat dash; /** *边界线绘制时的偏移量 */ -@property(nonatomic, assign) CGFloat offset; - +@property (nonatomic, assign) CGFloat offset; +- (instancetype)initWithColor:(UIColor *)color; --(instancetype)initWithColor:(UIColor*)color; +- (instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick; --(instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick; - --(instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick headIndent:(CGFloat)headIndent tailIndent:(CGFloat)tailIndent; - --(instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick headIndent:(CGFloat)headIndent tailIndent:(CGFloat)tailIndent offset:(CGFloat)offset; +- (instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick headIndent:(CGFloat)headIndent tailIndent:(CGFloat)tailIndent; +- (instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick headIndent:(CGFloat)headIndent tailIndent:(CGFloat)tailIndent offset:(CGFloat)offset; @end - -//兼容老版本的类名命名不规则的问题,这样老版本仍然可以用MyBorderLineDraw这个类名。 -typedef MyBorderline MyBorderLineDraw MYMETHODDEPRECATED("use MyBorderline to instead"); - - diff --git a/MyLayout/Lib/MyBorderline.m b/MyLayout/Lib/MyBorderline.m index 3bdb5d1..a5a3abc 100644 --- a/MyLayout/Lib/MyBorderline.m +++ b/MyLayout/Lib/MyBorderline.m @@ -10,82 +10,59 @@ @implementation MyBorderline - --(instancetype)init -{ +- (instancetype)init { self = [super init]; - if (self != nil) - { + if (self != nil) { _color = [UIColor blackColor]; _thick = 1; _headIndent = 0; _tailIndent = 0; - _dash = 0; + _dash = 0; _offset = 0; } - return self; } --(instancetype)initWithColor:(UIColor *)color -{ +- (instancetype)initWithColor:(UIColor *)color { self = [self init]; - if (self != nil) - { + if (self != nil) { _color = color; } - return self; } --(instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick -{ +- (instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick { self = [self initWithColor:color]; - if (self != nil) - { + if (self != nil) { self.thick = thick; } - return self; } --(instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick headIndent:(CGFloat)headIndent tailIndent:(CGFloat)tailIndent -{ +- (instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick headIndent:(CGFloat)headIndent tailIndent:(CGFloat)tailIndent { self = [self initWithColor:color thick:thick]; - if (self != nil) - { + if (self != nil) { _headIndent = headIndent; _tailIndent = tailIndent; } - return self; - } --(instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick headIndent:(CGFloat)headIndent tailIndent:(CGFloat)tailIndent offset:(CGFloat)offset -{ +- (instancetype)initWithColor:(UIColor *)color thick:(CGFloat)thick headIndent:(CGFloat)headIndent tailIndent:(CGFloat)tailIndent offset:(CGFloat)offset { self = [self initWithColor:color thick:thick headIndent:headIndent tailIndent:tailIndent]; - if (self != nil) - { + if (self != nil) { _offset = offset; } - return self; } --(void)setThick:(CGFloat)thick -{ - if (thick < 1) +- (void)setThick:(CGFloat)thick { + if (thick < 1) { thick = 1; - if (_thick != thick) - { + } + if (_thick != thick) { _thick = thick; } } @end - - - - - diff --git a/MyLayout/Lib/MyDimeScale.h b/MyLayout/Lib/MyDimeScale.h index daadd56..1e1063a 100644 --- a/MyLayout/Lib/MyDimeScale.h +++ b/MyLayout/Lib/MyDimeScale.h @@ -12,8 +12,6 @@ #import - - /** *一个用于计算位置和尺寸在不同设备屏幕下缩放比例的辅助类,用于实现不同大小设备屏幕之间的位置和尺寸的适配。 *比如某个视图的宽度在iPhone6下是100,那么在iPhone6+上大概应该是110,而在iPhone5下则大概应该是85。 @@ -23,58 +21,54 @@ /** *指定UI设计原型图所用的设备尺寸。请在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法的开始处调用这个方法,比如当UI设计人员用iPhone6作为界面的原型尺寸则将size设置为375,667。 */ -+(void)setUITemplateSize:(CGSize)size; ++ (void)setUITemplateSize:(CGSize)size; /** *返回屏幕尺寸缩放的比例 */ -+(CGFloat)scale:(CGFloat)val; ++ (CGFloat)scale:(CGFloat)val; /** *返回屏幕宽度缩放的比例 */ -+(CGFloat)scaleW:(CGFloat)val; ++ (CGFloat)scaleW:(CGFloat)val; /** *返回屏幕高度缩放的比例 */ -+(CGFloat)scaleH:(CGFloat)val; ++ (CGFloat)scaleH:(CGFloat)val; /** *根据屏幕清晰度将带小数的入参返回能转化为有效物理像素的最接近的设备点值。 *比如当入参为1.3时,那么在1倍屏幕下的有效值就是1,而在2倍屏幕下的有效值就是1.5,而在3倍屏幕下的有效值就是1.3333333了 */ -+(CGFloat)roundNumber:(CGFloat)number; ++ (CGFloat)roundNumber:(CGFloat)number; /** *根据屏幕清晰度将带小数的point入参返回能转化为有效物理像素的最接近的设备point点值。 */ -+(CGPoint)roundPoint:(CGPoint)point; - ++ (CGPoint)roundPoint:(CGPoint)point; /** *根据屏幕清晰度将带小数的size入参返回能转化为有效物理像素的最接近的设备size点值。 */ -+(CGSize)roundSize:(CGSize)size; ++ (CGSize)roundSize:(CGSize)size; /** *根据屏幕清晰度将带小数的rect入参返回能转化为有效物理像素的最接近的设备rect点值。 */ -+(CGRect)roundRect:(CGRect)rect; - - ++ (CGRect)roundRect:(CGRect)rect; @end -#define MYDIMESCALE(val) ([MyDimeScale scale:val]) -#define MYDIMESCALEW(val) ([MyDimeScale scaleW:val]) -#define MYDIMESCALEH(val) ([MyDimeScale scaleH:val]) +#define MYDIMESCALE(val) ([MyDimeScale scale:val]) +#define MYDIMESCALEW(val) ([MyDimeScale scaleW:val]) +#define MYDIMESCALEH(val) ([MyDimeScale scaleH:val]) #elif TARGET_OS_MAC -#define MYDIMESCALE(val) val -#define MYDIMESCALEW(val) val -#define MYDIMESCALEH(val) val - +#define MYDIMESCALE(val) val +#define MYDIMESCALEW(val) val +#define MYDIMESCALEH(val) val #endif diff --git a/MyLayout/Lib/MyDimeScale.m b/MyLayout/Lib/MyDimeScale.m index 4c708f1..baa2df3 100644 --- a/MyLayout/Lib/MyDimeScale.m +++ b/MyLayout/Lib/MyDimeScale.m @@ -15,61 +15,47 @@ extern CGSize _myCGSizeRound(CGSize); extern CGRect _myCGRectRound(CGRect); - @implementation MyDimeScale CGFloat _rate = 1; CGFloat _wrate = 1; CGFloat _hrate = 1; - -+(void)setUITemplateSize:(CGSize)size -{ - CGSize screenSize = [UIScreen mainScreen].bounds.size; - ++ (void)setUITemplateSize:(CGSize)size { + CGSize screenSize = [UIScreen mainScreen].fixedCoordinateSpace.bounds.size; _wrate = screenSize.width / size.width; _hrate = screenSize.height / size.height; _rate = sqrt((screenSize.width * screenSize.width + screenSize.height * screenSize.height) / (size.width * size.width + size.height * size.height)); } -+(CGFloat)scale:(CGFloat)val -{ ++ (CGFloat)scale:(CGFloat)val { return _myCGFloatRound(val * _rate); } -+(CGFloat)scaleW:(CGFloat)val -{ ++ (CGFloat)scaleW:(CGFloat)val { return _myCGFloatRound(val * _wrate); } -+(CGFloat)scaleH:(CGFloat)val -{ ++ (CGFloat)scaleH:(CGFloat)val { return _myCGFloatRound(val * _hrate); } -+(CGFloat)roundNumber:(CGFloat)number -{ ++ (CGFloat)roundNumber:(CGFloat)number { return _myCGFloatRound(number); } -+(CGPoint)roundPoint:(CGPoint)point -{ ++ (CGPoint)roundPoint:(CGPoint)point { return _myCGPointRound(point); } -+(CGSize)roundSize:(CGSize)size -{ ++ (CGSize)roundSize:(CGSize)size { return _myCGSizeRound(size); } -+(CGRect)roundRect:(CGRect)rect -{ ++ (CGRect)roundRect:(CGRect)rect { return _myCGRectRound(rect); } - - - @end #endif diff --git a/MyLayout/Lib/MyFlexLayout.h b/MyLayout/Lib/MyFlexLayout.h new file mode 100644 index 0000000..eb28ac1 --- /dev/null +++ b/MyLayout/Lib/MyFlexLayout.h @@ -0,0 +1,267 @@ +// +// MyFlexLayout.h +// MyLayout +// +// Created by oubaiquan on 2019/8/30. +// Copyright © 2019 YoungSoft. All rights reserved. +// + +#import "MyFlowLayout.h" + +/**定义flex的方向类型*/ +typedef enum : int { + /**横向从左到右排列(左对齐),默认的排列方式*/ + MyFlexDirection_Row, + /**纵向排列*/ + MyFlexDirection_Column, + /**反转横向排列(右对齐,从后往前排,最后一项排在最前面*/ + MyFlexDirection_Row_Reverse, + /**反转纵向排列,从后往前排,最后一项排在最上面*/ + MyFlexDirection_Column_Reverse +} MyFlexDirection; + +/**定义flex的换行类型*/ +typedef enum : int { + /**当子元素溢出父容器时不换行*/ + MyFlexWrap_NoWrap = 0, + /**当子元素溢出父容器时自动换行*/ + MyFlexWrap_Wrap = 4, + /**反转 wrap 排列*/ + MyFlexWrap_Wrap_Reverse = 12 +} MyFlexWrap; + +/**定义弹性盒以及条目的停靠对齐类型*/ +typedef enum : int { + /**开始位置对齐或停靠*/ + MyFlexGravity_Flex_Start, + /**结束位置对齐或停靠*/ + MyFlexGravity_Flex_End, + /**居中位置对齐或停靠*/ + MyFlexGravity_Center, + /**拉伸行或者列之间的间距*/ + MyFlexGravity_Space_Between, + /**拉伸行或者列之间的间距,头尾间距是其他间距的一半*/ + MyFlexGravity_Space_Around, + /**基线对齐*/ + MyFlexGravity_Baseline, + /**拉伸条目尺寸*/ + MyFlexGravity_Stretch +} MyFlexGravity; + +/**默认自动值*/ +extern const int MyFlex_Auto; + +/** + 条目视图属性 + */ +@protocol MyFlexItemAttrs + +@property (nonatomic, assign) NSInteger order; +@property (nonatomic, assign) CGFloat flex_grow; +@property (nonatomic, assign) CGFloat flex_shrink; +@property (nonatomic, assign) CGFloat flex_basis; +@property (nonatomic, assign) MyFlexGravity align_self; +@property (nonatomic, assign) CGFloat width; +@property (nonatomic, assign) CGFloat height; + +@end + +/** + 布局视图属性 + */ +@protocol MyFlexBoxAttrs + +@property (nonatomic, assign) MyFlexDirection flex_direction; +@property (nonatomic, assign) MyFlexWrap flex_wrap; +@property (nonatomic, assign) MyFlexGravity justify_content; +@property (nonatomic, assign) MyFlexGravity align_items; +@property (nonatomic, assign) MyFlexGravity align_content; +@property (nonatomic, assign) NSInteger item_size; +@property (nonatomic, assign) UIEdgeInsets padding; +@property (nonatomic, assign) CGFloat vert_space; +@property (nonatomic, assign) CGFloat horz_space; + +@end + +#define MyFlexNew(cls, var) (var = cls.new).myFlex + +@protocol MyFlexItem + +@property (nonatomic, strong, readonly) id attrs; +@property (nonatomic, weak, readonly) __kindof UIView *view; + +/** + 视图的宽度设置,如果宽度设置为大于0小于1则表明是相对于父视图宽度的比重值,如果是MyLayoutSize.wrap则表明宽度自适应,如果是MyLayoutSize.fill则表明宽度和父视图相等,如果是MyLayoutSize.empty则表明不设置宽度值。 其他的值就是一个固定宽度值。 + */ +- (id (^)(CGFloat))width; + +/** + 视图的宽度设置,percent表明占用父视图宽度的百分比值,inc表明在百分比值的基础上的增量值。 + */ +- (id (^)(CGFloat percent, CGFloat inc))width_percent; + +/** + 最小宽度限制设置 + */ +- (id (^)(CGFloat))min_width; + +/** + 最大宽度限制设置 + */ +- (id (^)(CGFloat))max_width; +/** + 视图的高度设置,如果高度设置为大于0小于1则表明是相对于父视图高度的比重值,如果是MyLayoutSize.wrap则表明高度自适应,如果是MyLayoutSize.fill则表明高度和父视图相等,如果是MyLayoutSize.empty则表明不设置高度值,其他的值就是一个固定高度值。 + */ +- (id (^)(CGFloat))height; + +/** + 视图的高度设置,percent表明占用父视图高度的百分比值,inc表明在百分比值的基础上的增量值。 + */ +- (id (^)(CGFloat percent, CGFloat inc))height_percent; + +/** + 最小高度限制设置 + */ +- (id (^)(CGFloat))min_height; + +/** + 最大高度限制设置 + */ +- (id (^)(CGFloat))max_height; + +//视图的外间距设置。 +/** + 视图的顶部外间距设置 + */ +- (id (^)(CGFloat))margin_top; +/** + 视图的底部外间距设置 + */ +- (id (^)(CGFloat))margin_bottom; +/** + 视图的左边外间距设置 + */ +- (id (^)(CGFloat))margin_left; +/** + 视图的右边外间距设置 + */ +- (id (^)(CGFloat))margin_right; +/** + 视图的四周外间距设置 + */ +- (id (^)(CGFloat))margin; +/** + 视图的可视设置 + */ +- (id (^)(MyVisibility))visibility; + +//添加到父视图中 +- (__kindof UIView * (^)(UIView *))addTo; + +//添加子视图 +- (id (^)(UIView *))add; + +//添加条目 +- (id (^)(id))addItem; + +/** + 条目在弹盒中的排列顺序,值越大越往后排。 + */ +- (id (^)(NSInteger))order; +/** + 设置或检索弹性盒的扩展比率。默认值为0表示不扩展 + */ +- (id (^)(CGFloat))flex_grow; +/** + 设置或检索弹性盒的收缩比率。默认值为1表示当条目尺寸超过弹性盒尺寸后会进行压缩。值越大压缩比越大 + */ +- (id (^)(CGFloat))flex_shrink; +/** + 设置或检索弹性盒伸缩基准值。默认值为MyFlex_Auto表示由其他属性决定,如果值为大于0小于1则表示相对值,其他为一个固定的尺寸值。 + */ +- (id (^)(CGFloat))flex_basis; +/** + 设置或检索弹性盒子元素自身在侧轴(纵轴)方向上的对齐方式。可选值为:MyFlexGravity_Flex_Start | MyFlexGravity_Flex_End | MyFlexGravity_Center | MyFlexGravity_Baseline | MyFlexGravity_Stretch中的一个,默认值为MyFlex_Auto + */ +- (id (^)(MyFlexGravity))align_self; + +@end + +@protocol MyFlexBox + +@property (nonatomic, strong) id attrs; + +/** + 设置或检索伸缩盒对象的子元素在父容器中的位置。默认值:MyFlexDirection_Row + */ +- (id (^)(MyFlexDirection))flex_direction; +/** + 设置或检索伸缩盒对象的子元素超出父容器时是否换行。默认值:MyFlexWrap_NoWrap + */ +- (id (^)(MyFlexWrap))flex_wrap; +/** + 同时设置检索伸缩盒对象的子元素在父容器中的位置和伸缩盒对象的子元素超出父容器时是否换行。二者通过 | 运算进行组合 + */ +- (id (^)(int))flex_flow; +/** + 设置或检索弹性盒子元素在主轴(横轴)方向上的对齐方式。可选值为:MyFlexGravity_Flex_Start | MyFlexGravity_Flex_End | MyFlexGravity_Center | MyFlexGravity_Space_Between | MyFlexGravity_Space_Around 中的一个,默认值为MyFlexGravity_Flex_Start + */ +- (id (^)(MyFlexGravity))justify_content; +/** + 设置或检索弹性盒子元素在侧轴(纵轴)方向上的对齐方式。可选值为:MyFlexGravity_Flex_Start | MyFlexGravity_Flex_End | MyFlexGravity_Center | MyFlexGravity_Baseline | MyFlexGravity_Stretch中的一个,默认值为MyFlexGravity_Flex_Start + */ +- (id (^)(MyFlexGravity))align_items; +/** + 设置或检索弹性盒堆叠伸缩行的对齐方式。可选值为:MyFlexGravity_Flex_Start | MyFlexGravity_Flex_End | MyFlexGravity_Center | MyFlexGravity_Between | MyFlexGravity_Around | MyFlexGravity_Stretch中的一个,默认值为MyFlexGravity_Stretch + */ +- (id (^)(MyFlexGravity))align_content; + +/** + 指定主轴的子条目的数量。只有在flex_wrap设置为wrap时才有效。默认值是0表示会根据条目的尺寸自动进行换行。 + */ +- (id (^)(NSInteger))item_size; +/** + 指定布局视图中每页的条目数量。这个值必须是item_size的倍数。 + */ +- (id (^)(NSInteger))page_size; +/** + 指定布局会根据条目的尺寸自动排列,默认值是NO。 + */ +- (id (^)(BOOL))auto_arrange; + +/** + 设置弹性盒的内边距 + */ +- (id (^)(UIEdgeInsets))padding; +/** + 设置弹性盒内所有条目视图之间的垂直间距 + */ +- (id (^)(CGFloat))vert_space; +/** + 设置弹性盒内所有条目视图之间的水平间距 + */ +- (id (^)(CGFloat))horz_space; + +@end + +@interface UIView (MyFlexLayout) + +/** + 用于弹盒视图中的子视图的布局设置。 + */ +@property (nonatomic, strong, readonly) id myFlex; + +@end + +/* + * 弹性布局是为了兼容flexbox语法而建立了一个布局,它是从MyFlowLayout派生。在MyFlowLayout中也是支持类似flexbox的一些特性的 + * 因为它的属性和flexbox不兼容,所以提供一个新的类MyFlexLayout来完全支持flexbox. + */ +@interface MyFlexLayout : MyFlowLayout + +/** + 用于弹盒布局视图自身的布局设置 + */ +@property (nonatomic, strong, readonly) id myFlex; + +@end diff --git a/MyLayout/Lib/MyFlexLayout.m b/MyLayout/Lib/MyFlexLayout.m new file mode 100644 index 0000000..32cb933 --- /dev/null +++ b/MyLayout/Lib/MyFlexLayout.m @@ -0,0 +1,797 @@ +// +// MyFlexLayout.m +// MyLayout +// +// Created by oubaiquan on 2019/8/30. +// Copyright © 2019 YoungSoft. All rights reserved. +// + +#import "MyFlexLayout.h" +#import "MyLayoutInner.h" +#import + +const char *const ASSOCIATEDOBJECT_KEY_MYLAYOUT_FLEXITEM = "ASSOCIATEDOBJECT_KEY_MYLAYOUT_FLEXITEM"; +const int MyFlex_Auto = -1; + +#pragma mark-- MyFlexItemAttrs + +@interface MyFlexItemAttrs : NSObject +@property (nonatomic, weak) UIView *view; +@property (nonatomic, assign) NSInteger order; +@property (nonatomic, assign) CGFloat flex_grow; +@property (nonatomic, assign) CGFloat flex_shrink; +@property (nonatomic, assign) CGFloat flex_basis; +@property (nonatomic, assign) MyFlexGravity align_self; +@end + +@implementation MyFlexItemAttrs + +- (instancetype)init { + self = [super init]; + if (self != nil) { + _order = 0; + _flex_grow = 0; + _flex_shrink = 1; + _flex_basis = MyFlex_Auto; + _align_self = MyFlex_Auto; + } + return self; +} + +- (void)setOrder:(NSInteger)order { + if (_order != order) { + _order = order; + [self.view.superview setNeedsLayout]; + } +} + +- (void)setFlex_grow:(CGFloat)flex_grow { + if (_flex_grow != flex_grow) { + _flex_grow = flex_grow; + [self.view.superview setNeedsLayout]; + } +} + +- (void)setFlex_shrink:(CGFloat)flex_shrink { + if (_flex_shrink != flex_shrink) { + _flex_shrink = flex_shrink; + [self.view.superview setNeedsLayout]; + } +} + +- (void)setFlex_basis:(CGFloat)flex_basis { + if (_flex_basis != flex_basis) { + _flex_basis = flex_basis; + [self.view.superview setNeedsLayout]; + } +} + +- (void)setAlign_self:(MyFlexGravity)align_self { + if (_align_self != align_self) { + _align_self = align_self; + [self.view.superview setNeedsLayout]; + } +} + +- (CGFloat)width { + if (self.view.widthSizeInner.isWrap) { + return MyLayoutSize.wrap; + } else if (self.view.widthSizeInner.isFill) { + return self.view.widthSizeInner.multiVal == 1 ? MyLayoutSize.fill : self.view.widthSizeInner.multiVal; + } else if (self.view.widthSizeInner != nil && self.view.widthSizeInner.valType == MyLayoutValType_Nil) { + return MyLayoutSize.empty; + } else if (self.view.widthSizeInner.numberVal != nil) { + return self.view.widthSizeInner.numberVal.doubleValue; + } else { + return MyLayoutSize.wrap; + } +} + +- (void)setWidth:(CGFloat)width { + if (width > 0 && width < 1) { + self.view.widthSize.equalTo(@(MyLayoutSize.fill)).multiply(width); + } else { + self.view.widthSize.equalTo(@(width)); + } +} + +- (CGFloat)height { + if (self.view.heightSizeInner.isWrap) { + return MyLayoutSize.wrap; + } else if (self.view.heightSizeInner.isFill) { + return self.view.heightSizeInner.multiVal == 1 ? MyLayoutSize.fill : self.view.heightSizeInner.multiVal; + } else if (self.view.heightSizeInner != nil && self.view.heightSizeInner.valType == MyLayoutValType_Nil) { + return MyLayoutSize.empty; + } else if (self.view.heightSizeInner.numberVal != nil) { + return self.view.heightSizeInner.numberVal.doubleValue; + } else { + return MyLayoutSize.wrap; + } +} + +- (void)setHeight:(CGFloat)height { + if (height > 0 && height < 1) { + self.view.heightSize.equalTo(@(MyLayoutSize.fill)).multiply(height); + } else { + self.view.heightSize.equalTo(@(height)); + } +} + +@end + +#pragma mark-- MyFlexBoxAttrs + +@interface MyFlexBoxAttrs : MyFlexItemAttrs +@property (nonatomic, assign) MyFlexDirection flex_direction; +@property (nonatomic, assign) MyFlexWrap flex_wrap; +@property (nonatomic, assign) MyFlexGravity justify_content; +@property (nonatomic, assign) MyFlexGravity align_items; +@property (nonatomic, assign) MyFlexGravity align_content; +@property (nonatomic, assign) NSInteger item_size; +@property (nonatomic, assign) UIEdgeInsets padding; +@property (nonatomic, assign) CGFloat vert_space; +@property (nonatomic, assign) CGFloat horz_space; +@end + +@implementation MyFlexBoxAttrs + +- (instancetype)init { + self = [super init]; + if (self != nil) { + _flex_direction = MyFlexDirection_Row; + _flex_wrap = MyFlexWrap_NoWrap; + _justify_content = MyFlexGravity_Flex_Start; + _align_items = MyFlexGravity_Stretch; + _align_content = MyFlexGravity_Stretch; + _item_size = 0; + } + return self; +} + +- (void)setFlex_direction:(MyFlexDirection)flex_direction { + if (_flex_direction != flex_direction) { + _flex_direction = flex_direction; + [self.view setNeedsLayout]; + } +} + +- (void)setFlex_wrap:(MyFlexWrap)flex_wrap { + if (_flex_wrap != flex_wrap) { + _flex_wrap = flex_wrap; + [self.view setNeedsLayout]; + } +} + +- (void)setJustify_content:(MyFlexGravity)justify_content { + if (_justify_content != justify_content) { + _justify_content = justify_content; + [self.view setNeedsLayout]; + } +} + +- (void)setAlign_items:(MyFlexGravity)align_items { + if (_align_items != align_items) { + _align_items = align_items; + [self.view setNeedsLayout]; + } +} + +- (void)setAlign_content:(MyFlexGravity)align_content { + if (_align_content != align_content) { + _align_content = align_content; + [self.view setNeedsLayout]; + } +} + +- (void)setItem_size:(NSInteger)item_size { + if (_item_size != item_size) { + _item_size = item_size; + [self.view setNeedsLayout]; + } +} + +- (UIEdgeInsets)padding { + return ((MyFlexLayout *)self.view).padding; +} + +- (void)setPadding:(UIEdgeInsets)padding { + ((MyFlexLayout *)self.view).padding = padding; +} + +- (CGFloat)vert_space { + return ((MyFlexLayout *)self.view).subviewVSpace; +} + +- (void)setVert_space:(CGFloat)vert_space { + ((MyFlexLayout *)self.view).subviewVSpace = vert_space; +} + +- (CGFloat)horz_space { + return ((MyFlexLayout *)self.view).subviewHSpace; +} + +- (void)setHorz_space:(CGFloat)horz_space { + ((MyFlexLayout *)self.view).subviewHSpace = horz_space; +} + +@end + +#pragma mark-- MyFlexItem +@interface MyFlexItem : NSObject +@property (nonatomic, weak, readonly) UIView *view; +@end + +@implementation MyFlexItem { + @package + id _attrs; +} +@dynamic attrs; + +- (instancetype)initWithView:(UIView *)view attrs:(id)attrs { + self = [super init]; + if (self != nil) { + _attrs = attrs; + _view = view; + } + return self; +} + +- (id)attrs { + return _attrs; +} + +- (id (^)(CGFloat))width { + return ^id(CGFloat val) { + if (val > 0 && val < 1) { + self.view.widthSize.equalTo(@(MyLayoutSize.fill)).multiply(val); + } else { + self.view.widthSize.equalTo(@(val)); + } + return self; + }; +} + +- (id (^)(CGFloat percent, CGFloat inc))width_percent { + return ^id(CGFloat percent, CGFloat inc) { + self.view.widthSize.equalTo(@(MyLayoutSize.fill)).multiply(percent).add(inc); + return self; + }; +} + +- (id (^)(CGFloat))min_width { + return ^id(CGFloat val) { + self.view.widthSize.min(val); + return self; + }; +} + +- (id (^)(CGFloat))max_width { + return ^id(CGFloat val) { + self.view.widthSize.max(val); + return self; + }; +} + +- (id (^)(CGFloat))height { + return ^id(CGFloat val) { + if (val > 0 && val < 1) { + self.view.heightSize.equalTo(@(MyLayoutSize.fill)).multiply(val); + } else { + self.view.heightSize.equalTo(@(val)); + } + return self; + }; +} + +- (id (^)(CGFloat percent, CGFloat inc))height_percent { + return ^id(CGFloat percent, CGFloat inc) { + self.view.heightSize.equalTo(@(MyLayoutSize.fill)).multiply(percent).add(inc); + return self; + }; +} + +- (id (^)(CGFloat))min_height { + return ^id(CGFloat val) { + self.view.heightSize.min(val); + return self; + }; +} + +- (id (^)(CGFloat))max_height { + return ^id(CGFloat val) { + self.view.heightSize.max(val); + return self; + }; +} + +- (id (^)(CGFloat))margin_top { + return ^id(CGFloat val) { + self.view.myTop = val; + return self; + }; +} + +- (id (^)(CGFloat))margin_bottom { + return ^id(CGFloat val) { + self.view.myBottom = val; + return self; + }; +} + +- (id (^)(CGFloat))margin_left { + return ^id(CGFloat val) { + self.view.myLeft = val; + return self; + }; +} + +- (id (^)(CGFloat))margin_right { + return ^id(CGFloat val) { + self.view.myRight = val; + return self; + }; +} + +- (id (^)(CGFloat))margin { + return ^id(CGFloat val) { + self.view.myLeft = val; + self.view.myRight = val; + self.view.myTop = val; + self.view.myBottom = val; + return self; + }; +} + +- (id (^)(MyVisibility))visibility { + return ^id(MyVisibility val) { + self.view.visibility = val; + return self; + }; +} + +- (__kindof UIView * (^)(UIView *))addTo { + return ^(UIView *val) { + [val addSubview:self.view]; + return self.view; + }; +} + +- (id (^)(UIView *))add { + return ^(UIView *val) { + [self.view addSubview:val]; + return self; + }; +} + +- (id (^)(id))addItem { + return ^(id item) { + [self.view addSubview:item.view]; + return self; + }; +} + +- (id (^)(NSInteger))order { + return ^id(NSInteger val) { + self.attrs.order = val; + return self; + }; +} + +- (id (^)(CGFloat))flex_grow { + return ^id(CGFloat val) { + self.attrs.flex_grow = val; + return self; + }; +} + +- (id (^)(CGFloat))flex_shrink { + return ^id(CGFloat val) { + self.attrs.flex_shrink = val; + return self; + }; +} + +- (id (^)(CGFloat))flex_basis { + return ^id(CGFloat val) { + self.attrs.flex_basis = val; + return self; + }; +} + +- (id (^)(MyFlexGravity))align_self { + return ^id(MyFlexGravity val) { + self.attrs.align_self = val; + return self; + }; +} + +@end + +#pragma mark-- MyFlexBox + +@interface MyFlexBox : MyFlexItem + +@end + +@implementation MyFlexBox + +- (id (^)(MyFlexDirection))flex_direction { + return ^id(MyFlexDirection val) { + self.attrs.flex_direction = val; + return self; + }; +} + +- (id (^)(MyFlexWrap))flex_wrap { + return ^id(MyFlexWrap val) { + self.attrs.flex_wrap = val; + return self; + }; +} + +- (id (^)(int))flex_flow { + return ^id(int val) { + //取方向值。 + MyFlexDirection direction = val & 0x03; + //取换行值。 + MyFlexWrap wrap = val & 0x0c; + return self.flex_direction(direction).flex_wrap(wrap); + }; +} + +- (id (^)(MyFlexGravity))justify_content { + return ^id(MyFlexGravity val) { + self.attrs.justify_content = val; + return self; + }; +} + +- (id (^)(MyFlexGravity))align_items { + return ^id(MyFlexGravity val) { + self.attrs.align_items = val; + return self; + }; +} + +- (id (^)(NSInteger))item_size { + return ^id(NSInteger val) { + self.attrs.item_size = val; + return self; + }; +} + +- (id (^)(NSInteger))page_size { + return ^id(NSInteger val) { + ((MyFlexLayout *)self.view).pagedCount = val; + return self; + }; +} + +- (id (^)(BOOL))auto_arrange { + return ^id(BOOL val) { + ((MyFlexLayout *)self.view).autoArrange = val; + return self; + }; +} + +- (id (^)(MyFlexGravity))align_content { + return ^id(MyFlexGravity val) { + self.attrs.align_content = val; + return self; + }; +} + +- (id (^)(UIEdgeInsets))padding { + return ^id(UIEdgeInsets val) { + ((MyFlexLayout *)self.view).padding = val; + return self; + }; +} + +- (id (^)(CGFloat))vert_space { + return ^id(CGFloat val) { + ((MyFlexLayout *)self.view).subviewVSpace = val; + return self; + }; +} + +- (id (^)(CGFloat))horz_space { + return ^id(CGFloat val) { + ((MyFlexLayout *)self.view).subviewHSpace = val; + return self; + }; +} + +@end + +#pragma mark - UIView + +@implementation UIView (MyFlexLayout) + +- (id)myFlex { + if ([self isKindOfClass:[MyFlexLayout class]]) { + return ((MyFlexLayout *)self).myFlex; + } else { + id obj = (id)objc_getAssociatedObject(self, ASSOCIATEDOBJECT_KEY_MYLAYOUT_FLEXITEM); + if (obj == nil) { + MyFlexItemAttrs *attrs = [MyFlexItemAttrs new]; + attrs.view = self; + obj = [[MyFlexItem alloc] initWithView:self attrs:attrs]; + } + objc_setAssociatedObject(self, ASSOCIATEDOBJECT_KEY_MYLAYOUT_FLEXITEM, obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return obj; + } +} + +@end + +@implementation MyFlexLayout + +- (instancetype)init { + self = [super init]; + if (self != nil) { + self.orientation = MyOrientation_Vert; //默认row + self.arrangedCount = NSIntegerMax; //默认单行 + self.isFlex = YES; //满足flexbox的需求。 + self.lastlineGravityPolicy = MyGravityPolicy_Always; + + MyFlexBoxAttrs *attrs = [MyFlexBoxAttrs new]; + attrs.view = self; + _myFlex = [[MyFlexBox alloc] initWithView:self attrs:attrs]; + } + return self; +} + +- (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { + //将flexbox中的属性映射为MyFlowLayout中的属性。 + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + if (context->subviewEngines == nil) { + context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; + } + + id myFlex = self.myFlex; + + //最先设置方向。 + switch (myFlex.attrs.flex_direction) { + case MyFlexDirection_Column_Reverse: { //column_reverse + layoutTraits.orientation = MyOrientation_Horz; + layoutTraits.layoutTransform = CGAffineTransformMake(1, 0, 0, -1, 0, 0); //垂直翻转 + } break; + case MyFlexDirection_Column: { //column; + layoutTraits.orientation = MyOrientation_Horz; + layoutTraits.layoutTransform = CGAffineTransformIdentity; + } break; + case MyFlexDirection_Row_Reverse: { //row_reverse + layoutTraits.orientation = MyOrientation_Vert; + layoutTraits.layoutTransform = CGAffineTransformMake(-1, 0, 0, 1, 0, 0); //水平翻转 + } break; + case MyFlexDirection_Row: + default: { + layoutTraits.orientation = MyOrientation_Vert; + layoutTraits.layoutTransform = CGAffineTransformIdentity; + } break; + } + + //设置换行 + switch (myFlex.attrs.flex_wrap) { + case MyFlexWrap_Wrap: { + layoutTraits.arrangedCount = myFlex.attrs.item_size; + } break; + case MyFlexWrap_Wrap_Reverse: { + layoutTraits.arrangedCount = myFlex.attrs.item_size; + layoutTraits.layoutTransform = CGAffineTransformConcat(layoutTraits.layoutTransform, (layoutTraits.orientation == MyOrientation_Vert) ? CGAffineTransformMake(1, 0, 0, -1, 0, 0) : CGAffineTransformMake(-1, 0, 0, 1, 0, 0)); + } break; + case MyFlexWrap_NoWrap: + default: { + layoutTraits.arrangedCount = NSIntegerMax; + } break; + } + + //按order排序。 + [context->subviewEngines sortWithOptions:NSSortStable + usingComparator:^NSComparisonResult(MyLayoutEngine *_Nonnull obj1, MyLayoutEngine *_Nonnull obj2) { + return obj1.currentSizeClass.view.myFlex.attrs.order - obj2.currentSizeClass.view.myFlex.attrs.order; + }]; + + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + id flexItem = subviewTraits.view.myFlex; + + //flex_grow,如果子视图有设置grow则父视图的换行不起作用。 + subviewTraits.weight = flexItem.attrs.flex_grow; + + //flex_shrink + if (layoutTraits.orientation == MyOrientation_Vert) { + subviewTraits.widthSize.shrink = flexItem.attrs.flex_shrink != MyFlex_Auto ? flexItem.attrs.flex_shrink : 0; + } else { + subviewTraits.heightSize.shrink = flexItem.attrs.flex_shrink != MyFlex_Auto ? flexItem.attrs.flex_shrink : 0; + } + + //如果没有设置尺寸约束则默认是自适应。 + if (subviewTraits.widthSizeInner.val == nil) { + [subviewTraits.widthSize _myEqualTo:@(MyLayoutSize.wrap)]; + } + if (subviewTraits.heightSizeInner.val == nil) { + [subviewTraits.heightSize _myEqualTo:@(MyLayoutSize.wrap)]; + } + + //基准值设置。 + if (flexItem.attrs.flex_basis != MyFlex_Auto) { + if (layoutTraits.orientation == MyOrientation_Vert) { + if (flexItem.attrs.flex_basis > 0 && flexItem.attrs.flex_basis < 1) { + [[subviewTraits.widthSize _myEqualTo:@(MyLayoutSize.fill)] _myMultiply:flexItem.attrs.flex_basis]; + } else { + [subviewTraits.widthSize _myEqualTo:@(flexItem.attrs.flex_basis)]; + } + } else { + if (flexItem.attrs.flex_basis > 0 && flexItem.attrs.flex_basis < 1) { + [[subviewTraits.heightSize _myEqualTo:@(MyLayoutSize.fill)] _myMultiply:flexItem.attrs.flex_basis]; + } else { + [subviewTraits.heightSize _myEqualTo:@(flexItem.attrs.flex_basis)]; + } + } + } + + //对齐方式设置。 + int align_self = flexItem.attrs.align_self; + switch (align_self) { + case -1: + subviewTraits.alignment = MyGravity_None; + break; + case MyFlexGravity_Flex_Start: + subviewTraits.alignment = (layoutTraits.orientation == MyOrientation_Vert) ? MyGravity_Vert_Top : MyGravity_Horz_Leading; + break; + case MyFlexGravity_Flex_End: + subviewTraits.alignment = (layoutTraits.orientation == MyOrientation_Vert) ? MyGravity_Vert_Bottom : MyGravity_Horz_Trailing; + break; + case MyFlexGravity_Center: + subviewTraits.alignment = (layoutTraits.orientation == MyOrientation_Vert) ? MyGravity_Vert_Center : MyGravity_Horz_Center; + break; + case MyFlexGravity_Baseline: + subviewTraits.alignment = (layoutTraits.orientation == MyOrientation_Vert) ? MyGravity_Vert_Baseline : MyGravity_None; + break; + case MyFlexGravity_Stretch: + subviewTraits.alignment = (layoutTraits.orientation == MyOrientation_Vert) ? MyGravity_Vert_Stretch : MyGravity_Horz_Stretch; + break; + default: + break; + } + } + + //设置主轴的水平对齐和拉伸 + MyGravity vertGravity = MYVERTGRAVITY(layoutTraits.gravity); + MyGravity horzGravity = MYHORZGRAVITY(layoutTraits.gravity); + switch (myFlex.attrs.justify_content) { + case MyFlexGravity_Flex_End: + if (layoutTraits.orientation == MyOrientation_Vert) { + layoutTraits.gravity = MyGravity_Horz_Trailing | vertGravity; + } else { + layoutTraits.gravity = MyGravity_Vert_Bottom | horzGravity; + } + break; + case MyFlexGravity_Center: + if (layoutTraits.orientation == MyOrientation_Vert) { + layoutTraits.gravity = MyGravity_Horz_Center | vertGravity; + } else { + layoutTraits.gravity = MyGravity_Vert_Center | horzGravity; + } + break; + case MyFlexGravity_Space_Between: + if (layoutTraits.orientation == MyOrientation_Vert) { + layoutTraits.gravity = MyGravity_Horz_Between | vertGravity; + } else { + layoutTraits.gravity = MyGravity_Vert_Between | horzGravity; + } + break; + case MyFlexGravity_Space_Around: + if (layoutTraits.orientation == MyOrientation_Vert) { + layoutTraits.gravity = MyGravity_Horz_Around | vertGravity; + } else { + layoutTraits.gravity = MyGravity_Vert_Around | horzGravity; + } + break; + case MyFlexGravity_Flex_Start: + if (layoutTraits.orientation == MyOrientation_Vert) { + layoutTraits.gravity = MyGravity_Horz_Leading | vertGravity; + } else { + layoutTraits.gravity = MyGravity_Vert_Top | horzGravity; + } + default: + break; + } + + //次轴的对齐处理。 + MyGravity vertArrangedGravity = MYVERTGRAVITY(layoutTraits.arrangedGravity); + MyGravity horzArrangedGravity = MYHORZGRAVITY(layoutTraits.arrangedGravity); + switch (myFlex.attrs.align_items) { + case MyFlexGravity_Flex_End: + if (layoutTraits.orientation == MyOrientation_Vert) { + layoutTraits.arrangedGravity = MyGravity_Vert_Bottom | horzArrangedGravity; + } else { + layoutTraits.arrangedGravity = MyGravity_Horz_Trailing | vertArrangedGravity; + } + break; + case MyFlexGravity_Center: + if (layoutTraits.orientation == MyOrientation_Vert) { + layoutTraits.arrangedGravity = MyGravity_Vert_Center | horzArrangedGravity; + } else { + layoutTraits.arrangedGravity = MyGravity_Horz_Center | vertArrangedGravity; + } + break; + case MyFlexGravity_Baseline: + if (layoutTraits.orientation == MyOrientation_Vert) { + layoutTraits.arrangedGravity = MyGravity_Vert_Baseline | horzArrangedGravity; + } else { + layoutTraits.arrangedGravity = MyGravity_Horz_Leading | vertArrangedGravity; + } + break; + case MyFlexGravity_Flex_Start: + if (layoutTraits.orientation == MyOrientation_Vert) { + layoutTraits.arrangedGravity = MyGravity_Vert_Top | horzArrangedGravity; + } else { + layoutTraits.arrangedGravity = MyGravity_Horz_Leading | vertArrangedGravity; + } + break; + case MyFlexGravity_Stretch: + default: + if (layoutTraits.orientation == MyOrientation_Vert) { + layoutTraits.arrangedGravity = MyGravity_Vert_Stretch | horzArrangedGravity; + } else { + layoutTraits.arrangedGravity = MyGravity_Horz_Stretch | vertArrangedGravity; + } + break; + } + + //多行下的整体停靠处理。 + vertGravity = MYVERTGRAVITY(layoutTraits.gravity); + horzGravity = MYHORZGRAVITY(layoutTraits.gravity); + switch (myFlex.attrs.align_content) { + case MyFlexGravity_Flex_End: + if (layoutTraits.orientation == MyOrientation_Horz) { + layoutTraits.gravity = MyGravity_Horz_Trailing | vertGravity; + } else { + layoutTraits.gravity = MyGravity_Vert_Bottom | horzGravity; + } + break; + case MyFlexGravity_Center: + if (layoutTraits.orientation == MyOrientation_Horz) { + layoutTraits.gravity = MyGravity_Horz_Center | vertGravity; + } else { + layoutTraits.gravity = MyGravity_Vert_Center | horzGravity; + } + break; + case MyFlexGravity_Space_Between: + if (layoutTraits.orientation == MyOrientation_Horz) { + layoutTraits.gravity = MyGravity_Horz_Between | vertGravity; + } else { + layoutTraits.gravity = MyGravity_Vert_Between | horzGravity; + } + break; + case MyFlexGravity_Space_Around: + if (layoutTraits.orientation == MyOrientation_Horz) { + layoutTraits.gravity = MyGravity_Horz_Around | vertGravity; + } else { + layoutTraits.gravity = MyGravity_Vert_Around | horzGravity; + } + break; + case MyFlexGravity_Flex_Start: + if (layoutTraits.orientation == MyOrientation_Horz) { + layoutTraits.gravity = MyGravity_Horz_Leading | vertGravity; + } else { + layoutTraits.gravity = MyGravity_Vert_Top | horzGravity; + } + break; + case MyFlexGravity_Stretch: + default: + if (layoutTraits.orientation == MyOrientation_Horz) { + layoutTraits.gravity = MyGravity_Horz_Stretch | vertGravity; + } else { + layoutTraits.gravity = MyGravity_Vert_Stretch | horzGravity; + } + break; + } + + return [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; +} + +@end diff --git a/MyLayout/Lib/MyFloatLayout.h b/MyLayout/Lib/MyFloatLayout.h index ed9c3f4..dc8df0b 100644 --- a/MyLayout/Lib/MyFloatLayout.h +++ b/MyLayout/Lib/MyFloatLayout.h @@ -8,9 +8,7 @@ #import "MyBaseLayout.h" - - -@interface UIView(MyFloatLayoutExt) +@interface UIView (MyFloatLayoutExt) /** 是否反方向浮动,默认是NO表示正向浮动。正向浮动和反向浮动的意义根据所在的父浮动布局视图的方向的不同而不同: @@ -47,8 +45,7 @@ @note 这个属性的定义是完全参考CSS样式表中float属性的定义 */ -@property(nonatomic,assign,getter=isReverseFloat) IBInspectable BOOL reverseFloat; - +@property (nonatomic, assign, getter=isReverseFloat) BOOL reverseFloat; /** 清除浮动,默认是NO。这个属性的意义也跟父浮动布局视图的方向相关。如果设置为了清除浮动属性则表示本子视图不会在浮动方向上紧跟在前一个浮动子视图的后面,而是会另外新起一行或者一列来重新排列。 @@ -69,12 +66,10 @@ @note 这个属性的定义是完全参考CSS样式表中clear属性的定义。 */ -@property(nonatomic,assign) IBInspectable BOOL clearFloat; +@property (nonatomic, assign) BOOL clearFloat; @end - - /** 浮动布局是一种里面的子视图按照约定的方向浮动停靠,当浮动布局的剩余空间不足容纳要加入的子视图的尺寸时会自动寻找最佳的位置进行浮动停靠的布局视图。浮动布局的理念源于HTML/CSS中的浮动定位技术,因此浮动布局可以专门用来实现那些不规则布局或者图文环绕的布局。 @@ -83,16 +78,15 @@ */ @interface MyFloatLayout : MyBaseLayout - /** 初始化一个浮动布局并指定布局的方向。当方向设置为MyOrientation_Vert时表示为左右浮动布局视图,而设置为MyOrientation_Horz则表示为上下浮动布局视图。 @param orientation 指定浮动布局的方向,要注意的这个方向是整体排列的方向。 @return 返回浮动布局的实例对象。 */ -+(instancetype)floatLayoutWithOrientation:(MyOrientation)orientation; --(instancetype)initWithOrientation:(MyOrientation)orientation; --(instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation; ++ (instancetype)floatLayoutWithOrientation:(MyOrientation)orientation; +- (instancetype)initWithOrientation:(MyOrientation)orientation; +- (instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation; /** 浮动布局的方向。 @@ -101,23 +95,7 @@ 2.MyOrientation_Horz 表示里面的子视图可以进行上下的浮动,但整体从左到右进行排列的布局方式,这个方式是默认方式。 */ -@property(nonatomic,assign) IBInspectable MyOrientation orientation; - - - - -/** - 不做布局边界尺寸的限制,子视图不会自动换行。因此当设置为YES时,子视图需要明确设置clearFloat来实现主动换行的处理。默认为NO。这个属性设置的意义使得我们可以自定义子视图的换行,而不是让子视图根据布局视图的尺寸限制自动换行。 - - 1. 当布局的orientation为MyOrientation_Vert并且wrapContentWidth为YES时,这个属性设置为YES才生效。 - - 2. 当布局的orientation为MyOrientation_Horz并且wrapContentHeight为YES时,这个属性设置为YES才生效。 - - @note 当属性设置为YES时,子视图不能将扩展属性reverseFloat设置为YES,同时不能设置weight属性,否则将导致结果异常。 - @note 这个属性设置为YES时,在左右浮动布局中,子视图只能向左浮动,并且没有右边界的限制,因此如果子视图没有clearFloat时则总是排列在前一个子视图的右边,并不会自动换行,因此为了让这个属性生效,布局视图必须要同时设置wrapContentWidth为YES。 - @note 这个属性设置为YES时,在上下浮动布局中,子视图只能向上浮动,并且没有下边界的限制,因此如果子视图没有设置clearFloat时则总是排列在前一个子视图的下边,并不会自动换行,因此为了让这个属性生效,布局视图必须要同时设置wrapContentHeight为YES. - */ -@property(nonatomic,assign) IBInspectable BOOL noBoundaryLimit; +@property (nonatomic, assign) MyOrientation orientation; /** 在一些应用场景中我们希望子视图的宽度是固定的但间距是浮动的,这样就尽可能在一排中容纳更多的子视图。比如设置每个子视图的宽度固定为80,那么在小屏幕下每排只能放3个,而大屏幕则每排能放4个或者5个子视图。 因此您可以通过如下方法来设置子视图的固定尺寸和最小最大浮动间距。这个方法会根据您当前布局的方向不同而具有不同的意义: @@ -132,20 +110,19 @@ @param minSpace 指定子视图之间的最小间距 @param maxSpace 指定子视图之间的最大间距 */ --(void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace; --(void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass; - - - -@end - - - -@interface MyFloatLayout(MyFloatLayoutDeprecated) - --(void)setSubviewFloatMargin:(CGFloat)subviewSize minMargin:(CGFloat)minMargin MYMETHODDEPRECATED("use method: setSubviews:minSpace:maxSpace to replace"); --(void)setSubviewFloatMargin:(CGFloat)subviewSize minMargin:(CGFloat)minMargin inSizeClass:(MySizeClass)sizeClass MYMETHODDEPRECATED("use method: setSubviews:minSpace:maxSpace:inSizeClass to replace"); +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace; +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass; +/** + 上面函数的加强版本。 + + @param subviewSize 指定子视图的尺寸 + @param minSpace 指定子视图之间的最小间距 + @param maxSpace 指定子视图之间的最大间距 + @param centered 指定是否所有子视图居中 + */ +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered; +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; @end diff --git a/MyLayout/Lib/MyFloatLayout.m b/MyLayout/Lib/MyFloatLayout.m index d8bb479..7a9ebda 100644 --- a/MyLayout/Lib/MyFloatLayout.m +++ b/MyLayout/Lib/MyFloatLayout.m @@ -9,1403 +9,1074 @@ #import "MyFloatLayout.h" #import "MyLayoutInner.h" -@implementation UIView(MyFloatLayoutExt) +@implementation UIView (MyFloatLayoutExt) - --(void)setReverseFloat:(BOOL)reverseFloat -{ - UIView *sc = self.myCurrentSizeClass; - if (sc.isReverseFloat != reverseFloat) - { - sc.reverseFloat = reverseFloat; - if (self.superview != nil) +- (void)setReverseFloat:(BOOL)reverseFloat { + MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; + if (viewTraits.isReverseFloat != reverseFloat) { + viewTraits.reverseFloat = reverseFloat; + if (self.superview != nil) { [self.superview setNeedsLayout]; + } } } --(BOOL)isReverseFloat -{ - return self.myCurrentSizeClass.isReverseFloat; - +- (BOOL)isReverseFloat { + return self.myDefaultSizeClassInner.isReverseFloat; } --(void)setClearFloat:(BOOL)clearFloat -{ - UIView *sc = self.myCurrentSizeClass; - if (sc.clearFloat != clearFloat) - { - sc.clearFloat = clearFloat; - if (self.superview != nil) +- (void)setClearFloat:(BOOL)clearFloat { + MyViewTraits *viewTraits = (MyViewTraits*)self.myDefaultSizeClass; + if (viewTraits.clearFloat != clearFloat) { + viewTraits.clearFloat = clearFloat; + if (self.superview != nil) { [self.superview setNeedsLayout]; + } } } --(BOOL)clearFloat -{ - return self.myCurrentSizeClass.clearFloat; +- (BOOL)clearFloat { + return self.myDefaultSizeClassInner.clearFloat; } @end - - - @implementation MyFloatLayout --(instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation -{ +#pragma mark-- Public Methods + +- (instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation { self = [super initWithFrame:frame]; - if (self != nil) - { - self.myCurrentSizeClass.orientation = orientation; + if (self != nil) { + self.myDefaultSizeClass.orientation = orientation; } - return self; } --(instancetype)initWithOrientation:(MyOrientation)orientation -{ +- (instancetype)initWithOrientation:(MyOrientation)orientation { return [self initWithFrame:CGRectZero orientation:orientation]; } - -+(instancetype)floatLayoutWithOrientation:(MyOrientation)orientation -{ ++ (instancetype)floatLayoutWithOrientation:(MyOrientation)orientation { MyFloatLayout *layout = [[[self class] alloc] initWithOrientation:orientation]; return layout; } --(void)setOrientation:(MyOrientation)orientation -{ - MyFloatLayout *lsc = self.myCurrentSizeClass; - if (lsc.orientation != orientation) - { - lsc.orientation = orientation; - [self setNeedsLayout]; - } -} - --(MyOrientation)orientation -{ - return self.myCurrentSizeClass.orientation; -} - - --(void)setNoBoundaryLimit:(BOOL)noBoundaryLimit -{ - MyFloatLayout *lsc = self.myCurrentSizeClass; - if (lsc.noBoundaryLimit != noBoundaryLimit) - { - lsc.noBoundaryLimit = noBoundaryLimit; +- (void)setOrientation:(MyOrientation)orientation { + MyFloatLayoutTraits *layoutTraits = (MyFloatLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.orientation != orientation) { + layoutTraits.orientation = orientation; [self setNeedsLayout]; } } --(BOOL)noBoundaryLimit -{ - return self.myCurrentSizeClass.noBoundaryLimit; +- (MyOrientation)orientation { + return self.myDefaultSizeClassInner.orientation; } - --(void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace -{ +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace { [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } --(void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass -{ - MyFloatLayoutViewSizeClass *lsc = (MyFloatLayoutViewSizeClass*)[self fetchLayoutSizeClass:sizeClass]; - lsc.subviewSize = subviewSize; - lsc.minSpace = minSpace; - lsc.maxSpace = maxSpace; - [self setNeedsLayout]; +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass { + [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace centered:NO inSizeClass:sizeClass]; } - -#pragma mark -- Deprecated Method - - --(void)setSubviewFloatMargin:(CGFloat)subviewSize minMargin:(CGFloat)minMargin -{ - [self setSubviewsSize:subviewSize minSpace:minMargin maxSpace:CGFLOAT_MAX]; +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered { + [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace centered:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } --(void)setSubviewFloatMargin:(CGFloat)subviewSize minMargin:(CGFloat)minMargin inSizeClass:(MySizeClass)sizeClass -{ - [self setSubviewsSize:subviewSize minSpace:minMargin maxSpace:CGFLOAT_MAX inSizeClass:sizeClass]; +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { + MySequentLayoutTraits *layoutTraits = (MySequentLayoutTraits *)[self fetchLayoutSizeClass:sizeClass]; + if (subviewSize == 0) { + layoutTraits.flexSpace = nil; + } else { + if (layoutTraits.flexSpace == nil) { + layoutTraits.flexSpace = [MySequentLayoutFlexSpacing new]; + } + layoutTraits.flexSpace.subviewSize = subviewSize; + layoutTraits.flexSpace.minSpace = minSpace; + layoutTraits.flexSpace.maxSpace = maxSpace; + layoutTraits.flexSpace.centered = centered; + } + [self setNeedsLayout]; } - - - -/* - // Only override drawRect: if you perform custom drawing. - // An empty implementation adversely affects performance during animation. - - (void)drawRect:(CGRect)rect { - // Drawing code - } - */ - - -#pragma mark -- Override Method - --(CGSize)calcLayoutRect:(CGSize)size isEstimate:(BOOL)isEstimate pHasSubLayout:(BOOL*)pHasSubLayout sizeClass:(MySizeClass)sizeClass sbs:(NSMutableArray*)sbs -{ - CGSize selfSize = [super calcLayoutRect:size isEstimate:isEstimate pHasSubLayout:pHasSubLayout sizeClass:sizeClass sbs:sbs]; - - if (sbs == nil) - sbs = [self myGetLayoutSubviews]; - - MyFloatLayout *lsc = self.myCurrentSizeClass; - - MyOrientation orientation = lsc.orientation; - - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (!isEstimate) - { - sbvmyFrame.frame = sbv.bounds; - [self myCalcSizeOfWrapContentSubview:sbv sbvsc:sbvsc sbvmyFrame:sbvmyFrame]; - } - - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - - if (sbvsc.wrapContentWidth) - { - if (sbvsc.widthSizeInner.dimeVal != nil || (orientation == MyOrientation_Vert && sbvsc.weight != 0)) - { - sbvsc.wrapContentWidth = NO; - } - } - - if (sbvsc.wrapContentHeight) - { - if (sbvsc.heightSizeInner.dimeVal != nil || (orientation == MyOrientation_Horz && sbvsc.weight != 0)) - { - sbvsc.wrapContentHeight = NO; - } - } - - BOOL isSbvWrap = sbvsc.wrapContentHeight || sbvsc.wrapContentWidth; - - if (pHasSubLayout != nil && isSbvWrap) - *pHasSubLayout = YES; - - if (isEstimate && isSbvWrap) - { - [(MyBaseLayout*)sbv sizeThatFits:sbvmyFrame.frame.size inSizeClass:sizeClass]; - if (sbvmyFrame.multiple) - { - sbvmyFrame.sizeClass = [sbv myBestSizeClass:sizeClass]; //因为sizeThatFits执行后会还原,所以这里要重新设置 - sbvsc = sbvmyFrame.sizeClass; - } - } - } +#pragma mark-- Override Methods + +- (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { + [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; + + MyFloatLayoutTraits *layoutTraits = (MyFloatLayoutTraits *)context->layoutViewEngine.currentSizeClass; + context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; + context->paddingTop = layoutTraits.myLayoutPaddingTop; + context->paddingBottom = layoutTraits.myLayoutPaddingBottom; + context->paddingLeading = layoutTraits.myLayoutPaddingLeading; + context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + context->vertGravity = MYVERTGRAVITY(layoutTraits.gravity); + context->horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.gravity)]; + context->vertSpace = layoutTraits.subviewVSpace; + context->horzSpace = layoutTraits.subviewHSpace; + if (context->subviewEngines == nil) { + context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; } - - if (orientation == MyOrientation_Vert) - selfSize = [self myLayoutSubviewsForVert:selfSize sbs:sbs isEstimate:isEstimate lsc:lsc]; - else - selfSize = [self myLayoutSubviewsForHorz:selfSize sbs:sbs isEstimate:isEstimate lsc:lsc]; - - - [self myAdjustLayoutSelfSize:&selfSize lsc:lsc]; - - [self myAdjustSubviewsRTLPos:sbs selfWidth:selfSize.width]; - - return [self myAdjustSizeWhenNoSubviews:selfSize sbs:sbs lsc:lsc]; + MyOrientation orientation = layoutTraits.orientation; + + [self myCalcSubviewsWrapContentSize:context + withCustomSetting:^(MyViewTraits *subviewTraits) { + if (subviewTraits.widthSizeInner.wrapVal && + orientation == MyOrientation_Vert && + subviewTraits.weight != 0 && + [subviewTraits.view isKindOfClass:[MyBaseLayout class]]) + [subviewTraits.widthSizeInner _myEqualTo:nil]; + + if (subviewTraits.heightSizeInner.wrapVal && + orientation == MyOrientation_Horz && + subviewTraits.weight != 0 && + [subviewTraits.view isKindOfClass:[MyBaseLayout class]]) + [subviewTraits.heightSizeInner _myEqualTo:nil]; + }]; + + if (orientation == MyOrientation_Vert) { + [self myDoVertOrientationLayoutWithContext:context]; + } else { + [self myDoHorzOrientationLayoutWithContext:context]; + } + return [self myAdjustLayoutViewSizeWithContext:context]; } --(id)createSizeClassInstance -{ - return [MyFloatLayoutViewSizeClass new]; +- (id)createSizeClassInstance { + return [MyFloatLayoutTraits new]; } -#pragma mark -- Private Method +#pragma mark-- Private Methods + +- (CGPoint)myFindTrailingCandidatePoint:(CGRect)leadingCandidateRect width:(CGFloat)width trailingBoundary:(CGFloat)trailingBoundary trailingCandidateRects:(NSArray *)trailingCandidateRects hasWeight:(BOOL)hasWeight paddingTop:(CGFloat)paddingTop { + CGPoint retPoint = {trailingBoundary, CGFLOAT_MAX}; --(CGPoint)myFindTrailingCandidatePoint:(CGRect)leadingCandidateRect width:(CGFloat)width trailingBoundary:(CGFloat)trailingBoundary trailingCandidateRects:(NSArray*)trailingCandidateRects hasWeight:(BOOL)hasWeight paddingTop:(CGFloat)paddingTop -{ - - CGPoint retPoint = {trailingBoundary,CGFLOAT_MAX}; - CGFloat lastHeight = paddingTop; - for (NSInteger i = trailingCandidateRects.count - 1; i >= 0; i--) - { - - CGRect trailingCandidateRect = ((NSValue*)trailingCandidateRects[i]).CGRectValue; - + for (NSInteger i = trailingCandidateRects.count - 1; i >= 0; i--) { + CGRect trailingCandidateRect = ((NSValue *)trailingCandidateRects[i]).CGRectValue; + //如果有比重则不能相等只能小于。 - if ((hasWeight ? _myCGFloatLess(CGRectGetMaxX(leadingCandidateRect) + width, CGRectGetMinX(trailingCandidateRect)) : _myCGFloatLessOrEqual(CGRectGetMaxX(leadingCandidateRect) + width, CGRectGetMinX(trailingCandidateRect)) - ) && - _myCGFloatGreat(CGRectGetMaxY(leadingCandidateRect), lastHeight)) - { - retPoint.y = _myCGFloatMax(CGRectGetMinY(leadingCandidateRect),lastHeight); + if ((hasWeight ? _myCGFloatLess(CGRectGetMaxX(leadingCandidateRect) + width, CGRectGetMinX(trailingCandidateRect)) : _myCGFloatLessOrEqual(CGRectGetMaxX(leadingCandidateRect) + width, CGRectGetMinX(trailingCandidateRect))) && + _myCGFloatGreat(CGRectGetMaxY(leadingCandidateRect), lastHeight)) { + retPoint.y = _myCGFloatMax(CGRectGetMinY(leadingCandidateRect), lastHeight); retPoint.x = CGRectGetMinX(trailingCandidateRect); - + if (hasWeight && CGRectGetHeight(leadingCandidateRect) == CGFLOAT_MAX && CGRectGetWidth(leadingCandidateRect) == 0 && - _myCGFloatGreatOrEqual(CGRectGetMinY(leadingCandidateRect), CGRectGetMaxY(trailingCandidateRect))) - { + _myCGFloatGreatOrEqual(CGRectGetMinY(leadingCandidateRect), CGRectGetMaxY(trailingCandidateRect))) { retPoint.x = trailingBoundary; } - + break; } - + lastHeight = CGRectGetMaxY(trailingCandidateRect); - } - - if (retPoint.y == CGFLOAT_MAX) - { - if ((hasWeight ? _myCGFloatLess(CGRectGetMaxX(leadingCandidateRect) + width, trailingBoundary) :_myCGFloatLessOrEqual(CGRectGetMaxX(leadingCandidateRect) + width, trailingBoundary) ) && - _myCGFloatGreat(CGRectGetMaxY(leadingCandidateRect), lastHeight)) - { - retPoint.y = _myCGFloatMax(CGRectGetMinY(leadingCandidateRect),lastHeight); + + if (retPoint.y == CGFLOAT_MAX) { + if ((hasWeight ? _myCGFloatLess(CGRectGetMaxX(leadingCandidateRect) + width, trailingBoundary) : _myCGFloatLessOrEqual(CGRectGetMaxX(leadingCandidateRect) + width, trailingBoundary)) && + _myCGFloatGreat(CGRectGetMaxY(leadingCandidateRect), lastHeight)) { + retPoint.y = _myCGFloatMax(CGRectGetMinY(leadingCandidateRect), lastHeight); } } - + return retPoint; } --(CGPoint)myFindBottomCandidatePoint:(CGRect)topCandidateRect height:(CGFloat)height bottomBoundary:(CGFloat)bottomBoundary bottomCandidateRects:(NSArray*)bottomCandidateRects hasWeight:(BOOL)hasWeight paddingLeading:(CGFloat)paddingLeading -{ - - CGPoint retPoint = {CGFLOAT_MAX,bottomBoundary}; - +- (CGPoint)myFindBottomCandidatePoint:(CGRect)topCandidateRect height:(CGFloat)height bottomBoundary:(CGFloat)bottomBoundary bottomCandidateRects:(NSArray *)bottomCandidateRects hasWeight:(BOOL)hasWeight paddingLeading:(CGFloat)paddingLeading { + CGPoint retPoint = {CGFLOAT_MAX, bottomBoundary}; CGFloat lastWidth = paddingLeading; - for (NSInteger i = bottomCandidateRects.count - 1; i >= 0; i--) - { - - CGRect bottomCandidateRect = ((NSValue*)bottomCandidateRects[i]).CGRectValue; + for (NSInteger i = bottomCandidateRects.count - 1; i >= 0; i--) { + CGRect bottomCandidateRect = ((NSValue *)bottomCandidateRects[i]).CGRectValue; - if ((hasWeight ? _myCGFloatLess(CGRectGetMaxY(topCandidateRect) + height, CGRectGetMinY(bottomCandidateRect)) : - _myCGFloatLessOrEqual(CGRectGetMaxY(topCandidateRect) + height, CGRectGetMinY(bottomCandidateRect))) && - _myCGFloatGreat(CGRectGetMaxX(topCandidateRect), lastWidth)) - { - retPoint.x = _myCGFloatMax(CGRectGetMinX(topCandidateRect),lastWidth); + if ((hasWeight ? _myCGFloatLess(CGRectGetMaxY(topCandidateRect) + height, CGRectGetMinY(bottomCandidateRect)) : _myCGFloatLessOrEqual(CGRectGetMaxY(topCandidateRect) + height, CGRectGetMinY(bottomCandidateRect))) && + _myCGFloatGreat(CGRectGetMaxX(topCandidateRect), lastWidth)) { + retPoint.x = _myCGFloatMax(CGRectGetMinX(topCandidateRect), lastWidth); retPoint.y = CGRectGetMinY(bottomCandidateRect); - + if (hasWeight && CGRectGetWidth(topCandidateRect) == CGFLOAT_MAX && CGRectGetHeight(topCandidateRect) == 0 && - _myCGFloatGreatOrEqual(CGRectGetMinX(topCandidateRect), CGRectGetMaxX(bottomCandidateRect))) - { + _myCGFloatGreatOrEqual(CGRectGetMinX(topCandidateRect), CGRectGetMaxX(bottomCandidateRect))) { retPoint.y = bottomBoundary; } - break; } - lastWidth = CGRectGetMaxX(bottomCandidateRect); - } - - if (retPoint.x == CGFLOAT_MAX) - { - if ((hasWeight ? _myCGFloatLess(CGRectGetMaxY(topCandidateRect) + height, bottomBoundary) : _myCGFloatLessOrEqual(CGRectGetMaxY(topCandidateRect) + height, bottomBoundary) ) && - _myCGFloatGreat(CGRectGetMaxX(topCandidateRect), lastWidth)) - { - retPoint.x = _myCGFloatMax(CGRectGetMinX(topCandidateRect),lastWidth); + + if (retPoint.x == CGFLOAT_MAX) { + if ((hasWeight ? _myCGFloatLess(CGRectGetMaxY(topCandidateRect) + height, bottomBoundary) : _myCGFloatLessOrEqual(CGRectGetMaxY(topCandidateRect) + height, bottomBoundary)) && + _myCGFloatGreat(CGRectGetMaxX(topCandidateRect), lastWidth)) { + retPoint.x = _myCGFloatMax(CGRectGetMinX(topCandidateRect), lastWidth); } } - return retPoint; } - --(CGPoint)myFindLeadingCandidatePoint:(CGRect)trailingCandidateRect width:(CGFloat)width leadingBoundary:(CGFloat)leadingBoundary leadingCandidateRects:(NSArray*)leadingCandidateRects hasWeight:(BOOL)hasWeight paddingTop:(CGFloat)paddingTop -{ - - CGPoint retPoint = {leadingBoundary,CGFLOAT_MAX}; - +- (CGPoint)myFindLeadingCandidatePoint:(CGRect)trailingCandidateRect width:(CGFloat)width leadingBoundary:(CGFloat)leadingBoundary leadingCandidateRects:(NSArray *)leadingCandidateRects hasWeight:(BOOL)hasWeight paddingTop:(CGFloat)paddingTop { + CGPoint retPoint = {leadingBoundary, CGFLOAT_MAX}; CGFloat lastHeight = paddingTop; - for (NSInteger i = leadingCandidateRects.count - 1; i >= 0; i--) - { - - CGRect leadingCandidateRect = ((NSValue*)leadingCandidateRects[i]).CGRectValue; + for (NSInteger i = leadingCandidateRects.count - 1; i >= 0; i--) { + CGRect leadingCandidateRect = ((NSValue *)leadingCandidateRects[i]).CGRectValue; - if ((hasWeight ? _myCGFloatGreat(CGRectGetMinX(trailingCandidateRect) - width, CGRectGetMaxX(leadingCandidateRect)) : - _myCGFloatGreatOrEqual(CGRectGetMinX(trailingCandidateRect) - width, CGRectGetMaxX(leadingCandidateRect))) && - _myCGFloatGreat(CGRectGetMaxY(trailingCandidateRect), lastHeight)) - { - retPoint.y = _myCGFloatMax(CGRectGetMinY(trailingCandidateRect),lastHeight); + if ((hasWeight ? _myCGFloatGreat(CGRectGetMinX(trailingCandidateRect) - width, CGRectGetMaxX(leadingCandidateRect)) : _myCGFloatGreatOrEqual(CGRectGetMinX(trailingCandidateRect) - width, CGRectGetMaxX(leadingCandidateRect))) && + _myCGFloatGreat(CGRectGetMaxY(trailingCandidateRect), lastHeight)) { + retPoint.y = _myCGFloatMax(CGRectGetMinY(trailingCandidateRect), lastHeight); retPoint.x = CGRectGetMaxX(leadingCandidateRect); - if (hasWeight && CGRectGetHeight(trailingCandidateRect) == CGFLOAT_MAX && CGRectGetWidth(trailingCandidateRect) == 0 && - _myCGFloatGreatOrEqual(CGRectGetMinY(trailingCandidateRect), CGRectGetMaxY(leadingCandidateRect))) - { + _myCGFloatGreatOrEqual(CGRectGetMinY(trailingCandidateRect), CGRectGetMaxY(leadingCandidateRect))) { retPoint.x = leadingBoundary; } - break; } - lastHeight = CGRectGetMaxY(leadingCandidateRect); - } - - if (retPoint.y == CGFLOAT_MAX) - { - if ((hasWeight ? _myCGFloatGreat(CGRectGetMinX(trailingCandidateRect) - width, leadingBoundary) : - _myCGFloatGreatOrEqual(CGRectGetMinX(trailingCandidateRect) - width, leadingBoundary)) && - _myCGFloatGreat(CGRectGetMaxY(trailingCandidateRect),lastHeight)) - { - retPoint.y = _myCGFloatMax(CGRectGetMinY(trailingCandidateRect),lastHeight); + + if (retPoint.y == CGFLOAT_MAX) { + if ((hasWeight ? _myCGFloatGreat(CGRectGetMinX(trailingCandidateRect) - width, leadingBoundary) : _myCGFloatGreatOrEqual(CGRectGetMinX(trailingCandidateRect) - width, leadingBoundary)) && + _myCGFloatGreat(CGRectGetMaxY(trailingCandidateRect), lastHeight)) { + retPoint.y = _myCGFloatMax(CGRectGetMinY(trailingCandidateRect), lastHeight); } } - return retPoint; } --(CGPoint)myFindTopCandidatePoint:(CGRect)bottomCandidateRect height:(CGFloat)height topBoundary:(CGFloat)topBoundary topCandidateRects:(NSArray*)topCandidateRects hasWeight:(BOOL)hasWeight paddingLeading:(CGFloat)paddingLeading -{ - +- (CGPoint)myFindTopCandidatePoint:(CGRect)bottomCandidateRect height:(CGFloat)height topBoundary:(CGFloat)topBoundary topCandidateRects:(NSArray *)topCandidateRects hasWeight:(BOOL)hasWeight paddingLeading:(CGFloat)paddingLeading { CGPoint retPoint = {CGFLOAT_MAX, topBoundary}; - + CGFloat lastWidth = paddingLeading; - for (NSInteger i = topCandidateRects.count - 1; i >= 0; i--) - { - - CGRect topCandidateRect = ((NSValue*)topCandidateRects[i]).CGRectValue; + for (NSInteger i = topCandidateRects.count - 1; i >= 0; i--) { + CGRect topCandidateRect = ((NSValue *)topCandidateRects[i]).CGRectValue; - if ((hasWeight ? _myCGFloatGreat(CGRectGetMinY(bottomCandidateRect) - height, CGRectGetMaxY(topCandidateRect)) : - _myCGFloatGreatOrEqual(CGRectGetMinY(bottomCandidateRect) - height, CGRectGetMaxY(topCandidateRect))) && - _myCGFloatGreat(CGRectGetMaxX(bottomCandidateRect), lastWidth)) - { - retPoint.x = _myCGFloatMax(CGRectGetMinX(bottomCandidateRect),lastWidth); + if ((hasWeight ? _myCGFloatGreat(CGRectGetMinY(bottomCandidateRect) - height, CGRectGetMaxY(topCandidateRect)) : _myCGFloatGreatOrEqual(CGRectGetMinY(bottomCandidateRect) - height, CGRectGetMaxY(topCandidateRect))) && + _myCGFloatGreat(CGRectGetMaxX(bottomCandidateRect), lastWidth)) { + retPoint.x = _myCGFloatMax(CGRectGetMinX(bottomCandidateRect), lastWidth); retPoint.y = CGRectGetMaxY(topCandidateRect); - + if (hasWeight && CGRectGetWidth(bottomCandidateRect) == CGFLOAT_MAX && CGRectGetHeight(bottomCandidateRect) == 0 && - _myCGFloatGreatOrEqual(CGRectGetMinX(bottomCandidateRect), CGRectGetMaxX(topCandidateRect))) - { + _myCGFloatGreatOrEqual(CGRectGetMinX(bottomCandidateRect), CGRectGetMaxX(topCandidateRect))) { retPoint.y = topBoundary; } - - break; } - + lastWidth = CGRectGetMaxX(topCandidateRect); - } - - if (retPoint.x == CGFLOAT_MAX) - { - if ((hasWeight ? _myCGFloatGreat(CGRectGetMinY(bottomCandidateRect) - height, topBoundary) : - _myCGFloatGreatOrEqual(CGRectGetMinY(bottomCandidateRect) - height, topBoundary)) && - _myCGFloatGreat(CGRectGetMaxX(bottomCandidateRect), lastWidth)) - { - retPoint.x = _myCGFloatMax(CGRectGetMinX(bottomCandidateRect),lastWidth); + + if (retPoint.x == CGFLOAT_MAX) { + if ((hasWeight ? _myCGFloatGreat(CGRectGetMinY(bottomCandidateRect) - height, topBoundary) : _myCGFloatGreatOrEqual(CGRectGetMinY(bottomCandidateRect) - height, topBoundary)) && + _myCGFloatGreat(CGRectGetMaxX(bottomCandidateRect), lastWidth)) { + retPoint.x = _myCGFloatMax(CGRectGetMinX(bottomCandidateRect), lastWidth); } } - + return retPoint; } +- (void)myCalcSubviewsSize:(CGFloat)specialMeasure isWidth:(BOOL)isWidth withContext:(MyLayoutContext *)context { + //设置子视图的宽度和高度。 + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + if (specialMeasure != 0.0) { + if (isWidth) { + subviewEngine.width = specialMeasure; + } else { + subviewEngine.height = specialMeasure; + } + } + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; --(CGSize)myLayoutSubviewsForVert:(CGSize)selfSize sbs:(NSArray*)sbs isEstimate:(BOOL)isEstimate lsc:(MyFloatLayout*)lsc -{ - //对于垂直浮动布局来说,默认是左浮动,当设置为RTL时则默认是右浮动,因此我们只需要改变一下sbv.reverseFloat的定义就好了。 - - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - CGFloat paddingHorz = paddingLeading + paddingTrailing; - CGFloat paddingVert = paddingTop + paddingBottom; - - BOOL hasBoundaryLimit = YES; - if (lsc.wrapContentWidth && lsc.noBoundaryLimit) - hasBoundaryLimit = NO; - - //如果没有边界限制我们将高度设置为最大。。 - if (!hasBoundaryLimit) - selfSize.width = CGFLOAT_MAX; - - //遍历所有的子视图,查看是否有子视图的宽度会比视图自身要宽,如果有且有包裹属性则扩充自身的宽度 - if (lsc.wrapContentWidth && hasBoundaryLimit) - { - CGFloat maxContentWidth = selfSize.width - paddingHorz; - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - CGFloat leadingSpace = sbvsc.leadingPosInner.absVal; - CGFloat trailingSpace = sbvsc.trailingPosInner.absVal; - CGRect rect = sbvmyFrame.frame; - - //因为这里是计算包裹宽度属性,所以只会计算那些设置了固定宽度的子视图 - - //这里有可能设置了固定的宽度 - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - //有可能宽度是和他的高度相等。 - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == sbvsc.heightSizeInner) - { - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == lsc.heightSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:(selfSize.height - paddingVert) ]; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - rect.size.width = [sbvsc.widthSizeInner measureWith:rect.size.height]; - } - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - if (_myCGFloatGreat(leadingSpace + rect.size.width + trailingSpace, maxContentWidth) && - (sbvsc.widthSizeInner.dimeRelaVal == nil || sbvsc.widthSizeInner.dimeRelaVal != lsc.widthSizeInner) && - sbvsc.weight == 0) - { - maxContentWidth = leadingSpace + rect.size.width + trailingSpace; - } + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { //特殊处理高度等于宽度的情况 + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; } - - selfSize.width = paddingHorz + maxContentWidth; - } - - //支持浮动水平间距。 - CGFloat vertSpace = lsc.subviewVSpace; - CGFloat horzSpace = lsc.subviewHSpace; - CGFloat subviewSize = ((MyFloatLayoutViewSizeClass*)self.myCurrentSizeClass).subviewSize; - if (subviewSize != 0) - { - -#ifdef DEBUG - //异常崩溃:当布局视图设置了noBoundaryLimit为YES时,不能设置最小垂直间距。 - NSCAssert(hasBoundaryLimit, @"Constraint exception!!, vertical float layout:%@ can not set noBoundaryLimit to YES when call setSubviewsSize:minSpace:maxSpace method",self); -#endif - - - CGFloat minSpace = ((MyFloatLayoutViewSizeClass*)self.myCurrentSizeClass).minSpace; - CGFloat maxSpace = ((MyFloatLayoutViewSizeClass*)self.myCurrentSizeClass).maxSpace; - - NSInteger rowCount = floor((selfSize.width - paddingHorz + minSpace) / (subviewSize + minSpace)); - if (rowCount > 1) - { - horzSpace = (selfSize.width - paddingHorz - subviewSize * rowCount)/(rowCount - 1); - - //如果超过最大间距则调整子视图的宽度。 - if (_myCGFloatGreat(horzSpace,maxSpace)) - { - horzSpace = maxSpace; - - subviewSize = (selfSize.width - paddingHorz - horzSpace * (rowCount - 1)) / rowCount; - - } - + + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { //特殊处理宽度等于高度的情况 + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; } } +} + +- (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { + MyFloatLayoutTraits *layoutTraits = (MyFloatLayoutTraits*)context->layoutViewEngine.currentSizeClass; + + BOOL isBeyondFlag = NO; //子视图是否超出剩余空间需要换行。 - + if (layoutTraits.widthSizeInner.wrapVal) { + //如果有最大限制则取最大值,解决那种宽度自适应,但是有最大值需要换行的情况。 + context->selfSize.width = [self myGetBoundLimitMeasure:layoutTraits.widthSizeInner.uBoundValInner subview:self anchorType:layoutTraits.widthSizeInner.anchorType subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size isUBound:YES]; + } + + //支持浮动水平间距。 + CGFloat subviewWidth = [layoutTraits.flexSpace calcMaxMinSubviewSizeForContent:context->selfSize.width paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing space:&context->horzSpace]; + + //计算所有子视图的宽度和高度。 + [self myCalcSubviewsSize:subviewWidth isWidth:YES withContext:context]; + //左边候选区域数组,保存的是CGRect值。 NSMutableArray *leadingCandidateRects = [NSMutableArray new]; //为了计算方便总是把最左边的个虚拟区域作为一个候选区域 - [leadingCandidateRects addObject:[NSValue valueWithCGRect:CGRectMake(paddingLeading, paddingTop, 0, CGFLOAT_MAX)]]; - + [leadingCandidateRects addObject:[NSValue valueWithCGRect:CGRectMake(context->paddingLeading, context->paddingTop, 0, CGFLOAT_MAX)]]; + //右边候选区域数组,保存的是CGRect值。 NSMutableArray *trailingCandidateRects = [NSMutableArray new]; //为了计算方便总是把最右边的个虚拟区域作为一个候选区域 - [trailingCandidateRects addObject:[NSValue valueWithCGRect:CGRectMake(selfSize.width - paddingTrailing, paddingTop, 0, CGFLOAT_MAX)]]; - + [trailingCandidateRects addObject:[NSValue valueWithCGRect:CGRectMake(context->selfSize.width - context->paddingTrailing, context->paddingTop, 0, CGFLOAT_MAX)]]; + //分别记录左边和右边的最后一个视图在Y轴的偏移量 - CGFloat leadingLastYOffset = paddingTop; - CGFloat trailingLastYOffset = paddingTop; - + CGFloat leadingLastYOffset = context->paddingTop; + CGFloat trailingLastYOffset = context->paddingTop; + //分别记录左右边和全局浮动视图的最高占用的Y轴的值 - CGFloat leadingMaxHeight = paddingTop; - CGFloat trailingMaxHeight = paddingTop; - CGFloat maxHeight = paddingTop; - CGFloat maxWidth = paddingLeading; - - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - CGFloat topSpace = sbvsc.topPosInner.absVal; - CGFloat leadingSpace = sbvsc.leadingPosInner.absVal; - CGFloat bottomSpace = sbvsc.bottomPosInner.absVal; - CGFloat trailingSpace = sbvsc.trailingPosInner.absVal; - CGRect rect = sbvmyFrame.frame; - - - if (subviewSize != 0) - rect.size.width = subviewSize; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == lsc.heightSizeInner && !lsc.wrapContentHeight) - rect.size.height = [sbvsc.heightSizeInner measureWith:(selfSize.height - paddingVert) ]; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == lsc.widthSizeInner) - rect.size.width = [sbvsc.widthSizeInner measureWith:(selfSize.width - paddingHorz) ]; - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == sbvsc.widthSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width]; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == sbvsc.heightSizeInner) - rect.size.width = [sbvsc.widthSizeInner measureWith:rect.size.height ]; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal.view != nil && sbvsc.widthSizeInner.dimeRelaVal.view != self && sbvsc.widthSizeInner.dimeRelaVal.view != sbv) - { - rect.size.width = [sbvsc.widthSizeInner measureWith:sbvsc.widthSizeInner.dimeRelaVal.view.estimatedRect.size.width]; - } - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal.view != nil && sbvsc.heightSizeInner.dimeRelaVal.view != self && sbvsc.heightSizeInner.dimeRelaVal.view != sbv) - { - rect.size.height = [sbvsc.heightSizeInner measureWith:sbvsc.heightSizeInner.dimeRelaVal.view.estimatedRect.size.height]; - } - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - //如果高度是浮动的则需要调整高度。 - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - + CGFloat leadingMaxHeight = context->paddingTop; + CGFloat trailingMaxHeight = context->paddingTop; + CGFloat maxLayoutHeight = context->paddingTop; + CGFloat maxLayoutWidth = context->paddingLeading; + + //记录是否有子视图设置了对齐,如果设置了对齐就会在后面对每行子视图做对齐处理。 + BOOL subviewHasAlignment = NO; + NSMutableIndexSet *lineIndexes = [NSMutableIndexSet new]; + + for (NSInteger idx = 0; idx < context->subviewEngines.count; idx++) { + MyLayoutEngine *subviewEngine = context->subviewEngines[idx]; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + + CGFloat topSpacing = subviewTraits.topPosInner.measure; + CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; + CGFloat bottomSpacing = subviewTraits.bottomPosInner.measure; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; + + //只要有一个子视图设置了对齐,就会做对齐处理,否则不会,这里这样做是为了对后面的对齐计算做优化。 + subviewHasAlignment |= (MYVERTGRAVITY(subviewTraits.alignment) > MyGravity_Vert_Top); + //如果是RTL的场景则默认是右对齐的。 - if (sbvsc.isReverseFloat) - { + if (subviewTraits.isReverseFloat) { #ifdef DEBUG - //异常崩溃:当布局视图设置了noBoundaryLimit为YES时子视图不能设置逆向浮动 - NSCAssert(hasBoundaryLimit, @"Constraint exception!!, vertical float layout:%@ can not set noBoundaryLimit to YES when the subview:%@ set reverseFloat to YES.",self, sbv); + //异常崩溃:当布局视图设置了宽度值为MyLayoutSize.wrap时,子视图不能逆向浮动 + NSCAssert(!layoutTraits.widthSizeInner.wrapVal, @"Constraint exception!!, vertical float layout:%@ can not set width to MyLayoutSize.wrap when the subview:%@ set reverseFloat to YES.", self, subviewTraits.view); #endif - - CGPoint nextPoint = {selfSize.width - paddingTrailing, leadingLastYOffset}; - CGFloat leadingCandidateXBoundary = paddingLeading; - if (sbvsc.clearFloat) - { + + CGPoint nextPoint = {context->selfSize.width - context->paddingTrailing, leadingLastYOffset}; + CGFloat leadingCandidateXBoundary = context->paddingLeading; + if (subviewTraits.clearFloat) { //找到最底部的位置。 nextPoint.y = _myCGFloatMax(trailingMaxHeight, leadingLastYOffset); - CGPoint leadingPoint = [self myFindLeadingCandidatePoint:CGRectMake(selfSize.width - paddingTrailing, nextPoint.y, 0, CGFLOAT_MAX) width:leadingSpace + (sbvsc.weight != 0 ? 0 : rect.size.width) + trailingSpace leadingBoundary:paddingLeading leadingCandidateRects:leadingCandidateRects hasWeight:sbvsc.weight != 0 paddingTop:paddingTop]; - if (leadingPoint.y != CGFLOAT_MAX) - { + CGPoint leadingPoint = [self myFindLeadingCandidatePoint:CGRectMake(context->selfSize.width - context->paddingTrailing, nextPoint.y, 0, CGFLOAT_MAX) width:leadingSpacing + (subviewTraits.weight != 0 ? 0 : subviewEngine.width) + trailingSpacing leadingBoundary:context->paddingLeading leadingCandidateRects:leadingCandidateRects hasWeight:subviewTraits.weight != 0 paddingTop:context->paddingTop]; + if (leadingPoint.y != CGFLOAT_MAX) { nextPoint.y = _myCGFloatMax(trailingMaxHeight, leadingPoint.y); leadingCandidateXBoundary = leadingPoint.x; } - } - else - { + } else { //依次从后往前,每个都比较右边的。 - for (NSInteger i = trailingCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)trailingCandidateRects[i]).CGRectValue; - CGPoint leadingPoint = [self myFindLeadingCandidatePoint:candidateRect width:leadingSpace + (sbvsc.weight != 0 ? 0 : rect.size.width) + trailingSpace leadingBoundary:paddingLeading leadingCandidateRects:leadingCandidateRects hasWeight:sbvsc.weight != 0 paddingTop:paddingTop]; - if (leadingPoint.y != CGFLOAT_MAX) - { + for (NSInteger i = trailingCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)trailingCandidateRects[i]).CGRectValue; + CGPoint leadingPoint = [self myFindLeadingCandidatePoint:candidateRect width:leadingSpacing + (subviewTraits.weight != 0 ? 0 :subviewEngine.width) + trailingSpacing leadingBoundary:context->paddingLeading leadingCandidateRects:leadingCandidateRects hasWeight:subviewTraits.weight != 0 paddingTop:context->paddingTop]; + if (leadingPoint.y != CGFLOAT_MAX) { nextPoint = CGPointMake(CGRectGetMinX(candidateRect), _myCGFloatMax(nextPoint.y, leadingPoint.y)); leadingCandidateXBoundary = leadingPoint.x; break; } - + nextPoint.y = CGRectGetMaxY(candidateRect); } } - + //重新设置宽度 - if (sbvsc.weight != 0) - { - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:(nextPoint.x - leadingCandidateXBoundary + sbvsc.widthSizeInner.addVal) * sbvsc.weight - leadingSpace - trailingSpace sbvSize:rect.size selfLayoutSize:selfSize]; - - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == sbvsc.widthSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width]; - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - + if (subviewTraits.weight != 0.0) { + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:(nextPoint.x - leadingCandidateXBoundary + subviewTraits.widthSizeInner.addVal) * subviewTraits.weight - leadingSpacing - trailingSpacing subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + //特殊处理高度等于宽度,并且高度依赖宽度的情况。 + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + + //特殊处理高度包裹的场景 + if (subviewTraits.heightSizeInner.wrapVal) { + subviewEngine.height = [self mySubview:subviewTraits wrapHeightSizeFits:subviewEngine.size withContext:context]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } } - - - rect.origin.x = nextPoint.x - trailingSpace - rect.size.width; - rect.origin.y = _myCGFloatMin(nextPoint.y, maxHeight) + topSpace; - + + subviewEngine.leading = nextPoint.x - trailingSpacing - subviewEngine.width; + subviewEngine.top = _myCGFloatMin(nextPoint.y, maxLayoutHeight) + topSpacing; + //如果有智能边界线则找出所有智能边界线。 - if (!isEstimate && self.intelligentBorderline != nil) - { + if (!context->isEstimate && self.intelligentBorderline != nil) { //优先绘制左边和上边的。绘制左边的和上边的。 - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - MyBaseLayout *sbvl = (MyBaseLayout*)sbv; - if (!sbvl.notUseIntelligentBorderline) - { - sbvl.bottomBorderline = nil; - sbvl.topBorderline = nil; - sbvl.trailingBorderline = nil; - sbvl.leadingBorderline = nil; - - - if (_myCGFloatLess(rect.origin.x + rect.size.width + trailingSpace, selfSize.width - paddingTrailing)) - { - - sbvl.trailingBorderline = self.intelligentBorderline; + if ([subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + MyBaseLayout *sublayout = (MyBaseLayout *)subviewTraits.view; + if (!sublayout.notUseIntelligentBorderline) { + sublayout.bottomBorderline = nil; + sublayout.topBorderline = nil; + sublayout.trailingBorderline = nil; + sublayout.leadingBorderline = nil; + + if (_myCGFloatLess(subviewEngine.leading + subviewEngine.width + trailingSpacing, context->selfSize.width - context->paddingTrailing)) { + sublayout.trailingBorderline = self.intelligentBorderline; } - - if (_myCGFloatLess(rect.origin.y + rect.size.height + bottomSpace, selfSize.height - paddingBottom)) - { - sbvl.bottomBorderline = self.intelligentBorderline; + + if (_myCGFloatLess(subviewEngine.top + subviewEngine.height + bottomSpacing, context->selfSize.height - context->paddingBottom)) { + sublayout.bottomBorderline = self.intelligentBorderline; } - - if (_myCGFloatGreat(rect.origin.x, leadingCandidateXBoundary) && sbvl == sbs.lastObject) - { - sbvl.leadingBorderline = self.intelligentBorderline; + + if (_myCGFloatGreat(subviewEngine.leading, leadingCandidateXBoundary) && subviewEngine == context->subviewEngines.lastObject) { + sublayout.leadingBorderline = self.intelligentBorderline; } - } - } } - - + //这里有可能子视图本身的宽度会超过布局视图本身,但是我们的候选区域则不存储超过的宽度部分。 - CGRect cRect = CGRectMake(_myCGFloatMax((rect.origin.x - leadingSpace - horzSpace),paddingLeading), rect.origin.y - topSpace, _myCGFloatMin((rect.size.width + leadingSpace + trailingSpace),(selfSize.width - paddingHorz)), rect.size.height + topSpace + bottomSpace + vertSpace); - + CGRect cRect = CGRectMake(_myCGFloatMax((subviewEngine.leading - leadingSpacing - context->horzSpace), context->paddingLeading), subviewEngine.top - topSpacing, _myCGFloatMin((subviewEngine.width + leadingSpacing + trailingSpacing), (context->selfSize.width - context->paddingLeading - context->paddingTrailing)), subviewEngine.height + topSpacing + bottomSpacing + context->vertSpace); + //把新的候选区域添加到数组中去。并删除高度小于新候选区域的其他区域 - for (NSInteger i = trailingCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)trailingCandidateRects[i]).CGRectValue; - if (_myCGFloatLessOrEqual(CGRectGetMaxY(candidateRect), CGRectGetMaxY(cRect))) - { + for (NSInteger i = trailingCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)trailingCandidateRects[i]).CGRectValue; + if (_myCGFloatLessOrEqual(CGRectGetMaxY(candidateRect), CGRectGetMaxY(cRect))) { [trailingCandidateRects removeObjectAtIndex:i]; } } - + //删除左边高度小于新添加区域的顶部的候选区域 - for (NSInteger i = leadingCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)leadingCandidateRects[i]).CGRectValue; - + for (NSInteger i = leadingCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)leadingCandidateRects[i]).CGRectValue; + CGFloat candidateMaxY = CGRectGetMaxY(candidateRect); CGFloat candidateMaxX = CGRectGetMaxX(candidateRect); CGFloat cMinx = CGRectGetMinX(cRect); - - if (_myCGFloatLessOrEqual(candidateMaxY, CGRectGetMinY(cRect))) - { - [leadingCandidateRects removeObjectAtIndex:i]; - } - else if (_myCGFloatEqual(candidateMaxY, CGRectGetMaxY(cRect)) && _myCGFloatLessOrEqual(cMinx,candidateMaxX)) - { + + if (_myCGFloatLessOrEqual(candidateMaxY, CGRectGetMinY(cRect))) { + [leadingCandidateRects removeObjectAtIndex:i]; + } else if (_myCGFloatEqual(candidateMaxY, CGRectGetMaxY(cRect)) && _myCGFloatLessOrEqual(cMinx, candidateMaxX)) { [leadingCandidateRects removeObjectAtIndex:i]; cRect = CGRectUnion(cRect, candidateRect); cRect.size.width += candidateMaxX - cMinx; - } + } - + //记录每一行的最大子视图位置的索引值。 + if (trailingLastYOffset != subviewEngine.top - topSpacing) { + [lineIndexes addIndex:idx - 1]; } - - [trailingCandidateRects addObject:[NSValue valueWithCGRect:cRect]]; - trailingLastYOffset = rect.origin.y - topSpace; - - if (_myCGFloatGreat(rect.origin.y + rect.size.height + bottomSpace + vertSpace, trailingMaxHeight)) - trailingMaxHeight = rect.origin.y + rect.size.height + bottomSpace + vertSpace; - } - else - { - CGPoint nextPoint = {paddingLeading, trailingLastYOffset}; - CGFloat trailingCandidateXBoundary = selfSize.width - paddingTrailing; - + trailingLastYOffset = subviewEngine.top - topSpacing; + + if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace, trailingMaxHeight)) { + trailingMaxHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace; + } + } else { + CGPoint nextPoint = {context->paddingLeading, trailingLastYOffset}; + CGFloat trailingCandidateXBoundary = context->selfSize.width - context->paddingTrailing; + //如果是清除了浮动则直换行移动到最下面。 - if (sbvsc.clearFloat) - { + if (subviewTraits.clearFloat) { //找到最低点。 nextPoint.y = _myCGFloatMax(leadingMaxHeight, trailingLastYOffset); - - CGPoint trailingPoint = [self myFindTrailingCandidatePoint:CGRectMake(paddingLeading, nextPoint.y, 0, CGFLOAT_MAX) width:leadingSpace + (sbvsc.weight != 0 ? 0 : rect.size.width) + trailingSpace trailingBoundary:trailingCandidateXBoundary trailingCandidateRects:trailingCandidateRects hasWeight:sbvsc.weight != 0 paddingTop:paddingTop]; - if (trailingPoint.y != CGFLOAT_MAX) - { + + CGPoint trailingPoint = [self myFindTrailingCandidatePoint:CGRectMake(context->paddingLeading, nextPoint.y, 0, CGFLOAT_MAX) width:leadingSpacing + (subviewTraits.weight != 0 ? 0 : subviewEngine.width) + trailingSpacing trailingBoundary:trailingCandidateXBoundary trailingCandidateRects:trailingCandidateRects hasWeight:subviewTraits.weight != 0 paddingTop:context->paddingTop]; + if (trailingPoint.y != CGFLOAT_MAX) { nextPoint.y = _myCGFloatMax(leadingMaxHeight, trailingPoint.y); trailingCandidateXBoundary = trailingPoint.x; } - } - else - { - + } else { //依次从后往前,每个都比较右边的。 - for (NSInteger i = leadingCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)leadingCandidateRects[i]).CGRectValue; + for (NSInteger i = leadingCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)leadingCandidateRects[i]).CGRectValue; - CGPoint trailingPoint = [self myFindTrailingCandidatePoint:candidateRect width:leadingSpace + (sbvsc.weight != 0 ? 0 : rect.size.width) + trailingSpace trailingBoundary:selfSize.width - paddingTrailing trailingCandidateRects:trailingCandidateRects hasWeight:sbvsc.weight != 0 paddingTop:paddingTop]; - if (trailingPoint.y != CGFLOAT_MAX) - { + CGPoint trailingPoint = [self myFindTrailingCandidatePoint:candidateRect width:leadingSpacing + (subviewTraits.weight != 0 ? 0 : subviewEngine.width) + trailingSpacing trailingBoundary:context->selfSize.width - context->paddingTrailing trailingCandidateRects:trailingCandidateRects hasWeight:subviewTraits.weight != 0 paddingTop:context->paddingTop]; + if (trailingPoint.y != CGFLOAT_MAX) { nextPoint = CGPointMake(CGRectGetMaxX(candidateRect), _myCGFloatMax(nextPoint.y, trailingPoint.y)); trailingCandidateXBoundary = trailingPoint.x; break; + } else { //这里表明剩余空间放不下了。 + isBeyondFlag = YES; } - nextPoint.y = CGRectGetMaxY(candidateRect); } } - + //重新设置宽度 - if (sbvsc.weight != 0) - { + if (subviewTraits.weight != 0) { #ifdef DEBUG - //异常崩溃:当布局视图设置了noBoundaryLimit为YES时子视图不能设置weight大于0 - NSCAssert(hasBoundaryLimit, @"Constraint exception!!, vertical float layout:%@ can not set noBoundaryLimit to YES when the subview:%@ set weight big than zero.",self, sbv); + //异常崩溃:当布局视图设置了宽度值为MyLayoutSize.wrap 子视图不能设置weight大于0 + NSCAssert(!layoutTraits.widthSizeInner.wrapVal, @"Constraint exception!!, vertical float layout:%@ can not set width to MyLayoutSize.wrap when the subview:%@ set weight big than zero.", self, subviewTraits.view); #endif - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:(trailingCandidateXBoundary - nextPoint.x + sbvsc.widthSizeInner.addVal) * sbvsc.weight - leadingSpace - trailingSpace sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == sbvsc.widthSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width]; - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:(trailingCandidateXBoundary - nextPoint.x + subviewTraits.widthSizeInner.addVal) * subviewTraits.weight - leadingSpacing - trailingSpacing subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + //特殊处理高度等于宽度,并且高度依赖宽度的情况。 + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + + //特殊处理高度包裹的场景 + if (subviewTraits.heightSizeInner.wrapVal) { + subviewEngine.height = [self mySubview:subviewTraits wrapHeightSizeFits:subviewEngine.size withContext:context]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } } - - rect.origin.x = nextPoint.x + leadingSpace; - rect.origin.y = _myCGFloatMin(nextPoint.y,maxHeight) + topSpace; - - if (!isEstimate && self.intelligentBorderline != nil) - { + + subviewEngine.leading = nextPoint.x + leadingSpacing; + subviewEngine.top = _myCGFloatMin(nextPoint.y, maxLayoutHeight) + topSpacing; + + if (!context->isEstimate && self.intelligentBorderline != nil) { //优先绘制左边和上边的。绘制左边的和上边的。 - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - MyBaseLayout *sbvl = (MyBaseLayout*)sbv; - - if (!sbvl.notUseIntelligentBorderline) - { - sbvl.bottomBorderline = nil; - sbvl.topBorderline = nil; - sbvl.trailingBorderline = nil; - sbvl.leadingBorderline = nil; - - if (_myCGFloatLess(rect.origin.x + rect.size.width + trailingSpace, selfSize.width - paddingTrailing)) - { - - sbvl.trailingBorderline = self.intelligentBorderline; + if ([subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + MyBaseLayout *sublayout = (MyBaseLayout *)subviewTraits.view; + + if (!sublayout.notUseIntelligentBorderline) { + sublayout.bottomBorderline = nil; + sublayout.topBorderline = nil; + sublayout.trailingBorderline = nil; + sublayout.leadingBorderline = nil; + + if (_myCGFloatLess(subviewEngine.leading + subviewEngine.width + trailingSpacing, context->selfSize.width - context->paddingTrailing)) { + sublayout.trailingBorderline = self.intelligentBorderline; } - - if (_myCGFloatLess(rect.origin.y + rect.size.height + bottomSpace, selfSize.height - paddingBottom)) - { - sbvl.bottomBorderline = self.intelligentBorderline; + + if (_myCGFloatLess(subviewEngine.top + subviewEngine.height + bottomSpacing, context->selfSize.height - context->paddingBottom)) { + sublayout.bottomBorderline = self.intelligentBorderline; } - - } - } } - - - CGRect cRect = CGRectMake(rect.origin.x - leadingSpace, rect.origin.y - topSpace, _myCGFloatMin((rect.size.width + leadingSpace + trailingSpace + horzSpace),(selfSize.width - paddingHorz)), rect.size.height + topSpace + bottomSpace + vertSpace); - - + + CGRect cRect = CGRectMake(subviewEngine.leading - leadingSpacing, subviewEngine.top - topSpacing, _myCGFloatMin((subviewEngine.width + leadingSpacing + trailingSpacing + context->horzSpace), (context->selfSize.width - context->paddingLeading - context->paddingTrailing)), subviewEngine.height + topSpacing + bottomSpacing + context->vertSpace); + //把新添加到候选中去。并删除高度小于的候选键。和高度 - for (NSInteger i = leadingCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)leadingCandidateRects[i]).CGRectValue; + for (NSInteger i = leadingCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)leadingCandidateRects[i]).CGRectValue; - if (_myCGFloatLessOrEqual(CGRectGetMaxY(candidateRect), CGRectGetMaxY(cRect))) - { + if (_myCGFloatLessOrEqual(CGRectGetMaxY(candidateRect), CGRectGetMaxY(cRect))) { [leadingCandidateRects removeObjectAtIndex:i]; } } - + //删除右边高度小于新添加区域的顶部的候选区域 - for (NSInteger i = trailingCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)trailingCandidateRects[i]).CGRectValue; + for (NSInteger i = trailingCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)trailingCandidateRects[i]).CGRectValue; CGFloat candidateMaxY = CGRectGetMaxY(candidateRect); CGFloat candidateMinX = CGRectGetMinX(candidateRect); CGFloat cMaxX = CGRectGetMaxX(cRect); - if (_myCGFloatLessOrEqual(candidateMaxY, CGRectGetMinY(cRect))) - { + if (_myCGFloatLessOrEqual(candidateMaxY, CGRectGetMinY(cRect))) { [trailingCandidateRects removeObjectAtIndex:i]; - } - else if ( _myCGFloatEqual(candidateMaxY, CGRectGetMaxY(cRect)) && _myCGFloatLessOrEqual(candidateMinX, cMaxX)) - {//当右边的高度和cRect的高度相等,又有重合时表明二者可以合并为一个区域。 + } else if (_myCGFloatEqual(candidateMaxY, CGRectGetMaxY(cRect)) && _myCGFloatLessOrEqual(candidateMinX, cMaxX)) { //当右边的高度和cRect的高度相等,又有重合时表明二者可以合并为一个区域。 [trailingCandidateRects removeObjectAtIndex:i]; cRect = CGRectUnion(cRect, candidateRect); cRect.size.width += cMaxX - candidateMinX; //要加上重叠部分来增加宽度,否则会出现宽度不正确的问题。 } } - + + //记录每一行的最大子视图位置的索引值。 + if (leadingLastYOffset != subviewEngine.top - topSpacing) { + [lineIndexes addIndex:idx - 1]; + } [leadingCandidateRects addObject:[NSValue valueWithCGRect:cRect]]; - leadingLastYOffset = rect.origin.y - topSpace; - - if (_myCGFloatGreat(rect.origin.y + rect.size.height + bottomSpace + vertSpace, leadingMaxHeight)) - leadingMaxHeight = rect.origin.y + rect.size.height + bottomSpace + vertSpace; - + leadingLastYOffset = subviewEngine.top - topSpacing; + + if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace, leadingMaxHeight)) { + leadingMaxHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace; + } } - - if (_myCGFloatGreat(rect.origin.y + rect.size.height + bottomSpace + vertSpace, maxHeight)) - maxHeight = rect.origin.y + rect.size.height + bottomSpace + vertSpace; - - if (_myCGFloatGreat(rect.origin.x + rect.size.width + trailingSpace + horzSpace, maxWidth)) - maxWidth = rect.origin.x + rect.size.width + trailingSpace + horzSpace; - - sbvmyFrame.frame = rect; - - } - - if (sbs.count > 0) - { - maxHeight -= vertSpace; - maxWidth -= horzSpace; - } - - maxHeight += paddingBottom; - maxWidth += paddingTrailing; - - if (!hasBoundaryLimit) - selfSize.width = maxWidth; - - if (lsc.wrapContentHeight) - selfSize.height = maxHeight; - else - { - CGFloat addYPos = 0; - MyGravity mgvert = lsc.gravity & MyGravity_Horz_Mask; - if (mgvert == MyGravity_Vert_Center) - { - addYPos = (selfSize.height - maxHeight) / 2; + + if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace, maxLayoutHeight)) { + maxLayoutHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace; } - else if (mgvert == MyGravity_Vert_Bottom) - { - addYPos = selfSize.height - maxHeight; + if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace, maxLayoutWidth)) { + maxLayoutWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace; } - - if (addYPos != 0) - { - for (int i = 0; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - - sbv.myFrame.top += addYPos; - } + } + + if (context->subviewEngines.count > 0) { + maxLayoutHeight -= context->vertSpace; + maxLayoutWidth -= context->horzSpace; + } + + maxLayoutWidth += context->paddingTrailing; + if (layoutTraits.widthSizeInner.wrapVal) { + //只有在设置了最大宽度限制并且超出了才认为最大宽度是限制宽度,否则是最大子视图宽度。 + if (context->selfSize.width == CGFLOAT_MAX || !isBeyondFlag) { + context->selfSize.width = maxLayoutWidth; } - } - - return selfSize; -} --(CGSize)myLayoutSubviewsForHorz:(CGSize)selfSize sbs:(NSArray*)sbs isEstimate:(BOOL)isEstimate lsc:(MyFloatLayout*)lsc -{ - //对于水平浮动布局来说,最终是从左到右排列,而对于RTL则是从右到左排列,因此这里先抽象定义头尾的概念,然后最后再计算时统一将抽象位置转化为CGRect的左边值。 + maxLayoutHeight += context->paddingBottom; + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:maxLayoutHeight subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - CGFloat paddingHorz = paddingLeading + paddingTrailing; - CGFloat paddingVert = paddingTop + paddingBottom; - - BOOL hasBoundaryLimit = YES; - if (lsc.wrapContentHeight && lsc.noBoundaryLimit) - hasBoundaryLimit = NO; - - //如果没有边界限制我们将高度设置为最大。。 - if (!hasBoundaryLimit) - selfSize.height = CGFLOAT_MAX; - - //遍历所有的子视图,查看是否有子视图的宽度会比视图自身要宽,如果有且有包裹属性则扩充自身的宽度 - if (lsc.wrapContentHeight && hasBoundaryLimit) - { - CGFloat maxContentHeight = selfSize.height - paddingVert; - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - CGFloat topSpace = sbvsc.topPosInner.absVal; - CGFloat bottomSpace = sbvsc.bottomPosInner.absVal; - CGRect rect = sbvmyFrame.frame; - - - //这里有可能设置了固定的高度 - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - //有可能高度是和他的宽度相等。 - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == sbvsc.widthSizeInner) - { - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == lsc.widthSizeInner) - rect.size.width = [sbvsc.widthSizeInner measureWith:(selfSize.width - paddingHorz) ]; - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width]; + if (context->selfSize.height != maxLayoutHeight) { + if (context->vertGravity != MyGravity_None) { + CGFloat addYPos = 0.0; + if (context->vertGravity == MyGravity_Vert_Center) { + addYPos = (context->selfSize.height - maxLayoutHeight) / 2; + } else if (context->vertGravity == MyGravity_Vert_Bottom) { + addYPos = context->selfSize.height - maxLayoutHeight; } - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - if (_myCGFloatGreat(topSpace + rect.size.height + bottomSpace, maxContentHeight) && - (sbvsc.heightSizeInner.dimeRelaVal == nil || sbvsc.heightSizeInner.dimeRelaVal != lsc.heightSizeInner) && - sbvsc.weight == 0) - { - maxContentHeight = topSpace + rect.size.height + bottomSpace; + + if (addYPos != 0.0) { + for (int i = 0; i < context->subviewEngines.count; i++) { + context->subviewEngines[i].top += addYPos; + } } } - - selfSize.height = paddingVert + maxContentHeight; } - - //支持浮动垂直间距。 - CGFloat horzSpace = lsc.subviewHSpace; - CGFloat vertSpace = lsc.subviewVSpace; - CGFloat subviewSize = ((MyFloatLayoutViewSizeClass*)self.myCurrentSizeClass).subviewSize; - if (subviewSize != 0) - { -#ifdef DEBUG - //异常崩溃:当布局视图设置了noBoundaryLimit为YES时,不能设置最小垂直间距。 - NSCAssert(hasBoundaryLimit, @"Constraint exception!!, horizontal float layout:%@ can not set noBoundaryLimit to YES when call setSubviewsSize:minSpace:maxSpace method",self); -#endif - - CGFloat minSpace = ((MyFloatLayoutViewSizeClass*)self.myCurrentSizeClass).minSpace; - CGFloat maxSpace = ((MyFloatLayoutViewSizeClass*)self.myCurrentSizeClass).maxSpace; - - NSInteger rowCount = floor((selfSize.height - paddingVert + minSpace) / (subviewSize + minSpace)); - if (rowCount > 1) - { - vertSpace = (selfSize.height - paddingVert - subviewSize * rowCount)/(rowCount - 1); - - if (_myCGFloatGreat(vertSpace,maxSpace)) - { - vertSpace = maxSpace; - - subviewSize = (selfSize.height - paddingVert - vertSpace * (rowCount - 1)) / rowCount; - - } - + + //如果有子视图设置了对齐属性,那么就要对处在同一行内的子视图进行对齐设置。 + //对齐的规则是以行内最高的子视图作为参考的对象,其他的子视图按照行内最高子视图进行垂直对齐的调整。 + if (subviewHasAlignment) { + //最后一行。 + if (context->subviewEngines.count > 0) { + [lineIndexes addIndex:context->subviewEngines.count - 1]; } + __block NSInteger lineFirstIndex = 0; + [lineIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + + BOOL lineHasAlignment = NO; + + //计算每行内的最高的子视图,作为行对齐的标准。 + CGFloat lineMaxHeight = 0; + for (NSInteger i = lineFirstIndex; i <= idx; i++) { + MyLayoutEngine *subviewEngine = context->subviewEngines[i]; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + if (subviewEngine.height > lineMaxHeight) { + lineMaxHeight = subviewEngine.height; + } + lineHasAlignment |= (MYVERTGRAVITY(subviewTraits.alignment) > MyGravity_Vert_Top); + } + + //设置行内的对齐 + if (lineHasAlignment) { + CGFloat baselinePos = CGFLOAT_MAX; + for (NSInteger i = lineFirstIndex; i <= idx; i++) { + MyLayoutEngine *subviewEngine = context->subviewEngines[i]; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + MyGravity subviewVertAlignment = MYVERTGRAVITY(subviewTraits.alignment); + UIFont *subviewFont = nil; + if (subviewVertAlignment == MyGravity_Vert_Baseline) { + subviewFont = [self myGetSubviewFont:subviewTraits.view]; + if (subviewFont == nil) { + subviewVertAlignment = MyGravity_Vert_Top; + } + } + + switch (subviewVertAlignment) { + case MyGravity_Vert_Center: + subviewEngine.top += (lineMaxHeight - subviewEngine.height) / 2.0; + break; + case MyGravity_Vert_Bottom: + subviewEngine.top += (lineMaxHeight - subviewEngine.height); + break; + case MyGravity_Vert_Fill: + subviewEngine.height = lineMaxHeight; + break; + case MyGravity_Vert_Stretch: { + if (subviewTraits.heightSizeInner.val == nil || (subviewTraits.heightSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { + subviewEngine.height = lineMaxHeight; + } + } break; + case MyGravity_Vert_Baseline: { + if (baselinePos == CGFLOAT_MAX) { + baselinePos = subviewEngine.top + (subviewEngine.height - subviewFont.lineHeight) / 2.0 + subviewFont.ascender; + } else { + subviewEngine.top = baselinePos - subviewFont.ascender - (subviewEngine.height - subviewFont.lineHeight) / 2; + } + } break; + default: + break; + } + } + } + + lineFirstIndex = idx + 1; + }]; } - - +} + +- (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { + MyFloatLayoutTraits *layoutTraits = (MyFloatLayoutTraits*)context->layoutViewEngine.currentSizeClass; + NSArray *subviewEngines = context->subviewEngines; + //如果没有边界限制我们将高度设置为最大。。 + BOOL isBeyondFlag = NO; //子视图是否超出剩余空间需要换行。 + if (layoutTraits.heightSizeInner.wrapVal) { + //如果有最大限制则取最大值,解决那种高度自适应,但是有最大值需要换行的情况。 + context->selfSize.height = [self myGetBoundLimitMeasure:layoutTraits.heightSizeInner.uBoundValInner subview:self anchorType:layoutTraits.heightSizeInner.anchorType subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size isUBound:YES]; + } + + //支持浮动垂直间距。 + CGFloat subviewHeight = [layoutTraits.flexSpace calcMaxMinSubviewSizeForContent:context->selfSize.height paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom space:&context->vertSpace]; + + //设置子视图的宽度和高度。 + [self myCalcSubviewsSize:subviewHeight isWidth:NO withContext:context]; + //上边候选区域数组,保存的是CGRect值。 NSMutableArray *topCandidateRects = [NSMutableArray new]; //为了计算方便总是把最上边的个虚拟区域作为一个候选区域 - [topCandidateRects addObject:[NSValue valueWithCGRect:CGRectMake(paddingLeading, paddingTop,CGFLOAT_MAX,0)]]; - - //右边候选区域数组,保存的是CGRect值。 + [topCandidateRects addObject:[NSValue valueWithCGRect:CGRectMake(context->paddingLeading, context->paddingTop, CGFLOAT_MAX, 0)]]; + + //下边候选区域数组,保存的是CGRect值。 NSMutableArray *bottomCandidateRects = [NSMutableArray new]; //为了计算方便总是把最下边的个虚拟区域作为一个候选区域,如果没有边界限制则 - [bottomCandidateRects addObject:[NSValue valueWithCGRect:CGRectMake(paddingLeading, selfSize.height - paddingBottom, CGFLOAT_MAX, 0)]]; - + [bottomCandidateRects addObject:[NSValue valueWithCGRect:CGRectMake(context->paddingLeading, context->selfSize.height - context->paddingBottom, CGFLOAT_MAX, 0)]]; + //分别记录上边和下边的最后一个视图在X轴的偏移量 - CGFloat topLastXOffset = paddingLeading; - CGFloat bottomLastXOffset = paddingLeading; - + CGFloat topLastXOffset = context->paddingLeading; + CGFloat bottomLastXOffset = context->paddingLeading; + //分别记录上下边和全局浮动视图的最宽占用的X轴的值 - CGFloat topMaxWidth = paddingLeading; - CGFloat bottomMaxWidth = paddingLeading; - CGFloat maxWidth = paddingLeading; - CGFloat maxHeight = paddingTop; - - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - CGFloat topSpace = sbvsc.topPosInner.absVal; - CGFloat leadingSpace = sbvsc.leadingPosInner.absVal; - CGFloat bottomSpace = sbvsc.bottomPosInner.absVal; - CGFloat trailingSpace = sbvsc.trailingPosInner.absVal; - CGRect rect = sbvmyFrame.frame; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - if (subviewSize != 0) - rect.size.height = subviewSize; - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == lsc.heightSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:(selfSize.height - paddingVert) ]; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == lsc.widthSizeInner && !lsc.wrapContentWidth) - rect.size.width = [sbvsc.widthSizeInner measureWith:(selfSize.width - paddingHorz) ]; - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == sbvsc.widthSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width]; - - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == sbvsc.heightSizeInner) - rect.size.width = [sbvsc.widthSizeInner measureWith:rect.size.height]; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal.view != nil && sbvsc.widthSizeInner.dimeRelaVal.view != self && sbvsc.widthSizeInner.dimeRelaVal.view != sbv) - { - rect.size.width = [sbvsc.widthSizeInner measureWith:sbvsc.widthSizeInner.dimeRelaVal.view.estimatedRect.size.width]; - } - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal.view != nil && sbvsc.heightSizeInner.dimeRelaVal.view != self && sbvsc.heightSizeInner.dimeRelaVal.view != sbv) - { - rect.size.height = [sbvsc.heightSizeInner measureWith:sbvsc.heightSizeInner.dimeRelaVal.view.estimatedRect.size.height]; - } - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - - //如果高度是浮动的则需要调整高度。 - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - + CGFloat topMaxWidth = context->paddingLeading; + CGFloat bottomMaxWidth = context->paddingLeading; + CGFloat maxLayoutWidth = context->paddingLeading; + CGFloat maxLayoutHeight = context->paddingTop; + + //记录是否有子视图设置了对齐,如果设置了对齐就会在后面对每行子视图做对齐处理。 + BOOL subviewHasAlignment = NO; + NSMutableIndexSet *lineIndexes = [NSMutableIndexSet new]; + + for (NSInteger idx = 0; idx < subviewEngines.count; idx++) { + MyLayoutEngine *subviewEngine = subviewEngines[idx]; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + + CGFloat topSpacing = subviewTraits.topPosInner.measure; + CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; + CGFloat bottomSpacing = subviewTraits.bottomPosInner.measure; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; - if (sbvsc.reverseFloat) - { + //只要有一个子视图设置了对齐,就会做对齐处理,否则不会,这里这样做是为了对后面的对齐计算做优化。 + subviewHasAlignment |= (MYHORZGRAVITY(subviewTraits.alignment) > MyGravity_Horz_Left); + + if (subviewTraits.reverseFloat) { #ifdef DEBUG - //异常崩溃:当布局视图设置了noBoundaryLimit为YES时子视图不能设置逆向浮动 - NSCAssert(hasBoundaryLimit, @"Constraint exception!!, horizontal float layout:%@ can not set noBoundaryLimit to YES when the subview:%@ set reverseFloat to YES.",self, sbv); + //异常崩溃:当布局视图设置了高度为MyLayoutSize.wrap时子视图不能设置逆向浮动 + NSCAssert(!layoutTraits.heightSizeInner.wrapVal, @"Constraint exception!!, horizontal float layout:%@ can not set height to wrap when the subview:%@ set reverseFloat to YES.", self, subviewTraits.view); #endif - - CGPoint nextPoint = {topLastXOffset, selfSize.height - paddingBottom}; - CGFloat topCandidateYBoundary = paddingTop; - if (sbvsc.clearFloat) - { + + CGPoint nextPoint = {topLastXOffset, context->selfSize.height - context->paddingBottom}; + CGFloat topCandidateYBoundary = context->paddingTop; + if (subviewTraits.clearFloat) { //找到最底部的位置。 nextPoint.x = _myCGFloatMax(bottomMaxWidth, topLastXOffset); - CGPoint topPoint = [self myFindTopCandidatePoint:CGRectMake(nextPoint.x, selfSize.height - paddingBottom, CGFLOAT_MAX, 0) height:topSpace + (sbvsc.weight != 0 ? 0 : rect.size.height) + bottomSpace topBoundary:topCandidateYBoundary topCandidateRects:topCandidateRects hasWeight:sbvsc.weight != 0 paddingLeading:paddingLeading]; - if (topPoint.x != CGFLOAT_MAX) - { + CGPoint topPoint = [self myFindTopCandidatePoint:CGRectMake(nextPoint.x, context->selfSize.height - context->paddingBottom, CGFLOAT_MAX, 0) height:topSpacing + (subviewTraits.weight != 0 ? 0 : subviewEngine.height) + bottomSpacing topBoundary:topCandidateYBoundary topCandidateRects:topCandidateRects hasWeight:subviewTraits.weight != 0 paddingLeading:context->paddingLeading]; + if (topPoint.x != CGFLOAT_MAX) { nextPoint.x = _myCGFloatMax(bottomMaxWidth, topPoint.x); topCandidateYBoundary = topPoint.y; } - } - else - { + } else { //依次从后往前,每个都比较右边的。 - for (NSInteger i = bottomCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)bottomCandidateRects[i]).CGRectValue; - - CGPoint topPoint = [self myFindTopCandidatePoint:candidateRect height:topSpace + (sbvsc.weight != 0 ? 0 : rect.size.height) + bottomSpace topBoundary:paddingTop topCandidateRects:topCandidateRects hasWeight:sbvsc.weight != 0 paddingLeading:paddingLeading]; - if (topPoint.x != CGFLOAT_MAX) - { - nextPoint = CGPointMake(_myCGFloatMax(nextPoint.x, topPoint.x),CGRectGetMinY(candidateRect)); + for (NSInteger i = bottomCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)bottomCandidateRects[i]).CGRectValue; + + CGPoint topPoint = [self myFindTopCandidatePoint:candidateRect height:topSpacing + (subviewTraits.weight != 0 ? 0 : subviewEngine.height) + bottomSpacing topBoundary:context->paddingTop topCandidateRects:topCandidateRects hasWeight:subviewTraits.weight != 0 paddingLeading:context->paddingLeading]; + if (topPoint.x != CGFLOAT_MAX) { + nextPoint = CGPointMake(_myCGFloatMax(nextPoint.x, topPoint.x), CGRectGetMinY(candidateRect)); topCandidateYBoundary = topPoint.y; break; } - + nextPoint.x = CGRectGetMaxX(candidateRect); } } - - if (sbvsc.weight != 0) - { - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:(nextPoint.y - topCandidateYBoundary + sbvsc.heightSizeInner.addVal) * sbvsc.weight - topSpace - bottomSpace sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == sbvsc.heightSizeInner) - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:[sbvsc.widthSizeInner measureWith: rect.size.height] sbvSize:rect.size selfLayoutSize:selfSize]; - + + if (subviewTraits.weight != 0.0) { + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:(nextPoint.y - topCandidateYBoundary + subviewTraits.heightSizeInner.addVal) * subviewTraits.weight - topSpacing - bottomSpacing subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + //特殊处理宽度等于高度的问题。 + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:[subviewTraits.widthSizeInner measureWith:subviewEngine.height] subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } } - - - rect.origin.y = nextPoint.y - bottomSpace - rect.size.height; - rect.origin.x = _myCGFloatMin(nextPoint.x, maxWidth) + leadingSpace; - + + subviewEngine.top = nextPoint.y - bottomSpacing - subviewEngine.height; + subviewEngine.leading = _myCGFloatMin(nextPoint.x, maxLayoutWidth) + leadingSpacing; + //如果有智能边界线则找出所有智能边界线。 - if (!isEstimate && self.intelligentBorderline != nil) - { + if (!context->isEstimate && self.intelligentBorderline != nil) { //优先绘制左边和上边的。绘制左边的和上边的。 - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - MyBaseLayout *sbvl = (MyBaseLayout*)sbv; - - if (!sbvl.notUseIntelligentBorderline) - { - sbvl.bottomBorderline = nil; - sbvl.topBorderline = nil; - sbvl.trailingBorderline = nil; - sbvl.leadingBorderline = nil; - + if ([subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + MyBaseLayout *sublayout = (MyBaseLayout *)subviewTraits.view; + + if (!sublayout.notUseIntelligentBorderline) { + sublayout.bottomBorderline = nil; + sublayout.topBorderline = nil; + sublayout.trailingBorderline = nil; + sublayout.leadingBorderline = nil; + //如果自己的上边和左边有子视图。 - if (_myCGFloatLess(rect.origin.x + rect.size.width + trailingSpace, selfSize.width - paddingTrailing)) - { - sbvl.trailingBorderline = self.intelligentBorderline; + if (_myCGFloatLess(subviewEngine.leading + subviewEngine.width + trailingSpacing, context->selfSize.width - context->paddingTrailing)) { + sublayout.trailingBorderline = self.intelligentBorderline; } - - if (_myCGFloatLess(rect.origin.y + rect.size.height + bottomSpace, selfSize.height - paddingBottom)) - { - sbvl.bottomBorderline = self.intelligentBorderline; + + if (_myCGFloatLess(subviewEngine.top + subviewEngine.height + bottomSpacing, context->selfSize.height - context->paddingBottom)) { + sublayout.bottomBorderline = self.intelligentBorderline; } - - if (_myCGFloatGreat(rect.origin.y, topCandidateYBoundary) && sbvl == sbs.lastObject) - { - sbvl.topBorderline = self.intelligentBorderline; + + if (_myCGFloatGreat(subviewEngine.top, topCandidateYBoundary) && subviewEngine == subviewEngines.lastObject) { + sublayout.topBorderline = self.intelligentBorderline; } - } - } } - - + //这里有可能子视图本身的高度会超过布局视图本身,但是我们的候选区域则不存储超过的高度部分。 - CGRect cRect = CGRectMake(rect.origin.x - leadingSpace, _myCGFloatMax((rect.origin.y - topSpace - vertSpace),paddingTop), rect.size.width + leadingSpace + trailingSpace + horzSpace, _myCGFloatMin((rect.size.height + topSpace + bottomSpace),(selfSize.height - paddingVert))); - + CGRect cRect = CGRectMake(subviewEngine.leading - leadingSpacing, _myCGFloatMax((subviewEngine.top - topSpacing - context->vertSpace), context->paddingTop), subviewEngine.width + leadingSpacing + trailingSpacing + context->horzSpace, _myCGFloatMin((subviewEngine.height + topSpacing + bottomSpacing), (context->selfSize.height - context->paddingTop - context->paddingBottom))); + //把新的候选区域添加到数组中去。并删除高度小于新候选区域的其他区域 - for (NSInteger i = bottomCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)bottomCandidateRects[i]).CGRectValue; - if (_myCGFloatLessOrEqual(CGRectGetMaxX(candidateRect), CGRectGetMaxX(cRect))) - { + for (NSInteger i = bottomCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)bottomCandidateRects[i]).CGRectValue; + if (_myCGFloatLessOrEqual(CGRectGetMaxX(candidateRect), CGRectGetMaxX(cRect))) { [bottomCandidateRects removeObjectAtIndex:i]; } } - + //删除顶部宽度小于新添加区域的顶部的候选区域 - for (NSInteger i = topCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)topCandidateRects[i]).CGRectValue; + for (NSInteger i = topCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)topCandidateRects[i]).CGRectValue; CGFloat candidateMaxX = CGRectGetMaxX(candidateRect); CGFloat candidateMaxY = CGRectGetMaxY(candidateRect); CGFloat cMinY = CGRectGetMinY(cRect); - - if (_myCGFloatLessOrEqual(candidateMaxX, CGRectGetMinX(cRect))) - { + + if (_myCGFloatLessOrEqual(candidateMaxX, CGRectGetMinX(cRect))) { [topCandidateRects removeObjectAtIndex:i]; - } - else if (_myCGFloatEqual(candidateMaxX, CGRectGetMaxX(cRect)) && _myCGFloatLessOrEqual(cMinY,candidateMaxY)) - { + } else if (_myCGFloatEqual(candidateMaxX, CGRectGetMaxX(cRect)) && _myCGFloatLessOrEqual(cMinY, candidateMaxY)) { [topCandidateRects removeObjectAtIndex:i]; cRect = CGRectUnion(cRect, candidateRect); cRect.size.height += candidateMaxY - cMinY; } + } + //记录每一列的最大子视图位置的索引值。 + if (bottomLastXOffset != subviewEngine.leading - leadingSpacing) { + [lineIndexes addIndex:idx - 1]; } - + [bottomCandidateRects addObject:[NSValue valueWithCGRect:cRect]]; - bottomLastXOffset = rect.origin.x - leadingSpace; - - if (_myCGFloatGreat(rect.origin.x + rect.size.width + trailingSpace + horzSpace, bottomMaxWidth)) - bottomMaxWidth = rect.origin.x + rect.size.width + trailingSpace + horzSpace; - } - else - { - CGPoint nextPoint = {bottomLastXOffset,paddingTop}; - CGFloat bottomCandidateYBoundary = selfSize.height - paddingBottom; + bottomLastXOffset = subviewEngine.leading - leadingSpacing; + + if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace, bottomMaxWidth)) { + bottomMaxWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace; + } + } else { + CGPoint nextPoint = {bottomLastXOffset, context->paddingTop}; + CGFloat bottomCandidateYBoundary = context->selfSize.height - context->paddingBottom; //如果是清除了浮动则直换行移动到最下面。 - if (sbvsc.clearFloat) - { + if (subviewTraits.clearFloat) { //找到最低点。 nextPoint.x = _myCGFloatMax(topMaxWidth, bottomLastXOffset); - - CGPoint bottomPoint = [self myFindBottomCandidatePoint:CGRectMake(nextPoint.x, paddingTop,CGFLOAT_MAX,0) height:topSpace + (sbvsc.weight != 0 ? 0: rect.size.height) + bottomSpace bottomBoundary:bottomCandidateYBoundary bottomCandidateRects:bottomCandidateRects hasWeight:sbvsc.weight != 0 paddingLeading:paddingLeading]; - if (bottomPoint.x != CGFLOAT_MAX) - { + + CGPoint bottomPoint = [self myFindBottomCandidatePoint:CGRectMake(nextPoint.x, context->paddingTop, CGFLOAT_MAX, 0) height:topSpacing + (subviewTraits.weight != 0 ? 0 : subviewEngine.height) + bottomSpacing bottomBoundary:bottomCandidateYBoundary bottomCandidateRects:bottomCandidateRects hasWeight:subviewTraits.weight != 0 paddingLeading:context->paddingLeading]; + if (bottomPoint.x != CGFLOAT_MAX) { nextPoint.x = _myCGFloatMax(topMaxWidth, bottomPoint.x); bottomCandidateYBoundary = bottomPoint.y; } - } - else - { - + } else { + //依次从后往前,每个都比较右边的。 - for (NSInteger i = topCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)topCandidateRects[i]).CGRectValue; - CGPoint bottomPoint = [self myFindBottomCandidatePoint:candidateRect height:topSpace + (sbvsc.weight != 0 ? 0: rect.size.height) + bottomSpace bottomBoundary:selfSize.height - paddingBottom bottomCandidateRects:bottomCandidateRects hasWeight:sbvsc.weight != 0 paddingLeading:paddingLeading]; - if (bottomPoint.x != CGFLOAT_MAX) - { - nextPoint = CGPointMake(_myCGFloatMax(nextPoint.x, bottomPoint.x),CGRectGetMaxY(candidateRect)); + for (NSInteger i = topCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)topCandidateRects[i]).CGRectValue; + CGPoint bottomPoint = [self myFindBottomCandidatePoint:candidateRect height:topSpacing + (subviewTraits.weight != 0 ? 0 : subviewEngine.height) + bottomSpacing bottomBoundary:context->selfSize.height - context->paddingBottom bottomCandidateRects:bottomCandidateRects hasWeight:subviewTraits.weight != 0 paddingLeading:context->paddingLeading]; + if (bottomPoint.x != CGFLOAT_MAX) { + nextPoint = CGPointMake(_myCGFloatMax(nextPoint.x, bottomPoint.x), CGRectGetMaxY(candidateRect)); bottomCandidateYBoundary = bottomPoint.y; break; + } else { + isBeyondFlag = YES; } - nextPoint.x = CGRectGetMaxX(candidateRect); } } - - if (sbvsc.weight != 0) - { - + + if (subviewTraits.weight != 0.0) { + #ifdef DEBUG - //异常崩溃:当布局视图设置了noBoundaryLimit为YES时子视图不能设置weight大于0 - NSCAssert(hasBoundaryLimit, @"Constraint exception!!, horizontal float layout:%@ can not set noBoundaryLimit to YES when the subview:%@ set weight big than zero.",self, sbv); + //异常崩溃:当布局视图设置了高度为wrap时子视图不能设置weight大于0 + NSCAssert(!layoutTraits.heightSizeInner.wrapVal, @"Constraint exception!!, horizontal float layout:%@ can not set height to wrap when the subview:%@ set weight big than zero.", self, subviewTraits.view); #endif - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:(bottomCandidateYBoundary - nextPoint.y + sbvsc.heightSizeInner.addVal) * sbvsc.weight - topSpace - bottomSpace sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == sbvsc.heightSizeInner) - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:[sbvsc.widthSizeInner measureWith: rect.size.height] sbvSize:rect.size selfLayoutSize:selfSize]; - + + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:(bottomCandidateYBoundary - nextPoint.y + subviewTraits.heightSizeInner.addVal) * subviewTraits.weight - topSpacing - bottomSpacing subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:[subviewTraits.widthSizeInner measureWith:subviewEngine.height] subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } } - - rect.origin.y = nextPoint.y + topSpace; - rect.origin.x = _myCGFloatMin(nextPoint.x,maxWidth) + leadingSpace; - + + subviewEngine.top = nextPoint.y + topSpacing; + subviewEngine.leading = _myCGFloatMin(nextPoint.x, maxLayoutWidth) + leadingSpacing; + //如果有智能边界线则找出所有智能边界线。 - if (!isEstimate && self.intelligentBorderline != nil) - { + if (!context->isEstimate && self.intelligentBorderline != nil) { //优先绘制左边和上边的。绘制左边的和上边的。 - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - MyBaseLayout *sbvl = (MyBaseLayout*)sbv; - if (!sbvl.notUseIntelligentBorderline) - { - sbvl.bottomBorderline = nil; - sbvl.topBorderline = nil; - sbvl.trailingBorderline = nil; - sbvl.leadingBorderline = nil; - + if ([subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + MyBaseLayout *sublayout = (MyBaseLayout *)subviewTraits.view; + if (!sublayout.notUseIntelligentBorderline) { + sublayout.bottomBorderline = nil; + sublayout.topBorderline = nil; + sublayout.trailingBorderline = nil; + sublayout.leadingBorderline = nil; + //如果自己的上边和左边有子视图。 - if (_myCGFloatLess(rect.origin.x + rect.size.width + trailingSpace,selfSize.width - paddingTrailing)) - { - sbvl.trailingBorderline = self.intelligentBorderline; + if (_myCGFloatLess(subviewEngine.leading + subviewEngine.width + trailingSpacing, context->selfSize.width - context->paddingTrailing)) { + sublayout.trailingBorderline = self.intelligentBorderline; } - - if (_myCGFloatLess(rect.origin.y + rect.size.height + bottomSpace, selfSize.height - paddingBottom)) - { - sbvl.bottomBorderline = self.intelligentBorderline; + + if (_myCGFloatLess(subviewEngine.top + subviewEngine.height + bottomSpacing, context->selfSize.height - context->paddingBottom)) { + sublayout.bottomBorderline = self.intelligentBorderline; } } - } } - - - CGRect cRect = CGRectMake(rect.origin.x - leadingSpace, rect.origin.y - topSpace,rect.size.width + leadingSpace + trailingSpace + horzSpace,_myCGFloatMin((rect.size.height + topSpace + bottomSpace + vertSpace),(selfSize.height - paddingVert))); - - + + CGRect cRect = CGRectMake(subviewEngine.leading - leadingSpacing, subviewEngine.top - topSpacing, subviewEngine.width + leadingSpacing + trailingSpacing + context->horzSpace, _myCGFloatMin((subviewEngine.height + topSpacing + bottomSpacing + context->vertSpace), (context->selfSize.height - context->paddingTop - context->paddingBottom))); + //把新添加到候选中去。并删除宽度小于的最新候选区域的候选区域 - for (NSInteger i = topCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)topCandidateRects[i]).CGRectValue; + for (NSInteger i = topCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)topCandidateRects[i]).CGRectValue; - if (_myCGFloatLessOrEqual(CGRectGetMaxX(candidateRect), CGRectGetMaxX(cRect))) - { + if (_myCGFloatLessOrEqual(CGRectGetMaxX(candidateRect), CGRectGetMaxX(cRect))) { [topCandidateRects removeObjectAtIndex:i]; } } - + //删除顶部宽度小于新添加区域的顶部的候选区域 - for (NSInteger i = bottomCandidateRects.count - 1; i >= 0; i--) - { - CGRect candidateRect = ((NSValue*)bottomCandidateRects[i]).CGRectValue; - + for (NSInteger i = bottomCandidateRects.count - 1; i >= 0; i--) { + CGRect candidateRect = ((NSValue *)bottomCandidateRects[i]).CGRectValue; + CGFloat candidateMaxX = CGRectGetMaxX(candidateRect); CGFloat candidateMinY = CGRectGetMinY(candidateRect); CGFloat cMaxY = CGRectGetMaxY(cRect); - if (_myCGFloatLessOrEqual(candidateMaxX, CGRectGetMinX(cRect))) - { + if (_myCGFloatLessOrEqual(candidateMaxX, CGRectGetMinX(cRect))) { [bottomCandidateRects removeObjectAtIndex:i]; - } - else if ( _myCGFloatEqual(candidateMaxX, CGRectGetMaxX(cRect)) && _myCGFloatLessOrEqual(candidateMinY, cMaxY)) - {//当右边的宽度和cRect的宽度相等,又有重合时表明二者可以合并为一个区域。 + } else if (_myCGFloatEqual(candidateMaxX, CGRectGetMaxX(cRect)) && _myCGFloatLessOrEqual(candidateMinY, cMaxY)) { //当右边的宽度和cRect的宽度相等,又有重合时表明二者可以合并为一个区域。 [bottomCandidateRects removeObjectAtIndex:i]; cRect = CGRectUnion(cRect, candidateRect); cRect.size.height += cMaxY - candidateMinY; //要加上重叠部分来增加高度,否则会出现高度不正确的问题。 } + } - + //记录每一列的最大子视图位置的索引值。 + if (topLastXOffset != subviewEngine.leading - leadingSpacing) { + [lineIndexes addIndex:idx - 1]; } - - [topCandidateRects addObject:[NSValue valueWithCGRect:cRect]]; - topLastXOffset = rect.origin.x - leadingSpace; - - if (_myCGFloatGreat(rect.origin.x + rect.size.width + trailingSpace + horzSpace,topMaxWidth)) - topMaxWidth = rect.origin.x + rect.size.width + trailingSpace + horzSpace; - + topLastXOffset = subviewEngine.leading - leadingSpacing; + + if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace, topMaxWidth)) { + topMaxWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace; + } + } + + if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace, maxLayoutWidth)) { + maxLayoutWidth = subviewEngine.leading + subviewEngine.width + trailingSpacing + context->horzSpace; + } + + if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace, maxLayoutHeight)) { + maxLayoutHeight = subviewEngine.top + subviewEngine.height + bottomSpacing + context->vertSpace; } - - if (_myCGFloatGreat(rect.origin.x + rect.size.width + trailingSpace + horzSpace, maxWidth)) - maxWidth = rect.origin.x + rect.size.width + trailingSpace + horzSpace; - - if (_myCGFloatGreat(rect.origin.y + rect.size.height + bottomSpace + vertSpace, maxHeight)) - maxHeight = rect.origin.y + rect.size.height + bottomSpace + vertSpace; - - sbvmyFrame.frame = rect; - } - - if (sbs.count > 0) - { - maxWidth -= horzSpace; - maxHeight -= vertSpace; + + if (subviewEngines.count > 0) { + maxLayoutWidth -= context->horzSpace; + maxLayoutHeight -= context->vertSpace; } - - maxWidth += paddingTrailing; - - maxHeight += paddingBottom; - if (!hasBoundaryLimit) - selfSize.height = maxHeight; - - if (lsc.wrapContentWidth) - selfSize.width = maxWidth; - else - { - CGFloat addXPos = 0; - MyGravity horzGravity = [self myConvertLeftRightGravityToLeadingTrailing:lsc.gravity & MyGravity_Vert_Mask]; - - if (horzGravity == MyGravity_Horz_Center) - { - addXPos = (selfSize.width - maxWidth) / 2; - } - else if (horzGravity == MyGravity_Horz_Trailing) - { - addXPos = selfSize.width - maxWidth; + + maxLayoutHeight += context->paddingBottom; + if (layoutTraits.heightSizeInner.wrapVal) { + if (context->selfSize.height == CGFLOAT_MAX || !isBeyondFlag) { + context->selfSize.height = maxLayoutHeight; } - - if (addXPos != 0) - { - for (int i = 0; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - - sbv.myFrame.leading += addXPos; + } + + maxLayoutWidth += context->paddingTrailing; + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:maxLayoutWidth subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + + if (context->selfSize.width != maxLayoutWidth) { + if (context->horzGravity != MyGravity_None) { + CGFloat addXPos = 0.0; + if (context->horzGravity == MyGravity_Horz_Center) { + addXPos = (context->selfSize.width - maxLayoutWidth) / 2; + } else if (context->horzGravity == MyGravity_Horz_Trailing) { + addXPos = context->selfSize.width - maxLayoutWidth; + } + + if (addXPos != 0.0) { + for (int i = 0; i < subviewEngines.count; i++) { + subviewEngines[i].leading += addXPos; + } } } - } - - return selfSize; -} + //如果有子视图设置了对齐属性,那么就要对处在同一列内的子视图进行对齐设置。 + //对齐的规则是以列内最宽的子视图作为参考的对象,其他的子视图按照列内最宽子视图进行水平对齐的调整。 + if (subviewHasAlignment) { + //最后一列。 + if (subviewEngines.count > 0) { + [lineIndexes addIndex:subviewEngines.count - 1]; + } + __block NSInteger lineFirstIndex = 0; + [lineIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + + BOOL lineHasAlignment = NO; + + //计算每列内的最宽的子视图,作为列对齐的标准。 + CGFloat lineMaxWidth = 0; + for (NSInteger i = lineFirstIndex; i <= idx; i++) { + MyLayoutEngine *subviewEngine = subviewEngines[i]; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + if (subviewEngine.width > lineMaxWidth) { + lineMaxWidth = subviewEngine.width; + } + lineHasAlignment |= (MYHORZGRAVITY(subviewTraits.alignment) > MyGravity_Horz_Left); + } + + //设置行内的对齐 + if (lineHasAlignment) { + for (NSInteger i = lineFirstIndex; i <= idx; i++) { + MyLayoutEngine *subviewEngine = subviewEngines[i]; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + switch ([MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]) { + case MyGravity_Horz_Center: + subviewEngine.leading += (lineMaxWidth - subviewEngine.width) / 2.0; + break; + case MyGravity_Horz_Trailing: + subviewEngine.leading += (lineMaxWidth - subviewEngine.width); + break; + case MyGravity_Horz_Fill: + subviewEngine.width = lineMaxWidth; + break; + case MyGravity_Horz_Stretch: { + if (subviewTraits.widthSizeInner.val == nil || (subviewTraits.widthSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { + subviewEngine.width = lineMaxWidth; + } + } break; + default: + break; + } + } + } + + lineFirstIndex = idx + 1; + }]; + } +} @end diff --git a/MyLayout/Lib/MyFlowLayout.h b/MyLayout/Lib/MyFlowLayout.h index b647493..d7398bf 100644 --- a/MyLayout/Lib/MyFlowLayout.h +++ b/MyLayout/Lib/MyFlowLayout.h @@ -8,16 +8,21 @@ #import "MyBaseLayout.h" - - /** - 流式布局是一种里面的子视图按照添加的顺序依次排列,当遇到某种约束限制后会另起一排再重新排列的多行多列展示的布局视图。这里的约束限制主要有数量约束限制和内容尺寸约束限制两种,排列的方向又分为垂直和水平方向,因此流式布局一共有垂直数量约束流式布局、垂直内容约束流式布局、水平数量约束流式布局、水平内容约束流式布局。流式布局主要应用于那些有规律排列的场景,在某种程度上可以作为UICollectionView的替代品,同时流式布局实现了CSS3的flexbox的几乎全部功能。 + 流式布局是一种条目按照添加的顺序依次排列,当满足某种规则后会另起一排再重新排列,最终呈现为多行多列展示的布局形态。 + 这里的换排规则主要有数量限制和尺寸限制两种,整体排列方向又分为垂直和水平两个方向,因此流式布局一共有垂直数量限制、垂直尺寸限制、水平数量限制、水平尺寸限制四种布局。流式布局主要应用于那些有规律排列的场景,在某种程度上可以作为UICollectionView的替代品,同时流式布局实现了CSS3的flexbox的几乎全部功能。 + + @note + 需要注意的是这里的方向和排的概念,排的概念它是相对于流式布局的方向而具体定义的:如果是垂直流式布局那么一排内的子视图将是水平排列,而整体则是每排从上往下垂直排列;如果是水平流式布局那么一排内的子视图将是垂直排列,而整体则是每排从左往右水平排列。因此流式布局的方向所描述的是排整体的排列方向。 - 1.垂直数量约束流式布局 - orientation为MyOrientation_Vert,arrangedCount不为0,支持wrapContentHeight,支持wrapContentWidth,不支持autoArrange。 + @note + 所谓数量限制流式布局是指事先约定每排的条目视图的数量,当一排的条目视图数量超过约定的数量后新添加的条目视图就会换按约定的方向新起一排重新排列;而尺寸限制流式布局就是当一排内添加到布局中的条目视图的宽度(垂直流式布局)之和或者高度(水平流式布局)之和超过布局视图的宽度或者高度时新添加条目视图就会按约定的方向新起一排重新排列。 + + 1.垂直数量限制流式布局 + orientation为MyOrientation_Vert,arrangedCount不为0 @code -每排数量为3的垂直数量约束流式布局 + 每排数量为3的垂直数量限制流式布局 => +------+---+-----+ | A | B | C | @@ -29,10 +34,10 @@ @endcode - 2.垂直内容约束流式布局. - orientation为MyOrientation_Vert,arrangedCount为0,支持wrapContentHeight,不支持wrapContentWidth,支持autoArrange。 + 2.垂直尺寸限制流式布局. + orientation为MyOrientation_Vert,arrangedCount为0 @code - 垂直内容约束流式布局 + 垂直尺寸限制流式布局 => +-----+-----------+ | A | B | @@ -44,11 +49,11 @@ @endcode - 3.水平数量约束流式布局。 - orientation为MyOrientation_Horz,arrangedCount不为0,支持wrapContentHeight,支持wrapContentWidth,不支持autoArrange。 + 3.水平数量限制流式布局。 + orientation为MyOrientation_Horz,arrangedCount不为0 @code - 每排数量为3的水平数量约束流式布局 + 每排数量为3的水平数量限制流式布局 => +-----+----+-----+ | A | D | | @@ -63,11 +68,11 @@ @endcode - 4.水平内容约束流式布局 - orientation为MyOrientation_Horz,arrangedCount为0,不支持wrapContentHeight,支持wrapContentWidth,支持autoArrange。 + 4.水平尺寸限制流式布局 + orientation为MyOrientation_Horz,arrangedCount为0 @code - 水平内容约束流式布局 + 水平尺寸限制流式布局 => +-----+----+-----+ | A | C | | @@ -82,41 +87,34 @@ @endcode - @note - 流式布局中排的概念是一个通用的称呼,对于垂直方向的流式布局来说一排就是一行,垂直流式布局每排依次从上到下排列,每排内的子视图则是由左往右依次排列;对于水平方向的流式布局来说一排就是一列,水平流式布局每排依次从左到右排列,每排内的子视图则是由上往下依次排列 - */ @interface MyFlowLayout : MyBaseLayout /** - 初始化一个流式布局并指定布局的方向和布局的数量,如果数量为0则表示内容约束流式布局。 + 初始化一个流式布局并指定布局的方向和每排条目视图的数量,如果数量为0则表示尺寸限制流式布局。 @param orientation 布局的方向,这个方向是指的排与排之间排列方向,而不是排内的子视图的排列方向。 - @param arrangedCount 每排内的子视图的数量,如果每排内的子视图的数量不固定则设置为0表示为内容约束流式布局。 + @param arrangedCount 每排内的子条目视图的数量,如果每排内的子条目视图的数量不固定则设置为0表示为尺寸限制流式布局。 @return 返回流式布局对象实例。 */ --(instancetype)initWithOrientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount; - +- (instancetype)initWithOrientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount; /** - 初始化一个流式布局并指定布局视图的frame值以及方向和布局的数量,如果数量为0则表示内容约束流式布局。 + 初始化一个流式布局并指定布局视图的frame值以及方向和每排条目视图的数量,如果数量为0则表示尺寸限制流式布局。 @param frame 布局视图的frame值,当布局视图的父视图是非布局视图时就可以用这个方法来初始化视图的frame。 @param orientation 布局的方向,这个方向是指的排与排之间排列方向,而不是排内的子视图的排列方向。 - @param arrangedCount 每排内的子视图的数量,如果每排内的子视图的数量不固定则设置为0表示为内容约束流式布局。 + @param arrangedCount 每排内的子条目视图的数量,如果每排内的子条目视图的数量不固定则设置为0表示为尺寸限制流式布局。 @return 返回流式布局对象实例 */ --(instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount; - - +- (instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount; /** - 初始化一个流式布局并指定布局的方向和布局的数量,如果数量为0则表示内容约束流式布局。 + 初始化一个流式布局并指定布局的方向和排内条目视图的数量,如果数量为0则表示尺寸限制流式布局。 @param orientation 布局的方向,这个方向是指的排与排之间排列方向,而不是排内的子视图的排列方向。 - @param arrangedCount 每排内的子视图的数量,如果每排内的子视图的数量不固定则设置为0表示为内容约束流式布局。 + @param arrangedCount 每排内的子条目视图的数量,如果每排内的子视图的数量不固定则设置为0表示为尺寸限制流式布局。 @return 返回流式布局对象实例。 */ -+(instancetype)flowLayoutWithOrientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount; - ++ (instancetype)flowLayoutWithOrientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount; /** 流式布局的布局方向: @@ -125,21 +123,17 @@ 2. MyOrientation_Horz 表示排内子视图从上到下垂直排列,排与排之间依次从左到右(RTL从右到左)水平排列。 */ -@property(nonatomic,assign) IBInspectable MyOrientation orientation; - - +@property (nonatomic, assign) MyOrientation orientation; /** - 每排内的子视图数量。默认是0表示每排内的子视图的数量不固定,而是根据子视图的尺寸自动换行或者换列处理。如果非0则每排内的子视图数量等于这个值后会自动换行或者换列 + 每排内的子条目视图数量。默认是0表示每排内的子条目视图的数量不固定,而是根据子视图的尺寸自动换行或者换列处理。如果非0则每排内的子视图数量等于这个值后会自动换排。 */ -@property(nonatomic, assign) IBInspectable NSInteger arrangedCount; - - +@property (nonatomic, assign) NSInteger arrangedCount; /** - 为流式布局提供分页展示的能力,默认是0表不支持分页展示。当设置为非0时则要求必须是arrangedCount的整数倍数,表示每页的子视图的数量。而arrangedCount则表示每排内的子视图的数量。当启用pagedCount时要求将流式布局加入到UIScrollView或者其派生类中才能生效。只有数量约束流式布局才支持分页展示的功能,通过pagedCount和wrapContentHeight以及wrapContentWidth配合使用能实现不同的分页展示能力: + 为流式布局提供分页展示的能力,默认是0表不支持分页展示。当设置为非0时则要求必须是arrangedCount的整数倍数,表示每页的子视图的数量。而arrangedCount则表示每排内的子视图的数量。当启用pagedCount时如果流式布局的父视图是UIScrollView或者其派生类就会有分页滚动的效果。只有数量限制流式布局才支持分页展示的功能,通过pagedCount和设置尺寸的高度或者宽度自适应配合使用能实现不同的分页展示能力: @note - 1. 垂直数量约束流式布局的wrapContentHeight设置为YES时则以UIScrollView的尺寸作为一页展示的大小,因为指定了一页的子视图数量,以及指定了一排的子视图数量,因此默认也会自动计算出子视图的宽度和高度,而不需要单独指出高度和宽度(子视图的宽度你也可以自定义),整体的分页滚动是从上到下滚动。(每页布局时从左到右再从上到下排列,新页往下滚动继续排列): + 1. 垂直数量限制流式布局的高度设置为自适应时则以UIScrollView的尺寸作为一页展示的大小,因为指定了一页的子视图数量,以及指定了一排的子视图数量,因此默认也会自动计算出子视图的宽度和高度,而不需要单独指出高度和宽度(子视图的宽度你也可以自定义),整体的分页滚动是从上到下滚动。(每页布局时从左到右再从上到下排列,新页往下滚动继续排列): @code 1 2 3 4 5 6 @@ -148,21 +142,21 @@ 10 11 12 @endcode - 2. 垂直数量约束流式布局的wrapContentWidth设置为YES时则以UIScrollView的尺寸作为一页展示的大小,因为指定了一页的子视图数量,以及指定了一排的子视图数量,因此默认也会自动计算出子视图的宽度和高度,而不需要单独指出高度和宽度(子视图的高度你也可以自定义),整体的分页滚动是从左到右滚动。(每页布局时从左到右再从上到下排列,新页往右滚动继续排列) + 2. 垂直数量限制流式布局的宽度设置为自适应时则以UIScrollView的尺寸作为一页展示的大小,因为指定了一页的子视图数量,以及指定了一排的子视图数量,因此默认也会自动计算出子视图的宽度和高度,而不需要单独指出高度和宽度(子视图的高度你也可以自定义),整体的分页滚动是从左到右滚动。(每页布局时从左到右再从上到下排列,新页往右滚动继续排列) @code 1 2 3 | 7 8 9 4 5 6 | 10 11 12 → @endcode - 3. 水平数量约束流式布局的wrapContentWidth设置为YES时则以UIScrollView的尺寸作为一页展示的大小,因为指定了一页的子视图数量,以及指定了一排的子视图数量,因此默认也会自动计算出子视图的宽度和高度,而不需要单独指出高度和宽度(子视图的高度你也可以自定义),整体的分页滚动是从左到右滚动。(每页布局时从上到下再从左到右排列,新页往右滚动继续排列) + 3. 水平数量限制流式布局的宽度设置为自适应时则以UIScrollView的尺寸作为一页展示的大小,因为指定了一页的子视图数量,以及指定了一排的子视图数量,因此默认也会自动计算出子视图的宽度和高度,而不需要单独指出高度和宽度(子视图的高度你也可以自定义),整体的分页滚动是从左到右滚动。(每页布局时从上到下再从左到右排列,新页往右滚动继续排列) @code 1 3 5 | 7 9 11 2 4 6 | 8 10 12 → @endcode - 4. 水平数量约束流式布局的wrapContentHeight设置为YES时则以UIScrollView的尺寸作为一页展示的大小,因为指定了一页的子视图数量,以及指定了一排的子视图数量,因此默认也会自动计算出子视图的宽度和高度,而不需要单独指出高度和宽度(子视图的宽度你也可以自定义),整体的分页滚动是从上到下滚动。(每页布局时从上到下再从左到右排列,新页往下滚动继续排列) + 4. 水平数量限制流式布局的高度设置为自适应时则以UIScrollView的尺寸作为一页展示的大小,因为指定了一页的子视图数量,以及指定了一排的子视图数量,因此默认也会自动计算出子视图的宽度和高度,而不需要单独指出高度和宽度(子视图的宽度你也可以自定义),整体的分页滚动是从上到下滚动。(每页布局时从上到下再从左到右排列,新页往下滚动继续排列) @code 1 3 5 2 4 6 @@ -171,21 +165,32 @@ 8 10 12 @endcode */ -@property(nonatomic, assign) IBInspectable NSInteger pagedCount; - - +@property (nonatomic, assign) NSInteger pagedCount; /** - 布局内子视图自动排列。这个属性只有在内容填充约束流式布局下才有用,默认为NO。当设置为YES时则根据子视图的尺寸自动填充,而不是根据加入的顺序来填充,以便保证不会出现多余空隙的情况。 + 布局内子视图自动排列或者让布局内的子视图的排列尽可能的紧凑,默认为NO。 + + 当流式布局是尺寸限制流式布局时,则当设置为YES时则根据子视图的尺寸自动填充,而不是根据加入的顺序来填充,以便保证不会出现多余空隙的情况。 + 当流式布局是数量限制流式布局时,则当设置为YES时每个子视图会按顺序找一个最佳的存放地点进行填充。 @note - 请在将所有子视图添加完毕并且初始布局完成后再设置这个属性,否则如果预先设置这个属性则在后续添加子视图时可能会非常耗性能。 + 如果在尺寸限制流式布局中使用此属性时,请在将所有子视图添加完毕并且初始布局完成后再设置这个属性,否则如果预先设置这个属性则在后续添加子视图时可能会非常耗性能。 */ -@property(nonatomic,assign) IBInspectable BOOL autoArrange; +@property (nonatomic, assign) BOOL autoArrange; +/** + 布局是否是兼容flexbox规则的布局,默认是NO。当设置为YES时有两个特性会产生差异: + 1.停靠属性gravity在所有排中都有效。在一些场景中,往往最后一排的子视图个数并没有填充满布局视图,如果这时候设置了gravity时产生的效果并不美观。 + 所以默认情况下最后一排的子视图并不会受到gravity设置的影响,但是特殊情况除外:1.只有一排时,2.最后一排的数量和其他排的数量一样时 这两种情况gravity会影响所有子视图。 因此当isFlex为YES时则所有行和列都会受到gravity属性的影响。 + + 2.子视图的weight属性可以和子视图的尺寸约束共存。默认情况下在垂直流式布局中如果子视图设置了weight属性则子视图的宽度约束将不起作用, + 而在水平流式布局中如果子视图设置了weight属性则子视图的高度约束将不起作用。另外在流式尺寸限制布局中weight表示的是剩余空间的比重。 + 因此当isFlex设置为YES时,表示子视图的weight可以和尺寸约束共存,并且weight就是表示排内的剩余空间的比重。 + */ +@property (nonatomic, assign) BOOL isFlex; /** - *设置流式布局中每排内所有子视图的对齐停靠方式。具体的对齐停靠方式依赖于布局视图的方向: + 设置流式布局中每排内所有子视图的对齐停靠方式。具体的对齐停靠方式依赖于布局视图的方向: 1. 如果是垂直流式布局则表示每排内子视图的上中下对齐方式,这里的对齐基础是以每排中的最高的子视图为基准。这个属性只支持: @code @@ -193,6 +198,10 @@ MyGravity_Vert_Center 垂直居中对齐 MyGravity_Vert_Bottom 底部对齐 MyGravity_Vert_Fill 两端对齐 + MyGravity_Vert_Stretch 如果子视图未设置约束或者非布局子视图高度自适应则子视图高度被拉伸 + MyGravity_Vert_Baseline 基线对齐,以每一行的第一个带有文字的视图作为基线进行对齐。 + MyGravity_Vert_Between 数量限制垂直流式布局有效,子视图会紧凑进行排列,当设置为这个属性值时,子视图的y轴的位置总是从对应列的上一行的结尾开始,而不是上一行的最高位置开始。 + MyGravity_Vert_Around 如果行内子视图没有设置高度约束,则子视图的高度填充整行,否则按子视图的高度是高度约束决定。 @endcode 2. 如果是水平流式布局则表示每排内子视图的左中右对齐方式,这里的对齐基础是以每排中的最宽的子视图为基准。这个属性只支持: @code @@ -200,47 +209,64 @@ MyGravity_Horz_Center 水平居中对齐 MyGravity_Horz_Right 右边对齐 MyGravity_Horz_Fill 两端对齐 + MyGravity_Horz_Stretch 如果子视图未设置约束或者非布局子视图宽度自适应则子视图宽度被拉伸 + MyGravity_Horz_Between 数量限制水平流式布局有效,子视图会紧凑进行排列,当设置为这个属性值时,子视图的x轴的位置总是从对应行的上一列的结尾开始,而不是上一列的最宽位置开始。 + MyGravity_Horz_Around 如果列内子视图没有设置宽度约束,则子视图的宽度填充整行,否则按子视图的宽度是宽度约束决定。 @endcode - @note 如果您想单独设置某个子视图在排内的对齐方式则请使用子视图的扩展属性myAlignment。 + @note + 如果您想单独设置某个子视图在行内的对齐方式则请使用子视图的扩展属性alignment。 */ -@property(nonatomic,assign) MyGravity arrangedGravity; +@property (nonatomic, assign) MyGravity arrangedGravity; +/** +指定流式布局最后一排的尺寸或间距的拉伸策略。默认值是MyGravityPolicy_No表示最后一排的尺寸或间接拉伸策略不生效。 +在流式布局中我们可以通过gravity属性来对每一排的尺寸或间距进行拉伸处理。但是在实际情况中最后一排的子视图数量可能会小于等于前面排的数量。 +在这种情况下如果对最后一排进行相同的尺寸或间距的拉伸处理则有可能会影响整体的布局效果。因此我们可通过这个属性来指定最后一排的尺寸或间距的生效策略。 +这个策略在不同的流式布局中效果不一样: +1.在数量限制布局中如果最后一排的子视图数量小于arrangedCount的值时,那么当我们使用gravity来对行内视图进间距拉伸时(between,around,among) +可以指定三种策略:no表示最后一排不进行任何间距拉伸处理,always表示最后一排总是进行间距拉伸处理,auto表示最后一排的每个子视图都和上一排对应位置视图左对齐。 + +2.在尺寸限制布局中因为每排的子视图数量不固定,所以只有no和always两种策略有效,并且这两种策略不仅影响子视图的尺寸的拉伸(fill,stretch)还影响间距的拉伸效果(between,around,among)。 + */ +@property (nonatomic, assign) MyGravityPolicy lastlineGravityPolicy; +/** + 单独为某一排定制的水平和垂直停靠对齐属性,默认情况下布局视图的gravity和arrangedGravity作用于所有排以及排内的停靠对齐。如果你想单独定制某一排的停靠对齐方式时 + 可以通过设置这个block属性。 + lineGravity的入参分别是布局对象、当前排的索引(0开始)、当前排的条目视图数量、是否是最后一排四个参数。 + 函数返回的是此排以及排内的停靠对齐方式,如果返回MyGravity_None则表示使用布局默认的gravity和arrangedGravity停靠对齐属性。 + */ +@property (nonatomic, copy) MyGravity (^lineGravity)(MyFlowLayout *layout, NSInteger lineIndex, NSInteger itemCount, BOOL isLastLine); /** - 在内容约束流式布局的一些应用场景中我们希望子视图的宽度是固定的但间距是浮动的,这样就尽可能在一排中容纳更多的子视图。比如设置每个子视图的宽度固定为80,那么在小屏幕下每排只能放3个,而大屏幕则每排能放4个或者5个子视图。 因此您可以通过如下方法来设置子视图的固定尺寸和最小最大浮动间距。这个方法会根据您当前布局的方向不同而具有不同的意义: + 在流式布局的一些应用场景中我们希望子视图的宽度或者高度是固定的但间距是浮动的,这样就尽可能在一排中容纳更多的子视图。比如设置每个子视图的宽度固定为80,那么在小屏幕下每排只能放3个,而大屏幕则每排能放4个或者5个子视图。 因此您可以通过如下方法来设置子视图的固定尺寸和最小最大浮动间距。这个方法会根据您当前布局的方向不同而具有不同的意义: 1.如果您的布局方向是垂直的则设置的是每排内子视图的水平浮动间距,其中的subviewSize指定的是子视图的固定宽度;minSpace指定的是最小的水平间距;maxSpace指定的是最大的水平间距,如果指定的subviewSize计算出的间距大于最大间距maxSpace则会缩小subviewSize的宽度值。 2.如果您的布局方向是水平的则设置的是每排内子视图的垂直浮动间距,其中的subviewSize指定的是子视图的固定高度;minSpace指定的是最小的垂直间距;maxSpace指定的是最大的垂直间距,如果指定的subviewSize计算出的间距大于最大间距maxSpace则会调整subviewSize的高度值。 - @note 如果您不想使用浮动间距则请将subviewSize设置为0就可以了。这个方法只在内容约束流式布局里面设置才有意义。 + @note 如果您不想使用浮动间距则请将subviewSize设置为0就可以了。 + + @note 对于数量限制流式布局来说,因为每排和每列的数量的固定的,因此不存在根据屏幕的大小自动换排的能力以及进行最佳数量的排列,但是可以使用这个方法来实现所有子视图尺寸固定但是间距是浮动的功能需求。 @param subviewSize 指定子视图的尺寸。 @param minSpace 指定子视图之间的最小间距 @param maxSpace 指定子视图之间的最大间距 */ --(void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace; --(void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass; - - -@end - - -@interface MyFlowLayout(MyFlowLayoutDeprecated) +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace; +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass; /** - 指定是否均分布局方向上的子视图的宽度或者高度,或者拉伸子视图的尺寸,默认是NO。 - 如果是MyOrientation_Vert则表示每行的子视图的宽度会被均分,这样子视图不需要指定宽度,但是布局视图必须要指定一个明确的宽度值,如果设置为YES则wrapContentWidth会失效。 - 如果是MyOrientation_Horz则表示每列的子视图的高度会被均分,这样子视图不需要指定高度,但是布局视图必须要指定一个明确的高度值,如果设置为YES则wrapContentHeight会失效。 - 内容填充约束流式布局下averageArrange设置为YES时表示拉伸子视图的宽度或者高度以便填充满整个布局视图。 - */ -@property(nonatomic,assign) BOOL averageArrange MYMETHODDEPRECATED("use gravity = MyGravity_Horz_Fill or gravity = MyGravity_Vert_Fill to instead"); + 上面函数的加强版本。 + @param subviewSize 指定子视图的尺寸 + @param minSpace 指定子视图之间的最小间距 + @param maxSpace 指定子视图之间的最大间距 + @param centered 指定是否所有子视图居中 + */ +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered; +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; @end - - - diff --git a/MyLayout/Lib/MyFlowLayout.m b/MyLayout/Lib/MyFlowLayout.m index a299060..6ce46e2 100644 --- a/MyLayout/Lib/MyFlowLayout.m +++ b/MyLayout/Lib/MyFlowLayout.m @@ -9,630 +9,771 @@ #import "MyFlowLayout.h" #import "MyLayoutInner.h" - @implementation MyFlowLayout +#pragma mark-- Public Methods --(instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount -{ - self = [super initWithFrame:frame]; - if (self != nil) - { - self.myCurrentSizeClass.orientation = orientation; - self.myCurrentSizeClass.arrangedCount = arrangedCount; +- (instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount { + self = [self initWithFrame:frame]; + if (self != nil) { + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; + layoutTraits.orientation = orientation; + layoutTraits.arrangedCount = arrangedCount; } - - return self; + return self; } --(instancetype)initWithOrientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount -{ +- (instancetype)initWithOrientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount { return [self initWithFrame:CGRectZero orientation:orientation arrangedCount:arrangedCount]; } - -+(instancetype)flowLayoutWithOrientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount -{ ++ (instancetype)flowLayoutWithOrientation:(MyOrientation)orientation arrangedCount:(NSInteger)arrangedCount { MyFlowLayout *layout = [[[self class] alloc] initWithOrientation:orientation arrangedCount:arrangedCount]; return layout; } --(void)setOrientation:(MyOrientation)orientation -{ - MyFlowLayout *lsc = self.myCurrentSizeClass; - if (lsc.orientation != orientation) - { - lsc.orientation = orientation; +- (void)setOrientation:(MyOrientation)orientation { + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.orientation != orientation) { + layoutTraits.orientation = orientation; [self setNeedsLayout]; } } --(MyOrientation)orientation -{ - return self.myCurrentSizeClass.orientation; +- (MyOrientation)orientation { + return self.myDefaultSizeClassInner.orientation; } - --(void)setArrangedCount:(NSInteger)arrangedCount -{ - MyFlowLayout *lsc = self.myCurrentSizeClass; - if (lsc.arrangedCount != arrangedCount) - { - lsc.arrangedCount = arrangedCount; +- (void)setArrangedCount:(NSInteger)arrangedCount { + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.arrangedCount != arrangedCount) { + layoutTraits.arrangedCount = arrangedCount; [self setNeedsLayout]; } } --(NSInteger)arrangedCount -{ - MyFlowLayout *lsc = self.myCurrentSizeClass; - return lsc.arrangedCount; +- (NSInteger)arrangedCount { + return self.myDefaultSizeClassInner.arrangedCount; } +- (void)setPagedCount:(NSInteger)pagedCount { + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.pagedCount != pagedCount) { + layoutTraits.pagedCount = pagedCount; + [self setNeedsLayout]; + } +} --(NSInteger)pagedCount -{ - MyFlowLayout *lsc = self.myCurrentSizeClass; - return lsc.pagedCount; +- (NSInteger)pagedCount { + return self.myDefaultSizeClassInner.pagedCount; } --(void)setPagedCount:(NSInteger)pagedCount -{ - MyFlowLayout *lsc = self.myCurrentSizeClass; - if (lsc.pagedCount != pagedCount) - { - lsc.pagedCount = pagedCount; +- (void)setAutoArrange:(BOOL)autoArrange { + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.autoArrange != autoArrange) { + layoutTraits.autoArrange = autoArrange; [self setNeedsLayout]; } } +- (BOOL)autoArrange { + return self.myDefaultSizeClassInner.autoArrange; +} --(void)setAutoArrange:(BOOL)autoArrange -{ - MyFlowLayout *lsc = self.myCurrentSizeClass; - - if (lsc.autoArrange != autoArrange) - { - lsc.autoArrange = autoArrange; +- (void)setArrangedGravity:(MyGravity)arrangedGravity { + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.arrangedGravity != arrangedGravity) { + layoutTraits.arrangedGravity = arrangedGravity; [self setNeedsLayout]; } } --(BOOL)autoArrange -{ - return self.myCurrentSizeClass.autoArrange; +- (MyGravity)arrangedGravity { + return self.myDefaultSizeClassInner.arrangedGravity; } - --(void)setArrangedGravity:(MyGravity)arrangedGravity -{ - MyFlowLayout *lsc = self.myCurrentSizeClass; - if (lsc.arrangedGravity != arrangedGravity) - { - lsc.arrangedGravity = arrangedGravity; +- (void)setIsFlex:(BOOL)isFlex { + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.isFlex != isFlex) { + layoutTraits.isFlex = isFlex; + if (isFlex) { + layoutTraits.lastlineGravityPolicy = MyGravityPolicy_Always; + } else { + layoutTraits.lastlineGravityPolicy = MyGravityPolicy_No; + } [self setNeedsLayout]; } } --(MyGravity)arrangedGravity -{ - return self.myCurrentSizeClass.arrangedGravity; +- (BOOL)isFlex { + return self.myDefaultSizeClassInner.isFlex; +} + +- (void)setLastlineGravityPolicy:(MyGravityPolicy)lastlineGravityPolicy { + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.lastlineGravityPolicy != lastlineGravityPolicy) { + layoutTraits.lastlineGravityPolicy = lastlineGravityPolicy; + [self setNeedsLayout]; + } } +- (MyGravityPolicy)lastlineGravityPolicy { + return self.myDefaultSizeClassInner.lastlineGravityPolicy; +} --(void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace -{ +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace { [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } --(void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass -{ - MyFlowLayoutViewSizeClass *lsc = (MyFlowLayoutViewSizeClass*)[self fetchLayoutSizeClass:sizeClass]; - lsc.subviewSize = subviewSize; - lsc.maxSpace = maxSpace; - lsc.minSpace = minSpace; - [self setNeedsLayout]; +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace inSizeClass:(MySizeClass)sizeClass { + [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace centered:NO inSizeClass:sizeClass]; } -#pragma mark -- Deprecated Method +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered { + [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace centered:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; +} +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { + MySequentLayoutTraits *layoutTraits = (MySequentLayoutTraits *)[self fetchLayoutSizeClass:sizeClass]; + if (subviewSize == 0) { + layoutTraits.flexSpace = nil; + } else { + if (layoutTraits.flexSpace == nil) { + layoutTraits.flexSpace = [MySequentLayoutFlexSpacing new]; + } + layoutTraits.flexSpace.subviewSize = subviewSize; + layoutTraits.flexSpace.minSpace = minSpace; + layoutTraits.flexSpace.maxSpace = maxSpace; + layoutTraits.flexSpace.centered = centered; + } + [self setNeedsLayout]; +} --(void)setAverageArrange:(BOOL)averageArrange -{ - MyFlowLayout *lsc = self.myCurrentSizeClass; - - if (lsc.orientation == MyOrientation_Vert) - { - if (averageArrange) - lsc.gravity = (lsc.gravity & MyGravity_Horz_Mask) | MyGravity_Horz_Fill; - else - lsc.gravity = (lsc.gravity & MyGravity_Horz_Mask) | MyGravity_None; +#pragma mark-- Override Methods + +- (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { + [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; + + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits *)context->layoutViewEngine.currentSizeClass; + context->paddingTop = layoutTraits.myLayoutPaddingTop; + context->paddingBottom = layoutTraits.myLayoutPaddingBottom; + context->paddingLeading = layoutTraits.myLayoutPaddingLeading; + context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + context->vertGravity = MYVERTGRAVITY(layoutTraits.gravity); + context->horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.gravity)]; + context->vertSpace = layoutTraits.subviewVSpace; + context->horzSpace = layoutTraits.subviewHSpace; + if (context->subviewEngines == nil) { + context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; } - else - { - if (averageArrange) - lsc.gravity = (lsc.gravity & MyGravity_Vert_Mask) | MyGravity_Vert_Fill; - else - lsc.gravity = (lsc.gravity & MyGravity_Vert_Mask) | MyGravity_None; + + MyOrientation orientation = layoutTraits.orientation; + MyGravity arrangedGravity = layoutTraits.arrangedGravity; + [self myCalcSubviewsWrapContentSize:context + withCustomSetting:^(MyViewTraits *subviewTraits) { + if (subviewTraits.widthSizeInner.wrapVal) { + if (layoutTraits.pagedCount > 0 || + (orientation == MyOrientation_Vert && subviewTraits.weight != 0.0) || + MYHORZGRAVITY(arrangedGravity) == MyGravity_Horz_Fill || + MYHORZGRAVITY(arrangedGravity) == MyGravity_Horz_Stretch || + context->horzGravity == MyGravity_Horz_Fill) { + if ([subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + [subviewTraits.widthSizeInner _myEqualTo:nil]; + } + } + } + + if (subviewTraits.heightSizeInner.wrapVal) { + if (layoutTraits.pagedCount > 0 || + (orientation == MyOrientation_Horz && subviewTraits.weight != 0.0) || + MYVERTGRAVITY(arrangedGravity) == MyGravity_Vert_Fill || + MYVERTGRAVITY(arrangedGravity) == MyGravity_Vert_Stretch || + context->vertGravity == MyGravity_Vert_Fill) { + if ([subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + [subviewTraits.heightSizeInner _myEqualTo:nil]; + } + } + } + }]; + + if (orientation == MyOrientation_Vert) { + if (layoutTraits.arrangedCount == 0) { + [self myDoVertOrientationContentLayoutWithContext:context]; + } else { + [self myDoVertOrientationCountLayoutWithContext:context]; + } + } else { + if (layoutTraits.arrangedCount == 0) { + [self myDoHorzOrientationContentLayoutWithContext:context]; + } else { + [self myDoHorzOrientationCountLayoutWithContext:context]; + } } + return [self myAdjustLayoutViewSizeWithContext:context]; } --(BOOL)averageArrange -{ - MyFlowLayout *lsc = self.myCurrentSizeClass; - - if (lsc.orientation == MyOrientation_Vert) - return (lsc.gravity & MyGravity_Vert_Mask) == MyGravity_Horz_Fill; - else - return (lsc.gravity & MyGravity_Horz_Mask) == MyGravity_Vert_Fill; +- (id)createSizeClassInstance { + return [MyFlowLayoutTraits new]; } +#pragma mark-- Private Methods -#pragma mark -- Override Method - --(CGSize)calcLayoutRect:(CGSize)size isEstimate:(BOOL)isEstimate pHasSubLayout:(BOOL*)pHasSubLayout sizeClass:(MySizeClass)sizeClass sbs:(NSMutableArray*)sbs -{ - CGSize selfSize = [super calcLayoutRect:size isEstimate:isEstimate pHasSubLayout:pHasSubLayout sizeClass:sizeClass sbs:sbs]; +//计算垂直流式布局下每行的比重值。 +- (void)myVertLayoutCalculateSinglelineWeight:(CGFloat)lineTotalWeight lineSpareWidth:(CGFloat)lineSpareWidth startItemIndex:(NSInteger)startItemIndex itemCount:(NSInteger)itemCount withContext:(MyLayoutContext *)context { + if (itemCount == 0 || lineSpareWidth <= 0.0) { + return; + } + + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits *)context->layoutViewEngine.currentSizeClass; + NSArray *subviewEngines = context->subviewEngines; - if (sbs == nil) - sbs = [self myGetLayoutSubviews]; + //按照flex规约,如果总的比重小于1则只会将剩余宽度的总比重部分来进行按比例拉伸。 + if (layoutTraits.isFlex && lineTotalWeight < 1.0) { + lineSpareWidth *= lineTotalWeight; + } - MyFlowLayout *lsc = self.myCurrentSizeClass; + for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { + MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + if (subviewTraits.weight != 0) { + CGFloat weightWidth = _myCGFloatRound((lineSpareWidth * subviewTraits.weight / lineTotalWeight)); + if (subviewTraits.widthSizeInner != nil && subviewTraits.widthSizeInner.val == nil) { + weightWidth = [subviewTraits.widthSizeInner measureWith:weightWidth]; + } + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:weightWidth + subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + } +} + +//计算水平流式布局下每行的比重值。 +- (void)myHorzLayoutCalculateSinglelineWeight:(CGFloat)lineTotalWeight lineSpareHeight:(CGFloat)lineSpareHeight startItemIndex:(NSInteger)startItemIndex itemCount:(NSInteger)itemCount withContext:(MyLayoutContext *)context { + if (itemCount == 0 || lineSpareHeight <= 0.0) { + return; + } + + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits *)context->layoutViewEngine.currentSizeClass; + NSArray *subviewEngines = context->subviewEngines; - MyOrientation orientation = lsc.orientation; - MyGravity gravity = lsc.gravity; - MyGravity arrangedGravity = lsc.arrangedGravity; + if (layoutTraits.isFlex && lineTotalWeight < 1.0) { + lineSpareHeight *= lineTotalWeight; + } - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (!isEstimate) - { - sbvmyFrame.frame = sbv.bounds; - [self myCalcSizeOfWrapContentSubview:sbv sbvsc:sbvsc sbvmyFrame:sbvmyFrame]; - } - - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - - if (sbvsc.wrapContentWidth) - { - if (lsc.pagedCount > 0 || sbvsc.widthSizeInner.dimeVal != nil || - (orientation == MyOrientation_Horz && (arrangedGravity & MyGravity_Vert_Mask) == MyGravity_Horz_Fill) || - (orientation == MyOrientation_Vert && ((gravity & MyGravity_Vert_Mask) == MyGravity_Horz_Fill || sbvsc.weight != 0))) - { - sbvsc.wrapContentWidth = NO; - } - } - - if (sbvsc.wrapContentHeight) - { - if (lsc.pagedCount > 0 || sbvsc.heightSizeInner.dimeVal != nil || - (orientation == MyOrientation_Vert && (arrangedGravity & MyGravity_Horz_Mask) == MyGravity_Vert_Fill) || - (orientation == MyOrientation_Horz && ((gravity & MyGravity_Horz_Mask) == MyGravity_Vert_Fill || sbvsc.weight != 0))) - { - sbvsc.wrapContentHeight = NO; - } + for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { + MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + if (subviewTraits.weight != 0) { + CGFloat weightHeight = _myCGFloatRound((lineSpareHeight * subviewTraits.weight / lineTotalWeight)); + if (subviewTraits.heightSizeInner != nil && subviewTraits.heightSizeInner.val == nil) { + weightHeight = [subviewTraits.heightSizeInner measureWith:weightHeight]; } - - - BOOL isSbvWrap = sbvsc.wrapContentHeight || sbvsc.wrapContentWidth; - - if (pHasSubLayout != nil && isSbvWrap) - *pHasSubLayout = YES; - - if (isEstimate && isSbvWrap) - { - [(MyBaseLayout*)sbv sizeThatFits:sbvmyFrame.frame.size inSizeClass:sizeClass]; - if (sbvmyFrame.multiple) - { - sbvmyFrame.sizeClass = [sbv myBestSizeClass:sizeClass]; //因为sizeThatFits执行后会还原,所以这里要重新设置 - sbvsc = sbvmyFrame.sizeClass; - } + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:weightHeight + subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:[subviewTraits.widthSizeInner measureWith:subviewEngine.height] subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; } } } +} - - - if (orientation == MyOrientation_Vert) - { - if (lsc.arrangedCount == 0) - selfSize = [self myLayoutSubviewsForVertContent:selfSize sbs:sbs isEstimate:isEstimate lsc:lsc]; - else - selfSize = [self myLayoutSubviewsForVert:selfSize sbs:sbs isEstimate:isEstimate lsc:lsc]; +//调整内容约束垂直流式布局的每行的宽度 +- (void)myVertLayoutAdjustSingleline:(NSInteger)lineIndex lineSpareWidth:(CGFloat)lineSpareWidth lineTotalWeight:(CGFloat)lineTotalWeight startItemIndex:(NSInteger)startItemIndex itemCount:(NSInteger)itemCount withContext:(MyLayoutContext *)context { + if (itemCount == 0 || lineSpareWidth <= 0.0) { + return; } - else - { - if (lsc.arrangedCount == 0) - selfSize = [self myLayoutSubviewsForHorzContent:selfSize sbs:sbs isEstimate:isEstimate lsc:lsc]; - else - selfSize = [self myLayoutSubviewsForHorz:selfSize sbs:sbs isEstimate:isEstimate lsc:lsc]; + + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + NSArray *subviewEngines = context->subviewEngines; + + MyGravity lineHorzGravity = context->horzGravity; + if (self.lineGravity != nil) { + lineHorzGravity = self.lineGravity(self, lineIndex, itemCount, (startItemIndex + itemCount) == subviewEngines.count) & MyGravity_Vert_Mask; + if (lineHorzGravity == MyGravity_None) { + lineHorzGravity = context->horzGravity; + } } - //调整布局视图自己的尺寸。 - [self myAdjustLayoutSelfSize:&selfSize lsc:lsc]; - //如果是反向则调整所有子视图的左右位置。 - [self myAdjustSubviewsRTLPos:sbs selfWidth:selfSize.width]; + //只有最后一行,并且数量小于arrangedCount,并且不是第一行才应用策略。 + BOOL applyLastlineGravityPolicy = ((startItemIndex + itemCount) == subviewEngines.count && + itemCount != layoutTraits.arrangedCount && + lineHorzGravity == MyGravity_Horz_Fill); + CGFloat incWidth = 0.0; + if (lineHorzGravity == MyGravity_Horz_Fill && lineTotalWeight == 0.0) { + if (!applyLastlineGravityPolicy || layoutTraits.lastlineGravityPolicy == MyGravityPolicy_Always) { + incWidth = lineSpareWidth / itemCount; + } + } else { + //flex规则:当总的比重小于1时,剩余的宽度要乘以这个比重值再进行分配。 + if (layoutTraits.isFlex && lineTotalWeight < 1.0) { + lineSpareWidth *= lineTotalWeight; + } + } - return [self myAdjustSizeWhenNoSubviews:selfSize sbs:sbs lsc:lsc]; + for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { + MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + if (subviewTraits.weight != 0.0 && lineTotalWeight != 0.0) { + CGFloat weightWidth = _myCGFloatRound((lineSpareWidth * subviewTraits.weight / lineTotalWeight)); + if (subviewTraits.widthSizeInner != nil && subviewTraits.widthSizeInner.val == nil) { + weightWidth = [subviewTraits.widthSizeInner measureWith:weightWidth]; + } + subviewEngine.width += weightWidth; + } + //添加拉伸的尺寸。 + if (incWidth != 0.0) { + subviewEngine.width += incWidth; + } + } } --(id)createSizeClassInstance -{ - return [MyFlowLayoutViewSizeClass new]; +//调整内容约束水平流式布局的每行的高度 +- (void)myHorzLayoutAdjustSingleline:(NSInteger)lineIndex lineSpareHeight:(CGFloat)lineSpareHeight lineTotalWeight:(CGFloat)lineTotalWeight startItemIndex:(NSInteger)startItemIndex itemCount:(NSInteger)itemCount withContext:(MyLayoutContext*)context { + if (itemCount == 0 || lineSpareHeight <= 0.0) { + return; + } + + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + NSArray *subviewEngines = context->subviewEngines; + + MyGravity lineVertGravity = context->vertGravity; + if (self.lineGravity != nil) { + lineVertGravity = self.lineGravity(self, lineIndex, itemCount, (startItemIndex + itemCount) == subviewEngines.count) & MyGravity_Horz_Mask; + if (lineVertGravity == MyGravity_None) { + lineVertGravity = context->vertGravity; + } + } + + //只有最后一行,并且数量小于arrangedCount,并且不是第一行才应用策略。 + BOOL applyLastlineGravityPolicy = ((startItemIndex + itemCount) == subviewEngines.count && + itemCount != layoutTraits.arrangedCount && + lineVertGravity == MyGravity_Vert_Fill); + CGFloat incHeight = 0.0; + if (lineVertGravity == MyGravity_Vert_Fill && lineTotalWeight == 0.0) { + if (!applyLastlineGravityPolicy || layoutTraits.lastlineGravityPolicy == MyGravityPolicy_Always) { + incHeight = lineSpareHeight / itemCount; + } + } else { + //flex规则:当总的比重小于1时,剩余的高度要乘以这个比重值再进行分配。 + if (layoutTraits.isFlex && lineTotalWeight < 1.0) { + lineSpareHeight *= lineTotalWeight; + } + } + + for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { + MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + if (subviewTraits.weight != 0.0 && lineTotalWeight != 0.0) { + CGFloat weightHeight = _myCGFloatRound((lineSpareHeight * subviewTraits.weight / lineTotalWeight)); + if (subviewTraits.heightSizeInner != nil && subviewTraits.heightSizeInner.val == nil) { + weightHeight = [subviewTraits.heightSizeInner measureWith:weightHeight]; + } + subviewEngine.height += weightHeight; + } + //添加拉伸的尺寸。 + if (incHeight != 0.0) { + subviewEngine.height += incHeight; + } + } } -#pragma mark -- Private Method - - -- (void)myCalcVertLayoutSinglelineWeight:(CGSize)selfSize totalFloatWidth:(CGFloat)totalFloatWidth totalWeight:(CGFloat)totalWeight sbs:(NSArray *)sbs startIndex:(NSInteger)startIndex count:(NSInteger)count -{ - for (NSInteger j = startIndex - count; j < startIndex; j++) - { - UIView *sbv = sbs[j]; - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (sbvsc.weight != 0) - { - CGFloat tempWidth = _myCGFloatRound((totalFloatWidth * sbvsc.weight / totalWeight)); - if (sbvsc.widthSizeInner != nil) - tempWidth = [sbvsc.widthSizeInner measureWith:tempWidth]; - - totalFloatWidth -= tempWidth; - totalWeight -= sbvsc.weight; - - sbvmyFrame.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:tempWidth sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - sbvmyFrame.trailing = sbvmyFrame.leading + sbvmyFrame.width; +- (void)myVertLayoutCalculateSinglelineShrink:(CGFloat)lineTotalShrink lineSpareWidth:(CGFloat)lineSpareWidth startItemIndex:(NSInteger)startItemIndex itemCount:(NSInteger)itemCount withContext:(MyLayoutContext*)context { + if (_myCGFloatGreatOrEqual(lineSpareWidth, 0.0)) { + lineTotalShrink = 0.0; + } + if (itemCount == 0 || lineTotalShrink == 0.0) { + return; + } + + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + NSArray *subviewEngines = context->subviewEngines; + + //根据flex规约:如果总的压缩比重小于1则超出部分会乘以这个压缩比再进行压缩。 + if (layoutTraits.isFlex && lineTotalShrink < 1.0) { + lineSpareWidth *= lineTotalShrink; + } + //如果有压缩则调整子视图的宽度。 + for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { + MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + if (subviewTraits.widthSizeInner.shrink != 0.0) { + subviewEngine.width += (subviewTraits.widthSizeInner.shrink / lineTotalShrink) * lineSpareWidth; + if (subviewEngine.width < 0.0) { + subviewEngine.width = 0.0; + } } } } -- (void)myCalcHorzLayoutSinglelineWeight:(CGSize)selfSize totalFloatHeight:(CGFloat)totalFloatHeight totalWeight:(CGFloat)totalWeight sbs:(NSArray *)sbs startIndex:(NSInteger)startIndex count:(NSInteger)count -{ - for (NSInteger j = startIndex - count; j < startIndex; j++) - { - UIView *sbv = sbs[j]; - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (sbvsc.weight != 0) - { - CGFloat tempHeight = _myCGFloatRound((totalFloatHeight * sbvsc.weight / totalWeight)); - if (sbvsc.heightSizeInner != nil) - tempHeight = [sbvsc.heightSizeInner measureWith:tempHeight]; - - totalFloatHeight -= tempHeight; - totalWeight -= sbvsc.weight; - - sbvmyFrame.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:tempHeight sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - sbvmyFrame.bottom = sbvmyFrame.top + sbvmyFrame.height; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == sbvsc.heightSizeInner) - sbvmyFrame.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:[sbvsc.widthSizeInner measureWith: sbvmyFrame.height ] sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - +- (void)myHorzLayoutCalculateSinglelineShrink:(CGFloat)lineTotalShrink lineSpareHeight:(CGFloat)lineSpareHeight startItemIndex:(NSInteger)startItemIndex itemCount:(NSInteger)itemCount withContext:(MyLayoutContext *)context { + if (_myCGFloatGreatOrEqual(lineSpareHeight, 0.0)) { + lineTotalShrink = 0.0; + } + if (itemCount == 0 || lineTotalShrink == 0.0) { + return; + } + + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + NSArray *subviewEngines = context->subviewEngines; + + //根据flex规约:如果总的压缩比重小于1则超出部分会乘以这个压缩比再进行压缩。 + if (layoutTraits.isFlex && lineTotalShrink < 1.0) { + lineSpareHeight *= lineTotalShrink; + } + //如果有压缩则调整子视图的高度。 + for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { + MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + if (subviewTraits.heightSizeInner.shrink != 0.0) { + subviewEngine.height += (subviewTraits.heightSizeInner.shrink / lineTotalShrink) * lineSpareHeight; + if (subviewEngine.height < 0.0) { + subviewEngine.height = 0.0; + } } } } - - -- (void)myCalcVertLayoutSinglelineAlignment:(CGSize)selfSize rowMaxHeight:(CGFloat)rowMaxHeight rowMaxWidth:(CGFloat)rowMaxWidth horzGravity:(MyGravity)horzGravity vertAlignment:(MyGravity)vertAlignment sbs:(NSArray *)sbs startIndex:(NSInteger)startIndex count:(NSInteger)count vertSpace:(CGFloat)vertSpace horzSpace:(CGFloat)horzSpace isEstimate:(BOOL)isEstimate lsc:(MyFlowLayout*)lsc -{ - - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - CGFloat paddingHorz = paddingLeading + paddingTrailing; - - CGFloat addXPos = 0; //多出来的空隙区域,用于停靠处理。 - CGFloat addXFill = 0; //多出来的平均区域,用于拉伸间距或者尺寸 - BOOL averageArrange = (horzGravity == MyGravity_Horz_Fill); - - if (!averageArrange || lsc.arrangedCount == 0) - { - switch (horzGravity) { - case MyGravity_Horz_Center: - { - addXPos = (selfSize.width - paddingHorz - rowMaxWidth) / 2; - } - break; - case MyGravity_Horz_Trailing: - { - addXPos = selfSize.width - paddingHorz - rowMaxWidth; //因为具有不考虑左边距,而原来的位置增加了左边距,因此 +- (void)myVertLayoutCalculateSingleline:(NSInteger)lineIndex vertAlignment:(MyGravity)vertAlignment lineMaxHeight:(CGFloat)lineMaxHeight lineMaxWidth:(CGFloat)lineMaxWidth lineTotalShrink:(CGFloat)lineTotalShrink startItemIndex:(NSInteger)startItemIndex itemCount:(NSInteger)itemCount withContext:(MyLayoutContext *)context { + if (itemCount == 0) { + return; + } + + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits *)context->layoutViewEngine.currentSizeClass; + NSArray *subviewEngines = context->subviewEngines; + CGFloat layoutContentWidth = context->selfSize.width - context->paddingLeading - context->paddingTrailing; + CGFloat lineSpareWidth = layoutContentWidth - lineMaxWidth; + if (_myCGFloatGreatOrEqual(lineSpareWidth, 0.0)) { + lineTotalShrink = 0.0; + } + if (lineTotalShrink != 0.0) { + lineMaxWidth = layoutContentWidth; + } + //计算每行的gravity情况。 + CGFloat addXPos = 0.0; //多出来的空隙区域,用于停靠处理。 + CGFloat addXPosInc = 0.0; + + MyGravity lineHorzGravity = context->horzGravity; + MyGravity lineVertAlignment = vertAlignment; + if (self.lineGravity != nil) { + MyGravity lineGravity = self.lineGravity(self, lineIndex, itemCount, (startItemIndex + itemCount) == subviewEngines.count); + lineHorzGravity = MYHORZGRAVITY(lineGravity); + if (lineHorzGravity == MyGravity_None) { + lineHorzGravity = context->horzGravity; + } else { + lineHorzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:lineHorzGravity]; + } + lineVertAlignment = MYVERTGRAVITY(lineGravity); + if (lineVertAlignment == MyGravity_None) { + lineVertAlignment = vertAlignment; + } + } + + //只有最后一行,并且数量小于arrangedCount,并且不是第一行才应用策略。 + BOOL applyLastlineGravityPolicy = ((startItemIndex + itemCount) == subviewEngines.count && + itemCount != layoutTraits.arrangedCount && + (lineHorzGravity == MyGravity_Horz_Between || lineHorzGravity == MyGravity_Horz_Around || lineHorzGravity == MyGravity_Horz_Among)); + + switch (lineHorzGravity) { + case MyGravity_Horz_Center: { + addXPos = (layoutContentWidth - lineMaxWidth) / 2.0; + } break; + case MyGravity_Horz_Trailing: { + addXPos = layoutContentWidth - lineMaxWidth; //因为具有不考虑左边距,而原来的位置增加了左边距,因此 + } break; + case MyGravity_Horz_Between: { + if (itemCount > 1 && (!applyLastlineGravityPolicy || layoutTraits.lastlineGravityPolicy == MyGravityPolicy_Always)) { + addXPosInc = (layoutContentWidth - lineMaxWidth) / (itemCount - 1); } - break; - case MyGravity_Horz_Between: - { - //总宽度减去最大的宽度。再除以数量表示每个应该扩展的空间。最后一行无效(如果最后一行的数量和其他行的数量一样除外)。 - if ((startIndex != sbs.count || count == lsc.arrangedCount) && count > 1) - { - addXFill = (selfSize.width - paddingHorz - rowMaxWidth) / (count - 1); + } break; + case MyGravity_Horz_Around: { + if (!applyLastlineGravityPolicy || layoutTraits.lastlineGravityPolicy == MyGravityPolicy_Always) { + if (itemCount > 1) { + addXPosInc = (layoutContentWidth - lineMaxWidth) / itemCount; + addXPos = addXPosInc / 2.0; + } else { + addXPos = (layoutContentWidth - lineMaxWidth) / 2.0; } } - break; - default: - break; - } - - //处理内容拉伸的情况。这里是只有内容约束布局才支持尺寸拉伸。 - if (lsc.arrangedCount == 0 && averageArrange) - { - //不是最后一行。。 - if (startIndex != sbs.count) - { - addXFill = (selfSize.width - paddingHorz - rowMaxWidth) / count; + } break; + case MyGravity_Horz_Among: { + if (!applyLastlineGravityPolicy || layoutTraits.lastlineGravityPolicy == MyGravityPolicy_Always) { + if (itemCount > 1) { + addXPosInc = (layoutContentWidth - lineMaxWidth) / (itemCount + 1); + addXPos = addXPosInc; + } else { + addXPos = (layoutContentWidth - lineMaxWidth) / 2.0; + } } - - } + } break; + default: + break; } - + //压缩减少的尺寸汇总。 + CGFloat totalShrinkSize = 0.0; + //基线位置 + CGFloat baselinePos = CGFLOAT_MAX; //将整行的位置进行调整。 - for (NSInteger j = startIndex - count; j < startIndex; j++) - { - UIView *sbv = sbs[j]; - - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (!isEstimate && self.intelligentBorderline != nil) - { - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - MyBaseLayout *sbvl = (MyBaseLayout*)sbv; - if (!sbvl.notUseIntelligentBorderline) - { - sbvl.leadingBorderline = nil; - sbvl.topBorderline = nil; - sbvl.trailingBorderline = nil; - sbvl.bottomBorderline = nil; + for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { + MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + if (!context->isEstimate && self.intelligentBorderline != nil) { + if ([subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + MyBaseLayout *sublayout = (MyBaseLayout *)subviewTraits.view; + if (!sublayout.notUseIntelligentBorderline) { + sublayout.leadingBorderline = nil; + sublayout.topBorderline = nil; + sublayout.trailingBorderline = nil; + sublayout.bottomBorderline = nil; //如果不是最后一行就画下面, - if (startIndex != sbs.count) - { - sbvl.bottomBorderline = self.intelligentBorderline; + if ((startItemIndex + itemCount) != subviewEngines.count) { + sublayout.bottomBorderline = self.intelligentBorderline; } - //如果不是最后一列就画右边, - if (j < startIndex - 1) - { - sbvl.trailingBorderline = self.intelligentBorderline; + if (itemIndex < (startItemIndex + itemCount) - 1) { + sublayout.trailingBorderline = self.intelligentBorderline; } - //如果最后一行的最后一个没有满列数时 - if (j == sbs.count - 1 && lsc.arrangedCount != count ) - { - sbvl.trailingBorderline = self.intelligentBorderline; + if (itemIndex == subviewEngines.count - 1 && layoutTraits.arrangedCount != itemCount) { + sublayout.trailingBorderline = self.intelligentBorderline; } - //如果有垂直间距则不是第一行就画上 - if (vertSpace != 0 && startIndex - count != 0) - { - sbvl.topBorderline = self.intelligentBorderline; + if (context->vertSpace != 0 && startItemIndex != 0) { + sublayout.topBorderline = self.intelligentBorderline; } - //如果有水平间距则不是第一列就画左 - if (horzSpace != 0 && j != startIndex - count) - { - sbvl.leadingBorderline = self.intelligentBorderline; + if (context->horzSpace != 0 && itemIndex != startItemIndex) { + sublayout.leadingBorderline = self.intelligentBorderline; } - } } } - MyGravity sbvVertAlignment = sbvsc.myAlignment & MyGravity_Horz_Mask; - if (sbvVertAlignment != MyGravity_None) - vertAlignment = sbvVertAlignment; + MyGravity sbvVertAlignment = MYVERTGRAVITY(subviewTraits.alignment); + if (sbvVertAlignment == MyGravity_None) { + sbvVertAlignment = lineVertAlignment; + } + //因为单行内的垂直间距拉伸被赋予紧凑排列,所以这里的定制化将不起作用。 + if (vertAlignment == MyGravity_Vert_Between) { + sbvVertAlignment = MyGravity_None; + } + UIFont *subviewFont = nil; + if (sbvVertAlignment == MyGravity_Vert_Baseline) { + subviewFont = [self myGetSubviewFont:subviewTraits.view]; + if (subviewFont == nil) { + sbvVertAlignment = MyGravity_Vert_Top; + } + } - if ((vertAlignment != MyGravity_None && vertAlignment != MyGravity_Vert_Top) || _myCGFloatNotEqual(addXPos, 0) || _myCGFloatNotEqual(addXFill, 0)) - { + if ((sbvVertAlignment != MyGravity_None && sbvVertAlignment != MyGravity_Vert_Top) || _myCGFloatNotEqual(addXPos, 0.0) || _myCGFloatNotEqual(addXPosInc, 0.0) || applyLastlineGravityPolicy || lineTotalShrink != 0.0) { + subviewEngine.leading += addXPos; - sbvmyFrame.leading += addXPos; - - //内容约束布局并且是拉伸尺寸。。 - if (lsc.arrangedCount == 0 && averageArrange) - { - //只拉伸宽度不拉伸间距 - sbvmyFrame.width += addXFill; + //处理对间距的压缩 + if (lineTotalShrink != 0.0) { + if (subviewTraits.leadingPosInner.shrink != 0.0) { + totalShrinkSize += (subviewTraits.leadingPosInner.shrink / lineTotalShrink) * lineSpareWidth; + } + subviewEngine.leading += totalShrinkSize; - if (j != startIndex - count) - { - sbvmyFrame.leading += addXFill * (j - (startIndex - count)); - + if (subviewTraits.trailingPosInner.shrink != 0.0) { + totalShrinkSize += (subviewTraits.trailingPosInner.shrink / lineTotalShrink) * lineSpareWidth; } } - else - { - //其他的只拉伸间距 - sbvmyFrame.leading += addXFill * (j - (startIndex - count)); - } + subviewEngine.leading += addXPosInc * (itemIndex - startItemIndex); + if (lineIndex != 0 && applyLastlineGravityPolicy && layoutTraits.lastlineGravityPolicy == MyGravityPolicy_Auto) { + //对齐前一行对应位置的 + subviewEngine.leading = subviewEngines[itemIndex - layoutTraits.arrangedCount].leading; + } - switch (vertAlignment) { - case MyGravity_Vert_Center: - { - sbvmyFrame.top += (rowMaxHeight - sbvsc.topPosInner.absVal - sbvsc.bottomPosInner.absVal - sbvmyFrame.height) / 2; - - } - break; - case MyGravity_Vert_Bottom: - { - sbvmyFrame.top += rowMaxHeight - sbvsc.topPosInner.absVal - sbvsc.bottomPosInner.absVal - sbvmyFrame.height; - } - break; - case MyGravity_Vert_Fill: - { - sbvmyFrame.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rowMaxHeight - sbvsc.topPosInner.absVal - sbvsc.bottomPosInner.absVal sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - } - break; + switch (sbvVertAlignment) { + case MyGravity_Vert_Center: { + subviewEngine.top += (lineMaxHeight - subviewTraits.topPosInner.measure - subviewTraits.bottomPosInner.measure - subviewEngine.height) / 2; + } break; + case MyGravity_Vert_Bottom: { + subviewEngine.top += lineMaxHeight - subviewTraits.topPosInner.measure - subviewTraits.bottomPosInner.measure - subviewEngine.height; + } break; + case MyGravity_Vert_Fill: { + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:lineMaxHeight - subviewTraits.topPosInner.measure - subviewTraits.bottomPosInner.measure subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } break; + case MyGravity_Vert_Stretch: { + if (subviewTraits.heightSizeInner.val == nil || (subviewTraits.heightSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:lineMaxHeight - subviewTraits.topPosInner.measure - subviewTraits.bottomPosInner.measure subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + } break; + case MyGravity_Vert_Baseline: { + if (baselinePos == CGFLOAT_MAX) { + baselinePos = subviewEngine.top + (subviewEngine.height - subviewFont.lineHeight) / 2.0 + subviewFont.ascender; + } else { + subviewEngine.top = baselinePos - subviewFont.ascender - (subviewEngine.height - subviewFont.lineHeight) / 2; + } + } break; default: break; } } } - } -- (void)myCalcHorzLayoutSinglelineAlignment:(CGSize)selfSize colMaxWidth:(CGFloat)colMaxWidth colMaxHeight:(CGFloat)colMaxHeight vertGravity:(MyGravity)vertGravity horzAlignment:(MyGravity)horzAlignment sbs:(NSArray *)sbs startIndex:(NSInteger)startIndex count:(NSInteger)count vertSpace:(CGFloat)vertSpace horzSpace:(CGFloat)horzSpace isEstimate:(BOOL)isEstimate lsc:(MyFlowLayout*)lsc -{ - - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingVert = paddingTop + paddingBottom; +- (void)myHorzLayoutCalculateSingleline:(NSInteger)lineIndex horzAlignment:(MyGravity)horzAlignment lineMaxWidth:(CGFloat)lineMaxWidth lineMaxHeight:(CGFloat)lineMaxHeight lineTotalShrink:(CGFloat)lineTotalShrink startItemIndex:(NSInteger)startItemIndex itemCount:(NSInteger)itemCount withContext:(MyLayoutContext *)context { + if (itemCount == 0) { + return; + } - CGFloat addYPos = 0; - CGFloat addYFill = 0; + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits *)context->layoutViewEngine.currentSizeClass; + NSArray *subviewEngines = context->subviewEngines; + CGFloat layoutContentHeight = context->selfSize.height - context->paddingTop - context->paddingBottom; + CGFloat lineSpareHeight = layoutContentHeight - lineMaxHeight; + if (_myCGFloatGreatOrEqual(lineSpareHeight, 0.0)) { + lineTotalShrink = 0.0; + } + if (lineTotalShrink != 0.0) { + lineMaxHeight = layoutContentHeight; + } - BOOL averageArrange = (vertGravity == MyGravity_Vert_Fill); + //计算每行的gravity情况。 + CGFloat addYPos = 0.0; + CGFloat addYPosInc = 0.0; + MyGravity lineHorzAlignment = horzAlignment; + MyGravity lineVertGravity = context->vertGravity; + if (self.lineGravity != nil) { + MyGravity lineGravity = self.lineGravity(self, lineIndex, itemCount, (startItemIndex + itemCount) == subviewEngines.count); + lineHorzAlignment = MYHORZGRAVITY(lineGravity); + if (lineHorzAlignment == MyGravity_None) { + lineHorzAlignment = horzAlignment; + } else { + lineHorzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:lineHorzAlignment]; + } + lineVertGravity = MYVERTGRAVITY(lineGravity); + if (lineVertGravity == MyGravity_None) { + lineVertGravity = context->vertGravity; + } + } - if (!averageArrange || lsc.arrangedCount == 0) - { - switch (vertGravity) { - case MyGravity_Vert_Center: - { - addYPos = (selfSize.height - paddingVert - colMaxHeight) / 2; - } - break; - case MyGravity_Vert_Bottom: - { - addYPos = selfSize.height - paddingVert - colMaxHeight; + //只有最后一行,并且数量小于arrangedCount才应用策略。 + BOOL applyLastlineGravityPolicy = ((startItemIndex + itemCount) == subviewEngines.count && + itemCount != layoutTraits.arrangedCount && + (lineVertGravity == MyGravity_Vert_Between || lineVertGravity == MyGravity_Vert_Around || lineVertGravity == MyGravity_Vert_Among)); + + switch (lineVertGravity) { + case MyGravity_Vert_Center: { + addYPos = (layoutContentHeight - lineMaxHeight) / 2.0; + } break; + case MyGravity_Vert_Bottom: { + addYPos = layoutContentHeight - lineMaxHeight; + } break; + case MyGravity_Vert_Between: { + if (itemCount > 1 && (!applyLastlineGravityPolicy || layoutTraits.lastlineGravityPolicy == MyGravityPolicy_Always)) { + addYPosInc = (layoutContentHeight - lineMaxHeight) / (itemCount - 1); } - break; - case MyGravity_Vert_Between: - { - //总宽度减去最大的宽度。再除以数量表示每个应该扩展的空间。最后一行无效(如果数量和单行的数量相等除外)。 - if ((startIndex != sbs.count || count == lsc.arrangedCount) && count > 1) - { - addYFill = (selfSize.height - paddingVert - colMaxHeight) / (count - 1); + } break; + case MyGravity_Vert_Around: { + if (!applyLastlineGravityPolicy || layoutTraits.lastlineGravityPolicy == MyGravityPolicy_Always) { + if (itemCount > 1) { + addYPosInc = (layoutContentHeight - lineMaxHeight) / itemCount; + addYPos = addYPosInc / 2.0; + } else { + addYPos = (layoutContentHeight - lineMaxHeight) / 2.0; } - } - default: - break; - } - - //处理内容拉伸的情况。 - if (lsc.arrangedCount == 0 && averageArrange) - { - if (startIndex != sbs.count) - { - addYFill = (selfSize.height - paddingVert - colMaxHeight) / count; + } break; + case MyGravity_Vert_Among: { + if (!applyLastlineGravityPolicy || layoutTraits.lastlineGravityPolicy == MyGravityPolicy_Always) { + if (itemCount > 1) { + addYPosInc = (layoutContentHeight - lineMaxHeight) / (itemCount + 1); + addYPos = addYPosInc; + } else { + addYPos = (layoutContentHeight - lineMaxHeight) / 2.0; + } } - - } - + } break; + default: + break; } - - - + //压缩减少的尺寸汇总。 + CGFloat totalShrinkSize = 0.0; //将整行的位置进行调整。 - for (NSInteger j = startIndex - count; j < startIndex; j++) - { - UIView *sbv = sbs[j]; - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - - if (!isEstimate && self.intelligentBorderline != nil) - { - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - MyBaseLayout *sbvl = (MyBaseLayout*)sbv; - if (!sbvl.notUseIntelligentBorderline) - { - sbvl.leadingBorderline = nil; - sbvl.topBorderline = nil; - sbvl.trailingBorderline = nil; - sbvl.bottomBorderline = nil; - + for (NSInteger itemIndex = startItemIndex; itemIndex < startItemIndex + itemCount; itemIndex++) { + MyLayoutEngine *subviewEngine = subviewEngines[itemIndex]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + + if (!context->isEstimate && self.intelligentBorderline != nil) { + if ([subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + MyBaseLayout *sublayout = (MyBaseLayout *)subviewTraits.view; + if (!sublayout.notUseIntelligentBorderline) { + sublayout.leadingBorderline = nil; + sublayout.topBorderline = nil; + sublayout.trailingBorderline = nil; + sublayout.bottomBorderline = nil; //如果不是最后一行就画下面, - if (j < startIndex - 1) - { - sbvl.bottomBorderline = self.intelligentBorderline; + if (itemIndex < (startItemIndex + itemCount) - 1) { + sublayout.bottomBorderline = self.intelligentBorderline; } - //如果不是最后一列就画右边, - if (startIndex != sbs.count ) - { - sbvl.trailingBorderline = self.intelligentBorderline; - + if ((startItemIndex + itemCount) != subviewEngines.count) { + sublayout.trailingBorderline = self.intelligentBorderline; } - //如果最后一行的最后一个没有满列数时 - if (j == sbs.count - 1 && lsc.arrangedCount != count ) - { - sbvl.bottomBorderline = self.intelligentBorderline; + if (itemIndex == subviewEngines.count - 1 && layoutTraits.arrangedCount != itemCount) { + sublayout.bottomBorderline = self.intelligentBorderline; } - //如果有垂直间距则不是第一行就画上 - if (vertSpace != 0 && j != startIndex - count) - { - sbvl.topBorderline = self.intelligentBorderline; + if (context->vertSpace != 0 && itemIndex != startItemIndex) { + sublayout.topBorderline = self.intelligentBorderline; } - //如果有水平间距则不是第一列就画左 - if (horzSpace != 0 && startIndex - count != 0 ) - { - sbvl.leadingBorderline = self.intelligentBorderline; - + if (context->horzSpace != 0 && startItemIndex != 0) { + sublayout.leadingBorderline = self.intelligentBorderline; } - - - } } } - CGFloat sbvHorzAlignment = [self myConvertLeftRightGravityToLeadingTrailing:sbvsc.myAlignment & MyGravity_Vert_Mask]; - if (sbvHorzAlignment != MyGravity_None) - horzAlignment = sbvHorzAlignment; - - if ((horzAlignment != MyGravity_None && horzAlignment != MyGravity_Horz_Leading) || _myCGFloatNotEqual(addYPos, 0) || _myCGFloatNotEqual(addYFill, 0) ) - { - sbvmyFrame.top += addYPos; + MyGravity subviewHorzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]; + if (subviewHorzAlignment == MyGravity_None) { + subviewHorzAlignment = lineHorzAlignment; + } + //因为单行内的水平间距拉伸被赋予紧凑排列,所以这里的定制化将不起作用。 + if (horzAlignment == MyGravity_Horz_Between) { + subviewHorzAlignment = MyGravity_None; + } + if ((subviewHorzAlignment != MyGravity_None && subviewHorzAlignment != MyGravity_Horz_Leading) || _myCGFloatNotEqual(addYPos, 0.0) || _myCGFloatNotEqual(addYPosInc, 0.0) || applyLastlineGravityPolicy || lineTotalShrink != 0.0) { + subviewEngine.top += addYPos; - if (lsc.arrangedCount == 0 && averageArrange) - { - //只拉伸宽度不拉伸间距 - sbvmyFrame.height += addYFill; + //处理对间距的压缩 + if (lineTotalShrink != 0.0) { + if (subviewTraits.topPosInner.shrink != 0.0) { + totalShrinkSize += (subviewTraits.topPosInner.shrink / lineTotalShrink) * lineSpareHeight; + } + subviewEngine.top += totalShrinkSize; - if (j != startIndex - count) - { - sbvmyFrame.top += addYFill * (j - (startIndex - count)); - + if (subviewTraits.bottomPosInner.shrink != 0.0) { + totalShrinkSize += (subviewTraits.bottomPosInner.shrink / lineTotalShrink) * lineSpareHeight; } } - else - { - //只拉伸间距 - sbvmyFrame.top += addYFill * (j - (startIndex - count)); - } + subviewEngine.top += addYPosInc * (itemIndex - startItemIndex); + if (lineIndex != 0 && applyLastlineGravityPolicy && layoutTraits.lastlineGravityPolicy == MyGravityPolicy_Auto) { + //对齐前一行对应位置的 + subviewEngine.top = subviewEngines[itemIndex - layoutTraits.arrangedCount].top; + } - switch (horzAlignment) { - case MyGravity_Horz_Center: - { - sbvmyFrame.leading += (colMaxWidth - sbvsc.leadingPosInner.absVal - sbvsc.trailingPosInner.absVal - sbvmyFrame.width) / 2; - - } - break; - case MyGravity_Horz_Trailing: - { - sbvmyFrame.leading += colMaxWidth - sbvsc.leadingPosInner.absVal - sbvsc.trailingPosInner.absVal - sbvmyFrame.width; - } - break; - case MyGravity_Horz_Fill: - { - sbvmyFrame.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:colMaxWidth - sbvsc.leadingPosInner.absVal - sbvsc.trailingPosInner.absVal sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - } - break; + switch (subviewHorzAlignment) { + case MyGravity_Horz_Center: { + subviewEngine.leading += (lineMaxWidth - subviewTraits.leadingPosInner.measure - subviewTraits.trailingPosInner.measure - subviewEngine.width) / 2; + } break; + case MyGravity_Horz_Trailing: { + subviewEngine.leading += lineMaxWidth - subviewTraits.leadingPosInner.measure - subviewTraits.trailingPosInner.measure - subviewEngine.width; + } break; + case MyGravity_Horz_Fill: { + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:lineMaxWidth - subviewTraits.leadingPosInner.measure - subviewTraits.trailingPosInner.measure subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } break; + case MyGravity_Horz_Stretch: { + if (subviewTraits.widthSizeInner.val == nil || (subviewTraits.widthSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:lineMaxWidth - subviewTraits.leadingPosInner.measure - subviewTraits.trailingPosInner.measure subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + } break; default: break; } @@ -640,40 +781,37 @@ - (void)myCalcHorzLayoutSinglelineAlignment:(CGSize)selfSize colMaxWidth:(CGFloa } } - --(CGFloat)myCalcSinglelineSize:(NSArray*)sbs space:(CGFloat)space -{ +- (CGFloat)myCalcSinglelineSize:(NSArray *)subviewEngines space:(CGFloat)space { CGFloat size = 0; - for (UIView *sbv in sbs) - { - size += sbv.myFrame.trailing; - if (sbv != sbs.lastObject) + for (MyLayoutEngine *subviewEngine in subviewEngines) { + size += subviewEngine.trailing; + if (subviewEngine != subviewEngines.lastObject) { size += space; + } } - return size; } --(NSArray*)myGetAutoArrangeSubviews:(NSMutableArray*)sbs selfSize:(CGFloat)selfSize space:(CGFloat)space -{ +- (NSArray *)myGetAutoArrangeSubviews:(NSMutableArray *)subviewEngines selfSize:(CGFloat)selfSize space:(CGFloat)space { - NSMutableArray *retArray = [NSMutableArray arrayWithCapacity:sbs.count]; - - NSMutableArray *bestSinglelineArray = [NSMutableArray arrayWithCapacity:sbs.count /2]; - - while (sbs.count) { + NSMutableArray *retArray = [NSMutableArray arrayWithCapacity:subviewEngines.count]; + //保存每行最佳 + NSMutableArray *bestSinglelineArray = [NSMutableArray arrayWithCapacity:subviewEngines.count / 2]; + while (subviewEngines.count) { - [self myCalcAutoArrangeSinglelineSubviews:sbs - index:0 - calcArray:@[] - selfSize:selfSize - space:space - bestSinglelineArray:bestSinglelineArray]; + //得到每行进行自动排列时最佳的子视图集。 + [self myCalcAutoArrangeSinglelineSubviews:subviewEngines + index:0 + calcArray:@[] + selfSize:selfSize + space:space + bestSinglelineArray:bestSinglelineArray]; [retArray addObjectsFromArray:bestSinglelineArray]; - [bestSinglelineArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) { - [sbs removeObject:obj]; + //将已经排列好的子视图从总的数组中删除。 + [bestSinglelineArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + [subviewEngines removeObject:obj]; }]; [bestSinglelineArray removeAllObjects]; @@ -682,1309 +820,1695 @@ -(NSArray*)myGetAutoArrangeSubviews:(NSMutableArray*)sbs selfSize:(CGFloat)selfS return retArray; } --(void)myCalcAutoArrangeSinglelineSubviews:(NSMutableArray*)sbs - index:(NSInteger)index - calcArray:(NSArray*)calcArray - selfSize:(CGFloat)selfSize - space:(CGFloat)space - bestSinglelineArray:(NSMutableArray*)bestSinglelineArray -{ - if (index >= sbs.count) - { +- (void)myCalcAutoArrangeSinglelineSubviews:(NSMutableArray *)subviewEngines + index:(NSInteger)index + calcArray:(NSArray *)calcArray + selfSize:(CGFloat)selfSize + space:(CGFloat)space + bestSinglelineArray:(NSMutableArray *)bestSinglelineArray { + if (index >= subviewEngines.count) { CGFloat s1 = [self myCalcSinglelineSize:calcArray space:space]; CGFloat s2 = [self myCalcSinglelineSize:bestSinglelineArray space:space]; - if (_myCGFloatLess(fabs(selfSize - s1), fabs(selfSize - s2)) && _myCGFloatLessOrEqual(s1, selfSize) ) - { + if (_myCGFloatLess(fabs(selfSize - s1), fabs(selfSize - s2)) && _myCGFloatLessOrEqual(s1, selfSize)) { [bestSinglelineArray setArray:calcArray]; } - return; } - - for (NSInteger i = index; i < sbs.count; i++) { + for (NSInteger i = index; i < subviewEngines.count; i++) { - - NSMutableArray *calcArray2 = [NSMutableArray arrayWithArray:calcArray]; - [calcArray2 addObject:sbs[i]]; + NSMutableArray *calcArray2 = [NSMutableArray arrayWithArray:calcArray]; + [calcArray2 addObject:subviewEngines[i]]; CGFloat s1 = [self myCalcSinglelineSize:calcArray2 space:space]; - if (_myCGFloatLessOrEqual(s1, selfSize)) - { + if (_myCGFloatLessOrEqual(s1, selfSize)) { CGFloat s2 = [self myCalcSinglelineSize:bestSinglelineArray space:space]; - if (_myCGFloatLess(fabs(selfSize - s1), fabs(selfSize - s2))) - { + if (_myCGFloatLess(fabs(selfSize - s1), fabs(selfSize - s2))) { [bestSinglelineArray setArray:calcArray2]; } - - if (_myCGFloatEqual(s1, selfSize)) + if (_myCGFloatEqual(s1, selfSize)) { break; - - [self myCalcAutoArrangeSinglelineSubviews:sbs - index:i + 1 - calcArray:calcArray2 - selfSize:selfSize - space:space - bestSinglelineArray:bestSinglelineArray]; - - } - else + } + [self myCalcAutoArrangeSinglelineSubviews:subviewEngines + index:i + 1 + calcArray:calcArray2 + selfSize:selfSize + space:space + bestSinglelineArray:bestSinglelineArray]; + + } else { break; - + } } - } - --(CGSize)myLayoutSubviewsForVertContent:(CGSize)selfSize sbs:(NSMutableArray*)sbs isEstimate:(BOOL)isEstimate lsc:(MyFlowLayout*)lsc -{ - - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - CGFloat paddingHorz = paddingLeading + paddingTrailing; - - CGFloat xPos = paddingLeading; - CGFloat yPos = paddingTop; - CGFloat rowMaxHeight = 0; //某一行的最高值。 - CGFloat rowMaxWidth = 0; //某一行的最宽值 +- (void)myDoVertOrientationContentLayoutWithContext:(MyLayoutContext *)context { + + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + NSMutableArray *subviewEngines = context->subviewEngines; - MyGravity vertGravity = lsc.gravity & MyGravity_Horz_Mask; - MyGravity horzGravity = [self myConvertLeftRightGravityToLeadingTrailing:lsc.gravity & MyGravity_Vert_Mask]; - MyGravity vertAlign = lsc.arrangedGravity & MyGravity_Horz_Mask; + MyGravity vertAlignment = MYVERTGRAVITY(layoutTraits.arrangedGravity); //支持浮动水平间距。 - CGFloat vertSpace = lsc.subviewVSpace; - CGFloat horzSpace = lsc.subviewHSpace; - CGFloat subviewSize = ((MyFlowLayoutViewSizeClass*)self.myCurrentSizeClass).subviewSize; - if (subviewSize != 0) - { - - CGFloat minSpace = ((MyFlowLayoutViewSizeClass*)self.myCurrentSizeClass).minSpace; - CGFloat maxSpace = ((MyFlowLayoutViewSizeClass*)self.myCurrentSizeClass).maxSpace; - - NSInteger rowCount = floor((selfSize.width - paddingHorz + minSpace) / (subviewSize + minSpace)); - if (rowCount > 1) - { - horzSpace = (selfSize.width - paddingHorz - subviewSize * rowCount)/(rowCount - 1); - if (_myCGFloatGreat(horzSpace, maxSpace)) - { - horzSpace = maxSpace; - - subviewSize = (selfSize.width - paddingHorz - horzSpace * (rowCount - 1)) / rowCount; - - } - } + CGFloat subviewWidth = [layoutTraits.flexSpace calcMaxMinSubviewSizeForContent:context->selfSize.width paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing space:&context->horzSpace]; + + CGFloat paddingHorz = context->paddingLeading + context->paddingTrailing; + CGFloat paddingVert = context->paddingTop + context->paddingBottom; + CGFloat xPos = context->paddingLeading; + CGFloat yPos = context->paddingTop; + CGFloat lineMaxHeight = 0.0; //某一行的最高值。 + CGFloat lineMaxWidth = 0.0; //某一行的最宽值。 + CGFloat maxLayoutWidth = 0.0; //所有行中最宽的值。 + + //limitedSelfWidth是用来限制子视图换行的宽度,默认是selfSize.width + //但是一种特殊情况就是布局视图宽度自适应,但是设置了最宽宽度的情况。 + //这种情况下当子视图超过最宽宽度时还是需要进行换行处理。 + //而如果没有设置最宽宽度的话那么默认限制的宽度就是最大值CGFLOAT_MAX + CGFloat limitedSelfWidth = context->selfSize.width; + if (layoutTraits.widthSizeInner.wrapVal) { + limitedSelfWidth = [self myGetBoundLimitMeasure:layoutTraits.widthSizeInner.uBoundValInner subview:self anchorType:layoutTraits.widthSizeInner.anchorType subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size isUBound:YES]; + limitedSelfWidth = _myCGFloatMin(limitedSelfWidth, CGFLOAT_MAX); } - - if (lsc.autoArrange) - { + if (layoutTraits.autoArrange) { //计算出每个子视图的宽度。 - for (UIView* sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; + for (MyLayoutEngine *subviewEngine in subviewEngines) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; #ifdef DEBUG //约束异常:垂直流式布局设置autoArrange为YES时,子视图不能将weight设置为非0. - NSCAssert(sbvsc.weight == 0, @"Constraint exception!! vertical flow layout:%@ 's subview:%@ can't set weight when the autoArrange set to YES",self, sbv); + NSCAssert(subviewTraits.weight == 0, @"Constraint exception!! vertical flow layout:%@ 's subview:%@ can't set weight when the autoArrange set to YES", self, subviewTraits.view); #endif - CGFloat leadingSpace = sbvsc.leadingPosInner.absVal; - CGFloat trailingSpace = sbvsc.trailingPosInner.absVal; - CGRect rect = sbvmyFrame.frame; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - - [self mySetSubviewRelativeDimeSize:sbvsc.widthSizeInner selfSize:selfSize lsc:lsc pRect:&rect]; + CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; - //暂时把宽度存放sbv.myFrame.trailing上。因为浮动布局来说这个属性无用。 - sbvmyFrame.trailing = leadingSpace + rect.size.width + trailingSpace; - if (_myCGFloatGreat(sbvmyFrame.trailing, selfSize.width - paddingHorz)) - sbvmyFrame.trailing = selfSize.width - paddingHorz; + //暂时把宽度存放sbv.myFrame.trailing上。因为流式布局来说这个属性无用。 + subviewEngine.trailing = leadingSpacing + subviewEngine.width + trailingSpacing; + if (_myCGFloatGreat(subviewEngine.trailing, context->selfSize.width - paddingHorz)) { + subviewEngine.trailing = context->selfSize.width - paddingHorz; + } } - [sbs setArray:[self myGetAutoArrangeSubviews:sbs selfSize:selfSize.width - paddingHorz space:horzSpace]]; - + [subviewEngines setArray:[self myGetAutoArrangeSubviews:subviewEngines selfSize:context->selfSize.width - paddingHorz space:context->horzSpace]]; } - - NSMutableIndexSet *arrangeIndexSet = [NSMutableIndexSet new]; - NSInteger arrangedIndex = 0; + //每行行首子视图的索引位置。 + NSMutableIndexSet *lineFirstSubviewIndexSet = [NSMutableIndexSet new]; + NSInteger lineIndex = 0; //行的索引。 + NSInteger itemIndex = 0; //行内子视图的索引 + CGFloat lineTotalWeight = 0.0; NSInteger i = 0; - for (; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - CGFloat topSpace = sbvsc.topPosInner.absVal; - CGFloat leadingSpace = sbvsc.leadingPosInner.absVal; - CGFloat bottomSpace = sbvsc.bottomPosInner.absVal; - CGFloat trailingSpace = sbvsc.trailingPosInner.absVal; - CGRect rect = sbvmyFrame.frame; + for (; i < subviewEngines.count; i++) { + MyLayoutEngine *subviewEngine = subviewEngines[i]; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; - if (subviewSize != 0) - rect.size.width = subviewSize; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; + //这里先算一下那些有约束的高度,因为有可能有一些子视图的宽度等于这个子视图的高度。 + if (subviewTraits.heightSizeInner.val != nil) { + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + //计算子视图的宽度。 + if (subviewWidth != 0.0) { + subviewEngine.width = subviewWidth - leadingSpacing - trailingSpacing; + } else if (subviewTraits.widthSizeInner.val != nil) { + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; + + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { //特殊处理宽度等于高度的情况 + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; + } + } else if (subviewTraits.weight != 0.0) { + if (layoutTraits.isFlex) { + subviewEngine.width = 0.0; + } else { + CGFloat lineSpareWidth = context->selfSize.width - context->paddingTrailing - xPos - leadingSpacing - trailingSpacing; + if (itemIndex != 0) { + lineSpareWidth -= context->horzSpace; + } + if (_myCGFloatLessOrEqual(lineSpareWidth, 0.0)) { + //如果当前行的剩余空间不够,则需要换行来计算相对的宽度占比,这时候剩余空间就是按整行来算,并且将行索引设置为0 + lineSpareWidth = context->selfSize.width - paddingHorz; + } + + subviewEngine.width = (lineSpareWidth + subviewTraits.widthSizeInner.addVal) * subviewTraits.weight - leadingSpacing - trailingSpacing; + } + } - [self mySetSubviewRelativeDimeSize:sbvsc.widthSizeInner selfSize:selfSize lsc:lsc pRect:&rect]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; - [self mySetSubviewRelativeDimeSize:sbvsc.heightSizeInner selfSize:selfSize lsc:lsc pRect:&rect]; + //特殊处理自身高度等于自身宽度的情况。 + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { //特殊处理高度等于宽度的情况 + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + //计算xPos的值加上leadingSpacing + subviewEngine.width + trailingSpacing 的值要小于整体的宽度。 + CGFloat place = xPos + leadingSpacing + subviewEngine.width + trailingSpacing; + if (itemIndex != 0) { + place += context->horzSpace; + } + place += context->paddingTrailing; - if (sbvsc.weight != 0) - { - //如果过了,则表示当前的剩余空间为0了,所以就按新的一行来算。。 - CGFloat floatWidth = selfSize.width - paddingHorz - rowMaxWidth; - if (_myCGFloatLessOrEqual(floatWidth, 0)) - { - floatWidth += rowMaxWidth; - arrangedIndex = 0; - } + //sbv所占据的宽度超过了布局视图的限制宽度时需要换行。 + if (place - limitedSelfWidth > 0.0001) { + context->selfSize.width = limitedSelfWidth; + + //保存行首子视图的索引 + [lineFirstSubviewIndexSet addIndex:i - itemIndex]; - if (arrangedIndex != 0) - floatWidth -= horzSpace; + //拉伸以及调整行内子视图的宽度。 + [self myVertLayoutAdjustSingleline:lineIndex lineSpareWidth:context->selfSize.width - context->paddingTrailing - xPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; - rect.size.width = (floatWidth + sbvsc.widthSizeInner.addVal) * sbvsc.weight - leadingSpace - trailingSpace; + xPos = context->paddingLeading; + + //如果这个sbv的宽度大于整体布局视图的宽度。则将子视图的宽度缩小变为和布局视图一样宽 + if (_myCGFloatGreat(leadingSpacing + trailingSpacing + subviewEngine.width, context->selfSize.width - paddingHorz)) { + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:context->selfSize.width - paddingHorz - leadingSpacing - trailingSpacing subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + lineTotalWeight = 0.0; + lineIndex++; + itemIndex = 0; } + if (itemIndex != 0) { + xPos += context->horzSpace; + } + subviewEngine.leading = xPos + leadingSpacing; + xPos += leadingSpacing + subviewEngine.width + trailingSpacing; - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == sbvsc.widthSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width ]; - + if (layoutTraits.isFlex && subviewTraits.weight != 0.0) { + lineTotalWeight += subviewTraits.weight; + } - //如果高度是浮动的则需要调整高度。 - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; + itemIndex++; + } + + //最后一行的行首索引 + [lineFirstSubviewIndexSet addIndex:i - itemIndex]; + + //在宽度为自适应时,如果没有设置最大宽度限制,那么就一定是单行,因此宽度就是子视图的总和。 + //如果设置了最大宽度限制时,那就要区分最后一行是单行还是多行,所以我们取限宽和当前计算出的宽度的最小值,并且再取selfSize.width和前面比较结果的最大值。 + if (layoutTraits.widthSizeInner.wrapVal) { + if (limitedSelfWidth == CGFLOAT_MAX) { + context->selfSize.width = _myCGFloatMax(xPos + context->paddingTrailing, [self myGetBoundLimitMeasure:layoutTraits.widthSizeInner.uBoundValInner subview:self anchorType:layoutTraits.widthSizeInner.anchorType subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size isUBound:NO]); + } else { + context->selfSize.width = _myCGFloatMax(_myCGFloatMin(xPos + context->paddingTrailing, limitedSelfWidth), context->selfSize.width); + } + } + + [self myVertLayoutAdjustSingleline:lineIndex lineSpareWidth:context->selfSize.width - context->paddingTrailing - xPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; + + xPos = context->paddingLeading; + lineIndex = 0; //行的索引。 + itemIndex = 0; //行内的子视图索引 + NSInteger oldLineFirstIndex = 0; + i = 0; + for (; i < subviewEngines.count; i++) { + MyLayoutEngine *subviewEngine = subviewEngines[i]; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + + CGFloat topSpace = subviewTraits.topPosInner.measure; + CGFloat leadingSpace = subviewTraits.leadingPosInner.measure; + CGFloat bottomSpace = subviewTraits.bottomPosInner.measure; + CGFloat trailingSpace = subviewTraits.trailingPosInner.measure; + + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + //计算子视图的高度。 + if (subviewTraits.heightSizeInner.val != nil) { + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; + } else if (context->vertGravity == MyGravity_Vert_Fill || context->vertGravity == MyGravity_Vert_Stretch) { + subviewEngine.height = 0; + } - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; - //计算xPos的值加上leadingSpace + rect.size.width + trailingSpace 的值要小于整体的宽度。 - CGFloat place = xPos + leadingSpace + rect.size.width + trailingSpace; - if (arrangedIndex != 0) - place += horzSpace; - place += paddingTrailing; + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { //特殊处理高度等于宽度的情况 + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } - //sbv所占据的宽度要超过了视图的整体宽度,因此需要换行。但是如果arrangedIndex为0的话表示这个控件的整行的宽度和布局视图保持一致。 - if (place - selfSize.width > 0.0001) - { - xPos = paddingLeading; - yPos += vertSpace; - yPos += rowMaxHeight; - - - [arrangeIndexSet addIndex:i - arrangedIndex]; - //计算每行的gravity情况。 - [self myCalcVertLayoutSinglelineAlignment:selfSize rowMaxHeight:rowMaxHeight rowMaxWidth:rowMaxWidth horzGravity:horzGravity vertAlignment:vertAlign sbs:sbs startIndex:i count:arrangedIndex vertSpace:vertSpace horzSpace:horzSpace isEstimate:isEstimate lsc:lsc]; + //计算xPos的值加上leadingSpacing + subviewEngine.width + trailingSpacing 的值要小于整体的宽度。 + maxLayoutWidth = xPos + leadingSpace + subviewEngine.width + trailingSpace; + if (itemIndex != 0) { + maxLayoutWidth += context->horzSpace; + } + maxLayoutWidth += context->paddingTrailing; + + NSUInteger lineFirstIndex = [lineFirstSubviewIndexSet indexLessThanOrEqualToIndex:i]; + if (oldLineFirstIndex != lineFirstIndex) { + oldLineFirstIndex = lineFirstIndex; - //计算单独的sbv的宽度是否大于整体的宽度。如果大于则缩小宽度。 - if (_myCGFloatGreat(leadingSpace + trailingSpace + rect.size.width, selfSize.width - paddingHorz)) - { - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:selfSize.width - paddingHorz - leadingSpace - trailingSpace sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - { - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - } - - } + xPos = context->paddingLeading; + yPos += context->vertSpace; + yPos += lineMaxHeight; - rowMaxHeight = 0; - rowMaxWidth = 0; - arrangedIndex = 0; + [self myVertLayoutCalculateSingleline:lineIndex vertAlignment:vertAlignment lineMaxHeight:lineMaxHeight lineMaxWidth:lineMaxWidth lineTotalShrink:0 startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; + lineMaxHeight = 0.0; + lineMaxWidth = 0.0; + itemIndex = 0; + lineIndex++; } - if (arrangedIndex != 0) - xPos += horzSpace; - - - rect.origin.x = xPos + leadingSpace; - rect.origin.y = yPos + topSpace; - xPos += leadingSpace + rect.size.width + trailingSpace; - - if (_myCGFloatLess(rowMaxHeight, topSpace + bottomSpace + rect.size.height)) - rowMaxHeight = topSpace + bottomSpace + rect.size.height; - - if (_myCGFloatLess(rowMaxWidth, (xPos - paddingLeading))) - rowMaxWidth = (xPos - paddingLeading); - - - - sbvmyFrame.frame = rect; - - arrangedIndex++; - - + if (itemIndex != 0) { + xPos += context->horzSpace; + } + subviewEngine.leading = xPos + leadingSpace; + subviewEngine.top = yPos + topSpace; + xPos += leadingSpace + subviewEngine.width + trailingSpace; + if (_myCGFloatLess(lineMaxHeight, topSpace + bottomSpace + subviewEngine.height)) { + lineMaxHeight = topSpace + bottomSpace + subviewEngine.height; + } + if (_myCGFloatLess(lineMaxWidth, (xPos - context->paddingLeading))) { + lineMaxWidth = (xPos - context->paddingLeading); + } + itemIndex++; } - //最后一行 - [arrangeIndexSet addIndex:i - arrangedIndex]; - - [self myCalcVertLayoutSinglelineAlignment:selfSize rowMaxHeight:rowMaxHeight rowMaxWidth:rowMaxWidth horzGravity:horzGravity vertAlignment:vertAlign sbs:sbs startIndex:i count:arrangedIndex vertSpace:vertSpace horzSpace:horzSpace isEstimate:isEstimate lsc:lsc]; - + yPos += lineMaxHeight + context->paddingBottom; - if (lsc.wrapContentHeight) - selfSize.height = yPos + paddingBottom + rowMaxHeight; - else - { - CGFloat addYPos = 0; - CGFloat between = 0; - CGFloat fill = 0; - - if (vertGravity == MyGravity_Vert_Center) - { - addYPos = (selfSize.height - paddingBottom - rowMaxHeight - yPos) / 2; - } - else if (vertGravity == MyGravity_Vert_Bottom) - { - addYPos = selfSize.height - paddingBottom - rowMaxHeight - yPos; - } - else if (vertGravity == MyGravity_Vert_Fill) - { - if (arrangeIndexSet.count > 0) - fill = (selfSize.height - paddingBottom - rowMaxHeight - yPos) / arrangeIndexSet.count; - } - else if (vertGravity == MyGravity_Vert_Between) - { - if (arrangeIndexSet.count > 1) - between = (selfSize.height - paddingBottom - rowMaxHeight - yPos) / (arrangeIndexSet.count - 1); - } - - if (addYPos != 0 || between != 0 || fill != 0) - { - int line = 0; - NSUInteger lastIndex = 0; - for (int i = 0; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - - MyFrame *sbvmyFrame = sbv.myFrame; - - sbvmyFrame.top += addYPos; - - //找到行的最初索引。 - NSUInteger index = [arrangeIndexSet indexLessThanOrEqualToIndex:i]; - if (lastIndex != index) - { - lastIndex = index; - line ++; + //内容填充约束布局的宽度包裹计算。 + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:maxLayoutWidth subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:yPos subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + NSInteger arranges = lineFirstSubviewIndexSet.count; + //根据flex规则:如果只有一行则整个高度都作为子视图的拉伸和停靠区域。 + if (layoutTraits.isFlex && arranges == 1) { + lineMaxHeight = context->selfSize.height - paddingVert; + } + //最后一行 + [self myVertLayoutCalculateSingleline:lineIndex vertAlignment:vertAlignment lineMaxHeight:lineMaxHeight lineMaxWidth:lineMaxWidth lineTotalShrink:0 startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; + + //整体的停靠 + if (context->vertGravity != MyGravity_None && context->selfSize.height != yPos) { + //根据flex标准:只有在多行下vertGravity才有意义。非flex标准则不受这个条件约束。 + if (arranges > 1 || !layoutTraits.isFlex) { + CGFloat addYPos = 0.0; + CGFloat between = 0.0; + CGFloat fill = 0.0; + + if (arranges <= 1 && context->vertGravity == MyGravity_Vert_Around) { + context->vertGravity = MyGravity_Vert_Center; + } + if (context->vertGravity == MyGravity_Vert_Center) { + addYPos = (context->selfSize.height - yPos) / 2; + } else if (context->vertGravity == MyGravity_Vert_Bottom) { + addYPos = context->selfSize.height - yPos; + } else if (context->vertGravity == MyGravity_Vert_Fill || context->vertGravity == MyGravity_Vert_Stretch) { + if (arranges > 0) { + fill = (context->selfSize.height - yPos) / arranges; + } + //满足flex规则:如果剩余的空间是负数,该值等效于'flex-start' + if (fill < 0.0 && context->vertGravity == MyGravity_Vert_Stretch) { + fill = 0.0; + } + } else if (context->vertGravity == MyGravity_Vert_Between) { + if (arranges > 1) { + between = (context->selfSize.height - yPos) / (arranges - 1); + } + } else if (context->vertGravity == MyGravity_Vert_Around) { + between = (context->selfSize.height - yPos) / arranges; + } else if (context->vertGravity == MyGravity_Vert_Among) { + between = (context->selfSize.height - yPos) / (arranges + 1); + } + + if (addYPos != 0.0 || between != 0.0 || fill != 0.0) { + int lineidx = 0; + NSUInteger lastIndex = 0; + for (int i = 0; i < subviewEngines.count; i++) { + + MyLayoutEngine *subviewEngine = subviewEngines[i]; + + subviewEngine.top += addYPos; + + //找到行的最初索引。 + NSUInteger index = [lineFirstSubviewIndexSet indexLessThanOrEqualToIndex:i]; + if (lastIndex != index) { + lastIndex = index; + lineidx++; + } + + if (context->vertGravity == MyGravity_Vert_Stretch) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + //只有在没有约束,或者非布局视图下的高度自适应约束才会被拉伸。 + if (subviewTraits.heightSizeInner.val == nil || (subviewTraits.heightSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { + subviewEngine.height += fill; + } else { + //因为每行都增加了fill。所以如果有行内对齐则需要这里调整。 + MyGravity subviewVertAlignment = MYVERTGRAVITY( subviewTraits.alignment); + if (subviewVertAlignment == MyGravity_None) { + subviewVertAlignment = vertAlignment; + } + if (subviewVertAlignment == MyGravity_Vert_Center) { + subviewEngine.top += fill / 2.0; + } else if (subviewVertAlignment == MyGravity_Vert_Bottom) { + subviewEngine.top += fill; + } + } + } else { + subviewEngine.height += fill; + } + subviewEngine.top += fill * lineidx; + + subviewEngine.top += between * lineidx; + + if (context->vertGravity == MyGravity_Vert_Around) { + subviewEngine.top += (between / 2.0); + } + if (context->vertGravity == MyGravity_Vert_Among) { + subviewEngine.top += between; + } } - - sbvmyFrame.height += fill; - sbvmyFrame.top += fill * line; - - sbvmyFrame.top += between * line; - } } - } +} + +- (void)myDoVertOrientationCountLayoutWithContext:(MyLayoutContext *)context { + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + NSMutableArray *subviewEngines = context->subviewEngines; - return selfSize; -} - - --(CGSize)myLayoutSubviewsForVert:(CGSize)selfSize sbs:(NSMutableArray*)sbs isEstimate:(BOOL)isEstimate lsc:(MyFlowLayout*)lsc -{ - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - CGFloat paddingHorz = paddingLeading + paddingTrailing; - CGFloat paddingVert = paddingTop + paddingBottom; - - NSInteger arrangedCount = lsc.arrangedCount; - CGFloat xPos = paddingLeading; - CGFloat yPos = paddingTop; - CGFloat rowMaxHeight = 0; //某一行的最高值。 - CGFloat rowMaxWidth = 0; //某一行的最宽值 - CGFloat maxWidth = paddingLeading; //全部行的最宽值 - MyGravity vertGravity = lsc.gravity & MyGravity_Horz_Mask; - MyGravity horzGravity = [self myConvertLeftRightGravityToLeadingTrailing:lsc.gravity & MyGravity_Vert_Mask]; - MyGravity vertAlign = lsc.arrangedGravity & MyGravity_Horz_Mask; + BOOL autoArrange = layoutTraits.autoArrange; + NSInteger arrangedCount = layoutTraits.arrangedCount; - + MyGravity vertAlignment = MYVERTGRAVITY(layoutTraits.arrangedGravity); - CGFloat vertSpace = lsc.subviewVSpace; - CGFloat horzSpace = lsc.subviewHSpace; + CGFloat subviewWidth = [layoutTraits.flexSpace calcMaxMinSubviewSize:context->selfSize.width arrangedCount:arrangedCount paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing space:&context->horzSpace]; + + CGFloat paddingHorz = context->paddingLeading + context->paddingTrailing; + CGFloat paddingVert = context->paddingTop + context->paddingBottom; + + CGFloat xPos = context->paddingLeading; + CGFloat yPos = context->paddingTop; + CGFloat lineMaxHeight = 0.0; //某一行的最高值。 + CGFloat lineMaxWidth = 0.0; //某一行的最宽值 + CGFloat maxLayoutWidth = 0.0; //全部行的最大宽度 + CGFloat maxLayoutHeight = context->paddingTop; //最大的高度 #if TARGET_OS_IOS //判断父滚动视图是否分页滚动 BOOL isPagingScroll = (self.superview != nil && - [self.superview isKindOfClass:[UIScrollView class]] && ((UIScrollView*)self.superview).isPagingEnabled); + [self.superview isKindOfClass:[UIScrollView class]] && + ((UIScrollView *)self.superview).isPagingEnabled); #else BOOL isPagingScroll = NO; #endif - CGFloat pagingItemHeight = 0; - CGFloat pagingItemWidth = 0; + CGFloat pagingItemHeight = 0.0; + CGFloat pagingItemWidth = 0.0; BOOL isVertPaging = NO; BOOL isHorzPaging = NO; - if (lsc.pagedCount > 0 && self.superview != nil) - { - NSInteger rows = lsc.pagedCount / arrangedCount; //每页的行数。 - + if (layoutTraits.pagedCount > 0 && self.superview != nil) { + NSInteger rows = layoutTraits.pagedCount / arrangedCount; //每页的行数。 //对于垂直流式布局来说,要求要有明确的宽度。因此如果我们启用了分页又设置了宽度包裹时则我们的分页是从左到右的排列。否则分页是从上到下的排列。 - if (lsc.wrapContentWidth) - { + if (layoutTraits.widthSizeInner.wrapVal) { isHorzPaging = YES; - if (isPagingScroll) - pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - paddingHorz - (arrangedCount - 1) * horzSpace ) / arrangedCount; - else - pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - paddingLeading - arrangedCount * horzSpace ) / arrangedCount; - - pagingItemHeight = (selfSize.height - paddingVert - (rows - 1) * vertSpace) / rows; - } - else - { + if (isPagingScroll) { + pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - paddingHorz - (arrangedCount - 1) * context->horzSpace) / arrangedCount; + } else { + pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - context->paddingLeading - arrangedCount * context->horzSpace) / arrangedCount; + } + //如果是水平滚动则如果布局不是高度自适应才让条目的高度生效。 + if (!layoutTraits.heightSizeInner.wrapVal) { + pagingItemHeight = (context->selfSize.height - paddingVert - (rows - 1) * context->vertSpace) / rows; + } + } else { isVertPaging = YES; - pagingItemWidth = (selfSize.width - paddingHorz - (arrangedCount - 1) * horzSpace) / arrangedCount; + pagingItemWidth = (context->selfSize.width - paddingHorz - (arrangedCount - 1) * context->horzSpace) / arrangedCount; //分页滚动时和非分页滚动时的高度计算是不一样的。 - if (isPagingScroll) - pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - paddingVert - (rows - 1) * vertSpace) / rows; - else - pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - paddingTop - rows * vertSpace) / rows; - + if (isPagingScroll) { + pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - paddingVert - (rows - 1) * context->vertSpace) / rows; + } else { + if ([self.superview isKindOfClass:[UIScrollView class]]) { + pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - context->paddingTop - rows * context->vertSpace) / rows; + } else { + pagingItemHeight = (context->selfSize.height - paddingVert - (rows - 1) * context->vertSpace) / rows; + } + } } - } + //在宽度自适应的情况下有可能有最小宽度的约束。 + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:0 subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + //平均宽度,当布局的gravity设置为Horz_Fill时指定这个平均宽度值。 + CGFloat averageWidth = 0.0; + if (context->horzGravity == MyGravity_Horz_Fill) { + averageWidth = (context->selfSize.width - paddingHorz - (arrangedCount - 1) * context->horzSpace) / arrangedCount; + if (averageWidth < 0.0) { + averageWidth = 0.0; + } + } - BOOL averageArrange = (horzGravity == MyGravity_Horz_Fill); - - NSInteger arrangedIndex = 0; + //得到行数 + NSInteger arranges = floor((subviewEngines.count + arrangedCount - 1.0) / arrangedCount); + CGFloat lineTotalFixedWidths[arranges]; //所有行的固定宽度。 + CGFloat lineTotalWeights[arranges]; //所有行的总比重 + CGFloat lineTotalShrinks[arranges]; //所有行的总压缩 + + CGFloat lineTotalFixedWidth = 0.0; + CGFloat lineTotalWeight = 0.0; + CGFloat lineTotalShrink = 0.0; //某一行的总压缩比重。 + BOOL hasTotalWeight = NO; //是否有比重计算,这个标志用于加快处理速度 + BOOL hasTotalShrink = NO; //是否有压缩计算,这个标志用于加快处理速度 + //行内子视图的索引号 NSInteger i = 0; - CGFloat rowTotalWeight = 0; - CGFloat rowTotalFixedWidth = 0; - for (; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (arrangedIndex >= arrangedCount) - { - arrangedIndex = 0; - - if (rowTotalWeight != 0 && !averageArrange) - { - [self myCalcVertLayoutSinglelineWeight:selfSize totalFloatWidth:selfSize.width - paddingHorz - rowTotalFixedWidth totalWeight:rowTotalWeight sbs:sbs startIndex:i count:arrangedCount]; + NSInteger itemIndex = 0; + NSInteger lineIndex = 0; //行索引 + for (; i < subviewEngines.count; i++) { + MyLayoutEngine *subviewEngine = subviewEngines[i]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + + CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; + CGFloat topSpacing = subviewTraits.topPosInner.measure; + CGFloat bottomSpacing = subviewTraits.bottomPosInner.measure; + + if (itemIndex >= arrangedCount) { + lineTotalFixedWidths[lineIndex] = lineTotalFixedWidth; + lineTotalWeights[lineIndex] = lineTotalWeight; + lineTotalShrinks[lineIndex] = lineTotalShrink; + + if (!hasTotalWeight) { + hasTotalWeight = lineTotalWeight > 0; + } + if (!hasTotalShrink) { + hasTotalShrink = lineTotalShrink > 0; } - rowTotalWeight = 0; - rowTotalFixedWidth = 0; + if (lineTotalFixedWidth > maxLayoutWidth) { + maxLayoutWidth = lineTotalFixedWidth; + } + lineTotalFixedWidth = 0.0; + lineTotalWeight = 0.0; + lineTotalShrink = 0.0; + itemIndex = 0; + lineIndex++; } - CGFloat leadingSpace = sbvsc.leadingPosInner.absVal; - CGFloat trailingSpace = sbvsc.trailingPosInner.absVal; - CGRect rect = sbvmyFrame.frame; + //计算每行的宽度。 + if (averageWidth != 0.0) { + subviewEngine.width = averageWidth - leadingSpacing - trailingSpacing; + } else if (subviewWidth != 0.0) { + subviewEngine.width = subviewWidth - leadingSpacing - trailingSpacing; + } else if (pagingItemWidth != 0.0) { + subviewEngine.width = pagingItemWidth - leadingSpacing - trailingSpacing; + } else if (subviewTraits.widthSizeInner.val != nil) { + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { //特殊处理宽度等于高度的情况 + + if (pagingItemHeight != 0.0) { + subviewEngine.height = pagingItemHeight - topSpacing - bottomSpacing; + } else { + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; + } + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; + } else { + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; + } + } else if (subviewTraits.weight != 0.0) { //在没有设置任何约束,并且weight不为0时则使用比重来求宽度,因此这预先将宽度设置为0 + subviewEngine.width = 0.0; + } + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + itemIndex++; - if (sbvsc.weight != 0) - { - - rowTotalWeight += sbvsc.weight; + lineTotalFixedWidth += subviewEngine.width; + lineTotalFixedWidth += leadingSpacing + trailingSpacing; + if (itemIndex < arrangedCount) { + lineTotalFixedWidth += context->horzSpace; } - else - { - if (pagingItemWidth != 0) - rect.size.width = pagingItemWidth; - - if (sbvsc.widthSizeInner.dimeNumVal != nil && !averageArrange) - rect.size.width = sbvsc.widthSizeInner.measure; - - - [self mySetSubviewRelativeDimeSize:sbvsc.widthSizeInner selfSize:selfSize lsc:lsc pRect:&rect]; - - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - rowTotalFixedWidth += rect.size.width; + //计算总拉伸比。 + if (subviewTraits.weight != 0.0) { + lineTotalWeight += subviewTraits.weight; } - rowTotalFixedWidth += leadingSpace + trailingSpace; - - if (arrangedIndex != (arrangedCount - 1)) - rowTotalFixedWidth += horzSpace; - - - sbvmyFrame.frame = rect; - - arrangedIndex++; - + //计算总的压缩比 + lineTotalShrink += subviewTraits.leadingPosInner.shrink + subviewTraits.trailingPosInner.shrink; + lineTotalShrink += subviewTraits.widthSizeInner.shrink; } //最后一行。 - if (rowTotalWeight != 0 && !averageArrange) - { - if (arrangedIndex < arrangedCount) - rowTotalFixedWidth -= horzSpace; + if (arranges > 0) { + if (itemIndex < arrangedCount) { + lineTotalFixedWidth -= context->horzSpace; + } + lineTotalWeights[lineIndex] = lineTotalWeight; + lineTotalFixedWidths[lineIndex] = lineTotalFixedWidth; + lineTotalShrinks[lineIndex] = lineTotalShrink; - [self myCalcVertLayoutSinglelineWeight:selfSize totalFloatWidth:selfSize.width - paddingHorz - rowTotalFixedWidth totalWeight:rowTotalWeight sbs:sbs startIndex:i count:arrangedIndex]; + if (!hasTotalWeight) { + hasTotalWeight = lineTotalWeight > 0; + } + if (!hasTotalShrink) { + hasTotalShrink = lineTotalShrink > 0; + } + if (lineTotalFixedWidth > maxLayoutWidth) { + maxLayoutWidth = lineTotalFixedWidth; + } + } + + maxLayoutWidth += context->paddingLeading + context->paddingTrailing; + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:maxLayoutWidth subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + + //进行所有行的宽度拉伸和压缩。 + if (context->horzGravity != MyGravity_Horz_Fill && (hasTotalWeight || hasTotalShrink)) { + NSInteger remainedCount = subviewEngines.count; + lineIndex = 0; + for (; lineIndex < arranges; lineIndex++) { + lineTotalFixedWidth = lineTotalFixedWidths[lineIndex]; + lineTotalWeight = lineTotalWeights[lineIndex]; + lineTotalShrink = lineTotalShrinks[lineIndex]; + + if (lineTotalWeight != 0) { + [self myVertLayoutCalculateSinglelineWeight:lineTotalWeight lineSpareWidth:context->selfSize.width - paddingHorz - lineTotalFixedWidth startItemIndex:lineIndex * arrangedCount itemCount:MIN(arrangedCount, remainedCount) withContext:context]; + } + + if (lineTotalShrink != 0) { + [self myVertLayoutCalculateSinglelineShrink:lineTotalShrink lineSpareWidth:context->selfSize.width - paddingHorz - lineTotalFixedWidth startItemIndex:lineIndex * arrangedCount itemCount:MIN(arrangedCount, remainedCount) withContext:context]; + } + + remainedCount -= arrangedCount; + } } + //初始化每行的下一个子视图的位置。 + NSMutableArray *nextPointOfRows = nil; + if (autoArrange) { + nextPointOfRows = [NSMutableArray arrayWithCapacity:arrangedCount]; + for (NSInteger idx = 0; idx < arrangedCount; idx++) { + [nextPointOfRows addObject:[NSValue valueWithCGPoint:CGPointMake(context->paddingLeading, context->paddingTop)]]; + } + } - CGFloat pageWidth = 0; //页宽。 - CGFloat averageWidth = (selfSize.width - paddingHorz - (arrangedCount - 1) * horzSpace) / arrangedCount; - arrangedIndex = 0; + CGFloat pageWidth = 0.0; //页宽。 + maxLayoutWidth = context->paddingLeading; + lineIndex = 0; //行索引 + itemIndex = 0; + lineTotalShrink = 0.0; i = 0; - for (; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; + for (; i < subviewEngines.count; i++) { + MyLayoutEngine *subviewEngine = subviewEngines[i]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; //新的一行 - if (arrangedIndex >= arrangedCount) - { - arrangedIndex = 0; - yPos += rowMaxHeight; - yPos += vertSpace; + if (itemIndex >= arrangedCount) { + itemIndex = 0; + yPos += context->vertSpace; + yPos += lineMaxHeight; + + [self myVertLayoutCalculateSingleline:lineIndex vertAlignment:vertAlignment lineMaxHeight:lineMaxHeight lineMaxWidth:lineMaxWidth lineTotalShrink:lineTotalShrink startItemIndex:i - arrangedCount itemCount:arrangedCount withContext:context]; //分别处理水平分页和垂直分页。 - if (isHorzPaging) - { - if (i % lsc.pagedCount == 0) - { + if (isHorzPaging) { + if (i % layoutTraits.pagedCount == 0) { pageWidth += CGRectGetWidth(self.superview.bounds); - if (!isPagingScroll) - pageWidth -= paddingLeading; - - yPos = paddingTop; + if (!isPagingScroll) { + pageWidth -= context->paddingLeading; + } + yPos = context->paddingTop; } - } - if (isVertPaging) - { + if (isVertPaging) { //如果是分页滚动则要多添加垂直间距。 - if (i % lsc.pagedCount == 0) - { - - if (isPagingScroll) - { - yPos -= vertSpace; + if (i % layoutTraits.pagedCount == 0) { + if (isPagingScroll) { + yPos -= context->vertSpace; yPos += paddingVert; - } } } + xPos = context->paddingLeading + pageWidth; - xPos = paddingLeading + pageWidth; - - - //计算每行的gravity情况。 - [self myCalcVertLayoutSinglelineAlignment:selfSize rowMaxHeight:rowMaxHeight rowMaxWidth:rowMaxWidth horzGravity:horzGravity vertAlignment:vertAlign sbs:sbs startIndex:i count:arrangedCount vertSpace:vertSpace horzSpace:horzSpace isEstimate:isEstimate lsc:lsc]; - rowMaxHeight = 0; - rowMaxWidth = 0; - + lineMaxHeight = 0.0; + lineMaxWidth = 0.0; + lineTotalShrink = 0.0; + lineIndex++; } - - CGFloat topSpace = sbvsc.topPosInner.absVal; - CGFloat leadingSpace = sbvsc.leadingPosInner.absVal; - CGFloat bottomSpace = sbvsc.bottomPosInner.absVal; - CGFloat trailingSpace = sbvsc.trailingPosInner.absVal; - CGRect rect = sbvmyFrame.frame; - BOOL isFlexedHeight = sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]] && sbvsc.heightSizeInner.dimeRelaVal.view != self; - - if (pagingItemHeight != 0) - rect.size.height = pagingItemHeight; - - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - if (averageArrange) - { - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:averageWidth - leadingSpace - trailingSpace sbvSize:rect.size selfLayoutSize:selfSize]; + CGFloat topSpacing = subviewTraits.topPosInner.measure; + CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; + CGFloat bottomSpacing = subviewTraits.bottomPosInner.measure; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; + + if (pagingItemHeight != 0) { + subviewEngine.height = pagingItemHeight - topSpacing - bottomSpacing; + } else if (subviewTraits.heightSizeInner.val != nil) { + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; + } else if (context->vertGravity == MyGravity_Vert_Fill || context->vertGravity == MyGravity_Vert_Stretch) { //如果没有设置高度约束但是又是垂直拉伸则将高度设置为0. + subviewEngine.height = 0; } + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; - [self mySetSubviewRelativeDimeSize:sbvsc.heightSizeInner selfSize:selfSize lsc:lsc pRect:&rect]; - - //如果高度是浮动的则需要调整高度。 - if (isFlexedHeight) - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - rect.origin.x = xPos + leadingSpace; - rect.origin.y = yPos + topSpace; - xPos += leadingSpace + rect.size.width + trailingSpace; - - if (arrangedIndex != (arrangedCount - 1)) - xPos += horzSpace; - - - if (_myCGFloatLess(rowMaxHeight, topSpace + bottomSpace + rect.size.height)) - rowMaxHeight = topSpace + bottomSpace + rect.size.height; + //再算一次宽度,只有比重为0并且不压缩的情况下计算。否则有可能前面被压缩或者被拉升而又会在这里重置了 + if (subviewTraits.weight == 0.0 && + subviewTraits.widthSizeInner.shrink == 0 && + context->horzGravity != MyGravity_Horz_Fill && + pagingItemWidth == 0.0 && + subviewWidth == 0.0) { + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; + } - if (_myCGFloatLess(rowMaxWidth, (xPos - paddingLeading))) - rowMaxWidth = (xPos - paddingLeading); + //特殊处理宽度和高度相互依赖的情况。。 + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { //特殊处理高度等于宽度的情况 + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } - if (_myCGFloatLess(maxWidth, xPos)) - maxWidth = xPos; + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner && context->horzGravity != MyGravity_Horz_Fill) { //特殊处理宽度等于高度的情况 + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + //得到最大的行高 + if (_myCGFloatLess(lineMaxHeight, topSpacing + bottomSpacing + subviewEngine.height)) { + lineMaxHeight = topSpacing + bottomSpacing + subviewEngine.height; + } + //自动排列。 + if (autoArrange) { + //查找能存放当前子视图的最小y轴的位置以及索引。 + CGPoint minPoint = CGPointMake(CGFLOAT_MAX, CGFLOAT_MAX); + NSInteger minNextPointIndex = 0; + for (int idx = 0; idx < arrangedCount; idx++) { + CGPoint pt = nextPointOfRows[idx].CGPointValue; + if (minPoint.y > pt.y) { + minPoint = pt; + minNextPointIndex = idx; + } + } + + //找到的minNextPointIndex中的 + xPos = minPoint.x; + yPos = minPoint.y; + + minPoint.y = minPoint.y + topSpacing + subviewEngine.height + bottomSpacing + context->vertSpace; + nextPointOfRows[minNextPointIndex] = [NSValue valueWithCGPoint:minPoint]; + if (minNextPointIndex + 1 <= arrangedCount - 1) { + minPoint = nextPointOfRows[minNextPointIndex + 1].CGPointValue; + minPoint.x = xPos + leadingSpacing + subviewEngine.width + trailingSpacing + context->horzSpace; + nextPointOfRows[minNextPointIndex + 1] = [NSValue valueWithCGPoint:minPoint]; + } + + if (_myCGFloatLess(maxLayoutHeight, yPos + topSpacing + subviewEngine.height + bottomSpacing)) { + maxLayoutHeight = yPos + topSpacing + subviewEngine.height + bottomSpacing; + } + } else if (vertAlignment == MyGravity_Vert_Between) { //当列是紧凑排列时需要特殊处理当前的垂直位置。 + //第0行特殊处理。 + if (i - arrangedCount < 0) { + yPos = context->paddingTop; + } else { + //取前一行的对应的列的子视图。 + MyLayoutEngine *prevColSubviewEngine = subviewEngines[i - arrangedCount]; + MyViewTraits *prevColSubviewTraits = prevColSubviewEngine.currentSizeClass; + //当前子视图的位置等于前一行对应列的最大y的值 + 前面对应列的底部间距 + 子视图之间的行间距。 + yPos = CGRectGetMaxY(prevColSubviewEngine.frame) + prevColSubviewTraits.bottomPosInner.measure + context->vertSpace; + } + + if (_myCGFloatLess(maxLayoutHeight, yPos + topSpacing + subviewEngine.height + bottomSpacing)) { + maxLayoutHeight = yPos + topSpacing + subviewEngine.height + bottomSpacing; + } + } else { //正常排列。 + //这里的最大其实就是最后一个视图的位置加上最高的子视图的尺寸。 + if (_myCGFloatLess(maxLayoutHeight, yPos + lineMaxHeight)) { + maxLayoutHeight = yPos + lineMaxHeight; + } + } + subviewEngine.leading = xPos + leadingSpacing; + subviewEngine.top = yPos + topSpacing; + xPos += leadingSpacing + subviewEngine.width + trailingSpacing; - sbvmyFrame.frame = rect; + if (_myCGFloatLess(lineMaxWidth, (xPos - context->paddingLeading))) { + lineMaxWidth = (xPos - context->paddingLeading); + } + if (_myCGFloatLess(maxLayoutWidth, xPos)) { + maxLayoutWidth = xPos; + } - arrangedIndex++; + if (itemIndex != (arrangedCount - 1) && !autoArrange) { + xPos += context->horzSpace; + } + itemIndex++; + //这里只对间距进行压缩比重的计算,因为前面压缩了宽度,这里只需要压缩间距了。 + lineTotalShrink += subviewTraits.leadingPosInner.shrink + subviewTraits.trailingPosInner.shrink; } - //最后一行 - [self myCalcVertLayoutSinglelineAlignment:selfSize rowMaxHeight:rowMaxHeight rowMaxWidth:rowMaxWidth horzGravity:horzGravity vertAlignment:vertAlign sbs:sbs startIndex:i count:arrangedIndex vertSpace:vertSpace horzSpace:horzSpace isEstimate:isEstimate lsc:lsc]; + maxLayoutHeight += context->paddingBottom; - if (lsc.wrapContentHeight) - { - selfSize.height = yPos + paddingBottom + rowMaxHeight; + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = maxLayoutHeight; - //只有在父视图为滚动视图,且开启了分页滚动时才会扩充具有包裹设置的布局视图的宽度。 - if (isVertPaging && isPagingScroll) - { - //算出页数来。如果包裹计算出来的宽度小于指定页数的宽度,因为要分页滚动所以这里会扩充布局的宽度。 - NSInteger totalPages = floor((sbs.count + lsc.pagedCount - 1.0 ) / lsc.pagedCount); - if (_myCGFloatLess(selfSize.height, totalPages * CGRectGetHeight(self.superview.bounds))) - selfSize.height = totalPages * CGRectGetHeight(self.superview.bounds); + //只有在父视图为滚动视图,且开启了分页滚动时才会扩充具有包裹设置的布局视图的高度。 + if (isVertPaging && isPagingScroll) { + //算出页数来。如果包裹计算出来的高度小于指定页数的高度,因为要分页滚动所以这里会扩充布局的高度。 + NSInteger totalPages = floor((subviewEngines.count + layoutTraits.pagedCount - 1.0) / layoutTraits.pagedCount); + if (_myCGFloatLess(context->selfSize.height, totalPages * CGRectGetHeight(self.superview.bounds))) { + context->selfSize.height = totalPages * CGRectGetHeight(self.superview.bounds); + } } + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:context->selfSize.height subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; } - else - { - CGFloat addYPos = 0; - CGFloat between = 0; - CGFloat fill = 0; - int arranges = floor((sbs.count + arrangedCount - 1.0) / arrangedCount); - - if (vertGravity == MyGravity_Vert_Center) - { - addYPos = (selfSize.height - paddingBottom - rowMaxHeight - yPos) / 2; - } - else if (vertGravity == MyGravity_Vert_Bottom) - { - addYPos = selfSize.height - paddingBottom - rowMaxHeight - yPos; - } - else if (vertGravity == MyGravity_Vert_Fill) - { - if (arranges > 0) - fill = (selfSize.height - paddingBottom - rowMaxHeight - yPos) / arranges; - } - else if (vertGravity == MyGravity_Vert_Between) - { + + //根据flex规则:如果只有一行则整个高度都作为子视图的拉伸和停靠区域。 + if (layoutTraits.isFlex && arranges == 1) { + lineMaxHeight = context->selfSize.height - paddingVert; + } + + //最后一行,有可能因为行宽的压缩导致那些高度依赖宽度以及高度自适应的视图会增加高度,从而使得行高被调整。 + [self myVertLayoutCalculateSingleline:lineIndex vertAlignment:vertAlignment lineMaxHeight:lineMaxHeight lineMaxWidth:lineMaxWidth lineTotalShrink:lineTotalShrink startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; + + //整体的停靠 + if (context->vertGravity != MyGravity_None && context->selfSize.height != maxLayoutHeight && !(isVertPaging && isPagingScroll)) { + //根据flex标准:只有在多行下vertGravity才有意义。非flex标准则不受这个条件约束。 + if (arranges > 1 || !layoutTraits.isFlex) { + CGFloat addYPos = 0.0; + CGFloat between = 0.0; + CGFloat fill = 0.0; - if (arranges > 1) - between = (selfSize.height - paddingBottom - rowMaxHeight - yPos) / (arranges - 1); - } - - - if (addYPos != 0 || between != 0 || fill != 0) - { - for (int i = 0; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - - MyFrame *sbvmyFrame = sbv.myFrame; - - int lines = i / arrangedCount; - sbvmyFrame.height += fill; - sbvmyFrame.top += fill * lines; - - sbvmyFrame.top += addYPos; - - sbvmyFrame.top += between * lines; - + if (arranges <= 1 && context->vertGravity == MyGravity_Vert_Around) { + context->vertGravity = MyGravity_Vert_Center; + } + if (context->vertGravity == MyGravity_Vert_Center) { + addYPos = (context->selfSize.height - maxLayoutHeight) / 2; + } else if (context->vertGravity == MyGravity_Vert_Bottom) { + addYPos = context->selfSize.height - maxLayoutHeight; + } else if (context->vertGravity == MyGravity_Vert_Fill || context->vertGravity == MyGravity_Vert_Stretch) { + if (arranges > 0) { + fill = (context->selfSize.height - maxLayoutHeight) / arranges; + } + //满足flex规则:如果剩余的空间是负数,该值等效于'flex-start' + if (fill < 0.0 && context->vertGravity == MyGravity_Vert_Stretch) { + fill = 0.0; + } + } else if (context->vertGravity == MyGravity_Vert_Between) { + if (arranges > 1) { + between = (context->selfSize.height - maxLayoutHeight) / (arranges - 1); + } + } else if (context->vertGravity == MyGravity_Vert_Around) { + between = (context->selfSize.height - maxLayoutHeight) / arranges; + } else if (context->vertGravity == MyGravity_Vert_Among) { + between = (context->selfSize.height - maxLayoutHeight) / (arranges + 1); + } + + if (addYPos != 0.0 || between != 0.0 || fill != 0.0) { + for (int i = 0; i < subviewEngines.count; i++) { + + MyLayoutEngine *subviewEngine = subviewEngines[i]; + + int lineidx = i / arrangedCount; + if (context->vertGravity == MyGravity_Vert_Stretch) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + if (subviewTraits.heightSizeInner.val == nil || (subviewTraits.heightSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { + subviewEngine.height += fill; + } else { + //因为每行都增加了fill。所以如果有行内对齐则需要这里调整。 + MyGravity subviewVertAlignment = MYVERTGRAVITY(subviewTraits.alignment); + if (subviewVertAlignment == MyGravity_None) { + subviewVertAlignment = vertAlignment; + } + if (subviewVertAlignment == MyGravity_Vert_Center) { + subviewEngine.top += fill / 2.0; + } else if (subviewVertAlignment == MyGravity_Vert_Bottom) { + subviewEngine.top += fill; + } + } + } else { + subviewEngine.height += fill; + } + subviewEngine.top += fill * lineidx; + + subviewEngine.top += addYPos; + + subviewEngine.top += between * lineidx; + + //如果是vert_around那么所有行都应该添加一半的between值。 + if (context->vertGravity == MyGravity_Vert_Around) { + subviewEngine.top += (between / 2.0); + } + if (context->vertGravity == MyGravity_Vert_Among) { + subviewEngine.top += between; + } + } } } - } - if (lsc.wrapContentWidth && !averageArrange) - { - selfSize.width = maxWidth + paddingTrailing; + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = maxLayoutWidth + context->paddingTrailing; //只有在父视图为滚动视图,且开启了分页滚动时才会扩充具有包裹设置的布局视图的宽度。 - if (isHorzPaging && isPagingScroll) - { + if (isHorzPaging && isPagingScroll) { //算出页数来。如果包裹计算出来的宽度小于指定页数的宽度,因为要分页滚动所以这里会扩充布局的宽度。 - NSInteger totalPages = floor((sbs.count + lsc.pagedCount - 1.0 ) / lsc.pagedCount); - if (_myCGFloatLess(selfSize.width, totalPages * CGRectGetWidth(self.superview.bounds))) - selfSize.width = totalPages * CGRectGetWidth(self.superview.bounds); + NSInteger totalPages = floor((subviewEngines.count + layoutTraits.pagedCount - 1.0) / layoutTraits.pagedCount); + if (_myCGFloatLess(context->selfSize.width, totalPages * CGRectGetWidth(self.superview.bounds))) { + context->selfSize.width = totalPages * CGRectGetWidth(self.superview.bounds); + } } - } - - return selfSize; } - - - - --(CGSize)myLayoutSubviewsForHorzContent:(CGSize)selfSize sbs:(NSMutableArray*)sbs isEstimate:(BOOL)isEstimate lsc:(MyFlowLayout*)lsc -{ - - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - CGFloat paddingVert = paddingTop + paddingBottom; - - CGFloat xPos = paddingLeading; - CGFloat yPos = paddingTop; - CGFloat colMaxWidth = 0; //某一列的最宽值。 - CGFloat colMaxHeight = 0; //某一列的最高值 +- (void)myDoHorzOrientationContentLayoutWithContext:(MyLayoutContext *)context { + + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + NSMutableArray *subviewEngines = context->subviewEngines; - MyGravity vertGravity = lsc.gravity & MyGravity_Horz_Mask; - MyGravity horzGravity = [self myConvertLeftRightGravityToLeadingTrailing:lsc.gravity & MyGravity_Vert_Mask]; - MyGravity horzAlign = [self myConvertLeftRightGravityToLeadingTrailing:lsc.arrangedGravity & MyGravity_Vert_Mask]; - + + MyGravity horzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.arrangedGravity)]; //支持浮动垂直间距。 - CGFloat vertSpace = lsc.subviewVSpace; - CGFloat horzSpace = lsc.subviewHSpace; - CGFloat subviewSize = ((MyFlowLayoutViewSizeClass*)self.myCurrentSizeClass).subviewSize; - if (subviewSize != 0) - { - - CGFloat minSpace = ((MyFlowLayoutViewSizeClass*)self.myCurrentSizeClass).minSpace; - CGFloat maxSpace = ((MyFlowLayoutViewSizeClass*)self.myCurrentSizeClass).maxSpace; - NSInteger rowCount = floor((selfSize.height - paddingVert + minSpace) / (subviewSize + minSpace)); - if (rowCount > 1) - { - vertSpace = (selfSize.height - paddingVert - subviewSize * rowCount)/(rowCount - 1); - if (_myCGFloatGreat(vertSpace, maxSpace)) - { - vertSpace = maxSpace; - - subviewSize = (selfSize.height - paddingVert - vertSpace * (rowCount - 1)) / rowCount; - - } - } + CGFloat subviewHeight = [layoutTraits.flexSpace calcMaxMinSubviewSizeForContent:context->selfSize.height paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom space:&context->vertSpace]; + + CGFloat paddingVert = context->paddingTop + context->paddingBottom; + CGFloat paddingHorz = context->paddingLeading + context->paddingTrailing; + + CGFloat xPos = context->paddingLeading; + CGFloat yPos = context->paddingTop; + CGFloat lineMaxWidth = 0.0; //某一列的最宽值。 + CGFloat lineMaxHeight = 0.0; //某一列的最高值 + CGFloat maxLayoutHeight = 0.0; //所有列的最宽行 + + //limitedSelfHeight是用来限制子视图换行的高度,默认是selfSize.height + //但是一种特殊情况就是布局视图高度自适应,但是设置了最高高度的情况。 + //这种情况下当子视图超过最高高度时还是需要进行换行处理。 + //而如果没有设置最高高度的话那么默认限制的高度就是最大值CGFLOAT_MAX + CGFloat limitedSelfHeight = context->selfSize.height; + if (layoutTraits.heightSizeInner.wrapVal) { + limitedSelfHeight = [self myGetBoundLimitMeasure:layoutTraits.heightSizeInner.uBoundValInner subview:self anchorType:layoutTraits.heightSizeInner.anchorType subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size isUBound:YES]; + limitedSelfHeight = _myCGFloatMin(limitedSelfHeight, CGFLOAT_MAX); } - - if (lsc.autoArrange) - { + if (layoutTraits.autoArrange) { //计算出每个子视图的宽度。 - for (UIView* sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; + for (MyLayoutEngine *subviewEngine in subviewEngines) { + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; #ifdef DEBUG //约束异常:水平流式布局设置autoArrange为YES时,子视图不能将weight设置为非0. - NSCAssert(sbvsc.weight == 0, @"Constraint exception!! horizontal flow layout:%@ 's subview:%@ can't set weight when the autoArrange set to YES",self, sbv); + NSCAssert(subviewTraits.weight == 0, @"Constraint exception!! horizontal flow layout:%@ 's subview:%@ can't set weight when the autoArrange set to YES", self, subviewTraits.view); #endif + CGFloat topSpacing = subviewTraits.topPosInner.measure; + CGFloat bottomSpacing = subviewTraits.bottomPosInner.measure; - CGFloat topSpace = sbvsc.topPosInner.absVal; - CGFloat bottomSpace = sbvsc.bottomPosInner.absVal; - CGRect rect = sbvmyFrame.frame; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - [self mySetSubviewRelativeDimeSize:sbvsc.heightSizeInner selfSize:selfSize lsc:lsc pRect:&rect]; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - [self mySetSubviewRelativeDimeSize:sbvsc.widthSizeInner selfSize:selfSize lsc:lsc pRect:&rect]; + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; - - //如果高度是浮动的则需要调整高度。 - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - { - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; + //暂时把宽度存放sbv.myFrame.trailing上。因为流式布局来说这个属性无用。 + subviewEngine.trailing = topSpacing + subviewEngine.height + bottomSpacing; + if (_myCGFloatGreat(subviewEngine.trailing, context->selfSize.height - paddingVert)) { + subviewEngine.trailing = context->selfSize.height - paddingVert; } - - - //暂时把宽度存放sbv.myFrame.trailing上。因为浮动布局来说这个属性无用。 - sbvmyFrame.trailing = topSpace + rect.size.height + bottomSpace; - if (_myCGFloatGreat(sbvmyFrame.trailing, selfSize.height - paddingVert)) - sbvmyFrame.trailing = selfSize.height - paddingVert; } - [sbs setArray:[self myGetAutoArrangeSubviews:sbs selfSize:selfSize.height - paddingVert space:vertSpace]]; - + [subviewEngines setArray:[self myGetAutoArrangeSubviews:subviewEngines selfSize:context->selfSize.height - paddingVert space:context->vertSpace]]; } - - - NSMutableIndexSet *arrangeIndexSet = [NSMutableIndexSet new]; - NSInteger arrangedIndex = 0; + NSMutableIndexSet *lineFirstSubviewIndexSet = [NSMutableIndexSet new]; + NSInteger lineIndex = 0; + NSInteger itemIndex = 0; + CGFloat lineTotalWeight = 0.0; NSInteger i = 0; - for (; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - - CGFloat topSpace = sbvsc.topPosInner.absVal; - CGFloat leadingSpace = sbvsc.leadingPosInner.absVal; - CGFloat bottomSpace = sbvsc.bottomPosInner.absVal; - CGFloat trailingSpace = sbvsc.trailingPosInner.absVal; - CGRect rect = sbvmyFrame.frame; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - [self mySetSubviewRelativeDimeSize:sbvsc.heightSizeInner selfSize:selfSize lsc:lsc pRect:&rect]; - - if (subviewSize != 0) - rect.size.height = subviewSize; - - - [self mySetSubviewRelativeDimeSize:sbvsc.widthSizeInner selfSize:selfSize lsc:lsc pRect:&rect]; - - - if (sbvsc.weight != 0) - { - //如果过了,则表示当前的剩余空间为0了,所以就按新的一行来算。。 - CGFloat floatHeight = selfSize.height - paddingVert - colMaxHeight; - if (_myCGFloatLessOrEqual(floatHeight, 0)) - { - floatHeight += colMaxHeight; - arrangedIndex = 0; + for (; i < subviewEngines.count; i++) { + MyLayoutEngine *subviewEngine = subviewEngines[i]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + + CGFloat topSpacing = subviewTraits.topPosInner.measure; + CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; + CGFloat bottomSpacing = subviewTraits.bottomPosInner.measure; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; + + //这里先计算一下宽度,因为有可能有宽度固定,高度自适应的情况。 + if (subviewTraits.widthSizeInner.val != nil) { + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; + + //当只有一行而且是flex标准并且是stretch时会把所有子视图的宽度都强制拉伸为布局视图的宽度 + //所以如果这里是宽度自适应时需要将宽度强制设置为和布局等宽,以便解决同时高度自适应时高度计算不正确的问题。 + if (lineIndex == 0 && + subviewTraits.widthSizeInner.wrapVal && + layoutTraits.isFlex && + context->horzGravity == MyGravity_Horz_Stretch && + subviewEngine.width > context->selfSize.width - paddingHorz - leadingSpacing - trailingSpacing) { + subviewEngine.width = context->selfSize.width - paddingHorz - leadingSpacing - trailingSpacing; } - if (arrangedIndex != 0) - floatHeight -= vertSpace; - - rect.size.height = (floatHeight + sbvsc.heightSizeInner.addVal) * sbvsc.weight - topSpace - bottomSpace; - + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } else if (context->horzGravity == MyGravity_Horz_Fill || context->horzGravity == MyGravity_Horz_Stretch) { + subviewEngine.width = 0.0; } + if (subviewHeight != 0.0) { + subviewEngine.height = subviewHeight - topSpacing - bottomSpacing; + } else if (subviewTraits.heightSizeInner.val != nil) { + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; + + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { //特殊处理高度等于宽度的情况 + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + } else if (subviewTraits.weight != 0.0) { + if (layoutTraits.isFlex) { + subviewEngine.height = 0.0; + } else { + //如果超过了布局尺寸,则表示当前的剩余空间为0了,所以就按新的一行来算。。 + CGFloat lineSpareHeight = context->selfSize.height - context->paddingBottom - yPos - topSpacing - bottomSpacing; + if (itemIndex != 0) { + lineSpareHeight -= context->vertSpace; + } + if (_myCGFloatLessOrEqual(lineSpareHeight, 0.0)) { + lineSpareHeight = context->selfSize.height - paddingVert; + } + + subviewEngine.height = (lineSpareHeight + subviewTraits.heightSizeInner.addVal) * subviewTraits.weight - topSpacing - bottomSpacing; + } + } - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == sbvsc.heightSizeInner) - rect.size.width = [sbvsc.widthSizeInner measureWith:rect.size.height ]; - - - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; - //如果高度是浮动的则需要调整高度。 - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - { - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; + //特殊处理宽度依赖高度的子视图。 + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { //特殊处理宽度等于高度的情况 + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; } - //计算yPos的值加上topSpace + rect.size.height + bottomSpace的值要小于整体的高度。 - CGFloat place = yPos + topSpace + rect.size.height + bottomSpace; - if (arrangedIndex != 0) - place += vertSpace; - place += paddingBottom; + //计算yPos的值加上topSpacing + subviewEngine.height + bottomSpacing的值要小于整体的高度。 + CGFloat place = yPos + topSpacing + subviewEngine.height + bottomSpacing; + if (itemIndex != 0) { + place += context->vertSpace; + } + place += context->paddingBottom; - //sbv所占据的宽度要超过了视图的整体宽度,因此需要换行。但是如果arrangedIndex为0的话表示这个控件的整行的宽度和布局视图保持一致。 - if (place - selfSize.height > 0.0001) - { - yPos = paddingTop; - xPos += horzSpace; - xPos += colMaxWidth; + //sbv所占据的高度要超过了视图的整体高度,因此需要换行。但是如果arrangedIndex为0的话表示这个控件的整行的高度和布局视图保持一致。 + if (place - limitedSelfHeight > 0.0001) { + context->selfSize.height = limitedSelfHeight; + + [lineFirstSubviewIndexSet addIndex:i - itemIndex]; + //拉伸以及调整行内子视图的高度。 + [self myHorzLayoutAdjustSingleline:lineIndex lineSpareHeight:context->selfSize.height - context->paddingBottom - yPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; - //计算每行的gravity情况。 - [arrangeIndexSet addIndex:i - arrangedIndex]; - [self myCalcHorzLayoutSinglelineAlignment:selfSize colMaxWidth:colMaxWidth colMaxHeight:colMaxHeight vertGravity:vertGravity horzAlignment:horzAlign sbs:sbs startIndex:i count:arrangedIndex vertSpace:vertSpace horzSpace:horzSpace isEstimate:isEstimate lsc:lsc]; + yPos = context->paddingTop; //计算单独的sbv的高度是否大于整体的高度。如果大于则缩小高度。 - if (_myCGFloatGreat(topSpace + bottomSpace + rect.size.height, selfSize.height - paddingVert)) - { - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:selfSize.height - paddingVert - topSpace - bottomSpace sbvSize:rect.size selfLayoutSize:selfSize]; + if (_myCGFloatGreat(topSpacing + bottomSpacing + subviewEngine.height, context->selfSize.height - paddingVert)) { + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:context->selfSize.height - paddingVert - topSpacing - bottomSpacing subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; } - colMaxWidth = 0; - colMaxHeight = 0; - arrangedIndex = 0; - + lineTotalWeight = 0.0; + lineIndex++; + itemIndex = 0; } - if (arrangedIndex != 0) - yPos += vertSpace; + if (itemIndex != 0) { + yPos += context->vertSpace; + } + subviewEngine.top = yPos + topSpacing; + yPos += topSpacing + subviewEngine.height + bottomSpacing; - rect.origin.x = xPos + leadingSpace; - rect.origin.y = yPos + topSpace; - yPos += topSpace + rect.size.height + bottomSpace; + if (layoutTraits.isFlex && subviewTraits.weight != 0) { + lineTotalWeight += subviewTraits.weight; + } - if (_myCGFloatLess(colMaxWidth, leadingSpace + trailingSpace + rect.size.width)) - colMaxWidth = leadingSpace + trailingSpace + rect.size.width; + itemIndex++; + } + + //最后一行的行首索引 + [lineFirstSubviewIndexSet addIndex:i - itemIndex]; + + //在高度为自适应时,如果没有设置最大高度限制,那么就一定是单行,因此高度就是子视图的总和。 + //如果设置了最大高度限制时,那就要区分最后一行是单行还是多行,所以我们取限高和当前计算出的高度的最小值,并且再取selfSize.height和前面比较结果的最大值。 + if (layoutTraits.heightSizeInner.wrapVal) { + if (limitedSelfHeight == CGFLOAT_MAX) { + context->selfSize.height = _myCGFloatMax(yPos + context->paddingBottom, [self myGetBoundLimitMeasure:layoutTraits.heightSizeInner.uBoundValInner subview:self anchorType:layoutTraits.heightSizeInner.anchorType subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size isUBound:NO]); + } else { + context->selfSize.height = _myCGFloatMax(_myCGFloatMin(yPos + context->paddingBottom, limitedSelfHeight), context->selfSize.height); + } + } + + [self myHorzLayoutAdjustSingleline:lineIndex lineSpareHeight:context->selfSize.height - context->paddingBottom - yPos lineTotalWeight:lineTotalWeight startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; + + yPos = context->paddingTop; + lineIndex = 0; //行的索引。 + itemIndex = 0; //行内的子视图索引 + NSInteger oldLineFirstIndex = 0; + i = 0; + for (; i < subviewEngines.count; i++) { + MyLayoutEngine *subviewEngine = subviewEngines[i]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; - if (_myCGFloatLess(colMaxHeight, (yPos - paddingTop))) - colMaxHeight = (yPos - paddingTop); + CGFloat topSpacing = subviewTraits.topPosInner.measure; + CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; + CGFloat bottomSpacing = subviewTraits.bottomPosInner.measure; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { //特殊处理宽度等于高度的情况 + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } - sbvmyFrame.frame = rect; + //计算yPos的值加上topSpacing + subviewEngine.height + bottomSpacing 的值要小于整体的高度。 + maxLayoutHeight = yPos + topSpacing + subviewEngine.height + bottomSpacing; + if (itemIndex != 0) { + maxLayoutHeight += context->vertSpace; + } + maxLayoutHeight += context->paddingBottom; - arrangedIndex++; + NSUInteger lineFirstIndex = [lineFirstSubviewIndexSet indexLessThanOrEqualToIndex:i]; + if (oldLineFirstIndex != lineFirstIndex) { + oldLineFirstIndex = lineFirstIndex; + + yPos = context->paddingTop; + xPos += context->horzSpace; + xPos += lineMaxWidth; + + [self myHorzLayoutCalculateSingleline:lineIndex horzAlignment:horzAlignment lineMaxWidth:lineMaxWidth lineMaxHeight:lineMaxHeight lineTotalShrink:0 startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; + + lineMaxWidth = 0.0; + lineMaxHeight = 0.0; + itemIndex = 0; + lineIndex++; + } + if (itemIndex != 0) { + yPos += context->vertSpace; + } + subviewEngine.leading = xPos + leadingSpacing; + subviewEngine.top = yPos + topSpacing; + yPos += topSpacing + subviewEngine.height + bottomSpacing; + if (_myCGFloatLess(lineMaxWidth, leadingSpacing + trailingSpacing + subviewEngine.width)) { + lineMaxWidth = leadingSpacing + trailingSpacing + subviewEngine.width; + } + if (_myCGFloatLess(lineMaxHeight, (yPos - context->paddingTop))) { + lineMaxHeight = (yPos - context->paddingTop); + } + itemIndex++; } + xPos += lineMaxWidth + context->paddingTrailing; + + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:maxLayoutHeight subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:xPos subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + NSInteger arranges = lineFirstSubviewIndexSet.count; + //根据flex规则:如果只有一列则整个宽度都作为子视图的拉伸和停靠区域。 + if (layoutTraits.isFlex && arranges == 1) { + lineMaxWidth = context->selfSize.width - paddingHorz; + } //最后一行 - [arrangeIndexSet addIndex:i - arrangedIndex]; - [self myCalcHorzLayoutSinglelineAlignment:selfSize colMaxWidth:colMaxWidth colMaxHeight:colMaxHeight vertGravity:vertGravity horzAlignment:horzAlign sbs:sbs startIndex:i count:arrangedIndex vertSpace:vertSpace horzSpace:horzSpace isEstimate:isEstimate lsc:lsc]; - - - if (lsc.wrapContentWidth) - selfSize.width = xPos + paddingTrailing + colMaxWidth; - else - { - CGFloat addXPos = 0; - CGFloat fill = 0; - CGFloat between = 0; - - if (horzGravity == MyGravity_Horz_Center) - { - addXPos = (selfSize.width - paddingTrailing - colMaxWidth - xPos) / 2; - } - else if (horzGravity == MyGravity_Horz_Trailing) - { - addXPos = selfSize.width - paddingTrailing - colMaxWidth - xPos; - } - else if (horzGravity == MyGravity_Horz_Fill) - { - if (arrangeIndexSet.count > 0) - fill = (selfSize.width - paddingTrailing - colMaxWidth - xPos) / arrangeIndexSet.count; - } - else if (horzGravity == MyGravity_Horz_Between) - { - if (arrangeIndexSet.count > 1) - between = (selfSize.width - paddingTrailing - colMaxWidth - xPos) / (arrangeIndexSet.count - 1); - } - - - if (addXPos != 0 || between != 0 || fill != 0) - { - int line = 0; - NSUInteger lastIndex = 0; - for (int i = 0; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - MyFrame *sbvmyFrame = sbv.myFrame; - - sbvmyFrame.leading += addXPos; - - //找到行的最初索引。 - NSUInteger index = [arrangeIndexSet indexLessThanOrEqualToIndex:i]; - if (lastIndex != index) - { - lastIndex = index; - line ++; + [self myHorzLayoutCalculateSingleline:lineIndex horzAlignment:horzAlignment lineMaxWidth:lineMaxWidth lineMaxHeight:lineMaxHeight lineTotalShrink:0 startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; + + //整体的停靠 + if (context->horzGravity != MyGravity_None && context->selfSize.width != xPos) { + //根据flex标准:只有在多行下horzGravity才有意义。非flex标准则不受这个条件约束。 + if (arranges > 1 || !layoutTraits.isFlex) { + CGFloat addXPos = 0.0; + CGFloat fill = 0.0; + CGFloat between = 0.0; + + if (arranges <= 1 && context->horzGravity == MyGravity_Horz_Around) { + context->horzGravity = MyGravity_Horz_Center; + } + if (context->horzGravity == MyGravity_Horz_Center) { + addXPos = (context->selfSize.width - xPos) / 2; + } else if (context->horzGravity == MyGravity_Horz_Trailing) { + addXPos = context->selfSize.width - xPos; + } else if (context->horzGravity == MyGravity_Horz_Fill || context->horzGravity == MyGravity_Horz_Stretch) { + if (arranges > 0) { + fill = (context->selfSize.width - xPos) / arranges; + } + //满足flex规则:如果剩余的空间是负数,该值等效于'flex-start' + if (fill < 0.0 && context->horzGravity == MyGravity_Horz_Stretch) { + fill = 0.0; + } + } else if (context->horzGravity == MyGravity_Horz_Between) { + if (arranges > 1) { + between = (context->selfSize.width - xPos) / (arranges - 1); + } + } else if (context->horzGravity == MyGravity_Horz_Around) { + between = (context->selfSize.width - xPos) / arranges; + } else if (context->horzGravity == MyGravity_Horz_Among) { + between = (context->selfSize.width - xPos) / (arranges + 1); + } + + if (addXPos != 0.0 || between != 0.0 || fill != 0.0) { + int lineidx = 0; + NSUInteger lastIndex = 0; + for (int i = 0; i < subviewEngines.count; i++) { + MyLayoutEngine *subviewEngine = subviewEngines[i]; + + subviewEngine.leading += addXPos; + + //找到行的最初索引。 + NSUInteger index = [lineFirstSubviewIndexSet indexLessThanOrEqualToIndex:i]; + if (lastIndex != index) { + lastIndex = index; + lineidx++; + } + + if (context->horzGravity == MyGravity_Horz_Stretch) { + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + if (subviewTraits.widthSizeInner.val == nil || (subviewTraits.widthSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { + subviewEngine.width += fill; + } else { + //因为每行都增加了fill。所以如果有行内对齐则需要这里调整。 + MyGravity subviewHorzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]; + if (subviewHorzAlignment == MyGravity_None) { + subviewHorzAlignment = horzAlignment; + } + if (subviewHorzAlignment == MyGravity_Horz_Center) { + subviewEngine.leading += fill / 2.0; + } else if (subviewHorzAlignment == MyGravity_Horz_Trailing) { + subviewEngine.leading += fill; + } + } + } else { + subviewEngine.width += fill; + } + subviewEngine.leading += fill * lineidx; + + subviewEngine.leading += between * lineidx; + + if (context->horzGravity == MyGravity_Horz_Around) { + subviewEngine.leading += (between / 2.0); + } + + if (context->horzGravity == MyGravity_Horz_Among) { + subviewEngine.leading += between; + } } - - sbvmyFrame.width += fill; - sbvmyFrame.leading += fill * line; - - sbvmyFrame.leading += between * line; - } } - } - - - return selfSize; } - - --(CGSize)myLayoutSubviewsForHorz:(CGSize)selfSize sbs:(NSMutableArray*)sbs isEstimate:(BOOL)isEstimate lsc:(MyFlowLayout*)lsc -{ - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - CGFloat paddingHorz = paddingLeading + paddingTrailing; - CGFloat paddingVert = paddingTop + paddingBottom; - - NSInteger arrangedCount = lsc.arrangedCount; - CGFloat xPos = paddingLeading; - CGFloat yPos = paddingTop; - CGFloat colMaxWidth = 0; //每列的最大宽度 - CGFloat colMaxHeight = 0; //每列的最大高度 - CGFloat maxHeight = paddingTop; +- (void)myDoHorzOrientationCountLayoutWithContext:(MyLayoutContext *)context { + MyFlowLayoutTraits *layoutTraits = (MyFlowLayoutTraits*)context->layoutViewEngine.currentSizeClass; + NSMutableArray *subviewEngines = context->subviewEngines; - MyGravity vertGravity = lsc.gravity & MyGravity_Horz_Mask; - MyGravity horzGravity = [self myConvertLeftRightGravityToLeadingTrailing:lsc.gravity & MyGravity_Vert_Mask]; - MyGravity horzAlign = [self myConvertLeftRightGravityToLeadingTrailing:lsc.arrangedGravity & MyGravity_Vert_Mask]; - - CGFloat vertSpace = lsc.subviewVSpace; - CGFloat horzSpace = lsc.subviewHSpace; + BOOL autoArrange = layoutTraits.autoArrange; + NSInteger arrangedCount = layoutTraits.arrangedCount; + + MyGravity horzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.arrangedGravity)]; + + CGFloat subviewHeight = [layoutTraits.flexSpace calcMaxMinSubviewSize:context->selfSize.height arrangedCount:arrangedCount paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom space:&context->vertSpace]; + + CGFloat paddingHorz = context->paddingLeading + context->paddingTrailing; + CGFloat paddingVert = context->paddingTop + context->paddingBottom; + + CGFloat xPos = context->paddingLeading; + CGFloat yPos = context->paddingTop; + CGFloat lineMaxWidth = 0.0; //每一列的最大宽度 + CGFloat lineMaxHeight = 0.0; //每一列的最大高度 + CGFloat maxLayoutHeight = 0.0; //全列的最大高度 + CGFloat maxLayoutWidth = context->paddingLeading; //最大的宽度。 //父滚动视图是否分页滚动。 #if TARGET_OS_IOS //判断父滚动视图是否分页滚动 BOOL isPagingScroll = (self.superview != nil && - [self.superview isKindOfClass:[UIScrollView class]] && ((UIScrollView*)self.superview).isPagingEnabled); + [self.superview isKindOfClass:[UIScrollView class]] && + ((UIScrollView *)self.superview).isPagingEnabled); #else BOOL isPagingScroll = NO; #endif - CGFloat pagingItemHeight = 0; - CGFloat pagingItemWidth = 0; + CGFloat pagingItemHeight = 0.0; + CGFloat pagingItemWidth = 0.0; BOOL isVertPaging = NO; BOOL isHorzPaging = NO; - if (lsc.pagedCount > 0 && self.superview != nil) - { - NSInteger cols = lsc.pagedCount / arrangedCount; //每页的列数。 + if (layoutTraits.pagedCount > 0 && self.superview != nil) { + NSInteger cols = layoutTraits.pagedCount / arrangedCount; //每页的列数。 //对于水平流式布局来说,要求要有明确的高度。因此如果我们启用了分页又设置了高度包裹时则我们的分页是从上到下的排列。否则分页是从左到右的排列。 - if (lsc.wrapContentHeight) - { + if (layoutTraits.heightSizeInner.wrapVal) { isVertPaging = YES; - if (isPagingScroll) - pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - paddingVert - (arrangedCount - 1) * vertSpace ) / arrangedCount; - else - pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - paddingTop - arrangedCount * vertSpace ) / arrangedCount; - - pagingItemWidth = (selfSize.width - paddingHorz - (cols - 1) * horzSpace) / cols; - } - else - { + if (isPagingScroll) { + pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - paddingVert - (arrangedCount - 1) * context->vertSpace) / arrangedCount; + } else { + pagingItemHeight = (CGRectGetHeight(self.superview.bounds) - context->paddingTop - arrangedCount * context->vertSpace) / arrangedCount; + } + //如果是水平滚动则如果布局不是高度自适应才让条目的高度生效。 + if (!layoutTraits.widthSizeInner.wrapVal) { + pagingItemWidth = (context->selfSize.width - paddingHorz - (cols - 1) * context->horzSpace) / cols; + } + } else { isHorzPaging = YES; - pagingItemHeight = (selfSize.height - paddingVert - (arrangedCount - 1) * vertSpace) / arrangedCount; + pagingItemHeight = (context->selfSize.height - paddingVert - (arrangedCount - 1) * context->vertSpace) / arrangedCount; //分页滚动时和非分页滚动时的宽度计算是不一样的。 - if (isPagingScroll) - pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - paddingHorz - (cols - 1) * horzSpace) / cols; - else - pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - paddingLeading - cols * horzSpace) / cols; - + if (isPagingScroll) { + pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - paddingHorz - (cols - 1) * context->horzSpace) / cols; + } else { + if ([self.superview isKindOfClass:[UIScrollView class]]) { + pagingItemWidth = (CGRectGetWidth(self.superview.bounds) - context->paddingLeading - cols * context->horzSpace) / cols; + } else { + pagingItemWidth = (context->selfSize.width - paddingHorz - (cols - 1) * context->horzSpace) / cols; + } + } } - } - BOOL averageArrange = (vertGravity == MyGravity_Vert_Fill); + //在高度自适应的情况下有可能有最大最小高度约束。 + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:0 subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + //平均高度,当布局的gravity设置为Vert_Fill时指定这个平均高度值。 + CGFloat averageHeight = 0.0; + if (context->vertGravity == MyGravity_Vert_Fill) { + averageHeight = (context->selfSize.height - paddingVert - (arrangedCount - 1) * context->vertSpace) / arrangedCount; + } + //得到行数 + NSInteger arranges = floor((subviewEngines.count + arrangedCount - 1.0) / arrangedCount); + CGFloat lineTotalFixedHeights[arranges]; //所有行的固定高度。 + CGFloat lineTotalWeights[arranges]; //所有行的总比重 + CGFloat lineTotalShrinks[arranges]; //所有行的总压缩 + + CGFloat lineTotalFixedHeight = 0.0; + CGFloat lineTotalWeight = 0.0; + CGFloat lineTotalShrink = 0.0; //某一行的总压缩比重。 + BOOL hasTotalWeight = NO; //是否有比重计算,这个标志用于加快处理速度 + BOOL hasTotalShrink = NO; //是否有压缩计算,这个标志用于加快处理速度 - NSInteger arrangedIndex = 0; NSInteger i = 0; - CGFloat rowTotalWeight = 0; - CGFloat rowTotalFixedHeight = 0; - for (; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (arrangedIndex >= arrangedCount) - { - arrangedIndex = 0; - - if (rowTotalWeight != 0 && !averageArrange) - { - [self myCalcHorzLayoutSinglelineWeight:selfSize totalFloatHeight:selfSize.height - paddingVert - rowTotalFixedHeight totalWeight:rowTotalWeight sbs:sbs startIndex:i count:arrangedCount]; + NSInteger itemIndex = 0; + NSInteger lineIndex = 0; //行索引 + for (; i < subviewEngines.count; i++) { + MyLayoutEngine *subviewEngine = subviewEngines[i]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + + CGFloat topSpacing = subviewTraits.topPosInner.measure; + CGFloat bottomSpacing = subviewTraits.bottomPosInner.measure; + CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; + + if (itemIndex >= arrangedCount) { + lineTotalFixedHeights[lineIndex] = lineTotalFixedHeight; + lineTotalWeights[lineIndex] = lineTotalWeight; + lineTotalShrinks[lineIndex] = lineTotalShrink; + + if (!hasTotalWeight) { + hasTotalWeight = lineTotalWeight > 0; } - - rowTotalWeight = 0; - rowTotalFixedHeight = 0; - + if (!hasTotalShrink) { + hasTotalShrink = lineTotalShrink > 0; + } + if (lineTotalFixedHeight > maxLayoutHeight) { + maxLayoutHeight = lineTotalFixedHeight; + } + lineTotalFixedHeight = 0.0; + lineTotalWeight = 0.0; + lineTotalShrink = 0.0; + itemIndex = 0; + lineIndex++; + } + //水平流式布局因为高度依赖宽度自适应的情况比较少,所以这里直接先计算宽度 + if (pagingItemWidth != 0.0) { + subviewEngine.width = pagingItemWidth - leadingSpacing - trailingSpacing; + } else if (subviewTraits.widthSizeInner.val != nil) { + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; + + // 当只有一行而且是flex标准并且是stretch时会把所有子视图的宽度都强制拉伸为布局视图的宽度 + // 所以如果这里是宽度自适应时需要将宽度强制设置为和布局等宽,以便解决同时高度自适应时高度计算不正确的问题。 + if (arranges == 1 && + subviewTraits.widthSizeInner.wrapVal && + layoutTraits.isFlex && + context->horzGravity == MyGravity_Horz_Stretch && + subviewEngine.width > context->selfSize.width - paddingHorz - leadingSpacing - trailingSpacing) { + subviewEngine.width = context->selfSize.width - paddingHorz - leadingSpacing - trailingSpacing; + } + } else if (context->horzGravity == MyGravity_Horz_Fill || context->horzGravity == MyGravity_Horz_Stretch) { + subviewEngine.width = 0; } - CGFloat topSpace = sbvsc.topPosInner.absVal; - CGFloat bottomSpace = sbvsc.bottomPosInner.absVal; - CGRect rect = sbvmyFrame.frame; - - - if (pagingItemWidth != 0) - rect.size.width = pagingItemWidth; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - //当子视图的尺寸是相对依赖于其他尺寸的值。 - [self mySetSubviewRelativeDimeSize:sbvsc.widthSizeInner selfSize:selfSize lsc:lsc pRect:&rect]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + if (averageHeight != 0.0) { + subviewEngine.height = averageHeight - topSpacing - bottomSpacing; + } else if (subviewHeight != 0.0) { + subviewEngine.height = subviewHeight - topSpacing - bottomSpacing; + } else if (pagingItemHeight != 0.0) { + subviewEngine.height = pagingItemHeight - topSpacing - bottomSpacing; + } else if (subviewTraits.heightSizeInner.val != nil) { + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { //特殊处理高度等于宽度的情况 + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + } else { + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; + } + } else if (subviewTraits.weight != 0.0) { + subviewEngine.height = 0.0; + } + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { //特殊处理宽度等于高度的情况 + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + itemIndex++; - if (sbvsc.weight != 0) - { - - rowTotalWeight += sbvsc.weight; + lineTotalFixedHeight += subviewEngine.height; + lineTotalFixedHeight += topSpacing + bottomSpacing; + if (itemIndex < arrangedCount) { + lineTotalFixedHeight += context->vertSpace; } - else - { - - BOOL isFlexedHeight = sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]] && sbvsc.heightSizeInner.dimeRelaVal.view != self; - - if (pagingItemHeight != 0) - rect.size.height = pagingItemHeight; - - if (sbvsc.heightSizeInner.dimeNumVal != nil && !averageArrange) - rect.size.height = sbvsc.heightSizeInner.measure; - - //当子视图的尺寸是相对依赖于其他尺寸的值。 - [self mySetSubviewRelativeDimeSize:sbvsc.heightSizeInner selfSize:selfSize lsc:lsc pRect:&rect]; - - - //如果高度是浮动的则需要调整高度。 - if (isFlexedHeight) - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == sbvsc.heightSizeInner) - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:[sbvsc.widthSizeInner measureWith: rect.size.height ] sbvSize:rect.size selfLayoutSize:selfSize]; - - rowTotalFixedHeight += rect.size.height; + if (subviewTraits.weight != 0) { + lineTotalWeight += subviewTraits.weight; } - - rowTotalFixedHeight += topSpace + bottomSpace; - - - if (arrangedIndex != (arrangedCount - 1)) - rowTotalFixedHeight += vertSpace; - - - sbvmyFrame.frame = rect; - - arrangedIndex++; - + //计算总的压缩比 + lineTotalShrink += subviewTraits.topPosInner.shrink + subviewTraits.bottomPosInner.shrink; + lineTotalShrink += subviewTraits.heightSizeInner.shrink; } //最后一行。 - if (rowTotalWeight != 0 && !averageArrange) - { - if (arrangedIndex < arrangedCount) - rowTotalFixedHeight -= vertSpace; + if (arranges > 0) { + if (itemIndex < arrangedCount) { + lineTotalFixedHeight -= context->vertSpace; + } + lineTotalFixedHeights[lineIndex] = lineTotalFixedHeight; + lineTotalWeights[lineIndex] = lineTotalWeight; + lineTotalShrinks[lineIndex] = lineTotalShrink; - [self myCalcHorzLayoutSinglelineWeight:selfSize totalFloatHeight:selfSize.height - paddingVert - rowTotalFixedHeight totalWeight:rowTotalWeight sbs:sbs startIndex:i count:arrangedIndex]; + if (!hasTotalWeight) { + hasTotalWeight = lineTotalWeight > 0; + } + if (!hasTotalShrink) { + hasTotalShrink = lineTotalShrink > 0; + } + if (lineTotalFixedHeight > maxLayoutHeight) { + maxLayoutHeight = lineTotalFixedHeight; + } } + maxLayoutHeight += context->paddingTop + context->paddingBottom; + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:maxLayoutHeight subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + //进行所有行的宽度拉伸和压缩。 + if (context->vertGravity != MyGravity_Vert_Fill && (hasTotalWeight || hasTotalShrink)) { + NSInteger remainedCount = subviewEngines.count; + lineIndex = 0; + for (; lineIndex < arranges; lineIndex++) { + lineTotalFixedHeight = lineTotalFixedHeights[lineIndex]; + lineTotalWeight = lineTotalWeights[lineIndex]; + lineTotalShrink = lineTotalShrinks[lineIndex]; + + if (lineTotalWeight != 0) { + [self myHorzLayoutCalculateSinglelineWeight:lineTotalWeight lineSpareHeight:context->selfSize.height - paddingVert - lineTotalFixedHeight startItemIndex:lineIndex * arrangedCount itemCount:MIN(arrangedCount, remainedCount) withContext:context]; + } + + if (lineTotalShrink != 0) { + [self myHorzLayoutCalculateSinglelineShrink:lineTotalShrink lineSpareHeight:context->selfSize.height - paddingVert - lineTotalFixedHeight startItemIndex:lineIndex * arrangedCount itemCount:MIN(arrangedCount, remainedCount) withContext:context]; + } + + remainedCount -= arrangedCount; + } + } + + //初始化每行的下一个子视图的位置。 + NSMutableArray *nextPointOfCols = nil; + if (autoArrange) { + nextPointOfCols = [NSMutableArray arrayWithCapacity:arrangedCount]; + for (NSInteger idx = 0; idx < arrangedCount; idx++) { + [nextPointOfCols addObject:[NSValue valueWithCGPoint:CGPointMake(context->paddingLeading, context->paddingTop)]]; + } + } - CGFloat pageHeight = 0; //页高 - CGFloat averageHeight = (selfSize.height - paddingVert - (arrangedCount - 1) * vertSpace) / arrangedCount; - arrangedIndex = 0; + CGFloat pageHeight = 0.0; //页高 + maxLayoutHeight = context->paddingTop; + lineIndex = 0; + itemIndex = 0; + lineTotalShrink = 0.0; i = 0; - for (; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (arrangedIndex >= arrangedCount) - { - arrangedIndex = 0; - xPos += colMaxWidth; - xPos += horzSpace; + for (; i < subviewEngines.count; i++) { + MyLayoutEngine *subviewEngine = subviewEngines[i]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + + if (itemIndex >= arrangedCount) { + itemIndex = 0; + xPos += context->horzSpace; + xPos += lineMaxWidth; + + [self myHorzLayoutCalculateSingleline:lineIndex horzAlignment:horzAlignment lineMaxWidth:lineMaxWidth lineMaxHeight:lineMaxHeight lineTotalShrink:lineTotalShrink startItemIndex:i - arrangedCount itemCount:arrangedCount withContext:context]; //分别处理水平分页和垂直分页。 - if (isVertPaging) - { - if (i % lsc.pagedCount == 0) - { + if (isVertPaging) { + if (i % layoutTraits.pagedCount == 0) { pageHeight += CGRectGetHeight(self.superview.bounds); - if (!isPagingScroll) - pageHeight -= paddingTop; - - xPos = paddingLeading; + if (!isPagingScroll) { + pageHeight -= context->paddingTop; + } + xPos = context->paddingLeading; } - } - if (isHorzPaging) - { + if (isHorzPaging) { //如果是分页滚动则要多添加垂直间距。 - if (i % lsc.pagedCount == 0) - { - - if (isPagingScroll) - { - xPos -= horzSpace; - xPos += paddingTrailing; - xPos += paddingLeading; + if (i % layoutTraits.pagedCount == 0) { + if (isPagingScroll) { + xPos -= context->horzSpace; + xPos += paddingHorz; } } } + yPos = context->paddingTop + pageHeight; - yPos = paddingTop + pageHeight; - - - //计算每行的gravity情况。 - [self myCalcHorzLayoutSinglelineAlignment:selfSize colMaxWidth:colMaxWidth colMaxHeight:colMaxHeight vertGravity:vertGravity horzAlignment:horzAlign sbs:sbs startIndex:i count:arrangedCount vertSpace:vertSpace horzSpace:horzSpace isEstimate:isEstimate lsc:lsc]; - - colMaxWidth = 0; - colMaxHeight = 0; + lineMaxWidth = 0.0; + lineMaxHeight = 0.0; + lineTotalShrink = 0.0; + lineIndex++; } - CGFloat topSpace = sbvsc.topPosInner.absVal; - CGFloat leadingSpace = sbvsc.leadingPosInner.absVal; - CGFloat bottomSpace = sbvsc.bottomPosInner.absVal; - CGFloat trailingSpace = sbvsc.trailingPosInner.absVal; - CGRect rect = sbvmyFrame.frame; - + CGFloat topSpacing = subviewTraits.topPosInner.measure; + CGFloat leadingSpacing = subviewTraits.leadingPosInner.measure; + CGFloat bottomSpacing = subviewTraits.bottomPosInner.measure; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.measure; - if (averageArrange) - { + //得到最大的列宽 + if (_myCGFloatLess(lineMaxWidth, leadingSpacing + trailingSpacing + subviewEngine.width)) { + lineMaxWidth = leadingSpacing + trailingSpacing + subviewEngine.width; + } + //自动排列。 + if (autoArrange) { + //查找能存放当前子视图的最小x轴的位置以及索引。 + CGPoint minPoint = CGPointMake(CGFLOAT_MAX, CGFLOAT_MAX); + NSInteger minNextPointIndex = 0; + for (int idx = 0; idx < arrangedCount; idx++) { + CGPoint pt = nextPointOfCols[idx].CGPointValue; + if (minPoint.x > pt.x) { + minPoint = pt; + minNextPointIndex = idx; + } + } - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:averageHeight - topSpace - bottomSpace sbvSize:rect.size selfLayoutSize:selfSize]; + //找到的minNextPointIndex中的 + xPos = minPoint.x; + yPos = minPoint.y; - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == sbvsc.heightSizeInner) - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:[sbvsc.widthSizeInner measureWith: rect.size.height ] sbvSize:rect.size selfLayoutSize:selfSize]; + minPoint.x = minPoint.x + leadingSpacing + subviewEngine.width + trailingSpacing + context->horzSpace; + nextPointOfCols[minNextPointIndex] = [NSValue valueWithCGPoint:minPoint]; + if (minNextPointIndex + 1 <= arrangedCount - 1) { + minPoint = nextPointOfCols[minNextPointIndex + 1].CGPointValue; + minPoint.y = yPos + topSpacing + subviewEngine.height + bottomSpacing + context->vertSpace; + nextPointOfCols[minNextPointIndex + 1] = [NSValue valueWithCGPoint:minPoint]; + } + + if (_myCGFloatLess(maxLayoutWidth, xPos + leadingSpacing + subviewEngine.width + trailingSpacing)) { + maxLayoutWidth = xPos + leadingSpacing + subviewEngine.width + trailingSpacing; + } + } else if (horzAlignment == MyGravity_Horz_Between) { //当列是紧凑排列时需要特殊处理当前的水平位置。 + //第0列特殊处理。 + if (i - arrangedCount < 0) { + xPos = context->paddingLeading; + } else { + //取前一列的对应的行的子视图。 + MyLayoutEngine *prevColSubviewEngine = subviewEngines[i - arrangedCount]; + MyViewTraits *prevColSubviewTraits = prevColSubviewEngine.currentSizeClass; + //当前子视图的位置等于前一列对应行的最大x的值 + 前面对应行的尾部间距 + 子视图之间的列间距。 + xPos = CGRectGetMaxX(prevColSubviewEngine.frame) + prevColSubviewTraits.trailingPosInner.measure + context->horzSpace; + } + + if (_myCGFloatLess(maxLayoutWidth, xPos + leadingSpacing + subviewEngine.width + trailingSpacing)) { + maxLayoutWidth = xPos + leadingSpacing + subviewEngine.width + trailingSpacing; + } + } else { //正常排列。 + //这里的最大其实就是最后一个视图的位置加上最宽的子视图的尺寸。 + if (_myCGFloatLess(maxLayoutWidth, xPos + lineMaxWidth)) { + maxLayoutWidth = xPos + lineMaxWidth; + } } + subviewEngine.leading = xPos + leadingSpacing; + subviewEngine.top = yPos + topSpacing; + yPos += topSpacing + subviewEngine.height + bottomSpacing; - rect.origin.y = yPos + topSpace; - rect.origin.x = xPos + leadingSpace; - yPos += topSpace + rect.size.height + bottomSpace; - - if (arrangedIndex != (arrangedCount - 1)) - yPos += vertSpace; - - - if (_myCGFloatLess(colMaxWidth, leadingSpace + trailingSpace + rect.size.width)) - colMaxWidth = leadingSpace + trailingSpace + rect.size.width; - - if (_myCGFloatLess(colMaxHeight, (yPos - paddingTop))) - colMaxHeight = yPos - paddingTop; - - if (_myCGFloatLess(maxHeight, yPos)) - maxHeight = yPos; - - - sbvmyFrame.frame = rect; + if (_myCGFloatLess(lineMaxHeight, (yPos - context->paddingTop))) { + lineMaxHeight = yPos - context->paddingTop; + } + if (_myCGFloatLess(maxLayoutHeight, yPos)) { + maxLayoutHeight = yPos; + } + //不是最后一行以及非自动排列时才添加布局视图设置的行间距。自动排列的情况下上面已经有添加行间距了。 + if (itemIndex != (arrangedCount - 1) && !autoArrange) { + yPos += context->vertSpace; + } - arrangedIndex++; + itemIndex++; + //这里只对间距进行压缩比重的计算。 + lineTotalShrink += subviewTraits.topPosInner.shrink + subviewTraits.bottomPosInner.shrink; } - //最后一列 - [self myCalcHorzLayoutSinglelineAlignment:selfSize colMaxWidth:colMaxWidth colMaxHeight:colMaxHeight vertGravity:vertGravity horzAlignment:horzAlign sbs:sbs startIndex:i count:arrangedIndex vertSpace:vertSpace horzSpace:horzSpace isEstimate:isEstimate lsc:lsc]; + maxLayoutWidth += context->paddingTrailing; - if (lsc.wrapContentHeight && !averageArrange) - { - selfSize.height = maxHeight + paddingBottom; + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = maxLayoutWidth; //只有在父视图为滚动视图,且开启了分页滚动时才会扩充具有包裹设置的布局视图的宽度。 - if (isVertPaging && isPagingScroll) - { + if (isHorzPaging && isPagingScroll) { //算出页数来。如果包裹计算出来的宽度小于指定页数的宽度,因为要分页滚动所以这里会扩充布局的宽度。 - NSInteger totalPages = floor((sbs.count + lsc.pagedCount - 1.0 ) / lsc.pagedCount); - if (_myCGFloatLess(selfSize.height, totalPages * CGRectGetHeight(self.superview.bounds))) - selfSize.height = totalPages * CGRectGetHeight(self.superview.bounds); + NSInteger totalPages = floor((subviewEngines.count + layoutTraits.pagedCount - 1.0) / layoutTraits.pagedCount); + if (_myCGFloatLess(context->selfSize.width, totalPages * CGRectGetWidth(self.superview.bounds))) { + context->selfSize.width = totalPages * CGRectGetWidth(self.superview.bounds); + } } - + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:context->selfSize.width subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + //根据flex规则:如果只有一行则整个宽度都作为子视图的拉伸和停靠区域。 + if (layoutTraits.isFlex && arranges == 1) { + lineMaxWidth = context->selfSize.width - paddingHorz; } + //最后一行 + [self myHorzLayoutCalculateSingleline:lineIndex horzAlignment:horzAlignment lineMaxWidth:lineMaxWidth lineMaxHeight:lineMaxHeight lineTotalShrink:lineTotalShrink startItemIndex:i - itemIndex itemCount:itemIndex withContext:context]; - if (lsc.wrapContentWidth) - { - selfSize.width = xPos + paddingTrailing + colMaxWidth; + //整体的停靠。 + if (context->horzGravity != MyGravity_None && context->selfSize.width != maxLayoutWidth && !(isHorzPaging && isPagingScroll)) { - //只有在父视图为滚动视图,且开启了分页滚动时才会扩充具有包裹设置的布局视图的宽度。 - if (isHorzPaging && isPagingScroll) - { - //算出页数来。如果包裹计算出来的宽度小于指定页数的宽度,因为要分页滚动所以这里会扩充布局的宽度。 - NSInteger totalPages = floor((sbs.count + lsc.pagedCount - 1.0 ) / lsc.pagedCount); - if (_myCGFloatLess(selfSize.width, totalPages * CGRectGetWidth(self.superview.bounds))) - selfSize.width = totalPages * CGRectGetWidth(self.superview.bounds); + //根据flex标准:只有在多行下horzGravity才有意义。非flex标准则不受这个条件约束。 + if (arranges > 1 || !layoutTraits.isFlex) { + CGFloat addXPos = 0.0; + CGFloat between = 0.0; + CGFloat fill = 0.0; + + if (arranges <= 1 && context->horzGravity == MyGravity_Horz_Around) { + context->horzGravity = MyGravity_Horz_Center; + } + + if (context->horzGravity == MyGravity_Horz_Center) { + addXPos = (context->selfSize.width - maxLayoutWidth) / 2; + } else if (context->horzGravity == MyGravity_Horz_Trailing) { + addXPos = context->selfSize.width - maxLayoutWidth; + } else if (context->horzGravity == MyGravity_Horz_Fill || context->horzGravity == MyGravity_Horz_Stretch) { + if (arranges > 0) { + fill = (context->selfSize.width - maxLayoutWidth) / arranges; + } + //满足flex规则:如果剩余的空间是负数,该值等效于'flex-start' + if (fill < 0.0 && context->horzGravity == MyGravity_Horz_Stretch) { + fill = 0.0; + } + } else if (context->horzGravity == MyGravity_Horz_Between) { + if (arranges > 1) { + between = (context->selfSize.width - maxLayoutWidth) / (arranges - 1); + } + } else if (context->horzGravity == MyGravity_Horz_Around) { + between = (context->selfSize.width - maxLayoutWidth) / arranges; + } else if (context->horzGravity == MyGravity_Horz_Among) { + between = (context->selfSize.width - maxLayoutWidth) / (arranges + 1); + } + + if (addXPos != 0.0 || between != 0.0 || fill != 0.0) { + for (int i = 0; i < subviewEngines.count; i++) { + + MyLayoutEngine *subviewEngine = subviewEngines[i]; + + int lineidx = i / arrangedCount; + if (context->horzGravity == MyGravity_Horz_Stretch) { + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + if (subviewTraits.widthSizeInner.val == nil || (subviewTraits.widthSizeInner.wrapVal && ![subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { + subviewEngine.width += fill; + } else { + //因为每行都增加了fill。所以如果有行内对齐则需要这里调整。 + MyGravity subviewHorzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(subviewTraits.alignment)]; + if (subviewHorzAlignment == MyGravity_None) { + subviewHorzAlignment = horzAlignment; + } + if (subviewHorzAlignment == MyGravity_Horz_Center) { + subviewEngine.leading += fill / 2.0; + } else if (subviewHorzAlignment == MyGravity_Horz_Trailing) { + subviewEngine.leading += fill; + } + } + } else { + subviewEngine.width += fill; + } + subviewEngine.leading += fill * lineidx; + + subviewEngine.leading += addXPos; + + subviewEngine.leading += between * lineidx; + + if (context->horzGravity == MyGravity_Horz_Around) { + subviewEngine.leading += (between / 2.0); + } + if (context->horzGravity == MyGravity_Horz_Among) { + subviewEngine.leading += between; + } + } + } } - } - else - { - - CGFloat addXPos = 0; - CGFloat between = 0; - CGFloat fill = 0; - int arranges = floor((sbs.count + arrangedCount - 1.0) / arrangedCount); - - if (horzGravity == MyGravity_Horz_Center) - { - addXPos = (selfSize.width - paddingTrailing - colMaxWidth - xPos) / 2; - } - else if (horzGravity == MyGravity_Horz_Trailing) - { - addXPos = selfSize.width - paddingTrailing - colMaxWidth - xPos; - } - else if (horzGravity == MyGravity_Horz_Fill) - { - if (arranges > 0) - fill = (selfSize.width - paddingTrailing - colMaxWidth - xPos) / arranges; - } - else if (horzGravity == MyGravity_Horz_Between) - { - if (arranges > 1) - between = (selfSize.width - paddingLeading - colMaxWidth - xPos) / (arranges - 1); - } + + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = maxLayoutHeight + context->paddingBottom; - if (addXPos != 0 || between != 0 || fill != 0) - { - for (int i = 0; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - - MyFrame *sbvmyFrame = sbv.myFrame; - - int lines = i / arrangedCount; - sbvmyFrame.width += fill; - sbvmyFrame.leading += fill * lines; - - sbvmyFrame.leading += addXPos; - - sbvmyFrame.leading += between * lines; - + //只有在父视图为滚动视图,且开启了分页滚动时才会扩充具有包裹设置的布局视图的宽度。 + if (isVertPaging && isPagingScroll) { + //算出页数来。如果包裹计算出来的宽度小于指定页数的宽度,因为要分页滚动所以这里会扩充布局的宽度。 + NSInteger totalPages = floor((subviewEngines.count + layoutTraits.pagedCount - 1.0) / layoutTraits.pagedCount); + if (_myCGFloatLess(context->selfSize.height, totalPages * CGRectGetHeight(self.superview.bounds))) { + context->selfSize.height = totalPages * CGRectGetHeight(self.superview.bounds); } } } - - - return selfSize; - } - - @end diff --git a/MyLayout/Lib/MyFrameLayout.h b/MyLayout/Lib/MyFrameLayout.h index fb35c7c..9990227 100644 --- a/MyLayout/Lib/MyFrameLayout.h +++ b/MyLayout/Lib/MyFrameLayout.h @@ -8,32 +8,9 @@ #import "MyBaseLayout.h" -/* -@interface UIView(MyFrameLayoutExt) - -//新版本1.3.3以后的版本将不再提供框架布局对子视图的这个扩展属性的支持。 -@property(nonatomic, assign) MyGravity marginGravity; - - 1.如果您想让子视图右对齐框架布局,则只需要设置myRight = 数值 和宽度即可 不再需要设置marginGravity = MyGravity_Horz_Right - 2.如果您想让子视图底对齐框架布局,则只需要设置myBottom = 数值 和高度即可,不再需要设置marginGravity = MyGravity_Vert_Bottom - 3.如果您想让子视图在框架布局中垂直居中,则只需要设置myCenterY = 数值 和高度即可,不再需要设置marginGravity = MyGravity_Vert_Center - 4.如果您想让子视图在框架布局中水平居中,则只需要设置myCenterX = 数值 和宽度即可,不再需要设置marginGravity = MyGravity_Horz_Center - 5.如果您想让子视图在框架布局中宽度填满,则只需要设置myHorzMargin = 0,不再需要设置marginGravity = MyGravity_Horz_Fill - 6.如果您想让子视图在框架布局中高度填满,则只需要设置myVertMargin = 0, 不再需要设置marginGravity = MyGravity_Vert_Fill - 7.如果你要水平和垂直方向都设置,则请同时设置对应位置对象的值。 - 8.新版本将不再支持让框架布局中的子视图在窗口中居中了。 - - - 请使用了marginGravity属性的代码进行对应的代码变更!!! - -@end -*/ - /** 框架布局是一种里面的子视图停靠在父视图特定方位并且可以重叠的布局视图。框架布局里面的子视图的布局位置和添加的顺序无关,框架布局中的所有子视图的约束依赖都只是针对于和父布局视图的。框架布局是一种特殊的相对布局,因此如果某些布局里面的子视图只依赖于父视图的边界时则可以用框架布局来代替,从而加快布局的速度。 */ @interface MyFrameLayout : MyBaseLayout - @end - diff --git a/MyLayout/Lib/MyFrameLayout.m b/MyLayout/Lib/MyFrameLayout.m index eb354eb..2a4cfde 100644 --- a/MyLayout/Lib/MyFrameLayout.m +++ b/MyLayout/Lib/MyFrameLayout.m @@ -9,247 +9,64 @@ #import "MyFrameLayout.h" #import "MyLayoutInner.h" - - @implementation MyFrameLayout -/* - // Only override drawRect: if you perform custom drawing. - // An empty implementation adversely affects performance during animation. - - (void)drawRect:(CGRect)rect { - // Drawing code - } - */ - +#pragma mark-- Override Methods -#pragma mark -- Override Method +- (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { + [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; - --(CGSize)calcLayoutRect:(CGSize)size isEstimate:(BOOL)isEstimate pHasSubLayout:(BOOL*)pHasSubLayout sizeClass:(MySizeClass)sizeClass sbs:(NSMutableArray *)sbs -{ - CGSize selfSize = [super calcLayoutRect:size isEstimate:isEstimate pHasSubLayout:pHasSubLayout sizeClass:sizeClass sbs:sbs]; - - if (sbs == nil) - sbs = [self myGetLayoutSubviews]; - - MyFrameLayout *lsc = self.myCurrentSizeClass; - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - - MyGravity horzGravity = [self myConvertLeftRightGravityToLeadingTrailing:lsc.gravity & MyGravity_Vert_Mask]; - MyGravity vertGravity = lsc.gravity & MyGravity_Horz_Mask; - - - CGSize maxWrapSize = CGSizeMake(paddingLeading + paddingTrailing, paddingTop + paddingBottom); + MyFrameLayoutTraits *layoutTraits = (MyFrameLayoutTraits *)context->layoutViewEngine.currentSizeClass; + context->paddingTop = layoutTraits.myLayoutPaddingTop; + context->paddingBottom = layoutTraits.myLayoutPaddingBottom; + context->paddingLeading = layoutTraits.myLayoutPaddingLeading; + context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + context->vertGravity = MYVERTGRAVITY(layoutTraits.gravity); + context->horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.gravity)]; + if (context->subviewEngines == nil) { + context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; + } + + CGSize maxWrapSize = CGSizeMake(context->paddingLeading + context->paddingTrailing, context->paddingTop + context->paddingBottom); CGSize *pMaxWrapSize = &maxWrapSize; - if (!lsc.wrapContentHeight && !lsc.wrapContentWidth) + if (!layoutTraits.heightSizeInner.wrapVal && !layoutTraits.widthSizeInner.wrapVal) { pMaxWrapSize = NULL; - - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - - [self myAdjustSubviewWrapContentSet:sbv isEstimate:isEstimate sbvmyFrame:sbvmyFrame sbvsc:sbvsc selfSize:selfSize sizeClass:sizeClass pHasSubLayout:pHasSubLayout]; + } + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + [self myAdjustSizeSettingOfSubviewEngine:subviewEngine withContext:context]; //计算自己的位置和高宽 - [self myCalcSubViewRect:sbv sbvsc:sbvsc sbvmyFrame:sbvmyFrame lsc:lsc vertGravity:vertGravity horzGravity:horzGravity inSelfSize:selfSize paddingTop:paddingTop paddingLeading:paddingLeading paddingBottom:paddingBottom paddingTrailing:paddingTrailing pMaxWrapSize:pMaxWrapSize]; - + [self myCalcRectOfSubviewEngine:subviewEngine pMaxWrapSize:pMaxWrapSize withContext:context]; } - - if (lsc.wrapContentWidth) - { - selfSize.width = maxWrapSize.width; + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:maxWrapSize.width subviewSize: context->selfSize selfLayoutSize:self.superview.bounds.size]; } - - if (lsc.wrapContentHeight) - { - selfSize.height = maxWrapSize.height; + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:maxWrapSize.height subviewSize: context->selfSize selfLayoutSize:self.superview.bounds.size]; } - - //调整布局视图自己的尺寸。 - [self myAdjustLayoutSelfSize:&selfSize lsc:lsc]; - //如果布局视图具有包裹属性这里要调整那些依赖父视图宽度和高度的子视图的位置和尺寸。 - if ((lsc.wrapContentWidth && horzGravity != MyGravity_Horz_Fill) || (lsc.wrapContentHeight && vertGravity != MyGravity_Vert_Fill)) - { - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - + if ((layoutTraits.widthSizeInner.wrapVal && context->horzGravity != MyGravity_Horz_Fill) || (layoutTraits.heightSizeInner.wrapVal && context->vertGravity != MyGravity_Vert_Fill)) { + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + //只有子视图的尺寸或者位置依赖父视图的情况下才需要重新计算位置和尺寸。 - if ((sbvsc.trailingPosInner.posVal != nil) || - (sbvsc.bottomPosInner.posVal != nil) || - (sbvsc.centerXPosInner.posVal != nil) || - (sbvsc.centerYPosInner.posVal != nil) || - (sbvsc.widthSizeInner.dimeRelaVal.view == self) || - (sbvsc.heightSizeInner.dimeRelaVal.view == self) - ) - { - [self myCalcSubViewRect:sbv sbvsc:sbvsc sbvmyFrame:sbvmyFrame lsc:lsc vertGravity:vertGravity horzGravity:horzGravity inSelfSize:selfSize paddingTop:paddingTop paddingLeading:paddingLeading paddingBottom:paddingBottom paddingTrailing:paddingTrailing pMaxWrapSize:NULL]; + if ((subviewTraits.trailingPosInner.val != nil) || + (subviewTraits.bottomPosInner.val != nil) || + (subviewTraits.centerXPosInner.val != nil) || + (subviewTraits.centerYPosInner.val != nil) || + (subviewTraits.widthSizeInner.anchorVal.view == self) || + (subviewTraits.heightSizeInner.anchorVal.view == self) || + subviewTraits.widthSizeInner.fillVal || + subviewTraits.heightSizeInner.fillVal) { + [self myCalcRectOfSubviewEngine:subviewEngine pMaxWrapSize:NULL withContext:context]; } - } } - - - //如果是反向则调整左右位置。 - [self myAdjustSubviewsRTLPos:sbs selfWidth:selfSize.width]; - - return [self myAdjustSizeWhenNoSubviews:selfSize sbs:sbs lsc:lsc]; - + return [self myAdjustLayoutViewSizeWithContext:context]; } --(id)createSizeClassInstance -{ - return [MyFrameLayoutViewSizeClass new]; +- (id)createSizeClassInstance { + return [MyFrameLayoutTraits new]; } - - - --(void)myCalcSubViewRect:(UIView*)sbv - sbvsc:(UIView*)sbvsc - sbvmyFrame:(MyFrame*)sbvmyFrame - lsc:(MyFrameLayout*)lsc - vertGravity:(MyGravity)vertGravity - horzGravity:(MyGravity)horzGravity - inSelfSize:(CGSize)selfSize - paddingTop:(CGFloat)paddingTop - paddingLeading:(CGFloat)paddingLeading - paddingBottom:(CGFloat)paddingBottom - paddingTrailing:(CGFloat)paddingTrailing - pMaxWrapSize:(CGSize*)pMaxWrapSize -{ - - - CGRect rect = sbvmyFrame.frame; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - {//宽度等于固定的值。 - - rect.size.width = sbvsc.widthSizeInner.measure; - } - else if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal.view != sbv) - {//宽度等于其他的依赖的视图。 - - if (sbvsc.widthSizeInner.dimeRelaVal == self.widthSizeInner) - rect.size.width = [sbvsc.widthSizeInner measureWith:selfSize.width - paddingLeading - paddingTrailing]; - else if (sbvsc.widthSizeInner.dimeRelaVal == self.heightSizeInner) - rect.size.width = [sbvsc.widthSizeInner measureWith:selfSize.height - paddingTop - paddingBottom]; - else - rect.size.width = [sbvsc.widthSizeInner measureWith:sbvsc.widthSizeInner.dimeRelaVal.view.estimatedRect.size.width]; - } - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - [self myCalcHorzGravity:[self myGetSubviewHorzGravity:sbv sbvsc:sbvsc horzGravity:horzGravity] sbv:sbv sbvsc:sbvsc paddingLeading:paddingLeading paddingTrailing:paddingTrailing selfSize:selfSize pRect:&rect]; - - - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - {//高度等于固定的值。 - rect.size.height = sbvsc.heightSizeInner.measure; - } - else if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal.view != sbv) - {//高度等于其他依赖的视图 - if (sbvsc.heightSizeInner.dimeRelaVal == self.heightSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:selfSize.height - paddingTop - paddingBottom]; - else if (sbvsc.heightSizeInner.dimeRelaVal == self.widthSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:selfSize.width - paddingLeading - paddingTrailing]; - else - rect.size.height = [sbvsc.heightSizeInner measureWith:sbvsc.heightSizeInner.dimeRelaVal.view.estimatedRect.size.height]; - } - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - {//高度等于内容的高度 - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - } - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - [self myCalcVertGravity:[self myGetSubviewVertGravity:sbv sbvsc:sbvsc vertGravity:vertGravity] sbv:sbv sbvsc:sbvsc paddingTop:paddingTop paddingBottom:paddingBottom baselinePos:CGFLOAT_MAX selfSize:selfSize pRect:&rect]; - - - //特殊处理宽度等于高度 - if (sbvsc.widthSizeInner.dimeRelaVal.view == sbv && sbvsc.widthSizeInner.dimeRelaVal.dime == MyGravity_Vert_Fill) - { - rect.size.width = [sbvsc.widthSizeInner measureWith:rect.size.height]; - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - [self myCalcHorzGravity:[self myGetSubviewHorzGravity:sbv sbvsc:sbvsc horzGravity:horzGravity] sbv:sbv sbvsc:sbvsc paddingLeading:paddingLeading paddingTrailing:paddingTrailing selfSize:selfSize pRect:&rect]; - } - - //特殊处理高度等于宽度。 - if (sbvsc.heightSizeInner.dimeRelaVal.view == sbv && sbvsc.heightSizeInner.dimeRelaVal.dime == MyGravity_Horz_Fill) - { - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width]; - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - { - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - } - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - [self myCalcVertGravity:[self myGetSubviewVertGravity:sbv sbvsc:sbvsc vertGravity:vertGravity] sbv:sbv sbvsc:sbvsc paddingTop:paddingTop paddingBottom:paddingBottom baselinePos:CGFLOAT_MAX selfSize:selfSize pRect:&rect]; - - } - - sbvmyFrame.frame = rect; - - if (pMaxWrapSize != NULL) - { - if (lsc.wrapContentWidth) - { - //如果同时设置左右边界则左右边界为最小的宽度 - if (sbvsc.leadingPosInner.posVal != nil && sbvsc.trailingPosInner.posVal != nil) - { - if (_myCGFloatLess(pMaxWrapSize->width, sbvsc.leadingPosInner.absVal + sbvsc.trailingPosInner.absVal + paddingLeading + paddingTrailing)) - pMaxWrapSize->width = sbvsc.leadingPosInner.absVal + sbvsc.trailingPosInner.absVal + paddingLeading + paddingTrailing; - } - - //宽度不依赖布局并且没有同时设置左右边距则参与最大宽度计算。 - if ((sbvsc.widthSizeInner.dimeRelaVal.view != self) && - (sbvsc.leadingPosInner.posVal == nil || sbvsc.trailingPosInner.posVal == nil)) - { - - if (_myCGFloatLess(pMaxWrapSize->width, sbvmyFrame.width + sbvsc.leadingPosInner.absVal + sbvsc.centerXPosInner.absVal + sbvsc.trailingPosInner.absVal + paddingLeading + paddingTrailing)) - pMaxWrapSize->width = sbvmyFrame.width + sbvsc.leadingPosInner.absVal + sbvsc.centerXPosInner.absVal + sbvsc.trailingPosInner.absVal + paddingLeading + paddingTrailing; - - if (_myCGFloatLess(pMaxWrapSize->width,sbvmyFrame.trailing + sbvsc.trailingPosInner.absVal + paddingTrailing)) - pMaxWrapSize->width = sbvmyFrame.trailing + sbvsc.trailingPosInner.absVal + paddingTrailing; - - } - } - - if (lsc.wrapContentHeight) - { - //如果同时设置上下边界则上下边界为最小的高度 - if (sbvsc.topPosInner.posVal != nil && sbvsc.bottomPosInner.posVal != nil) - { - if (_myCGFloatLess(pMaxWrapSize->height, sbvsc.topPosInner.absVal + sbvsc.bottomPosInner.absVal + paddingTop + paddingBottom)) - pMaxWrapSize->height = sbvsc.topPosInner.absVal + sbvsc.bottomPosInner.absVal + paddingTop + paddingBottom; - } - - //高度不依赖布局并且没有同时设置上下边距则参与最大高度计算。 - if ((sbvsc.heightSizeInner.dimeRelaVal.view != self) && - (sbvsc.topPosInner.posVal == nil || sbvsc.bottomPosInner.posVal == nil)) - { - if (_myCGFloatLess(pMaxWrapSize->height, sbvmyFrame.height + sbvsc.topPosInner.absVal + sbvsc.centerYPosInner.absVal + sbvsc.bottomPosInner.absVal + paddingTop + paddingBottom)) - pMaxWrapSize->height = sbvmyFrame.height + sbvsc.topPosInner.absVal + sbvsc.centerYPosInner.absVal + sbvsc.bottomPosInner.absVal + paddingTop + paddingBottom; - - if (_myCGFloatLess(pMaxWrapSize->height, sbvmyFrame.bottom + sbvsc.bottomPosInner.absVal + paddingBottom)) - pMaxWrapSize->height = sbvmyFrame.bottom + sbvsc.bottomPosInner.absVal + paddingBottom; - } - } - } - - -} - - @end diff --git a/MyLayout/Lib/MyGrid.h b/MyLayout/Lib/MyGrid.h index a3c4227..1b6cd3d 100644 --- a/MyLayout/Lib/MyGrid.h +++ b/MyLayout/Lib/MyGrid.h @@ -6,73 +6,67 @@ // Copyright © 2017年 YoungSoft. All rights reserved. // -#import "MyLayoutDef.h" #import "MyBorderline.h" +#import "MyLayoutDef.h" /** * 定义格式化栅格描述语言的key和对应的value。这些值可以用来设置栅格布局的gridDictionary属性中字典的各种键值对。 */ -extern NSString * const kMyGridTag; //NSNumber类型,设置栅格的标签,对应MyGrid的tag属性。 具体值为NSInteger -extern NSString * const kMyGridAction; //NSString类型,设置栅格的触摸动作,对应MyGrid的setTarget中的action参数。 -extern NSString * const kMyGridActionData; //id类型, 设置栅格的动作数据,对应MyGrid的actionData属性。 -extern NSString * const kMyGridRows; //NSArray类型,表示里面的值是行子栅格数组.数组的元素是一个个子栅格字典对象 -extern NSString * const kMyGridCols; //NSArray类型,表示里面的值是列子栅格数组.数组的元素是一个个子栅格字典对象 -extern NSString * const kMyGridSize; //NSString类型或者NSNumber类型。设置栅格的尺寸,可以为特定的值vMyGridSizeWrap或者vMyGridSizeFill,也可以为某个具体的数字比如20.0, 还可以为百分比数字比如:@"20%" 或者@"-20%"。 -extern NSString * const kMyGridPadding; //NSString类型,设置栅格的内边距,对应MyGrid的padding属性,具体的值的格式为:@"{上,左,下,右}" -extern NSString * const kMyGridSpace; //NSNumber类型,栅格的内子栅格的间距,对应MyGrid的subviewSpace属性。 -extern NSString * const kMyGridGravity; //NSString类型,栅格的停靠属性,对应MyGrid的gravity属性,具体的值请参考下面的定义,比如:@"top|left" -extern NSString * const kMyGridPlaceholder; //NSNumber类型,栅格的占位属性,对应MyGrid的placeholder 属性,具体的值设置为YES or NO -extern NSString * const kMyGridAnchor; //NSNumber类型,栅格的锚点属性,对应MyGrid的anchor属性,具体的值设置为YES or NO -extern NSString * const kMyGridOverlap; //NSString类型,重叠视图停靠属性,对应MyGrid的gravity属性,具体的值请参考下面的定义,比如:@"top|left" -extern NSString * const kMyGridTopBorderline; //NSDictionary类型 栅格的顶部边界线对象。 -extern NSString * const kMyGridBottomBorderline; //NSDictionary类型 栅格的底部边界线对象。 -extern NSString * const kMyGridLeftBorderline; //NSDictionary类型 栅格的左边边界线对象。 -extern NSString * const kMyGridRightBorderline; //NSDictionary类型 栅格的右边边界线对象。 +extern NSString *const kMyGridTag; //NSNumber类型,设置栅格的标签,对应MyGrid的tag属性。 具体值为NSInteger +extern NSString *const kMyGridAction; //NSString类型,设置栅格的触摸动作,对应MyGrid的setTarget中的action参数。 +extern NSString *const kMyGridActionData; //id类型, 设置栅格的动作数据,对应MyGrid的actionData属性。 +extern NSString *const kMyGridRows; //NSArray类型,表示里面的值是行子栅格数组.数组的元素是一个个子栅格字典对象 +extern NSString *const kMyGridCols; //NSArray类型,表示里面的值是列子栅格数组.数组的元素是一个个子栅格字典对象 +extern NSString *const kMyGridSize; //NSString类型或者NSNumber类型。设置栅格的尺寸,可以为特定的值vMyGridSizeWrap或者vMyGridSizeFill,也可以为某个具体的数字比如20.0, 还可以为百分比数字比如:@"20%" 或者@"-20%"。 +extern NSString *const kMyGridPadding; //NSString类型,设置栅格的内边距,对应MyGrid的padding属性,具体的值的格式为:@"{上,左,下,右}" +extern NSString *const kMyGridSpace; //NSNumber类型,栅格的内子栅格的间距,对应MyGrid的subviewSpace属性。 +extern NSString *const kMyGridGravity; //NSString类型,栅格的停靠属性,对应MyGrid的gravity属性,具体的值请参考下面的定义,比如:@"top|left" +extern NSString *const kMyGridPlaceholder; //NSNumber类型,栅格的占位属性,对应MyGrid的placeholder 属性,具体的值设置为YES or NO +extern NSString *const kMyGridAnchor; //NSNumber类型,栅格的锚点属性,对应MyGrid的anchor属性,具体的值设置为YES or NO +extern NSString *const kMyGridOverlap; //NSString类型,重叠视图停靠属性,对应MyGrid的gravity属性,具体的值请参考下面的定义,比如:@"top|left" +extern NSString *const kMyGridTopBorderline; //NSDictionary类型 栅格的顶部边界线对象。 +extern NSString *const kMyGridBottomBorderline; //NSDictionary类型 栅格的底部边界线对象。 +extern NSString *const kMyGridLeftBorderline; //NSDictionary类型 栅格的左边边界线对象。 +extern NSString *const kMyGridRightBorderline; //NSDictionary类型 栅格的右边边界线对象。 //栅格的边界线对象所能设置的key -extern NSString * const kMyGridBorderlineColor; //NSString类型,用字符串格式描述的颜色值。具体为:@"#FF0000" 表示红色 -extern NSString * const kMyGridBorderlineThick; //NSNumber类型, 边界线的粗细 -extern NSString * const kMyGridBorderlineHeadIndent; //NSNumber类型,边界线的头部缩进值 -extern NSString * const kMyGridBorderlineTailIndent; //NSNumber类型,边界线的尾部缩进值。 -extern NSString * const kMyGridBorderlineOffset; //NSNumber类型,边界线的偏移值。 -extern NSString * const kMyGridBorderlineDash; //NSNumber类型,边界线是虚线。 - +extern NSString *const kMyGridBorderlineColor; //NSString类型,用字符串格式描述的颜色值。具体为:@"#FF0000" 表示红色 +extern NSString *const kMyGridBorderlineThick; //NSNumber类型, 边界线的粗细 +extern NSString *const kMyGridBorderlineHeadIndent; //NSNumber类型,边界线的头部缩进值 +extern NSString *const kMyGridBorderlineTailIndent; //NSNumber类型,边界线的尾部缩进值。 +extern NSString *const kMyGridBorderlineOffset; //NSNumber类型,边界线的偏移值。 +extern NSString *const kMyGridBorderlineDash; //NSNumber类型,边界线是虚线。 //kMyGridSize可以设置的特殊值 -extern NSString * const vMyGridSizeWrap; //表示尺寸由子栅格决定。 -extern NSString * const vMyGridSizeFill; //表示尺寸填充父栅格的剩余部分 +extern NSString *const vMyGridSizeWrap; //表示尺寸由子栅格决定。 +extern NSString *const vMyGridSizeFill; //表示尺寸填充父栅格的剩余部分 //kMyGridGravity可以设置的值 -extern NSString * const vMyGridGravityTop; //对应MyGravity_Vert_Top -extern NSString * const vMyGridGravityBottom; //对应MyGravity_Vert_Bottom -extern NSString * const vMyGridGravityLeft; //对应MyGravity_Horz_Left -extern NSString * const vMyGridGravityRight; //对应MyGravity_Horz_Right -extern NSString * const vMyGridGravityLeading; //对应MyGravity_Horz_Leading -extern NSString * const vMyGridGravityTrailing; //对应MyGravity_Horz_Trailing -extern NSString * const vMyGridGravityCenterX; //对应MyGravity_Horz_CenterX -extern NSString * const vMyGridGravityCenterY; //对应MyGravity_Vert_CenterY -extern NSString * const vMyGridGravityWidthFill; //对应MyGravity_Horz_Fill -extern NSString * const vMyGridGravityHeightFill; //对应MyGravity_Vert_Fill - +extern NSString *const vMyGridGravityTop; //对应MyGravity_Vert_Top +extern NSString *const vMyGridGravityBottom; //对应MyGravity_Vert_Bottom +extern NSString *const vMyGridGravityLeft; //对应MyGravity_Horz_Left +extern NSString *const vMyGridGravityRight; //对应MyGravity_Horz_Right +extern NSString *const vMyGridGravityLeading; //对应MyGravity_Horz_Leading +extern NSString *const vMyGridGravityTrailing; //对应MyGravity_Horz_Trailing +extern NSString *const vMyGridGravityCenterX; //对应MyGravity_Horz_CenterX +extern NSString *const vMyGridGravityCenterY; //对应MyGravity_Vert_CenterY +extern NSString *const vMyGridGravityWidthFill; //对应MyGravity_Horz_Fill +extern NSString *const vMyGridGravityHeightFill; //对应MyGravity_Vert_Fill /** 栅格动作接口,您可以触摸栅格来执行特定的动作和事件。 */ -@protocol MyGridAction - +@protocol MyGridAction /** 栅格的标签标识,用于在事件中区分栅格。 */ -@property(nonatomic) NSInteger tag; - +@property (nonatomic) NSInteger tag; /** 栅格的动作数据,这个数据是栅格的扩展数据,您可以在动作中使用这个附加的数据来进行一系列操作。他可以是一个数值,也可以是个字符串,甚至可以是一段JS脚本。 */ -@property(nonatomic, strong) id actionData; - - +@property (nonatomic, strong) id actionData; /** 设置栅格的事件,如果取消栅格事件则设置target为nil @@ -80,46 +74,38 @@ extern NSString * const vMyGridGravityHeightFill; //对应MyGravity_Vert_Fill @param target action事件的调用者 @param action action事件,格式为:-(void)handle:(id)sender */ --(void)setTarget:(id)target action:(SEL)action; +- (void)setTarget:(id)target action:(SEL)action; @end - - - /** 栅格协议。用来描述栅格矩形区域,所以一个栅格就是一个矩形区域。 这个接口用来描述栅格的一些属性以及栅格的添加和删除。栅格可以按某个方向拆分为众多子栅格,而且这个过程可以递归进行。 所有栅格布局中的子视图都将依次放入叶子栅格的区域中。 */ @protocol MyGrid - //设置和获取栅格的尺寸 -@property(nonatomic, assign) CGFloat measure; - +@property (nonatomic, assign) CGFloat measure; //得到父栅格。根栅格的父栅格为nil -@property(nonatomic, weak, readonly) id superGrid; - - -//得到所有子栅格 -@property(nonatomic, strong, readonly) NSArray> *subGrids; +@property (nonatomic, weak, readonly) id superGrid; +/** + 得到所有子栅格 + */ +@property (nonatomic, strong, readonly) NSArray> *subGrids; -//克隆出一个新栅格以及其下的所有子栅格。 --(id)cloneGrid; /** 栅格内子栅格之间的间距。 */ -@property(nonatomic, assign) CGFloat subviewSpace; +@property (nonatomic, assign) CGFloat subviewSpace; /** 栅格内子栅格或者叶子栅格内视图的四周内边距。 */ -@property(nonatomic, assign) UIEdgeInsets padding; - +@property (nonatomic, assign) UIEdgeInsets padding; /** 栅格内子栅格或者叶子栅格内视图的对齐停靠方式. @@ -127,41 +113,38 @@ extern NSString * const vMyGridGravityHeightFill; //对应MyGravity_Vert_Fill 1.对于非叶子栅格来说只能设置一个方向的停靠。具体只能设置左中右或者上中下 2.对于叶子栅格来说,如果设置了gravity 则填充的子视图必须要设置明确的尺寸。 */ -@property(nonatomic, assign) MyGravity gravity; +@property (nonatomic, assign) MyGravity gravity; /** 占位标志,只用叶子栅格,当设置为YES时则表示这个格子只用于占位,子视图不能填充到这个栅格中。 */ -@property(nonatomic, assign) BOOL placeholder; - +@property (nonatomic, assign) BOOL placeholder; /** 锚点标志,表示这个栅格虽然是非叶子栅格,也可以用来填充视图。如果将非叶子栅格的锚点标志设置为YES,那么这个栅格也可以用来填充子视图,一般用来当做背景视图使用。 */ -@property(nonatomic, assign) BOOL anchor; +@property (nonatomic, assign) BOOL anchor; /** 重叠视图的对齐停靠方式 - 对于叶子栅格来说,如果设置了gravity 则填充的子视图必须要设置明确的尺寸 + 根据栅格视图的规则默认是不存在重叠能力的,所谓重叠能力就是指视图显示时出现重叠和覆盖的现象。 + 这个属性是:anchor=YES,measure=0,gravity=overlap 三个属性的简化设置。也就是设置这个属性后这个格子对应的尺寸将是0, 因为是0同时又设置了子栅格的停靠方式,因此就相当于起到了重叠的效果。 */ -@property(nonatomic, assign) MyGravity overlap; - +@property (nonatomic, assign) MyGravity overlap; /**顶部边界线*/ -@property(nonatomic, strong) MyBorderline *topBorderline; +@property (nonatomic, strong) MyBorderline *topBorderline; /**头部边界线*/ -@property(nonatomic, strong) MyBorderline *leadingBorderline; +@property (nonatomic, strong) MyBorderline *leadingBorderline; /**底部边界线*/ -@property(nonatomic, strong) MyBorderline *bottomBorderline; +@property (nonatomic, strong) MyBorderline *bottomBorderline; /**尾部边界线*/ -@property(nonatomic, strong) MyBorderline *trailingBorderline; +@property (nonatomic, strong) MyBorderline *trailingBorderline; /**如果您不需要考虑国际化的问题则请用这个属性设置左边边界线,否则用leadingBorderline*/ -@property(nonatomic, strong) MyBorderline *leftBorderline; +@property (nonatomic, strong) MyBorderline *leftBorderline; /**如果您不需要考虑国际化的问题则请用这个属性设置右边边界线,否则用trailingBorderline*/ -@property(nonatomic, strong) MyBorderline *rightBorderline; - - +@property (nonatomic, strong) MyBorderline *rightBorderline; /** 添加行栅格,返回新的栅格。其中的measure可以设置如下的值: @@ -171,7 +154,7 @@ extern NSString * const vMyGridGravityHeightFill; //对应MyGravity_Vert_Fill 4.MyLayoutSize.wrap 表示尺寸由子栅格包裹 5.MyLayoutSize.fill 表示占用栅格剩余的尺寸 */ --(id)addRow:(CGFloat)measure; +- (id)addRow:(CGFloat)measure; /** 添加列栅格,返回新的栅格。其中的measure可以设置如下的值: @@ -181,26 +164,29 @@ extern NSString * const vMyGridGravityHeightFill; //对应MyGravity_Vert_Fill 4.MyLayoutSize.wrap 表示尺寸由子栅格包裹 5.MyLayoutSize.fill 表示占用栅格剩余的尺寸 */ --(id)addCol:(CGFloat)measure; +- (id)addCol:(CGFloat)measure; //添加栅格,返回被添加的栅格。这个方法和下面的cloneGrid配合使用可以用来构建那些需要重复添加栅格的场景。 --(id)addRowGrid:(id)grid; --(id)addColGrid:(id)grid; +- (id)addRowGrid:(id)grid; +- (id)addColGrid:(id)grid; --(id)addRowGrid:(id)grid measure:(CGFloat)measure; --(id)addColGrid:(id)grid measure:(CGFloat)measure; +//添加栅格,返回被添加的栅格。这个方法会重新设定grid中的measure属性值。 +- (id)addRowGrid:(id)grid measure:(CGFloat)measure; +- (id)addColGrid:(id)grid measure:(CGFloat)measure; +/** + 克隆出一个新栅格以及其下的所有子栅格。 + */ +- (id)cloneGrid; //从父栅格中删除。 --(void)removeFromSuperGrid; - +- (void)removeFromSuperGrid; //用字典的方式来构造栅格。 -@property(nonatomic, strong) NSDictionary *gridDictionary; +@property (nonatomic, strong) NSDictionary *gridDictionary; @end - @interface UIColor (MyGrid) /** @@ -216,8 +202,7 @@ extern NSString * const vMyGridGravityHeightFill; //对应MyGravity_Vert_Fill @return 来自字符串的UIColor对象,如果发生错误,则为nil */ -+ ( UIColor *)myColorWithHexString:(NSString *)hexString; - ++ (UIColor *)myColorWithHexString:(NSString *)hexString; /** RGB 颜色值 @@ -226,6 +211,3 @@ extern NSString * const vMyGridGravityHeightFill; //对应MyGravity_Vert_Fill - (NSString *)myHexString; @end - - - diff --git a/MyLayout/Lib/MyGridLayout.h b/MyLayout/Lib/MyGridLayout.h index 22a4734..5d77612 100644 --- a/MyLayout/Lib/MyGridLayout.h +++ b/MyLayout/Lib/MyGridLayout.h @@ -9,8 +9,6 @@ #import "MyBaseLayout.h" #import "MyGrid.h" - - /** 栅格布局。栅格布局是一种将一个矩形的视图区域按行或者按列的方式划分为多个子区域,子区域根据布局的要求可以继续递归划分。栅格布局里面的子视图将按照添加的顺序依次填充到对应的叶子区域中去的布局方式。 栅格布局通过一套自定义的布局体系来划分位置和尺寸,添加到栅格布局里面的子视图将不再需要指定位置和尺寸而是由栅格布局中的子栅格来完成,因此可以很很方便的调整布局结构,从而实现动态布局的能力。 @@ -18,8 +16,7 @@ 所谓栅格其实就是一个矩形区域,我们知道一个视图其实就是一个矩形区域,而子视图的frame属性其实就是父视图中的某个特定的子区域部分。既然子视图最终占据的是父视图的某个子矩形区域。那么我们也可以先将一个矩形区域按照某种规则分解为多个子矩形区域,然后再将子视图填充到对应的子矩形区域中去,这就是栅格布局的实现思想。 */ -@interface MyGridLayout : MyBaseLayout - +@interface MyGridLayout : MyBaseLayout /** 建立一个栅格模板,注意栅格模板不能添加到栅格布局中去,而应该采用clone的方法来将克隆的栅格添加到栅格布局中去 @@ -27,13 +24,11 @@ @param gridTag 栅格模板的标签 @return 返回一个模板栅格。 */ -+(id)createTemplateGrid:(NSInteger)gridTag; - ++ (id)createTemplateGrid:(NSInteger)gridTag; //删除布局下所有的子栅格 --(void)removeGrids; --(void)removeGridsIn:(MySizeClass)sizeClass; - +- (void)removeGrids; +- (void)removeGridsIn:(MySizeClass)sizeClass; /** 返回布局视图某个点所在的栅格 @@ -41,8 +36,7 @@ @param point 点坐标 @return 返回点所在的栅格。 */ --(id) gridHitTest:(CGPoint)point; - +- (id)gridHitTest:(CGPoint)point; /** 得到子视图所在的栅格 @@ -50,8 +44,7 @@ @param subview 子视图 @return 返回包含子视图的最顶部栅格。 */ --(id) gridContainsSubview:(UIView*)subview; - +- (id)gridContainsSubview:(UIView *)subview; /** 返回某个栅格包含的所有子视图 @@ -59,8 +52,7 @@ @param grid 栅格 @return 返回包含的子视图,如果没有则返回空数组 */ --(NSArray*) subviewsContainedInGrid:(id)grid; - +- (NSArray *)subviewsContainedInGrid:(id)grid; /**************************************************************************************************************************** *下面一系列方法用于处理数据和栅格都从远端下发并且用来进行动态构建的机制,我们称为视图组。一旦您开启了视图组的模式,那么用addSubview方法来构建的视图的顺序将无法得到保证。 @@ -68,7 +60,6 @@ * 视图组和栅格布局标签的绑定是可以复用的,这种复用机制有点类似于UITableview的视图复用一样,只不过在这里是通过replaceViewGroup方法来实现的。 ****************************************************************************************************************************/ - /** 将某个视图组绑定添加到某类栅格中去。并将这部分子视图对应到栅格的actionData上去。 @@ -76,8 +67,7 @@ @param actionData 如果视图组对应的栅格支持点击事件时可以获取到的动作数据,这个参数可以为nil @param gridTag 视图组对应的栅格的标签,也就是这个视图组会放入具有对应栅格标签的栅格里面。 */ --(void)addViewGroup:(NSArray *)viewGroup withActionData:(id)actionData to:(NSInteger)gridTag; - +- (void)addViewGroup:(NSArray *)viewGroup withActionData:(id)actionData to:(NSInteger)gridTag; /** 将某个视图组绑定添加到某类栅格中去。并将这部分子视图对应到栅格的actionData上去。 @@ -87,8 +77,7 @@ @param index 栅格标签内的视图组索引位置。 @param gridTag 视图组对应的栅格的标签,也就是这个视图组会放入具有对应栅格标签的栅格里面 */ --(void)insertViewGroup:(NSArray *)viewGroup withActionData:(id)actionData atIndex:(NSUInteger)index to:(NSInteger)gridTag; - +- (void)insertViewGroup:(NSArray *)viewGroup withActionData:(id)actionData atIndex:(NSUInteger)index to:(NSInteger)gridTag; /** 替换指定标签下指定位置的视图组。如果没有指定的标签或者索引位置则会实现和addViewGroup相同的功能, @@ -98,8 +87,7 @@ @param index 要替换的索引位置 @param gridTag 标签 */ --(void)replaceViewGroup:(NSArray *)viewGroup withActionData:(id)actionData atIndex:(NSUInteger)index to:(NSInteger)gridTag; - +- (void)replaceViewGroup:(NSArray *)viewGroup withActionData:(id)actionData atIndex:(NSUInteger)index to:(NSInteger)gridTag; /** 移动视图组从一个标签到另外一个标签中 @@ -108,8 +96,7 @@ @param origGridTag 视图组索引所在的标签 @param destGridTag 新视图组所在的标签 */ --(void)moveViewGroupAtIndex:(NSUInteger)index from:(NSInteger)origGridTag to:(NSInteger)destGridTag; - +- (void)moveViewGroupAtIndex:(NSUInteger)index from:(NSInteger)origGridTag to:(NSInteger)destGridTag; /** 移动视图组从一个标签到另外一个标签中 @@ -119,8 +106,7 @@ @param index2 要移动到的目标标签内的索引 @param destGridTag 要移动的目标标签 */ --(void)moveViewGroupAtIndex:(NSUInteger)index1 from:(NSInteger)origGridTag toIndex:(NSUInteger)index2 to:(NSInteger)destGridTag; - +- (void)moveViewGroupAtIndex:(NSUInteger)index1 from:(NSInteger)origGridTag toIndex:(NSUInteger)index2 to:(NSInteger)destGridTag; /** 删除某个gridTag标签下指定索引位置的视图组 @@ -128,18 +114,14 @@ @param index 视图组在gridTag标签下的位置索引 @param gridTag 视图组所在的gridTag */ --(void)removeViewGroupAtIndex:(NSUInteger)index from:(NSInteger)gridTag; - - +- (void)removeViewGroupAtIndex:(NSUInteger)index from:(NSInteger)gridTag; /** 删除某个gridTag标签下的所有视图组 @param gridTag gridTag标签 */ --(void)removeViewGroupFrom:(NSInteger)gridTag; - - +- (void)removeViewGroupFrom:(NSInteger)gridTag; /** 交换两个tag之间两个位置索引下的视图组 @@ -149,8 +131,7 @@ @param index2 tag标签2下的位置索引 @param gridTag2 tag标签2 */ --(void)exchangeViewGroupAtIndex:(NSUInteger)index1 from:(NSInteger)gridTag1 withViewGroupAtIndex:(NSUInteger)index2 from:(NSInteger)gridTag2; - +- (void)exchangeViewGroupAtIndex:(NSUInteger)index1 from:(NSInteger)gridTag1 withViewGroupAtIndex:(NSUInteger)index2 from:(NSInteger)gridTag2; /** 返回某个tag标签下的视图组的数量 @@ -158,8 +139,7 @@ @param gridTag tag标签 @return 返回某个tag标签下的视图组的数量 */ --(NSUInteger)viewGroupCountOf:(NSInteger)gridTag; - +- (NSUInteger)viewGroupCountOf:(NSInteger)gridTag; /** 返回某个tag标签中的某个索引位置下的视图组 @@ -168,12 +148,7 @@ @param gridTag tag标签 @return 返回对应索引下的视图组,如果没有则返回nil */ --(NSArray *)viewGroupAtIndex:(NSUInteger)index from:(NSInteger)gridTag; - - - - - +- (NSArray *)viewGroupAtIndex:(NSUInteger)index from:(NSInteger)gridTag; /** 栅格的字典描述。我们可以用addRow, addCol的方式来建立子栅格系统从而完成栅格的建立。我们也可以采用字典的方式来描述树形的栅格结构,并赋值给gridDictionary来实现栅格的建立和更新。因此您可以用这个属性来实现格式化语言描述的栅格的建立方法。下面是我们可以通过字典的方式来构造一个栅格系统的语法: @@ -202,14 +177,11 @@ 具体可以设置的key和value可以参考MyGrid.h头部定义的key和value的关键字。 */ -@property(nonatomic, strong) NSDictionary *gridDictionary; - - +@property (nonatomic, strong) NSDictionary *gridDictionary; /** 主要为那些动态下发事件来服务。我们知道从服务器下发的JSON或者字典是不可能包含要执行事件的target的。因此我们可以通过给布局视图设置一个预先设置好的target来作为事件的执行者,这样就可以实现当某个栅格里面设置了action时可以用这个属性来指定这个action消息的接收者。 */ -@property(nonatomic, weak) id gridActionTarget; - +@property (nonatomic, weak) id gridActionTarget; @end diff --git a/MyLayout/Lib/MyGridLayout.m b/MyLayout/Lib/MyGridLayout.m index fa76ded..eff6fed 100644 --- a/MyLayout/Lib/MyGridLayout.m +++ b/MyLayout/Lib/MyGridLayout.m @@ -7,970 +7,740 @@ // #import "MyGridLayout.h" -#import "MyLayoutInner.h" #import "MyGridNode.h" +#import "MyLayoutInner.h" - NSString * const kMyGridTag = @"tag"; - NSString * const kMyGridAction = @"action"; - NSString * const kMyGridActionData = @"action-data"; - NSString * const kMyGridRows = @"rows"; - NSString * const kMyGridCols = @"cols"; - NSString * const kMyGridSize = @"size"; - NSString * const kMyGridPadding = @"padding"; - NSString * const kMyGridSpace = @"space"; - NSString * const kMyGridGravity = @"gravity"; - NSString * const kMyGridPlaceholder = @"placeholder"; - NSString * const kMyGridAnchor = @"anchor"; - NSString * const kMyGridOverlap = @"overlap"; - NSString * const kMyGridTopBorderline = @"top-borderline"; - NSString * const kMyGridBottomBorderline = @"bottom-borderline"; - NSString * const kMyGridLeftBorderline = @"left-borderline"; - NSString * const kMyGridRightBorderline = @"right-borderline"; - - NSString * const kMyGridBorderlineColor = @"color"; - NSString * const kMyGridBorderlineThick = @"thick"; - NSString * const kMyGridBorderlineHeadIndent = @"head"; - NSString * const kMyGridBorderlineTailIndent = @"tail"; - NSString * const kMyGridBorderlineOffset = @"offset"; - NSString * const kMyGridBorderlineDash = @"dash"; - - - NSString * const vMyGridSizeWrap = @"wrap"; - NSString * const vMyGridSizeFill = @"fill"; - - - NSString * const vMyGridGravityTop = @"top"; - NSString * const vMyGridGravityBottom = @"bottom"; - NSString * const vMyGridGravityLeft = @"left"; - NSString * const vMyGridGravityRight = @"right"; - NSString * const vMyGridGravityLeading = @"leading"; - NSString * const vMyGridGravityTrailing = @"trailing"; - NSString * const vMyGridGravityCenterX = @"centerX"; - NSString * const vMyGridGravityCenterY = @"centerY"; - NSString * const vMyGridGravityWidthFill = @"width"; - NSString * const vMyGridGravityHeightFill = @"height"; - +NSString *const kMyGridTag = @"tag"; +NSString *const kMyGridAction = @"action"; +NSString *const kMyGridActionData = @"action-data"; +NSString *const kMyGridRows = @"rows"; +NSString *const kMyGridCols = @"cols"; +NSString *const kMyGridSize = @"size"; +NSString *const kMyGridPadding = @"padding"; +NSString *const kMyGridSpace = @"space"; +NSString *const kMyGridGravity = @"gravity"; +NSString *const kMyGridPlaceholder = @"placeholder"; +NSString *const kMyGridAnchor = @"anchor"; +NSString *const kMyGridOverlap = @"overlap"; +NSString *const kMyGridTopBorderline = @"top-borderline"; +NSString *const kMyGridBottomBorderline = @"bottom-borderline"; +NSString *const kMyGridLeftBorderline = @"left-borderline"; +NSString *const kMyGridRightBorderline = @"right-borderline"; + +NSString *const kMyGridBorderlineColor = @"color"; +NSString *const kMyGridBorderlineThick = @"thick"; +NSString *const kMyGridBorderlineHeadIndent = @"head"; +NSString *const kMyGridBorderlineTailIndent = @"tail"; +NSString *const kMyGridBorderlineOffset = @"offset"; +NSString *const kMyGridBorderlineDash = @"dash"; + +NSString *const vMyGridSizeWrap = @"wrap"; +NSString *const vMyGridSizeFill = @"fill"; + +NSString *const vMyGridGravityTop = @"top"; +NSString *const vMyGridGravityBottom = @"bottom"; +NSString *const vMyGridGravityLeft = @"left"; +NSString *const vMyGridGravityRight = @"right"; +NSString *const vMyGridGravityLeading = @"leading"; +NSString *const vMyGridGravityTrailing = @"trailing"; +NSString *const vMyGridGravityCenterX = @"centerX"; +NSString *const vMyGridGravityCenterY = @"centerY"; +NSString *const vMyGridGravityWidthFill = @"width"; +NSString *const vMyGridGravityHeightFill = @"height"; //视图组和动作数据 @interface MyViewGroupAndActionData : NSObject -@property(nonatomic, strong) NSMutableArray *viewGroup; -@property(nonatomic, strong) id actionData; +//视图组,这里除了可以添加UIView外还可以添加占位类型NSNull +@property (nonatomic, strong) NSMutableArray *viewGroup; +@property (nonatomic, strong) id actionData; -+(instancetype)viewGroup:(NSArray*)viewGroup actionData:(id)actionData; ++ (instancetype)viewGroup:(NSArray *)viewGroup actionData:(id)actionData; @end @implementation MyViewGroupAndActionData --(instancetype)initWithViewGroup:(NSArray*)viewGroup actionData:(id)actionData -{ +- (instancetype)initWithViewGroup:(NSArray *)viewGroup actionData:(id)actionData { self = [self init]; - if (self != nil) - { + if (self != nil) { _viewGroup = [NSMutableArray arrayWithArray:viewGroup]; _actionData = actionData; } - return self; } -+(instancetype)viewGroup:(NSArray*)viewGroup actionData:(id)actionData -{ ++ (instancetype)viewGroup:(NSArray *)viewGroup actionData:(id)actionData { return [[[self class] alloc] initWithViewGroup:viewGroup actionData:actionData]; } - @end +@interface MyGridLayout () - - - -@interface MyGridLayout() - -@property(nonatomic, weak) MyGridLayoutViewSizeClass *lastSizeClass; - -@property(nonatomic, strong) NSMutableDictionary *tagsDict; -@property(nonatomic, assign) BOOL tagsDictLock; +@property (nonatomic, weak) MyGridLayoutTraits *lastSizeClass; +//保存某个标签下对应的 视图组和数据 数组。字典。一个标签下可以有N个视图组。 +@property (nonatomic, strong) NSMutableDictionary *> *tagsDict; +//这个锁并不是用于同步处理,而是用来区分是外部主动调用删除子视图还是类内部删除子视图。因为二者都会触发willRemoveSubview方法的调用,这个标志 +//用来表明是否是内部触发的删除子视图的标志。 +@property (nonatomic, assign) BOOL tagsDictLock; @end - @implementation MyGridLayout --(NSMutableDictionary*)tagsDict -{ - if (_tagsDict == nil) - { +- (NSMutableDictionary *)tagsDict { + if (_tagsDict == nil) { _tagsDict = [NSMutableDictionary new]; } - return _tagsDict; } -#pragma mark -- Public Method +#pragma mark-- Public Methods -+(id)createTemplateGrid:(NSInteger)gridTag -{ - id grid = [[MyGridNode alloc] initWithMeasure:0 superGrid:nil]; ++ (id)createTemplateGrid:(NSInteger)gridTag { + id grid = [[MyGridNode alloc] initWithMeasure:0 superGrid:nil]; grid.tag = gridTag; - return grid; } - //删除所有子栅格 --(void)removeGrids -{ +- (void)removeGrids { [self removeGridsIn:MySizeClass_hAny | MySizeClass_wAny]; } --(void)removeGridsIn:(MySizeClass)sizeClass -{ - id lsc = (id)[self fetchLayoutSizeClass:sizeClass]; - [lsc.subGrids removeAllObjects]; - lsc.subGridsType = MySubGridsType_Unknown; - +- (void)removeGridsIn:(MySizeClass)sizeClass { + id gridNode = (id)[self fetchLayoutSizeClass:sizeClass]; + [gridNode.subGrids removeAllObjects]; + gridNode.subGridsType = MySubGridsType_Unknown; [self setNeedsLayout]; } --(id) gridContainsSubview:(UIView*)subview -{ +- (id)gridContainsSubview:(UIView *)subview { return [self gridHitTest:subview.center]; } --(NSArray*) subviewsContainedInGrid:(id)grid -{ - +- (NSArray *)subviewsContainedInGrid:(id)grid { id gridNode = (id)grid; - + #ifdef DEBUG NSAssert([gridNode gridLayoutView] == self, @"oops! 非栅格布局中的栅格"); #endif - + NSMutableArray *retSbs = [NSMutableArray new]; - NSArray *sbs = [self myGetLayoutSubviews]; - for (UIView *sbv in sbs) - { - if (CGRectContainsRect(gridNode.gridRect, sbv.frame)) - { - [retSbs addObject:sbv]; + for (UIView *subview in self.subviews) { + if (!subview.isHidden && CGRectContainsRect(gridNode.gridRect, subview.frame)) { + [retSbs addObject:subview]; } } - return retSbs; } - --(void)addViewGroup:(NSArray *)viewGroup withActionData:(id)actionData to:(NSInteger)gridTag -{ +- (void)addViewGroup:(NSArray *)viewGroup withActionData:(id)actionData to:(NSInteger)gridTag { [self insertViewGroup:viewGroup withActionData:actionData atIndex:(NSUInteger)-1 to:gridTag]; } --(void)insertViewGroup:(NSArray *)viewGroup withActionData:(id)actionData atIndex:(NSUInteger)index to:(NSInteger)gridTag -{ - if (gridTag == 0) - { - for (UIView *sbv in viewGroup) - { - if (sbv != (UIView*)[NSNull null]) - [self addSubview:sbv]; +- (void)insertViewGroup:(NSArray *)viewGroup withActionData:(id)actionData atIndex:(NSUInteger)index to:(NSInteger)gridTag { + if (gridTag != 0) { + NSNumber *key = @(gridTag); + NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; + if (viewGroupArray == nil) { + viewGroupArray = [NSMutableArray new]; + [self.tagsDict setObject:viewGroupArray forKey:key]; } - return; - } - - //... - NSNumber *key = @(gridTag); - NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; - if (viewGroupArray == nil) - { - viewGroupArray = [NSMutableArray new]; - [self.tagsDict setObject:viewGroupArray forKey:key]; - } - - MyViewGroupAndActionData *va = [MyViewGroupAndActionData viewGroup:viewGroup actionData:actionData]; - if (index == (NSUInteger)-1) - { - [viewGroupArray addObject:va]; - } - else - { - [viewGroupArray insertObject:va atIndex:index]; + MyViewGroupAndActionData *va = [MyViewGroupAndActionData viewGroup:viewGroup actionData:actionData]; + if (index == (NSUInteger)-1) { + [viewGroupArray addObject:va]; + } else { + [viewGroupArray insertObject:va atIndex:index]; + } } - for (UIView *sbv in viewGroup) - { - if (sbv != (UIView*)[NSNull null]) + for (UIView *sbv in viewGroup) { + if (sbv != (UIView *)[NSNull null]) { [self addSubview:sbv]; + } } [self setNeedsLayout]; } --(void)replaceViewGroup:(NSArray *)viewGroup withActionData:(id)actionData atIndex:(NSUInteger)index to:(NSInteger)gridTag -{ - if (gridTag == 0) - { +- (void)replaceViewGroup:(NSArray *)viewGroup withActionData:(id)actionData atIndex:(NSUInteger)index to:(NSInteger)gridTag { + if (gridTag == 0) { return; } - //... NSNumber *key = @(gridTag); - NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; - if (viewGroupArray == nil || (index >= viewGroupArray.count)) - { + NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; + if (viewGroupArray == nil || (index >= viewGroupArray.count)) { [self addViewGroup:viewGroup withActionData:actionData to:gridTag]; return; } - - + //这里面有可能有存在的视图, 有可能存在于子视图数组里面,有可能存在于其他视图组里面。 //如果存在于其他标签则要从其他标签删除。。。 //而且多余的还要删除。。。这个好复杂啊。。 //先不考虑这么复杂的情况,只认为替换掉当前索引的视图即可,如果视图本来就在子视图里面则不删除,否则就添加。而被替换掉的则需要删除。 //每个视图都在老的里面查找,如果找到则处理,如果没有找到 self.tagsDictLock = YES; - + MyViewGroupAndActionData *va = viewGroupArray[index]; va.actionData = actionData; - - if (va.viewGroup != viewGroup) - { - for (UIView *sbv in viewGroup) - { + + if (va.viewGroup != viewGroup) { + for (UIView *sbv in viewGroup) { NSUInteger oldIndex = [va.viewGroup indexOfObject:sbv]; - if (oldIndex == NSNotFound) - { - if (sbv != (UIView*)[NSNull null]) + if (oldIndex == NSNotFound) { + if (sbv != (UIView *)[NSNull null]) { [self addSubview:sbv]; - } - else - { + } + } else { [va.viewGroup removeObjectAtIndex:oldIndex]; } } - + //原来多余的视图删除 - for (UIView *sbv in va.viewGroup) - { - if (sbv != (UIView*)[NSNull null]) + for (UIView *sbv in va.viewGroup) { + if (sbv != (UIView *)[NSNull null]) { [sbv removeFromSuperview]; + } } - //将新的视图组给替换掉。 [va.viewGroup setArray:viewGroup]; } - self.tagsDictLock = NO; - - [self setNeedsLayout]; - } - --(void)moveViewGroupAtIndex:(NSUInteger)index from:(NSInteger)origGridTag to:(NSInteger)destGridTag -{ +- (void)moveViewGroupAtIndex:(NSUInteger)index from:(NSInteger)origGridTag to:(NSInteger)destGridTag { [self moveViewGroupAtIndex:index from:origGridTag toIndex:-1 to:destGridTag]; } --(void)moveViewGroupAtIndex:(NSUInteger)index1 from:(NSInteger)origGridTag toIndex:(NSUInteger)index2 to:(NSInteger)destGridTag -{ - if (origGridTag == 0 || destGridTag == 0 || (origGridTag == destGridTag)) +- (void)moveViewGroupAtIndex:(NSUInteger)index1 from:(NSInteger)origGridTag toIndex:(NSUInteger)index2 to:(NSInteger)destGridTag { + if (origGridTag == 0 || destGridTag == 0 || (origGridTag == destGridTag)) { return; - - if (_tagsDict == nil) + } + if (_tagsDict == nil) { return; - + } NSNumber *origKey = @(origGridTag); - NSMutableArray *origViewGroupArray = [self.tagsDict objectForKey:origKey]; - - if (index1 < origViewGroupArray.count) - { - + NSMutableArray *origViewGroupArray = [self.tagsDict objectForKey:origKey]; + + if (index1 < origViewGroupArray.count) { NSNumber *destKey = @(destGridTag); - - NSMutableArray *destViewGroupArray = [self.tagsDict objectForKey:destKey]; - if (destViewGroupArray == nil) - { + NSMutableArray *destViewGroupArray = [self.tagsDict objectForKey:destKey]; + if (destViewGroupArray == nil) { destViewGroupArray = [NSMutableArray new]; [self.tagsDict setObject:destViewGroupArray forKey:destKey]; } - - if (index2 > destViewGroupArray.count) + + if (index2 > destViewGroupArray.count) { index2 = destViewGroupArray.count; - - + } MyViewGroupAndActionData *va = origViewGroupArray[index1]; [origViewGroupArray removeObjectAtIndex:index1]; - if (origViewGroupArray.count == 0) - { + if (origViewGroupArray.count == 0) { [self.tagsDict removeObjectForKey:origKey]; } - [destViewGroupArray insertObject:va atIndex:index2]; - - } - [self setNeedsLayout]; - } - - --(void)removeViewGroupAtIndex:(NSUInteger)index from:(NSInteger)gridTag -{ - if (gridTag == 0) +- (void)removeViewGroupAtIndex:(NSUInteger)index from:(NSInteger)gridTag { + if (gridTag == 0) { return; - - if (_tagsDict == nil) + } + if (_tagsDict == nil) { return; - + } NSNumber *key = @(gridTag); - NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; - if (index < viewGroupArray.count) - { + NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; + if (index < viewGroupArray.count) { MyViewGroupAndActionData *va = viewGroupArray[index]; - + self.tagsDictLock = YES; - for (UIView *sbv in va.viewGroup) - { - if (sbv != (UIView*)[NSNull null]) + for (UIView *sbv in va.viewGroup) { + if (sbv != (UIView *)[NSNull null]) { [sbv removeFromSuperview]; + } } self.tagsDictLock = NO; - - [viewGroupArray removeObjectAtIndex:index]; - - if (viewGroupArray.count == 0) - { + + if (viewGroupArray.count == 0) { [self.tagsDict removeObjectForKey:key]; } - } - [self setNeedsLayout]; } - - --(void)removeViewGroupFrom:(NSInteger)gridTag -{ - if (gridTag == 0) +- (void)removeViewGroupFrom:(NSInteger)gridTag { + if (gridTag == 0) { return; - - if (_tagsDict == nil) + } + if (_tagsDict == nil) { return; - + } NSNumber *key = @(gridTag); - NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; - if (viewGroupArray != nil) - { + NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; + if (viewGroupArray != nil) { self.tagsDictLock = YES; - for (MyViewGroupAndActionData * va in viewGroupArray) - { - for (UIView *sbv in va.viewGroup) - { - if (sbv != (UIView*)[NSNull null]) + for (MyViewGroupAndActionData *va in viewGroupArray) { + for (UIView *sbv in va.viewGroup) { + if (sbv != (UIView *)[NSNull null]) { [sbv removeFromSuperview]; + } } } - self.tagsDictLock = NO; - [self.tagsDict removeObjectForKey:key]; } - [self setNeedsLayout]; - } - - --(void)exchangeViewGroupAtIndex:(NSUInteger)index1 from:(NSInteger)gridTag1 withViewGroupAtIndex:(NSUInteger)index2 from:(NSInteger)gridTag2 -{ - if (gridTag1 == 0 || gridTag2 == 0) +- (void)exchangeViewGroupAtIndex:(NSUInteger)index1 from:(NSInteger)gridTag1 withViewGroupAtIndex:(NSUInteger)index2 from:(NSInteger)gridTag2 { + if (gridTag1 == 0 || gridTag2 == 0) { return; - + } NSNumber *key1 = @(gridTag1); - NSMutableArray *viewGroupArray1 = [self.tagsDict objectForKey:key1]; - - NSMutableArray *viewGroupArray2 = nil; - - if (gridTag1 == gridTag2) - { + NSMutableArray *viewGroupArray1 = [self.tagsDict objectForKey:key1]; + + NSMutableArray *viewGroupArray2 = nil; + + if (gridTag1 == gridTag2) { viewGroupArray2 = viewGroupArray1; - if (index1 == index2) + if (index1 == index2) { return; - } - else - { + } + } else { NSNumber *key2 = @(gridTag2); viewGroupArray2 = [self.tagsDict objectForKey:key2]; } - - if (index1 < viewGroupArray1.count && index2 < viewGroupArray2.count) - { + + if (index1 < viewGroupArray1.count && index2 < viewGroupArray2.count) { self.tagsDictLock = YES; - - if (gridTag1 == gridTag2) - { + if (gridTag1 == gridTag2) { [viewGroupArray1 exchangeObjectAtIndex:index1 withObjectAtIndex:index2]; - } - else - { + } else { MyViewGroupAndActionData *va1 = viewGroupArray1[index1]; MyViewGroupAndActionData *va2 = viewGroupArray2[index2]; - + [viewGroupArray1 removeObjectAtIndex:index1]; [viewGroupArray2 removeObjectAtIndex:index2]; - + [viewGroupArray1 insertObject:va2 atIndex:index1]; [viewGroupArray2 insertObject:va1 atIndex:index2]; } - - self.tagsDictLock = NO; - - } - [self setNeedsLayout]; - } - --(NSUInteger)viewGroupCountOf:(NSInteger)gridTag -{ - if (gridTag == 0) +- (NSUInteger)viewGroupCountOf:(NSInteger)gridTag { + if (gridTag == 0) { return 0; - - if (_tagsDict == nil) + } + if (_tagsDict == nil) { return 0; - + } NSNumber *key = @(gridTag); - NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; + NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; return viewGroupArray.count; } - - --(NSArray *)viewGroupAtIndex:(NSUInteger)index from:(NSInteger)gridTag -{ - if (gridTag == 0) +- (NSArray *)viewGroupAtIndex:(NSUInteger)index from:(NSInteger)gridTag { + if (gridTag == 0) { return nil; - - if (_tagsDict == nil) + } + if (_tagsDict == nil) { return nil; - - + } NSNumber *key = @(gridTag); - NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; - if (index < viewGroupArray.count) - { + NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; + if (index < viewGroupArray.count) { return viewGroupArray[index].viewGroup; } - return nil; } +#pragma mark-- MyGrid - - - - - - -#pragma mark -- MyGrid - --(id)actionData -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - return lsc.actionData; +- (id)actionData { + return self.myDefaultSizeClass.actionData; } --(void)setActionData:(id)actionData -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)setActionData:(id)actionData { + MyGridLayout *lsc = self.myDefaultSizeClass; lsc.actionData = actionData; } //添加行。返回新的栅格。 --(id)addRow:(CGFloat)measure -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (id)addRow:(CGFloat)measure { + MyGridLayout *lsc = self.myDefaultSizeClass; id node = (id)[lsc addRow:measure]; node.superGrid = self; return node; } //添加列。返回新的栅格。 --(id)addCol:(CGFloat)measure -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (id)addCol:(CGFloat)measure { + MyGridLayout *lsc = self.myDefaultSizeClass; id node = (id)[lsc addCol:measure]; node.superGrid = self; return node; } --(id)addRowGrid:(id)grid -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (id)addRowGrid:(id)grid { + MyGridLayout *lsc = self.myDefaultSizeClass; id node = (id)[lsc addRowGrid:grid]; node.superGrid = self; return node; } --(id)addColGrid:(id)grid -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (id)addColGrid:(id)grid { + MyGridLayout *lsc = self.myDefaultSizeClass; id node = (id)[lsc addColGrid:grid]; node.superGrid = self; return node; } --(id)addRowGrid:(id)grid measure:(CGFloat)measure -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (id)addRowGrid:(id)grid measure:(CGFloat)measure { + MyGridLayout *lsc = self.myDefaultSizeClass; id node = (id)[lsc addRowGrid:grid measure:measure]; node.superGrid = self; return node; - } --(id)addColGrid:(id)grid measure:(CGFloat)measure -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (id)addColGrid:(id)grid measure:(CGFloat)measure { + MyGridLayout *lsc = self.myDefaultSizeClass; id node = (id)[lsc addColGrid:grid measure:measure]; node.superGrid = self; return node; - } - - --(id)cloneGrid -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - return [lsc cloneGrid]; +- (id)cloneGrid { + return [self.myDefaultSizeClass cloneGrid]; } --(void)removeFromSuperGrid -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - return [lsc removeFromSuperGrid]; - +- (void)removeFromSuperGrid { + return [self.myDefaultSizeClass removeFromSuperGrid]; } --(id)superGrid -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - - return lsc.superGrid; +- (id)superGrid { + return self.myDefaultSizeClass.superGrid; } --(void)setSuperGrid:(id)superGrid -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)setSuperGrid:(id)superGrid { + MyGridLayout *lsc = self.myDefaultSizeClass; lsc.superGrid = superGrid; } --(BOOL)placeholder -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - - return lsc.placeholder; +- (BOOL)placeholder { + return self.myDefaultSizeClass.placeholder; } --(void)setPlaceholder:(BOOL)placeholder -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)setPlaceholder:(BOOL)placeholder { + MyGridLayout *lsc = self.myDefaultSizeClass; lsc.placeholder = placeholder; } - --(BOOL)anchor -{ - - MyGridLayout *lsc = self.myCurrentSizeClass; - - return lsc.anchor; +- (BOOL)anchor { + return self.myDefaultSizeClass.anchor; } --(void)setAnchor:(BOOL)anchor -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)setAnchor:(BOOL)anchor { + MyGridLayout *lsc = self.myDefaultSizeClass; lsc.anchor = anchor; } --(MyGravity)overlap -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - - return lsc.overlap; +- (MyGravity)overlap { + return self.myDefaultSizeClass.overlap; } --(void)setOverlap:(MyGravity)overlap -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)setOverlap:(MyGravity)overlap { + MyGridLayout *lsc = self.myDefaultSizeClass; lsc.overlap = overlap; } --(NSDictionary*)gridDictionary -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - return lsc.gridDictionary; +- (NSDictionary *)gridDictionary { + return self.myDefaultSizeClass.gridDictionary; } - --(void)setGridDictionary:(NSDictionary *)gridDictionary -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)setGridDictionary:(NSDictionary *)gridDictionary { + MyGridLayout *lsc = self.myDefaultSizeClass; lsc.gridDictionary = gridDictionary; } +#pragma mark-- MyGridNode -#pragma mark -- MyGridNode - - --(NSMutableArray> *)subGrids -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - return (NSMutableArray> *)(lsc.subGrids); +- (NSMutableArray> *)subGrids { + return (NSMutableArray> *)(self.myDefaultSizeClass.subGrids); } --(void)setSubGrids:(NSMutableArray> *)subGrids -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)setSubGrids:(NSMutableArray> *)subGrids { + MyGridLayout *lsc = self.myDefaultSizeClass; lsc.subGrids = subGrids; } --(MySubGridsType)subGridsType -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - - return lsc.subGridsType; +- (MySubGridsType)subGridsType { + return self.myDefaultSizeClass.subGridsType; } --(void)setSubGridsType:(MySubGridsType)subGridsType -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)setSubGridsType:(MySubGridsType)subGridsType { + MyGridLayout *lsc = self.myDefaultSizeClass; lsc.subGridsType = subGridsType; } - --(CGFloat)measure -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - return lsc.measure; +- (CGFloat)measure { + return self.myDefaultSizeClass.measure; } --(void)setMeasure:(CGFloat)measure -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)setMeasure:(CGFloat)measure { + MyGridLayout *lsc = self.myDefaultSizeClass; lsc.measure = measure; } --(CGRect)gridRect -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - return lsc.gridRect; +- (CGRect)gridRect { + return self.myDefaultSizeClass.gridRect; } --(void)setGridRect:(CGRect)gridRect -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)setGridRect:(CGRect)gridRect { + MyGridLayout *lsc = self.myDefaultSizeClass; lsc.gridRect = gridRect; } //更新格子尺寸。 --(CGFloat)updateGridSize:(CGSize)superSize superGrid:(id)superGrid withMeasure:(CGFloat)measure -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - return [lsc updateGridSize:superSize superGrid:superGrid withMeasure:measure]; +- (CGFloat)updateGridSize:(CGSize)superSize superGrid:(id)superGrid withMeasure:(CGFloat)measure { + return [self.myDefaultSizeClass updateGridSize:superSize superGrid:superGrid withMeasure:measure]; } --(CGFloat)updateGridOrigin:(CGPoint)superOrigin superGrid:(id)superGrid withOffset:(CGFloat)offset -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - - return [lsc updateGridOrigin:superOrigin superGrid:superGrid withOffset:offset]; +- (CGFloat)updateWrapGridSizeInSuperGrid:(id)superGrid withMeasure:(CGFloat)measure { + return [self.myDefaultSizeClass updateWrapGridSizeInSuperGrid:superGrid withMeasure:measure]; } +- (CGFloat)updateGridOrigin:(CGPoint)superOrigin superGrid:(id)superGrid withOffset:(CGFloat)offset { + return [self.myDefaultSizeClass updateGridOrigin:superOrigin superGrid:superGrid withOffset:offset]; +} --(UIView*)gridLayoutView -{ +- (UIView *)gridLayoutView { return self; } --(SEL)gridAction -{ +- (SEL)gridAction { return nil; } --(void)setBorderlineNeedLayoutIn:(CGRect)rect withLayer:(CALayer *)layer -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)setBorderlineNeedLayoutIn:(CGRect)rect withLayer:(CALayer *)layer { + MyGridLayout *lsc = self.myDefaultSizeClass; [lsc setBorderlineNeedLayoutIn:rect withLayer:layer]; - } --(void)showBorderline:(BOOL)show -{ - MyGridLayout *lsc = self.myCurrentSizeClass; +- (void)showBorderline:(BOOL)show { + MyGridLayout *lsc = self.myDefaultSizeClass; [lsc showBorderline:show]; - } --(id)gridHitTest:(CGPoint)point -{ - MyGridLayout *lsc = self.myCurrentSizeClass; - return [lsc gridHitTest:point]; +- (id)gridHitTest:(CGPoint)point { + return [self.myDefaultSizeClass gridHitTest:point]; } +#pragma mark-- Touches Event -#pragma mark -- Touches Event - --(id)myBestHitGrid:(NSSet *)touches -{ +- (id)myBestHitGrid:(NSSet *)touches { MySizeClass sizeClass = [self myGetGlobalSizeClass]; - id bestSC = (id)[self myBestSizeClass:sizeClass]; - + id bestSC = (id)[self.myEngine fetchView:self bestLayoutSizeClass:sizeClass]; + UITouch *touch = [touches anyObject]; CGPoint point = [touch locationInView:self]; - return [bestSC gridHitTest:point]; + return [bestSC gridHitTest:point]; } -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -{ - +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [[self myBestHitGrid:touches] touchesBegan:touches withEvent:event]; [super touchesBegan:touches withEvent:event]; - } -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event -{ +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { [[self myBestHitGrid:touches] touchesMoved:touches withEvent:event]; [super touchesMoved:touches withEvent:event]; } - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event -{ +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [[self myBestHitGrid:touches] touchesEnded:touches withEvent:event]; [super touchesEnded:touches withEvent:event]; } -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event -{ +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [[self myBestHitGrid:touches] touchesCancelled:touches withEvent:event]; [super touchesCancelled:touches withEvent:event]; } +#pragma mark-- Override Methods - -#pragma mark -- Override Method - --(void)dealloc -{ +- (void)dealloc { //这里提前释放所有的数据,防止willRemoveSubview中重复删除。。 _tagsDict = nil; } --(void)removeAllSubviews -{ - _tagsDict = nil; //提前释放所有绑定的数据 +- (void)removeAllSubviews { + _tagsDict = nil; //提前释放所有绑定的数据 [super removeAllSubviews]; } --(void)willRemoveSubview:(UIView *)subview -{ +- (void)willRemoveSubview:(UIView *)subview { [super willRemoveSubview:subview]; - + //如果子试图在样式里面则从样式里面删除 - if (_tagsDict != nil && !self.tagsDictLock) - { - [_tagsDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL * stop) { - - NSMutableArray *viewGroupArray = (NSMutableArray*)obj; + if (_tagsDict != nil && !self.tagsDictLock) { + [_tagsDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + NSMutableArray *viewGroupArray = (NSMutableArray *)obj; NSInteger sbsCount = viewGroupArray.count; - for (NSInteger j = 0; j < sbsCount; j++) - { + for (NSInteger j = 0; j < sbsCount; j++) { MyViewGroupAndActionData *va = viewGroupArray[j]; NSInteger sbvCount = va.viewGroup.count; - for (NSInteger i = 0; i < sbvCount; i++) - { - if (va.viewGroup[i] == subview) - { + for (NSInteger i = 0; i < sbvCount; i++) { + if (va.viewGroup[i] == subview) { [va.viewGroup removeObjectAtIndex:i]; break; *stop = YES; } } - - if (va.viewGroup.count == 0) - { + + if (va.viewGroup.count == 0) { [viewGroupArray removeObjectAtIndex:j]; break; } - - if (*stop) + + if (*stop) { break; + } } - - }]; } } - --(CGSize)calcLayoutRect:(CGSize)size isEstimate:(BOOL)isEstimate pHasSubLayout:(BOOL*)pHasSubLayout sizeClass:(MySizeClass)sizeClass sbs:(NSMutableArray*)sbs -{ - CGSize selfSize = [super calcLayoutRect:size isEstimate:isEstimate pHasSubLayout:pHasSubLayout sizeClass:sizeClass sbs:sbs]; - - if (sbs == nil) - sbs = [self myGetLayoutSubviews]; - +- (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { + CGSize selfSize = [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; - MyFrame *myFrame = self.myFrame; - - MyGridLayout *lsc = [self myCurrentSizeClassFrom:myFrame]; + MyGridLayoutTraits *layoutTraits = (MyGridLayoutTraits *)context->layoutViewEngine.currentSizeClass; + context->paddingTop = layoutTraits.myLayoutPaddingTop; + context->paddingBottom = layoutTraits.myLayoutPaddingBottom; + context->paddingLeading = layoutTraits.myLayoutPaddingLeading; + context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + context->vertGravity = MYVERTGRAVITY(layoutTraits.gravity); + context->horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.gravity)]; + context->vertSpace = layoutTraits.subviewVSpace; + context->horzSpace = layoutTraits.subviewHSpace; + if (context->subviewEngines == nil) { + context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; + } + //只有在非评估,并且当sizeclass的数量大于1个,并且当前的sizeclass和lastSizeClass不一致的时候 - if (!isEstimate && myFrame.multiple) - { + if (!context->isEstimate && context->layoutViewEngine.multiple) { //将子栅格中的layer隐藏。 - if (self.lastSizeClass != nil && ((MyGridLayoutViewSizeClass*)lsc) != self.lastSizeClass) + if (self.lastSizeClass != nil && ((MyGridLayoutTraits *)layoutTraits) != self.lastSizeClass) { [((id)self.lastSizeClass) showBorderline:NO]; - - self.lastSizeClass = (MyGridLayoutViewSizeClass*)lsc; + } + self.lastSizeClass = (MyGridLayoutTraits *)layoutTraits; } - - + //设置根格子的rect为布局视图的大小。 - lsc.gridRect = CGRectMake(0, 0, selfSize.width, selfSize.height); - - + layoutTraits.gridRect = CGRectMake(0, 0, selfSize.width, selfSize.height); + NSMutableDictionary *tagKeyIndexDict = [NSMutableDictionary dictionaryWithCapacity:self.tagsDict.count]; - for (NSNumber *key in self.tagsDict) - { + for (NSNumber *key in self.tagsDict) { [tagKeyIndexDict setObject:@(0) forKey:key]; } + //构造出子视图列表。 + NSMutableArray *subviews = [NSMutableArray arrayWithCapacity:context->subviewEngines.count]; + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + [subviews addObject:subviewEngine.currentSizeClass.view]; + } + //遍历尺寸 NSInteger index = 0; - CGFloat selfMeasure = [self myTraversalGridSize:lsc gridSize:selfSize lsc:lsc sbs:sbs pIndex:&index tagViewGroupIndexDict:tagKeyIndexDict tagViewGroup:nil pTagIndex:nil]; - if (lsc.wrapContentHeight) - { - selfSize.height = selfMeasure; + CGFloat selfMeasure = [self myTraversalGrid:layoutTraits gridSize:selfSize lsc:layoutTraits sbs:subviews pIndex:&index tagViewGroupIndexDict:tagKeyIndexDict tagViewGroup:nil pTagIndex:nil withContext:context]; + if (layoutTraits.heightSizeInner.wrapVal) { + selfSize.height = selfMeasure; } - - if (lsc.wrapContentWidth) - { + if (layoutTraits.widthSizeInner.wrapVal) { selfSize.width = selfMeasure; } - //遍历位置。 - for (NSNumber *key in self.tagsDict) - { + for (NSNumber *key in self.tagsDict) { [tagKeyIndexDict setObject:@(0) forKey:key]; } - - NSEnumerator *enumerator = sbs.objectEnumerator; - [self myTraversalGridOrigin:lsc gridOrigin:CGPointMake(0, 0) lsc:lsc sbvEnumerator:enumerator tagViewGroupIndexDict:tagKeyIndexDict tagSbvEnumerator:nil isEstimate:isEstimate sizeClass:sizeClass pHasSubLayout:pHasSubLayout]; - - + + NSEnumerator *enumerator = subviews.objectEnumerator; + [self myTraversalGrid:layoutTraits gridOrigin:CGPointMake(0, 0) layoutTraits:layoutTraits sbvEnumerator:enumerator tagViewGroupIndexDict:tagKeyIndexDict tagSbvEnumerator:nil withContext:context]; + //遍历那些还剩余的然后设置为0. - [tagKeyIndexDict enumerateKeysAndObjectsUsingBlock:^(id key, NSNumber *viewGroupIndexNumber, BOOL * stop) { - - NSArray *viewGroupArray = self.tagsDict[key]; + [tagKeyIndexDict enumerateKeysAndObjectsUsingBlock:^(id key, NSNumber *viewGroupIndexNumber, BOOL *stop) { + NSArray *viewGroupArray = self.tagsDict[key]; NSInteger viewGroupIndex = viewGroupIndexNumber.integerValue; - for (NSInteger i = viewGroupIndex; i < viewGroupArray.count; i++) - { + for (NSInteger i = viewGroupIndex; i < viewGroupArray.count; i++) { MyViewGroupAndActionData *va = viewGroupArray[i]; - for (UIView *sbv in va.viewGroup) - { - if (sbv != (UIView*)[NSNull null]) - { - sbv.myFrame.frame = CGRectZero; - + for (UIView *sbv in va.viewGroup) { + if (sbv != (UIView *)[NSNull null]) { + sbv.myEngine.frame = CGRectZero; + //这里面让所有视图的枚举器也走一遍,解决下面的重复设置的问题。 UIView *anyway = enumerator.nextObject; - anyway = nil; //防止有anyway编译告警而设置。 + anyway = nil; //防止有anyway编译告警而设置。 } } } }]; - - + //处理那些剩余没有放入格子的子视图的frame设置为0 - for (UIView *sbv = enumerator.nextObject; sbv; sbv = enumerator.nextObject) - { - sbv.myFrame.frame = CGRectZero; + for (UIView *sbv = enumerator.nextObject; sbv; sbv = enumerator.nextObject) { + sbv.myEngine.frame = CGRectZero; } - - - [self myAdjustLayoutSelfSize:&selfSize lsc:lsc]; - - [self myAdjustSubviewsRTLPos:sbs selfWidth:selfSize.width]; - - return [self myAdjustSizeWhenNoSubviews:selfSize sbs:sbs lsc:lsc]; + context->selfSize = selfSize; + return [self myAdjustLayoutViewSizeWithContext:context]; } --(id)createSizeClassInstance -{ - return [MyGridLayoutViewSizeClass new]; +- (id)createSizeClassInstance { + return [MyGridLayoutTraits new]; } -#pragma mark -- Private Method +#pragma mark-- Private Methods //遍历位置 --(void)myTraversalGridOrigin:(id)grid gridOrigin:(CGPoint)gridOrigin lsc:(MyGridLayout*)lsc sbvEnumerator:(NSEnumerator*)sbvEnumerator tagViewGroupIndexDict:(NSMutableDictionary*)tagViewGroupIndexDict tagSbvEnumerator:(NSEnumerator*)tagSbvEnumerator isEstimate:(BOOL)isEstimate sizeClass:(MySizeClass)sizeClass pHasSubLayout:(BOOL*)pHasSubLayout -{ +- (void)myTraversalGrid:(id)grid gridOrigin:(CGPoint)gridOrigin layoutTraits:(MyGridLayoutTraits *)layoutTraits sbvEnumerator:(NSEnumerator *)sbvEnumerator tagViewGroupIndexDict:(NSMutableDictionary *)tagViewGroupIndexDict tagSbvEnumerator:(NSEnumerator *)tagSbvEnumerator withContext:(MyLayoutContext *)context { //这要优化减少不必要的空数组的建立。。 - NSArray> * subGrids = nil; + NSArray> *subGrids = nil; if (grid.subGridsType != MySubGridsType_Unknown) - subGrids = grid.subGrids; + subGrids = grid.subGrids; //绘制边界线。。 - if (!isEstimate) - { + if (!context->isEstimate) { [grid setBorderlineNeedLayoutIn:grid.gridRect withLayer:self.layer]; } - - - if (grid.tag != 0) - { - NSNumber *key = @(grid.tag); - NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; + if (grid.tag != 0) { + NSNumber *key = @(grid.tag); + NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; NSNumber *viewGroupIndex = [tagViewGroupIndexDict objectForKey:key]; - if (viewGroupArray != nil && viewGroupIndex != nil) - { - if (viewGroupIndex.integerValue < viewGroupArray.count) - { + if (viewGroupArray != nil && viewGroupIndex != nil) { + if (viewGroupIndex.integerValue < viewGroupArray.count) { //这里将动作的数据和栅格进行关联。 grid.actionData = viewGroupArray[viewGroupIndex.integerValue].actionData; - - tagSbvEnumerator = viewGroupArray[viewGroupIndex.integerValue].viewGroup.objectEnumerator; + + tagSbvEnumerator = viewGroupArray[viewGroupIndex.integerValue].viewGroup.objectEnumerator; [tagViewGroupIndexDict setObject:@(viewGroupIndex.integerValue + 1) forKey:key]; - } - else - { + } else { grid.actionData = nil; tagSbvEnumerator = nil; sbvEnumerator = nil; } - } - else - { + } else { tagSbvEnumerator = nil; } } @@ -979,62 +749,52 @@ -(void)myTraversalGridOrigin:(id)grid gridOrigin:(CGPoint)gridOrigi CGFloat paddingLeading; CGFloat paddingBottom; CGFloat paddingTrailing; - if (grid == lsc) - { - paddingTop = lsc.myLayoutTopPadding; - paddingLeading = lsc.myLayoutLeadingPadding; - paddingBottom = lsc.myLayoutBottomPadding; - paddingTrailing = lsc.myLayoutTrailingPadding; - } - else - { + if (grid == (id)layoutTraits) { + paddingTop = layoutTraits.myLayoutPaddingTop; + paddingLeading = layoutTraits.myLayoutPaddingLeading; + paddingBottom = layoutTraits.myLayoutPaddingBottom; + paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + } else { UIEdgeInsets gridPadding = grid.padding; paddingTop = gridPadding.top; paddingLeading = [MyBaseLayout isRTL] ? gridPadding.right : gridPadding.left; paddingBottom = gridPadding.bottom; paddingTrailing = [MyBaseLayout isRTL] ? gridPadding.left : gridPadding.right; } - + //处理叶子节点。 - if ((grid.anchor || subGrids.count == 0) && !grid.placeholder) - { + if ((grid.anchor || subGrids.count == 0) && !grid.placeholder) { //设置子视图的位置和尺寸。。 UIView *sbv = nil; UIView *tagSbv = tagSbvEnumerator.nextObject; - if (tagSbv != (UIView*)[NSNull null]) - sbv = sbvEnumerator.nextObject; - - if (tagSbv != nil && tagSbv != (UIView*)[NSNull null] && tagSbvEnumerator != nil) + if (tagSbv != (UIView *)[NSNull null]) { + sbv = sbvEnumerator.nextObject; + } + if (tagSbv != nil && tagSbv != (UIView *)[NSNull null] && tagSbvEnumerator != nil) { sbv = tagSbv; - - if (sbv != nil) - { + } + if (sbv != nil) { //调整位置和尺寸。。。 - MyFrame *sbvmyFrame = sbv.myFrame; - - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - + MyLayoutEngine *subviewEngine = sbv.myEngine; //取垂直和水平对齐 - MyGravity vertGravity = grid.gravity & MyGravity_Horz_Mask; - if (vertGravity == MyGravity_None) + MyGravity vertGravity = MYVERTGRAVITY(grid.gravity); + if (vertGravity == MyGravity_None) { vertGravity = MyGravity_Vert_Fill; - - MyGravity horzGravity = grid.gravity & MyGravity_Vert_Mask; - if (horzGravity == MyGravity_None) + } + MyGravity horzGravity = MYHORZGRAVITY(grid.gravity); + if (horzGravity == MyGravity_None) { horzGravity = MyGravity_Horz_Fill; - else - horzGravity = [self myConvertLeftRightGravityToLeadingTrailing:horzGravity]; - - + } else { + horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:horzGravity]; + } //如果非叶子栅格设置为anchor则子视图的内容总是填充的 CGFloat tempPaddingTop = paddingTop; CGFloat tempPaddingLeading = paddingLeading; CGFloat tempPaddingBottom = paddingBottom; CGFloat tempPaddingTrailing = paddingTrailing; - - if (grid.anchor && subGrids.count > 0) - { + + if (grid.anchor && subGrids.count > 0) { vertGravity = MyGravity_Vert_Fill; horzGravity = MyGravity_Horz_Fill; tempPaddingTop = 0; @@ -1042,172 +802,143 @@ -(void)myTraversalGridOrigin:(id)grid gridOrigin:(CGPoint)gridOrigi tempPaddingBottom = 0; tempPaddingTrailing = 0; } - //如果是尺寸为0,并且设置为了anchor的话那么就根据自身 - //如果尺寸是0则因为前面有算出尺寸,所以这里就不进行调整了。 - if (grid.measure != 0 && [sbv isKindOfClass:[MyBaseLayout class]]) - { - [self myAdjustSubviewWrapContentSet:sbv isEstimate:isEstimate sbvmyFrame:sbvmyFrame sbvsc:sbvsc selfSize:grid.gridRect.size sizeClass:sizeClass pHasSubLayout:pHasSubLayout]; - } - else - { + if (grid.measure != 0 && [sbv isKindOfClass:[MyBaseLayout class]]) { + context->horzGravity = horzGravity; + context->vertGravity = vertGravity; + context->selfSize = grid.gridRect.size; + [self myAdjustSizeSettingOfSubviewEngine:subviewEngine withContext:context]; } + context->paddingTop = tempPaddingTop; + context->paddingBottom = tempPaddingBottom; + context->paddingLeading = tempPaddingLeading; + context->paddingTrailing = tempPaddingTrailing; + context->horzGravity = horzGravity; + context->vertGravity = vertGravity; + context->selfSize = grid.gridRect.size; + [self myCalcRectOfSubviewEngine:subviewEngine pMaxWrapSize:NULL withContext:context]; - [self myCalcSubViewRect:sbv sbvsc:sbvsc sbvmyFrame:sbvmyFrame lsc:lsc vertGravity:vertGravity horzGravity:horzGravity inSelfSize:grid.gridRect.size paddingTop:tempPaddingTop paddingLeading:tempPaddingLeading paddingBottom:tempPaddingBottom paddingTrailing:tempPaddingTrailing pMaxWrapSize:NULL]; - - sbvmyFrame.leading += gridOrigin.x; - sbvmyFrame.top += gridOrigin.y; - + subviewEngine.leading += gridOrigin.x; + subviewEngine.top += gridOrigin.y; } } - - //处理子格子的位置。 - CGFloat offset = 0; - if (grid.subGridsType == MySubGridsType_Col) - { + if (grid.subGridsType == MySubGridsType_Col) { offset = gridOrigin.x + paddingLeading; - - MyGravity horzGravity = [self myConvertLeftRightGravityToLeadingTrailing:grid.gravity & MyGravity_Vert_Mask]; - if (horzGravity == MyGravity_Horz_Center || horzGravity == MyGravity_Horz_Trailing) - { + + MyGravity horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(grid.gravity)]; + if (horzGravity == MyGravity_Horz_Center || horzGravity == MyGravity_Horz_Trailing) { //得出所有子栅格的宽度综合 CGFloat subGridsWidth = 0; - for (id sbvGrid in subGrids) - { + for (id sbvGrid in subGrids) { subGridsWidth += sbvGrid.gridRect.size.width; } - - if (subGrids.count > 1) - subGridsWidth += grid.subviewSpace * (subGrids.count - 1); - - if (horzGravity == MyGravity_Horz_Center) - { - offset += (grid.gridRect.size.width - paddingLeading - paddingTrailing - subGridsWidth)/2; + if (subGrids.count > 1) { + subGridsWidth += grid.subviewSpace * (subGrids.count - 1); } - else - { + if (horzGravity == MyGravity_Horz_Center) { + offset += (grid.gridRect.size.width - paddingLeading - paddingTrailing - subGridsWidth) / 2; + } else { offset += grid.gridRect.size.width - paddingLeading - paddingTrailing - subGridsWidth; } } - - - } - else if (grid.subGridsType == MySubGridsType_Row) - { + + } else if (grid.subGridsType == MySubGridsType_Row) { offset = gridOrigin.y + paddingTop; - - MyGravity vertGravity = grid.gravity & MyGravity_Horz_Mask; - if (vertGravity == MyGravity_Vert_Center || vertGravity == MyGravity_Vert_Bottom) - { + + MyGravity vertGravity = MYVERTGRAVITY(grid.gravity); + if (vertGravity == MyGravity_Vert_Center || vertGravity == MyGravity_Vert_Bottom) { //得出所有子栅格的宽度综合 CGFloat subGridsHeight = 0; - for (id sbvGrid in subGrids) - { + for (id sbvGrid in subGrids) { subGridsHeight += sbvGrid.gridRect.size.height; } - - if (subGrids.count > 1) + + if (subGrids.count > 1) { subGridsHeight += grid.subviewSpace * (subGrids.count - 1); - - if (vertGravity == MyGravity_Vert_Center) - { - offset += (grid.gridRect.size.height - paddingTop - paddingBottom - subGridsHeight)/2; } - else - { + if (vertGravity == MyGravity_Vert_Center) { + offset += (grid.gridRect.size.height - paddingTop - paddingBottom - subGridsHeight) / 2; + } else { offset += grid.gridRect.size.height - paddingTop - paddingBottom - subGridsHeight; } } - } - else - { - - } - - CGPoint paddingGridOrigin = CGPointMake(gridOrigin.x + paddingLeading, gridOrigin.y + paddingTop); - for (id sbvGrid in subGrids) - { + for (id sbvGrid in subGrids) { offset += [sbvGrid updateGridOrigin:paddingGridOrigin superGrid:grid withOffset:offset]; offset += grid.subviewSpace; - [self myTraversalGridOrigin:sbvGrid gridOrigin:sbvGrid.gridRect.origin lsc:lsc sbvEnumerator:sbvEnumerator tagViewGroupIndexDict:tagViewGroupIndexDict tagSbvEnumerator:((sbvGrid.tag != 0)? nil: tagSbvEnumerator) isEstimate:isEstimate sizeClass:sizeClass pHasSubLayout:pHasSubLayout]; + [self myTraversalGrid:sbvGrid gridOrigin:sbvGrid.gridRect.origin layoutTraits:layoutTraits sbvEnumerator:sbvEnumerator tagViewGroupIndexDict:tagViewGroupIndexDict tagSbvEnumerator:((sbvGrid.tag != 0) ? nil : tagSbvEnumerator) withContext:context]; } - + //如果栅格中的tagSbvEnumerator还有剩余的视图没有地方可填,那么就将尺寸和位置设置为0 - if (grid.tag != 0) - { + if (grid.tag != 0) { //枚举那些剩余的 - for (UIView *sbv = tagSbvEnumerator.nextObject; sbv; sbv = tagSbvEnumerator.nextObject) - { - if (sbv != (UIView*)[NSNull null]) - { - sbv.myFrame.frame = CGRectZero; - + for (UIView *sbv = tagSbvEnumerator.nextObject; sbv; sbv = tagSbvEnumerator.nextObject) { + if (sbv != (UIView *)[NSNull null]) { + sbv.myEngine.frame = CGRectZero; + //所有子视图枚举器也要移动。 UIView *anyway = sbvEnumerator.nextObject; anyway = nil; } } } - - } +} --(void)myBlankTraverse:(id)grid sbs:(NSArray*)sbs pIndex:(NSInteger*)pIndex tagSbs:(NSArray *)tagSbs pTagIndex:(NSInteger*)pTagIndex -{ +- (void)myBlankTraverse:(id)grid sbs:(NSArray *)sbs pIndex:(NSInteger *)pIndex tagSbs:(NSArray *)tagSbs pTagIndex:(NSInteger *)pTagIndex { NSArray> *subGrids = nil; - if (grid.subGridsType != MySubGridsType_Unknown) + if (grid.subGridsType != MySubGridsType_Unknown) { subGrids = grid.subGrids; - - if ((grid.anchor || subGrids.count == 0) && !grid.placeholder) - { + } + if ((grid.anchor || subGrids.count == 0) && !grid.placeholder) { BOOL isNoNullSbv = YES; - if (grid.tag == 0 && pTagIndex != NULL) - { + if (grid.tag == 0 && pTagIndex != NULL) { *pTagIndex = *pTagIndex + 1; - - if (tagSbs != nil && *pTagIndex < tagSbs.count && tagSbs[*pTagIndex] == (UIView*)[NSNull null]) + + if (tagSbs != nil && *pTagIndex < tagSbs.count && tagSbs[*pTagIndex] == (UIView *)[NSNull null]) { isNoNullSbv = NO; + } } - - if (isNoNullSbv) - *pIndex = *pIndex + 1; + if (isNoNullSbv) { + *pIndex = *pIndex + 1; + } } - - for (id sbvGrid in subGrids) - { - [self myBlankTraverse:sbvGrid sbs:sbs pIndex:pIndex tagSbs:tagSbs pTagIndex:(grid.tag != 0)? NULL : pTagIndex]; + + for (id sbvGrid in subGrids) { + [self myBlankTraverse:sbvGrid sbs:sbs pIndex:pIndex tagSbs:tagSbs pTagIndex:(grid.tag != 0) ? NULL : pTagIndex]; } } -//遍历尺寸。 --(CGFloat)myTraversalGridSize:(id)grid gridSize:(CGSize)gridSize lsc:(MyGridLayout*)lsc sbs:(NSArray*)sbs pIndex:(NSInteger*)pIndex tagViewGroupIndexDict:(NSMutableDictionary*)tagViewGroupIndexDict tagViewGroup:(NSArray*)tagViewGroup pTagIndex:(NSInteger*)pTagIndex -{ +/* +遍历尺寸。 + pIndex 指定格子所对应的子视图的索引指针。 + pTagIndex 指格子所对应的视图组内的索引指针? + tagViewGroupIndexDict: 记录标签中的视图组索引字典。 + 函数返回格子的尺寸值。 + */ +- (CGFloat)myTraversalGrid:(id)grid gridSize:(CGSize)gridSize lsc:(MyGridLayoutTraits *)lsc sbs:(NSArray *)sbs pIndex:(NSInteger *)pIndex tagViewGroupIndexDict:(NSMutableDictionary *)tagViewGroupIndexDict tagViewGroup:(NSArray *)tagViewGroup pTagIndex:(NSInteger *)pTagIndex withContext:(MyLayoutContext *)context { NSArray> *subGrids = nil; - if (grid.subGridsType != MySubGridsType_Unknown) - subGrids = grid.subGrids; - - + if (grid.subGridsType != MySubGridsType_Unknown) { + subGrids = grid.subGrids; + } + CGFloat paddingTop; CGFloat paddingLeading; CGFloat paddingBottom; CGFloat paddingTrailing; - if (grid == lsc) - { - paddingTop = lsc.myLayoutTopPadding; - paddingLeading = lsc.myLayoutLeadingPadding; - paddingBottom = lsc.myLayoutBottomPadding; - paddingTrailing = lsc.myLayoutTrailingPadding; - } - else - { + if (grid == (id)lsc) { + paddingTop = lsc.myLayoutPaddingTop; + paddingLeading = lsc.myLayoutPaddingLeading; + paddingBottom = lsc.myLayoutPaddingBottom; + paddingTrailing = lsc.myLayoutPaddingTrailing; + } else { UIEdgeInsets gridPadding = grid.padding; paddingTop = gridPadding.top; paddingLeading = [MyBaseLayout isRTL] ? gridPadding.right : gridPadding.left; @@ -1215,287 +946,324 @@ -(CGFloat)myTraversalGridSize:(id)grid gridSize:(CGSize)gridSize lsc paddingTrailing = [MyBaseLayout isRTL] ? gridPadding.left : gridPadding.right; } - CGFloat fixedMeasure = 0; //固定部分的尺寸 - CGFloat validMeasure = 0; //整体有效的尺寸 - if (subGrids.count > 1) + CGFloat fixedMeasure = 0; //固定部分的尺寸 + CGFloat validMeasure = 0; //整体有效的尺寸 + if (subGrids.count > 1) { fixedMeasure += (subGrids.count - 1) * grid.subviewSpace; - - if (grid.subGridsType == MySubGridsType_Col) - { + } + if (grid.subGridsType == MySubGridsType_Col) { fixedMeasure += paddingLeading + paddingTrailing; validMeasure = grid.gridRect.size.width - fixedMeasure; - } - else if(grid.subGridsType == MySubGridsType_Row) - { + } else if (grid.subGridsType == MySubGridsType_Row) { fixedMeasure += paddingTop + paddingBottom; validMeasure = grid.gridRect.size.height - fixedMeasure; } - else; - + + //要从格子找到对应的视图,因为格子是树形结构而视图是数组结构,因此每遍历完一个可以放视图的格子,视图的索引就要加1 + //还有一种特殊的就是开启了视图组的功能,因此某些标签下的格子要单独遍历对应视图组中的视图。 + //但是这里是否存在视图组和非视图组并存的情况呢? //得到匹配的form - if (grid.tag != 0) - { + if (grid.tag != 0) { NSNumber *key = @(grid.tag); - NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; + NSMutableArray *viewGroupArray = [self.tagsDict objectForKey:key]; NSNumber *viewGroupIndex = [tagViewGroupIndexDict objectForKey:key]; - if (viewGroupArray != nil && viewGroupIndex != nil) - { - if (viewGroupIndex.integerValue < viewGroupArray.count) - { + if (viewGroupArray != nil && viewGroupIndex != nil) { + if (viewGroupIndex.integerValue < viewGroupArray.count) { tagViewGroup = viewGroupArray[viewGroupIndex.integerValue].viewGroup; NSInteger tagIndex = 0; pTagIndex = &tagIndex; [tagViewGroupIndexDict setObject:@(viewGroupIndex.integerValue + 1) forKey:key]; - } - else - { + } else { tagViewGroup = nil; pTagIndex = NULL; sbs = nil; } - } - else - { + } else { tagViewGroup = nil; pTagIndex = NULL; } } - - //叶子节点 - if ((grid.anchor || subGrids.count == 0) && !grid.placeholder) - { + //叶子节点,或者用来放置视图的格子。 + if ((grid.anchor || subGrids.count == 0) && !grid.placeholder) { BOOL isNotNullSbv = YES; - NSArray *tempSbs = sbs; + NSArray *tempSbs = sbs; NSInteger *pTempIndex = pIndex; - - if (tagViewGroup != nil && pTagIndex != NULL) - { + + if (tagViewGroup != nil && pTagIndex != NULL) { tempSbs = tagViewGroup; pTempIndex = pTagIndex; } - + //如果尺寸是包裹 - if (grid.measure == MyLayoutSize.wrap || (grid.measure == 0 && grid.anchor)) - { - if (*pTempIndex < tempSbs.count) - { + if (grid.measure == MyLayoutSize.wrap || (grid.measure == 0 && grid.anchor)) { + if (*pTempIndex < tempSbs.count) { //加这个条件是根栅格如果是叶子栅格的话不处理这种情况。 - if (grid.superGrid != nil) - { + if (grid.superGrid != nil) { UIView *sbv = tempSbs[*pTempIndex]; - if (sbv != (UIView*)[NSNull null]) - { - + if (sbv != (UIView *)[NSNull null]) { //叶子节点 - if (!grid.anchor || (grid.measure == 0 && grid.anchor)) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - sbvmyFrame.frame = sbv.bounds; - - //如果子视图不设置任何约束但是又是包裹的则这里特殊处理。 - if (sbvsc.widthSizeInner == nil && sbvsc.heightSizeInner == nil && !sbvsc.wrapContentSize) - { + if (!grid.anchor || (grid.measure == 0 && grid.anchor)) { + MyLayoutEngine *subviewEngine = sbv.myEngine; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + subviewEngine.frame = sbv.bounds; + + //如果子视图不设置任何约束要进行特殊处理。 + if (subviewTraits.widthSizeInner.val == nil && subviewTraits.heightSizeInner.val == nil) { CGSize size = CGSizeZero; - if (grid.superGrid.subGridsType == MySubGridsType_Row) - { + if (grid.superGrid.subGridsType == MySubGridsType_Row) { size.width = gridSize.width - paddingLeading - paddingTrailing; - } - else - { + } else { size.height = gridSize.height - paddingTop - paddingBottom; } - size = [sbv sizeThatFits:size]; - sbvmyFrame.width = size.width; - sbvmyFrame.height = size.height; - } - else - { - - [self myCalcSizeOfWrapContentSubview:sbv sbvsc:sbvsc sbvmyFrame:sbvmyFrame]; + subviewEngine.width = size.width; + subviewEngine.height = size.height; - [self myCalcSubViewRect:sbv sbvsc:sbvsc sbvmyFrame:sbvmyFrame lsc:lsc vertGravity:MyGravity_None horzGravity:MyGravity_None inSelfSize:grid.gridRect.size paddingTop:paddingTop paddingLeading:paddingLeading paddingBottom:paddingBottom paddingTrailing:paddingTrailing pMaxWrapSize:NULL]; + } else { + context->vertGravity = MyGravity_None; + context->horzGravity = MyGravity_None; + context->paddingTop = paddingTop; + context->paddingBottom = paddingBottom; + context->paddingLeading = paddingLeading; + context->paddingTrailing = paddingTrailing; + context->selfSize = grid.gridRect.size; + [self myCalcRectOfSubviewEngine:subviewEngine pMaxWrapSize:NULL withContext:context]; } - - if (grid.superGrid.subGridsType == MySubGridsType_Row) - { - fixedMeasure = paddingTop + paddingBottom + sbvmyFrame.height; - } - else - { - fixedMeasure = paddingLeading + paddingTrailing + sbvmyFrame.width; + + if (grid.superGrid.subGridsType == MySubGridsType_Row) { + fixedMeasure = paddingTop + paddingBottom + subviewEngine.height; + if (grid.superGrid.measure == MyLayoutSize.wrap && grid.superGrid.superGrid != nil && grid.superGrid.superGrid.subGridsType == MySubGridsType_Col) { + [grid updateWrapGridSizeInSuperGrid:grid.superGrid.superGrid withMeasure:paddingLeading + paddingTrailing + subviewEngine.width]; + } + + } else { + fixedMeasure = paddingLeading + paddingTrailing + subviewEngine.width; + if (grid.superGrid.measure == MyLayoutSize.wrap && grid.superGrid.superGrid != nil && grid.superGrid.superGrid.subGridsType == MySubGridsType_Row) { + [grid updateWrapGridSizeInSuperGrid:grid.superGrid.superGrid withMeasure:paddingTop + paddingBottom + subviewEngine.height]; + } } } - } - else + } else { isNotNullSbv = NO; + } } } } - + //索引加1跳转到下一个节点。 - if (tagViewGroup != nil && pTagIndex != NULL) - { + if (tagViewGroup != nil && pTagIndex != NULL) { *pTempIndex = *pTempIndex + 1; } - - if (isNotNullSbv) + if (isNotNullSbv) { *pIndex = *pIndex + 1; + } } - - if (subGrids.count > 0) - { - - NSMutableArray> *weightSubGrids = [NSMutableArray new]; //比重尺寸子格子数组 - NSMutableArray *weightSubGridsIndexs = [NSMutableArray new]; //比重尺寸格子的开头子视图位置索引 - NSMutableArray *weightSubGridsTagIndexs = [NSMutableArray new]; //比重尺寸格子的开头子视图位置索引 - - - NSMutableArray> *fillSubGrids = [NSMutableArray new]; //填充尺寸格子数组 - NSMutableArray *fillSubGridsIndexs = [NSMutableArray new]; //填充尺寸格子的开头子视图位置索引 - NSMutableArray *fillSubGridsTagIndexs = [NSMutableArray new]; //填充尺寸格子的开头子视图位置索引 - - + //如果自己的尺寸是wrap的并且子节点的行列类型和自身的行列类型不一致,则尺寸为最大的值。 + BOOL wrapMeasure = NO; + CGFloat maxMeasure = 0.0; + + if (subGrids.count > 0) { + NSMutableArray> *weightSubGrids = [NSMutableArray new]; //比重尺寸子格子数组 + NSMutableArray *weightSubGridsIndexs = [NSMutableArray new]; //比重尺寸格子的开头子视图位置索引 + NSMutableArray *weightSubGridsTagIndexs = [NSMutableArray new]; //比重尺寸格子的开头子视图位置索引 + + NSMutableArray> *fillSubGrids = [NSMutableArray new]; //填充尺寸格子数组 + NSMutableArray *fillSubGridsIndexs = [NSMutableArray new]; //填充尺寸格子的开头子视图位置索引 + NSMutableArray *fillSubGridsTagIndexs = [NSMutableArray new]; //填充尺寸格子的开头子视图位置索引 + //包裹尺寸先遍历所有子格子 CGSize gridSize2 = gridSize; - if (grid.subGridsType == MySubGridsType_Row) - { + if (grid.subGridsType == MySubGridsType_Row) { gridSize2.width -= (paddingLeading + paddingTrailing); - } - else - { + } else { gridSize2.height -= (paddingTop + paddingBottom); } - for (id sbvGrid in subGrids) - { - if (sbvGrid.measure == MyLayoutSize.wrap) - { - - CGFloat measure = [self myTraversalGridSize:sbvGrid gridSize:gridSize2 lsc:lsc sbs:sbs pIndex:pIndex tagViewGroupIndexDict:tagViewGroupIndexDict tagViewGroup:tagViewGroup pTagIndex:pTagIndex]; - fixedMeasure += [sbvGrid updateGridSize:gridSize2 superGrid:grid withMeasure:measure]; - + if (grid == (id)lsc) { + + if (lsc.heightSizeInner.isWrap && grid.subGridsType == MySubGridsType_Col) { + wrapMeasure = YES; } - else if (sbvGrid.measure >= 1 || sbvGrid.measure == 0) - { - fixedMeasure += [sbvGrid updateGridSize:gridSize2 superGrid:grid withMeasure:sbvGrid.measure]; + + if (lsc.widthSizeInner.isWrap && grid.subGridsType == MySubGridsType_Row) { + wrapMeasure = YES; + } + + } else { + wrapMeasure = (grid.measure == MyLayoutSize.wrap) && (grid.superGrid.subGridsType != grid.subGridsType); + } + + for (id sbvGrid in subGrids) { + if (sbvGrid.measure == MyLayoutSize.wrap) { + + //先将父节点的尺寸更新到非wrap维度的自身尺寸上,便于计算另外维度的子格子尺寸。 + [sbvGrid updateGridSize:gridSize2 superGrid:grid withMeasure:0]; + CGFloat measure = [self myTraversalGrid:sbvGrid gridSize:gridSize2 lsc:lsc sbs:sbs pIndex:pIndex tagViewGroupIndexDict:tagViewGroupIndexDict tagViewGroup:tagViewGroup pTagIndex:pTagIndex withContext:context]; + if (wrapMeasure) { + fixedMeasure += measure; + measure = [sbvGrid updateWrapGridSizeInSuperGrid:grid withMeasure:measure]; + //取子节点中的最高值。 + if (measure > maxMeasure) { + maxMeasure = measure; + } + } else { + + fixedMeasure += [sbvGrid updateGridSize:gridSize2 superGrid:grid withMeasure:measure]; + } + + } else if (sbvGrid.measure >= 1 || sbvGrid.measure == 0) { + CGFloat measure = sbvGrid.measure; + if (wrapMeasure) { + fixedMeasure += measure; + [sbvGrid updateWrapGridSizeInSuperGrid:grid withMeasure:measure]; + } else { + fixedMeasure += [sbvGrid updateGridSize:gridSize2 superGrid:grid withMeasure:measure]; + } + //遍历儿子节点。。 - [self myTraversalGridSize:sbvGrid gridSize:sbvGrid.gridRect.size lsc:lsc sbs:sbs pIndex:pIndex tagViewGroupIndexDict:tagViewGroupIndexDict tagViewGroup:tagViewGroup pTagIndex:pTagIndex]; + [self myTraversalGrid:sbvGrid gridSize:sbvGrid.gridRect.size lsc:lsc sbs:sbs pIndex:pIndex tagViewGroupIndexDict:tagViewGroupIndexDict tagViewGroup:tagViewGroup pTagIndex:pTagIndex withContext:context]; - } - else if (sbvGrid.measure > 0 && sbvGrid.measure < 1) - { - fixedMeasure += [sbvGrid updateGridSize:gridSize2 superGrid:grid withMeasure:validMeasure * sbvGrid.measure]; + if (wrapMeasure) { + measure = [sbvGrid updateWrapGridSizeInSuperGrid:grid withMeasure:measure]; + if (measure > maxMeasure) { + maxMeasure = measure; + } + } + + } else if (sbvGrid.measure > 0 && sbvGrid.measure < 1) { + CGFloat measure = validMeasure * sbvGrid.measure; + if (wrapMeasure) { + fixedMeasure += measure; + [sbvGrid updateWrapGridSizeInSuperGrid:grid withMeasure:measure]; + } else { + fixedMeasure += [sbvGrid updateGridSize:gridSize2 superGrid:grid withMeasure:measure]; + } + //遍历儿子节点。。 - [self myTraversalGridSize:sbvGrid gridSize:sbvGrid.gridRect.size lsc:lsc sbs:sbs pIndex:pIndex tagViewGroupIndexDict:tagViewGroupIndexDict tagViewGroup:tagViewGroup pTagIndex:pTagIndex]; + [self myTraversalGrid:sbvGrid gridSize:sbvGrid.gridRect.size lsc:lsc sbs:sbs pIndex:pIndex tagViewGroupIndexDict:tagViewGroupIndexDict tagViewGroup:tagViewGroup pTagIndex:pTagIndex withContext:context]; - } - else if (sbvGrid.measure < 0 && sbvGrid.measure > -1) - { + if (wrapMeasure) { + measure = [sbvGrid updateWrapGridSizeInSuperGrid:grid withMeasure:measure]; + if (measure > maxMeasure) { + maxMeasure = measure; + } + } + + } else if (sbvGrid.measure < 0 && sbvGrid.measure > -1) { [weightSubGrids addObject:sbvGrid]; [weightSubGridsIndexs addObject:@(*pIndex)]; - - if (pTagIndex != NULL) - { + + if (pTagIndex != NULL) { [weightSubGridsTagIndexs addObject:@(*pTagIndex)]; } - //这里面空转一下。 [self myBlankTraverse:sbvGrid sbs:sbs pIndex:pIndex tagSbs:tagViewGroup pTagIndex:pTagIndex]; - - - } - else if (sbvGrid.measure == MyLayoutSize.fill) - { + + } else if (sbvGrid.measure == MyLayoutSize.fill) { [fillSubGrids addObject:sbvGrid]; - + [fillSubGridsIndexs addObject:@(*pIndex)]; - - if (pTagIndex != NULL) - { + + if (pTagIndex != NULL) { [fillSubGridsTagIndexs addObject:@(*pTagIndex)]; } - //这里面空转一下。 [self myBlankTraverse:sbvGrid sbs:sbs pIndex:pIndex tagSbs:tagViewGroup pTagIndex:pTagIndex]; - } - else - { + } else { NSAssert(0, @"oops!"); } } - - + //算出剩余的尺寸。 BOOL hasTagIndex = (pTagIndex != NULL); CGFloat remainedMeasure = 0; - if (grid.subGridsType == MySubGridsType_Col) - { + if (grid.subGridsType == MySubGridsType_Col) { remainedMeasure = grid.gridRect.size.width - fixedMeasure; - } - else if (grid.subGridsType == MySubGridsType_Row) - { + } else if (grid.subGridsType == MySubGridsType_Row) { remainedMeasure = grid.gridRect.size.height - fixedMeasure; } - else; - + NSInteger weightSubGridCount = weightSubGrids.count; - if (weightSubGridCount > 0) - { - for (NSInteger i = 0; i < weightSubGridCount; i++) - { + if (weightSubGridCount > 0) { + for (NSInteger i = 0; i < weightSubGridCount; i++) { id sbvGrid = weightSubGrids[i]; - remainedMeasure -= [sbvGrid updateGridSize:gridSize2 superGrid:grid withMeasure:-1 * remainedMeasure * sbvGrid.measure]; + CGFloat measure = -1 * remainedMeasure * sbvGrid.measure; + if (wrapMeasure) { + remainedMeasure -= measure; + [sbvGrid updateWrapGridSizeInSuperGrid:grid withMeasure:measure]; + } else { + remainedMeasure -= [sbvGrid updateGridSize:gridSize2 superGrid:grid withMeasure:measure]; + } + NSInteger index = weightSubGridsIndexs[i].integerValue; - if (hasTagIndex) - { + if (hasTagIndex) { NSInteger tagIndex = weightSubGridsTagIndexs[i].integerValue; pTagIndex = &tagIndex; - } - else - { + } else { pTagIndex = NULL; } + + [self myTraversalGrid:sbvGrid gridSize:sbvGrid.gridRect.size lsc:lsc sbs:sbs pIndex:&index tagViewGroupIndexDict:tagViewGroupIndexDict tagViewGroup:tagViewGroup pTagIndex:pTagIndex withContext:context]; - [self myTraversalGridSize:sbvGrid gridSize:sbvGrid.gridRect.size lsc:lsc sbs:sbs pIndex:&index tagViewGroupIndexDict:tagViewGroupIndexDict tagViewGroup:tagViewGroup pTagIndex:pTagIndex]; + if (wrapMeasure) { + measure = [sbvGrid updateWrapGridSizeInSuperGrid:grid withMeasure:measure]; + if (measure > maxMeasure) { + maxMeasure = measure; + } + } } } - + NSInteger fillSubGridsCount = fillSubGrids.count; - if (fillSubGridsCount > 0) - { + if (fillSubGridsCount > 0) { NSInteger totalCount = fillSubGridsCount; - for (NSInteger i = 0; i < fillSubGridsCount; i++) - { + for (NSInteger i = 0; i < fillSubGridsCount; i++) { id sbvGrid = fillSubGrids[i]; - remainedMeasure -= [sbvGrid updateGridSize:gridSize2 superGrid:grid withMeasure:_myCGFloatRound(remainedMeasure * (1.0/totalCount))]; + CGFloat measure = _myCGFloatRound(remainedMeasure * (1.0 / totalCount)); + if (wrapMeasure) { + remainedMeasure -= measure; + [sbvGrid updateWrapGridSizeInSuperGrid:grid withMeasure:measure]; + } else { + remainedMeasure -= [sbvGrid updateGridSize:gridSize2 superGrid:grid withMeasure:measure]; + } totalCount -= 1; - NSInteger index = fillSubGridsIndexs[i].integerValue; - - if (hasTagIndex) - { + if (hasTagIndex) { NSInteger tagIndex = fillSubGridsTagIndexs[i].integerValue; pTagIndex = &tagIndex; - } - else - { + } else { pTagIndex = nil; } + [self myTraversalGrid:sbvGrid gridSize:sbvGrid.gridRect.size lsc:lsc sbs:sbs pIndex:&index tagViewGroupIndexDict:tagViewGroupIndexDict tagViewGroup:tagViewGroup pTagIndex:pTagIndex withContext:context]; - [self myTraversalGridSize:sbvGrid gridSize:sbvGrid.gridRect.size lsc:lsc sbs:sbs pIndex:&index tagViewGroupIndexDict:tagViewGroupIndexDict tagViewGroup:tagViewGroup pTagIndex:pTagIndex]; + if (wrapMeasure) { + measure = [sbvGrid updateWrapGridSizeInSuperGrid:grid withMeasure:measure]; + if (measure > maxMeasure) { + maxMeasure = measure; + } + } } } } - return fixedMeasure; + + if (wrapMeasure) { + + if (self.subGridsType == MySubGridsType_Col) { + maxMeasure += paddingTop + paddingBottom; + }else { + maxMeasure += paddingLeading + paddingTrailing; + } + + return maxMeasure; + } else { + return fixedMeasure; + } } - @end diff --git a/MyLayout/Lib/MyGridNode.h b/MyLayout/Lib/MyGridNode.h index b82c6ce..bc62e3d 100644 --- a/MyLayout/Lib/MyGridNode.h +++ b/MyLayout/Lib/MyGridNode.h @@ -6,8 +6,8 @@ // Copyright © 2017年 YoungSoft. All rights reserved. // -#import #import "MyGrid.h" +#import //子栅格类型。 typedef enum : unsigned char { @@ -16,91 +16,80 @@ typedef enum : unsigned char { MySubGridsType_Col, } MySubGridsType; +@protocol MyGridNode -@protocol MyGridNode - -@property(nonatomic, weak) id superGrid; +@property (nonatomic, weak) id superGrid; //子栅格数组 -@property(nonatomic, strong) NSMutableArray> *subGrids; +@property (nonatomic, strong) NSMutableArray> *subGrids; //子栅格类型 -@property(nonatomic, assign) MySubGridsType subGridsType; - +@property (nonatomic, assign) MySubGridsType subGridsType; //栅格的rect -@property(nonatomic) CGRect gridRect; - +@property (nonatomic) CGRect gridRect; //得到栅格布局视图 --(UIView*)gridLayoutView; +- (UIView *)gridLayoutView; //得到栅格的动作 --(SEL)gridAction; +- (SEL)gridAction; //更新栅格尺寸。返回对应方向的尺寸 --(CGFloat)updateGridSize:(CGSize)superSize superGrid:(id)superGrid withMeasure:(CGFloat)measure; +- (CGFloat)updateGridSize:(CGSize)superSize superGrid:(id)superGrid withMeasure:(CGFloat)measure; +//只更新栅格一边的尺寸,返回另外一边方向的尺寸 +- (CGFloat)updateWrapGridSizeInSuperGrid:(id)superGrid withMeasure:(CGFloat)measure; //更新栅格位置。返回对应方向的尺寸 --(CGFloat)updateGridOrigin:(CGPoint)superOrigin superGrid:(id)superGrid withOffset:(CGFloat)offset; - +- (CGFloat)updateGridOrigin:(CGPoint)superOrigin superGrid:(id)superGrid withOffset:(CGFloat)offset; //栅格边界线的支持。 --(void)setBorderlineNeedLayoutIn:(CGRect)rect withLayer:(CALayer*)layer; --(void)showBorderline:(BOOL)show; - +- (void)setBorderlineNeedLayoutIn:(CGRect)rect withLayer:(CALayer *)layer; +- (void)showBorderline:(BOOL)show; //栅格触摸事件的支持。 //栅格点击测试,返回最佳的响应事件的子栅格或者自己。 --(id)gridHitTest:(CGPoint)point; +- (id)gridHitTest:(CGPoint)point; - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; - @end - //普通节点。内部使用,外部不使用 -@interface MyGridNode : NSObject - - --(instancetype)initWithMeasure:(CGFloat)measure superGrid:(id)superGrid; - - -@property(nonatomic, assign) CGFloat measure; +@interface MyGridNode : NSObject +- (instancetype)initWithMeasure:(CGFloat)measure superGrid:(id)superGrid; -@property(nonatomic) NSInteger tag; +@property (nonatomic, assign) CGFloat measure; -@property(nonatomic, strong) id actionData; +@property (nonatomic) NSInteger tag; +@property (nonatomic, strong) id actionData; -@property(nonatomic, weak) id superGrid; +@property (nonatomic, weak) id superGrid; //子栅格是否列栅格 -@property(nonatomic, strong) NSMutableArray> *subGrids; -@property(nonatomic, assign) MySubGridsType subGridsType; - +@property (nonatomic, strong) NSMutableArray> *subGrids; +@property (nonatomic, assign) MySubGridsType subGridsType; //格子内子栅格的间距 -@property(nonatomic, assign) CGFloat subviewSpace; +@property (nonatomic, assign) CGFloat subviewSpace; //格子内视图的内边距。 -@property(nonatomic, assign) UIEdgeInsets padding; +@property (nonatomic, assign) UIEdgeInsets padding; //格子内子视图的对齐停靠方式。 -@property(nonatomic, assign) MyGravity gravity; +@property (nonatomic, assign) MyGravity gravity; //是否占位标志。 -@property(nonatomic, assign) BOOL placeholder; +@property (nonatomic, assign) BOOL placeholder; //是否是锚点。 -@property(nonatomic, assign) BOOL anchor; - +@property (nonatomic, assign) BOOL anchor; /** 指定栅格内的视图具有重叠属性,并指定重叠视图的对齐停靠方式。这个属性是一个计算属性他等价于 @@ -115,18 +104,14 @@ typedef enum : unsigned char { 目的是为了方便设置具有重叠功能的栅格。 */ -@property(nonatomic, assign) MyGravity overlap; - +@property (nonatomic, assign) MyGravity overlap; -@property(nonatomic) CGRect gridRect; - - -@property(nonatomic, strong) NSDictionary* gridDictionary; +@property (nonatomic) CGRect gridRect; +@property (nonatomic, strong) NSDictionary *gridDictionary; ///////////////////////////////////////////////////////////////////////////////////////////// - //节点的报文解析部分。 /** 字典转节点。 @@ -134,9 +119,7 @@ typedef enum : unsigned char { @param gridDictionary 数据 @param gridNode 节点 */ -+(void)translateGridDicionary:(NSDictionary *)gridDictionary toGridNode:(id)gridNode; - - ++ (void)translateGridDicionary:(NSDictionary *)gridDictionary toGridNode:(id)gridNode; /** 节点转换字典 @@ -144,10 +127,6 @@ typedef enum : unsigned char { @param gridNode 节点 @return 字典 */ -+(NSDictionary *)translateGridNode:(id)gridNode toGridDictionary:(NSMutableDictionary *)gridDictionary; ++ (NSDictionary *)translateGridNode:(id)gridNode toGridDictionary:(NSMutableDictionary *)gridDictionary; @end - - - - diff --git a/MyLayout/Lib/MyGridNode.m b/MyLayout/Lib/MyGridNode.m index 04259f6..37a2549 100644 --- a/MyLayout/Lib/MyGridNode.m +++ b/MyLayout/Lib/MyGridNode.m @@ -7,52 +7,41 @@ // #import "MyGridNode.h" -#import "MyLayoutDelegate.h" #import "MyBaseLayout.h" #import "MyGridLayout.h" - +#import "MyLayoutDelegate.h" ///////////////////////////////////////////////////////////////////////////////////////////// - - /** 为支持栅格触摸而建立的触摸委托派生类。 */ @interface MyGridNodeTouchEventDelegate : MyTouchEventDelegate -@property(nonatomic, weak) id grid; -@property(nonatomic, weak) CALayer *gridLayer; +@property (nonatomic, weak) id grid; +@property (nonatomic, weak) CALayer *gridLayer; -@property(nonatomic, assign) NSInteger tag; -@property(nonatomic, strong) id actionData; +@property (nonatomic, assign) NSInteger tag; +@property (nonatomic, strong) id actionData; --(instancetype)initWithLayout:(MyBaseLayout*)layout grid:(id)grid; +- (instancetype)initWithLayout:(MyBaseLayout *)layout grid:(id)grid; @end - @implementation MyGridNodeTouchEventDelegate --(instancetype)initWithLayout:(MyBaseLayout*)layout grid:(id)grid -{ +- (instancetype)initWithLayout:(MyBaseLayout *)layout grid:(id)grid { self = [super initWithLayout:layout]; - if (self != nil) - { + if (self != nil) { _grid = grid; } - return self; } - --(void)mySetTouchHighlighted -{ +- (void)mySetTouchHighlighted { //如果有高亮则建立一个高亮子层。 - if (self.layout.highlightedBackgroundColor != nil) - { - if (self.gridLayer == nil) - { + if (self.layout.highlightedBackgroundColor != nil) { + if (self.gridLayer == nil) { CALayer *layer = [[CALayer alloc] init]; layer.zPosition = -1; [self.layout.layer addSublayer:layer]; @@ -63,72 +52,57 @@ -(void)mySetTouchHighlighted } } --(void)myResetTouchHighlighted -{ +- (void)myResetTouchHighlighted { //恢复高亮,删除子图层 - if (self.gridLayer != nil) - { + if (self.gridLayer != nil) { [self.gridLayer removeFromSuperlayer]; } self.gridLayer = nil; } --(id)myActionSender -{ +- (id)myActionSender { return _grid; } --(void)dealloc -{ +- (void)dealloc { [self myResetTouchHighlighted]; } - @end +typedef struct _MyGridOptionalProperties1 { + uint32_t subGridType : 2; + uint32_t gravity : 16; + uint32_t placeholder : 1; + uint32_t anchor : 1; - -typedef struct _MyGridOptionalProperties1 -{ - uint32_t subGridType:2; - uint32_t gravity:16; - uint32_t placeholder:1; - uint32_t anchor:1; - -}MyGridOptionalProperties1; - +} MyGridOptionalProperties1; /** 为节省栅格的内存而构建的可选属性列表1:子栅格间距,栅格内边距,停靠方式。 */ -typedef struct _MyGridOptionalProperties2 -{ +typedef struct _MyGridOptionalProperties2 { //格子内子栅格的间距 CGFloat subviewSpace; //格子内视图的内边距。 UIEdgeInsets padding; //格子内子视图的对齐停靠方式。 -}MyGridOptionalProperties2; - - -@interface MyGridNode() +} MyGridOptionalProperties2; -@property(nonatomic, assign) MyGridOptionalProperties1 optionalProperties1; -@property(nonatomic, assign) MyGridOptionalProperties2 *optionalProperties2; -@property(nonatomic, strong) MyBorderlineLayerDelegate *borderlineLayerDelegate; -@property(nonatomic, strong) MyGridNodeTouchEventDelegate *touchEventDelegate; +@interface MyGridNode () +@property (nonatomic, assign) MyGridOptionalProperties1 optionalProperties1; +@property (nonatomic, assign) MyGridOptionalProperties2 *optionalProperties2; +@property (nonatomic, strong) MyBorderlineLayerDelegate *borderlineLayerDelegate; +@property (nonatomic, strong) MyGridNodeTouchEventDelegate *touchEventDelegate; @end - @implementation MyGridNode --(instancetype)initWithMeasure:(CGFloat)measure superGrid:(id)superGrid -{ +- (instancetype)initWithMeasure:(CGFloat)measure superGrid:(id)superGrid { self = [self init]; - if (self != nil) - { + if (self != nil) { _measure = measure; _subGrids = nil; _gridRect = CGRectZero; @@ -138,237 +112,178 @@ -(instancetype)initWithMeasure:(CGFloat)measure superGrid:(id)superG _touchEventDelegate = nil; memset(&_optionalProperties1, 0, sizeof(MyGridOptionalProperties1)); } - return self; } --(MyGridOptionalProperties2*)optionalProperties2 -{ - if (_optionalProperties2 == NULL) - { - _optionalProperties2 = (MyGridOptionalProperties2*)malloc(sizeof(MyGridOptionalProperties2)); +- (MyGridOptionalProperties2 *)optionalProperties2 { + if (_optionalProperties2 == NULL) { + _optionalProperties2 = (MyGridOptionalProperties2 *)malloc(sizeof(MyGridOptionalProperties2)); memset(_optionalProperties2, 0, sizeof(MyGridOptionalProperties2)); } - return _optionalProperties2; } --(void)dealloc -{ - if (_optionalProperties2 != NULL) +- (void)dealloc { + if (_optionalProperties2 != NULL) { free(_optionalProperties2); + } _optionalProperties2 = NULL; } -#pragma mark -- MyGridAction +#pragma mark-- MyGridAction --(NSInteger)tag -{ +- (NSInteger)tag { return _touchEventDelegate.tag; } --(void)setTag:(NSInteger)tag -{ - - if (_touchEventDelegate == nil && tag != 0) - { - _touchEventDelegate = [[MyGridNodeTouchEventDelegate alloc] initWithLayout:(MyBaseLayout*)[self gridLayoutView] grid:self]; +- (void)setTag:(NSInteger)tag { + if (_touchEventDelegate == nil && tag != 0) { + _touchEventDelegate = [[MyGridNodeTouchEventDelegate alloc] initWithLayout:(MyBaseLayout *)[self gridLayoutView] grid:self]; } - _touchEventDelegate.tag = tag; } --(id)actionData -{ +- (id)actionData { return _touchEventDelegate.actionData; } -- (NSString *)action -{ +- (NSString *)action { return NSStringFromSelector(_touchEventDelegate.action); } --(void)setActionData:(id)actionData -{ - if (_touchEventDelegate == nil && actionData != nil) - { - _touchEventDelegate = [[MyGridNodeTouchEventDelegate alloc] initWithLayout:(MyBaseLayout*)[self gridLayoutView] grid:self]; +- (void)setActionData:(id)actionData { + if (_touchEventDelegate == nil && actionData != nil) { + _touchEventDelegate = [[MyGridNodeTouchEventDelegate alloc] initWithLayout:(MyBaseLayout *)[self gridLayoutView] grid:self]; } - _touchEventDelegate.actionData = actionData; } - --(void)setTarget:(id)target action:(SEL)action -{ - if (_touchEventDelegate == nil && target != nil) - { - _touchEventDelegate = [[MyGridNodeTouchEventDelegate alloc] initWithLayout:(MyBaseLayout*)[self gridLayoutView] grid:self]; +- (void)setTarget:(id)target action:(SEL)action { + if (_touchEventDelegate == nil && target != nil) { + _touchEventDelegate = [[MyGridNodeTouchEventDelegate alloc] initWithLayout:(MyBaseLayout *)[self gridLayoutView] grid:self]; } - [_touchEventDelegate setTarget:target action:action]; - - // if (target == nil) - // _touchEventDelegate = nil; } - -- (NSDictionary *)gridDictionary -{ - return [MyGridNode translateGridNode:self toGridDictionary: - [NSMutableDictionary new]]; +- (NSDictionary *)gridDictionary { + return [MyGridNode translateGridNode:self + toGridDictionary:[NSMutableDictionary new]]; } -- (void)setGridDictionary:(NSDictionary *)gridDictionary -{ +- (void)setGridDictionary:(NSDictionary *)gridDictionary { [_subGrids removeAllObjects]; self.subGridsType = MySubGridsType_Unknown; - if (gridDictionary == nil) + if (gridDictionary == nil) { return; - + } [MyGridNode translateGridDicionary:gridDictionary toGridNode:self]; } +#pragma mark-- MyGrid - - -#pragma mark -- MyGrid - --(MySubGridsType)subGridsType -{ +- (MySubGridsType)subGridsType { return (MySubGridsType)_optionalProperties1.subGridType; } --(void)setSubGridsType:(MySubGridsType)subGridsType -{ +- (void)setSubGridsType:(MySubGridsType)subGridsType { _optionalProperties1.subGridType = subGridsType; } - --(MyGravity)gravity -{ +- (MyGravity)gravity { return (MyGravity)_optionalProperties1.gravity; } --(void)setGravity:(MyGravity)gravity -{ +- (void)setGravity:(MyGravity)gravity { _optionalProperties1.gravity = gravity; } - --(BOOL)placeholder -{ +- (BOOL)placeholder { return _optionalProperties1.placeholder == 1; } --(void)setPlaceholder:(BOOL)placeholder -{ +- (void)setPlaceholder:(BOOL)placeholder { _optionalProperties1.placeholder = placeholder ? 1 : 0; } --(BOOL)anchor -{ +- (BOOL)anchor { return _optionalProperties1.anchor; } --(void)setAnchor:(BOOL)anchor -{ +- (void)setAnchor:(BOOL)anchor { _optionalProperties1.anchor = anchor ? 1 : 0; } --(MyGravity)overlap -{ +- (MyGravity)overlap { return self.gravity; } --(void)setOverlap:(MyGravity)overlap -{ +- (void)setOverlap:(MyGravity)overlap { self.anchor = YES; self.measure = 0; self.gravity = overlap; } - - --(id)addRow:(CGFloat)measure -{ +- (id)addRow:(CGFloat)measure { //如果有子格子,但是第一个子格子不是行则报错误。 - if (self.subGridsType == MySubGridsType_Col) - { + if (self.subGridsType == MySubGridsType_Col) { NSAssert(0, @"oops! 当前的子格子是列格子,不能再添加行格子。"); } - MyGridNode *rowGrid = [[MyGridNode alloc] initWithMeasure:measure superGrid:self]; self.subGridsType = MySubGridsType_Row; [self.subGrids addObject:rowGrid]; return rowGrid; } --(id)addCol:(CGFloat)measure -{ +- (id)addCol:(CGFloat)measure { //如果有子格子,但是第一个子格子不是行则报错误。 - if (self.subGridsType == MySubGridsType_Row) - { + if (self.subGridsType == MySubGridsType_Row) { NSAssert(0, @"oops! 当前的子格子是行格子,不能再添加列格子。"); } - MyGridNode *colGrid = [[MyGridNode alloc] initWithMeasure:measure superGrid:self]; self.subGridsType = MySubGridsType_Col; [self.subGrids addObject:colGrid]; return colGrid; } --(id)addRowGrid:(id)grid -{ +- (id)addRowGrid:(id)grid { NSAssert(grid.superGrid == nil, @"oops!"); - + //如果有子格子,但是第一个子格子不是行则报错误。 - if (self.subGridsType == MySubGridsType_Col) - { + if (self.subGridsType == MySubGridsType_Col) { NSAssert(0, @"oops! 当前的子格子是列格子,不能再添加行格子。"); } - self.subGridsType = MySubGridsType_Row; [self.subGrids addObject:(id)grid]; - ((MyGridNode*)grid).superGrid = self; + ((MyGridNode *)grid).superGrid = self; return grid; } --(id)addColGrid:(id)grid -{ +- (id)addColGrid:(id)grid { NSAssert(grid.superGrid == nil, @"oops!"); - + //如果有子格子,但是第一个子格子不是行则报错误。 - if (self.subGridsType == MySubGridsType_Row) - { + if (self.subGridsType == MySubGridsType_Row) { NSAssert(0, @"oops! 当前的子格子是列格子,不能再添加行格子。"); } - self.subGridsType = MySubGridsType_Col; [self.subGrids addObject:(id)grid]; - ((MyGridNode*)grid).superGrid = self; + ((MyGridNode *)grid).superGrid = self; return grid; - } --(id)addRowGrid:(id)grid measure:(CGFloat)measure -{ - id gridNode = (id)[self addRowGrid:grid]; +- (id)addRowGrid:(id)grid measure:(CGFloat)measure { + id gridNode = (id)[self addRowGrid:grid]; gridNode.measure = measure; return gridNode; } --(id)addColGrid:(id)grid measure:(CGFloat)measure -{ - id gridNode = (id)[self addColGrid:grid]; +- (id)addColGrid:(id)grid measure:(CGFloat)measure { + id gridNode = (id)[self addColGrid:grid]; gridNode.measure = measure; return gridNode; } - - --(id)cloneGrid -{ +- (id)cloneGrid { MyGridNode *grid = [[MyGridNode alloc] initWithMeasure:self.measure superGrid:nil]; //克隆各种属性。 grid.subGridsType = self.subGridsType; @@ -377,389 +292,318 @@ -(void)setOverlap:(MyGravity)overlap grid.gravity = self.gravity; grid.tag = self.tag; grid.actionData = self.actionData; - if (self->_optionalProperties2 != NULL) - { + if (self->_optionalProperties2 != NULL) { grid.subviewSpace = self.subviewSpace; grid.padding = self.padding; } - + //拷贝分割线。 - if (self->_borderlineLayerDelegate != nil) - { + if (self->_borderlineLayerDelegate != nil) { grid.topBorderline = self.topBorderline; grid.bottomBorderline = self.bottomBorderline; grid.leadingBorderline = self.leadingBorderline; grid.trailingBorderline = self.trailingBorderline; } - + //拷贝事件处理。 - if (self->_touchEventDelegate != nil) - { + if (self->_touchEventDelegate != nil) { [grid setTarget:self->_touchEventDelegate.target action:self->_touchEventDelegate.action]; } - //克隆子节点。 - if (self.subGridsType != MySubGridsType_Unknown) - { - for (MyGridNode *subGrid in self.subGrids) - { - MyGridNode *newsubGrid = (MyGridNode*)[subGrid cloneGrid]; - if (self.subGridsType == MySubGridsType_Row) + if (self.subGridsType != MySubGridsType_Unknown) { + for (MyGridNode *subGrid in self.subGrids) { + MyGridNode *newsubGrid = (MyGridNode *)[subGrid cloneGrid]; + if (self.subGridsType == MySubGridsType_Row) { [grid addRowGrid:newsubGrid]; - else + } else { [grid addColGrid:newsubGrid]; + } } } - return grid; } - --(void)removeFromSuperGrid -{ +- (void)removeFromSuperGrid { [self.superGrid.subGrids removeObject:self]; - if (self.superGrid.subGrids.count == 0) - { + if (self.superGrid.subGrids.count == 0) { self.superGrid.subGridsType = MySubGridsType_Unknown; } - //如果可能销毁高亮层。 [_touchEventDelegate myResetTouchHighlighted]; _borderlineLayerDelegate = nil; self.superGrid = nil; } -#pragma mark -- MyGridNode +#pragma mark-- MyGridNode --(NSMutableArray>*)subGrids -{ - if (_subGrids == nil) - { +- (NSMutableArray> *)subGrids { + if (_subGrids == nil) { _subGrids = [NSMutableArray new]; } return _subGrids; } //格子内子栅格的间距 --(CGFloat)subviewSpace -{ - if (_optionalProperties2 == NULL) +- (CGFloat)subviewSpace { + if (_optionalProperties2 == NULL) { return 0; - else + } else { return _optionalProperties2->subviewSpace; + } } --(void)setSubviewSpace:(CGFloat)subviewSpace -{ +- (void)setSubviewSpace:(CGFloat)subviewSpace { self.optionalProperties2->subviewSpace = subviewSpace; } //格子内视图的内边距。 --(UIEdgeInsets)padding -{ - if (_optionalProperties2 == NULL) +- (UIEdgeInsets)padding { + if (_optionalProperties2 == NULL) { return UIEdgeInsetsZero; - else + } else { return _optionalProperties2->padding; - + } } --(void)setPadding:(UIEdgeInsets)padding -{ +- (void)setPadding:(UIEdgeInsets)padding { self.optionalProperties2->padding = padding; } - - - --(CGFloat)updateGridSize:(CGSize)superSize superGrid:(id)superGrid withMeasure:(CGFloat)measure -{ - if (superGrid.subGridsType == MySubGridsType_Col) - { +- (CGFloat)updateGridSize:(CGSize)superSize superGrid:(id)superGrid withMeasure:(CGFloat)measure { + if (superGrid.subGridsType == MySubGridsType_Col) { _gridRect.size.width = measure; _gridRect.size.height = superSize.height; - } - else - { + } else { _gridRect.size.width = superSize.width; _gridRect.size.height = measure; - } - return measure; } --(CGFloat)updateGridOrigin:(CGPoint)superOrigin superGrid:(id)superGrid withOffset:(CGFloat)offset -{ - if (superGrid.subGridsType == MySubGridsType_Col) - { +- (CGFloat)updateWrapGridSizeInSuperGrid:(id)superGrid withMeasure:(CGFloat)measure { + if (superGrid.subGridsType == MySubGridsType_Col) { + _gridRect.size.width = measure; + if (self.subGrids.count > 0) { + _gridRect.size.height = self.subGrids.firstObject.gridRect.size.height; + } + return _gridRect.size.height; + } else { + _gridRect.size.height = measure; + if (self.subGrids.count > 0) { + _gridRect.size.width = self.subGrids.firstObject.gridRect.size.width; + } + return _gridRect.size.width; + } + return 0.0f; +} + +- (CGFloat)updateGridOrigin:(CGPoint)superOrigin superGrid:(id)superGrid withOffset:(CGFloat)offset { + if (superGrid.subGridsType == MySubGridsType_Col) { _gridRect.origin.x = offset; _gridRect.origin.y = superOrigin.y; return _gridRect.size.width; - } - else if (superGrid.subGridsType == MySubGridsType_Row) - { + } else if (superGrid.subGridsType == MySubGridsType_Row) { _gridRect.origin.x = superOrigin.x; _gridRect.origin.y = offset; - return _gridRect.size.height; - } - else - { + } else { return 0; } - } - --(UIView*)gridLayoutView -{ +- (UIView *)gridLayoutView { return [[self superGrid] gridLayoutView]; } --(SEL)gridAction -{ +- (SEL)gridAction { return _touchEventDelegate.action; } - --(void)setBorderlineNeedLayoutIn:(CGRect)rect withLayer:(CALayer*)layer -{ +- (void)setBorderlineNeedLayoutIn:(CGRect)rect withLayer:(CALayer *)layer { [_borderlineLayerDelegate setNeedsLayoutIn:rect withLayer:layer]; } --(void)showBorderline:(BOOL)show -{ +- (void)showBorderline:(BOOL)show { _borderlineLayerDelegate.topBorderlineLayer.hidden = !show; _borderlineLayerDelegate.bottomBorderlineLayer.hidden = !show; _borderlineLayerDelegate.leadingBorderlineLayer.hidden = !show; _borderlineLayerDelegate.trailingBorderlineLayer.hidden = !show; - + //销毁高亮。。 按理来说不应该出现在这里的。。。。 - if (!show) + if (!show) { [_touchEventDelegate myResetTouchHighlighted]; - - for (MyGridNode *subGrid in self.subGrids) - { + } + for (MyGridNode *subGrid in self.subGrids) { [subGrid showBorderline:show]; } } - --(MyBorderline*)topBorderline -{ +- (MyBorderline *)topBorderline { return _borderlineLayerDelegate.topBorderline; } --(void)setTopBorderline:(MyBorderline *)topBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setTopBorderline:(MyBorderline *)topBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:[self gridLayoutView].layer]; } - _borderlineLayerDelegate.topBorderline = topBorderline; } - --(MyBorderline*)bottomBorderline -{ +- (MyBorderline *)bottomBorderline { return _borderlineLayerDelegate.bottomBorderline; } --(void)setBottomBorderline:(MyBorderline *)bottomBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setBottomBorderline:(MyBorderline *)bottomBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:[self gridLayoutView].layer]; } - _borderlineLayerDelegate.bottomBorderline = bottomBorderline; } - --(MyBorderline*)leftBorderline -{ +- (MyBorderline *)leftBorderline { return _borderlineLayerDelegate.leftBorderline; } --(void)setLeftBorderline:(MyBorderline *)leftBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setLeftBorderline:(MyBorderline *)leftBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:[self gridLayoutView].layer]; } - _borderlineLayerDelegate.leftBorderline = leftBorderline; } - --(MyBorderline*)rightBorderline -{ +- (MyBorderline *)rightBorderline { return _borderlineLayerDelegate.rightBorderline; } --(void)setRightBorderline:(MyBorderline *)rightBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setRightBorderline:(MyBorderline *)rightBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:[self gridLayoutView].layer]; } - _borderlineLayerDelegate.rightBorderline = rightBorderline; } - --(MyBorderline*)leadingBorderline -{ +- (MyBorderline *)leadingBorderline { return _borderlineLayerDelegate.leadingBorderline; } --(void)setLeadingBorderline:(MyBorderline *)leadingBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setLeadingBorderline:(MyBorderline *)leadingBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:[self gridLayoutView].layer]; } - _borderlineLayerDelegate.leadingBorderline = leadingBorderline; } - - --(MyBorderline*)trailingBorderline -{ +- (MyBorderline *)trailingBorderline { return _borderlineLayerDelegate.trailingBorderline; } --(void)setTrailingBorderline:(MyBorderline *)trailingBorderline -{ - if (_borderlineLayerDelegate == nil) - { +- (void)setTrailingBorderline:(MyBorderline *)trailingBorderline { + if (_borderlineLayerDelegate == nil) { _borderlineLayerDelegate = [[MyBorderlineLayerDelegate alloc] initWithLayoutLayer:[self gridLayoutView].layer]; } - _borderlineLayerDelegate.trailingBorderline = trailingBorderline; } - --(id)gridHitTest:(CGPoint)point -{ - +- (id)gridHitTest:(CGPoint)point { //如果不在范围内点击则直接返回 - if (!CGRectContainsPoint(self.gridRect, point)) + if (!CGRectContainsPoint(self.gridRect, point)) { return nil; - - + } //优先测试子视图。。然后再自己。。 - NSArray> * subGrids = nil; - if (self.subGridsType != MySubGridsType_Unknown) + NSArray> *subGrids = nil; + if (self.subGridsType != MySubGridsType_Unknown) { subGrids = self.subGrids; - - - for (id sbvGrid in subGrids) - { - id testGrid = [sbvGrid gridHitTest:point]; - if (testGrid != nil) + } + for (id sbvGrid in subGrids) { + id testGrid = [sbvGrid gridHitTest:point]; + if (testGrid != nil) { return testGrid; + } } - - if (_touchEventDelegate != nil) + + if (_touchEventDelegate != nil) { return self; - + } return nil; } -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -{ - if (_touchEventDelegate != nil && _touchEventDelegate.layout == nil) - { - _touchEventDelegate.layout = (MyBaseLayout*)[self gridLayoutView]; +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + if (_touchEventDelegate != nil && _touchEventDelegate.layout == nil) { + _touchEventDelegate.layout = (MyBaseLayout *)[self gridLayoutView]; } [_touchEventDelegate touchesBegan:touches withEvent:event]; } -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event -{ - if (_touchEventDelegate != nil && _touchEventDelegate.layout == nil) - { - _touchEventDelegate.layout = (MyBaseLayout*)[self gridLayoutView]; +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + if (_touchEventDelegate != nil && _touchEventDelegate.layout == nil) { + _touchEventDelegate.layout = (MyBaseLayout *)[self gridLayoutView]; } [_touchEventDelegate touchesMoved:touches withEvent:event]; } -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event -{ - if (_touchEventDelegate != nil && _touchEventDelegate.layout == nil) - { - _touchEventDelegate.layout = (MyBaseLayout*)[self gridLayoutView]; +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + if (_touchEventDelegate != nil && _touchEventDelegate.layout == nil) { + _touchEventDelegate.layout = (MyBaseLayout *)[self gridLayoutView]; } [_touchEventDelegate touchesEnded:touches withEvent:event]; } -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event -{ - if (_touchEventDelegate != nil && _touchEventDelegate.layout == nil) - { - _touchEventDelegate.layout = (MyBaseLayout*)[self gridLayoutView]; +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + if (_touchEventDelegate != nil && _touchEventDelegate.layout == nil) { + _touchEventDelegate.layout = (MyBaseLayout *)[self gridLayoutView]; } [_touchEventDelegate touchesCancelled:touches withEvent:event]; } - - //////////////////////////////////////////// - -+(void)translateGridDicionary:(NSDictionary *)gridDictionary toGridNode:(id)gridNode -{ ++ (void)translateGridDicionary:(NSDictionary *)gridDictionary toGridNode:(id)gridNode { id actionData = [gridDictionary objectForKey:kMyGridActionData]; [self createActionData:actionData gridNode:gridNode]; - + NSNumber *tag = [gridDictionary objectForKey:kMyGridTag]; [self createTag:tag.integerValue gridNode:gridNode]; - + NSString *action = [gridDictionary objectForKey:kMyGridAction]; [self createAction:action gridNode:gridNode]; - + NSString *padding = [gridDictionary objectForKey:kMyGridPadding]; [self createPadding:padding gridNode:gridNode]; - + NSNumber *space = [gridDictionary objectForKey:kMyGridSpace]; [self createSpace:space.doubleValue gridNode:gridNode]; - + NSNumber *placeholder = [gridDictionary objectForKey:kMyGridPlaceholder]; [self createPlaceholder:placeholder.boolValue gridNode:gridNode]; - - NSNumber *anchor = [gridDictionary objectForKey:kMyGridAnchor];; + + NSNumber *anchor = [gridDictionary objectForKey:kMyGridAnchor]; [self createAnchor:anchor.boolValue gridNode:gridNode]; - + NSString *overlap = [gridDictionary objectForKey:kMyGridOverlap]; [self createOverlap:overlap gridNode:gridNode]; NSString *gravity = [gridDictionary objectForKey:kMyGridGravity]; [self createGravity:gravity gridNode:gridNode]; - + NSDictionary *dictionary = [gridDictionary objectForKey:kMyGridTopBorderline]; [self createBorderline:dictionary gridNode:gridNode borderline:0]; - + dictionary = [gridDictionary objectForKey:kMyGridLeftBorderline]; [self createBorderline:dictionary gridNode:gridNode borderline:1]; - + dictionary = [gridDictionary objectForKey:kMyGridBottomBorderline]; [self createBorderline:dictionary gridNode:gridNode borderline:2]; - + dictionary = [gridDictionary objectForKey:kMyGridRightBorderline]; [self createBorderline:dictionary gridNode:gridNode borderline:3]; - + id tempCols = [gridDictionary objectForKey:kMyGridCols]; [self createCols:tempCols gridNode:gridNode]; - + id tempRows = [gridDictionary objectForKey:kMyGridRows]; [self createRows:tempRows gridNode:gridNode]; } - /** 添加行栅格,返回新的栅格。其中的measure可以设置如下的值: 1.大于等于1的常数,表示固定尺寸。 @@ -768,20 +612,19 @@ +(void)translateGridDicionary:(NSDictionary *)gridDictionary toGridNode:(id)gridNode -{ ++ (void)createActionData:(id)actionData gridNode:(id)gridNode { if (actionData) { gridNode.actionData = actionData; } } //tag:1 -+ (void)createTag:(NSInteger)tag gridNode:(id)gridNode -{ ++ (void)createTag:(NSInteger)tag gridNode:(id)gridNode { if (tag != 0) { gridNode.tag = tag; } } //action -+ (void)createAction:(NSString *)action gridNode:(id)gridNode -{ ++ (void)createAction:(NSString *)action gridNode:(id)gridNode { if (action.length > 0) { MyGridLayout *layout = (MyGridLayout *)[gridNode gridLayoutView]; SEL sel = NSSelectorFromString(action); @@ -817,111 +657,110 @@ + (void)createAction:(NSString *)action gridNode:(id)gridNode } //padding:"{10,10,10,10}" -+ (void)createPadding:(NSString *)padding gridNode:(id)gridNode -{ - if (padding.length > 0){ ++ (void)createPadding:(NSString *)padding gridNode:(id)gridNode { + if (padding.length > 0) { gridNode.padding = UIEdgeInsetsFromString(padding); } } //space:10.0 -+ (void)createSpace:(CGFloat)space gridNode:(id)gridNode -{ - if (space != 0.0){ ++ (void)createSpace:(CGFloat)space gridNode:(id)gridNode { + if (space != 0.0) { gridNode.subviewSpace = space; } } //palceholder:true/false -+ (void)createPlaceholder:(BOOL)placeholder gridNode:(id)gridNode -{ ++ (void)createPlaceholder:(BOOL)placeholder gridNode:(id)gridNode { if (placeholder) { gridNode.placeholder = placeholder; } } //anchor:true/false -+ (void)createAnchor:(BOOL)anchor gridNode:(id)gridNode -{ ++ (void)createAnchor:(BOOL)anchor gridNode:(id)gridNode { if (anchor) { gridNode.anchor = anchor; } } //overlap:@"top|bottom|left|right|centerX|centerY|width|height" -+ (void)createOverlap:(NSString *)overlap gridNode:(id)gridNode -{ ++ (void)createOverlap:(NSString *)overlap gridNode:(id)gridNode { MyGravity tempGravity = MyGravity_None; NSArray *array = [overlap componentsSeparatedByString:@"|"]; for (NSString *temp in array) { tempGravity |= [self returnGravity:temp]; } - if (tempGravity != MyGravity_None){ + if (tempGravity != MyGravity_None) { gridNode.overlap = tempGravity; } } //gravity:@"top|bottom|left|right|centerX|centerY|width|height" -+ (void)createGravity:(NSString *)gravity gridNode:(id)gridNode -{ ++ (void)createGravity:(NSString *)gravity gridNode:(id)gridNode { MyGravity tempGravity = MyGravity_None; NSArray *array = [gravity componentsSeparatedByString:@"|"]; for (NSString *temp in array) { tempGravity |= [self returnGravity:temp]; } - - if (tempGravity != MyGravity_None) + + if (tempGravity != MyGravity_None) { gridNode.gravity = tempGravity; + } } -+ (MyGravity)returnGravity:(NSString *)gravity -{ - ++ (MyGravity)returnGravity:(NSString *)gravity { if ([gravity rangeOfString:vMyGridGravityTop].location != NSNotFound) { - return MyGravity_Vert_Top; - }else if ([gravity rangeOfString:vMyGridGravityBottom].location != NSNotFound) { + return MyGravity_Vert_Top; + } else if ([gravity rangeOfString:vMyGridGravityBottom].location != NSNotFound) { return MyGravity_Vert_Bottom; - }else if ([gravity rangeOfString:vMyGridGravityLeft].location != NSNotFound) { + } else if ([gravity rangeOfString:vMyGridGravityLeft].location != NSNotFound) { return MyGravity_Horz_Left; - }else if ([gravity rangeOfString:vMyGridGravityRight].location != NSNotFound) { + } else if ([gravity rangeOfString:vMyGridGravityRight].location != NSNotFound) { return MyGravity_Horz_Right; - }else if ([gravity rangeOfString:vMyGridGravityLeading].location != NSNotFound) { + } else if ([gravity rangeOfString:vMyGridGravityLeading].location != NSNotFound) { return MyGravity_Horz_Leading; - }else if ([gravity rangeOfString:vMyGridGravityTrailing].location != NSNotFound) { + } else if ([gravity rangeOfString:vMyGridGravityTrailing].location != NSNotFound) { return MyGravity_Horz_Trailing; - }else if ([gravity rangeOfString:vMyGridGravityCenterX].location != NSNotFound) { + } else if ([gravity rangeOfString:vMyGridGravityCenterX].location != NSNotFound) { return MyGravity_Horz_Center; - }else if ([gravity rangeOfString:vMyGridGravityCenterY].location != NSNotFound) { + } else if ([gravity rangeOfString:vMyGridGravityCenterY].location != NSNotFound) { return MyGravity_Vert_Center; - }else if ([gravity rangeOfString:vMyGridGravityWidthFill].location != NSNotFound) { + } else if ([gravity rangeOfString:vMyGridGravityWidthFill].location != NSNotFound) { return MyGravity_Horz_Fill; - }else if ([gravity rangeOfString:vMyGridGravityHeightFill].location != NSNotFound) { + } else if ([gravity rangeOfString:vMyGridGravityHeightFill].location != NSNotFound) { return MyGravity_Vert_Fill; - }else{ + } else { return MyGravity_None; } } - //{"color":"#AAA",thick:1.0, head:1.0, tail:1.0, offset:1} borderline:{上,左,下,右} -+ (void)createBorderline:(NSDictionary *)dictionary gridNode:(id)gridNode borderline:(NSInteger)borderline -{ ++ (void)createBorderline:(NSDictionary *)dictionary gridNode:(id)gridNode borderline:(NSInteger)borderline { if (dictionary) { MyBorderline *line = [MyBorderline new]; NSString *color = [dictionary objectForKey:kMyGridBorderlineColor]; if (color) { line.color = [UIColor myColorWithHexString:color]; } - line.thick = [[dictionary objectForKey:kMyGridBorderlineThick] doubleValue]; + line.thick = [[dictionary objectForKey:kMyGridBorderlineThick] doubleValue]; line.headIndent = [[dictionary objectForKey:kMyGridBorderlineHeadIndent] doubleValue]; line.tailIndent = [[dictionary objectForKey:kMyGridBorderlineTailIndent] doubleValue]; line.offset = [[dictionary objectForKey:kMyGridBorderlineOffset] doubleValue]; line.dash = [[dictionary objectForKey:kMyGridBorderlineDash] doubleValue]; switch (borderline) { - case 0: gridNode.topBorderline = line; break; - case 1: gridNode.leftBorderline = line;break; - case 2: gridNode.bottomBorderline = line;break; - case 3: gridNode.rightBorderline = line;break; + case 0: + gridNode.topBorderline = line; + break; + case 1: + gridNode.leftBorderline = line; + break; + case 2: + gridNode.bottomBorderline = line; + break; + case 3: + gridNode.rightBorderline = line; + break; default: break; } @@ -929,28 +768,25 @@ + (void)createBorderline:(NSDictionary *)dictionary gridNode:(id)gri } //"cols":[{}] -+ (void)createCols:(NSArray*)cols gridNode:(id)gridNode -{ ++ (void)createCols:(NSArray *)cols gridNode:(id)gridNode { for (NSDictionary *dic in cols) { NSString *gridSize = [dic objectForKey:kMyGridSize]; CGFloat measure = [self createLayoutSize:gridSize]; - MyGridNode *temp = (MyGridNode*)[gridNode addCol:measure]; + MyGridNode *temp = (MyGridNode *)[gridNode addCol:measure]; [self translateGridDicionary:dic toGridNode:temp]; } } //"rows":[{}] -+ (void)createRows:(id)rows gridNode:(id)gridNode -{ ++ (void)createRows:(id)rows gridNode:(id)gridNode { for (NSDictionary *dic in rows) { NSString *gridSize = [dic objectForKey:kMyGridSize]; CGFloat measure = [self createLayoutSize:gridSize]; - MyGridNode *temp = (MyGridNode*)[gridNode addRow:measure]; + MyGridNode *temp = (MyGridNode *)[gridNode addRow:measure]; [self translateGridDicionary:dic toGridNode:temp]; } } - #pragma mark 节点转换字典 /** @@ -959,44 +795,43 @@ + (void)createRows:(id)rows gridNode:(id)gridNode @param gridNode 节点 @return 字典 */ -+ (NSDictionary *)translateGridNode:(id)gridNode toGridDictionary:(NSMutableDictionary *)gridDictionary -{ - if (gridNode == nil) return nil; - ++ (NSDictionary *)translateGridNode:(id)gridNode toGridDictionary:(NSMutableDictionary *)gridDictionary { + if (gridNode == nil) { + return nil; + } [self returnLayoutSizeWithGridNode:gridNode result:gridDictionary]; - + [self returnActionDataWithGridNode:gridNode result:gridDictionary]; - + [self returnTagWithGridNode:gridNode result:gridDictionary]; - + [self returnActionWithGridNode:gridNode result:gridDictionary]; - + [self returnPaddingWithGridNode:gridNode result:gridDictionary]; - + [self returnSpaceWithGridNode:gridNode result:gridDictionary]; - + [self returnPlaceholderWithGridNode:gridNode result:gridDictionary]; - + [self returnAnchorWithGridNode:gridNode result:gridDictionary]; - + [self returnOverlapWithGridNode:gridNode result:gridDictionary]; - + [self returnGravityWithGridNode:gridNode result:gridDictionary]; - + [self returnBorderlineWithGridNode:gridNode borderline:0 result:gridDictionary]; - + [self returnBorderlineWithGridNode:gridNode borderline:1 result:gridDictionary]; - + [self returnBorderlineWithGridNode:gridNode borderline:2 result:gridDictionary]; - + [self returnBorderlineWithGridNode:gridNode borderline:3 result:gridDictionary]; - + [self returnArrayColsRowsGridNode:gridNode result:gridDictionary]; - + return gridDictionary; } - /** 添加行栅格,返回新的栅格。其中的measure可以设置如下的值: 1.大于等于1的常数,表示固定尺寸。 @@ -1005,19 +840,18 @@ + (NSDictionary *)translateGridNode:(id)gridNode toGridDictionary:(N 4.MyLayoutSize.wrap 表示尺寸由子栅格包裹 5.MyLayoutSize.fill 表示占用栅格剩余的尺寸 */ -+ (void)returnLayoutSizeWithGridNode:(id)gridNode result:(NSMutableDictionary *)result -{ ++ (void)returnLayoutSizeWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { id value = nil; if (gridNode.measure == MyLayoutSize.wrap) { - value = vMyGridSizeWrap; - }else if (gridNode.measure == MyLayoutSize.fill) { - value = vMyGridSizeFill; - }else{ + value = vMyGridSizeWrap; + } else if (gridNode.measure == MyLayoutSize.fill) { + value = vMyGridSizeFill; + } else { if (gridNode.measure > 0 && gridNode.measure < 1) { - value = [NSString stringWithFormat:@"%@%%",(@(gridNode.measure * 100)).stringValue]; - }else if (gridNode.measure > -1 && gridNode.measure < 0) { - value = [NSString stringWithFormat:@"%@%%",(@(gridNode.measure * 100)).stringValue]; - }else{ + value = [NSString stringWithFormat:@"%@%%", (@(gridNode.measure * 100)).stringValue]; + } else if (gridNode.measure > -1 && gridNode.measure < 0) { + value = [NSString stringWithFormat:@"%@%%", (@(gridNode.measure * 100)).stringValue]; + } else { value = [NSNumber numberWithDouble:gridNode.measure]; } } @@ -1025,24 +859,21 @@ + (void)returnLayoutSizeWithGridNode:(id)gridNode result:(NSMutableD } //action-data 数据 -+ (void)returnActionDataWithGridNode:(id)gridNode result:(NSMutableDictionary *)result -{ ++ (void)returnActionDataWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { if (gridNode.actionData) { [result setObject:gridNode.actionData forKey:kMyGridActionData]; } } //tag:1 -+ (void)returnTagWithGridNode:(id)gridNode result:(NSMutableDictionary *)result -{ ++ (void)returnTagWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { if (gridNode.tag != 0) { [result setObject:[NSNumber numberWithInteger:gridNode.tag] forKey:kMyGridTag]; } } //action -+ (void)returnActionWithGridNode:(id)gridNode result:(NSMutableDictionary *)result -{ ++ (void)returnActionWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { SEL action = gridNode.gridAction; if (action) { [result setObject:NSStringFromSelector(action) forKey:kMyGridAction]; @@ -1050,74 +881,65 @@ + (void)returnActionWithGridNode:(id)gridNode result:(NSMutableDicti } //padding:"{10,10,10,10}" -+ (void)returnPaddingWithGridNode:(id)gridNode result:(NSMutableDictionary *)result -{ ++ (void)returnPaddingWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { NSString *temp = NSStringFromUIEdgeInsets(gridNode.padding); if (temp && ![temp isEqualToString:@"{0, 0, 0, 0}"]) { [result setObject:temp forKey:kMyGridPadding]; } } - - //space:10.0 -+ (void)returnSpaceWithGridNode:(id)gridNode result:(NSMutableDictionary *)result -{ ++ (void)returnSpaceWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { if (gridNode.subviewSpace != 0.0) { [result setObject:[NSNumber numberWithDouble:gridNode.subviewSpace] forKey:kMyGridSpace]; } } //palceholder:true/false -+ (void)returnPlaceholderWithGridNode:(id)gridNode result:(NSMutableDictionary *)result -{ ++ (void)returnPlaceholderWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { if (gridNode.placeholder) { [result setObject:[NSNumber numberWithBool:gridNode.placeholder] forKey:kMyGridPlaceholder]; } } //anchor:true/false -+ (void)returnAnchorWithGridNode:(id)gridNode result:(NSMutableDictionary *)result -{ ++ (void)returnAnchorWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { if (gridNode.anchor) { [result setObject:[NSNumber numberWithBool:gridNode.anchor] forKey:kMyGridAnchor]; } } //overlap:@"top|bottom|left|right|centerX|centerY|width|height" -+ (void)returnOverlapWithGridNode:(id)gridNode result:(NSMutableDictionary *)result -{ ++ (void)returnOverlapWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { MyGravity gravity = gridNode.overlap; - if (gravity != MyGravity_None) - { + if (gravity != MyGravity_None) { static NSDictionary *data = nil; - if (data == nil) - { + if (data == nil) { data = @{ - [NSNumber numberWithUnsignedShort:MyGravity_Vert_Top]:vMyGridGravityTop, - [NSNumber numberWithUnsignedShort:MyGravity_Vert_Bottom]:vMyGridGravityBottom, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Left]:vMyGridGravityLeft, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Right]:vMyGridGravityRight, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Leading]:vMyGridGravityLeading, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Trailing]:vMyGridGravityTrailing, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Center]:vMyGridGravityCenterX, - [NSNumber numberWithUnsignedShort:MyGravity_Vert_Center]:vMyGridGravityCenterY, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Fill]:vMyGridGravityWidthFill, - [NSNumber numberWithUnsignedShort:MyGravity_Vert_Fill]:vMyGridGravityHeightFill - }; + [NSNumber numberWithUnsignedShort:MyGravity_Vert_Top]: vMyGridGravityTop, + [NSNumber numberWithUnsignedShort:MyGravity_Vert_Bottom]: vMyGridGravityBottom, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Left]: vMyGridGravityLeft, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Right]: vMyGridGravityRight, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Leading]: vMyGridGravityLeading, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Trailing]: vMyGridGravityTrailing, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Center]: vMyGridGravityCenterX, + [NSNumber numberWithUnsignedShort:MyGravity_Vert_Center]: vMyGridGravityCenterY, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Fill]: vMyGridGravityWidthFill, + [NSNumber numberWithUnsignedShort:MyGravity_Vert_Fill]: vMyGridGravityHeightFill + }; } - + NSMutableArray *gravitystrs = [NSMutableArray new]; - MyGravity horzGravity = gravity & MyGravity_Vert_Mask; + MyGravity horzGravity = MYHORZGRAVITY(gravity); NSString *horzstr = data[@(horzGravity)]; - if (horzstr != nil) + if (horzstr != nil) { [gravitystrs addObject:horzstr]; - - MyGravity vertGravity = gravity & MyGravity_Horz_Mask; + } + MyGravity vertGravity = MYVERTGRAVITY(gravity); NSString *vertstr = data[@(vertGravity)]; - if (vertstr != nil) + if (vertstr != nil) { [gravitystrs addObject:vertstr]; - + } NSString *temp = [gravitystrs componentsJoinedByString:@"|"]; if (temp.length) { [result setObject:temp forKey:kMyGridOverlap]; @@ -1126,39 +948,36 @@ + (void)returnOverlapWithGridNode:(id)gridNode result:(NSMutableDict } //gravity:@"top|bottom|left|right|centerX|centerY|width|height" -+ (void)returnGravityWithGridNode:(id)gridNode result:(NSMutableDictionary *)result -{ ++ (void)returnGravityWithGridNode:(id)gridNode result:(NSMutableDictionary *)result { MyGravity gravity = gridNode.gravity; - if (gravity != MyGravity_None) - { + if (gravity != MyGravity_None) { static NSDictionary *data = nil; - if (data == nil) - { + if (data == nil) { data = @{ - [NSNumber numberWithUnsignedShort:MyGravity_Vert_Top]:vMyGridGravityTop, - [NSNumber numberWithUnsignedShort:MyGravity_Vert_Bottom]:vMyGridGravityBottom, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Left]:vMyGridGravityLeft, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Right]:vMyGridGravityRight, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Leading]:vMyGridGravityLeading, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Trailing]:vMyGridGravityTrailing, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Center]:vMyGridGravityCenterX, - [NSNumber numberWithUnsignedShort:MyGravity_Vert_Center]:vMyGridGravityCenterY, - [NSNumber numberWithUnsignedShort:MyGravity_Horz_Fill]:vMyGridGravityWidthFill, - [NSNumber numberWithUnsignedShort:MyGravity_Vert_Fill]:vMyGridGravityHeightFill - }; + [NSNumber numberWithUnsignedShort:MyGravity_Vert_Top]: vMyGridGravityTop, + [NSNumber numberWithUnsignedShort:MyGravity_Vert_Bottom]: vMyGridGravityBottom, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Left]: vMyGridGravityLeft, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Right]: vMyGridGravityRight, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Leading]: vMyGridGravityLeading, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Trailing]: vMyGridGravityTrailing, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Center]: vMyGridGravityCenterX, + [NSNumber numberWithUnsignedShort:MyGravity_Vert_Center]: vMyGridGravityCenterY, + [NSNumber numberWithUnsignedShort:MyGravity_Horz_Fill]: vMyGridGravityWidthFill, + [NSNumber numberWithUnsignedShort:MyGravity_Vert_Fill]: vMyGridGravityHeightFill + }; } - + NSMutableArray *gravitystrs = [NSMutableArray new]; - MyGravity horzGravity = gravity & MyGravity_Vert_Mask; + MyGravity horzGravity = MYHORZGRAVITY(gravity); NSString *horzstr = data[@(horzGravity)]; - if (horzstr != nil) + if (horzstr != nil) { [gravitystrs addObject:horzstr]; - - MyGravity vertGravity = gravity & MyGravity_Horz_Mask; + } + MyGravity vertGravity = MYVERTGRAVITY(gravity); NSString *vertstr = data[@(vertGravity)]; - if (vertstr != nil) + if (vertstr != nil) { [gravitystrs addObject:vertstr]; - + } NSString *temp = [gravitystrs componentsJoinedByString:@"|"]; if (temp.length) { [result setObject:temp forKey:kMyGridGravity]; @@ -1166,23 +985,32 @@ + (void)returnGravityWithGridNode:(id)gridNode result:(NSMutableDict } } - - //{"color":"#AAA",thick:1.0, head:1.0, tail:1.0, offset:1} borderline:{上,左,下,右} -+ (void)returnBorderlineWithGridNode:(id)gridNode borderline:(NSInteger)borderline result:(NSMutableDictionary *)result -{ ++ (void)returnBorderlineWithGridNode:(id)gridNode borderline:(NSInteger)borderline result:(NSMutableDictionary *)result { NSString *key = nil; MyBorderline *line = nil; switch (borderline) { - case 0: key = kMyGridTopBorderline;line = gridNode.topBorderline; break; - case 1: key = kMyGridLeftBorderline;line = gridNode.leftBorderline;break; - case 2: key = kMyGridBottomBorderline;line = gridNode.bottomBorderline;break; - case 3: key = kMyGridRightBorderline;line = gridNode.rightBorderline;break; + case 0: + key = kMyGridTopBorderline; + line = gridNode.topBorderline; + break; + case 1: + key = kMyGridLeftBorderline; + line = gridNode.leftBorderline; + break; + case 2: + key = kMyGridBottomBorderline; + line = gridNode.bottomBorderline; + break; + case 3: + key = kMyGridRightBorderline; + line = gridNode.rightBorderline; + break; default: break; } if (line) { - NSMutableDictionary *dictionary = [NSMutableDictionary new]; + NSMutableDictionary *dictionary = [NSMutableDictionary new]; if (line.color) { [dictionary setObject:[line.color myHexString] forKey:kMyGridBorderlineColor]; } @@ -1198,7 +1026,7 @@ + (void)returnBorderlineWithGridNode:(id)gridNode borderline:(NSInte if (line.offset != 0) { [dictionary setObject:[NSNumber numberWithDouble:line.offset] forKey:kMyGridBorderlineOffset]; } - if (line.dash != 0){ + if (line.dash != 0) { [dictionary setObject:[NSNumber numberWithDouble:line.offset] forKey:kMyGridBorderlineDash]; } [result setObject:dictionary forKey:key]; @@ -1206,111 +1034,94 @@ + (void)returnBorderlineWithGridNode:(id)gridNode borderline:(NSInte } //"cols":[{}] "rows":[{}] -+ (void)returnArrayColsRowsGridNode:(id)gridNode result:(NSMutableDictionary *)result -{ ++ (void)returnArrayColsRowsGridNode:(id)gridNode result:(NSMutableDictionary *)result { MySubGridsType subGridsType = gridNode.subGridsType; if (subGridsType != MySubGridsType_Unknown) { NSMutableArray *temp = [NSMutableArray arrayWithCapacity:gridNode.subGrids.count]; NSString *key = nil; switch (subGridsType) { - case MySubGridsType_Col: - { + case MySubGridsType_Col: { key = kMyGridCols; break; } - case MySubGridsType_Row: - { + case MySubGridsType_Row: { key = kMyGridRows; break; } default: break; } - for (id node in gridNode.subGrids) { + for (id node in gridNode.subGrids) { [temp addObject:[self translateGridNode:node toGridDictionary:[NSMutableDictionary new]]]; } - [result setObject:temp forKey:key]; + + if (key) { + [result setObject:temp forKey:key]; + } } } - - @end - @implementation UIColor (MyGrid) -static NSDictionary* myDefaultColors() -{ +static NSDictionary *myDefaultColors() { static NSDictionary *colors = nil; - if (colors == nil) - { + if (colors == nil) { colors = @{ - @"black":UIColor.blackColor, - @"darkgray":UIColor.darkGrayColor, - @"lightgray":UIColor.lightGrayColor, - @"white":UIColor.whiteColor, - @"gray":UIColor.grayColor, - @"red":UIColor.redColor, - @"green":UIColor.greenColor, - @"cyan":UIColor.cyanColor, - @"yellow":UIColor.yellowColor, - @"magenta":UIColor.magentaColor, - @"orange":UIColor.orangeColor, - @"purple":UIColor.purpleColor, - @"brown":UIColor.brownColor, - @"clear":UIColor.clearColor - }; - } - + @"black": UIColor.blackColor, + @"darkgray": UIColor.darkGrayColor, + @"lightgray": UIColor.lightGrayColor, + @"white": UIColor.whiteColor, + @"gray": UIColor.grayColor, + @"red": UIColor.redColor, + @"green": UIColor.greenColor, + @"cyan": UIColor.cyanColor, + @"yellow": UIColor.yellowColor, + @"magenta": UIColor.magentaColor, + @"orange": UIColor.orangeColor, + @"purple": UIColor.purpleColor, + @"brown": UIColor.brownColor, + @"clear": UIColor.clearColor + }; + } + return colors; } - -+ (UIColor *)myColorWithHexString:(NSString *)hexString -{ - if (hexString.length == 0) ++ (UIColor *)myColorWithHexString:(NSString *)hexString { + if (hexString.length == 0) { return nil; - + } //判断是否以#开头,如果不是则直接读取具体的颜色值。 - if ([hexString characterAtIndex:0] != '#') - { + if ([hexString characterAtIndex:0] != '#') { return [myDefaultColors() objectForKey:hexString.lowercaseString]; } - - if (hexString.length != 7 && hexString.length != 9) + if (hexString.length != 7 && hexString.length != 9) { return nil; - + } NSScanner *scanner = [NSScanner scannerWithString:[hexString substringFromIndex:1]]; unsigned int val = 0; [scanner scanHexInt:&val]; - - unsigned char blue = val & 0xFF; + + unsigned char blue = val & 0xFF; unsigned char green = (val >> 8) & 0xFF; unsigned char red = (val >> 16) & 0xFF; unsigned char alpha = hexString.length == 7 ? 0xFF : (val >> 24) & 0xFF; - - return [[UIColor alloc ] initWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f]; -} + return [[UIColor alloc] initWithRed:red / 255.0f green:green / 255.0f blue:blue / 255.0f alpha:alpha / 255.0f]; +} - (NSString *)myHexString { - CGFloat red = 0, green = 0, blue = 0, alpha = 1; - - if (![self getRed:&red green:&green blue:&blue alpha:&alpha]) + if (![self getRed:&red green:&green blue:&blue alpha:&alpha]) { return nil; - - if (alpha != 1) - { - return [NSString stringWithFormat:@"#%02X%02X%02X%02X", (int)(red * 255), (int)(green * 255), (int)(blue * 255), (int)(alpha * 255)]; } - else - { + if (alpha != 1) { + return [NSString stringWithFormat:@"#%02X%02X%02X%02X", (int)(red * 255), (int)(green * 255), (int)(blue * 255), (int)(alpha * 255)]; + } else { return [NSString stringWithFormat:@"#%02X%02X%02X", (int)(red * 255), (int)(green * 255), (int)(blue * 255)]; } } @end - - diff --git a/MyLayout/Lib/MyLayout.h b/MyLayout/Lib/MyLayout.h index 9269636..7d48a7f 100644 --- a/MyLayout/Lib/MyLayout.h +++ b/MyLayout/Lib/MyLayout.h @@ -14,19 +14,19 @@ // /* The MIT License (MIT) - + Copyright (c) 2015 YoungSoft - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -36,26 +36,25 @@ SOFTWARE. */ - -//Current version is 1.5.0, please open: https://github.com/youngsoft/MyLinearLayout/blob/master/CHANGELOG.md to show the changes. - +// Current version is 1.9.10, please open:https://github.com/youngsoft/MyLinearLayout/blob/master/CHANGELOG.md to show the changes. #ifndef MyLayout_MyLayout_h #define MyLayout_MyLayout_h - -#import "MyLayoutDef.h" -#import "MyDimeScale.h" -#import "MyLayoutPos.h" -#import "MyLayoutSize.h" -#import "MyLinearLayout.h" -#import "MyFrameLayout.h" +#import "MyPathLayout.h" #import "MyRelativeLayout.h" #import "MyTableLayout.h" -#import "MyFlowLayout.h" +#import "MyFlexLayout.h" #import "MyFloatLayout.h" -#import "MyPathLayout.h" +#import "MyFlowLayout.h" +#import "MyFrameLayout.h" #import "MyGridLayout.h" +#import "MyLinearLayout.h" +#import "MyLayoutPos.h" +#import "MyLayoutSize.h" +#import "MyLayoutDef.h" #import "MyMaker.h" +#import "MyDimeScale.h" #endif + diff --git a/MyLayout/Lib/MyLayoutDef.h b/MyLayout/Lib/MyLayoutDef.h index 30d3e2d..44354da 100644 --- a/MyLayout/Lib/MyLayoutDef.h +++ b/MyLayout/Lib/MyLayoutDef.h @@ -9,22 +9,18 @@ #import #import - - #ifndef MYDEPRECATED - #define MYDEPRECATED(desc) __attribute__((deprecated(desc))) +#define MYDEPRECATED(desc) __attribute__((deprecated(desc))) #endif - - //如果定义了这个宏则使用老的方法不告警,这个宏用于那些升级功能使用来老方法而不出现告警提示的场景。 #ifdef MY_USEOLDMETHODNOWARNING - #define MYMETHODDEPRECATED(desc) +#define MYMETHODDEPRECATED(desc) #else - #define MYMETHODDEPRECATED(desc) MYDEPRECATED(desc) +#define MYMETHODDEPRECATED(desc) MYDEPRECATED(desc) #endif @@ -32,31 +28,25 @@ *布局视图方向的枚举类型定义。用来指定布局内子视图的整体排列布局方向。 */ typedef enum : unsigned char { - MyOrientation_Vert = 0, /**垂直方向,布局视图内所有子视图整体从上到下排列布局*/ - MyOrientation_Horz = 1, /**水平方向,布局视图内所有子视图整体从左到右排列布局*/ - - - //兼容老版本而定义,请使用新的定义值。 - MyLayoutViewOrientation_Vert MYMETHODDEPRECATED("use MyOrientation_Vert to instead") = MyOrientation_Vert, - MyLayoutViewOrientation_Horz MYMETHODDEPRECATED("use MyOrientation_Horz to instead") = MyOrientation_Horz, - + /**垂直方向,布局视图内所有子视图整体从上到下排列布局*/ + MyOrientation_Vert = 0, + /**水平方向,布局视图内所有子视图整体从左到右排列布局*/ + MyOrientation_Horz = 1, } MyOrientation; -//为了兼容老版本而定义 -typedef MyOrientation MyLayoutViewOrientation MYMETHODDEPRECATED("use MyOrientation to instead"); - - /** *视图的可见性枚举类型定义。用来指定视图是否在布局中可见,他是对hidden属性的扩展设置。 */ typedef enum : unsigned char { - - MyVisibility_Visible, /**视图可见,等价于hidden = false*/ - MyVisibility_Invisible, /**视图隐藏,等价于hidden = true, 但是会在父布局视图中占位空白区域*/ - MyVisibility_Gone /**视图隐藏,等价于hidden = true, 但是不会在父视图中占位空白区域*/ - -}MyVisibility; + /**视图可见,等价于hidden = false*/ + MyVisibility_Visible, + /**视图隐藏,等价于hidden = true, 但是会在父布局视图中占位空白区域*/ + MyVisibility_Invisible, + /**视图隐藏,等价于hidden = true, 但是不会在父视图中占位空白区域*/ + MyVisibility_Gone + +} MyVisibility; /** *布局视图内所有子视图的停靠方向和填充拉伸属性以及对齐方式的枚举类型定义。 @@ -66,118 +56,218 @@ typedef enum : unsigned char { 在线性布局、流式布局、浮动布局中有一个gravity属性用来表示布局内所有子视图的停靠方向和填充拉伸属性;在流式布局中有一个arrangedGravity属性用来表示布局内每排子视图的对齐方式。 */ typedef enum : unsigned short { - - - - - MyGravity_None = 0, /**默认值,不停靠、不填充、不对齐。*/ - - - //水平方向 - MyGravity_Horz_Left = 1, /**左边停靠或者左对齐*/ - MyGravity_Horz_Center = 2, /**水平中心停靠或者水平居中对齐*/ - MyGravity_Horz_Right = 4, /**右边停靠或者右对齐*/ - MyGravity_Horz_Window_Center = 8, /**窗口水平中心停靠,表示在屏幕窗口的水平中心停靠*/ - MyGravity_Horz_Between = 16, /**水平间距拉伸*/ - MyGravity_Horz_Leading = 32, /**头部对齐,对于阿拉伯国家来说是和Right等价的,对于非阿拉伯国家则是和Left等价的*/ - MyGravity_Horz_Trailing = 64, /**尾部对齐,对于阿拉伯国家来说是和Left等价的,对于非阿拉伯国家则是和Right等价的*/ - MyGravity_Horz_Fill = MyGravity_Horz_Left | MyGravity_Horz_Center | MyGravity_Horz_Right, /**水平宽度填充*/ - MyGravity_Horz_Mask = 0xFF00, /**水平掩码,用来获取水平方向的枚举值*/ - - //垂直方向 - MyGravity_Vert_Top = 1 << 8, /**上边停靠或者上对齐*/ - MyGravity_Vert_Center = 2 << 8, /**垂直中心停靠或者垂直居中对齐*/ - MyGravity_Vert_Bottom = 4 << 8, /**下边停靠或者下边对齐*/ - MyGravity_Vert_Window_Center = 8 << 8, /**窗口垂直中心停靠,表示在屏幕窗口的垂直中心停靠*/ - MyGravity_Vert_Between = 16 << 8, /**垂直间距拉伸*/ - MyGravity_Vert_Baseline = 32 << 8, /**基线对齐,只支持水平线性布局,指定基线对齐必须要指定出一个基线标准的子视图*/ - MyGravity_Vert_Fill = MyGravity_Vert_Top | MyGravity_Vert_Center | MyGravity_Vert_Bottom, /**垂直高度填充*/ - MyGravity_Vert_Mask = 0x00FF, /**垂直掩码,用来获取垂直方向的枚举值*/ - - - MyGravity_Center = MyGravity_Horz_Center | MyGravity_Vert_Center, /**整体居中*/ - - - MyGravity_Fill = MyGravity_Horz_Fill | MyGravity_Vert_Fill, /**全部填充*/ - - - MyGravity_Between = MyGravity_Horz_Between | MyGravity_Vert_Between, /**全部拉伸*/ - - - //为了更正确的统一命名规范以及和TangramKit保持一致,下列属性定义设置为过期!!。您在更新版本后只需要统一将MyMarginGravity替换为MyGravity 即可 - MyMarginGravity_None MYMETHODDEPRECATED("use MyGravity_None to instead") = MyGravity_None, - MyMarginGravity_Horz_Left MYMETHODDEPRECATED("use MyGravity_Horz_Left to instead") = MyGravity_Horz_Left, - MyMarginGravity_Horz_Center MYMETHODDEPRECATED("use MyGravity_Horz_Center to instead") = MyGravity_Horz_Center, - MyMarginGravity_Horz_Right MYMETHODDEPRECATED("use MyGravity_Horz_Right to instead") = MyGravity_Horz_Right, - MyMarginGravity_Horz_Window_Center MYMETHODDEPRECATED("use MyGravity_Horz_Window_Center to instead") = MyGravity_Horz_Window_Center, - MyMarginGravity_Horz_Between MYMETHODDEPRECATED("use MyGravity_Horz_Between to instead") = MyGravity_Horz_Between, - MyMarginGravity_Horz_Fill MYMETHODDEPRECATED("use MyGravity_Horz_Fill to instead") = MyGravity_Horz_Fill, - MyMarginGravity_Horz_Mask MYMETHODDEPRECATED("use MyGravity_Horz_Mask to instead") = MyGravity_Horz_Mask, - MyMarginGravity_Vert_Top MYMETHODDEPRECATED("use MyGravity_Vert_Top to instead") = MyGravity_Vert_Top, - MyMarginGravity_Vert_Center MYMETHODDEPRECATED("use MyGravity_Vert_Center to instead") = MyGravity_Vert_Center, - MyMarginGravity_Vert_Bottom MYMETHODDEPRECATED("use MyGravity_Vert_Bottom to instead") = MyGravity_Vert_Bottom, - MyMarginGravity_Vert_Window_Center MYMETHODDEPRECATED("use MyGravity_Vert_Window_Center to instead") = MyGravity_Vert_Window_Center, - MyMarginGravity_Vert_Between MYMETHODDEPRECATED("use MyGravity_Vert_Between to instead") = MyGravity_Vert_Between, - MyMarginGravity_Vert_Fill MYMETHODDEPRECATED("use MyGravity_Vert_Fill to instead") = MyGravity_Vert_Fill, - MyMarginGravity_Vert_Mask MYMETHODDEPRECATED("use MyGravity_Vert_Mask to instead") = MyGravity_Vert_Mask, - MyMarginGravity_Center MYMETHODDEPRECATED("use MyGravity_Center to instead") = MyGravity_Center, - MyMarginGravity_Fill MYMETHODDEPRECATED("use MyGravity_Fill to instead") = MyGravity_Fill, - MyMarginGravity_Between MYMETHODDEPRECATED("use MyGravity_Between to instead") = MyGravity_Between - + + /**默认值,不停靠、不填充、不对齐。*/ + MyGravity_None = 0, + + //水平方向 + /**左边停靠或者左对齐*/ + MyGravity_Horz_Left = 1, + /**水平中心停靠或者水平居中对齐*/ + MyGravity_Horz_Center = 2, + /**右边停靠或者右对齐*/ + MyGravity_Horz_Right = 4, + /**窗口水平中心停靠,表示在屏幕窗口的水平中心停靠*/ + MyGravity_Horz_Window_Center = 8, + /**水平间距拉伸,并且头尾部分的间距是0, 如果只有一个子视图则变为左边停靠*/ + MyGravity_Horz_Between = 16, + /**头部对齐,对于阿拉伯国家来说是和Right等价的,对于非阿拉伯国家则是和Left等价的*/ + MyGravity_Horz_Leading = 32, + /**尾部对齐,对于阿拉伯国家来说是和Left等价的,对于非阿拉伯国家则是和Right等价的*/ + MyGravity_Horz_Trailing = 64, + /**水平间距环绕拉伸,并且头尾部分为其他部分间距的一半,如果只有一个子视图则变为水平居中停靠*/ + MyGravity_Horz_Around = 128, + /**水平间距等分拉伸,并且头尾部分和其他部分间距的一样, + 如果只有一个子视图则变为水平居中停靠*/ + MyGravity_Horz_Among = MyGravity_Horz_Between | MyGravity_Horz_Around, + /**水平宽度填充*/ + MyGravity_Horz_Fill = + MyGravity_Horz_Left | MyGravity_Horz_Center | MyGravity_Horz_Right, + + /**水平宽度拉伸,它跟宽度填充的区别是如果子视图指定了宽度(非布局子视图宽度自适应除外)则不会被拉伸*/ + MyGravity_Horz_Stretch = MyGravity_Horz_Left | MyGravity_Horz_Right, + /**水平掩码,用来获取垂直方向的枚举值*/ + MyGravity_Horz_Mask = 0xFF00, + + //垂直方向 + /**上边停靠或者上对齐*/ + MyGravity_Vert_Top = 1 << 8, + /**垂直中心停靠或者垂直居中对齐*/ + MyGravity_Vert_Center = 2 << 8, + /**下边停靠或者下边对齐*/ + MyGravity_Vert_Bottom = 4 << 8, + /**窗口垂直中心停靠,表示在屏幕窗口的垂直中心停靠*/ + MyGravity_Vert_Window_Center = 8 << 8, + /**垂直间距拉伸,并且头尾部分的间距是0, 如果只有一个子视图则变为上边停靠*/ + MyGravity_Vert_Between = 16 << 8, + /**基线对齐,只支持水平线性布局,指定基线对齐必须要指定出一个基线标准的子视图*/ + MyGravity_Vert_Baseline = 32 << 8, + /**垂直间距环绕拉伸,并且头尾部分为其他部分间距的一半, + 如果只有一个子视图则变为垂直居中停靠*/ + MyGravity_Vert_Around = 64 << 8, + /**垂直间距等分拉伸,并且头尾部分和其他部分间距的一样, + 如果只有一个子视图则变为垂直居中停靠*/ + MyGravity_Vert_Among = MyGravity_Vert_Between | MyGravity_Vert_Around, + /**垂直高度填充*/ + MyGravity_Vert_Fill = + MyGravity_Vert_Top | MyGravity_Vert_Center | MyGravity_Vert_Bottom, + /**垂直高度拉伸,它跟高度填充的区别是如果子视图指定了高度(非布局子视图高度自适应除外)则不会被拉伸*/ + MyGravity_Vert_Stretch = MyGravity_Vert_Top | MyGravity_Vert_Bottom, + /**垂直掩码,用来获取水平方向的枚举值*/ + MyGravity_Vert_Mask = 0x00FF, + + /**整体居中*/ + MyGravity_Center = MyGravity_Horz_Center | MyGravity_Vert_Center, + + /**全部填充*/ + MyGravity_Fill = MyGravity_Horz_Fill | MyGravity_Vert_Fill, + + /**全部拉伸*/ + MyGravity_Between = MyGravity_Horz_Between | MyGravity_Vert_Between, + } MyGravity; -//为了兼容老版本出现告警而定义。 -typedef MyGravity MyMarginGravity MYMETHODDEPRECATED("use MyGravity to instead"); +#define MYHORZGRAVITY(gravity) (gravity&MyGravity_Vert_Mask) +#define MYVERTGRAVITY(gravity) (gravity&MyGravity_Horz_Mask) +/** + 停靠对齐的生效策略,主要用于流式布局中的最后一行。 + */ +typedef enum : unsigned char { + /**不做任何停靠对齐*/ + MyGravityPolicy_No, + /**总是停靠对齐*/ + MyGravityPolicy_Always, + /**自动使用前面的停靠对齐策略*/ + MyGravityPolicy_Auto, +} MyGravityPolicy; /** *设置当将布局视图嵌入到UIScrollView以及其派生类时对UIScrollView的contentSize的调整设置模式的枚举类型定义。 *当将一个布局视图作为子视图添加到UIScrollView或者其派生类时(UITableView,UICollectionView除外)系统会自动调整和计算并设置其中的contentSize值。您可以使用布局视图的属性adjustScrollViewContentSizeMode来进行设置定义的枚举值。 */ -typedef enum :unsigned char -{ - MyAdjustScrollViewContentSizeModeAuto = 0, /**自动调整,在添加到UIScrollView之前(UITableView, UICollectionView除外)。如果值被设置Auto则在添加到父视图后自动会变为YES。*/ - MyAdjustScrollViewContentSizeModeNo = 1, /**不调整,任何加入到UIScrollView中的布局视图在尺寸变化时都不会调整和设置contentSize的值。*/ - MyAdjustScrollViewContentSizeModeYes = 2, /**会调整,任何加入到UIScrollView中的布局视图在尺寸变化时都会调整和设置contentSize的值。*/ - - - //下面为兼容老版本而定义,请使用新属性 - MyLayoutAdjustScrollViewContentSizeModeAuto MYMETHODDEPRECATED("use MyAdjustScrollViewContentSizeModeAuto to instead") = MyAdjustScrollViewContentSizeModeAuto, - MyLayoutAdjustScrollViewContentSizeModeNo MYMETHODDEPRECATED("use MyAdjustScrollViewContentSizeModeNo to instead") = MyAdjustScrollViewContentSizeModeNo, - MyLayoutAdjustScrollViewContentSizeModeYes MYMETHODDEPRECATED("use MyAdjustScrollViewContentSizeModeYes to instead") = MyAdjustScrollViewContentSizeModeYes, - -}MyAdjustScrollViewContentSizeMode; - -//为兼容老版本而定义 -typedef MyAdjustScrollViewContentSizeMode MyLayoutAdjustScrollViewContentSizeMode MYMETHODDEPRECATED("use MyAdjustScrollViewContentSizeMode to instead"); +typedef enum : unsigned char { + /**自动调整,在添加到UIScrollView之前(UITableView, + UICollectionView除外)。如果值被设置Auto则在添加到父视图后自动会变为YES。*/ + MyAdjustScrollViewContentSizeModeAuto = 0, + /**不调整,任何加入到UIScrollView中的布局视图在尺寸变化时都不会调整和设置contentSize的值。*/ + MyAdjustScrollViewContentSizeModeNo = 1, + /**会调整,任何加入到UIScrollView中的布局视图在尺寸变化时都会调整和设置contentSize的值。*/ + MyAdjustScrollViewContentSizeModeYes = 2, + +} MyAdjustScrollViewContentSizeMode; /** *用来设置当线性布局中的子视图的尺寸大于线性布局的尺寸时的子视图的压缩策略和压缩内容枚举类型定义。请参考线性布局的shrinkType属性的定义。 */ -typedef enum : NSUInteger { - MySubviewsShrink_None = 0, /**不压缩。*/ - MySubviewsShrink_Average = 1, /**平均压缩。*/ - MySubviewsShrink_Weight = 2, /**比例压缩。*/ - MySubviewsShrink_Auto = 4, /**自动压缩。这个属性只有在水平线性布局里面并且只有2个子视图的宽度等于自身时才有用。这个属性主要用来实现左右两个子视图根据自身内容来进行缩放,以便实现最佳的宽度空间利用。*/ - - //上面部分是压缩的策略,下面部分指定压缩的内容,因此一个shrinkType的指定时上面部分和下面部分的 | 操作。比如让间距平均压缩:MySubviewsShrink_Average | MySubviewsShrink_Space - MySubviewsShrink_Size = 0 << 4, /**只压缩尺寸,因为这里是0所以这部分可以不设置,为默认。*/ - MySubviewsShrink_Space = 1 << 4, /**只压缩间距。*/ - MySubviewsShrink_SizeAndSpace = 2 << 4 /**压缩尺寸和间距。暂时不支持!!!*/ - +typedef enum : unsigned char { + /**不压缩。*/ + MySubviewsShrink_None = 0, + /**平均压缩。*/ + MySubviewsShrink_Average = 1, + /**比例压缩。*/ + MySubviewsShrink_Weight = 2, + /**自动压缩。这个属性只有在水平线性布局里面并且只有2个子视图的宽度等于自身时才有用。这个属性主要用来实现左右两个子视图根据自身内容来进行缩放,以便实现最佳的宽度空间利用。*/ + MySubviewsShrink_Auto = 4, + + //上面部分是压缩的策略,下面部分指定压缩的内容,因此一个shrinkType的指定时上面部分和下面部分的 + //| 操作。比如让间距平均压缩:MySubviewsShrink_Average | + // MySubviewsShrink_Space + + /**只压缩尺寸,因为这里是0所以这部分可以不设置,为默认。*/ + MySubviewsShrink_Size = 0 << 4, + /**只压缩间距。*/ + MySubviewsShrink_Space = 1 << 4, + /**压缩尺寸和间距。暂时不支持!!!*/ + MySubviewsShrink_SizeAndSpace = 2 << 4 + } MySubviewsShrinkType; +/* + SizeClass的尺寸定义,用于定义苹果设备的各种屏幕的尺寸,对于任意一种设备来说某个纬度的尺寸都可以描述为:Any任意,Compact压缩,Regular常规 + 三种形式,比如下面就列出了苹果各种设备的SizeClass定义: + + iPhone4S,iPhone5/5s,iPhone6 + 竖屏:(w:Compact h:Regular) + 横屏:(w:Compact h:Compact) + iPhone6 Plus + 竖屏:(w:Compact h:Regular) + 横屏:(w:Regular h:Compact) + iPad + 竖屏:(w:Regular h:Regular) + 横屏:(w:Regular h:Regular) + Apple Watch + 竖屏:(w:Compact h:Compact) + 横屏:(w:Compact h:Compact) + + 我们可以专门为某种设备的SizeClass来设置具体的各种子视图和布局的约束,但是为了兼容多种设备,我们提出了SizeClass的继承关系,其中的继承关系如下: + + w:Compact h:Compact 继承 (w:Any h:Compact , w:Compact h:Any , w:Any h:Any) + w:Regular h:Compact 继承 (w:Any h:Compact , w:Regular h:Any , w:Any h:Any) + w:Compact h:Regular 继承 (w:Any h:Regular , w:Compact h:Any , w:Any h:Any) + w:Regular h:Regular 继承 (w:Any h:Regular , w:Regular h:Any , w:Any h:Any) + + + 也就是说设备当前是:w:Compact h:Compact + 则会找出某个视图是否定义了这个SizeClass的界面约束布局,如果没有则找w:Any + h:Compact。如果找到了 则使用,否则继续往上找,直到w:Any + h:Any这种尺寸,因为默认所有视图和布局视图的约束设置都是基于w:Any + h:Any的。所以总是会找到对应的视图定义的约束的。 + + 在上述的定义中我们发现了2个问题,一个就是没有一个明确来指定横屏和竖屏这种屏幕的情况;另外一个是iPad设备的宽度和高度都是regular,而无法区分横屏和竖屏。因此这里对 + MySizeClass新增加了两个定义:竖屏MySizeClass_Portrait和横屏MySizeClass_Landscape。我们可以用这两个SizeClass来定义全局横屏以及某类设备的横屏和竖屏 + + 在默认情况下现有的布局以及子视图的约束设置都是基于w:Any + h:Any的,如果我们要为某种SizeClass设置约束则可以调用视图的扩展方法: + + -(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass; + -(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass + copyFrom:(MySizeClass)srcSizeClass; + + 这两个方法需要传递一个宽度的MySizeClass定义和高度的MySizeClass定义,并通过 | + 运算符来组合。 比如: + + 1.想设置所有iPhone设备的横屏的约束 + UIView *lsc = [某视图 + fetchLayoutSizeClass:MySizeClass_wAny|MySizeClass_hCompact]; + + 2.想设置所有iPad设备的横屏的约束 + UIView *lsc = [某视图 fetchLayoutSizeClass: MySizeClass_wRegular | + MySizeClass_hRegular | MySizeClass_Landscape]; + + 3.想设置iphone6plus下的横屏的约束 + UIView *lsc = [某视图 + fetchLayoutSizeClass:MySizeClass_wRegular|MySizeClass_hCompact]; + + 4.想设置ipad下的约束 + UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_wRegular | + MySizeClass_hRegular]; + + 5.想设置所有设备下的约束,也是默认的视图的约束 + UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_wAny | + MySizeClass_hAny]; + + 6.所有设备的竖屏约束: + UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_Portrait]; + + 7.所有设备的横屏约束: + UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_Landscape]; + + fetchLayoutSizeClass虽然返回的是一个instancetype,但实际得到了一个MyLayoutSizeClass对象或者其派生类,而MyLayoutSizeClass类中又定义了跟UIView一样相同的布局方法,因此虽然是返回视图对象,并设置各种约束,但实际上是设置MyLayoutSizeClass对象的各种约束。 + + */ +typedef enum : unsigned char { + MySizeClass_wAny = 0, //宽度任意尺寸 + MySizeClass_wCompact = 1, //宽度压缩尺寸,这个属性在iOS8以下不支持 + MySizeClass_wRegular = 2, //宽度常规尺寸,这个属性在iOS8以下不支持 + MySizeClass_hAny = 0, //高度任意尺寸 + MySizeClass_hCompact = 1 << 2, //高度压缩尺寸,这个属性在iOS8以下不支持 + MySizeClass_hRegular = 2 << 2, //高度常规尺寸,这个属性在iOS8以下不支持 -//内部使用 -typedef enum : unsigned char -{ - MyLayoutValueType_Nil, - MyLayoutValueType_NSNumber, - MyLayoutValueType_LayoutDime, - MyLayoutValueType_LayoutPos, - MyLayoutValueType_Array, - MyLayoutValueType_UILayoutSupport, - MyLayoutValueType_SafeArea, -}MyLayoutValueType; + MySizeClass_Any = 0x0, //所有设备,等价于MySizeClass_wAny|MySizeClass_hAny + MySizeClass_Portrait = 0x40, //竖屏 + MySizeClass_Landscape = + 0x80, //横屏,注意横屏和竖屏不支持 | 运算操作,只能指定一个。 +} MySizeClass; diff --git a/MyLayout/Lib/MyLayoutDelegate.h b/MyLayout/Lib/MyLayoutDelegate.h index 0823384..df3b2f3 100644 --- a/MyLayout/Lib/MyLayoutDelegate.h +++ b/MyLayout/Lib/MyLayoutDelegate.h @@ -6,78 +6,67 @@ // Copyright © 2017年 YoungSoft. All rights reserved. // -#import #include "MyBorderline.h" +#import @class MyBaseLayout; /**绘制线条层委托实现类**/ #ifdef MAC_OS_X_VERSION_10_12 -@interface MyBorderlineLayerDelegate : NSObject +@interface MyBorderlineLayerDelegate : NSObject #else @interface MyBorderlineLayerDelegate : NSObject #endif -@property(nonatomic, strong) MyBorderline *topBorderline; /**顶部边界线*/ -@property(nonatomic, strong) MyBorderline *leadingBorderline; /**头部边界线*/ -@property(nonatomic, strong) MyBorderline *bottomBorderline; /**底部边界线*/ -@property(nonatomic, strong) MyBorderline *trailingBorderline; /**尾部边界线*/ -@property(nonatomic, strong) MyBorderline *leftBorderline; /**左边边界线*/ -@property(nonatomic, strong) MyBorderline *rightBorderline; /**左边边界线*/ - - -@property(nonatomic ,strong) CAShapeLayer *topBorderlineLayer; -@property(nonatomic ,strong) CAShapeLayer *leadingBorderlineLayer; -@property(nonatomic ,strong) CAShapeLayer *bottomBorderlineLayer; -@property(nonatomic ,strong) CAShapeLayer *trailingBorderlineLayer; +@property (nonatomic, strong) MyBorderline *topBorderline; /**顶部边界线*/ +@property (nonatomic, strong) MyBorderline *leadingBorderline; /**头部边界线*/ +@property (nonatomic, strong) MyBorderline *bottomBorderline; /**底部边界线*/ +@property (nonatomic, strong) MyBorderline *trailingBorderline; /**尾部边界线*/ +@property (nonatomic, strong) MyBorderline *leftBorderline; /**左边边界线*/ +@property (nonatomic, strong) MyBorderline *rightBorderline; /**右边边界线*/ +@property (nonatomic, strong) CAShapeLayer *topBorderlineLayer; +@property (nonatomic, strong) CAShapeLayer *leadingBorderlineLayer; +@property (nonatomic, strong) CAShapeLayer *bottomBorderlineLayer; +@property (nonatomic, strong) CAShapeLayer *trailingBorderlineLayer; --(instancetype)initWithLayoutLayer:(CALayer*)layoutLayer; - --(void)setNeedsLayoutIn:(CGRect)rect withLayer:(CALayer*)layer; +- (instancetype)initWithLayoutLayer:(CALayer *)layoutLayer; +- (void)setNeedsLayoutIn:(CGRect)rect withLayer:(CALayer *)layer; +- (void)updateAllBorderlineColor; @end - - - //触摸事件的委托代码基类。 @interface MyTouchEventDelegate : NSObject -@property(nonatomic, weak) MyBaseLayout *layout; -@property(nonatomic, weak) id target; -@property(nonatomic) SEL action; +@property (nonatomic, weak) MyBaseLayout *layout; +@property (nonatomic, weak) id target; +@property (nonatomic, assign) SEL action; --(instancetype)initWithLayout:(MyBaseLayout*)layout; +- (instancetype)initWithLayout:(MyBaseLayout *)layout; - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; --(void)setTarget:(id)target action:(SEL)action; --(void)setTouchDownTarget:(id)target action:(SEL)action; --(void)setTouchCancelTarget:(id)target action:(SEL)action; - +- (void)setTarget:(id)target action:(SEL)action; +- (void)setTouchDownTarget:(id)target action:(SEL)action; +- (void)setTouchCancelTarget:(id)target action:(SEL)action; //subclass override this method --(void)mySetTouchHighlighted; --(void)myResetTouchHighlighted; --(void)myResetTouchHighlighted2; --(id)myActionSender; - +- (void)mySetTouchHighlighted; +- (void)myResetTouchHighlighted; +- (void)myResetTouchHighlighted2; +- (id)myActionSender; @end - //布局视图的触摸委托代理。 @interface MyLayoutTouchEventDelegate : MyTouchEventDelegate -@property(nonatomic,strong) UIColor *highlightedBackgroundColor; - -@property(nonatomic,assign) CGFloat highlightedOpacity; - -@property(nonatomic,strong) UIImage *highlightedBackgroundImage; +@property (nonatomic, strong) UIColor *highlightedBackgroundColor; +@property (nonatomic, assign) CGFloat highlightedOpacity; +@property (nonatomic, strong) UIImage *highlightedBackgroundImage; @end - diff --git a/MyLayout/Lib/MyLayoutDelegate.m b/MyLayout/Lib/MyLayoutDelegate.m index 19e0156..2759d03 100644 --- a/MyLayout/Lib/MyLayoutDelegate.m +++ b/MyLayout/Lib/MyLayoutDelegate.m @@ -10,630 +10,509 @@ #import "MyBaseLayout.h" #import "MyLayoutMath.h" - -@implementation MyBorderlineLayerDelegate -{ +@implementation MyBorderlineLayerDelegate { __weak CALayer *_layoutLayer; CGRect _layoutRect; } --(id)initWithLayoutLayer:(CALayer*)layoutLayer -{ +- (id)initWithLayoutLayer:(CALayer *)layoutLayer { self = [self init]; - if (self != nil) - { + if (self != nil) { _layoutLayer = layoutLayer; _layoutRect = CGRectZero; } - return self; } --(void)dealloc -{ - if (_topBorderlineLayer != nil) - { +- (void)dealloc { + if (_topBorderlineLayer != nil) { _topBorderlineLayer.delegate = nil; [_topBorderlineLayer removeFromSuperlayer]; _topBorderlineLayer = nil; } - - if (_bottomBorderlineLayer != nil) - { + + if (_bottomBorderlineLayer != nil) { _bottomBorderlineLayer.delegate = nil; [_bottomBorderlineLayer removeFromSuperlayer]; _bottomBorderlineLayer = nil; } - - if (_leadingBorderlineLayer != nil) - { + + if (_leadingBorderlineLayer != nil) { _leadingBorderlineLayer.delegate = nil; [_leadingBorderlineLayer removeFromSuperlayer]; _leadingBorderlineLayer = nil; } - - if (_trailingBorderlineLayer != nil) - { + + if (_trailingBorderlineLayer != nil) { _trailingBorderlineLayer.delegate = nil; [_trailingBorderlineLayer removeFromSuperlayer]; _trailingBorderlineLayer = nil; } - - - } --(void)setTopBorderline:(MyBorderline *)topBorderline -{ - if (_topBorderline != topBorderline) - { +- (void)setTopBorderline:(MyBorderline *)topBorderline { + if (_topBorderline != topBorderline) { _topBorderline = topBorderline; - + CAShapeLayer *borderLayer = _topBorderlineLayer; [self updateBorderLayer:&borderLayer withBorderline:_topBorderline]; _topBorderlineLayer = borderLayer; - } } --(void)setLeadingBorderline:(MyBorderline *)leadingBorderline -{ - if (_leadingBorderline != leadingBorderline) - { +- (void)setLeadingBorderline:(MyBorderline *)leadingBorderline { + if (_leadingBorderline != leadingBorderline) { _leadingBorderline = leadingBorderline; - + CAShapeLayer *borderLayer = _leadingBorderlineLayer; [self updateBorderLayer:&borderLayer withBorderline:_leadingBorderline]; _leadingBorderlineLayer = borderLayer; } } --(void)setBottomBorderline:(MyBorderline *)bottomBorderline -{ - if (_bottomBorderline != bottomBorderline) - { +- (void)setBottomBorderline:(MyBorderline *)bottomBorderline { + if (_bottomBorderline != bottomBorderline) { _bottomBorderline = bottomBorderline; - + CAShapeLayer *borderLayer = _bottomBorderlineLayer; [self updateBorderLayer:&borderLayer withBorderline:_bottomBorderline]; _bottomBorderlineLayer = borderLayer; } } - --(void)setTrailingBorderline:(MyBorderline *)trailingBorderline -{ - if (_trailingBorderline != trailingBorderline) - { +- (void)setTrailingBorderline:(MyBorderline *)trailingBorderline { + if (_trailingBorderline != trailingBorderline) { _trailingBorderline = trailingBorderline; - + CAShapeLayer *borderLayer = _trailingBorderlineLayer; [self updateBorderLayer:&borderLayer withBorderline:_trailingBorderline]; _trailingBorderlineLayer = borderLayer; } - } --(MyBorderline*)leftBorderline -{ - if ([MyBaseLayout isRTL]) +- (MyBorderline *)leftBorderline { + if ([MyBaseLayout isRTL]) { return self.trailingBorderline; - else + } else { return self.leadingBorderline; + } } --(void)setLeftBorderline:(MyBorderline *)leftBorderline -{ - if ([MyBaseLayout isRTL]) +- (void)setLeftBorderline:(MyBorderline *)leftBorderline { + if ([MyBaseLayout isRTL]) { self.trailingBorderline = leftBorderline; - else + } else { self.leadingBorderline = leftBorderline; + } } - --(MyBorderline*)rightBorderline -{ - if ([MyBaseLayout isRTL]) +- (MyBorderline *)rightBorderline { + if ([MyBaseLayout isRTL]) { return self.leadingBorderline; - else + } else { return self.trailingBorderline; + } } --(void)setRightBorderline:(MyBorderline *)rightBorderline -{ - if ([MyBaseLayout isRTL]) +- (void)setRightBorderline:(MyBorderline *)rightBorderline { + if ([MyBaseLayout isRTL]) { self.leadingBorderline = rightBorderline; - else + } else { self.trailingBorderline = rightBorderline; + } } - - --(void)updateBorderLayer:(CAShapeLayer**)ppLayer withBorderline:(MyBorderline*)borderline -{ - - if (borderline == nil) - { - - if (*ppLayer != nil) - { +- (void)updateBorderLayer:(CAShapeLayer **)ppLayer withBorderline:(MyBorderline *)borderline { + if (borderline == nil) { + if (*ppLayer != nil) { [(*ppLayer) removeFromSuperlayer]; (*ppLayer).delegate = nil; *ppLayer = nil; } - } - else - { - - if ( *ppLayer == nil) - { + } else { + if (*ppLayer == nil) { *ppLayer = [[CAShapeLayer alloc] init]; (*ppLayer).zPosition = 10000; (*ppLayer).delegate = self; - if (_layoutLayer != nil) + if (_layoutLayer != nil) { [_layoutLayer addSublayer:*ppLayer]; + } } - + //如果是点线则是用path,否则就用背景色 - if (borderline.dash != 0) - { + if (borderline.dash != 0) { (*ppLayer).lineDashPhase = borderline.dash / 2; NSNumber *num = @(borderline.dash); - (*ppLayer).lineDashPattern = @[num,num]; - + (*ppLayer).lineDashPattern = @[num, num]; + (*ppLayer).strokeColor = borderline.color.CGColor; (*ppLayer).lineWidth = borderline.thick; (*ppLayer).backgroundColor = nil; - - } - else - { + } else { (*ppLayer).lineDashPhase = 0; (*ppLayer).lineDashPattern = nil; - + (*ppLayer).strokeColor = nil; (*ppLayer).lineWidth = 0; (*ppLayer).backgroundColor = borderline.color.CGColor; - } - [(*ppLayer) setNeedsLayout]; - } } - --(void)layoutSublayersOfLayer:(CAShapeLayer *)layer -{ - if (_layoutLayer == nil) +- (void)layoutSublayersOfLayer:(CAShapeLayer *)layer { + if (_layoutLayer == nil) { return; - + } CGPoint layoutPoint = _layoutRect.origin; - CGSize layoutSize = _layoutRect.size; //_layoutLayer.bounds.size; - - if (layoutSize.width == 0 || layoutSize.height == 0 || layer.isHidden) + CGSize layoutSize = _layoutRect.size; //_layoutLayer.bounds.size; + + if (layoutSize.width == 0 || layoutSize.height == 0 || layer.isHidden) { return; - + } CGRect layerRect; CGPoint fromPoint; CGPoint toPoint; CGFloat scale = [UIScreen mainScreen].scale; - - if (layer == self.leadingBorderlineLayer) - { + + if (layer == self.leadingBorderlineLayer) { layerRect = CGRectMake(self.leadingBorderline.offset + layoutPoint.x, self.leadingBorderline.headIndent + layoutPoint.y, - self.leadingBorderline.thick/scale, + self.leadingBorderline.thick / scale, layoutSize.height - self.leadingBorderline.headIndent - self.leadingBorderline.tailIndent); fromPoint = CGPointMake(0, 0); toPoint = CGPointMake(0, layerRect.size.height); - - } - else if (layer == self.trailingBorderlineLayer) - { + + } else if (layer == self.trailingBorderlineLayer) { layerRect = CGRectMake(layoutSize.width - self.trailingBorderline.thick / scale - self.trailingBorderline.offset + layoutPoint.x, self.trailingBorderline.headIndent + layoutPoint.y, self.trailingBorderline.thick / scale, layoutSize.height - self.trailingBorderline.headIndent - self.trailingBorderline.tailIndent); fromPoint = CGPointMake(0, 0); toPoint = CGPointMake(0, layerRect.size.height); - - } - else if (layer == self.topBorderlineLayer) - { + + } else if (layer == self.topBorderlineLayer) { layerRect = CGRectMake(self.topBorderline.headIndent + layoutPoint.x, self.topBorderline.offset + layoutPoint.y, layoutSize.width - self.topBorderline.headIndent - self.topBorderline.tailIndent, - self.topBorderline.thick/scale); + self.topBorderline.thick / scale); fromPoint = CGPointMake(0, 0); toPoint = CGPointMake(layerRect.size.width, 0); - } - else if (layer == self.bottomBorderlineLayer) - { + } else if (layer == self.bottomBorderlineLayer) { layerRect = CGRectMake(self.bottomBorderline.headIndent + layoutPoint.x, layoutSize.height - self.bottomBorderline.thick / scale - self.bottomBorderline.offset + layoutPoint.y, layoutSize.width - self.bottomBorderline.headIndent - self.bottomBorderline.tailIndent, - self.bottomBorderline.thick /scale); + self.bottomBorderline.thick / scale); fromPoint = CGPointMake(0, 0); toPoint = CGPointMake(layerRect.size.width, 0); - - } - else - { + + } else { layerRect = CGRectZero; fromPoint = CGPointZero; toPoint = CGPointZero; NSAssert(0, @"oops!"); } - - if ([MyBaseLayout isRTL]) - { + + if ([MyBaseLayout isRTL]) { layerRect.origin.x = layoutSize.width - layerRect.origin.x - layerRect.size.width; } - - //把动画效果取消。 - - - if (!_myCGRectEqual(layer.frame, layerRect)) - { - if (layer.lineDashPhase == 0) - { + if (!_myCGRectEqual(layer.frame, layerRect)) { + if (layer.lineDashPhase == 0) { layer.path = nil; - } - else - { + } else { CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, nil, fromPoint.x, fromPoint.y); - CGPathAddLineToPoint(path, nil, toPoint.x,toPoint.y); + CGPathAddLineToPoint(path, nil, toPoint.x, toPoint.y); layer.path = path; CGPathRelease(path); } layer.frame = layerRect; - } - } - -- (nullable id)actionForLayer:(CALayer *)layer forKey:(NSString *)event -{ +- (nullable id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { return [NSNull null]; } - --(void)setNeedsLayoutIn:(CGRect)rect withLayer:(CALayer*)layer -{ +- (void)setNeedsLayoutIn:(CGRect)rect withLayer:(CALayer *)layer { _layoutLayer = layer; _layoutRect = rect; - if (_topBorderlineLayer != nil) - { - if (_topBorderlineLayer.superlayer == nil) + if (_topBorderlineLayer != nil) { + if (_topBorderlineLayer.superlayer == nil) { [layer addSublayer:_topBorderlineLayer]; - - if (_topBorderlineLayer.isHidden) + } + if (_topBorderlineLayer.isHidden) { _topBorderlineLayer.hidden = NO; + } [_topBorderlineLayer setNeedsLayout]; } - - if (_bottomBorderlineLayer != nil) - { - if (_bottomBorderlineLayer.superlayer == nil) + + if (_bottomBorderlineLayer != nil) { + if (_bottomBorderlineLayer.superlayer == nil) { [layer addSublayer:_bottomBorderlineLayer]; - - if (_bottomBorderlineLayer.isHidden) + } + if (_bottomBorderlineLayer.isHidden) { _bottomBorderlineLayer.hidden = NO; + } [_bottomBorderlineLayer setNeedsLayout]; } - - if (_leadingBorderlineLayer != nil) - { - if (_leadingBorderlineLayer.superlayer == nil) + + if (_leadingBorderlineLayer != nil) { + if (_leadingBorderlineLayer.superlayer == nil) { [layer addSublayer:_leadingBorderlineLayer]; - - if (_leadingBorderlineLayer.isHidden) + } + if (_leadingBorderlineLayer.isHidden) { _leadingBorderlineLayer.hidden = NO; + } [_leadingBorderlineLayer setNeedsLayout]; } - - if (_trailingBorderlineLayer != nil) - { - if (_trailingBorderlineLayer.superlayer == nil) + + if (_trailingBorderlineLayer != nil) { + if (_trailingBorderlineLayer.superlayer == nil) { [layer addSublayer:_trailingBorderlineLayer]; - - if (_trailingBorderlineLayer.isHidden) + } + if (_trailingBorderlineLayer.isHidden) { _trailingBorderlineLayer.hidden = NO; + } [_trailingBorderlineLayer setNeedsLayout]; } - } +- (void)updateAllBorderlineColor { + [self updateBorderlineColorHelper:_topBorderlineLayer borderline:_topBorderline]; + [self updateBorderlineColorHelper:_bottomBorderlineLayer borderline:_bottomBorderline]; + [self updateBorderlineColorHelper:_leadingBorderlineLayer borderline:_leadingBorderline]; + [self updateBorderlineColorHelper:_trailingBorderlineLayer borderline:_trailingBorderline]; +} -@end +- (void)updateBorderlineColorHelper:(CAShapeLayer *)layer borderline:(MyBorderline *)borderline { + if (layer != nil && borderline != nil) { + if (borderline.dash != 0.0) { + layer.strokeColor = borderline.color.CGColor; + } else { + layer.backgroundColor = borderline.color.CGColor; + } + } +} +@end //布局的事件处理委托对象 -@implementation MyTouchEventDelegate -{ - __weak id _touchDownTarget; - SEL _touchDownAction; - - __weak id _touchCancelTarget; - SEL _touchCancelAction; - BOOL _hasDoCancel; - - - BOOL _forbidTouch; - BOOL _canCallAction; - CGPoint _beginPoint; - -} + +@interface MyTouchEventDelegate() + +@property (nonatomic, assign) BOOL hasDoCancel; +@property (nonatomic, assign) BOOL forbidTouch; +@property (nonatomic, assign) BOOL canCallAction; + +@property (nonatomic, weak) id touchDownTarget; +@property (nonatomic, assign) SEL touchDownAction; + +@property (nonatomic, weak) id touchCancelTarget; +@property (nonatomic, assign) SEL touchCancelAction; + +@property (nonatomic, assign) CGPoint beginPoint; + +@end + +@implementation MyTouchEventDelegate BOOL _hasBegin; -__weak MyBaseLayout * _currentLayout; +__weak MyBaseLayout *_currentLayout; --(instancetype)initWithLayout:(MyBaseLayout *)layout -{ +- (instancetype)initWithLayout:(MyBaseLayout *)layout { self = [self init]; - if (self != nil) - { + if (self != nil) { _layout = layout; _currentLayout = nil; } - return self; } -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -{ +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; - - if (_layout != nil && _target != nil && !_forbidTouch && touch.tapCount == 1 && !_hasBegin) - { + + if (_layout != nil && _target != nil && !_forbidTouch && !_hasBegin) { _hasBegin = YES; _currentLayout = _layout; _canCallAction = YES; _beginPoint = [touch locationInView:_layout]; - + [self mySetTouchHighlighted]; - + _hasDoCancel = NO; [self myPerformTouch:_touchDownTarget withAction:_touchDownAction]; } } -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event -{ - if (_layout != nil && _target != nil && _hasBegin && (_layout == _currentLayout || _currentLayout == nil)) - { - if (_canCallAction) - { - CGPoint pt = [((UITouch*)[touches anyObject]) locationInView:_layout]; - if (fabs(pt.x - _beginPoint.x) > 2 || fabs(pt.y - _beginPoint.y) > 2) - { +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + if (_layout != nil && _target != nil && _hasBegin && (_layout == _currentLayout || _currentLayout == nil)) { + if (_canCallAction) { + CGPoint pt = [((UITouch *)[touches anyObject]) locationInView:_layout]; + if (fabs(pt.x - _beginPoint.x) > 2 || fabs(pt.y - _beginPoint.y) > 2) { _canCallAction = NO; - - if (!_hasDoCancel) - { + if (!_hasDoCancel) { [self myPerformTouch:_touchCancelTarget withAction:_touchCancelAction]; - _hasDoCancel = YES; - } - } } } } --(void)doTargetAction:(UITouch*)touch -{ - +- (void)doTargetAction:(UITouch *)touch { [self myResetTouchHighlighted]; - + //距离太远则不会处理 CGPoint pt = [touch locationInView:_layout]; - if (CGRectContainsPoint(_layout.bounds, pt) && _action != nil && _canCallAction) - { + if (CGRectContainsPoint(_layout.bounds, pt) && _action != nil && _canCallAction) { [self myPerformTouch:_target withAction:_action]; - - } - else - { - if (!_hasDoCancel) - { + } else { + if (!_hasDoCancel) { [self myPerformTouch:_touchCancelTarget withAction:_touchCancelAction]; _hasDoCancel = YES; } - } - _forbidTouch = NO; - } - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event -{ - - if (_layout != nil && _target != nil && _hasBegin && (_layout == _currentLayout || _currentLayout == nil)) - { +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + if (_layout != nil && _target != nil && _hasBegin && (_layout == _currentLayout || _currentLayout == nil)) { //设置一个延时. _forbidTouch = YES; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [self performSelector:@selector(doTargetAction:) withObject:[touches anyObject] afterDelay:0.12]; + [self performSelector:@selector(doTargetAction:) + withObject:[touches anyObject] + afterDelay:0.12]; #pragma clang diagnostic pop - + _hasBegin = NO; _currentLayout = nil; } } -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event -{ - - if (_layout != nil && _target != nil && _hasBegin && (_layout == _currentLayout || _currentLayout == nil)) - { +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + if (_layout != nil && _target != nil && _hasBegin && (_layout == _currentLayout || _currentLayout == nil)) { [self myResetTouchHighlighted]; - _hasBegin = NO; _currentLayout = nil; - - if (!_hasDoCancel) - { + if (!_hasDoCancel) { [self myPerformTouch:_touchCancelTarget withAction:_touchCancelAction]; - _hasDoCancel = YES; } - } } - ///设置触摸时的高亮 --(void)mySetTouchHighlighted -{ +- (void)mySetTouchHighlighted { } //恢复触摸时的高亮。 --(void)myResetTouchHighlighted -{ +- (void)myResetTouchHighlighted { } --(void)myResetTouchHighlighted2 -{ - +- (void)myResetTouchHighlighted2 { } - --(id)myActionSender -{ +- (id)myActionSender { return _layout; } - --(void)myPerformTouch:(id)target withAction:(SEL)action -{ - if (_layout != nil && target != nil && action != nil && self.myActionSender != nil) - { +- (void)myPerformTouch:(id)target withAction:(SEL)action { + if (_layout != nil && target != nil && action != nil && self.myActionSender != nil) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [target performSelector:action withObject:[self myActionSender]]; + [target performSelector:action + withObject:[self myActionSender]]; #pragma clang diagnostic pop - } } --(void)setTarget:(id)target action:(SEL)action -{ +- (void)setTarget:(id)target action:(SEL)action { _target = target; _action = action; } --(void)setTouchDownTarget:(id)target action:(SEL)action -{ +- (void)setTouchDownTarget:(id)target action:(SEL)action { _touchDownTarget = target; _touchDownAction = action; } --(void)setTouchCancelTarget:(id)target action:(SEL)action -{ +- (void)setTouchCancelTarget:(id)target action:(SEL)action { _touchCancelTarget = target; _touchCancelAction = action; - } - @end - +@interface MyLayoutTouchEventDelegate() +@property (nonatomic, strong) UIColor *oldBackgroundColor; +@property (nonatomic, strong) UIImage *oldBackgroundImage; +@property (nonatomic, assign) CGFloat oldAlpha; +@end @implementation MyLayoutTouchEventDelegate -{ - UIColor *_oldBackgroundColor; - UIImage *_oldBackgroundImage; - CGFloat _oldAlpha; -} --(instancetype)initWithLayout:(MyBaseLayout *)layout -{ +- (instancetype)initWithLayout:(MyBaseLayout *)layout { self = [super initWithLayout:layout]; - if (self != nil) - { + if (self != nil) { _oldAlpha = 1; } - return self; } ///设置触摸时的高亮 --(void)mySetTouchHighlighted -{ - if (self.highlightedOpacity != 0) - { +- (void)mySetTouchHighlighted { + if (self.highlightedOpacity != 0) { _oldAlpha = self.layout.alpha; self.layout.alpha = 1 - self.highlightedOpacity; } - - if (self.highlightedBackgroundColor != nil) - { + + if (self.highlightedBackgroundColor != nil) { _oldBackgroundColor = self.layout.backgroundColor; self.layout.backgroundColor = self.highlightedBackgroundColor; } - - if (self.highlightedBackgroundImage != nil) - { + + if (self.highlightedBackgroundImage != nil) { _oldBackgroundImage = self.layout.backgroundImage; self.layout.backgroundImage = self.highlightedBackgroundImage; } - } //恢复触摸时的高亮。 --(void)myResetTouchHighlighted -{ - if (self.highlightedOpacity != 0) - { +- (void)myResetTouchHighlighted { + if (self.highlightedOpacity != 0) { self.layout.alpha = _oldAlpha; _oldAlpha = 1; } - - if (self.highlightedBackgroundColor != nil) - { + + if (self.highlightedBackgroundColor != nil) { self.layout.backgroundColor = _oldBackgroundColor; _oldBackgroundColor = nil; } - - - if (self.highlightedBackgroundImage != nil) - { + + if (self.highlightedBackgroundImage != nil) { self.layout.backgroundImage = _oldBackgroundImage; _oldBackgroundImage = nil; } - } --(void)myResetTouchHighlighted2 -{ - if (_oldAlpha != 1) - { +- (void)myResetTouchHighlighted2 { + if (_oldAlpha != 1) { self.layout.alpha = _oldAlpha; _oldAlpha = 1; } - - if (_oldBackgroundColor != nil) - { + + if (_oldBackgroundColor != nil) { self.layout.backgroundColor = _oldBackgroundColor; _oldBackgroundColor = nil; } - - if (_oldBackgroundImage != nil) - { + + if (_oldBackgroundImage != nil) { self.layout.backgroundImage = _oldBackgroundImage; _oldBackgroundImage = nil; } } @end - - diff --git a/MyLayout/Lib/MyLayoutInner.h b/MyLayout/Lib/MyLayoutInner.h index 693c116..c8db240 100644 --- a/MyLayout/Lib/MyLayoutInner.h +++ b/MyLayout/Lib/MyLayoutInner.h @@ -6,190 +6,117 @@ // Copyright (c) 2015年 YoungSoft. All rights reserved. // -#import "MyLayoutMath.h" #import "MyLayoutDef.h" +#import "MyLayoutMath.h" #import "MyLayoutPosInner.h" -#import "MyLayoutSizeInner.h" #import "MyLayoutSizeClass.h" +#import "MyLayoutSizeInner.h" -//视图在布局中的评估测量值 -@interface MyFrame : NSObject - -@property(nonatomic, assign) CGFloat top; -@property(nonatomic, assign) CGFloat leading; -@property(nonatomic, assign) CGFloat bottom; -@property(nonatomic, assign) CGFloat trailing; -@property(nonatomic, assign) CGFloat width; -@property(nonatomic, assign) CGFloat height; - -@property(nonatomic, weak) UIView *sizeClass; - -@property(nonatomic, assign, readonly) BOOL multiple; //是否设置了多个sizeclass - -@property(nonatomic, strong) NSMutableDictionary *sizeClasses; - -@property(nonatomic, assign) BOOL hasObserver; - --(void)reset; - -@property(nonatomic,assign) CGRect frame; +typedef struct _MyLayoutContext { -@end + BOOL isEstimate; + + MySizeClass sizeClass; + + MyGravity vertGravity; + MyGravity horzGravity; + MyLayoutEngine *layoutViewEngine; + NSMutableArray *subviewEngines; + + //布局视图相关属性。 + CGSize selfSize; + CGFloat paddingLeading; + CGFloat paddingTrailing; + CGFloat paddingTop; + CGFloat paddingBottom; + + CGFloat horzSpace; + CGFloat vertSpace; + +} MyLayoutContext; -@interface MyBaseLayout() +@interface MyBaseLayout () +@property (nonatomic, assign) BOOL isMyLayouting; //派生类重载这个函数进行布局 --(CGSize)calcLayoutRect:(CGSize)size isEstimate:(BOOL)isEstimate pHasSubLayout:(BOOL*)pHasSubLayout sizeClass:(MySizeClass)sizeClass sbs:(NSMutableArray*)sbs; - --(id)createSizeClassInstance; - +- (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context; -//判断margin是否是相对margin --(BOOL)myIsRelativePos:(CGFloat)margin; +- (id)createSizeClassInstance; --(MyGravity)myGetSubviewVertGravity:(UIView*)sbv sbvsc:(UIView*)sbvsc vertGravity:(MyGravity)vertGravity; - - --(void)myCalcVertGravity:(MyGravity)vert - sbv:(UIView *)sbv - sbvsc:(UIView*)sbvsc - paddingTop:(CGFloat)paddingTop - paddingBottom:(CGFloat)paddingBottom +- (CGFloat)myCalcSubview:(MyLayoutEngine *)subviewEngine + vertGravity:(MyGravity)vertGravity baselinePos:(CGFloat)baselinePos - selfSize:(CGSize)selfSize - pRect:(CGRect*)pRect; - --(MyGravity)myGetSubviewHorzGravity:(UIView*)sbv sbvsc:(UIView*)sbvsc horzGravity:(MyGravity)horzGravity; - - --(void)myCalcHorzGravity:(MyGravity)horz - sbv:(UIView *)sbv - sbvsc:(UIView*)sbvsc - paddingLeading:(CGFloat)paddingLeading - paddingTrailing:(CGFloat)paddingTrailing - selfSize:(CGSize)selfSize - pRect:(CGRect*)pRect; - --(void)myCalcSizeOfWrapContentSubview:(UIView*)sbv sbvsc:(UIView*)sbvsc sbvmyFrame:(MyFrame*)sbvmyFrame; - --(CGFloat)myHeightFromFlexedHeightView:(UIView*)sbv sbvsc:(UIView*)sbvsc inWidth:(CGFloat)width; + withContext:(MyLayoutContext *)context; --(CGFloat)myValidMeasure:(MyLayoutSize*)dime sbv:(UIView*)sbv calcSize:(CGFloat)calcSize sbvSize:(CGSize)sbvSize selfLayoutSize:(CGSize)selfLayoutSize; +- (CGFloat)myCalcSubview:(MyLayoutEngine *)subviewEngine + horzGravity:(MyGravity)horz + withContext:(MyLayoutContext *)context; --(CGFloat)myValidMargin:(MyLayoutPos*)pos sbv:(UIView*)sbv calcPos:(CGFloat)calcPos selfLayoutSize:(CGSize)selfLayoutSize; +- (CGFloat)mySubview:(MyViewTraits *)subviewTraits wrapHeightSizeFits:(CGSize)size withContext:(MyLayoutContext *)context; --(BOOL)myIsNoLayoutSubview:(UIView*)sbv; +- (CGFloat)myGetBoundLimitMeasure:(MyLayoutSize *)anchor subview:(UIView *)subview anchorType:(MyLayoutAnchorType)anchorType subviewSize:(CGSize)subviewSize selfLayoutSize:(CGSize)selfLayoutSize isUBound:(BOOL)isUBound; --(NSMutableArray*)myGetLayoutSubviews; --(NSMutableArray*)myGetLayoutSubviewsFrom:(NSArray*)sbsFrom; +- (CGFloat)myValidMeasure:(MyLayoutSize *)anchor subview:(UIView *)subview calcSize:(CGFloat)calcSize subviewSize:(CGSize)subviewSize selfLayoutSize:(CGSize)selfLayoutSize; -//设置子视图的相对依赖的尺寸 --(void)mySetSubviewRelativeDimeSize:(MyLayoutSize*)dime selfSize:(CGSize)selfSize lsc:(MyBaseLayout*)lsc pRect:(CGRect*)pRect; +- (CGFloat)myValidMargin:(MyLayoutPos *)anchor subview:(UIView *)subview calcPos:(CGFloat)calcPos selfLayoutSize:(CGSize)selfLayoutSize; --(CGSize)myAdjustSizeWhenNoSubviews:(CGSize)size sbs:(NSArray*)sbs lsc:(MyBaseLayout*)lsc; +- (NSArray *)myUpdateCurrentSizeClass:(MySizeClass)sizeClass subviews:(NSArray *)subviews; -- (void)myAdjustLayoutSelfSize:(CGSize *)pSelfSize lsc:(MyBaseLayout*)lsc; --(void)myAdjustSubviewsRTLPos:(NSArray*)sbs selfWidth:(CGFloat)selfWidth; - --(MyGravity)myConvertLeftRightGravityToLeadingTrailing:(MyGravity)horzGravity; +- (CGSize)myAdjustLayoutViewSizeWithContext:(MyLayoutContext *)context; //为支持iOS11的safeArea而进行的padding的转化 --(CGFloat)myLayoutTopPadding; --(CGFloat)myLayoutBottomPadding; --(CGFloat)myLayoutLeftPadding; --(CGFloat)myLayoutRightPadding; --(CGFloat)myLayoutLeadingPadding; --(CGFloat)myLayoutTrailingPadding; - --(void)myAdjustSubviewWrapContentSet:(UIView*)sbv isEstimate:(BOOL)isEstimate sbvmyFrame:(MyFrame*)sbvmyFrame sbvsc:(UIView*)sbvsc selfSize:(CGSize)selfSize sizeClass:(MySizeClass)sizeClass pHasSubLayout:(BOOL*)pHasSubLayout; - +- (CGFloat)myLayoutPaddingTop; +- (CGFloat)myLayoutPaddingBottom; +- (CGFloat)myLayoutPaddingLeft; +- (CGFloat)myLayoutPaddingRight; +- (CGFloat)myLayoutPaddingLeading; +- (CGFloat)myLayoutPaddingTrailing; --(void)myCalcSubViewRect:(UIView*)sbv - sbvsc:(UIView*)sbvsc - sbvmyFrame:(MyFrame*)sbvmyFrame - lsc:(MyBaseLayout*)lsc - vertGravity:(MyGravity)vertGravity - horzGravity:(MyGravity)horzGravity - inSelfSize:(CGSize)selfSize - paddingTop:(CGFloat)paddingTop - paddingLeading:(CGFloat)paddingLeading - paddingBottom:(CGFloat)paddingBottom - paddingTrailing:(CGFloat)paddingTrailing - pMaxWrapSize:(CGSize*)pMaxWrapSize; - --(UIFont*)myGetSubviewFont:(UIView*)sbv; +- (void)myAdjustSizeSettingOfSubviewEngine:(MyLayoutEngine *)subviewEngine withContext:(MyLayoutContext *)context; --(MySizeClass)myGetGlobalSizeClass; +//根据子视图的宽度约束得到宽度值 +- (CGFloat)myWidthSizeValueOfSubviewEngine:(MyLayoutEngine *)subviewEngine + withContext:(MyLayoutContext *)context; +//根据子视图的高度约束得到高度值 +- (CGFloat)myHeightSizeValueOfSubviewEngine:(MyLayoutEngine *)subviewEngine + withContext:(MyLayoutContext *)context; -@end +- (void)myCalcRectOfSubviewEngine:(MyLayoutEngine *)subviewEngine + pMaxWrapSize:(CGSize *)pMaxWrapSize + withContext:(MyLayoutContext *)context; +- (UIFont *)myGetSubviewFont:(UIView *)subview; +- (MySizeClass)myGetGlobalSizeClass; -@interface MyViewSizeClass() +//给父布局视图机会来更改子布局视图的边界线的显示的rect +- (void)myHookSublayout:(MyBaseLayout *)sublayout borderlineRect:(CGRect *)pRect; -@property(nonatomic, strong,readonly) MyLayoutPos *topPosInner; -@property(nonatomic, strong,readonly) MyLayoutPos *leadingPosInner; -@property(nonatomic, strong,readonly) MyLayoutPos *bottomPosInner; -@property(nonatomic, strong,readonly) MyLayoutPos *trailingPosInner; -@property(nonatomic, strong,readonly) MyLayoutPos *centerXPosInner; -@property(nonatomic, strong,readonly) MyLayoutPos *centerYPosInner; -@property(nonatomic, strong,readonly) MyLayoutSize *widthSizeInner; -@property(nonatomic, strong,readonly) MyLayoutSize *heightSizeInner; - -@property(nonatomic, strong,readonly) MyLayoutPos *leftPosInner; -@property(nonatomic, strong,readonly) MyLayoutPos *rightPosInner; - -@property(nonatomic, strong,readonly) MyLayoutPos *baselinePosInner; - - -#if UIKIT_DEFINE_AS_PROPERTIES -@property(class, nonatomic, assign) BOOL isRTL; -#else -+(BOOL)isRTL; -+(void)setIsRTL:(BOOL)isRTL; -#endif +- (void)myCalcSubviewsWrapContentSize:(MyLayoutContext *)context withCustomSetting:(void (^)(MyViewTraits *subviewTraits))customSetting; @end +//为了减少布局视图不必要的内存占用,这里将一些可选数据保存到这个类中来 +@interface MyBaseLayoutOptionalData : NSObject -@interface UIView(MyLayoutExtInner) - -@property(nonatomic, strong, readonly) MyFrame *myFrame; +//特定场景处理的回调block +@property (nonatomic, copy) void (^beginLayoutBlock)(void); +@property (nonatomic, copy) void (^endLayoutBlock)(void); +@property (nonatomic, copy) void (^rotationToDeviceOrientationBlock)(MyBaseLayout *layout, BOOL isFirst, BOOL isPortrait); +@property (nonatomic, assign) int lastScreenOrientation; //为0为初始状态,为1为竖屏,为2为横屏。内部使用。 +//动画扩展 +@property (nonatomic, assign) NSTimeInterval aniDuration; +@property (nonatomic, assign) UIViewAnimationOptions aniOptions; +@property (nonatomic, copy) void (^aniCompletion)(BOOL finished); --(instancetype)myDefaultSizeClass; - --(instancetype)myBestSizeClass:(MySizeClass)sizeClass; - --(instancetype)myCurrentSizeClass; - --(instancetype)myCurrentSizeClassInner; - - --(instancetype)myCurrentSizeClassFrom:(MyFrame*)myFrame; - --(id)createSizeClassInstance; - - -@property(nonatomic, readonly) MyLayoutPos *topPosInner; -@property(nonatomic, readonly) MyLayoutPos *leadingPosInner; -@property(nonatomic, readonly) MyLayoutPos *bottomPosInner; -@property(nonatomic, readonly) MyLayoutPos *trailingPosInner; -@property(nonatomic, readonly) MyLayoutPos *centerXPosInner; -@property(nonatomic, readonly) MyLayoutPos *centerYPosInner; -@property(nonatomic, readonly) MyLayoutSize *widthSizeInner; -@property(nonatomic, readonly) MyLayoutSize *heightSizeInner; - -@property(nonatomic, readonly) MyLayoutPos *leftPosInner; -@property(nonatomic, readonly) MyLayoutPos *rightPosInner; +@end -@property(nonatomic, readonly) MyLayoutPos *baselinePosInner; -@end diff --git a/MyLayout/Lib/MyLayoutMath.h b/MyLayout/Lib/MyLayoutMath.h index 89c9279..625a3d0 100644 --- a/MyLayout/Lib/MyLayoutMath.h +++ b/MyLayout/Lib/MyLayoutMath.h @@ -8,11 +8,9 @@ #import - extern BOOL _myCGFloatErrorEqual(CGFloat f1, CGFloat f2, CGFloat error); extern BOOL _myCGFloatErrorNotEqual(CGFloat f1, CGFloat f2, CGFloat error); - extern BOOL _myCGFloatLess(CGFloat f1, CGFloat f2); extern BOOL _myCGFloatGreat(CGFloat f1, CGFloat f2); extern BOOL _myCGFloatEqual(CGFloat f1, CGFloat f2); @@ -23,7 +21,6 @@ extern BOOL _myCGSizeEqual(CGSize sz1, CGSize sz2); extern BOOL _myCGPointEqual(CGPoint pt1, CGPoint pt2); extern BOOL _myCGRectEqual(CGRect rect1, CGRect rect2); - extern CGFloat _myCGFloatRound(CGFloat f); extern CGRect _myCGRectRound(CGRect rect); extern CGSize _myCGSizeRound(CGSize size); @@ -35,3 +32,43 @@ extern CGFloat _myCGFloatMin(CGFloat a, CGFloat b); //a*b + c extern CGFloat _myCGFloatFma(CGFloat a, CGFloat b, CGFloat c); + +/** + * 定义优先级 + */ +typedef enum : short { + MyPriority_Low = 0, + MyPriority_Normal = 500, //默认值。 + MyPriority_High = 1000, +} MyPriority; + +//内部使用先放在这里。后续再移到合适位置。 +typedef enum : unsigned char { + MyLayoutValType_Nil, + MyLayoutValType_Number, + MyLayoutValType_LayoutSize, + MyLayoutValType_LayoutPos, + MyLayoutValType_Array, + MyLayoutValType_UILayoutSupport, + MyLayoutValType_SafeArea, + MyLayoutValType_Most, + MyLayoutValType_LayoutAnchorClone, + MyLayoutValType_Fill, + MyLayoutValType_Wrap, +} MyLayoutValType; + + +//位置尺寸类型 +typedef enum : unsigned short { + MyLayoutAnchorType_Width = 1|2|4, + MyLayoutAnchorType_Height = (1|2|4)<<8, + MyLayoutAnchorType_Leading = 32, + MyLayoutAnchorType_Trailing = 64, + MyLayoutAnchorType_Top = 1<<8, + MyLayoutAnchorType_Bottom = 4<<8, + MyLayoutAnchorType_CenterX = 2, + MyLayoutAnchorType_CenterY = 2<<8, + MyLayoutAnchorType_Baseline = 32<<8, + MyLayoutAnchorType_HorzMask = 0xFF00, + MyLayoutAnchorType_VertMask = 0x00FF +} MyLayoutAnchorType; diff --git a/MyLayout/Lib/MyLayoutMath.m b/MyLayout/Lib/MyLayoutMath.m index fd2b501..0d7a6e1 100644 --- a/MyLayout/Lib/MyLayoutMath.m +++ b/MyLayout/Lib/MyLayoutMath.m @@ -9,9 +9,7 @@ #import "MyLayoutMath.h" #import - -BOOL _myCGFloatErrorEqual(CGFloat f1, CGFloat f2, CGFloat error) -{ +BOOL _myCGFloatErrorEqual(CGFloat f1, CGFloat f2, CGFloat error) { #if CGFLOAT_IS_DOUBLE == 1 return fabs(f1 - f2) < error; #else @@ -19,8 +17,7 @@ BOOL _myCGFloatErrorEqual(CGFloat f1, CGFloat f2, CGFloat error) #endif } -BOOL _myCGFloatErrorNotEqual(CGFloat f1, CGFloat f2, CGFloat error) -{ +BOOL _myCGFloatErrorNotEqual(CGFloat f1, CGFloat f2, CGFloat error) { #if CGFLOAT_IS_DOUBLE == 1 return fabs(f1 - f2) > error; #else @@ -28,11 +25,7 @@ BOOL _myCGFloatErrorNotEqual(CGFloat f1, CGFloat f2, CGFloat error) #endif } - - - -BOOL _myCGFloatEqual(CGFloat f1, CGFloat f2) -{ +BOOL _myCGFloatEqual(CGFloat f1, CGFloat f2) { #if CGFLOAT_IS_DOUBLE == 1 return fabs(f1 - f2) < 0.0000001; #else @@ -40,8 +33,7 @@ BOOL _myCGFloatEqual(CGFloat f1, CGFloat f2) #endif } -BOOL _myCGFloatNotEqual(CGFloat f1, CGFloat f2) -{ +BOOL _myCGFloatNotEqual(CGFloat f1, CGFloat f2) { #if CGFLOAT_IS_DOUBLE == 1 return fabs(f1 - f2) > 0.0000001; #else @@ -49,18 +41,15 @@ BOOL _myCGFloatNotEqual(CGFloat f1, CGFloat f2) #endif } -BOOL _myCGFloatLess(CGFloat f1, CGFloat f2) -{ +BOOL _myCGFloatLess(CGFloat f1, CGFloat f2) { #if CGFLOAT_IS_DOUBLE == 1 return f2 - f1 > 0.0000001; #else return f2 - f1 > 0.0001f; #endif - } -BOOL _myCGFloatGreat(CGFloat f1, CGFloat f2) -{ +BOOL _myCGFloatGreat(CGFloat f1, CGFloat f2) { #if CGFLOAT_IS_DOUBLE == 1 return f1 - f2 > 0.0000001; #else @@ -68,9 +57,7 @@ BOOL _myCGFloatGreat(CGFloat f1, CGFloat f2) #endif } -BOOL _myCGFloatLessOrEqual(CGFloat f1, CGFloat f2) -{ - +BOOL _myCGFloatLessOrEqual(CGFloat f1, CGFloat f2) { #if CGFLOAT_IS_DOUBLE == 1 return f1 < f2 || fabs(f1 - f2) < 0.0000001; #else @@ -78,8 +65,7 @@ BOOL _myCGFloatLessOrEqual(CGFloat f1, CGFloat f2) #endif } -BOOL _myCGFloatGreatOrEqual(CGFloat f1, CGFloat f2) -{ +BOOL _myCGFloatGreatOrEqual(CGFloat f1, CGFloat f2) { #if CGFLOAT_IS_DOUBLE == 1 return f1 > f2 || fabs(f1 - f2) < 0.0000001; #else @@ -87,95 +73,80 @@ BOOL _myCGFloatGreatOrEqual(CGFloat f1, CGFloat f2) #endif } -BOOL _myCGSizeEqual(CGSize sz1, CGSize sz2) -{ +BOOL _myCGSizeEqual(CGSize sz1, CGSize sz2) { return _myCGFloatEqual(sz1.width, sz2.width) && _myCGFloatEqual(sz1.height, sz2.height); } -BOOL _myCGPointEqual(CGPoint pt1, CGPoint pt2) -{ +BOOL _myCGPointEqual(CGPoint pt1, CGPoint pt2) { return _myCGFloatEqual(pt1.x, pt2.x) && _myCGFloatEqual(pt1.y, pt2.y); } -BOOL _myCGRectEqual(CGRect rect1, CGRect rect2) -{ +BOOL _myCGRectEqual(CGRect rect1, CGRect rect2) { return _myCGSizeEqual(rect1.size, rect2.size) && _myCGPointEqual(rect1.origin, rect2.origin); } - -CGFloat _myCGFloatRound(CGFloat f) -{ - if (f == 0 || f == CGFLOAT_MAX || f == -CGFLOAT_MAX) +CGFloat _myCGFloatRound(CGFloat f) { + if (f == 0 || f == CGFLOAT_MAX || f == -CGFLOAT_MAX) { return f; - + } static CGFloat scale = 0; - if (scale == 0) - scale = [UIScreen mainScreen].scale; - - //因为设备点转化为像素时,如果偏移了半个像素点就有可能会产生虚化的效果,因此这里要加上1/4个点的偏移来防止这种虚化现象的产生。 - // floor((f + 0.5 / scale )* scale) 的目的是将设备逻辑点转化为有效的像素。再除以倍数则是转化为有效的设备逻辑点。 + if (scale == 0) { + scale = [UIScreen mainScreen].scale; + } + //因为设备点转化为像素时,如果偏移了半个像素点就有可能会产生虚化的效果,因此这里要将设备点先转化为像素点, + //然后再添加0.5个偏移取整后再除以倍数则是转化为有效的设备逻辑点。 #if CGFLOAT_IS_DOUBLE == 1 - if (f < 0) + if (f < 0) { return ceil(fma(f, scale, -0.5)) / scale; - else + } else { return floor(fma(f, scale, 0.5)) / scale; + } #else - if (f < 0) + if (f < 0) { return ceilf(fmaf(f, scale, -0.5f)) / scale; - else + } else { return floorf(fmaf(f, scale, 0.5f)) / scale; + } #endif } -CGRect _myLayoutCGRectRound(CGRect rect) -{ +CGRect _myLayoutCGRectRound(CGRect rect) { CGFloat x1 = rect.origin.x; CGFloat y1 = rect.origin.y; CGFloat w1 = rect.size.width; CGFloat h1 = rect.size.height; - - rect.origin.x = _myCGFloatRound(x1); + + rect.origin.x = _myCGFloatRound(x1); rect.origin.y = _myCGFloatRound(y1); - + CGFloat mx = _myCGFloatRound(x1 + w1); CGFloat my = _myCGFloatRound(y1 + h1); - rect.size.width = mx - rect.origin.x; rect.size.height = my - rect.origin.y; - return rect; - } - - -CGRect _myCGRectRound(CGRect rect) -{ - rect.origin.x = _myCGFloatRound(rect.origin.x); +CGRect _myCGRectRound(CGRect rect) { + rect.origin.x = _myCGFloatRound(rect.origin.x); rect.origin.y = _myCGFloatRound(rect.origin.y); rect.size.width = _myCGFloatRound(rect.size.width); rect.size.height = _myCGFloatRound(rect.size.height); - return rect; - } -CGSize _myCGSizeRound(CGSize size) -{ +CGSize _myCGSizeRound(CGSize size) { size.width = _myCGFloatRound(size.width); size.height = _myCGFloatRound(size.height); return size; } -CGPoint _myCGPointRound(CGPoint point) -{ +CGPoint _myCGPointRound(CGPoint point) { point.x = _myCGFloatRound(point.x); point.y = _myCGFloatRound(point.y); return point; } -CGFloat _myCGFloatMax(CGFloat a, CGFloat b) -{ +CGFloat _myCGFloatMax(CGFloat a, CGFloat b) { #if CGFLOAT_IS_DOUBLE == 1 return fmax(a, b); #else @@ -183,18 +154,15 @@ CGFloat _myCGFloatMax(CGFloat a, CGFloat b) #endif } -CGFloat _myCGFloatMin(CGFloat a, CGFloat b) -{ +CGFloat _myCGFloatMin(CGFloat a, CGFloat b) { #if CGFLOAT_IS_DOUBLE == 1 return fmin(a, b); #else return fminf(a, b); #endif - } -extern CGFloat _myCGFloatFma(CGFloat a, CGFloat b, CGFloat c) -{ +extern CGFloat _myCGFloatFma(CGFloat a, CGFloat b, CGFloat c) { #if CGFLOAT_IS_DOUBLE == 1 return fma(a, b, c); #else diff --git a/MyLayout/Lib/MyLayoutPos.h b/MyLayout/Lib/MyLayoutPos.h index 6a626b4..afec8de 100644 --- a/MyLayout/Lib/MyLayoutPos.h +++ b/MyLayout/Lib/MyLayoutPos.h @@ -8,6 +8,8 @@ #import "MyLayoutDef.h" +@class MyLayoutMostPos; + /** *视图的布局位置类,用于定位视图在布局视图中的位置。位置可分为水平方向的位置和垂直方向的位置,在视图定位时必要同时指定水平方向的位置和垂直方向的位置。水平方向的位置可以分为左,水平居中,右三种位置,垂直方向的位置可以分为上,垂直居中,下三种位置。 其中的offset方法可以用来设置布局位置的偏移值,一般只在equalTo设置为MyLayoutPos或者NSArray时配合使用。比如A.leftPos.equalTo(B.rightPos).offset(5)表示A在B的右边再偏移5个点 @@ -58,7 +60,6 @@ */ - /** 视图的布局位置类是用来描述视图与其他视图之间的位置关系的类。视图在进行定位时需要明确的指出其在父视图坐标轴上的水平位置(x轴上的位置)和垂直位置(y轴上的位置)。 视图的水平位置可以用左、水平中、右三个方位的值来描述,垂直位置则可以用上、垂直中、下三个方位的值来描述。 @@ -73,23 +74,7 @@ uBound.posVal,uBound.offsetVal是通过uBound方法设置。 @endcode */ -@interface MyLayoutPos : NSObject - -//because masonry defined macro MAS_SHORTHAND_GLOBALS. the equalTo, offset may conflict with below method. so -//if you used MyLayout and Masonry concurrently and you defined MAS_SHORTHAND_GLOBALS in masonry, then you can define MY_USEPREFIXMETHOD to solve the conflict. -#ifdef MY_USEPREFIXMETHOD --(MyLayoutPos* (^)(id val))myEqualTo; --(MyLayoutPos* (^)(CGFloat val))myOffset; --(MyLayoutPos* (^)(CGFloat val))myMin; --(MyLayoutPos* (^)(id posVal, CGFloat offset))myLBound; --(MyLayoutPos* (^)(CGFloat val))myMax; --(MyLayoutPos* (^)(id posVal, CGFloat offset))myUBound; --(void)myClear; - -#else - - -#if UIKIT_DEFINE_AS_PROPERTIES +@interface MyLayoutPos : NSObject /** 特殊的位置。只用在布局视图和非布局父视图之间的位置约束和没有导航条时的布局视图内子视图的padding设置上。 @@ -111,9 +96,9 @@ @code - //在一个没有导航条的界面中,因为iPhoneX和其他设备的状态栏的高度不一致。所以你可以让布局视图的topPadding设置如下: - layoutView.topPadding = MyLayoutPos.safeAreaMargin + 10; //顶部内边距是安全区域外加10。那么这个和设置如下的: - layoutView.topPadding = CGRectGetHeight([UIApplication sharedApplication].statusBarFrame) + 10; + //在一个没有导航条的界面中,因为iPhoneX和其他设备的状态栏的高度不一致。所以你可以让布局视图的paddingTop设置如下: + layoutView.paddingTop = MyLayoutPos.safeAreaMargin + 10; //顶部内边距是安全区域外加10。那么这个和设置如下的: + layoutView.paddingTop = CGRectGetHeight([UIApplication sharedApplication].statusBarFrame) + 10; //这两者之间的区别后者是一个设置好的常数,一旦你的设备要支持横竖屏则不管横屏下是否有状态栏都会缩进固定的内边距,而前者则会根据当前是否状态栏而进行动态调整。 //当然如果你的顶部就是要固定缩进状态栏的高度的话那么你可以直接直接用后者。 @@ -125,18 +110,25 @@ 需要注意的是这个值并不是一个真值,只是一个特殊值,不能用于读取。而且只能用于在MyLayoutPos的equalTo方法和布局视图上的padding属性上使用,其他地方使用后果未可知。 */ -@property(class, nonatomic, assign,readonly) CGFloat safeAreaMargin; - -#else +@property (class, nonatomic, assign, readonly) CGFloat safeAreaMargin; -+(CGFloat)safeAreaMargin; -#endif +//because masonry defined macro MAS_SHORTHAND_GLOBALS. the equalTo, offset may conflict with below method. so +//if you used MyLayout and Masonry concurrently and you defined MAS_SHORTHAND_GLOBALS in masonry, then you can define MY_USEPREFIXMETHOD to solve the conflict. +#ifdef MY_USEPREFIXMETHOD +- (MyLayoutPos * (^)(id val))myEqualTo; +- (MyLayoutPos * (^)(CGFloat val))myOffset; +- (MyLayoutPos * (^)(CGFloat val))myMin; +- (MyLayoutPos * (^)(id posVal, CGFloat offset))myLBound; +- (MyLayoutPos * (^)(CGFloat val))myMax; +- (MyLayoutPos * (^)(id posVal, CGFloat offset))myUBound; +- (void)myClear; +#else /** - 设置布局位置的值。参数val可以接收下面六种类型的值: + 设置布局位置的值。参数val可以接收下面七种类型的值: - 1. NSNumber表示位置是一个具体的数值。 + 1. NSNumber表示位置是一个具体的数值。也可以设置特殊值:MyLayoutPos.safeAreaMargin来表示安全区。 对于框架布局和线性布局中的子视图来说,如果数值是一个大于0而小于1的数值时表示的是相对的间距或者边距。如果是相对边距那么真实的位置 = 布局视图尺寸*相对边距值;如果是相对间距那么真实的位置 = 布局视图剩余尺寸 * 相对间距值 /(所有相对间距值的总和)。 2. MyLayoutPos表示位置依赖于另外一个位置。 @@ -147,10 +139,11 @@ 5. UIView表示位置依赖指定视图的对应位置。 - 6. nil表示位置的值被清除。 + 6.MyLayoutMostPos表示位置是取数组中所有元素位置中的最大的一个或者最小的一个元素的位置值, 只有相对布局中的子视图的位置才能设置这种类型。 + + 7. nil表示位置的值被清除。 */ --(MyLayoutPos* (^)(id val))equalTo; - +- (MyLayoutPos * (^)(id val))equalTo; /** 设置布局位置值的偏移量。 所谓偏移量是指布局位置在设置了某种值后增加或减少的偏移值。 @@ -170,13 +163,12 @@ 2.比如:A.rightPos.equalTo(B.rightPos).offset(5)表示A视图的右边位置等于B视图的右边位置再往左偏移5。 @endcode */ --(MyLayoutPos* (^)(CGFloat val))offset; - +- (MyLayoutPos * (^)(CGFloat val))offset; /** *设置位置的最小边界数值,min方法是lBound方法的简化版本。比如:A.min(10) <==> A.lBound(@10, 0) */ --(MyLayoutPos* (^)(CGFloat val))min; +- (MyLayoutPos * (^)(CGFloat val))min; /** *设置布局位置的最小边界值。 如果位置对象没有设置最小边界值,那么最小边界默认就是无穷小-CGFLOAT_MAX。lBound方法除了能设置为NSNumber外,还可以设置为MyLayoutPos值,并且还可以指定最小位置的偏移量值。只有在相对布局中的子视图的位置对象才能设置最小边界值为MyLayoutPos类型的值,其他类型布局中的子视图只支持NSNumber类型的最小边界值。 @@ -195,13 +187,12 @@ A.leftPos.lBound(B.rightPos, 20); //这时A是不必要指定明确的宽度的。 @endcode */ --(MyLayoutPos* (^)(id posVal, CGFloat offsetVal))lBound; - +- (MyLayoutPos * (^)(id posVal, CGFloat offsetVal))lBound; /** *设置位置的最大边界数值,max方法是uBound方法的简化版本。比如:A.max(10) <==> A.uBound(@10, 0) */ --(MyLayoutPos* (^)(CGFloat val))max; +- (MyLayoutPos * (^)(CGFloat val))max; /** 设置布局位置的最大边界值。 如果位置对象没有设置最大边界值,那么最大边界默认就是无穷大CGFLOAT_MAX。uBound方法除了能设置为NSNumber外,还可以设置为MyLayoutPos值,并且还可以指定最大位置的偏移量值。只有在相对布局中的子视图的位置对象才能设置最大边界值为MyLayoutPos类型的值,其他类型布局中的子视图只支持NSNumber类型的最大边界值。 @@ -224,13 +215,11 @@ offsetVal 指定位置边界值的偏移量。 */ --(MyLayoutPos* (^)(id posVal, CGFloat offsetVal))uBound; - - +- (MyLayoutPos * (^)(id posVal, CGFloat offsetVal))uBound; /** *清除所有设置的约束值,这样位置对象将不会再生效了。 */ --(void)clear; +- (void)clear; #endif @@ -238,14 +227,37 @@ *设置布局位置是否是活动的,默认是YES表示活动的,如果设置为NO则表示这个布局位置对象设置的约束值将不会起作用。 *active设置为YES和clear的相同点是位置对象设置的约束值都不会生效了,区别是前者不会清除所有设置的约束,而后者则会清除所有设置的约束。 */ -@property(nonatomic, assign, getter=isActive) BOOL active; +@property (nonatomic, assign, getter=isActive) BOOL active; +/** + 在布局视图的宽度或者高度是固定的情况下,当某一列或者行中的所有子视图的间距值和尺寸超过父视图的尺寸时子视图的间距压缩比重,默认值为0表示不压缩。数字越大表明压缩的比重越大。目前只有线性布局和框架布局和流式布局中的子视图支持这个属性,而且这特性只在子视图的间距超过布局视图时才有效。 + */ +@property (nonatomic, assign) CGFloat shrink; //通过如下属性获取上面方法设置的值。 -@property(nonatomic, strong, readonly) id posVal; -@property(nonatomic, assign, readonly) CGFloat offsetVal; -@property(nonatomic, assign, readonly) CGFloat minVal; -@property(nonatomic, assign, readonly) CGFloat maxVal; +@property (nonatomic, assign, readonly) CGFloat offsetVal; +@property (nonatomic, assign, readonly) CGFloat minVal; +@property (nonatomic, assign, readonly) CGFloat maxVal; + + +@end + +@interface MyLayoutPos (Clone) + +//从布局位置中克隆出一个位置对象来。这个克隆出来的位置值是源位置对象的值加上offsetVal。这个方法通常用于下面数组元素的构造 +- (MyLayoutPos * (^)(CGFloat offsetVal))clone; + +@end + +/** + 我们可以从一个数组中获取众多位置的最大最小的位置值。 + 这里要求数组的元素只能是MyLayoutPos或者NSNumber两种对象类型的值。如果是NSNumber类型则是一个绝对位置值,也就是包括布局视图padding设置的偏移值。 + */ +@interface NSArray (MyLayoutMostPos) +//从数组中得到最小的位置值。 +@property (nonatomic, readonly) MyLayoutMostPos *myMinPos; +//从数组中得到最大的位置值。 +@property (nonatomic, readonly) MyLayoutMostPos *myMaxPos; @end diff --git a/MyLayout/Lib/MyLayoutPos.m b/MyLayout/Lib/MyLayoutPos.m index 9f21eef..fd41ec9 100644 --- a/MyLayout/Lib/MyLayoutPos.m +++ b/MyLayout/Lib/MyLayoutPos.m @@ -7,286 +7,205 @@ // #import "MyLayoutPos.h" -#import "MyLayoutPosInner.h" #import "MyBaseLayout.h" -#import "MyLayoutMath.h" +#import "MyLayoutInner.h" +#import "MyLayoutPosInner.h" +@implementation MyLayoutPos -@implementation MyLayoutPos -{ - id _posVal; - CGFloat _offsetVal; - MyLayoutPos *_lBoundVal; - MyLayoutPos *_uBoundVal; -} - -+(CGFloat)safeAreaMargin -{ ++ (CGFloat)safeAreaMargin { //在2017年10月3号定义的一个数字,没有其他特殊意义。 return -20171003.0; } --(id)init -{ +- (id)init { self = [super init]; - if (self != nil) - { + if (self != nil) { _active = YES; _view = nil; - _pos = MyGravity_None; - _posVal = nil; - _posValType = MyLayoutValueType_Nil; + _val = nil; + _valType = MyLayoutValType_Nil; _offsetVal = 0; _lBoundVal = nil; _uBoundVal = nil; + _shrink = 0.0; } - + return self; } - - --(MyLayoutPos* (^)(id val))myEqualTo -{ - return ^id(id val){ - - return [self __equalTo:val]; +- (MyLayoutPos * (^)(id val))myEqualTo { + return ^id(id val) { + [self _myEqualTo:val]; + [self setNeedsLayout]; + return self; }; } - --(MyLayoutPos* (^)(CGFloat val))myOffset -{ - return ^id(CGFloat val){ - - return [self __offset:val]; +- (MyLayoutPos * (^)(CGFloat val))myOffset { + return ^id(CGFloat val) { + [self _myOffset:val]; + [self setNeedsLayout]; + return self; }; } - --(MyLayoutPos* (^)(CGFloat val))myMin -{ - return ^id(CGFloat val){ - - return [self __min:val]; +- (MyLayoutPos * (^)(CGFloat val))myMin { + return ^id(CGFloat val) { + [self _myMin:val]; + [self setNeedsLayout]; + return self; }; } - --(MyLayoutPos* (^)(CGFloat val))myMax -{ - return ^id(CGFloat val){ - - return [self __max:val]; +- (MyLayoutPos * (^)(CGFloat val))myMax { + return ^id(CGFloat val) { + [self _myMax:val]; + [self setNeedsLayout]; + return self; }; } --(MyLayoutPos* (^)(id posVal, CGFloat offset))myLBound -{ - return ^id(id posVal, CGFloat offset){ - - return [self __lBound:posVal offsetVal:offset]; +- (MyLayoutPos * (^)(id posVal, CGFloat offset))myLBound { + return ^id(id posVal, CGFloat offset) { + [self _myLBound:posVal offsetVal:offset]; + [self setNeedsLayout]; + return self; }; } --(MyLayoutPos* (^)(id posVal, CGFloat offset))myUBound -{ - return ^id(id posVal, CGFloat offset){ - - return [self __uBound:posVal offsetVal:offset]; +- (MyLayoutPos * (^)(id posVal, CGFloat offset))myUBound { + return ^id(id posVal, CGFloat offset) { + [self _myUBound:posVal offsetVal:offset]; + [self setNeedsLayout]; + return self; }; } - - - - --(void)myClear -{ - [self __clear]; +- (void)myClear { + [self _myClear]; + [self setNeedsLayout]; } - --(MyLayoutPos* (^)(id val))equalTo -{ - return ^id(id val){ - - return [self __equalTo:val]; - }; +- (MyLayoutPos * (^)(id val))equalTo { + return self.myEqualTo; } - --(MyLayoutPos* (^)(CGFloat val))offset -{ - return ^id(CGFloat val){ - - return [self __offset:val]; - }; +- (MyLayoutPos * (^)(CGFloat val))offset { + return self.myOffset; } - --(MyLayoutPos* (^)(CGFloat val))min -{ - return ^id(CGFloat val){ - - return [self __min:val]; - }; +- (MyLayoutPos * (^)(CGFloat val))min { + return self.myMin; } --(MyLayoutPos* (^)(id posVal, CGFloat offsetVal))lBound -{ - return ^id(id posVal, CGFloat offsetVal){ - - return [self __lBound:posVal offsetVal:offsetVal]; - }; +- (MyLayoutPos * (^)(id posVal, CGFloat offsetVal))lBound { + return self.myLBound; } - --(MyLayoutPos* (^)(CGFloat val))max -{ - return ^id(CGFloat val){ - - return [self __max:val]; - }; +- (MyLayoutPos * (^)(CGFloat val))max { + return self.myMax; } --(MyLayoutPos* (^)(id posVal, CGFloat offsetVal))uBound -{ - return ^id(id posVal, CGFloat offsetVal){ - - return [self __uBound:posVal offsetVal:offsetVal]; - }; +- (MyLayoutPos * (^)(id posVal, CGFloat offsetVal))uBound { + return self.myUBound; } - - - --(void)clear -{ - [self __clear]; +- (void)clear { + [self myClear]; } - --(void)setActive:(BOOL)active -{ - if (_active != active) - { - _active = active; - _lBoundVal.active = active; - _uBoundVal.active = active; - [self setNeedsLayout]; +- (void)setActive:(BOOL)active { + if (_active != active) { + [self _mySetActive:active]; + [self setNeedsLayout]; } } --(id)posVal -{ - return self.isActive ? _posVal : nil; +- (CGFloat)shrink { + return self.isActive ? _shrink : 0; } --(CGFloat)offsetVal -{ - return self.isActive? _offsetVal : 0; +- (id)val { + return self.isActive ? _val : nil; } --(CGFloat)minVal -{ - return self.isActive && _lBoundVal != nil ? _lBoundVal.posNumVal.doubleValue : -CGFLOAT_MAX; +- (CGFloat)offsetVal { + return self.isActive ? _offsetVal : 0; } --(CGFloat)maxVal -{ - return self.isActive && _uBoundVal != nil ? _uBoundVal.posNumVal.doubleValue : CGFLOAT_MAX; +- (CGFloat)minVal { + return self.isActive && _lBoundVal != nil ? _lBoundVal.numberVal.doubleValue : -CGFLOAT_MAX; } +- (CGFloat)maxVal { + return self.isActive && _uBoundVal != nil ? _uBoundVal.numberVal.doubleValue : CGFLOAT_MAX; +} +#pragma mark-- NSCopying -#pragma mark -- NSCopying - --(id)copyWithZone:(NSZone *)zone -{ - MyLayoutPos *lp = [[[self class] allocWithZone:zone] init]; - lp.view = self.view; - lp->_active = _active; - lp->_pos = _pos; - lp->_posValType = _posValType; - lp->_posVal = _posVal; - lp->_offsetVal = _offsetVal; - if (_lBoundVal != nil) - { - lp->_lBoundVal = [[[self class] allocWithZone:zone] init]; - lp->_lBoundVal->_active = _active; - [[lp->_lBoundVal __equalTo:_lBoundVal.posVal] __offset:_lBoundVal.offsetVal]; +- (id)copyWithZone:(NSZone *)zone { + MyLayoutPos *layoutPos = [[[self class] allocWithZone:zone] init]; + layoutPos.view = self.view; + layoutPos->_active = _active; + layoutPos->_shrink = _shrink; + layoutPos->_anchorType = _anchorType; + layoutPos->_valType = _valType; + layoutPos->_val = _val; + layoutPos->_offsetVal = _offsetVal; + if (_lBoundVal != nil) { + layoutPos->_lBoundVal = [[[self class] allocWithZone:zone] init]; + layoutPos->_lBoundVal->_active = _active; + [[layoutPos->_lBoundVal _myEqualTo:_lBoundVal.val] _myOffset:_lBoundVal.offsetVal]; } - if (_uBoundVal != nil) - { - lp->_uBoundVal = [[[self class] allocWithZone:zone] init]; - lp->_uBoundVal->_active = _active; - [[lp->_uBoundVal __equalTo:_uBoundVal.posVal] __offset:_uBoundVal.offsetVal]; + if (_uBoundVal != nil) { + layoutPos->_uBoundVal = [[[self class] allocWithZone:zone] init]; + layoutPos->_uBoundVal->_active = _active; + [[layoutPos->_uBoundVal _myEqualTo:_uBoundVal.val] _myOffset:_uBoundVal.offsetVal]; } - - return lp; + return layoutPos; } +#pragma mark-- Private Methods -#pragma mark -- Private Method - - --(NSNumber*)posNumVal -{ - if (_posVal == nil || !self.isActive) +- (NSNumber *)numberVal { + if (_val == nil || !self.isActive) { return nil; - - if (_posValType == MyLayoutValueType_NSNumber) - { - return _posVal; } - else if (_posValType == MyLayoutValueType_UILayoutSupport) - { - //只有在11以后并且是设置了safearea缩进才忽略UILayoutSupport。 + if (_valType == MyLayoutValType_Number) { + return _val; + } else if (_valType == MyLayoutValType_UILayoutSupport) { + //只有在11以后并且是设置了safearea缩进才忽略UILayoutSupport。 UIView *superview = self.view.superview; - if (superview != nil && - [UIDevice currentDevice].systemVersion.doubleValue >= 11 && - [superview isKindOfClass:[MyBaseLayout class]]) - { - UIRectEdge edge = ((MyBaseLayout*)superview).insetsPaddingFromSafeArea; - if ((_pos == MyGravity_Vert_Top && (edge & UIRectEdgeTop) == UIRectEdgeTop) || - (_pos == MyGravity_Vert_Bottom && (edge & UIRectEdgeBottom) == UIRectEdgeBottom)) - { + if (@available(iOS 11.0, *)) { + if (superview != nil && [superview isKindOfClass:[MyBaseLayout class]]) { + UIRectEdge edge = ((MyBaseLayout *)superview).insetsPaddingFromSafeArea; + if ((_anchorType == MyLayoutAnchorType_Top && (edge & UIRectEdgeTop) == UIRectEdgeTop) || + (_anchorType == MyLayoutAnchorType_Bottom && (edge & UIRectEdgeBottom) == UIRectEdgeBottom)) { return @(0); } } - - return @([((id)_posVal) length]); - } - else if (_posValType == MyLayoutValueType_SafeArea) - { + } + + return @([((id)_val) length]); + } else if (_valType == MyLayoutValType_SafeArea) { #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - + if (@available(iOS 11.0, *)) { - UIView *superView = self.view.superview; - /* UIEdgeInsets insets = superView.safeAreaInsets; - UIScrollView *superScrollView = nil; - if ([superView isKindOfClass:[UIScrollView class]]) - { - superScrollView = (UIScrollView*)superView; - - } - */ - - switch (_pos) { - case MyGravity_Horz_Leading: - return [MyBaseLayout isRTL]? @(superView.safeAreaInsets.right) : @(superView.safeAreaInsets.left); + switch (_anchorType) { + case MyLayoutAnchorType_Leading: + return [MyBaseLayout isRTL] ? @(superView.safeAreaInsets.right) : @(superView.safeAreaInsets.left); break; - case MyGravity_Horz_Trailing: - return [MyBaseLayout isRTL]? @(superView.safeAreaInsets.left) : @(superView.safeAreaInsets.right); + case MyLayoutAnchorType_Trailing: + return [MyBaseLayout isRTL] ? @(superView.safeAreaInsets.left) : @(superView.safeAreaInsets.right); break; - case MyGravity_Vert_Top: + case MyLayoutAnchorType_Top: return @(superView.safeAreaInsets.top); break; - case MyGravity_Vert_Bottom: + case MyLayoutAnchorType_Bottom: return @(superView.safeAreaInsets.bottom); break; default: @@ -295,423 +214,411 @@ -(NSNumber*)posNumVal } } #endif - if (_pos == MyGravity_Vert_Top) - { + if (_anchorType == MyLayoutAnchorType_Top) { return @([self findContainerVC].topLayoutGuide.length); - } - else if (_pos == MyGravity_Vert_Bottom) - { + } else if (_anchorType == MyLayoutAnchorType_Bottom) { return @([self findContainerVC].bottomLayoutGuide.length); } - return @(0); - } - - return nil; - } --(UIViewController*)findContainerVC -{ +- (UIViewController *)findContainerVC { UIViewController *vc = nil; - @try { - UIView *v = self.view; - while (v != nil) - { + while (v != nil) { vc = [v valueForKey:@"viewDelegate"]; - if (vc != nil) + if (vc != nil) { break; + } v = [v superview]; } - } @catch (NSException *exception) { - } - + return vc; } - - --(MyLayoutPos*)posRelaVal -{ - if (_posVal == nil || !self.isActive) +- (MyLayoutPos *)anchorVal { + if (_val == nil || !self.isActive) { return nil; - - if (_posValType == MyLayoutValueType_LayoutPos) - return _posVal; - + } + if (_valType == MyLayoutValType_LayoutPos) { + return _val; + } return nil; - } +- (NSArray *)arrayVal { + if (_val == nil || !self.isActive) { + return nil; + } + if (_valType == MyLayoutValType_Array) { + return _val; + } + return nil; +} --(NSArray*)posArrVal -{ - if (_posVal == nil || !self.isActive) +- (NSNumber *)mostVal { + if (_val == nil || !self.isActive) { return nil; - - if (_posValType == MyLayoutValueType_Array) - return _posVal; - + } + if (_valType == MyLayoutValType_Most) { + return @([((MyLayoutMostPos *)_val) getMostAxisValFrom:self]); + } return nil; - } --(MyLayoutPos*)lBoundVal -{ - if (_lBoundVal == nil) - { +- (MyLayoutPos *)lBoundVal { + if (_lBoundVal == nil) { _lBoundVal = [[MyLayoutPos alloc] init]; _lBoundVal->_active = _active; - [_lBoundVal __equalTo:@(-CGFLOAT_MAX)]; + [_lBoundVal _myEqualTo:@(-CGFLOAT_MAX)]; } return _lBoundVal; } --(MyLayoutPos*)uBoundVal -{ - if (_uBoundVal == nil) - { +- (MyLayoutPos *)uBoundVal { + if (_uBoundVal == nil) { _uBoundVal = [[MyLayoutPos alloc] init]; _uBoundVal->_active = _active; - [_uBoundVal __equalTo:@(CGFLOAT_MAX)]; + [_uBoundVal _myEqualTo:@(CGFLOAT_MAX)]; } - return _uBoundVal; } --(MyLayoutPos*)lBoundValInner -{ +- (MyLayoutPos *)lBoundValInner { return _lBoundVal; } --(MyLayoutPos*)uBoundValInner -{ +- (MyLayoutPos *)uBoundValInner { return _uBoundVal; } - - --(MyLayoutPos*)__equalTo:(id)val -{ - - if (![_posVal isEqual:val]) - { - if ([val isKindOfClass:[NSNumber class]]) - { +- (MyLayoutPos *)_myEqualTo:(id)val { + if (![_val isEqual:val]) { + if ([val isKindOfClass:[NSNumber class]]) { //特殊处理设置为safeAreaMargin边距的值。 - if ([val doubleValue] == [MyLayoutPos safeAreaMargin]) - { - - _posValType = MyLayoutValueType_SafeArea; - } - else - { - _posValType = MyLayoutValueType_NSNumber; + if ([val doubleValue] == [MyLayoutPos safeAreaMargin]) { + _valType = MyLayoutValType_SafeArea; + } else { + _valType = MyLayoutValType_Number; } - } - else if ([val isKindOfClass:[MyLayoutPos class]]) - _posValType = MyLayoutValueType_LayoutPos; - else if ([val isKindOfClass:[NSArray class]]) - _posValType = MyLayoutValueType_Array; - else if ([val conformsToProtocol:@protocol(UILayoutSupport)]) - { + } else if ([val isKindOfClass:[MyLayoutPos class]]) { + _valType = MyLayoutValType_LayoutPos; + } else if ([val isKindOfClass:[NSArray class]]) { + _valType = MyLayoutValType_Array; + } else if ([val conformsToProtocol:@protocol(UILayoutSupport)]) { //这里只有上边和下边支持,其他不支持。。 - if (_pos != MyGravity_Vert_Top && _pos != MyGravity_Vert_Bottom) - { + if (_anchorType != MyLayoutAnchorType_Top && _anchorType != MyLayoutAnchorType_Bottom) { NSAssert(0, @"oops! only topPos or bottomPos can set to id"); } - - _posValType = MyLayoutValueType_UILayoutSupport; - } - else if ([val isKindOfClass:[UIView class]]) - { - UIView *rview = (UIView*)val; - _posValType = MyLayoutValueType_LayoutPos; - - switch (_pos) { - case MyGravity_Horz_Leading: + _valType = MyLayoutValType_UILayoutSupport; + } else if ([val isKindOfClass:[MyLayoutMostPos class]]) { + _valType = MyLayoutValType_Most; + } else if ([val isKindOfClass:[UIView class]]) { + UIView *rview = (UIView *)val; + _valType = MyLayoutValType_LayoutPos; + + switch (_anchorType) { + case MyLayoutAnchorType_Leading: val = rview.leadingPos; break; - case MyGravity_Horz_Center: + case MyLayoutAnchorType_CenterX: val = rview.centerXPos; break; - case MyGravity_Horz_Trailing: + case MyLayoutAnchorType_Trailing: val = rview.trailingPos; break; - case MyGravity_Vert_Top: + case MyLayoutAnchorType_Top: val = rview.topPos; break; - case MyGravity_Vert_Center: + case MyLayoutAnchorType_CenterY: val = rview.centerYPos; break; - case MyGravity_Vert_Bottom: + case MyLayoutAnchorType_Bottom: val = rview.bottomPos; break; - case MyGravity_Vert_Baseline: + case MyLayoutAnchorType_Baseline: val = rview.baselinePos; break; default: NSAssert(0, @"oops!"); break; } - + } else { + _valType = MyLayoutValType_Nil; } - else - _posValType = MyLayoutValueType_Nil; - - _posVal = val; - [self setNeedsLayout]; + _val = val; } - + return self; } --(MyLayoutPos*)__offset:(CGFloat)val -{ - - if (_offsetVal != val) - { +- (MyLayoutPos *)_myOffset:(CGFloat)val { + if (_offsetVal != val) { _offsetVal = val; - [self setNeedsLayout]; } - return self; } --(MyLayoutPos*)__min:(CGFloat)val -{ - - if (self.lBoundVal.posNumVal.doubleValue != val) - { - [self.lBoundVal __equalTo:@(val)]; - - [self setNeedsLayout]; +- (MyLayoutPos *)_myMin:(CGFloat)val { + if (self.lBoundVal.numberVal.doubleValue != val) { + [self.lBoundVal _myEqualTo:@(val)]; } - return self; } --(MyLayoutPos*)__lBound:(id)posVal offsetVal:(CGFloat)offsetVal -{ - - [[self.lBoundVal __equalTo:posVal] __offset:offsetVal]; - - [self setNeedsLayout]; - +- (MyLayoutPos *)_myLBound:(id)posVal offsetVal:(CGFloat)offsetVal { + [[self.lBoundVal _myEqualTo:posVal] _myOffset:offsetVal]; return self; } - --(MyLayoutPos*)__max:(CGFloat)val -{ - - if (self.uBoundVal.posNumVal.doubleValue != val) - { - [self.uBoundVal __equalTo:@(val)]; - [self setNeedsLayout]; +- (MyLayoutPos *)_myMax:(CGFloat)val { + if (self.uBoundVal.numberVal.doubleValue != val) { + [self.uBoundVal _myEqualTo:@(val)]; } - return self; } --(MyLayoutPos*)__uBound:(id)posVal offsetVal:(CGFloat)offsetVal -{ - - [[self.uBoundVal __equalTo:posVal] __offset:offsetVal]; - - [self setNeedsLayout]; - +- (MyLayoutPos *)_myUBound:(id)posVal offsetVal:(CGFloat)offsetVal { + [[self.uBoundVal _myEqualTo:posVal] _myOffset:offsetVal]; return self; } - - --(void)__clear -{ +- (void)_myClear { _active = YES; - _posVal = nil; - _posValType = MyLayoutValueType_Nil; + _val = nil; + _valType = MyLayoutValType_Nil; _offsetVal = 0; _lBoundVal = nil; _uBoundVal = nil; - [self setNeedsLayout]; + _shrink = 0; } +- (void)_mySetActive:(BOOL)active { + _active = active; + [_lBoundVal _mySetActive:active]; + [_uBoundVal _mySetActive:active]; +} - - --(CGFloat)absVal -{ - if (self.isActive) - { +- (CGFloat)measure { + if (self.isActive) { CGFloat retVal = _offsetVal; - - if (self.posNumVal != nil) - retVal +=self.posNumVal.doubleValue; - - if (_uBoundVal != nil) - retVal = _myCGFloatMin(_uBoundVal.posNumVal.doubleValue, retVal); - - if (_lBoundVal != nil) - retVal = _myCGFloatMax(_lBoundVal.posNumVal.doubleValue, retVal); - + if (self.numberVal != nil) { + retVal += self.numberVal.doubleValue; + } + if (_uBoundVal != nil) { + retVal = _myCGFloatMin(_uBoundVal.numberVal.doubleValue, retVal); + } + if (_lBoundVal != nil) { + retVal = _myCGFloatMax(_lBoundVal.numberVal.doubleValue, retVal); + } return retVal; - } - else + } else { return 0; + } } --(BOOL)isRelativePos -{ - if (self.isActive) - { - CGFloat realPos = self.posNumVal.doubleValue; +- (BOOL)isRelativePos { + if (self.isActive) { + CGFloat realPos = self.numberVal.doubleValue; return realPos > 0 && realPos < 1; - - } - else + } else { return NO; + } } --(BOOL)isSafeAreaPos -{ - return self.isActive && (_posValType == MyLayoutValueType_SafeArea || _posValType == MyLayoutValueType_UILayoutSupport); +- (BOOL)isSafeAreaPos { + return self.isActive && (_valType == MyLayoutValType_SafeArea || _valType == MyLayoutValType_UILayoutSupport); } +- (CGFloat)measureWith:(CGFloat)refVal { + if (self.isActive) { + CGFloat retVal = self.numberVal.doubleValue; + if (retVal > 0 && retVal < 1) { + retVal *= refVal; + } + retVal += _offsetVal; --(CGFloat)realPosIn:(CGFloat)size -{ - if (self.isActive) - { - CGFloat realPos = self.posNumVal.doubleValue; - if (realPos > 0 && realPos < 1) - realPos *= size; - - realPos += _offsetVal; - - if (_uBoundVal != nil) - realPos = _myCGFloatMin(_uBoundVal.posNumVal.doubleValue, realPos); - - if (_lBoundVal != nil) - realPos = _myCGFloatMax(_lBoundVal.posNumVal.doubleValue, realPos); - - return realPos; - } - else + if (_uBoundVal != nil) { + retVal = _myCGFloatMin(_uBoundVal.numberVal.doubleValue, retVal); + } + if (_lBoundVal != nil) { + retVal = _myCGFloatMax(_lBoundVal.numberVal.doubleValue, retVal); + } + return retVal; + } else { return 0; - + } } - --(void)setNeedsLayout -{ - if (_view != nil && _view.superview != nil && [_view.superview isKindOfClass:[MyBaseLayout class]]) - { - MyBaseLayout* lb = (MyBaseLayout*)_view.superview; - if (!lb.isMyLayouting) +- (void)setNeedsLayout { + if (_view != nil && _view.superview != nil && [_view.superview isKindOfClass:[MyBaseLayout class]]) { + MyBaseLayout *lb = (MyBaseLayout *)_view.superview; + if (!lb.isMyLayouting) { [_view.superview setNeedsLayout]; + } } - } - - -+(NSString*)posstrFromPos:(MyLayoutPos*)posobj showView:(BOOL)showView -{ - ++ (NSString *)axisstrFromAnchor:(MyLayoutPos *)anchor showView:(BOOL)showView { NSString *viewstr = @""; - if (showView) - { - viewstr = [NSString stringWithFormat:@"View:%p.", posobj.view]; + if (showView) { + viewstr = [NSString stringWithFormat:@"view:%p.", anchor.view]; } - - NSString *posStr = @""; - - switch (posobj.pos) { - case MyGravity_Horz_Leading: - posStr = @"leadingPos"; + NSString *axisstr = @""; + + switch (anchor.anchorType) { + case MyLayoutAnchorType_Leading: + axisstr = @"leadingPos"; break; - case MyGravity_Horz_Center: - posStr = @"centerXPos"; + case MyLayoutAnchorType_CenterX: + axisstr = @"centerXPos"; break; - case MyGravity_Horz_Trailing: - posStr = @"trailingPos"; + case MyLayoutAnchorType_Trailing: + axisstr = @"trailingPos"; break; - case MyGravity_Vert_Top: - posStr = @"topPos"; + case MyLayoutAnchorType_Top: + axisstr = @"topPos"; break; - case MyGravity_Vert_Center: - posStr = @"centerYPos"; + case MyLayoutAnchorType_CenterY: + axisstr = @"centerYPos"; break; - case MyGravity_Vert_Bottom: - posStr = @"bottomPos"; + case MyLayoutAnchorType_Bottom: + axisstr = @"bottomPos"; break; - case MyGravity_Vert_Baseline: - posStr = @"baselinePos"; + case MyLayoutAnchorType_Baseline: + axisstr = @"baselinePos"; break; default: break; } - - return [NSString stringWithFormat:@"%@%@",viewstr,posStr]; - - -} + return [NSString stringWithFormat:@"%@%@", viewstr, axisstr]; +} -#pragma mark -- Override Method +#pragma mark-- Override Method --(NSString*)description -{ - NSString *posValStr = @""; - switch (_posValType) { - case MyLayoutValueType_Nil: - posValStr = @"nil"; +- (NSString *)description { + NSString *axisStr = @""; + switch (_valType) { + case MyLayoutValType_Nil: + axisStr = @"nil"; break; - case MyLayoutValueType_NSNumber: - posValStr = [_posVal description]; + case MyLayoutValType_Number: + axisStr = [_val description]; break; - case MyLayoutValueType_LayoutPos: - posValStr = [MyLayoutPos posstrFromPos:_posVal showView:YES]; + case MyLayoutValType_LayoutPos: + axisStr = [MyLayoutPos axisstrFromAnchor:_val showView:YES]; break; - case MyLayoutValueType_Array: - { - posValStr = @"["; - for (NSObject *obj in _posVal) - { - if ([obj isKindOfClass:[MyLayoutPos class]]) - { - posValStr = [posValStr stringByAppendingString:[MyLayoutPos posstrFromPos:(MyLayoutPos*)obj showView:YES]]; + case MyLayoutValType_Array: { + axisStr = @"["; + for (NSObject *item in _val) { + if ([item isKindOfClass:[MyLayoutPos class]]) { + axisStr = [axisStr stringByAppendingString:[MyLayoutPos axisstrFromAnchor:(MyLayoutPos *)item showView:YES]]; + } else { + axisStr = [axisStr stringByAppendingString:[item description]]; } - else - { - posValStr = [posValStr stringByAppendingString:[obj description]]; - + if (item != [_val lastObject]) { + axisStr = [axisStr stringByAppendingString:@", "]; } - - if (obj != [_posVal lastObject]) - posValStr = [posValStr stringByAppendingString:@", "]; - } - - posValStr = [posValStr stringByAppendingString:@"]"]; - + + axisStr = [axisStr stringByAppendingString:@"]"]; } default: break; } - - return [NSString stringWithFormat:@"%@=%@, Offset=%g, Max=%g, Min=%g",[MyLayoutPos posstrFromPos:self showView:NO], posValStr, _offsetVal, _uBoundVal.posNumVal.doubleValue == CGFLOAT_MAX ? NAN : _uBoundVal.posNumVal.doubleValue , _uBoundVal.posNumVal.doubleValue == -CGFLOAT_MAX ? NAN : _lBoundVal.posNumVal.doubleValue]; - + + return [NSString stringWithFormat:@"%@=%@, offset=%g, max=%g, min=%g", [MyLayoutPos axisstrFromAnchor:self showView:NO], axisStr, _offsetVal, _uBoundVal.numberVal.doubleValue == CGFLOAT_MAX ? NAN : _uBoundVal.numberVal.doubleValue, _uBoundVal.numberVal.doubleValue == -CGFLOAT_MAX ? NAN : _lBoundVal.numberVal.doubleValue]; } +@end +@implementation MyLayoutPos (Clone) + +- (MyLayoutPos * (^)(CGFloat offsetVal))clone { + return ^id(CGFloat offsetVal) { + MyLayoutPos *clonedAnchor = [[[self class] allocWithZone:nil] init]; + clonedAnchor->_offsetVal = offsetVal; + clonedAnchor->_val = self; + clonedAnchor->_valType = MyLayoutValType_LayoutAnchorClone; + return clonedAnchor; + }; +} @end +#pragma mark-- MyLayoutMostPos + +@implementation MyLayoutMostPos { + NSArray *_axes; + BOOL _isMax; +} + +- (instancetype)initWith:(NSArray *)axes isMax:(BOOL)isMax { + self = [self init]; + if (self != nil) { + _axes = axes; + _isMax = isMax; + } + return self; +} + +- (CGFloat)getMostAxisValFrom:(MyLayoutPos *)srcAnchor { + CGFloat mostAxisVal = _isMax ? -CGFLOAT_MAX : CGFLOAT_MAX; + for (id axis in _axes) { + CGFloat axisVal = 0; + if ([axis isKindOfClass:[NSNumber class]]) { + axisVal = [(NSNumber *)axis doubleValue]; + } else if ([axis isKindOfClass:[MyLayoutPos class]]) { + MyLayoutPos *anchor = (MyLayoutPos *)axis; + CGFloat offset = 0.0; + if (anchor.valType == MyLayoutValType_LayoutAnchorClone) { + offset = anchor.offsetVal; + anchor = (MyLayoutPos *)anchor.val; + } + + MyLayoutEngine *viewEngine = anchor.view.myEngine; + + if (srcAnchor.anchorType & MyLayoutAnchorType_VertMask) { //水平 + if (anchor.anchorType == MyLayoutAnchorType_Leading) { + axisVal = viewEngine.leading + offset; + } else if (anchor.anchorType == MyLayoutAnchorType_CenterX) { + axisVal = viewEngine.leading + viewEngine.width / 2.0 + offset; + } else { + axisVal = viewEngine.trailing - offset; + } + } else { //垂直 + if (anchor.anchorType == MyLayoutAnchorType_Top) { + axisVal = viewEngine.top + offset; + } else if (anchor.anchorType == MyLayoutAnchorType_CenterY) { + axisVal = viewEngine.top + viewEngine.height / 2.0 + offset; + } else { + axisVal = viewEngine.bottom - offset; + } + } + } else { + NSAssert(NO, @"oops!, invalid type, only support NSNumber or MyLayoutPos"); + } + mostAxisVal = _isMax ? _myCGFloatMax(axisVal, mostAxisVal) : _myCGFloatMin(axisVal, mostAxisVal); + } + return mostAxisVal; +} + +@end + +@implementation NSArray (MyLayoutMostPos) + +- (MyLayoutMostPos *)myMinPos { + return [[MyLayoutMostPos alloc] initWith:self isMax:NO]; +} + +- (MyLayoutMostPos *)myMaxPos { + return [[MyLayoutMostPos alloc] initWith:self isMax:YES]; +} + +@end diff --git a/MyLayout/Lib/MyLayoutPosInner.h b/MyLayout/Lib/MyLayoutPosInner.h index a25b432..c02f112 100644 --- a/MyLayout/Lib/MyLayoutPosInner.h +++ b/MyLayout/Lib/MyLayoutPosInner.h @@ -6,49 +6,64 @@ // Copyright (c) 2015年 YoungSoft. All rights reserved. // +#import "MyLayoutMath.h" #import "MyLayoutPos.h" - //布局位置内部定义 -@interface MyLayoutPos() - -@property(nonatomic, weak) UIView *view; -@property(nonatomic, assign) MyGravity pos; -@property(nonatomic, assign) MyLayoutValueType posValType; - -@property(nonatomic, readonly, strong) NSNumber *posNumVal; -@property(nonatomic, readonly, strong) MyLayoutPos *posRelaVal; -@property(nonatomic, readonly, strong) NSArray *posArrVal; - -@property(nonatomic, readonly, strong) MyLayoutPos *lBoundVal; -@property(nonatomic, readonly, strong) MyLayoutPos *uBoundVal; - -@property(nonatomic, readonly, strong) MyLayoutPos *lBoundValInner; -@property(nonatomic, readonly, strong) MyLayoutPos *uBoundValInner; - - - --(MyLayoutPos*)__equalTo:(id)val; --(MyLayoutPos*)__offset:(CGFloat)val; --(MyLayoutPos*)__min:(CGFloat)val; --(MyLayoutPos*)__lBound:(id)posVal offsetVal:(CGFloat)offsetVal; --(MyLayoutPos*)__max:(CGFloat)val; --(MyLayoutPos*)__uBound:(id)posVal offsetVal:(CGFloat)offsetVal; --(void)__clear; - - +@interface MyLayoutPos () + +@property (nonatomic, weak) UIView *view; +@property (nonatomic, assign) MyLayoutAnchorType anchorType; +@property (nonatomic, assign) MyLayoutValType valType; +@property (nonatomic, strong) id val; +@property (nonatomic, readonly, strong) NSNumber *numberVal; +@property (nonatomic, readonly, strong) MyLayoutPos *anchorVal; +@property (nonatomic, readonly, strong) NSArray *arrayVal; +@property (nonatomic, readonly, strong) NSNumber *mostVal; + +@property (nonatomic, strong) MyLayoutPos *lBoundVal; +@property (nonatomic, strong) MyLayoutPos *uBoundVal; + +@property (nonatomic, readonly, strong) MyLayoutPos *lBoundValInner; +@property (nonatomic, readonly, strong) MyLayoutPos *uBoundValInner; + +@property (nonatomic, assign) CGFloat offsetVal; + +- (MyLayoutPos * (^)(id val))myEqualTo; +- (MyLayoutPos * (^)(CGFloat val))myOffset; +- (MyLayoutPos * (^)(CGFloat val))myMin; +- (MyLayoutPos * (^)(id posVal, CGFloat offset))myLBound; +- (MyLayoutPos * (^)(CGFloat val))myMax; +- (MyLayoutPos * (^)(id posVal, CGFloat offset))myUBound; +- (void)myClear; + +- (MyLayoutPos *)_myEqualTo:(id)val; +- (MyLayoutPos *)_myOffset:(CGFloat)val; +- (MyLayoutPos *)_myMin:(CGFloat)val; +- (MyLayoutPos *)_myLBound:(id)posVal offsetVal:(CGFloat)offsetVal; +- (MyLayoutPos *)_myMax:(CGFloat)val; +- (MyLayoutPos *)_myUBound:(id)posVal offsetVal:(CGFloat)offsetVal; +- (void)_myClear; +- (void)_mySetActive:(BOOL)active; // minVal <= posNumVal + offsetVal <=maxVal . 注意这个只试用于相对布局。对于线性布局和框架布局来说,因为可以支持相对边距。 // 所以线性布局和框架布局不能使用这个属性。 -@property(nonatomic,readonly, assign) CGFloat absVal; +@property (nonatomic, readonly, assign) CGFloat measure; //获取真实的位置值 --(CGFloat)realPosIn:(CGFloat)size; +- (CGFloat)measureWith:(CGFloat)size; --(BOOL)isRelativePos; +- (BOOL)isRelativePos; + +- (BOOL)isSafeAreaPos; + +@end --(BOOL)isSafeAreaPos; +@interface MyLayoutMostPos : NSObject +- (instancetype)initWith:(NSArray *)poss isMax:(BOOL)isMax; +//获取极限值 +- (CGFloat)getMostAxisValFrom:(MyLayoutPos *)layoutPos; @end diff --git a/MyLayout/Lib/MyLayoutSize.h b/MyLayout/Lib/MyLayoutSize.h index bcf137a..dc02af3 100644 --- a/MyLayout/Lib/MyLayoutSize.h +++ b/MyLayout/Lib/MyLayoutSize.h @@ -8,6 +8,7 @@ #import "MyLayoutDef.h" +@class MyLayoutMostSize; /** *视图的布局尺寸类,用来设置视图在布局视图中宽度和高度的尺寸值。布局尺寸类是对尺寸的一个抽象,一个尺寸不一定描述为一个具体的数值,也有可能描述为和另外一个尺寸相等也就是依赖另外一个尺寸,同时一个尺寸可能也会有最大和最小值的限制等等。因此用MyLayoutSize类来描述这种尺寸的抽象概念。 @@ -22,47 +23,39 @@ uBound.sizeVal,uBound.multiVal,uBound.addVal是通过uBound方法设置的值。他表示尺寸的最大边界值。 @endcode */ -@interface MyLayoutSize : NSObject +@interface MyLayoutSize : NSObject -//because masonry defined macro MAS_SHORTHAND_GLOBALS. the equalTo, offset may conflict with below method. so -//if you used MyLayout and Masonry concurrently and you defined MAS_SHORTHAND_GLOBALS in masonry, then you can define MY_USEPREFIXMETHOD to solve the conflict. -#ifdef MY_USEPREFIXMETHOD +/**特殊的尺寸,表示尺寸由子视图决定或者由内容决定,也就是说尺寸自适应*/ +@property (class, nonatomic, assign, readonly) NSInteger wrap; --(MyLayoutSize* (^)(id val))myEqualTo; --(MyLayoutSize* (^)(CGFloat val))myAdd; --(MyLayoutSize* (^)(CGFloat val))myMultiply; --(MyLayoutSize* (^)(CGFloat val))myMin; --(MyLayoutSize* (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))myLBound; --(MyLayoutSize* (^)(CGFloat val))myMax; --(MyLayoutSize* (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))myUBound; --(void)myClear; - - -#else +/**特殊的尺寸,表示尺寸会填充满父视图的剩余空间。*/ +@property (class, nonatomic, assign, readonly) NSInteger fill; +/**特殊的尺寸,表示清除尺寸的约束设置,等价于:equalTo(nil)*/ +@property (class, nonatomic, assign, readonly) NSInteger empty; -#if UIKIT_DEFINE_AS_PROPERTIES - -/**特殊的尺寸,表示尺寸由子视图决定或者由内容决定。目前只用在表格布局MyTableLayout和栅格布局MyGridLayout中。*/ -@property(class, nonatomic, assign,readonly) CGFloat wrap; -/**特殊的尺寸,表示尺寸会填充满父视图的剩余空间。目前只用在表格布局MyTableLayout和栅格布局MyGridLayout中。*/ -@property(class, nonatomic, assign,readonly) CGFloat fill; /**特殊的尺寸,表示尺寸会均分父视图的剩余空间。目前只用在表格布局MyTableLayout */ -@property(class, nonatomic, assign,readonly) CGFloat average; - -#else +@property (class, nonatomic, assign, readonly) NSInteger average; -+(CGFloat)wrap; -+(CGFloat)fill; -+(CGFloat)average; +//because masonry defined macro MAS_SHORTHAND_GLOBALS. the equalTo, offset may conflict with below method. so +//if you used MyLayout and Masonry concurrently and you defined MAS_SHORTHAND_GLOBALS in masonry, then you can define MY_USEPREFIXMETHOD to solve the conflict. +#ifdef MY_USEPREFIXMETHOD -#endif +- (MyLayoutSize * (^)(id val))myEqualTo; +- (MyLayoutSize * (^)(CGFloat val))myAdd; +- (MyLayoutSize * (^)(CGFloat val))myMultiply; +- (MyLayoutSize * (^)(CGFloat val))myMin; +- (MyLayoutSize * (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))myLBound; +- (MyLayoutSize * (^)(CGFloat val))myMax; +- (MyLayoutSize * (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))myUBound; +- (void)myClear; +#else /** - 设置尺寸的具体值,这个具体值可以设置为NSNumber, MyLayoutSize以及NSArray数组,UIView和nil值。 + 设置尺寸的具体值,这个具体值可以设置为NSNumber, MyLayoutSize以及NSArray数组,UIView, MyLayoutMostSize和nil值。 - 1. 设置为NSNumber值表示指定具体的宽度或者高度数值 + 1. 设置为NSNumber值表示指定具体的宽度或者高度数值,如果设置为特殊值MyLayoutSize.wrap则表示尺寸自适应,如果设置为MyLayoutSize.fill则表示等于父视图的尺寸,如果设置为MyLayoutSize.empty则表示清空尺寸约束。 2. 设置为MyLayoutSize值表示宽度和高度与设置的对象有依赖关系, 甚至可以依赖对象本身 @@ -70,29 +63,31 @@ 4. 设置为UIView的概念就是尺寸依赖于指定视图的相对应的尺寸。 - 5. 设置为nil时则清除设置的具体值。 + 5. 设置为MyLayoutMostSize值表示取数组中所有元素尺寸中的最大的一个或者最小的一个元素的尺寸值。 + 这个对象从NSArray对象的Category属性:myMaxSize和myMinSize中获取。同时要求数组中的元素只能是MyLayoutSize或者NSNumber。 + 如果元素中有一个值为MyLayoutSize.wrap则表示自身的自适应尺寸也参与比较。 + 这个设置还有一个要求就是数组中的元素值如果是MyLayoutSize的话则要求这个尺寸必须在本视图之前就计算好的约束尺寸,否则可能设置无效。 + 6. 设置为nil时则清除设置的具体值。 */ --(MyLayoutSize* (^)(id val))equalTo; +- (MyLayoutSize * (^)(id val))equalTo; /** *设置尺寸增加值,默认值是0。如果设置为负数则表示减少值 */ --(MyLayoutSize* (^)(CGFloat val))add; - +- (MyLayoutSize * (^)(CGFloat val))add; /** * 设置尺寸的放大缩小倍数,默认值是1。 */ --(MyLayoutSize* (^)(CGFloat val))multiply; - +- (MyLayoutSize * (^)(CGFloat val))multiply; /** *设置尺寸的最小边界数值。min方法是lBound方法的简化版本。比如:A.min(10) <==> A.lBound(@10, 0, 1) */ --(MyLayoutSize* (^)(CGFloat val))min; +- (MyLayoutSize * (^)(CGFloat val))min; /** - 设置尺寸的最小边界值,如果尺寸对象没有设置最小边界值,那么最小边界默认就是无穷小-CGFLOAT_MAX。lBound方法除了能设置为数值外,还可以设置为MyLayoutSize值,并且还可以指定增量值和倍数值。 + 设置尺寸的最小边界值,如果尺寸对象没有设置最小边界值,那么最小边界默认就是无穷小-CGFLOAT_MAX。lBound方法除了能设置为数值外,还可以设置为MyLayoutSize值和MyLayoutMostSize值和nil值,并且还可以指定增量值和倍数值。 1. 比如我们有一个UILabel的宽度是由内容决定的,但是最小的宽度大于等于父视图的宽度,则设置为: @code @@ -120,17 +115,16 @@ multiVal 指定边界值的倍数值,如果没有倍数请设置为1。 */ --(MyLayoutSize* (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))lBound; +- (MyLayoutSize * (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))lBound; /** *设置尺寸的最大边界数值。max方法是uBound方法的简化版本。比如:A.max(10) <==> A.uBound(@10, 0, 1) */ --(MyLayoutSize* (^)(CGFloat val))max; - +- (MyLayoutSize * (^)(CGFloat val))max; /** - 设置尺寸的最大边界值,如果尺寸对象没有设置最大边界值,那么最大边界默认就是无穷大CGFLOAT_MAX。uBound方法除了能设置为数值外,还可以设置为MyLayoutSize值和nil值,并且还可以指定增量值和倍数值。 + 设置尺寸的最大边界值,如果尺寸对象没有设置最大边界值,那么最大边界默认就是无穷大CGFLOAT_MAX。uBound方法除了能设置为数值外,还可以设置为MyLayoutSize值和MyLayoutMostSize值和nil值,并且还可以指定增量值和倍数值。 1. 比如我们有一个UILabel的宽度是由内容决定的,但是最大的宽度小于等于父视图的宽度,则设置为: @code @@ -158,13 +152,12 @@ multiVal 指定边界值的倍数值,如果没有倍数请设置为1。 */ --(MyLayoutSize* (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))uBound; - +- (MyLayoutSize * (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))uBound; /** *清除所有设置的约束值,这样尺寸对象将不会再生效了。 */ --(void)clear; +- (void)clear; #endif @@ -172,18 +165,70 @@ *设置布局尺寸是否是活动的,默认是YES表示活动的,如果设置为NO则表示这个布局尺寸对象设置的约束值将不会起作用。 *active设置为YES和clear的相同点是尺寸对象设置的约束值都不会生效了,区别是前者不会清除所有设置的约束,而后者则会清除所有设置的约束。 */ -@property(nonatomic, assign, getter=isActive) BOOL active; +@property (nonatomic, assign, getter=isActive) BOOL active; +/** + 在布局视图的宽度或者高度是固定的情况下,当某一列或者行中的所有子视图尺寸超过父视图的尺寸时子视图的尺寸压缩比重,默认值为0表示不压缩。数字越大表明压缩的比重越大。目前只有线性布局和框架布局和流式布局中的子视图支持这个属性,而且这特性只在子视图尺寸超过布局视图时才有效。 + + 子视图设置shrink属性时要注意如下几点: + + - 垂直线性布局中的子视图设置为非0时,表明当所有子视图的高度高于父布局视图时会对子视图的高度进行按比例压缩。 + + - 水平线性布局中的子视图设置为非0时,表明当所有子视图的宽度宽于父布局视图时会对子视图的宽度进行按比例压缩。 + + - 垂直流式布局中的子视图设置为非0时,表明当子视图所在行中的所有子视图的宽度宽于父布局视图时会对子视图的宽度进行按比例压缩。 + + - 水平流式布局中的子视图设置为非0时,表明当子视图所在列中的所有子视图的高度高于父布局视图时会对子视图的高度进行按比例压缩。 + + @note + 假设某个水平线性布局的宽度是100,其中有子视图A,B,C的宽度约束分别为:50,50,30,并且对应的shrink值分别为:0,1,2。因为三个子视图的 + 宽度总和为130已经超过父布局30的宽度。因此需要对A,B,C分别进行压缩处理。那A,B,C视图需要压缩的值分别为: + A: 30 * (0/(0+1+2)) = 0 + B: 30 * (1/(0+1+2)) = 10 + C: 30 * (2/(0+1+2)) = 20 + + 这样最终布局完成时A,B,C三个子视图的最终宽度分别为: 50, 40(50-10), 10(30-20)。最终三个子视图的总和不会再超出父视图的宽度了。 + + @note + shrink属性和子视图的weight属性的区别是:前者在剩余空间不足时起作用,后者在有剩余空间时起作用。 + */ +@property (nonatomic, assign) CGFloat shrink; //上面方法设置的属性的获取。 -@property(nonatomic, strong, readonly) id dimeVal; -@property(nonatomic, assign, readonly) CGFloat addVal; -@property(nonatomic, assign, readonly) CGFloat multiVal; -@property(nonatomic, assign, readonly) CGFloat minVal; -@property(nonatomic, assign, readonly) CGFloat maxVal; +@property (nonatomic, assign, readonly) CGFloat addVal; +@property (nonatomic, assign, readonly) CGFloat multiVal; +@property (nonatomic, assign, readonly) CGFloat minVal; +@property (nonatomic, assign, readonly) CGFloat maxVal; - +/** + 判断尺寸值是否是自适应值。 + */ +@property (nonatomic, assign, readonly) BOOL isWrap; + +/** + 判断尺寸是不是填充比重值。 + */ +@property (nonatomic, assign, readonly) BOOL isFill; + +@end + +@interface MyLayoutSize (Clone) + +//从布局尺寸中克隆一个尺寸对象来。这个克隆出来的尺寸值是源尺寸对象的值乘以multival再加上addVal。这个方法通常用于下面数组元素的构造 +- (MyLayoutSize * (^)(CGFloat addVal, CGFloat multiVal))clone; + +@end + +/** + 我们可以从一个数组中获取众多尺寸的最大最小的尺寸值。 + 这里要求数组的元素只能是MyLayoutSize或者NSNumber两种对象类型的值。 + */ +@interface NSArray (MyLayoutMostSize) +//从数组中得到最小的尺寸值。 +@property (nonatomic, readonly) MyLayoutMostSize *myMinSize; +//从数组中得到最大的尺寸值。 +@property (nonatomic, readonly) MyLayoutMostSize *myMaxSize; @end diff --git a/MyLayout/Lib/MyLayoutSize.m b/MyLayout/Lib/MyLayoutSize.m index 3189c57..eed4477 100644 --- a/MyLayout/Lib/MyLayoutSize.m +++ b/MyLayout/Lib/MyLayoutSize.m @@ -7,592 +7,563 @@ // #import "MyLayoutSize.h" -#import "MyLayoutSizeInner.h" #import "MyBaseLayout.h" -#import "MyLayoutMath.h" +#import "MyLayoutInner.h" +#import "MyLayoutSizeInner.h" +@implementation MyLayoutSize -@implementation MyLayoutSize -{ - id _dimeVal; - CGFloat _addVal; - CGFloat _multiVal; - MyLayoutSize *_lBoundVal; - MyLayoutSize *_uBoundVal; ++ (NSInteger)empty { + return -100000; //这么定义存粹是一个数字没有其他意义 } - -+(CGFloat)wrap -{ - return -1; ++ (NSInteger)wrap { + return -99999; //这么定义纯粹是一个数字没有其他意义 } -+(CGFloat)fill -{ - return -2; ++ (NSInteger)fill { + return -99998; //这么定义纯粹是一个数字没有其他意义 } -+(CGFloat)average -{ - return -3; ++ (NSInteger)average { + return -99997; //这么定义纯粹是一个数字没有其他意义 } - --(id)init -{ - self= [super init]; - if (self !=nil) - { +- (id)init { + self = [super init]; + if (self != nil) { _active = YES; _view = nil; - _dime = MyGravity_None; - _dimeVal = nil; - _dimeValType = MyLayoutValueType_Nil; + _val = nil; + _valType = MyLayoutValType_Nil; _addVal = 0; _multiVal = 1; _lBoundVal = nil; _uBoundVal = nil; + _shrink = 0.0; + _priority = MyPriority_Normal; } - return self; } - --(MyLayoutSize* (^)(id val))myEqualTo -{ - return ^id(id val){ - - return [self __equalTo:val]; +- (MyLayoutSize * (^)(id val))myEqualTo { + return ^id(id val) { + [self _myEqualTo:val]; + //如果尺寸是自适应,并且当前视图是布局视图则直接布局视图自身刷新布局,否则由视图的父视图来刷新布局,这里特殊处理。 + if ([val isKindOfClass:[NSNumber class]]) { + if ([val integerValue] == MyLayoutSize.wrap && [self.view isKindOfClass:[MyBaseLayout class]]) { + [self.view setNeedsLayout]; + return self; + } + } + [self setNeedsLayout]; + return self; }; } --(MyLayoutSize* (^)(CGFloat val))myAdd -{ - return ^id(CGFloat val){ - - return [self __add:val]; +- (MyLayoutSize * (^)(CGFloat val))myAdd { + return ^id(CGFloat val) { + [self _myAdd:val]; + [self setNeedsLayout]; + return self; }; } --(MyLayoutSize* (^)(CGFloat val))myMultiply -{ - return ^id(CGFloat val){ - - return [self __multiply:val]; +- (MyLayoutSize * (^)(CGFloat val))myMultiply { + return ^id(CGFloat val) { + [self _myMultiply:val]; + [self setNeedsLayout]; + return self; }; - } --(MyLayoutSize* (^)(CGFloat val))myMin -{ - return ^id(CGFloat val){ - - return [self __min:val]; +- (MyLayoutSize * (^)(CGFloat val))myMin { + return ^id(CGFloat val) { + [self _myMin:val]; + [self setNeedsLayout]; + return self; }; - } --(MyLayoutSize* (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))myLBound -{ - - return ^id(id sizeVal, CGFloat addVal, CGFloat multiVal){ - - return [self __lBound:sizeVal addVal:addVal multiVal:multiVal]; - +- (MyLayoutSize * (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))myLBound { + return ^id(id sizeVal, CGFloat addVal, CGFloat multiVal) { + [self _myLBound:sizeVal addVal:addVal multiVal:multiVal]; + [self setNeedsLayout]; + return self; }; } --(MyLayoutSize* (^)(CGFloat val))myMax -{ - return ^id(CGFloat val){ - - return [self __max:val]; +- (MyLayoutSize * (^)(CGFloat val))myMax { + return ^id(CGFloat val) { + [self _myMax:val]; + [self setNeedsLayout]; + return self; }; } --(MyLayoutSize* (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))myUBound -{ - return ^id(id sizeVal, CGFloat addVal, CGFloat multiVal){ - - return [self __uBound:sizeVal addVal:addVal multiVal:multiVal]; +- (MyLayoutSize * (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))myUBound { + return ^id(id sizeVal, CGFloat addVal, CGFloat multiVal) { + [self _myUBound:sizeVal addVal:addVal multiVal:multiVal]; + [self setNeedsLayout]; + return self; }; - } --(void)myClear -{ - [self __clear]; +- (void)myClear { + [self _myClear]; + [self setNeedsLayout]; } - --(MyLayoutSize* (^)(id val))equalTo -{ - return ^id(id val){ - - return [self __equalTo:val]; - }; +- (MyLayoutSize * (^)(id val))equalTo { + return self.myEqualTo; } --(MyLayoutSize* (^)(CGFloat val))add -{ - return ^id(CGFloat val){ - - return [self __add:val]; - }; +- (MyLayoutSize * (^)(CGFloat val))add { + return self.myAdd; } --(MyLayoutSize* (^)(CGFloat val))multiply -{ - return ^id(CGFloat val){ - - return [self __multiply:val]; - }; - +- (MyLayoutSize * (^)(CGFloat val))multiply { + return self.myMultiply; } --(MyLayoutSize* (^)(CGFloat val))min -{ - return ^id(CGFloat val){ - - return [self __min:val]; - }; - +- (MyLayoutSize * (^)(CGFloat val))min { + return self.myMin; } --(MyLayoutSize* (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))lBound -{ - - return ^id(id sizeVal, CGFloat addVal, CGFloat multiVal){ - - return [self __lBound:sizeVal addVal:addVal multiVal:multiVal]; - - }; +- (MyLayoutSize * (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))lBound { + return self.myLBound; } --(MyLayoutSize* (^)(CGFloat val))max -{ - return ^id(CGFloat val){ - - return [self __max:val]; - }; +- (MyLayoutSize * (^)(CGFloat val))max { + return self.myMax; } --(MyLayoutSize* (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))uBound -{ - return ^id(id sizeVal, CGFloat addVal, CGFloat multiVal){ - - return [self __uBound:sizeVal addVal:addVal multiVal:multiVal]; - }; - +- (MyLayoutSize * (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))uBound { + return self.myUBound; } --(void)clear -{ - [self __clear]; +- (void)clear { + [self myClear]; } --(void)setActive:(BOOL)active -{ - if (_active != active) - { - _active = active; - _lBoundVal.active = active; - _uBoundVal.active = active; +- (void)setActive:(BOOL)active { + if (_active != active) { + [self _mySetActive:active]; [self setNeedsLayout]; } } - - --(id)dimeVal -{ - if (self.isActive) - { - if (_dimeValType == MyLayoutValueType_LayoutDime && _dimeVal == nil) - return self; - - return _dimeVal; - } - else - return nil; - +- (CGFloat)shrink { + return _active ? _shrink : 0.0; } --(CGFloat)minVal -{ - return (self.isActive && _lBoundVal != nil) ? _lBoundVal.dimeNumVal.doubleValue : -CGFLOAT_MAX; +- (id)val { + return self.isActive ? _val : nil; } --(CGFloat)maxVal -{ - return (self.isActive && _uBoundVal != nil) ? _uBoundVal.dimeNumVal.doubleValue : CGFLOAT_MAX; +- (BOOL)isWrap { + return _valType == MyLayoutValType_Wrap; } +- (BOOL)isFill { + return _valType == MyLayoutValType_Fill; +} -#pragma mark -- NSCopying - --(id)copyWithZone:(NSZone *)zone -{ - MyLayoutSize *ld = [[[self class] allocWithZone:zone] init]; - ld.view = self.view; - ld->_active = _active; - ld->_dime = _dime; - ld->_addVal = _addVal; - ld->_multiVal = _multiVal; - ld->_dimeVal = _dimeVal; - ld->_dimeValType = _dimeValType; - if (_lBoundVal != nil) - { - ld->_lBoundVal = [[[self class] allocWithZone:zone] init]; - ld->_lBoundVal->_active = _active; - [[[ld->_lBoundVal __equalTo:_lBoundVal.dimeVal] __add:_lBoundVal.addVal] __multiply:_lBoundVal.multiVal]; +#pragma mark-- NSCopying +- (id)copyWithZone:(NSZone *)zone { + MyLayoutSize *layoutSize = [[[self class] allocWithZone:zone] init]; + layoutSize->_view = _view; + layoutSize->_active = _active; + layoutSize->_shrink = _shrink; + layoutSize->_anchorType = _anchorType; + layoutSize->_addVal = _addVal; + layoutSize->_multiVal = _multiVal; + layoutSize->_val = _val; + layoutSize->_valType = _valType; + layoutSize->_priority = _priority; + if (_lBoundVal != nil) { + layoutSize->_lBoundVal = [[[self class] allocWithZone:zone] init]; + layoutSize->_lBoundVal->_active = _active; + [[[layoutSize->_lBoundVal _myEqualTo:_lBoundVal.val] _myAdd:_lBoundVal.addVal] _myMultiply:_lBoundVal.multiVal]; } - - if (_uBoundVal != nil) - { - ld->_uBoundVal = [[[self class] allocWithZone:zone] init]; - ld->_uBoundVal->_active = _active; - [[[ld->_uBoundVal __equalTo:_uBoundVal.dimeVal] __add:_uBoundVal.addVal] __multiply:_uBoundVal.multiVal]; - + if (_uBoundVal != nil) { + layoutSize->_uBoundVal = [[[self class] allocWithZone:zone] init]; + layoutSize->_uBoundVal->_active = _active; + [[[layoutSize->_uBoundVal _myEqualTo:_uBoundVal.val] _myAdd:_uBoundVal.addVal] _myMultiply:_uBoundVal.multiVal]; } - - - return self; + return layoutSize; } -#pragma mark -- Private Method - +#pragma mark-- Private Methods --(NSNumber*)dimeNumVal -{ - if (_dimeVal == nil || !self.isActive) +- (NSNumber *)numberVal { + if (_val == nil || !self.isActive) { return nil; - - if (_dimeValType == MyLayoutValueType_NSNumber) - return _dimeVal; + } + if (_valType == MyLayoutValType_Number) { + return _val; + } + if (_valType == MyLayoutValType_Most) { + return @([((MyLayoutMostSize *)_val) getMostDimenValFrom:self]); + } return nil; } --(MyLayoutSize*)dimeRelaVal -{ - if (_dimeVal == nil || !self.isActive) +- (MyLayoutSize *)anchorVal { + if (_val == nil || !self.isActive) { return nil; - if (_dimeValType == MyLayoutValueType_LayoutDime) - return _dimeVal; + } + if (_valType == MyLayoutValType_LayoutSize) { + return _val; + } return nil; - } - --(NSArray*)dimeArrVal -{ - if (_dimeVal == nil || !self.isActive) +- (NSArray *)arrayVal { + if (_val == nil || !self.isActive) { return nil; - if (_dimeValType == MyLayoutValueType_Array) - return _dimeVal; + } + if (_valType == MyLayoutValType_Array) { + return _val; + } return nil; - } - --(MyLayoutSize*)dimeSelfVal -{ - if (_dimeValType == MyLayoutValueType_LayoutDime && _dimeVal == nil && self.isActive) - return self; - - return nil; +- (BOOL)wrapVal { + return self.isActive && _valType == MyLayoutValType_Wrap; } +- (BOOL)fillVal { + return self.isActive && _valType == MyLayoutValType_Fill; +} --(MyLayoutSize*)lBoundVal -{ - if (_lBoundVal == nil) - { +- (MyLayoutSize *)lBoundVal { + if (_lBoundVal == nil) { _lBoundVal = [[MyLayoutSize alloc] init]; _lBoundVal->_active = _active; - [_lBoundVal __equalTo:@(-CGFLOAT_MAX)]; + [_lBoundVal _myEqualTo:@(-CGFLOAT_MAX)]; } - return _lBoundVal; } --(MyLayoutSize*)uBoundVal -{ - - if (_uBoundVal == nil) - { +- (MyLayoutSize *)uBoundVal { + if (_uBoundVal == nil) { _uBoundVal = [[MyLayoutSize alloc] init]; _uBoundVal->_active = _active; - [_uBoundVal __equalTo:@(CGFLOAT_MAX)]; + [_uBoundVal _myEqualTo:@(CGFLOAT_MAX)]; } return _uBoundVal; } --(MyLayoutSize*)lBoundValInner -{ +- (MyLayoutSize *)lBoundValInner { return _lBoundVal; } --(MyLayoutSize*)uBoundValInner -{ +- (MyLayoutSize *)uBoundValInner { return _uBoundVal; } +- (CGFloat)minVal { + return (self.isActive && _lBoundVal != nil) ? _lBoundVal.numberVal.doubleValue : -CGFLOAT_MAX; +} --(MyLayoutSize*)__equalTo:(id)val -{ - - if (![_dimeVal isEqual:val]) - { - if ([val isKindOfClass:[NSNumber class]]) - { - _dimeValType = MyLayoutValueType_NSNumber; - } - else if ([val isMemberOfClass:[MyLayoutSize class]]) - { - _dimeValType = MyLayoutValueType_LayoutDime; - +- (CGFloat)maxVal { + return (self.isActive && _uBoundVal != nil) ? _uBoundVal.numberVal.doubleValue : CGFLOAT_MAX; +} + +- (MyLayoutSize *)_myEqualTo:(id)val { + return [self _myEqualTo:val priority:MyPriority_Normal]; +} + +- (MyLayoutSize *)_myEqualTo:(id)val priority:(NSInteger)priority { + _priority = priority; + if (![_val isEqual:val]) { + if ([val isKindOfClass:[NSNumber class]]) { + //特殊处理。 + if ([val integerValue] == MyLayoutSize.wrap) { + _valType = MyLayoutValType_Wrap; + } else if ([val integerValue] == MyLayoutSize.fill) { + _valType = MyLayoutValType_Fill; + } else if ([val integerValue] == MyLayoutSize.empty) { + _valType = MyLayoutValType_Nil; + val = nil; + } else { + _valType = MyLayoutValType_Number; + } + } else if ([val isMemberOfClass:[MyLayoutSize class]]) { //我们支持尺寸等于自己的情况,用来支持那些尺寸包裹内容但又想扩展尺寸的场景,为了不造成循环引用这里做特殊处理 //当尺寸等于自己时,我们只记录_dimeValType,而把值设置为nil - if (val == self) - { - val = nil; + if (val == self) { +#if DEBUG + NSLog(@"不建议这样设置,请使用MyLayoutSize.wrap代替!"); +#endif + _valType = MyLayoutValType_Wrap; + val = @(MyLayoutSize.wrap); + } else { + _valType = MyLayoutValType_LayoutSize; } - } - else if ([val isKindOfClass:[UIView class]]) - { - - UIView *rview = (UIView*)val; - _dimeValType = MyLayoutValueType_LayoutDime; - - switch (_dime) { - case MyGravity_Horz_Fill: - val = rview.widthSize; + } else if ([val isKindOfClass:[UIView class]]) { + UIView *viewVal = (UIView *)val; + _valType = MyLayoutValType_LayoutSize; + switch (_anchorType) { + case MyLayoutAnchorType_Width: + val = viewVal.widthSize; break; - case MyGravity_Vert_Fill: - val = rview.heightSize; + case MyLayoutAnchorType_Height: + val = viewVal.heightSize; break; default: NSAssert(0, @"oops!"); break; } - - } - else if ([val isKindOfClass:[NSArray class]]) - { - _dimeValType = MyLayoutValueType_Array; + } else if ([val isKindOfClass:[NSArray class]]) { + _valType = MyLayoutValType_Array; + } else if ([val isKindOfClass:[MyLayoutMostSize class]]) { + _valType = MyLayoutValType_Most; + } else { + _valType = MyLayoutValType_Nil; } - else - { - _dimeValType = MyLayoutValueType_Nil; - } - - _dimeVal = val; - [self setNeedsLayout]; - } - else - { - //参考上面自己等于自己的特殊情况需要特殊处理。 - if (val == nil && _dimeVal == nil && _dimeValType == MyLayoutValueType_LayoutDime) - { - _dimeValType = MyLayoutValueType_Nil; - [self setNeedsLayout]; + //特殊处理UILabel的高度是wrap的情况。 + if (_valType == MyLayoutValType_Wrap && _view != nil && _anchorType == MyLayoutAnchorType_Height) { + if ([_view isKindOfClass:[UILabel class]]) { + if (((UILabel *)_view).numberOfLines == 1) { + ((UILabel *)_view).numberOfLines = 0; + } + } } + _val = val; } - return self; } //加 --(MyLayoutSize*)__add:(CGFloat)val -{ - - if (_addVal != val) - { +- (MyLayoutSize *)_myAdd:(CGFloat)val { + if (_addVal != val) { _addVal = val; - [self setNeedsLayout]; } - return self; } - //乘 --(MyLayoutSize*)__multiply:(CGFloat)val -{ - - if (_multiVal != val) - { +- (MyLayoutSize *)_myMultiply:(CGFloat)val { + if (_multiVal != val) { _multiVal = val; - [self setNeedsLayout]; } - return self; - } - --(MyLayoutSize*)__min:(CGFloat)val -{ - if (self.lBoundVal.dimeNumVal.doubleValue != val) - { - [self.lBoundVal __equalTo:@(val)]; - [self setNeedsLayout]; +- (MyLayoutSize *)_myMin:(CGFloat)val { + if (self.lBoundVal.numberVal.doubleValue != val) { + [self.lBoundVal _myEqualTo:@(val)]; } - return self; } +- (MyLayoutSize *)_myLBound:(id)sizeVal addVal:(CGFloat)addVal multiVal:(CGFloat)multiVal { + if (sizeVal == self) { +#if DEBUG + NSLog(@"不建议这样设置,请使用MyLayoutSize.wrap代替!"); +#endif + sizeVal = @(MyLayoutSize.wrap); + } --(MyLayoutSize*)__lBound:(id)sizeVal addVal:(CGFloat)addVal multiVal:(CGFloat)multiVal -{ - if (sizeVal == self) - sizeVal = self.lBoundVal; - - [[[self.lBoundVal __equalTo:sizeVal] __add:addVal] __multiply:multiVal]; - [self setNeedsLayout]; - + [[[self.lBoundVal _myEqualTo:sizeVal] _myAdd:addVal] _myMultiply:multiVal]; return self; } - --(MyLayoutSize*)__max:(CGFloat)val -{ - if (self.uBoundVal.dimeNumVal.doubleValue != val) - { - [self.uBoundVal __equalTo:@(val)]; - [self setNeedsLayout]; +- (MyLayoutSize *)_myMax:(CGFloat)val { + if (self.uBoundVal.numberVal.doubleValue != val) { + [self.uBoundVal _myEqualTo:@(val)]; } - return self; } --(MyLayoutSize*)__uBound:(id)sizeVal addVal:(CGFloat)addVal multiVal:(CGFloat)multiVal -{ - if (sizeVal == self) - sizeVal = self.uBoundVal; - - [[[self.uBoundVal __equalTo:sizeVal] __add:addVal] __multiply:multiVal]; - [self setNeedsLayout]; - +- (MyLayoutSize *)_myUBound:(id)sizeVal addVal:(CGFloat)addVal multiVal:(CGFloat)multiVal { + if (sizeVal == self) { +#if DEBUG + NSLog(@"不建议这样设置,请使用MyLayoutSize.wrap代替!"); +#endif + sizeVal = @(MyLayoutSize.wrap); + } + [[[self.uBoundVal _myEqualTo:sizeVal] _myAdd:addVal] _myMultiply:multiVal]; return self; } - - --(void)__clear -{ +- (void)_myClear { _active = YES; _addVal = 0; _multiVal = 1; _lBoundVal = nil; _uBoundVal = nil; - _dimeVal = nil; - _dimeValType = MyLayoutValueType_Nil; - - [self setNeedsLayout]; + _val = nil; + _shrink = 0; + _priority = MyPriority_Normal; + _valType = MyLayoutValType_Nil; } - - --(CGFloat) measure -{ - return self.isActive ? _myCGFloatFma(self.dimeNumVal.doubleValue, _multiVal, _addVal) : 0; +- (void)_mySetActive:(BOOL)active { + _active = active; + [_lBoundVal _mySetActive:active]; + [_uBoundVal _mySetActive:active]; } --(CGFloat)measureWith:(CGFloat)size -{ - return self.isActive ? _myCGFloatFma(size, _multiVal , _addVal) : size; +- (CGFloat)measure { + return self.isActive ? _myCGFloatFma(self.numberVal.doubleValue, _multiVal, _addVal) : 0; } +- (CGFloat)measureWith:(CGFloat)size { + return self.isActive ? _myCGFloatFma(size, _multiVal, _addVal) : size; +} - --(void)setNeedsLayout -{ - if (_view != nil && _view.superview != nil && [_view.superview isKindOfClass:[MyBaseLayout class]]) - { - MyBaseLayout* lb = (MyBaseLayout*)_view.superview; - if (!lb.isMyLayouting) +- (void)setNeedsLayout { + if (_view != nil && _view.superview != nil && [_view.superview isKindOfClass:[MyBaseLayout class]]) { + MyBaseLayout *layoutView = (MyBaseLayout *)_view.superview; + if (!layoutView.isMyLayouting) { [_view.superview setNeedsLayout]; + } } - } - -+(NSString*)dimestrFromDime:(MyLayoutSize*)dimeobj showView:(BOOL)showView -{ - ++ (NSString *)dimenstrFromAnchor:(MyLayoutSize *)anchor showView:(BOOL)showView { NSString *viewstr = @""; - if (showView) - { - viewstr = [NSString stringWithFormat:@"View:%p.", dimeobj.view]; + if (showView) { + viewstr = [NSString stringWithFormat:@"view:%p.", anchor.view]; } - - NSString *dimeStr = @""; - - switch (dimeobj.dime) { - case MyGravity_Horz_Fill: - dimeStr = @"widthSize"; + NSString *dimenstr = @""; + + switch (anchor.anchorType) { + case MyLayoutAnchorType_Width: + dimenstr = @"widthSize"; break; - case MyGravity_Vert_Fill: - dimeStr = @"heightSize"; + case MyLayoutAnchorType_Height: + dimenstr = @"heightSize"; break; default: break; } - - return [NSString stringWithFormat:@"%@%@",viewstr,dimeStr]; - + return [NSString stringWithFormat:@"%@%@", viewstr, dimenstr]; } -#pragma mark -- Override Method +#pragma mark-- Override Methods --(NSString*)description -{ - NSString *dimeValStr = @""; - switch (_dimeValType) { - case MyLayoutValueType_Nil: - dimeValStr = @"nil"; +- (NSString *)description { + NSString *dimenStr = @""; + switch (_valType) { + case MyLayoutValType_Nil: + dimenStr = @"nil"; break; - case MyLayoutValueType_NSNumber: - dimeValStr = [_dimeVal description]; + case MyLayoutValType_Number: + dimenStr = [_val description]; break; - case MyLayoutValueType_LayoutDime: - dimeValStr = [MyLayoutSize dimestrFromDime:_dimeVal showView:YES]; + case MyLayoutValType_Wrap: + dimenStr = @"wrap"; break; - case MyLayoutValueType_Array: - { - dimeValStr = @"["; - for (NSObject *obj in _dimeVal) - { - if ([obj isMemberOfClass:[MyLayoutSize class]]) - { - dimeValStr = [dimeValStr stringByAppendingString:[MyLayoutSize dimestrFromDime:(MyLayoutSize*)obj showView:YES]]; + case MyLayoutValType_Fill: + dimenStr = @"fill"; + break; + case MyLayoutValType_LayoutSize: + dimenStr = [MyLayoutSize dimenstrFromAnchor:_val showView:YES]; + break; + case MyLayoutValType_Array: { + dimenStr = @"["; + for (NSObject *item in _val) { + if ([item isKindOfClass:[MyLayoutSize class]]) { + dimenStr = [dimenStr stringByAppendingString:[MyLayoutSize dimenstrFromAnchor:(MyLayoutSize *)item showView:YES]]; + } else { + dimenStr = [dimenStr stringByAppendingString:[item description]]; } - else - { - dimeValStr = [dimeValStr stringByAppendingString:[obj description]]; - + if (item != [_val lastObject]) { + dimenStr = [dimenStr stringByAppendingString:@", "]; } - - if (obj != [_dimeVal lastObject]) - dimeValStr = [dimeValStr stringByAppendingString:@", "]; - } - - dimeValStr = [dimeValStr stringByAppendingString:@"]"]; - + dimenStr = [dimenStr stringByAppendingString:@"]"]; } default: break; } - - return [NSString stringWithFormat:@"%@=%@, Multiply=%g, Add=%g, Max=%g, Min=%g",[MyLayoutSize dimestrFromDime:self showView:NO], dimeValStr, _multiVal, _addVal, _uBoundVal.dimeNumVal.doubleValue == CGFLOAT_MAX ? NAN : _uBoundVal.dimeNumVal.doubleValue , _lBoundVal.dimeNumVal.doubleValue == -CGFLOAT_MAX ? NAN : _lBoundVal.dimeNumVal.doubleValue]; - + + return [NSString stringWithFormat:@"%@=%@, multiple=%g, increment=%g, max=%g, min=%g", [MyLayoutSize dimenstrFromAnchor:self showView:NO], dimenStr, _multiVal, _addVal, _uBoundVal.numberVal.doubleValue == CGFLOAT_MAX ? NAN : _uBoundVal.numberVal.doubleValue, _lBoundVal.numberVal.doubleValue == -CGFLOAT_MAX ? NAN : _lBoundVal.numberVal.doubleValue]; +} + +@end + +@implementation MyLayoutSize (Clone) + +- (MyLayoutSize * (^)(CGFloat addVal, CGFloat multiVal))clone { + return ^id(CGFloat addVal, CGFloat multiVal) { + MyLayoutSize *clonedAnchor = [[[self class] allocWithZone:nil] init]; + clonedAnchor->_addVal = addVal; + clonedAnchor->_multiVal = multiVal; + clonedAnchor->_val = self; + clonedAnchor->_valType = MyLayoutValType_LayoutAnchorClone; + return clonedAnchor; + }; +} + +@end + +#pragma mark-- MyLayoutMostSize + +@implementation MyLayoutMostSize { + NSArray *_dimens; + BOOL _isMax; +} + +- (instancetype)initWith:(NSArray *)dimens isMax:(BOOL)isMax { + self = [self init]; + if (self != nil) { + _dimens = dimens; + _isMax = isMax; + } + return self; } +- (CGFloat)getMostDimenValFrom:(MyLayoutSize *)srcAnchor { + CGFloat mostDimenVal = _isMax ? -CGFLOAT_MAX : CGFLOAT_MAX; + for (id dimen in _dimens) { + CGFloat dimenVal = 0; + if ([dimen isKindOfClass:[NSNumber class]]) { + dimenVal = [(NSNumber *)dimen doubleValue]; + if (dimenVal == MyLayoutSize.wrap) { //特殊的自适应值。 + CGSize size = [srcAnchor.view sizeThatFits:CGSizeZero]; + if (srcAnchor.anchorType == MyLayoutAnchorType_Width) { + dimenVal = size.width; + } else { + dimenVal = size.height; + } + } + } else if ([dimen isKindOfClass:[MyLayoutSize class]]) { + MyLayoutSize *anchor = (MyLayoutSize *)dimen; + CGFloat increment = 0.0; + CGFloat multiple = 1.0; + if (anchor.valType == MyLayoutValType_LayoutAnchorClone) { + increment = anchor.addVal; + multiple = anchor.multiVal; + anchor = (MyLayoutSize *)anchor.val; + } + dimenVal = (anchor.anchorType == MyLayoutAnchorType_Width) ? anchor.view.myEstimatedWidth : anchor.view.myEstimatedHeight; + dimenVal *= multiple; + dimenVal += increment; + } else { + NSAssert(NO, @"oops!, invalid type, only support NSNumber or MyLayoutSize"); + } + mostDimenVal = _isMax ? _myCGFloatMax(dimenVal, mostDimenVal) : _myCGFloatMin(dimenVal, mostDimenVal); + } + return mostDimenVal; +} @end +@implementation NSArray (MyLayoutMostSize) + +- (MyLayoutMostSize *)myMinSize { + return [[MyLayoutMostSize alloc] initWith:self isMax:NO]; +} + +- (MyLayoutMostSize *)myMaxSize { + return [[MyLayoutMostSize alloc] initWith:self isMax:YES]; +} + +@end diff --git a/MyLayout/Lib/MyLayoutSizeClass.h b/MyLayout/Lib/MyLayoutSizeClass.h index f037b10..0327d26 100644 --- a/MyLayout/Lib/MyLayoutSizeClass.h +++ b/MyLayout/Lib/MyLayoutSizeClass.h @@ -6,277 +6,278 @@ // Copyright © 2016年 YoungSoft. All rights reserved. // - +#import "MyGrid.h" #import "MyLayoutDef.h" #import "MyLayoutPos.h" #import "MyLayoutSize.h" -#import "MyGrid.h" @class MyBaseLayout; -/* - SizeClass的尺寸定义,用于定义苹果设备的各种屏幕的尺寸,对于任意一种设备来说某个纬度的尺寸都可以描述为:Any任意,Compact压缩,Regular常规 - 三种形式,比如下面就列出了苹果各种设备的SizeClass定义: - - iPhone4S,iPhone5/5s,iPhone6 - 竖屏:(w:Compact h:Regular) - 横屏:(w:Compact h:Compact) - iPhone6 Plus - 竖屏:(w:Compact h:Regular) - 横屏:(w:Regular h:Compact) - iPad - 竖屏:(w:Regular h:Regular) - 横屏:(w:Regular h:Regular) - Apple Watch - 竖屏:(w:Compact h:Compact) - 横屏:(w:Compact h:Compact) - - 我们可以专门为某种设备的SizeClass来设置具体的各种子视图和布局的约束,但是为了兼容多种设备,我们提出了SizeClass的继承关系,其中的继承关系如下: - - w:Compact h:Compact 继承 (w:Any h:Compact , w:Compact h:Any , w:Any h:Any) - w:Regular h:Compact 继承 (w:Any h:Compact , w:Regular h:Any , w:Any h:Any) - w:Compact h:Regular 继承 (w:Any h:Regular , w:Compact h:Any , w:Any h:Any) - w:Regular h:Regular 继承 (w:Any h:Regular , w:Regular h:Any , w:Any h:Any) - - - 也就是说设备当前是:w:Compact h:Compact 则会找出某个视图是否定义了这个SizeClass的界面约束布局,如果没有则找w:Any h:Compact。如果找到了 - 则使用,否则继续往上找,直到w:Any h:Any这种尺寸,因为默认所有视图和布局视图的约束设置都是基于w:Any h:Any的。所以总是会找到对应的视图定义的约束的。 - - 在上述的定义中我们发现了2个问题,一个就是没有一个明确来指定横屏和竖屏这种屏幕的情况;另外一个是iPad设备的宽度和高度都是regular,而无法区分横屏和竖屏。因此这里对 - MySizeClass新增加了两个定义:竖屏MySizeClass_Portrait和横屏MySizeClass_Landscape。我们可以用这两个SizeClass来定义全局横屏以及某类设备的横屏和竖屏 - - 在默认情况下现有的布局以及子视图的约束设置都是基于w:Any h:Any的,如果我们要为某种SizeClass设置约束则可以调用视图的扩展方法: - - -(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass; - -(instancetype)fetchLayoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass; - - - 这两个方法需要传递一个宽度的MySizeClass定义和高度的MySizeClass定义,并通过 | 运算符来组合。 比如: - - 1.想设置所有iPhone设备的横屏的约束 - UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_wAny|MySizeClass_hCompact]; - - 2.想设置所有iPad设备的横屏的约束 - UIView *lsc = [某视图 fetchLayoutSizeClass: MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape]; - - 3.想设置iphone6plus下的横屏的约束 - UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_wRegular|MySizeClass_hCompact]; - - 4.想设置ipad下的约束 - UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hRegular]; - - 5.想设置所有设备下的约束,也是默认的视图的约束 - UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hAny]; - - 6.所有设备的竖屏约束: - UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_Portrait]; - - 7.所有设备的横屏约束: - UIView *lsc = [某视图 fetchLayoutSizeClass:MySizeClass_Landscape]; - - - - fetchLayoutSizeClass虽然返回的是一个instancetype,但实际得到了一个MyLayoutSizeClass对象或者其派生类,而MyLayoutSizeClass类中又定义了跟UIView一样相同的布局方法,因此虽然是返回视图对象,并设置各种约束,但实际上是设置MyLayoutSizeClass对象的各种约束。 - - */ -typedef enum : unsigned char{ - MySizeClass_wAny = 0, //宽度任意尺寸 - MySizeClass_wCompact = 1, //宽度压缩尺寸,这个属性在iOS8以下不支持 - MySizeClass_wRegular = 2, //宽度常规尺寸,这个属性在iOS8以下不支持 - - MySizeClass_hAny = 0, //高度任意尺寸 - MySizeClass_hCompact = 1 << 2, //高度压缩尺寸,这个属性在iOS8以下不支持 - MySizeClass_hRegular = 2 << 2, //高度常规尺寸,这个属性在iOS8以下不支持 +@class MyViewTraits; - MySizeClass_Any = 0x0, //所有设备,等价于MySizeClass_wAny|MySizeClass_hAny - MySizeClass_Portrait = 0x40, //竖屏 - MySizeClass_Landscape = 0x80, //横屏,注意横屏和竖屏不支持 | 运算操作,只能指定一个。 -}MySizeClass; +//视图的布局引擎。 +@interface MyLayoutEngine : NSObject +@property (nonatomic, assign) CGFloat top; +@property (nonatomic, assign) CGFloat leading; +@property (nonatomic, assign) CGFloat bottom; +@property (nonatomic, assign) CGFloat trailing; +@property (nonatomic, assign) CGFloat width; +@property (nonatomic, assign) CGFloat height; +@property (nonatomic, assign) CGPoint origin; +@property (nonatomic, assign) CGSize size; +@property (nonatomic, assign) CGRect frame; -/* - 布局的尺寸类型类,这个类的功能用来支持类似于iOS的Size Class机制用来实现各种屏幕下的视图的约束。 - MyLayoutSizeClass类中定义的各种属性跟视图和布局的各种扩展属性是一致的。 - - 我们所有的视图的默认的约束设置都是基于MySizeClass_wAny|MySizeClass_hAny这种SizeClass的。 - - 需要注意的是因为MyLayoutSizeClass是基于苹果SizeClass实现的,因此如果是iOS7的系统则只能支持MySizeClass_wAny|MySizeClass_hAny这种 - SizeClass,以及MySizeClass_Portrait或者MySizeClass_Landscape 也就是设置布局默认的约束。而iOS8以上的系统则能支持所有的SizeClass. - - */ -@interface MyViewSizeClass:NSObject +//默认的布局分类,所有视图的布局相关的属性,都是设置到这个分类上。 +@property (nonatomic, weak) MyViewTraits *defaultSizeClass; -@property(nonatomic, weak) UIView *view; +//当前的布局分类,当前正在执行布局时所用的布局分类。 +@property (nonatomic, weak) MyViewTraits *currentSizeClass; -//所有视图通用 -@property(nonatomic, strong) MyLayoutPos *topPos; -@property(nonatomic, strong) MyLayoutPos *leadingPos; -@property(nonatomic, strong) MyLayoutPos *bottomPos; -@property(nonatomic, strong) MyLayoutPos *trailingPos; -@property(nonatomic, strong) MyLayoutPos *centerXPos; -@property(nonatomic, strong) MyLayoutPos *centerYPos; +@property (nonatomic, assign, readonly) BOOL multiple; //是否设置了多个sizeclass +@property (nonatomic, strong) NSMutableDictionary *sizeClasses; -@property(nonatomic, strong,readonly) MyLayoutPos *leftPos; -@property(nonatomic, strong,readonly) MyLayoutPos *rightPos; +@property (nonatomic, assign) BOOL hasObserver; -@property(nonatomic, strong) MyLayoutPos *baselinePos; +- (void)reset; +- (MyViewTraits *)fetchView:(UIView *)view layoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass; -@property(nonatomic, assign) CGFloat myTop; -@property(nonatomic, assign) CGFloat myLeading; -@property(nonatomic, assign) CGFloat myBottom; -@property(nonatomic, assign) CGFloat myTrailing; -@property(nonatomic, assign) CGFloat myCenterX; -@property(nonatomic, assign) CGFloat myCenterY; -@property(nonatomic, assign) CGPoint myCenter; +- (MyViewTraits *)fetchView:(UIView *)view bestLayoutSizeClass:(MySizeClass)sizeClass; +- (void)setView:(UIView *)view layoutSizeClass:(MySizeClass)sizeClass withTraits:(MyViewTraits *)traits; -@property(nonatomic, assign) CGFloat myLeft; -@property(nonatomic, assign) CGFloat myRight; +@end +@interface UIView (MyLayoutExtInner) -@property(nonatomic, assign) CGFloat myMargin; -@property(nonatomic, assign) CGFloat myHorzMargin; -@property(nonatomic, assign) CGFloat myVertMargin; +@property (nonatomic, strong, readonly) MyLayoutEngine *myEngine; +@property (nonatomic, strong, readonly) MyLayoutEngine *myEngineInner; -@property(nonatomic, strong) MyLayoutSize *widthSize; -@property(nonatomic, strong) MyLayoutSize *heightSize; +- (id)createSizeClassInstance; -@property(nonatomic, assign) CGFloat myWidth; -@property(nonatomic, assign) CGFloat myHeight; -@property(nonatomic, assign) CGSize mySize; +- (instancetype)myDefaultSizeClass; +- (instancetype)myDefaultSizeClassInner; +- (instancetype)myCurrentSizeClass; +- (instancetype)myCurrentSizeClassInner; -@property(nonatomic, assign) BOOL wrapContentWidth; -@property(nonatomic, assign) BOOL wrapContentHeight; +@property (nonatomic, readonly) MyLayoutPos *topPosInner; +@property (nonatomic, readonly) MyLayoutPos *leadingPosInner; +@property (nonatomic, readonly) MyLayoutPos *bottomPosInner; +@property (nonatomic, readonly) MyLayoutPos *trailingPosInner; +@property (nonatomic, readonly) MyLayoutPos *centerXPosInner; +@property (nonatomic, readonly) MyLayoutPos *centerYPosInner; +@property (nonatomic, readonly) MyLayoutSize *widthSizeInner; +@property (nonatomic, readonly) MyLayoutSize *heightSizeInner; -@property(nonatomic, assign) BOOL wrapContentSize; +@property (nonatomic, readonly) MyLayoutPos *leftPosInner; +@property (nonatomic, readonly) MyLayoutPos *rightPosInner; -@property(nonatomic, assign) BOOL useFrame; -@property(nonatomic, assign) BOOL noLayout; +@property (nonatomic, readonly) MyLayoutPos *baselinePosInner; -@property(nonatomic, assign) MyVisibility myVisibility; -@property(nonatomic, assign) MyGravity myAlignment; +@property (nonatomic, readonly) CGFloat myEstimatedWidth; +@property (nonatomic, readonly) CGFloat myEstimatedHeight; -@property(nonatomic, copy) void (^viewLayoutCompleteBlock)(MyBaseLayout* layout, UIView *v); +@end -//线性布局和浮动布局子视图专用 -@property(nonatomic, assign) CGFloat weight; -//浮动布局子视图专用 -@property(nonatomic,assign,getter=isReverseFloat) BOOL reverseFloat; -@property(nonatomic,assign) BOOL clearFloat; -@end +/* + 布局的属性特征集合类,这个类的功能用来存储涉及到布局的各种属性。MyViewTraits类中定义的各种属性跟视图和布局的各种扩展属性是一致的。 + */ +@interface MyViewTraits : NSObject +@property (nonatomic, weak) UIView *view; -@interface MyLayoutViewSizeClass : MyViewSizeClass +//所有视图通用 +@property (nonatomic, strong) MyLayoutPos *topPos; +@property (nonatomic, strong) MyLayoutPos *leadingPos; +@property (nonatomic, strong) MyLayoutPos *bottomPos; +@property (nonatomic, strong) MyLayoutPos *trailingPos; +@property (nonatomic, strong) MyLayoutPos *centerXPos; +@property (nonatomic, strong) MyLayoutPos *centerYPos; -@property(nonatomic, assign) CGFloat topPadding; -@property(nonatomic, assign) CGFloat leadingPadding; -@property(nonatomic, assign) CGFloat bottomPadding; -@property(nonatomic, assign) CGFloat trailingPadding; -@property(nonatomic, assign) UIEdgeInsets padding; +@property (nonatomic, strong, readonly) MyLayoutPos *leftPos; +@property (nonatomic, strong, readonly) MyLayoutPos *rightPos; +@property (nonatomic, strong) MyLayoutPos *baselinePos; -@property(nonatomic, assign) CGFloat leftPadding; -@property(nonatomic, assign) CGFloat rightPadding; +@property (nonatomic, assign) CGFloat myTop; +@property (nonatomic, assign) CGFloat myLeading; +@property (nonatomic, assign) CGFloat myBottom; +@property (nonatomic, assign) CGFloat myTrailing; +@property (nonatomic, assign) CGFloat myCenterX; +@property (nonatomic, assign) CGFloat myCenterY; +@property (nonatomic, assign) CGPoint myCenter; +@property (nonatomic, assign) CGFloat myLeft; +@property (nonatomic, assign) CGFloat myRight; -@property(nonatomic, assign) BOOL zeroPadding; +@property (nonatomic, assign) CGFloat myMargin; +@property (nonatomic, assign) CGFloat myHorzMargin; +@property (nonatomic, assign) CGFloat myVertMargin; -@property(nonatomic, assign) UIRectEdge insetsPaddingFromSafeArea; -@property(nonatomic, assign) BOOL insetLandscapeFringePadding; +@property (nonatomic, strong) MyLayoutSize *widthSize; +@property (nonatomic, strong) MyLayoutSize *heightSize; +@property (nonatomic, assign) CGFloat myWidth; +@property (nonatomic, assign) CGFloat myHeight; +@property (nonatomic, assign) CGSize mySize; +@property (nonatomic, assign) BOOL wrapContentWidth; +@property (nonatomic, assign) BOOL wrapContentHeight; -@property(nonatomic ,assign) CGFloat subviewVSpace; -@property(nonatomic, assign) CGFloat subviewHSpace; -@property(nonatomic, assign) CGFloat subviewSpace; +@property (nonatomic, assign) BOOL wrapContentSize; -@property(nonatomic, assign) MyGravity gravity; +@property (nonatomic, assign) BOOL useFrame; +@property (nonatomic, assign) BOOL noLayout; -@property(nonatomic, assign) BOOL reverseLayout; //逆序布局,子视图从后往前。 +@property (nonatomic, assign) MyVisibility visibility; +@property (nonatomic, assign) MyGravity alignment; +@property (nonatomic, copy) void (^viewLayoutCompleteBlock)(MyBaseLayout *layout, UIView *v); +//线性布局和浮动布局和流式布局子视图专用 +@property (nonatomic, assign) CGFloat weight; -@end +//浮动布局子视图专用 +@property (nonatomic, assign, getter=isReverseFloat) BOOL reverseFloat; +@property (nonatomic, assign) BOOL clearFloat; +//内部属性 +@property (nonatomic, strong, readonly) MyLayoutPos *topPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *leadingPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *bottomPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *trailingPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *centerXPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *centerYPosInner; +@property (nonatomic, strong, readonly) MyLayoutSize *widthSizeInner; +@property (nonatomic, strong, readonly) MyLayoutSize *heightSizeInner; -@interface MySequentLayoutViewSizeClass : MyLayoutViewSizeClass +@property (nonatomic, strong, readonly) MyLayoutPos *leftPosInner; +@property (nonatomic, strong, readonly) MyLayoutPos *rightPosInner; -@property(nonatomic,assign) MyOrientation orientation; +@property (nonatomic, strong, readonly) MyLayoutPos *baselinePosInner; +@property (class, nonatomic, assign) BOOL isRTL; +//内部方法 +- (BOOL)invalid; -@end ++ (MyGravity)convertLeadingTrailingGravityFromLeftRightGravity:(MyGravity)horzGravity; +- (MyGravity)finalVertGravityFrom:(MyGravity)layoutVertGravity; +- (MyGravity)finalHorzGravityFrom:(MyGravity)layoutHorzGravity; +@end +@interface MyLayoutTraits : MyViewTraits +@property (nonatomic, assign) BOOL zeroPadding; -@interface MyLinearLayoutViewSizeClass : MySequentLayoutViewSizeClass +@property (nonatomic, assign) BOOL reverseLayout; +@property (nonatomic, assign) CGAffineTransform layoutTransform; //布局变换。 -@property(nonatomic, assign) MySubviewsShrinkType shrinkType; +@property (nonatomic, assign) MyGravity gravity; -@end +@property (nonatomic, assign) BOOL insetLandscapeFringePadding; +@property (nonatomic, assign) CGFloat paddingTop; +@property (nonatomic, assign) CGFloat paddingLeading; +@property (nonatomic, assign) CGFloat paddingBottom; +@property (nonatomic, assign) CGFloat paddingTrailing; +@property (nonatomic, assign) UIEdgeInsets padding; +@property (nonatomic, assign) CGFloat paddingLeft; +@property (nonatomic, assign) CGFloat paddingRight; -@interface MyTableLayoutViewSizeClass : MyLinearLayoutViewSizeClass +//兼容1.9.2以及以前的老版本,因为老版本的命名不符合规范,所以这里重新命名。 +//@property (nonatomic, assign, getter=paddingTop, setter=setPaddingTop:) CGFloat topPadding; +//@property (nonatomic, assign, getter=paddingLeading, setter=setPaddingLeading:) CGFloat leadingPadding; +//@property (nonatomic, assign, getter=paddingBottom, setter=setPaddingBottom:) CGFloat bottomPadding; +//@property (nonatomic, assign, getter=paddingTrailing, setter=setPaddingTrailing:) CGFloat trailingPadding; +//@property (nonatomic, assign, getter=paddingLeft, setter=setPaddingLeft:) CGFloat leftPadding; +//@property (nonatomic, assign, getter=paddingRight, setter=setPaddingRight:) CGFloat rightPadding; -@end +//为支持iOS11的safeArea而进行的padding的转化 +- (CGFloat)myLayoutPaddingTop; +- (CGFloat)myLayoutPaddingBottom; +- (CGFloat)myLayoutPaddingLeft; +- (CGFloat)myLayoutPaddingRight; +- (CGFloat)myLayoutPaddingLeading; +- (CGFloat)myLayoutPaddingTrailing; +@property (nonatomic, assign) UIRectEdge insetsPaddingFromSafeArea; -@interface MyFlowLayoutViewSizeClass : MySequentLayoutViewSizeClass +@property (nonatomic, assign) CGFloat subviewVSpace; +@property (nonatomic, assign) CGFloat subviewHSpace; +@property (nonatomic, assign) CGFloat subviewSpace; -@property(nonatomic,assign) NSInteger arrangedCount; -@property(nonatomic, assign) NSInteger pagedCount; -@property(nonatomic,assign) BOOL autoArrange; -@property(nonatomic,assign) MyGravity arrangedGravity; +//从全部子视图引擎数组中过滤出需要进行布局的子视图布局引擎数组子集。 +- (NSMutableArray *)filterEngines:(NSMutableArray *)subviewEngines; -@property(nonatomic, assign) CGFloat subviewSize; -@property(nonatomic, assign) CGFloat minSpace; -@property(nonatomic, assign) CGFloat maxSpace; +@end +@interface MySequentLayoutFlexSpacing : NSObject +@property (nonatomic, assign) CGFloat subviewSize; +@property (nonatomic, assign) CGFloat minSpace; +@property (nonatomic, assign) CGFloat maxSpace; +@property (nonatomic, assign) BOOL centered; +- (CGFloat)calcMaxMinSubviewSizeForContent:(CGFloat)selfSize paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding space:(CGFloat *)pSpace; +- (CGFloat)calcMaxMinSubviewSize:(CGFloat)selfSize arrangedCount:(NSInteger)arrangedCount paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding space:(CGFloat *)pSpace; @end +@interface MySequentLayoutTraits : MyLayoutTraits -@interface MyFloatLayoutViewSizeClass : MySequentLayoutViewSizeClass - -@property(nonatomic, assign) CGFloat subviewSize; -@property(nonatomic, assign) CGFloat minSpace; -@property(nonatomic, assign) CGFloat maxSpace; -@property(nonatomic,assign) BOOL noBoundaryLimit; +@property (nonatomic, assign) MyOrientation orientation; +@property (nonatomic, strong) MySequentLayoutFlexSpacing *flexSpace; @end +@interface MyLinearLayoutTraits : MySequentLayoutTraits -@interface MyRelativeLayoutViewSizeClass : MyLayoutViewSizeClass +@property (nonatomic, assign) MySubviewsShrinkType shrinkType; +@end + +@interface MyTableLayoutTraits : MyLinearLayoutTraits @end +@interface MyFlowLayoutTraits : MySequentLayoutTraits -@interface MyFrameLayoutViewSizeClass : MyLayoutViewSizeClass +@property (nonatomic, assign) MyGravity arrangedGravity; +@property (nonatomic, assign) BOOL autoArrange; +@property (nonatomic, assign) BOOL isFlex; +@property (nonatomic, assign) MyGravityPolicy lastlineGravityPolicy; +@property (nonatomic, assign) NSInteger arrangedCount; +@property (nonatomic, assign) NSInteger pagedCount; @end -@interface MyPathLayoutViewSizeClass : MyLayoutViewSizeClass - +@interface MyFloatLayoutTraits : MySequentLayoutTraits @end +@interface MyRelativeLayoutTraits : MyLayoutTraits + +@end -@interface MyGridLayoutViewSizeClass : MyLayoutViewSizeClass +@interface MyFrameLayoutTraits : MyLayoutTraits @end +@interface MyPathLayoutTraits : MyLayoutTraits +@end + +@interface MyGridLayoutTraits : MyLayoutTraits +@end diff --git a/MyLayout/Lib/MyLayoutSizeClass.m b/MyLayout/Lib/MyLayoutSizeClass.m index 26a9f6d..f36e8a9 100644 --- a/MyLayout/Lib/MyLayoutSizeClass.m +++ b/MyLayout/Lib/MyLayoutSizeClass.m @@ -7,1322 +7,1327 @@ // #import "MyLayoutSizeClass.h" +#import "MyGridNode.h" #import "MyLayoutPosInner.h" #import "MyLayoutSizeInner.h" -#import "MyGridNode.h" -#import "MyBaseLayout.h" -@interface MyViewSizeClass() -@property(nonatomic, assign) BOOL wrapWidth; -@property(nonatomic, assign) BOOL wrapHeight; +@implementation MyLayoutEngine + +- (id)init { + self = [super init]; + if (self != nil) { + _leading = CGFLOAT_MAX; + _trailing = CGFLOAT_MAX; + _top = CGFLOAT_MAX; + _bottom = CGFLOAT_MAX; + _width = CGFLOAT_MAX; + _height = CGFLOAT_MAX; + } + + return self; +} + +- (void)reset { + _leading = CGFLOAT_MAX; + _trailing = CGFLOAT_MAX; + _top = CGFLOAT_MAX; + _bottom = CGFLOAT_MAX; + _width = CGFLOAT_MAX; + _height = CGFLOAT_MAX; +} + +- (CGRect)frame { + return CGRectMake(_leading, _top, _width, _height); +} + +- (void)setFrame:(CGRect)frame { + _leading = frame.origin.x; + _top = frame.origin.y; + _width = frame.size.width; + _height = frame.size.height; + _trailing = _leading + _width; + _bottom = _top + _height; +} + +- (CGSize)size { + return CGSizeMake(_width, _height); +} + +- (void)setSize:(CGSize)size { + _width = size.width; + _height = size.height; +} + +- (CGPoint)origin { + return CGPointMake(_leading, _top); +} + +- (void)setOrigin:(CGPoint)origin { + _leading = origin.x; + _top = origin.y; +} + +- (BOOL)multiple { + return self.sizeClasses.count > 1; +} + +- (NSString *)description { + return [NSString stringWithFormat:@"leading:%g, top:%g, width:%g, height:%g, trailing:%g, bottom:%g", _leading, _top, _width, _height, _trailing, _bottom]; +} + +- (MyViewTraits *)fetchView:(UIView *)view layoutSizeClass:(MySizeClass)sizeClass copyFrom:(MySizeClass)srcSizeClass { + MyViewTraits *viewTraits = nil; + if (self.sizeClasses == nil) { + self.sizeClasses = [NSMutableDictionary new]; + } else { + viewTraits = (MyViewTraits *)[self.sizeClasses objectForKey:@(sizeClass)]; + } + + if (viewTraits == nil) { + MyViewTraits *srcTraits = (MyViewTraits *)[self.sizeClasses objectForKey:@(srcSizeClass)]; + if (srcTraits == nil) { + viewTraits = [view createSizeClassInstance]; + } else { + viewTraits = [srcTraits copy]; + } + viewTraits.view = view; + [self.sizeClasses setObject:viewTraits forKey:@(sizeClass)]; + } + return viewTraits; +} + +- (MyViewTraits *)fetchView:(UIView *)view bestLayoutSizeClass:(MySizeClass)sizeClass { + + MySizeClass wscType = sizeClass & 0x03; + MySizeClass hscType = sizeClass & 0x0C; + MySizeClass oriType = sizeClass & 0xC0; + + if (self.sizeClasses == nil) { + self.sizeClasses = [NSMutableDictionary new]; + } + MySizeClass searchType; + MyViewTraits *viewTraits = nil; + if (self.sizeClasses.count > 1) { + //first search the most exact SizeClass + searchType = wscType | hscType | oriType; + viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + if (viewTraits != nil) { + return viewTraits; + } + searchType = wscType | hscType; + if (searchType != sizeClass) { + viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + if (viewTraits != nil) { + return viewTraits; + } + } + + searchType = MySizeClass_wAny | hscType | oriType; + if (oriType != 0 && searchType != sizeClass) { + viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + if (viewTraits != nil) { + return viewTraits; + } + } + + searchType = MySizeClass_wAny | hscType; + if (searchType != sizeClass) { + viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + if (viewTraits != nil) { + return viewTraits; + } + } + + searchType = wscType | MySizeClass_hAny | oriType; + if (oriType != 0 && searchType != sizeClass) { + viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + if (viewTraits != nil) { + return viewTraits; + } + } + + searchType = wscType | MySizeClass_hAny; + if (searchType != sizeClass) { + viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + if (viewTraits != nil) { + return viewTraits; + } + } + + searchType = MySizeClass_wAny | MySizeClass_hAny | oriType; + if (oriType != 0 && searchType != sizeClass) { + viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + if (viewTraits != nil) { + return viewTraits; + } + } + } + + searchType = MySizeClass_wAny | MySizeClass_hAny; + viewTraits = [self.sizeClasses objectForKey:@(searchType)]; + if (viewTraits == nil) { + viewTraits = [view createSizeClassInstance]; + viewTraits.view = view; + [self.sizeClasses setObject:viewTraits forKey:@(searchType)]; + } + return viewTraits; +} + +- (void)setView:(UIView *)view layoutSizeClass:(MySizeClass)sizeClass withTraits:(MyViewTraits *)traits { + if (self.sizeClasses == nil) { + self.sizeClasses = [NSMutableDictionary new]; + } + traits.view = view; + traits.topPosInner.view = view; + traits.bottomPosInner.view = view; + traits.leadingPosInner.view = view; + traits.trailingPosInner.view = view; + traits.centerXPosInner.view = view; + traits.centerYPosInner.view = view; + traits.baselinePosInner.view = view; + traits.widthSizeInner.view = view; + traits.heightSizeInner.view = view; + + [self.sizeClasses setObject:traits forKey:@(sizeClass)]; +} + @end -@implementation MyViewSizeClass + +@implementation MyViewTraits BOOL _myisRTL = NO; -+(BOOL)isRTL -{ ++ (BOOL)isRTL { return _myisRTL; } -+(void)setIsRTL:(BOOL)isRTL -{ ++ (void)setIsRTL:(BOOL)isRTL { _myisRTL = isRTL; } --(id)init -{ +- (id)init { return [super init]; } - - - - --(MyLayoutPos*)topPosInner -{ +- (MyLayoutPos *)topPosInner { return _topPos; } --(MyLayoutPos*)leadingPosInner -{ +- (MyLayoutPos *)leadingPosInner { return _leadingPos; } - --(MyLayoutPos*)bottomPosInner -{ +- (MyLayoutPos *)bottomPosInner { return _bottomPos; } --(MyLayoutPos*)trailingPosInner -{ +- (MyLayoutPos *)trailingPosInner { return _trailingPos; } --(MyLayoutPos*)centerXPosInner -{ +- (MyLayoutPos *)centerXPosInner { return _centerXPos; } --(MyLayoutPos*)centerYPosInner -{ +- (MyLayoutPos *)centerYPosInner { return _centerYPos; } --(MyLayoutPos*)leftPosInner -{ - return [MyViewSizeClass isRTL] ? self.trailingPosInner : self.leadingPosInner; +- (MyLayoutPos *)leftPosInner { + return [MyViewTraits isRTL] ? self.trailingPosInner : self.leadingPosInner; } --(MyLayoutPos*)rightPosInner -{ - return [MyViewSizeClass isRTL] ? self.leadingPosInner : self.trailingPosInner; +- (MyLayoutPos *)rightPosInner { + return [MyViewTraits isRTL] ? self.leadingPosInner : self.trailingPosInner; } --(MyLayoutPos*)baselinePosInner -{ +- (MyLayoutPos *)baselinePosInner { return _baselinePos; } --(MyLayoutSize*)widthSizeInner -{ +- (MyLayoutSize *)widthSizeInner { return _widthSize; } - --(MyLayoutSize*)heightSizeInner -{ +- (MyLayoutSize *)heightSizeInner { return _heightSize; } - - //.. --(MyLayoutPos*)topPos -{ - if (_topPos == nil) - { +- (MyLayoutPos *)topPos { + if (_topPos == nil) { _topPos = [MyLayoutPos new]; _topPos.view = self.view; - _topPos.pos = MyGravity_Vert_Top; - + _topPos.anchorType = MyLayoutAnchorType_Top; } - return _topPos; } --(MyLayoutPos*)leadingPos -{ - if (_leadingPos == nil) - { +- (MyLayoutPos *)leadingPos { + if (_leadingPos == nil) { _leadingPos = [MyLayoutPos new]; _leadingPos.view = self.view; - _leadingPos.pos = MyGravity_Horz_Leading; + _leadingPos.anchorType = MyLayoutAnchorType_Leading; } - + return _leadingPos; } - --(MyLayoutPos*)bottomPos -{ - if (_bottomPos == nil) - { +- (MyLayoutPos *)bottomPos { + if (_bottomPos == nil) { _bottomPos = [MyLayoutPos new]; _bottomPos.view = self.view; - _bottomPos.pos = MyGravity_Vert_Bottom; - + _bottomPos.anchorType = MyLayoutAnchorType_Bottom; } - return _bottomPos; } - --(MyLayoutPos*)trailingPos -{ - if (_trailingPos == nil) - { +- (MyLayoutPos *)trailingPos { + if (_trailingPos == nil) { _trailingPos = [MyLayoutPos new]; _trailingPos.view = self.view; - _trailingPos.pos = MyGravity_Horz_Trailing; + _trailingPos.anchorType = MyLayoutAnchorType_Trailing; } - return _trailingPos; - } - --(MyLayoutPos*)centerXPos -{ - if (_centerXPos == nil) - { +- (MyLayoutPos *)centerXPos { + if (_centerXPos == nil) { _centerXPos = [MyLayoutPos new]; _centerXPos.view = self.view; - _centerXPos.pos = MyGravity_Horz_Center; - + _centerXPos.anchorType = MyLayoutAnchorType_CenterX; } - return _centerXPos; } --(MyLayoutPos*)centerYPos -{ - if (_centerYPos == nil) - { +- (MyLayoutPos *)centerYPos { + if (_centerYPos == nil) { _centerYPos = [MyLayoutPos new]; _centerYPos.view = self.view; - _centerYPos.pos = MyGravity_Vert_Center; - + _centerYPos.anchorType = MyLayoutAnchorType_CenterY; } - return _centerYPos; } - - --(MyLayoutPos*)leftPos -{ - return [MyViewSizeClass isRTL] ? self.trailingPos : self.leadingPos; +- (MyLayoutPos *)leftPos { + return [MyViewTraits isRTL] ? self.trailingPos : self.leadingPos; } --(MyLayoutPos*)rightPos -{ - return [MyViewSizeClass isRTL] ? self.leadingPos : self.trailingPos; +- (MyLayoutPos *)rightPos { + return [MyViewTraits isRTL] ? self.leadingPos : self.trailingPos; } --(MyLayoutPos*)baselinePos -{ - if (_baselinePos == nil) - { +- (MyLayoutPos *)baselinePos { + if (_baselinePos == nil) { _baselinePos = [MyLayoutPos new]; _baselinePos.view = self.view; - _baselinePos.pos = MyGravity_Vert_Baseline; + _baselinePos.anchorType = MyLayoutAnchorType_Baseline; } - return _baselinePos; } - - --(CGFloat)myTop -{ - return self.topPosInner.absVal; +- (CGFloat)myTop { + return self.topPosInner.measure; } --(void)setMyTop:(CGFloat)myTop -{ - [self.topPos __equalTo:@(myTop)]; +- (void)setMyTop:(CGFloat)myTop { + self.topPos.myEqualTo(@(myTop)); } - --(CGFloat)myLeading -{ - return self.leadingPosInner.absVal; +- (CGFloat)myLeading { + return self.leadingPosInner.measure; } --(void)setMyLeading:(CGFloat)myLeading -{ - [self.leadingPos __equalTo:@(myLeading)]; +- (void)setMyLeading:(CGFloat)myLeading { + self.leadingPos.myEqualTo(@(myLeading)); } - --(CGFloat)myBottom -{ - return self.bottomPosInner.absVal; +- (CGFloat)myBottom { + return self.bottomPosInner.measure; } --(void)setMyBottom:(CGFloat)myBottom -{ - [self.bottomPos __equalTo:@(myBottom)]; +- (void)setMyBottom:(CGFloat)myBottom { + self.bottomPos.myEqualTo(@(myBottom)); } - --(CGFloat)myTrailing -{ - return self.trailingPosInner.absVal; +- (CGFloat)myTrailing { + return self.trailingPosInner.measure; } --(void)setMyTrailing:(CGFloat)myTrailing -{ - [self.trailingPos __equalTo:@(myTrailing)]; +- (void)setMyTrailing:(CGFloat)myTrailing { + self.trailingPos.myEqualTo(@(myTrailing)); } - --(CGFloat)myCenterX -{ - return self.centerXPosInner.absVal; +- (CGFloat)myCenterX { + return self.centerXPosInner.measure; } --(void)setMyCenterX:(CGFloat)myCenterX -{ - [self.centerXPos __equalTo:@(myCenterX)]; +- (void)setMyCenterX:(CGFloat)myCenterX { + self.centerXPos.myEqualTo(@(myCenterX)); } --(CGFloat)myCenterY -{ - return self.centerYPosInner.absVal; +- (CGFloat)myCenterY { + return self.centerYPosInner.measure; } --(void)setMyCenterY:(CGFloat)myCenterY -{ - [self.centerYPos __equalTo:@(myCenterY)]; +- (void)setMyCenterY:(CGFloat)myCenterY { + self.centerYPos.myEqualTo(@(myCenterY)); } - --(CGPoint)myCenter -{ +- (CGPoint)myCenter { return CGPointMake(self.myCenterX, self.myCenterY); } --(void)setMyCenter:(CGPoint)myCenter -{ +- (void)setMyCenter:(CGPoint)myCenter { self.myCenterX = myCenter.x; self.myCenterY = myCenter.y; } - - --(CGFloat)myLeft -{ - return self.leftPosInner.absVal; +- (CGFloat)myLeft { + return self.leftPosInner.measure; } --(void)setMyLeft:(CGFloat)myLeft -{ - [self.leftPos __equalTo:@(myLeft)]; - +- (void)setMyLeft:(CGFloat)myLeft { + self.leftPos.myEqualTo(@(myLeft)); } --(CGFloat)myRight -{ - return self.rightPosInner.absVal; +- (CGFloat)myRight { + return self.rightPosInner.measure; } --(void)setMyRight:(CGFloat)myRight -{ - [self.rightPos __equalTo:@(myRight)]; +- (void)setMyRight:(CGFloat)myRight { + self.rightPos.myEqualTo(@(myRight)); } - - - --(CGFloat)myMargin -{ - return self.leftPosInner.absVal; +- (CGFloat)myMargin { + return self.leftPosInner.measure; } --(void)setMyMargin:(CGFloat)myMargin -{ - [self.topPos __equalTo:@(myMargin)]; - [self.leftPos __equalTo:@(myMargin)]; - [self.rightPos __equalTo:@(myMargin)]; - [self.bottomPos __equalTo:@(myMargin)]; +- (void)setMyMargin:(CGFloat)myMargin { + self.topPos.myEqualTo(@(myMargin)); + self.leftPos.myEqualTo(@(myMargin)); + self.rightPos.myEqualTo(@(myMargin)); + self.bottomPos.myEqualTo(@(myMargin)); } - --(CGFloat)myHorzMargin -{ - return self.leftPosInner.absVal; +- (CGFloat)myHorzMargin { + return self.leftPosInner.measure; } --(void)setMyHorzMargin:(CGFloat)myHorzMargin -{ - [self.leftPos __equalTo:@(myHorzMargin)]; - [self.rightPos __equalTo:@(myHorzMargin)]; +- (void)setMyHorzMargin:(CGFloat)myHorzMargin { + self.leftPos.myEqualTo(@(myHorzMargin)); + self.rightPos.myEqualTo(@(myHorzMargin)); } --(CGFloat)myVertMargin -{ - return self.topPosInner.absVal; +- (CGFloat)myVertMargin { + return self.topPosInner.measure; } --(void)setMyVertMargin:(CGFloat)myVertMargin -{ - [self.topPos __equalTo:@(myVertMargin)]; - [self.bottomPos __equalTo:@(myVertMargin)]; +- (void)setMyVertMargin:(CGFloat)myVertMargin { + self.topPos.myEqualTo(@(myVertMargin)); + self.bottomPos.myEqualTo(@(myVertMargin)); } - - - --(MyLayoutSize*)widthSize -{ - if (_widthSize == nil) - { +- (MyLayoutSize *)widthSize { + if (_widthSize == nil) { _widthSize = [MyLayoutSize new]; _widthSize.view = self.view; - _widthSize.dime = MyGravity_Horz_Fill; - + _widthSize.anchorType = MyLayoutAnchorType_Width; } - return _widthSize; } - --(MyLayoutSize*)heightSize -{ - if (_heightSize == nil) - { +- (MyLayoutSize *)heightSize { + if (_heightSize == nil) { _heightSize = [MyLayoutSize new]; _heightSize.view = self.view; - _heightSize.dime = MyGravity_Vert_Fill; - + _heightSize.anchorType = MyLayoutAnchorType_Height; } - return _heightSize; } - --(CGFloat)myWidth -{ - return self.widthSizeInner.measure; +- (CGFloat)myWidth { + //特殊处理设置为MyLayoutSize.wrap和MyLayoutSize.fill的返回。 + if (self.widthSizeInner.valType == MyLayoutValType_Wrap) { + return MyLayoutSize.wrap; + } else if (self.widthSizeInner.valType == MyLayoutValType_Fill) { + return MyLayoutSize.fill; + } else { + return self.widthSizeInner.measure; + } } --(void)setMyWidth:(CGFloat)width -{ - [self.widthSize __equalTo:@(width)]; +- (void)setMyWidth:(CGFloat)width { + self.widthSize.myEqualTo(@(width)); } --(CGFloat)myHeight -{ - return self.heightSizeInner.measure; +- (CGFloat)myHeight { + if (self.heightSizeInner.valType == MyLayoutValType_Wrap) { + return MyLayoutSize.wrap; + } else if (self.heightSizeInner.valType == MyLayoutValType_Fill) { + return MyLayoutSize.fill; + } else { + return self.heightSizeInner.measure; + } } --(void)setMyHeight:(CGFloat)height -{ - [self.heightSize __equalTo:@(height)]; +- (void)setMyHeight:(CGFloat)height { + self.heightSize.myEqualTo(@(height)); } --(CGSize)mySize -{ +- (CGSize)mySize { return CGSizeMake(self.myWidth, self.myHeight); } --(void)setMySize:(CGSize)mySize -{ +- (void)setMySize:(CGSize)mySize { self.myWidth = mySize.width; self.myHeight = mySize.height; } - - --(void)setWeight:(CGFloat)weight -{ - if (weight < 0) +- (void)setWeight:(CGFloat)weight { + if (weight < 0) { weight = 0; - - if (_weight != weight) + } + if (_weight != weight) { _weight = weight; + } } --(BOOL)wrapContentWidth -{ - return self.wrapWidth; +- (BOOL)wrapContentWidth { + return self.widthSizeInner.wrapVal; } --(BOOL)wrapContentHeight -{ - return self.wrapHeight; +- (BOOL)wrapContentHeight { + return self.heightSizeInner.wrapVal; } --(void)setWrapContentWidth:(BOOL)wrapContentWidth -{ - if (self.wrapWidth != wrapContentWidth) - { - self.wrapWidth = wrapContentWidth; - - if (wrapContentWidth) - { - self.widthSize.equalTo(self.widthSize); +- (void)setWrapContentWidth:(BOOL)wrapContentWidth { + if (wrapContentWidth) { + self.widthSize.myEqualTo(@(MyLayoutSize.wrap)); + } else { + //只有是以前设置了宽度自适应,这里取消自适应后才会将尺寸清除!目的是为了兼容老版本。 + if (_widthSize.isWrap) { + _widthSize = nil; } - else - { - if (self.widthSizeInner.dimeSelfVal != nil) - self.widthSizeInner.equalTo(nil); - } - } } --(void)setWrapContentHeight:(BOOL)wrapContentHeight -{ - if (self.wrapHeight != wrapContentHeight) - { - self.wrapHeight = wrapContentHeight; - - if (wrapContentHeight) - { - if([_view isKindOfClass:[UILabel class]]) - { - if (((UILabel*)_view).numberOfLines == 1) - ((UILabel*)_view).numberOfLines = 0; - } +- (void)setWrapContentHeight:(BOOL)wrapContentHeight { + if (wrapContentHeight) { + self.heightSize.myEqualTo(@(MyLayoutSize.wrap)); + } else { + //只有是以前设置了高度自适应,这里取消自适应后才会将尺寸清除!目的是为了兼容老版本。 + if (_heightSize.isWrap) { + _heightSize = nil; } } } --(BOOL)wrapContentSize -{ +- (BOOL)wrapContentSize { return self.wrapContentWidth && self.wrapContentHeight; } - --(void)setWrapContentSize:(BOOL)wrapContentSize -{ +- (void)setWrapContentSize:(BOOL)wrapContentSize { self.wrapContentWidth = self.wrapContentHeight = wrapContentSize; } +- (NSString *)debugDescription { + NSString *dbgDesc = [NSString stringWithFormat:@"\nView:\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%@\nweight=%f\nuseFrame=%@\nnoLayout=%@\nvisibility=%c\nalignment=%hu\nreverseFloat=%@\nclearFloat=%@", + self.topPosInner, + self.leadingPosInner, + self.bottomPosInner, + self.trailingPosInner, + self.centerXPosInner, + self.centerYPosInner, + self.widthSizeInner, + self.heightSizeInner, + self.weight, + self.useFrame ? @"YES" : @"NO", + self.noLayout ? @"YES" : @"NO", + self.visibility, + self.alignment, + self.reverseFloat ? @"YES" : @"NO", + self.clearFloat ? @"YES" : @"NO"]; - - --(NSString*)debugDescription -{ - - NSString*dbgDesc = [NSString stringWithFormat:@"\nView:\n%@\n%@\n%@\n%@\n%@\n%@\n%@\n%@\nweight=%f\nuseFrame=%@\nnoLayout=%@\nmyVisibility=%c\nmyAlignment=%hu\nwrapContentWidth=%@\nwrapContentHeight=%@\nreverseFloat=%@\nclearFloat=%@", - self.topPosInner, - self.leadingPosInner, - self.bottomPosInner, - self.trailingPosInner, - self.centerXPosInner, - self.centerYPosInner, - self.widthSizeInner, - self.heightSizeInner, - self.weight, - self.useFrame ? @"YES":@"NO", - self.noLayout? @"YES":@"NO", - self.myVisibility, - self.myAlignment, - self.wrapContentWidth ? @"YES":@"NO", - self.wrapContentHeight ? @"YES":@"NO", - self.reverseFloat ? @"YES":@"NO", - self.clearFloat ? @"YES":@"NO"]; - - return dbgDesc; } +#pragma mark-- NSCopying -#pragma mark -- NSCopying +- (id)copyWithZone:(NSZone *)zone { + MyViewTraits *layoutTraits = [[[self class] allocWithZone:zone] init]; -- (id)copyWithZone:(NSZone *)zone -{ - MyViewSizeClass *lsc = [[[self class] allocWithZone:zone] init]; - - //这里不会复制hidden属性 - lsc->_view = _view; - lsc->_topPos = [self.topPosInner copy]; - lsc->_leadingPos = [self.leadingPosInner copy]; - lsc->_bottomPos = [self.bottomPosInner copy]; - lsc->_trailingPos = [self.trailingPosInner copy]; - lsc->_centerXPos = [self.centerXPosInner copy]; - lsc->_centerYPos = [self.centerYPosInner copy]; - lsc->_baselinePos = [self.baselinePos copy]; - lsc->_widthSize = [self.widthSizeInner copy]; - lsc->_heightSize = [self.heightSizeInner copy]; - lsc->_wrapWidth = self.wrapWidth; - lsc->_wrapHeight = self.wrapHeight; - lsc.useFrame = self.useFrame; - lsc.noLayout = self.noLayout; - lsc.myVisibility = self.myVisibility; - lsc.myAlignment = self.myAlignment; - lsc.weight = self.weight; - lsc.reverseFloat = self.isReverseFloat; - lsc.clearFloat = self.clearFloat; + layoutTraits->_view = _view; + layoutTraits->_topPos = [self.topPosInner copy]; + layoutTraits->_leadingPos = [self.leadingPosInner copy]; + layoutTraits->_bottomPos = [self.bottomPosInner copy]; + layoutTraits->_trailingPos = [self.trailingPosInner copy]; + layoutTraits->_centerXPos = [self.centerXPosInner copy]; + layoutTraits->_centerYPos = [self.centerYPosInner copy]; + layoutTraits->_baselinePos = [self.baselinePos copy]; + layoutTraits->_widthSize = [self.widthSizeInner copy]; + layoutTraits->_heightSize = [self.heightSizeInner copy]; + layoutTraits.useFrame = self.useFrame; + layoutTraits.noLayout = self.noLayout; + layoutTraits.visibility = self.visibility; + layoutTraits.alignment = self.alignment; + layoutTraits.weight = self.weight; + layoutTraits.reverseFloat = self.isReverseFloat; + layoutTraits.clearFloat = self.clearFloat; + + return layoutTraits; +} + +#pragma mark -- Helper + ++ (MyGravity)convertLeadingTrailingGravityFromLeftRightGravity:(MyGravity)leftRightGravity{ + if (leftRightGravity == MyGravity_Horz_Left) { + if ([self isRTL]) { + return MyGravity_Horz_Trailing; + } else { + return MyGravity_Horz_Leading; + } + } else if (leftRightGravity == MyGravity_Horz_Right) { + if ([self isRTL]) { + return MyGravity_Horz_Leading; + } else { + return MyGravity_Horz_Trailing; + } + } else { + return leftRightGravity; + } +} + +- (BOOL)invalid { + if (self.useFrame) { + return YES; + } - return lsc; + if (self.view.isHidden) { + return self.visibility != MyVisibility_Invisible; + } else { + return self.visibility == MyVisibility_Gone; + } +} + +- (MyGravity)finalVertGravityFrom:(MyGravity)layoutVertGravity { + MyGravity finalVertGravity = MyGravity_Vert_Top; // + MyGravity vertAlignment = MYVERTGRAVITY(self.alignment); + if (layoutVertGravity != MyGravity_None) { + finalVertGravity = layoutVertGravity; + if (vertAlignment != MyGravity_None) { + finalVertGravity = vertAlignment; + } + } else { + if (vertAlignment != MyGravity_None) { + finalVertGravity = vertAlignment; + } + if (self.topPosInner.val != nil && self.bottomPosInner.val != nil) { + //只有在没有设置高度约束,或者高度约束优先级很低的情况下同时设置上下才转化为填充。 + if (self.heightSizeInner.val == nil || self.heightSizeInner.priority == MyPriority_Low) { + finalVertGravity = MyGravity_Vert_Fill; + } + } else if (self.centerYPosInner.val != nil) { + finalVertGravity = MyGravity_Vert_Center; + } else if (self.topPosInner.val != nil) { + finalVertGravity = MyGravity_Vert_Top; + } else if (self.bottomPosInner.val != nil) { + finalVertGravity = MyGravity_Vert_Bottom; + } + } + return finalVertGravity; +} + +- (MyGravity)finalHorzGravityFrom:(MyGravity)layoutHorzGravity { + MyGravity finalHorzGravity = MyGravity_Horz_Leading; + MyGravity horzAlignment = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(self.alignment)]; + if (layoutHorzGravity != MyGravity_None) { + finalHorzGravity = layoutHorzGravity; + if (horzAlignment != MyGravity_None) { + finalHorzGravity = horzAlignment; + } + } else { + if (horzAlignment != MyGravity_None) { + finalHorzGravity = horzAlignment; + } + if (self.leadingPosInner.val != nil && self.trailingPosInner.val != nil) { + //只有在没有设置宽度约束,或者宽度约束优先级很低的情况下同时设置左右才转化为填充。 + if (self.widthSizeInner.val == nil || self.widthSizeInner.priority == MyPriority_Low) { + finalHorzGravity = MyGravity_Horz_Fill; + } + } else if (self.centerXPosInner.val != nil) { + finalHorzGravity = MyGravity_Horz_Center; + } else if (self.leadingPosInner.val != nil) { + finalHorzGravity = MyGravity_Horz_Leading; + } else if (self.trailingPosInner.val != nil) { + finalHorzGravity = MyGravity_Horz_Trailing; + } + } + return finalHorzGravity; } @end -@implementation MyLayoutViewSizeClass +@implementation MyLayoutTraits --(id)init -{ +- (id)init { self = [super init]; - if (self != nil) - { + if (self != nil) { _zeroPadding = YES; _insetsPaddingFromSafeArea = UIRectEdgeLeft | UIRectEdgeRight; _insetLandscapeFringePadding = NO; - + _layoutTransform = CGAffineTransformIdentity; } - return self; } --(void)setWrapContentWidth:(BOOL)wrapContentWidth -{ - if (self.wrapWidth != wrapContentWidth) - { - self.wrapWidth = wrapContentWidth; - } - +- (UIEdgeInsets)padding { + return UIEdgeInsetsMake(self.paddingTop, self.paddingLeft, self.paddingBottom, self.paddingRight); } --(void)setWrapContentHeight:(BOOL)wrapContentHeight -{ - if (self.wrapHeight != wrapContentHeight) - { - self.wrapHeight = wrapContentHeight; - } - +- (void)setPadding:(UIEdgeInsets)padding { + self.paddingTop = padding.top; + self.paddingLeft = padding.left; + self.paddingBottom = padding.bottom; + self.paddingRight = padding.right; } - - --(UIEdgeInsets)padding -{ - return UIEdgeInsetsMake(self.topPadding, self.leftPadding, self.bottomPadding, self.rightPadding); +- (CGFloat)paddingLeft { + return [MyViewTraits isRTL] ? self.paddingTrailing : self.paddingLeading; } --(void)setPadding:(UIEdgeInsets)padding -{ - self.topPadding = padding.top; - self.leftPadding = padding.left; - self.bottomPadding = padding.bottom; - self.rightPadding = padding.right; -} - --(CGFloat)leftPadding -{ - return [MyViewSizeClass isRTL] ? self.trailingPadding : self.leadingPadding; -} - --(void)setLeftPadding:(CGFloat)leftPadding -{ - if ([MyViewSizeClass isRTL]) - { - self.trailingPadding = leftPadding; - } - else - { - self.leadingPadding = leftPadding; - } +- (void)setPaddingLeft:(CGFloat)paddingLeft { + if ([MyViewTraits isRTL]) { + self.paddingTrailing = paddingLeft; + } else { + self.paddingLeading = paddingLeft; + } } --(CGFloat)rightPadding -{ - return [MyViewSizeClass isRTL] ? self.leadingPadding : self.trailingPadding; +- (CGFloat)paddingRight { + return [MyViewTraits isRTL] ? self.paddingLeading : self.paddingTrailing; } --(void)setRightPadding:(CGFloat)rightPadding -{ - if ([MyViewSizeClass isRTL]) - { - self.leadingPadding = rightPadding; - } - else - { - self.trailingPadding = rightPadding; +- (void)setPaddingRight:(CGFloat)paddingRight { + if ([MyViewTraits isRTL]) { + self.paddingLeading = paddingRight; + } else { + self.paddingTrailing = paddingRight; } } - --(CGFloat)myLayoutTopPadding -{ +- (CGFloat)myLayoutPaddingTop { //如果padding值是特殊的值。 - if (self.topPadding >= MyLayoutPos.safeAreaMargin - 2000 && self.topPadding <= MyLayoutPos.safeAreaMargin + 2000) - { - return self.topPadding - MyLayoutPos.safeAreaMargin + CGRectGetHeight([UIApplication sharedApplication].statusBarFrame); + if (self.paddingTop >= MyLayoutPos.safeAreaMargin - 2000 && self.paddingTop <= MyLayoutPos.safeAreaMargin + 2000) { + CGFloat paddingTopAdd = 20.0; //默认高度是状态栏的高度。 +#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) + if (@available(iOS 11.0, *)) { + paddingTopAdd = self.view.safeAreaInsets.top; + } +#endif + return self.paddingTop - MyLayoutPos.safeAreaMargin + paddingTopAdd; } - - if ((self.insetsPaddingFromSafeArea & UIRectEdgeTop) == UIRectEdgeTop) - { + + if ((self.insetsPaddingFromSafeArea & UIRectEdgeTop) == UIRectEdgeTop) { #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - return self.topPadding + self.view.safeAreaInsets.top; + return self.paddingTop + self.view.safeAreaInsets.top; } #endif } - - return self.topPadding; + return self.paddingTop; } --(CGFloat)myLayoutBottomPadding -{ +- (CGFloat)myLayoutPaddingBottom { //如果padding值是特殊的值。 - if (self.bottomPadding >= MyLayoutPos.safeAreaMargin - 2000 && self.bottomPadding <= MyLayoutPos.safeAreaMargin + 2000) - { - CGFloat bottomPaddingAdd = 0; -#if TARGET_OS_IOS - //如果设备是iPhoneX就特殊处理。竖屏是34,横屏是21 - if ([UIScreen mainScreen].bounds.size.height == 812 || [UIScreen mainScreen].bounds.size.width == 812) - { - if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) - bottomPaddingAdd = 21; - else - bottomPaddingAdd = 34; - } + if (self.paddingBottom >= MyLayoutPos.safeAreaMargin - 2000 && self.paddingBottom <= MyLayoutPos.safeAreaMargin + 2000) { + CGFloat paddingBottomAdd = 0; +#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) + if (@available(iOS 11.0, *)) { + paddingBottomAdd = self.view.safeAreaInsets.bottom; + } #endif - return self.bottomPadding - MyLayoutPos.safeAreaMargin + bottomPaddingAdd; + return self.paddingBottom - MyLayoutPos.safeAreaMargin + paddingBottomAdd; } - - if ((self.insetsPaddingFromSafeArea & UIRectEdgeBottom) == UIRectEdgeBottom ) - { -#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) + if ((self.insetsPaddingFromSafeArea & UIRectEdgeBottom) == UIRectEdgeBottom) { +#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) if (@available(iOS 11.0, *)) { - - return self.bottomPadding + self.view.safeAreaInsets.bottom; + return self.paddingBottom + self.view.safeAreaInsets.bottom; } #endif } - - return self.bottomPadding; + return self.paddingBottom; } --(CGFloat)myLayoutLeadingPadding -{ - if (self.leadingPadding >= MyLayoutPos.safeAreaMargin - 2000 && self.leadingPadding <= MyLayoutPos.safeAreaMargin + 2000) - { - CGFloat leadingPaddingAdd = 0; -#if TARGET_OS_IOS - //如果设备是iPhoneX就特殊处理。竖屏是34,横屏是21 - if ([UIScreen mainScreen].bounds.size.height == 812 || [UIScreen mainScreen].bounds.size.width == 812) - { - if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) - leadingPaddingAdd = 44; - else - leadingPaddingAdd = 0; +- (CGFloat)myLayoutPaddingLeading { + if (self.paddingLeading >= MyLayoutPos.safeAreaMargin - 2000 && self.paddingLeading <= MyLayoutPos.safeAreaMargin + 2000) { + CGFloat paddingLeadingAdd = 0; +#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) + if (@available(iOS 11.0, *)) { + paddingLeadingAdd = self.view.safeAreaInsets.left; //因为这里左右的缩进都是一样的,因此不需要考虑RTL的情况。 } #endif - return self.leadingPadding - MyLayoutPos.safeAreaMargin + leadingPaddingAdd; + return self.paddingLeading - MyLayoutPos.safeAreaMargin + paddingLeadingAdd; } - CGFloat inset = 0; - #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - - UIRectEdge edge = [MyViewSizeClass isRTL]? UIRectEdgeRight:UIRectEdgeLeft; + UIRectEdge edge = [MyViewTraits isRTL] ? UIRectEdgeRight : UIRectEdgeLeft; #if TARGET_OS_IOS - UIDeviceOrientation devori = [MyViewSizeClass isRTL]? UIDeviceOrientationLandscapeLeft: UIDeviceOrientationLandscapeRight; + UIDeviceOrientation devori = [MyViewTraits isRTL] ? UIDeviceOrientationLandscapeLeft : UIDeviceOrientationLandscapeRight; #endif - if ((self.insetsPaddingFromSafeArea & edge) == edge) - { + if ((self.insetsPaddingFromSafeArea & edge) == edge) { #if TARGET_OS_IOS - //如果只缩进刘海那一边。并且同时设置了左右缩进,并且当前刘海方向是尾部那么就不缩进了。 if (self.insetLandscapeFringePadding && (self.insetsPaddingFromSafeArea & (UIRectEdgeLeft | UIRectEdgeRight)) == (UIRectEdgeLeft | UIRectEdgeRight) && - [UIDevice currentDevice].orientation == devori) - { + [UIDevice currentDevice].orientation == devori) { inset = 0; - } - else + } else #endif - inset = [MyViewSizeClass isRTL]? self.view.safeAreaInsets.right : self.view.safeAreaInsets.left; + inset = [MyViewTraits isRTL] ? self.view.safeAreaInsets.right : self.view.safeAreaInsets.left; } } #endif - - return self.leadingPadding + inset; + return self.paddingLeading + inset; } --(CGFloat)myLayoutTrailingPadding -{ - if (self.trailingPadding >= MyLayoutPos.safeAreaMargin - 2000 && self.trailingPadding <= MyLayoutPos.safeAreaMargin + 2000) - { - CGFloat trailingPaddingAdd = 0; -#if TARGET_OS_IOS - //如果设备是iPhoneX就特殊处理。竖屏是34,横屏是21 - if ([UIScreen mainScreen].bounds.size.height == 812 || [UIScreen mainScreen].bounds.size.width == 812) - { - if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) - trailingPaddingAdd = 44; - else - trailingPaddingAdd = 0; +- (CGFloat)myLayoutPaddingTrailing { + if (self.paddingTrailing >= MyLayoutPos.safeAreaMargin - 2000 && self.paddingTrailing <= MyLayoutPos.safeAreaMargin + 2000) { + CGFloat paddingTrailingAdd = 0; +#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) + if (@available(iOS 11.0, *)) { + paddingTrailingAdd = self.view.safeAreaInsets.right; } #endif - return self.trailingPadding - MyLayoutPos.safeAreaMargin + trailingPaddingAdd; + return self.paddingTrailing - MyLayoutPos.safeAreaMargin + paddingTrailingAdd; } - CGFloat inset = 0; - #if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 110000) || (__TV_OS_VERSION_MAX_ALLOWED >= 110000) - if (@available(iOS 11.0, *)) { - UIRectEdge edge = [MyViewSizeClass isRTL]? UIRectEdgeLeft:UIRectEdgeRight; + UIRectEdge edge = [MyViewTraits isRTL] ? UIRectEdgeLeft : UIRectEdgeRight; #if TARGET_OS_IOS - UIDeviceOrientation devori = [MyViewSizeClass isRTL]? UIDeviceOrientationLandscapeRight: UIDeviceOrientationLandscapeLeft; + UIDeviceOrientation devori = [MyViewTraits isRTL] ? UIDeviceOrientationLandscapeRight : UIDeviceOrientationLandscapeLeft; #endif - if ((self.insetsPaddingFromSafeArea & edge) == edge) - { + if ((self.insetsPaddingFromSafeArea & edge) == edge) { #if TARGET_OS_IOS //如果只缩进刘海那一边。并且同时设置了左右缩进,并且当前刘海方向是头部那么就不缩进了。 if (self.insetLandscapeFringePadding && (self.insetsPaddingFromSafeArea & (UIRectEdgeLeft | UIRectEdgeRight)) == (UIRectEdgeLeft | UIRectEdgeRight) && - [UIDevice currentDevice].orientation == devori) - { + [UIDevice currentDevice].orientation == devori) { inset = 0; - } - else + } else #endif - inset = [MyViewSizeClass isRTL]? self.view.safeAreaInsets.left : self.view.safeAreaInsets.right; + inset = [MyViewTraits isRTL] ? self.view.safeAreaInsets.left : self.view.safeAreaInsets.right; } } #endif - - return self.trailingPadding + inset; + return self.paddingTrailing + inset; } --(CGFloat)myLayoutLeftPadding -{ - return [MyViewSizeClass isRTL] ? [self myLayoutTrailingPadding] : [self myLayoutLeadingPadding]; +- (CGFloat)myLayoutPaddingLeft { + return [MyViewTraits isRTL] ? [self myLayoutPaddingTrailing] : [self myLayoutPaddingLeading]; } --(CGFloat)myLayoutRightPadding -{ - return [MyViewSizeClass isRTL] ? [self myLayoutLeadingPadding] : [self myLayoutTrailingPadding]; +- (CGFloat)myLayoutPaddingRight { + return [MyViewTraits isRTL] ? [self myLayoutPaddingLeading] : [self myLayoutPaddingTrailing]; } - --(CGFloat)subviewSpace -{ +- (CGFloat)subviewSpace { return self.subviewVSpace; } --(void)setSubviewSpace:(CGFloat)subviewSpace -{ +- (void)setSubviewSpace:(CGFloat)subviewSpace { self.subviewVSpace = subviewSpace; self.subviewHSpace = subviewSpace; } +- (id)copyWithZone:(NSZone *)zone { + MyLayoutTraits *layoutTraits = [super copyWithZone:zone]; + layoutTraits.paddingTop = self.paddingTop; + layoutTraits.paddingLeading = self.paddingLeading; + layoutTraits.paddingBottom = self.paddingBottom; + layoutTraits.paddingTrailing = self.paddingTrailing; + layoutTraits.zeroPadding = self.zeroPadding; + layoutTraits.insetsPaddingFromSafeArea = self.insetsPaddingFromSafeArea; + layoutTraits.insetLandscapeFringePadding = self.insetLandscapeFringePadding; + layoutTraits.gravity = self.gravity; + layoutTraits.reverseLayout = self.reverseLayout; + layoutTraits.layoutTransform = self.layoutTransform; + layoutTraits.subviewVSpace = self.subviewVSpace; + layoutTraits.subviewHSpace = self.subviewHSpace; -- (id)copyWithZone:(NSZone *)zone -{ - MyLayoutViewSizeClass *lsc = [super copyWithZone:zone]; - lsc.topPadding = self.topPadding; - lsc.leadingPadding = self.leadingPadding; - lsc.bottomPadding = self.bottomPadding; - lsc.trailingPadding = self.trailingPadding; - lsc.zeroPadding = self.zeroPadding; - lsc.insetsPaddingFromSafeArea = self.insetsPaddingFromSafeArea; - lsc.insetLandscapeFringePadding = self.insetLandscapeFringePadding; - lsc.gravity = self.gravity; - lsc.reverseLayout = self.reverseLayout; - lsc.subviewVSpace = self.subviewVSpace; - lsc.subviewHSpace = self.subviewHSpace; - - return lsc; + return layoutTraits; } --(NSString*)debugDescription -{ +- (NSString *)debugDescription { NSString *dbgDesc = [super debugDescription]; - dbgDesc = [NSString stringWithFormat:@"%@\nLayout:\npadding=%@\nzeroPadding=%@\ngravity=%hu\nreverseLayout=%@\nsubviewVertSpace=%f\nsubviewHorzSpace=%f", - dbgDesc, - NSStringFromUIEdgeInsets(self.padding), - self.zeroPadding?@"YES":@"NO", - self.gravity, - self.reverseLayout ? @"YES":@"NO", - self.subviewVSpace, - self.subviewHSpace - ]; - - + dbgDesc, + NSStringFromUIEdgeInsets(self.padding), + self.zeroPadding ? @"YES" : @"NO", + self.gravity, + self.reverseLayout ? @"YES" : @"NO", + self.subviewVSpace, + self.subviewHSpace]; + return dbgDesc; } +#pragma mark -- Helper -@end - - -@implementation MySequentLayoutViewSizeClass - - - -- (id)copyWithZone:(NSZone *)zone -{ - MySequentLayoutViewSizeClass *lsc = [super copyWithZone:zone]; - lsc.orientation = self.orientation; - +- (NSMutableArray *)filterEngines:(NSMutableArray *)subviewEngines { - return lsc; -} - --(NSString*)debugDescription -{ - NSString *dbgDesc = [super debugDescription]; + if (subviewEngines == nil) { + subviewEngines = [NSMutableArray arrayWithCapacity:self.view.subviews.count]; + for (UIView *subview in self.view.subviews) { + [subviewEngines addObject:subview.myEngine]; + } + } - dbgDesc = [NSString stringWithFormat:@"%@\nSequentLayout: \norientation=%lu", - dbgDesc, - (unsigned long)self.orientation - ]; + NSInteger count = subviewEngines.count; + if (self.reverseLayout) { + //翻转一个数组。 + for (NSInteger i = 0; i < count / 2; i++) { + [subviewEngines exchangeObjectAtIndex:i withObjectAtIndex:count - 1 - i]; + } + } + for (NSInteger i = count - 1; i >= 0; i--) { + MyViewTraits *subviewTraits = subviewEngines[i].currentSizeClass; + if ([subviewTraits invalid]) { + [subviewEngines removeObjectAtIndex:i]; + } + } - return dbgDesc; + return subviewEngines; } - @end +@implementation MySequentLayoutFlexSpacing -@implementation MyLinearLayoutViewSizeClass +- (CGFloat)calcMaxMinSubviewSizeForContent:(CGFloat)selfSize paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding space:(CGFloat *)pSpace { + CGFloat subviewSize = self.subviewSize; -- (id)copyWithZone:(NSZone *)zone -{ - MyLinearLayoutViewSizeClass *lsc = [super copyWithZone:zone]; - - lsc.shrinkType = self.shrinkType; - - return lsc; -} - --(NSString*)debugDescription -{ - NSString *dbgDesc = [super debugDescription]; - - dbgDesc = [NSString stringWithFormat:@"%@\nLinearLayout: \nshrinkType=%lu", - dbgDesc, - (unsigned long)self.shrinkType - ]; - - - return dbgDesc; + CGFloat extralSpace = self.minSpace; + if (self.centered) { + extralSpace *= -1; + } + NSInteger rowCount = MAX(floor((selfSize - (*pStarPadding) - (*pEndPadding) + extralSpace) / (subviewSize + self.minSpace)), 1); + NSInteger spaceCount = rowCount - 1; + if (self.centered) { + spaceCount += 2; + } + if (spaceCount > 0) { + *pSpace = (selfSize - (*pStarPadding) - (*pEndPadding) - subviewSize * rowCount) / spaceCount; + if (_myCGFloatGreat(*pSpace, self.maxSpace)) { + *pSpace = self.maxSpace; + subviewSize = (selfSize - (*pStarPadding) - (*pEndPadding) - (*pSpace) * spaceCount) / rowCount; + } + if (self.centered) { + *pStarPadding = (*pStarPadding + *pSpace); + *pEndPadding = (*pEndPadding + *pSpace); + } + } + return subviewSize; } +- (CGFloat)calcMaxMinSubviewSize:(CGFloat)selfSize arrangedCount:(NSInteger)arrangedCount paddingStart:(CGFloat *)pStarPadding paddingEnd:(CGFloat *)pEndPadding space:(CGFloat *)pSpace { + CGFloat subviewSize = self.subviewSize; + NSInteger spaceCount = arrangedCount - 1; + if (self.centered) { + spaceCount += 2; + } + if (spaceCount > 0) { + *pSpace = (selfSize - *pStarPadding - *pEndPadding - subviewSize * arrangedCount) / spaceCount; -@end - - -@implementation MyTableLayoutViewSizeClass + if (_myCGFloatGreat(*pSpace, self.maxSpace) || _myCGFloatLess(*pSpace, self.minSpace)) { + if (_myCGFloatGreat(*pSpace, self.maxSpace)) { + *pSpace = self.maxSpace; + } + if (_myCGFloatLess(*pSpace, self.minSpace)) { + *pSpace = self.minSpace; + } + subviewSize = (selfSize - *pStarPadding - *pEndPadding - (*pSpace) * spaceCount) / arrangedCount; + } + if (self.centered) { + *pStarPadding = (*pStarPadding + *pSpace); + *pEndPadding = (*pEndPadding + *pSpace); + } + } + return subviewSize; +} @end -@implementation MyFloatLayoutViewSizeClass +@implementation MySequentLayoutTraits + +- (id)copyWithZone:(NSZone *)zone { + MySequentLayoutTraits *layoutTraits = [super copyWithZone:zone]; + layoutTraits.orientation = self.orientation; + if (self.flexSpace != nil) { + layoutTraits.flexSpace = [MySequentLayoutFlexSpacing new]; + layoutTraits.flexSpace.subviewSize = self.flexSpace.subviewSize; + layoutTraits.flexSpace.minSpace = self.flexSpace.minSpace; + layoutTraits.flexSpace.maxSpace = self.flexSpace.maxSpace; + layoutTraits.flexSpace.centered = self.flexSpace.centered; + } -- (id)copyWithZone:(NSZone *)zone -{ - MyFloatLayoutViewSizeClass *lsc = [super copyWithZone:zone]; - - lsc.subviewSize = self.subviewSize; - lsc.minSpace = self.minSpace; - lsc.maxSpace = self.maxSpace; - lsc.noBoundaryLimit = self.noBoundaryLimit; - - return lsc; + return layoutTraits; } - --(NSString*)debugDescription -{ +- (NSString *)debugDescription { NSString *dbgDesc = [super debugDescription]; - - dbgDesc = [NSString stringWithFormat:@"%@\nFloatLayout: \nnoBoundaryLimit=%@", - dbgDesc, - self.noBoundaryLimit ? @"YES":@"NO"]; - + dbgDesc = [NSString stringWithFormat:@"%@\nSequentLayout: \norientation=%lu", + dbgDesc, + (unsigned long)self.orientation]; + return dbgDesc; } - - @end +@implementation MyLinearLayoutTraits -@implementation MyFlowLayoutViewSizeClass - -- (id)copyWithZone:(NSZone *)zone -{ - MyFlowLayoutViewSizeClass *lsc = [super copyWithZone:zone]; - - lsc.arrangedCount = self.arrangedCount; - lsc.autoArrange = self.autoArrange; - lsc.arrangedGravity = self.arrangedGravity; - lsc.subviewSize = self.subviewSize; - lsc.minSpace = self.minSpace; - lsc.maxSpace = self.maxSpace; - lsc.pagedCount = self.pagedCount; - - return lsc; +- (id)copyWithZone:(NSZone *)zone { + MyLinearLayoutTraits *layoutTraits = [super copyWithZone:zone]; + layoutTraits.shrinkType = self.shrinkType; + return layoutTraits; } - --(NSString*)debugDescription -{ +- (NSString *)debugDescription { NSString *dbgDesc = [super debugDescription]; - - dbgDesc = [NSString stringWithFormat:@"%@\nFlowLayout: \narrangedCount=%ld\nautoArrange=%@\narrangedGravity=%hu\npagedCount=%ld", - dbgDesc, - (long)self.arrangedCount, - self.autoArrange ? @"YES":@"NO", - self.arrangedGravity, - (long)self.pagedCount - ]; - + dbgDesc = [NSString stringWithFormat:@"%@\nLinearLayout: \nshrinkType=%lu", + dbgDesc, + (unsigned long)self.shrinkType]; return dbgDesc; } +@end + +@implementation MyTableLayoutTraits @end +@implementation MyFloatLayoutTraits -@implementation MyRelativeLayoutViewSizeClass +- (id)copyWithZone:(NSZone *)zone { + MyFloatLayoutTraits *layoutTraits = [super copyWithZone:zone]; + return layoutTraits; +} @end -@implementation MyFrameLayoutViewSizeClass - +@implementation MyFlowLayoutTraits +- (id)copyWithZone:(NSZone *)zone { + MyFlowLayoutTraits *layoutTraits = [super copyWithZone:zone]; + layoutTraits.arrangedCount = self.arrangedCount; + layoutTraits.autoArrange = self.autoArrange; + layoutTraits.isFlex = self.isFlex; + layoutTraits.lastlineGravityPolicy = self.lastlineGravityPolicy; + layoutTraits.arrangedGravity = self.arrangedGravity; + layoutTraits.pagedCount = self.pagedCount; + return layoutTraits; +} -@end +- (NSString *)debugDescription { + NSString *dbgDesc = [super debugDescription]; + dbgDesc = [NSString stringWithFormat:@"%@\nFlowLayout: \narrangedCount=%ld\nautoArrange=%@\nisFlex=%@\narrangedGravity=%hu\npagedCount=%ld", + dbgDesc, + (long)self.arrangedCount, + self.autoArrange ? @"YES" : @"NO", + self.isFlex ? @"YES" : @"NO", + self.arrangedGravity, + (long)self.pagedCount]; -@implementation MyPathLayoutViewSizeClass + return dbgDesc; +} @end +@implementation MyRelativeLayoutTraits -@interface MyGridLayoutViewSizeClass() +@end -@property(nonatomic, strong) MyGridNode *rootGrid; +@implementation MyFrameLayoutTraits +@end +@implementation MyPathLayoutTraits @end +@interface MyGridLayoutTraits () -@implementation MyGridLayoutViewSizeClass +@property (nonatomic, strong) MyGridNode *rootGrid; +@end --(MyGridNode*)rootGrid -{ - if (_rootGrid == nil) - { +@implementation MyGridLayoutTraits + +- (MyGridNode *)rootGrid { + if (_rootGrid == nil) { _rootGrid = [[MyGridNode alloc] initWithMeasure:0 superGrid:nil]; } return _rootGrid; } //添加行栅格,返回新的栅格。 --(id)addRow:(CGFloat)measure -{ +- (id)addRow:(CGFloat)measure { id node = (id)[self.rootGrid addRow:measure]; node.superGrid = self; return node; } //添加列栅格,返回新的栅格。 --(id)addCol:(CGFloat)measure -{ +- (id)addCol:(CGFloat)measure { id node = (id)[self.rootGrid addCol:measure]; node.superGrid = self; return node; } //添加栅格,返回被添加的栅格。这个方法和下面的cloneGrid配合使用可以用来构建那些需要重复添加栅格的场景。 --(id)addRowGrid:(id)grid -{ +- (id)addRowGrid:(id)grid { id node = (id)[self.rootGrid addRowGrid:grid]; node.superGrid = self; return node; } --(id)addColGrid:(id)grid -{ +- (id)addColGrid:(id)grid { id node = (id)[self.rootGrid addColGrid:grid]; node.superGrid = self; return node; } --(id)addRowGrid:(id)grid measure:(CGFloat)measure -{ +- (id)addRowGrid:(id)grid measure:(CGFloat)measure { id node = (id)[self.rootGrid addRowGrid:grid measure:measure]; node.superGrid = self; return node; - } --(id)addColGrid:(id)grid measure:(CGFloat)measure -{ +- (id)addColGrid:(id)grid measure:(CGFloat)measure { id node = (id)[self.rootGrid addColGrid:grid measure:measure]; node.superGrid = self; return node; - } - //克隆出一个新栅格以及其下的所有子栅格。 --(id)cloneGrid -{ +- (id)cloneGrid { return nil; } //从父栅格中删除。 --(void)removeFromSuperGrid -{ +- (void)removeFromSuperGrid { } //得到父栅格。 --(id)superGrid -{ +- (id)superGrid { return nil; } --(void)setSuperGrid:(id)superGrid -{ - +- (void)setSuperGrid:(id)superGrid { } --(BOOL)placeholder -{ +- (BOOL)placeholder { return NO; } --(void)setPlaceholder:(BOOL)placeholder -{ +- (void)setPlaceholder:(BOOL)placeholder { } --(BOOL)anchor -{ +- (BOOL)anchor { return NO; } --(void)setAnchor:(BOOL)anchor -{ +- (void)setAnchor:(BOOL)anchor { //do nothing } --(MyGravity)overlap -{ +- (MyGravity)overlap { return self.gravity; } --(void)setOverlap:(MyGravity)overlap -{ +- (void)setOverlap:(MyGravity)overlap { self.gravity = overlap; } - --(NSInteger)tag -{ +- (NSInteger)tag { return self.view.tag; } --(void)setTag:(NSInteger)tag -{ +- (void)setTag:(NSInteger)tag { self.view.tag = tag; } --(id)actionData -{ +- (id)actionData { return self.rootGrid.actionData; } --(void)setActionData:(id)actionData -{ +- (void)setActionData:(id)actionData { self.rootGrid.actionData = actionData; } --(void)setTarget:(id)target action:(SEL)action -{ +- (void)setTarget:(id)target action:(SEL)action { //do nothing. } //得到所有子栅格 --(NSArray> *)subGrids -{ +- (NSArray> *)subGrids { return self.rootGrid.subGrids; } - --(void)setSubGrids:(NSMutableArray *)subGrids -{ +- (void)setSubGrids:(NSMutableArray *)subGrids { self.rootGrid.subGrids = subGrids; } --(MySubGridsType)subGridsType -{ +- (MySubGridsType)subGridsType { return self.rootGrid.subGridsType; } --(void)setSubGridsType:(MySubGridsType)subGridsType -{ +- (void)setSubGridsType:(MySubGridsType)subGridsType { self.rootGrid.subGridsType = subGridsType; } - --(MyBorderline*)topBorderline -{ +- (MyBorderline *)topBorderline { return nil; } --(void)setTopBorderline:(MyBorderline *)topBorderline -{ +- (void)setTopBorderline:(MyBorderline *)topBorderline { } - --(MyBorderline*)bottomBorderline -{ +- (MyBorderline *)bottomBorderline { return nil; } --(void)setBottomBorderline:(MyBorderline *)bottomBorderline -{ +- (void)setBottomBorderline:(MyBorderline *)bottomBorderline { } - --(MyBorderline*)leftBorderline -{ +- (MyBorderline *)leftBorderline { return nil; } --(void)setLeftBorderline:(MyBorderline *)leftBorderline -{ +- (void)setLeftBorderline:(MyBorderline *)leftBorderline { } - --(MyBorderline*)rightBorderline -{ +- (MyBorderline *)rightBorderline { return nil; } --(void)setRightBorderline:(MyBorderline *)rightBorderline -{ +- (void)setRightBorderline:(MyBorderline *)rightBorderline { } --(MyBorderline*)leadingBorderline -{ +- (MyBorderline *)leadingBorderline { return nil; } --(void)setLeadingBorderline:(MyBorderline *)leadingBorderline -{ - +- (void)setLeadingBorderline:(MyBorderline *)leadingBorderline { } --(MyBorderline*)trailingBorderline -{ +- (MyBorderline *)trailingBorderline { return nil; } --(void)setTrailingBorderline:(MyBorderline *)trailingBorderline -{ - +- (void)setTrailingBorderline:(MyBorderline *)trailingBorderline { } - --(NSDictionary*)gridDictionary -{ +- (NSDictionary *)gridDictionary { return [MyGridNode translateGridNode:self toGridDictionary:[NSMutableDictionary new]]; } --(void)setGridDictionary:(NSDictionary *)gridDictionary -{ +- (void)setGridDictionary:(NSDictionary *)gridDictionary { MyGridNode *rootNode = self.rootGrid; [rootNode.subGrids removeAllObjects]; rootNode.subGridsType = MySubGridsType_Unknown; - + [self.view setNeedsLayout]; - - if (gridDictionary == nil) + + if (gridDictionary == nil) { return; - + } [MyGridNode translateGridDicionary:gridDictionary toGridNode:self]; } --(CGFloat)measure -{ +- (CGFloat)measure { return MyLayoutSize.fill; //return self.rootGrid.measure; } --(void)setMeasure:(CGFloat)measure -{ +- (void)setMeasure:(CGFloat)measure { //self.rootGrid.measure = measure; } --(CGRect)gridRect -{ +- (CGRect)gridRect { return self.rootGrid.gridRect; } --(void)setGridRect:(CGRect)gridRect -{ +- (void)setGridRect:(CGRect)gridRect { self.rootGrid.gridRect = gridRect; } //更新格子尺寸。 --(CGFloat)updateGridSize:(CGSize)superSize superGrid:(id)superGrid withMeasure:(CGFloat)measure -{ +- (CGFloat)updateGridSize:(CGSize)superSize superGrid:(id)superGrid withMeasure:(CGFloat)measure { return [self.rootGrid updateGridSize:superSize superGrid:superGrid withMeasure:measure]; } +- (CGFloat)updateWrapGridSizeInSuperGrid:(id)superGrid withMeasure:(CGFloat)measure { + return [self.rootGrid updateWrapGridSizeInSuperGrid:superGrid withMeasure:measure]; +} --(CGFloat)updateGridOrigin:(CGPoint)superOrigin superGrid:(id)superGrid withOffset:(CGFloat)offset -{ + +- (CGFloat)updateGridOrigin:(CGPoint)superOrigin superGrid:(id)superGrid withOffset:(CGFloat)offset { return [self.rootGrid updateGridOrigin:superOrigin superGrid:superGrid withOffset:offset]; } - --(UIView*)gridLayoutView -{ +- (UIView *)gridLayoutView { return self.view; } --(SEL)gridAction -{ +- (SEL)gridAction { return nil; } --(void)setBorderlineNeedLayoutIn:(CGRect)rect withLayer:(CALayer *)layer -{ +- (void)setBorderlineNeedLayoutIn:(CGRect)rect withLayer:(CALayer *)layer { [self.rootGrid setBorderlineNeedLayoutIn:rect withLayer:layer]; } --(void)showBorderline:(BOOL)show -{ +- (void)showBorderline:(BOOL)show { [self.rootGrid showBorderline:show]; } --(id)gridHitTest:(CGPoint)point -{ +- (id)gridHitTest:(CGPoint)point { return [self.rootGrid gridHitTest:point]; } - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -{ +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //do nothing; } -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event -{ +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { //do nothing; } -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event -{ +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { //do nothing; } -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event -{ +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { //do nothing; } - - - -- (id)copyWithZone:(NSZone *)zone -{ - MyGridLayoutViewSizeClass *lsc = [super copyWithZone:zone]; - lsc->_rootGrid = (MyGridNode*)[self.rootGrid cloneGrid]; - - +- (id)copyWithZone:(NSZone *)zone { + MyGridLayoutTraits *lsc = [super copyWithZone:zone]; + lsc->_rootGrid = (MyGridNode *)[self.rootGrid cloneGrid]; return lsc; } - @end diff --git a/MyLayout/Lib/MyLayoutSizeInner.h b/MyLayout/Lib/MyLayoutSizeInner.h index 3fd7990..c21d157 100644 --- a/MyLayout/Lib/MyLayoutSizeInner.h +++ b/MyLayout/Lib/MyLayoutSizeInner.h @@ -6,44 +6,68 @@ // Copyright (c) 2015年 YoungSoft. All rights reserved. // +#import "MyLayoutMath.h" #import "MyLayoutSize.h" - //尺寸对象内部定义 -@interface MyLayoutSize() - -@property(nonatomic, weak) UIView *view; -@property(nonatomic, assign) MyGravity dime; -@property(nonatomic, assign) MyLayoutValueType dimeValType; - -@property(nonatomic, readonly, strong) NSNumber *dimeNumVal; -@property(nonatomic, readonly, strong) MyLayoutSize *dimeRelaVal; -@property(nonatomic, readonly, strong) NSArray *dimeArrVal; -@property(nonatomic, readonly, strong) MyLayoutSize *dimeSelfVal; - -@property(nonatomic, readonly, strong) MyLayoutSize *lBoundVal; -@property(nonatomic, readonly, strong) MyLayoutSize *uBoundVal; - -@property(nonatomic, readonly, strong) MyLayoutSize *lBoundValInner; -@property(nonatomic, readonly, strong) MyLayoutSize *uBoundValInner; +@interface MyLayoutSize () + +@property (nonatomic, weak) UIView *view; +@property (nonatomic, assign) MyLayoutAnchorType anchorType; + +@property (nonatomic, assign) MyLayoutValType valType; +@property (nonatomic, strong) id val; +@property (nonatomic, readonly, strong) NSNumber *numberVal; +@property (nonatomic, readonly, strong) MyLayoutSize *anchorVal; +@property (nonatomic, readonly, strong) NSArray *arrayVal; + +@property (nonatomic, readonly, assign) BOOL wrapVal; +@property (nonatomic, readonly, assign) BOOL fillVal; + +@property (nonatomic, strong) MyLayoutSize *lBoundVal; +@property (nonatomic, strong) MyLayoutSize *uBoundVal; + +@property (nonatomic, readonly, strong) MyLayoutSize *lBoundValInner; +@property (nonatomic, readonly, strong) MyLayoutSize *uBoundValInner; + +@property (nonatomic, assign) CGFloat addVal; +@property (nonatomic, assign) CGFloat multiVal; + +//优先级,内部使用,值是0,500, 1000 分别代表低、中、高,默认是500,这个属性先内部生效。 +@property (nonatomic, assign) MyPriority priority; + +- (MyLayoutSize * (^)(id val))myEqualTo; +- (MyLayoutSize * (^)(CGFloat val))myAdd; +- (MyLayoutSize * (^)(CGFloat val))myMultiply; +- (MyLayoutSize * (^)(CGFloat val))myMin; +- (MyLayoutSize * (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))myLBound; +- (MyLayoutSize * (^)(CGFloat val))myMax; +- (MyLayoutSize * (^)(id sizeVal, CGFloat addVal, CGFloat multiVal))myUBound; +- (void)myClear; + +- (MyLayoutSize *)_myEqualTo:(id)val; +- (MyLayoutSize *)_myEqualTo:(id)val priority:(NSInteger)priority; +- (MyLayoutSize *)_myAdd:(CGFloat)val; +- (MyLayoutSize *)_myMultiply:(CGFloat)val; +- (MyLayoutSize *)_myMin:(CGFloat)val; +- (MyLayoutSize *)_myLBound:(id)sizeVal addVal:(CGFloat)addVal multiVal:(CGFloat)multiVal; +- (MyLayoutSize *)_myMax:(CGFloat)val; +- (MyLayoutSize *)_myUBound:(id)sizeVal addVal:(CGFloat)addVal multiVal:(CGFloat)multiVal; +- (void)_myClear; +- (void)_mySetActive:(BOOL)active; +//只有为数值时才有意义。 +@property (nonatomic, readonly, assign) CGFloat measure; +- (CGFloat)measureWith:(CGFloat)size; --(MyLayoutSize*)__equalTo:(id)val; --(MyLayoutSize*)__add:(CGFloat)val; --(MyLayoutSize*)__multiply:(CGFloat)val; --(MyLayoutSize*)__min:(CGFloat)val; --(MyLayoutSize*)__lBound:(id)sizeVal addVal:(CGFloat)addVal multiVal:(CGFloat)multiVal; --(MyLayoutSize*)__max:(CGFloat)val; --(MyLayoutSize*)__uBound:(id)sizeVal addVal:(CGFloat)addVal multiVal:(CGFloat)multiVal; --(void)__clear; - - +@end -//只有为数值时才有意义。 -@property(nonatomic, readonly, assign) CGFloat measure; +@interface MyLayoutMostSize : NSObject +- (instancetype)initWith:(NSArray *)sizes isMax:(BOOL)isMax; --(CGFloat)measureWith:(CGFloat)size; +//获取极限值 +- (CGFloat)getMostDimenValFrom:(MyLayoutSize *)layoutSize; @end diff --git a/MyLayout/Lib/MyLinearLayout.h b/MyLayout/Lib/MyLinearLayout.h index acf4db1..f6b74bb 100644 --- a/MyLayout/Lib/MyLinearLayout.h +++ b/MyLayout/Lib/MyLinearLayout.h @@ -8,7 +8,6 @@ #import "MyBaseLayout.h" - /** 线性布局是一种里面的子视图按添加的顺序从上到下或者从左到右依次排列的单行(单列)布局视图。线性布局里面的子视图是通过添加的顺序建立约束和依赖关系的。 @@ -36,17 +35,16 @@ */ @interface MyLinearLayout : MyBaseLayout - /** 初始化一个线性布局,并指定子视图排列的方向。如果不明确指定方向则默认是建立一个垂直线性布局。 - @note 建立一个垂直线性布局时默认的wrapContentHeight设置为YES,表示高度默认由所有子视图的高度决定;而建立一个水平线性布局时默认的wrapContentWidth设置为YES,表示宽度默认由所有子视图的宽度决定。 + @note 建立一个垂直线性布局时默认的高度是自适应,表示高度默认由所有子视图的高度决定;而建立一个水平线性布局时默认的宽度是自适应,表示宽度默认由所有子视图的宽度决定。 @param orientation 布局视图内子视图的排列方向。 @return 返回线性布局对象实例。 */ -+(instancetype)linearLayoutWithOrientation:(MyOrientation)orientation; --(instancetype)initWithOrientation:(MyOrientation)orientation; --(instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation; ++ (instancetype)linearLayoutWithOrientation:(MyOrientation)orientation; +- (instancetype)initWithOrientation:(MyOrientation)orientation; +- (instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation; /** 线性布局的布局方向: @@ -55,12 +53,7 @@ 2. MyOrientation_Horz 表示布局内子视图依次从左到右(RTL从右到左)水平排列。 */ -@property(nonatomic,assign) IBInspectable MyOrientation orientation; - - - - - +@property (nonatomic, assign) MyOrientation orientation; /** 用来设置当线性布局中的所有子视图的尺寸和间距的和大于线性布局的尺寸时子视图压缩策略。默认值是MySubviewsShrink_None,表示什么不压缩子视图的尺寸和间距。 @@ -110,22 +103,22 @@ @code MyLinearLayout *horzLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; horzLayout.myHorzMargin = 0; - horzLayout.wrapContentHeight = YES; + horzLayout.myHeight = MyLayoutSize.wrap; horzLayout.subviewSpace = 10; //二者的最小间距不能小于20 horzLayout.shrinkType = MySubviewsShrink_Auto; UILabel *A = [UILabel new]; A.text = @"xxxxxxx"; - A.widthSize.equalTo(A.widthSize); //宽度等于自身内容的宽度,必须要这么设置和 MySubviewsShrink_Auto 结合使用。 - A.wrapContentHeight = YES; //自动换行 + A.myWidth = MyLayoutSize.wrap; //宽度自适应,必须要这么设置和 MySubviewsShrink_Auto 结合使用。 + A.myHeight = MyLayoutSize.wrap; //高度自适应 A.rightPos.equalTo(@0.5); //右边间距是剩余的50% [horzLayout addSubview:A]; UILabel *B = [UILabel new]; B.text = @"XXXXXXXX"; - B.widthSize.equalTo(B.widthSize); //宽度等于自身内容的宽度,必须要这么设置和 MySubviewsShrink_Auto 结合使用。 - B.wrapContentHeight = YES; //自动换行 + B.myWidth = MyLayoutSize.wrap; //宽度自适应,必须要这么设置和 MySubviewsShrink_Auto 结合使用。 + B.myHeight = MyLayoutSize.wrap; //高度自适应 B.leftPos.equalTo(@0.5); //左边间距是剩余的50% [horzLayout addSubview:B]; @endcode @@ -143,19 +136,19 @@ A的最终topPos = 10 - 40 *(10/30) = -3 D的最终topPos = 20 - 40 *(20/30) = -7 @endcode + + @note + 你可以将子视图的尺寸和位置的shrink属性设置为非0,这样这些子视图的尺寸的和位置的压缩策略将优先于这个属性的设置。一旦有子视图设置压缩策略则这个属性设置将失效。 + */ -@property(nonatomic, assign) MySubviewsShrinkType shrinkType; - - +@property (nonatomic, assign) MySubviewsShrinkType shrinkType; /** 设置水平线性布局里面的基线对齐基准视图,所有其他子视图的基线都以这个为准。 这个属性要和gravity属性设置为MyGravity_Vert_Baseline配合使用。并且要求这个属性所指定的视图,必须具有font属性。 目前支持具有font属性的有UILabel,UITextField,UITextView, UIButton几个系统控件。 */ -@property(nonatomic, assign) UIView *baselineBaseView; - - +@property (nonatomic, assign) UIView *baselineBaseView; /** 均分子视图的尺寸和间距。布局会根据里面的子视图的数量来平均分配子视图的高度或者宽度以及间距。 @@ -164,10 +157,8 @@ @param centered 指定是否所有子视图居中,当居中时对于垂直线性布局来说顶部和底部会保留出间距,而不居中时则顶部和底部不留出间距。水平线性布局则是指头部和尾部。 */ --(void)equalizeSubviews:(BOOL)centered; --(void)equalizeSubviews:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; - - +- (void)equalizeSubviews:(BOOL)centered; +- (void)equalizeSubviews:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; /** 均分子视图尺寸,并指定固定的间距。上面的方法会导致子视图的高度或者宽度和他们之间的间距相等,而这个方法则指定间距为一个固定的值而子视图的高度或者宽度则会被均分。 @@ -176,10 +167,8 @@ @param centered 指定是否所有子视图居中,当居中时对于垂直线性布局来说顶部和底部会保留出间距,而不居中时则顶部和底部不留出间距。水平线性布局则是指头部和尾部 @param space 子视图之间的间距。 */ --(void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space; --(void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space inSizeClass:(MySizeClass)sizeClass; - - +- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space; +- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space inSizeClass:(MySizeClass)sizeClass; /** 均分子视图之间的间距,上面方法会均分子视图的尺寸以及间距,而这个方法则子视图的尺寸保持不变而间距自动平均分配,也就是用布局视图的剩余空间来均分间距 @@ -188,28 +177,24 @@ @param centered 参数描述是否所有子视图居中,当居中时对于垂直线性布局来说顶部和底部会保留出间距,而不居中时则顶部和底部不保持间距。水平线性布局则是指头部和尾部 */ --(void)equalizeSubviewsSpace:(BOOL)centered; --(void)equalizeSubviewsSpace:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; - - -@end - - -@interface MyLinearLayout(MyLinearLayoutDeprecated) +- (void)equalizeSubviewsSpace:(BOOL)centered; +- (void)equalizeSubviewsSpace:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; /** - * 过期的方法,这些过期的方法名取名不规范,因此为了和TangramKit统一,这里将这些不规范的方法设置为过期。 + 在一些应用场景中我们希望子视图的宽度是固定的但间距是浮动的,这样就尽可能在一排中容纳更多的子视图。比如设置每个子视图的宽度固定为80,那么在小屏幕下每排只能放3个,而大屏幕则每排能放4个或者5个子视图。 因此您可以通过如下方法来设置子视图的固定尺寸和最小最大浮动间距。这个方法会根据您当前布局的方向不同而具有不同的意义: + + 1.如果您的布局方向是垂直的则设置的是每行内子视图的水平浮动间距,其中的subviewSize指定的是子视图的固定宽度;minSpace指定的是最小的水平间距;maxSpace指定的是最大的水平间距,如果指定的subviewSize计算出的间距大于最大间距maxSpace则会缩小subviewSize的宽度值。 + + 2.如果您的布局方向是水平的则设置的是每列内子视图的垂直浮动间距,其中的subviewSize指定的是子视图的固定高度;minSpace指定的是最小的垂直间距;maxSpace指定的是最大的垂直间距,如果指定的subviewSize计算出的间距大于最大间距maxSpace则会调整subviewSize的高度值。 + + @note 如果您不想使用浮动间距则请将subviewSize设置为0就可以了。 + + @param subviewSize 指定子视图的尺寸。 + @param minSpace 指定子视图之间的最小间距 + @param maxSpace 指定子视图之间的最大间距 + @param centered 指定是否所有子视图居中 */ --(void)averageSubviews:(BOOL)centered MYMETHODDEPRECATED("use ’equalizeSubviews:(BOOL)centered‘ to instead"); --(void)averageSubviews:(BOOL)centered inSizeClass:(MySizeClass)sizeClass MYMETHODDEPRECATED("use ‘equalizeSubviews:(BOOL)centered inSizeClass:(MySizeClass)sizeClass’ to instead"); - --(void)averageSubviews:(BOOL)centered withMargin:(CGFloat)margin MYMETHODDEPRECATED("use ‘equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space’ to instead"); --(void)averageSubviews:(BOOL)centered withMargin:(CGFloat)margin inSizeClass:(MySizeClass)sizeClass MYMETHODDEPRECATED("use ‘equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space inSizeClass:(MySizeClass)sizeClass’ to instead"); - --(void)averageMargin:(BOOL)centered MYMETHODDEPRECATED("use ‘equalizeSubviewsSpace:(BOOL)centered’ to instead"); --(void)averageMargin:(BOOL)centered inSizeClass:(MySizeClass)sizeClass MYMETHODDEPRECATED("use ‘equalizeSubviewsSpace:(BOOL)centered inSizeClass:(MySizeClass)sizeClass’ to instead"); - +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered; +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass; @end - - diff --git a/MyLayout/Lib/MyLinearLayout.m b/MyLayout/Lib/MyLinearLayout.m index 3f96915..221466d 100644 --- a/MyLayout/Lib/MyLinearLayout.m +++ b/MyLayout/Lib/MyLinearLayout.m @@ -11,1827 +11,1133 @@ @implementation MyLinearLayout --(instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation -{ +#pragma mark-- Public Methods + +- (instancetype)initWithFrame:(CGRect)frame orientation:(MyOrientation)orientation { self = [super initWithFrame:frame]; - if (self) - { - MyLinearLayout *lsc = self.myCurrentSizeClass; - if (orientation == MyOrientation_Vert) - lsc.wrapContentHeight = YES; - else - lsc.wrapContentWidth = YES; - lsc.orientation = orientation; + if (self) { + MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)self.myDefaultSizeClass; + if (orientation == MyOrientation_Vert) { + [layoutTraits.heightSize _myEqualTo:@(MyLayoutSize.wrap) priority:MyPriority_Low]; //这是暂时先设置为低优先级,为了兼容老版本。 + } else { + [layoutTraits.widthSize _myEqualTo:@(MyLayoutSize.wrap) priority:MyPriority_Low]; + } + layoutTraits.orientation = orientation; } - return self; } - --(instancetype)initWithOrientation:(MyOrientation)orientation -{ +- (instancetype)initWithOrientation:(MyOrientation)orientation { return [self initWithFrame:CGRectZero orientation:orientation]; } -+(instancetype)linearLayoutWithOrientation:(MyOrientation)orientation -{ ++ (instancetype)linearLayoutWithOrientation:(MyOrientation)orientation { return [[[self class] alloc] initWithOrientation:orientation]; } --(void)setOrientation:(MyOrientation)orientation -{ - MyLinearLayout *lsc = self.myCurrentSizeClass; - if (lsc.orientation != orientation) - { - lsc.orientation = orientation; +- (void)setOrientation:(MyOrientation)orientation { + MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.orientation != orientation) { + layoutTraits.orientation = orientation; [self setNeedsLayout]; } } --(MyOrientation)orientation -{ - return self.myCurrentSizeClass.orientation; +- (MyOrientation)orientation { + return self.myDefaultSizeClassInner.orientation; } - - --(void)setShrinkType:(MySubviewsShrinkType)shrinkType -{ - MyLinearLayout *lsc = self.myCurrentSizeClass; - if (lsc.shrinkType != shrinkType) - { - lsc.shrinkType = shrinkType; +- (void)setShrinkType:(MySubviewsShrinkType)shrinkType { + MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)self.myDefaultSizeClass; + if (layoutTraits.shrinkType != shrinkType) { + layoutTraits.shrinkType = shrinkType; [self setNeedsLayout]; } } --(MySubviewsShrinkType)shrinkType -{ - return self.myCurrentSizeClass.shrinkType; +- (MySubviewsShrinkType)shrinkType { + return self.myDefaultSizeClassInner.shrinkType; } - --(void)equalizeSubviews:(BOOL)centered -{ - [self equalizeSubviews:centered withSpace:CGFLOAT_MAX]; +- (void)equalizeSubviews:(BOOL)centered { + [self equalizeSubviews:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } --(void)equalizeSubviews:(BOOL)centered inSizeClass:(MySizeClass)sizeClass -{ +- (void)equalizeSubviews:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { [self equalizeSubviews:centered withSpace:CGFLOAT_MAX inSizeClass:sizeClass]; } --(void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space -{ +- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space { [self equalizeSubviews:centered withSpace:space inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; - [self setNeedsLayout]; } --(void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space inSizeClass:(MySizeClass)sizeClass -{ - self.myFrame.sizeClass = [self fetchLayoutSizeClass:sizeClass]; - for (UIView *sbv in self.subviews) - sbv.myFrame.sizeClass = [sbv fetchLayoutSizeClass:sizeClass]; - - if (self.orientation == MyOrientation_Vert) - { - [self myEqualizeSubviewsForVert:centered withSpace:space]; - } - else - { - [self myEqualizeSubviewsForHorz:centered withSpace:space]; - } +- (void)equalizeSubviews:(BOOL)centered withSpace:(CGFloat)space inSizeClass:(MySizeClass)sizeClass { + NSArray *tuple = [self myUpdateCurrentSizeClass:sizeClass subviews:nil]; + MyLayoutEngine *layoutViewEngine = tuple.firstObject; + NSMutableArray *subviewEngines = tuple.lastObject; - self.myFrame.sizeClass = self.myDefaultSizeClass; - for (UIView *sbv in self.subviews) - sbv.myFrame.sizeClass = sbv.myDefaultSizeClass; - + MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)layoutViewEngine.currentSizeClass; + subviewEngines = [layoutTraits filterEngines:subviewEngines]; + [self myEqualizeSubviewEngines:subviewEngines orientation:layoutTraits.orientation centered:centered space:space]; + [self setNeedsLayout]; } --(void)equalizeSubviewsSpace:(BOOL)centered -{ +- (void)equalizeSubviewsSpace:(BOOL)centered { [self equalizeSubviewsSpace:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; - [self setNeedsLayout]; - } --(void)equalizeSubviewsSpace:(BOOL)centered inSizeClass:(MySizeClass)sizeClass -{ - - self.myFrame.sizeClass = [self fetchLayoutSizeClass:sizeClass]; - for (UIView *sbv in self.subviews) - sbv.myFrame.sizeClass = [sbv fetchLayoutSizeClass:sizeClass]; - - if (self.orientation == MyOrientation_Vert) - { - [self myEqualizeSubviewsSpaceForVert:centered]; - } - else - { - [self myEqualizeSubviewsSpaceForHorz:centered]; - } +- (void)equalizeSubviewsSpace:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { + NSArray *tuple = [self myUpdateCurrentSizeClass:sizeClass subviews:nil]; + MyLayoutEngine *layoutViewEngine = tuple.firstObject; + NSMutableArray *subviewEngines = tuple.lastObject; - self.myFrame.sizeClass = self.myDefaultSizeClass; - for (UIView *sbv in self.subviews) - sbv.myFrame.sizeClass = sbv.myDefaultSizeClass; - -} - - -#pragma mark -- Deprecated Method - --(void)averageSubviews:(BOOL)centered -{ - [self equalizeSubviews:centered]; -} - --(void)averageSubviews:(BOOL)centered inSizeClass:(MySizeClass)sizeClass -{ - [self equalizeSubviews:centered inSizeClass:sizeClass]; -} - - --(void)averageSubviews:(BOOL)centered withMargin:(CGFloat)margin -{ - [self equalizeSubviews:centered withSpace:margin]; -} - --(void)averageSubviews:(BOOL)centered withMargin:(CGFloat)margin inSizeClass:(MySizeClass)sizeClass -{ - [self equalizeSubviews:centered withSpace:margin inSizeClass:sizeClass]; -} - - - --(void)averageMargin:(BOOL)centered -{ - [self equalizeSubviewsSpace:centered]; + MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)layoutViewEngine.currentSizeClass; + subviewEngines = [layoutTraits filterEngines:subviewEngines]; + [self myEqualizeSubviewEngines:subviewEngines orientation:layoutTraits.orientation centered:centered]; + [self setNeedsLayout]; } --(void)averageMargin:(BOOL)centered inSizeClass:(MySizeClass)sizeClass -{ - [self equalizeSubviewsSpace:centered inSizeClass:sizeClass]; +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered { + [self setSubviewsSize:subviewSize minSpace:minSpace maxSpace:maxSpace centered:centered inSizeClass:MySizeClass_hAny | MySizeClass_wAny]; } - -#pragma mark -- Override Method - -- (void)willMoveToSuperview:(UIView*)newSuperview -{ - //减少约束冲突的提示。。 - MyLinearLayout *lsc = self.myCurrentSizeClass; - - if (lsc.orientation == MyOrientation_Vert) - { - if (lsc.heightSizeInner.dimeVal != nil && lsc.wrapContentHeight) - { - lsc.wrapContentHeight = NO; - } - - } - else - { - if (lsc.widthSizeInner.dimeVal != nil && lsc.wrapContentWidth) - { - lsc.wrapContentWidth = NO; +- (void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered inSizeClass:(MySizeClass)sizeClass { + MySequentLayoutTraits *layoutTraits = (MySequentLayoutTraits *)[self fetchLayoutSizeClass:sizeClass]; + if (subviewSize == 0.0) { + layoutTraits.flexSpace = nil; + } else { + if (layoutTraits.flexSpace == nil) { + layoutTraits.flexSpace = [MySequentLayoutFlexSpacing new]; } - + layoutTraits.flexSpace.subviewSize = subviewSize; + layoutTraits.flexSpace.minSpace = minSpace; + layoutTraits.flexSpace.maxSpace = maxSpace; + layoutTraits.flexSpace.centered = centered; } - - [super willMoveToSuperview:newSuperview]; + [self setNeedsLayout]; } -- (void)willRemoveSubview:(UIView *)subview -{ +#pragma mark-- Override Methods + +- (void)willRemoveSubview:(UIView *)subview { [super willRemoveSubview:subview]; - if (subview == self.baselineBaseView) - { + if (subview == self.baselineBaseView) { self.baselineBaseView = nil; } } - - - --(CGSize)calcLayoutRect:(CGSize)size isEstimate:(BOOL)isEstimate pHasSubLayout:(BOOL*)pHasSubLayout sizeClass:(MySizeClass)sizeClass sbs:(NSMutableArray *)sbs -{ - CGSize selfSize = [super calcLayoutRect:size isEstimate:isEstimate pHasSubLayout:pHasSubLayout sizeClass:sizeClass sbs:sbs]; - - if (sbs == nil) - sbs = [self myGetLayoutSubviews]; - - - MyLinearLayout *lsc = self.myCurrentSizeClass; - - MyGravity vertGravity = lsc.gravity & MyGravity_Horz_Mask; - MyGravity horzGravity = lsc.gravity & MyGravity_Vert_Mask; - MyOrientation oreintation = lsc.orientation; - - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (!isEstimate) - { - sbvmyFrame.frame = sbv.bounds; - [self myCalcSizeOfWrapContentSubview:sbv sbvsc:sbvsc sbvmyFrame:sbvmyFrame]; - } - - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - [self myAdjustSubviewWrapContent:sbv sbvsc:sbvsc orientation:oreintation gravity:(oreintation == MyOrientation_Vert)? horzGravity : vertGravity]; - - BOOL isSbvWrap = sbvsc.wrapContentHeight || sbvsc.wrapContentWidth; - if (pHasSubLayout != nil && isSbvWrap) - *pHasSubLayout = YES; - - if (isEstimate && isSbvWrap) - { - [(MyBaseLayout*)sbv sizeThatFits:sbvmyFrame.frame.size inSizeClass:sizeClass]; - if (sbvmyFrame.multiple) - { - sbvmyFrame.sizeClass = [sbv myBestSizeClass:sizeClass]; //因为sizeThatFits执行后会还原,所以这里要重新设置 - sbvsc = sbvmyFrame.sizeClass; - } - } - } +- (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { + [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; + + MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits *)context->layoutViewEngine.currentSizeClass; + context->paddingTop = layoutTraits.myLayoutPaddingTop; + context->paddingBottom = layoutTraits.myLayoutPaddingBottom; + context->paddingLeading = layoutTraits.myLayoutPaddingLeading; + context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + context->vertGravity = MYVERTGRAVITY(layoutTraits.gravity); + context->horzGravity = [MyViewTraits convertLeadingTrailingGravityFromLeftRightGravity:MYHORZGRAVITY(layoutTraits.gravity)]; + context->vertSpace = layoutTraits.subviewVSpace; + context->horzSpace = layoutTraits.subviewHSpace; + if (context->subviewEngines == nil) { + context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; } + + MyOrientation orientation = layoutTraits.orientation; - if (oreintation == MyOrientation_Vert) - { - if (vertGravity != MyGravity_None) - selfSize = [self myLayoutSubviewsForVertGravity:selfSize sbs:sbs lsc:lsc]; - else - selfSize = [self myLayoutSubviewsForVert:selfSize sbs:sbs lsc:lsc]; - } - else - { - if (horzGravity != MyGravity_None) - selfSize = [self myLayoutSubviewsForHorzGravity:selfSize sbs:sbs lsc:lsc]; - else - selfSize = [self myLayoutSubviewsForHorz:selfSize sbs:sbs lsc:lsc]; + [self myCalcSubviewsWrapContentSize:context + withCustomSetting:^(MyViewTraits *subviewTraits) { + [self myLayout:layoutTraits orientation:orientation gravity:(orientation == MyOrientation_Vert) ? context->horzGravity : context->vertGravity adjustSizeSettingOfSubview:subviewTraits]; + }]; + + if (orientation == MyOrientation_Vert) { + [self myDoVertOrientationLayoutWithContext:context]; + } else { + [self myDoHorzOrientationLayoutWithContext:context]; } - //绘制智能线。 - if (!isEstimate) - { - [self mySetLayoutIntelligentBorderline:sbs lsc:lsc]; - } - - - //调整布局视图自己的尺寸。 - [self myAdjustLayoutSelfSize:&selfSize lsc:lsc]; - //如果是反向则调整所有子视图的左右位置。 - [self myAdjustSubviewsRTLPos:sbs selfWidth:selfSize.width]; - - return [self myAdjustSizeWhenNoSubviews:selfSize sbs:sbs lsc:lsc]; - + [self mySetIntelligentBorderlineWithContext:context]; + return [self myAdjustLayoutViewSizeWithContext:context]; } --(id)createSizeClassInstance -{ - return [MyLinearLayoutViewSizeClass new]; +- (id)createSizeClassInstance { + return [MyLinearLayoutTraits new]; } - -#pragma mark -- Private Method +#pragma mark-- Private Methods //调整子视图的wrapContent设置 -- (void)myAdjustSubviewWrapContent:(UIView*)sbv sbvsc:(UIView*)sbvsc orientation:(MyOrientation)orientation gravity:(MyGravity)gravity -{ - if (orientation == MyOrientation_Vert) - { - if (sbvsc.wrapContentWidth) - { - //只要同时设置了左右边距或者设置了宽度或者设置了子视图宽度填充则应该把wrapContentWidth置为NO - if ((sbvsc.widthSizeInner.dimeVal != nil) || - (sbvsc.leadingPosInner.posVal != nil && sbvsc.trailingPosInner.posVal != nil) || - (gravity == MyGravity_Horz_Fill) - ) - { - sbvsc.wrapContentWidth = NO; - } +- (void)myLayout:(MyLinearLayoutTraits *)layoutTraits orientation:(MyOrientation)orientation gravity:(MyGravity)gravity adjustSizeSettingOfSubview:(MyViewTraits *)subviewTraits { + if (orientation == MyOrientation_Vert) { + //如果是拉伸处理则需要把包裹宽度取消。 + if (subviewTraits.widthSizeInner.wrapVal && gravity == MyGravity_Horz_Fill) { + [subviewTraits.widthSizeInner _myEqualTo:nil]; } - - if (sbvsc.wrapContentHeight) - { - //只要同时设置了高度或者比重属性则应该把wrapContentHeight置为NO - if ((sbvsc.heightSizeInner.dimeVal != nil) || - (sbvsc.weight != 0)) - { - sbvsc.wrapContentHeight = NO; - } + //如果同时设置了左右依赖。并且优先级低时或者布局视图不是宽度自适应则取消自视图宽度自适应,这里是为了兼容老版本。 + if (subviewTraits.leadingPosInner.val != nil && subviewTraits.trailingPosInner.val != nil && (subviewTraits.widthSizeInner.priority == MyPriority_Low || !layoutTraits.widthSizeInner.wrapVal)) { + [subviewTraits.widthSizeInner _myEqualTo:nil]; } - - } - else - { - - if (sbvsc.wrapContentHeight) - { - //只要同时设置了高度或者上下边距或者父视图的填充属性则应该把wrapContentHeight置为NO - if ((sbvsc.heightSizeInner.dimeVal != nil) || - (sbvsc.topPosInner.posVal != nil && sbvsc.bottomPosInner.posVal != nil) || - (gravity == MyGravity_Vert_Fill) - ) - { - sbvsc.wrapContentHeight = NO; - } + //只要同时设置了高度或者比重属性则应该把尺寸设置为空 + if (subviewTraits.heightSizeInner.wrapVal && subviewTraits.weight != 0) { + [subviewTraits.heightSizeInner _myEqualTo:nil]; } - - if (sbvsc.wrapContentWidth) - { - //只要同时设置了宽度或者比重属性则应该把wrapContentWidth置为NO - if (sbvsc.widthSizeInner.dimeVal != nil || sbvsc.weight != 0) - { - sbvsc.wrapContentWidth = NO; - } + } else { + //如果是拉伸处理则需要把包裹高度 + if (subviewTraits.heightSizeInner.wrapVal && gravity == MyGravity_Vert_Fill) { + [subviewTraits.heightSizeInner _myEqualTo:nil]; + } + //如果同时设置了左右依赖。并且优先级低时则取消宽度自适应,这里是为了兼容老版本。 + if (subviewTraits.topPosInner.val != nil && subviewTraits.bottomPosInner.val != nil && (subviewTraits.heightSizeInner.priority == MyPriority_Low || !layoutTraits.heightSizeInner.wrapVal)) { + [subviewTraits.heightSizeInner _myEqualTo:nil]; + } + //只要同时设置了宽度或者比重属性则应该把宽度置空 + if (subviewTraits.widthSizeInner.wrapVal && subviewTraits.weight != 0) { + [subviewTraits.widthSizeInner _myEqualTo:nil]; } } } //设置智能边界线 --(void)mySetLayoutIntelligentBorderline:(NSArray*)sbs lsc:(MyLinearLayout*)lsc -{ - if (self.intelligentBorderline == nil) +- (void)mySetIntelligentBorderlineWithContext:(MyLayoutContext *)context { + if (context->isEstimate) { return; + } - BOOL isVert = (lsc.orientation == MyOrientation_Vert); - CGFloat subviewSpace = (lsc.orientation == MyOrientation_Vert) ? lsc.subviewVSpace : lsc.subviewHSpace; - for (int i = 0; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - if (![sbv isKindOfClass:[MyBaseLayout class]]) + MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)context->layoutViewEngine.currentSizeClass; + NSArray *subviewEngines = context->subviewEngines; + if (self.intelligentBorderline == nil) { + return; + } + BOOL isVert = (layoutTraits.orientation == MyOrientation_Vert); + CGFloat subviewSpace = (layoutTraits.orientation == MyOrientation_Vert) ? layoutTraits.subviewVSpace : layoutTraits.subviewHSpace; + for (int i = 0; i < subviewEngines.count; i++) { + UIView *subview = subviewEngines[i].currentSizeClass.view; + if (![subview isKindOfClass:[MyBaseLayout class]]) { continue; - - MyBaseLayout *sbvl = (MyBaseLayout*)sbv; - if (sbvl.notUseIntelligentBorderline) + } + MyBaseLayout *sublayoutview = (MyBaseLayout *)subview; + if (sublayoutview.notUseIntelligentBorderline) { continue; - - if (isVert) - { - sbvl.topBorderline = nil; - sbvl.bottomBorderline = nil; } - else - { - sbvl.leadingBorderline = nil; - sbvl.trailingBorderline = nil; + if (isVert) { + sublayoutview.topBorderline = nil; + sublayoutview.bottomBorderline = nil; + } else { + sublayoutview.leadingBorderline = nil; + sublayoutview.trailingBorderline = nil; } - + UIView *prevSiblingView = nil; UIView *nextSiblingView = nil; - - if (i != 0) - { - prevSiblingView = sbs[i - 1]; + + if (i != 0) { + prevSiblingView = subviewEngines[i - 1].currentSizeClass.view; } - - if (i + 1 != sbs.count) - { - nextSiblingView = sbs[i + 1]; + if (i + 1 != subviewEngines.count) { + nextSiblingView = subviewEngines[i + 1].currentSizeClass.view; } - - if (prevSiblingView != nil) - { + if (prevSiblingView != nil) { BOOL ok = YES; - if ([prevSiblingView isKindOfClass:[MyBaseLayout class]] && subviewSpace == 0) - { - MyBaseLayout *prevSiblingLayout = (MyBaseLayout*)prevSiblingView; - if (prevSiblingLayout.notUseIntelligentBorderline) + if ([prevSiblingView isKindOfClass:[MyBaseLayout class]] && subviewSpace == 0) { + MyBaseLayout *prevSiblingLayout = (MyBaseLayout *)prevSiblingView; + if (prevSiblingLayout.notUseIntelligentBorderline) { ok = NO; + } } - - if (ok) - { - if (isVert) - sbvl.topBorderline = self.intelligentBorderline; - else - sbvl.leadingBorderline = self.intelligentBorderline; + + if (ok) { + if (isVert) { + sublayoutview.topBorderline = self.intelligentBorderline; + } else { + sublayoutview.leadingBorderline = self.intelligentBorderline; + } } } - - if (nextSiblingView != nil && (![nextSiblingView isKindOfClass:[MyBaseLayout class]] || subviewSpace != 0)) - { - if (isVert) - sbvl.bottomBorderline = self.intelligentBorderline; - else - sbvl.trailingBorderline = self.intelligentBorderline; - } - } -} -//计算得到最大的包裹宽度 -- (CGSize)myCalcMaxWrapWidth:(NSArray *)sbs selfSize:(CGSize)selfSize paddingHorz:(CGFloat)paddingHorz lsc:(MyLinearLayout*)lsc -{ - CGFloat maxWrapWidth = 0; - if (lsc.wrapContentWidth) - { - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - //只有宽度不依赖于父视图并且没有设置左右边距则参与最大包裹宽度计算。 - MyLayoutSize *relaSize = sbvsc.widthSizeInner.dimeRelaVal; - if (relaSize.view != self && (sbvsc.leadingPosInner.posVal == nil || sbvsc.trailingPosInner.posVal == nil)) - { - - CGFloat subviewWidth = sbvmyFrame.width; - if (sbvsc.widthSizeInner.dimeNumVal != nil) - { - subviewWidth = sbvsc.widthSizeInner.measure; - } - sbvmyFrame.width = subviewWidth; - - //头部 + 中间偏移 + 宽度 + 尾部。 这里要进行计算的原因是有可能左边距或者右边距设置的是相对边距,因此这里要推导出布局视图的宽度。 - maxWrapWidth = [self myCalcSelfSize:maxWrapWidth - subviewSize:subviewWidth - headPos:sbvsc.leadingPosInner - centerPos:sbvsc.centerXPosInner - tailPos:sbvsc.trailingPosInner]; - - + if (nextSiblingView != nil && (![nextSiblingView isKindOfClass:[MyBaseLayout class]] || subviewSpace != 0)) { + if (isVert) { + sublayoutview.bottomBorderline = self.intelligentBorderline; + } else { + sublayoutview.trailingBorderline = self.intelligentBorderline; } } - - selfSize.width = maxWrapWidth + paddingHorz; } - - return selfSize; } --(CGSize)myLayoutSubviewsForVert:(CGSize)selfSize sbs:(NSArray*)sbs lsc:(MyLinearLayout*)lsc -{ - CGFloat subviewSpace = lsc.subviewVSpace; - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - CGFloat paddingHorz = paddingLeading + paddingTrailing; - CGFloat paddingVert = paddingTop + paddingBottom; - MyGravity horzGravity = [self myConvertLeftRightGravityToLeadingTrailing:lsc.gravity & MyGravity_Vert_Mask]; - - CGFloat fixedHeight = 0; //计算固定部分的高度 - CGFloat totalWeight = 0; //剩余部分的总比重 - CGFloat addSpace = 0; //用于压缩时的间距压缩增量。 - - selfSize = [self myCalcMaxWrapWidth:sbs selfSize:selfSize paddingHorz:paddingHorz lsc:lsc]; //调整自身的宽度 - - NSMutableArray *fixedSizeSbs = [NSMutableArray new]; - CGFloat fixedSizeHeight = 0; - NSInteger fixedSpaceCount = 0; //固定间距的子视图数量。 - CGFloat fixedSpaceHeight = 0; //固定间距的子视图的宽度。 - CGFloat pos = paddingTop; - for (UIView *sbv in sbs) - { - - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - CGRect rect = sbvmyFrame.frame; - - [self myCalcLeadingTrailingRect:horzGravity - selfSize:selfSize - rect_p:&rect - sbv:sbv - paddingTrailing:paddingTrailing - paddingLeading:paddingLeading - sbvsc:sbvsc - lsc:lsc]; - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]] && sbvsc.weight == 0) - {//特殊处理高度等于wrap的情况。 - - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; +- (void)myDoVertOrientationLayoutWithContext:(MyLayoutContext *)context { + MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits *)context->layoutViewEngine.currentSizeClass; + + CGFloat subviewHeight = [layoutTraits.flexSpace calcMaxMinSubviewSize:context->selfSize.height arrangedCount:context->subviewEngines.count paddingStart:&context->paddingTop paddingEnd:&context->paddingBottom space:&context->vertSpace]; + CGFloat paddingVert = context->paddingTop + context->paddingBottom; + CGFloat totalHeight = 0.0; //计算固定部分的总高度 + CGFloat totalWeight = 0.0; //剩余部分的总比重 + CGFloat totalShrink = 0.0; //总的压缩比重 + CGFloat addSpace = 0.0; //用于压缩时的间距压缩增量。 + CGFloat maxLayoutWidth = 0.0; + + //高度可以被伸缩的子视图集合 + NSMutableSet *flexHeightSubviewEngines = [NSMutableSet new]; + //高度是固定尺寸的子视图集合 + NSMutableArray *fixedHeightSubviewEngines = [NSMutableArray new]; + CGFloat totalFixedHeight = 0.0; + NSInteger fixedSpacingSubviewCount = 0; //固定间距的子视图数量。 + CGFloat totalFixedSpacing = 0.0; //固定间距的子视图的高度。 + CGFloat pos = context->paddingTop; + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + if (context->vertGravity == MyGravity_Vert_Fill || context->vertGravity == MyGravity_Vert_Stretch) { + BOOL isFlexEngine = YES; + //比重高度的子视图不能被伸缩。 + if (subviewTraits.weight != 0.0) { + isFlexEngine = NO; + } + //高度依赖于父视图的不能被伸缩 + if ((subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == layoutTraits.heightSizeInner) || subviewTraits.heightSizeInner.fillVal) { + isFlexEngine = NO; + } + //布局视图的高度自适应尺寸的不能被伸缩。 + if (subviewTraits.heightSizeInner.wrapVal && [subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + isFlexEngine = NO; + } + //高度最小是自身尺寸的不能被伸缩。 + if (subviewTraits.heightSizeInner.lBoundValInner.wrapVal) { + isFlexEngine = NO; + } + //对于拉伸来说,只要是设置了高度约束(除了非布局子视图的高度自适应除外)都不会被伸缩 + if (context->vertGravity == MyGravity_Vert_Stretch && + subviewTraits.heightSizeInner.val != nil && + (!subviewTraits.heightSizeInner.wrapVal || [subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { + isFlexEngine = NO; + } + if (isFlexEngine) { + [flexHeightSubviewEngines addObject:subviewEngine]; + } + //在计算拉伸时,如果没有设置宽度约束则将宽度设置为0 + if (subviewTraits.heightSizeInner.val == nil && !subviewTraits.heightSizeInner.lBoundValInner.wrapVal) { + subviewEngine.height = 0; + } } - else if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == sbvsc.widthSizeInner) - {//特殊处理高度等于宽度的情况 - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width]; - } - else if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == lsc.heightSizeInner) - {//高度依赖父视图高度 - rect.size.height = [sbvsc.heightSizeInner measureWith:selfSize.height - paddingVert]; + //计算子视图的高度 + if (subviewHeight != 0.0) { + subviewEngine.height = subviewHeight; + } else { + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; } - else if (sbvsc.heightSizeInner.dimeNumVal != nil) - { //计算出相对高度和上下位置。 - rect.size.height = sbvsc.heightSizeInner.measure; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview :subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + //特殊处理宽度等于高度的情况 + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; } - else; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - //计算固定高度尺寸和浮动高度尺寸部分 - - if (sbvsc.topPosInner.isRelativePos) - { - totalWeight += sbvsc.topPosInner.posNumVal.doubleValue; - - fixedHeight += sbvsc.topPosInner.offsetVal; + //计算子视图宽度以及对齐, 先计算宽度的原因是处理那些高度依赖宽度并且是wrap的情况。 + CGFloat tempLayoutWidth = [self mySubviewEngine:subviewEngine calcLeadingTrailingWithContext:context]; + + //左右依赖的,或者依赖父视图宽度的不参数最宽计算!! + if ((tempLayoutWidth > maxLayoutWidth) && + (subviewTraits.widthSizeInner.anchorVal == nil || subviewTraits.widthSizeInner.anchorVal != layoutTraits.widthSizeInner) && + !subviewTraits.widthSizeInner.fillVal && + (subviewTraits.leadingPosInner.val == nil || subviewTraits.trailingPosInner.val == nil || subviewTraits.widthSizeInner.val != nil)) { + maxLayoutWidth = tempLayoutWidth; } - else - { - fixedHeight += sbvsc.topPosInner.absVal; - - if (sbvsc.topPosInner.absVal != 0) - { - fixedSpaceCount += 1; - fixedSpaceHeight += sbvsc.topPosInner.absVal; + + if (subviewHeight == 0.0) { + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { //特殊处理高度等于宽度的情况 + + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview :subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; } + //再次特殊处理高度包裹的场景 + if (subviewTraits.heightSizeInner.wrapVal) { + subviewEngine.height = [self mySubview:subviewTraits wrapHeightSizeFits:subviewEngine.size withContext:context]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview :subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } } - - pos += sbvsc.topPosInner.absVal; - rect.origin.y = pos; - - - if (sbvsc.weight > 0.0) - { - totalWeight += sbvsc.weight; + + //计算固定高度尺寸和浮动高度尺寸部分 + if (subviewTraits.topPosInner.isRelativePos) { + totalWeight += subviewTraits.topPosInner.numberVal.doubleValue; + totalHeight += subviewTraits.topPosInner.offsetVal; + } else { + totalHeight += subviewTraits.topPosInner.measure; + + if (subviewTraits.topPosInner.measure != 0.0) { + fixedSpacingSubviewCount += 1; + totalFixedSpacing += subviewTraits.topPosInner.measure; + } } - else - { - fixedHeight += rect.size.height; - + totalShrink += subviewTraits.topPosInner.shrink; + pos += subviewTraits.topPosInner.measure; + subviewEngine.top = pos; + + if (subviewTraits.weight != 0.0) { + totalWeight += subviewTraits.weight; + } else { + totalHeight += subviewEngine.height; + totalShrink += subviewTraits.heightSizeInner.shrink; + //如果最小高度不为自身并且高度不是包裹的则可以进行缩小。 - if (sbvsc.heightSizeInner.lBoundValInner.dimeSelfVal == nil) - { - fixedSizeHeight += rect.size.height; - [fixedSizeSbs addObject:sbv]; + if (!subviewTraits.heightSizeInner.lBoundValInner.wrapVal) { + totalFixedHeight += subviewEngine.height; + [fixedHeightSubviewEngines addObject:subviewEngine]; } } - pos += rect.size.height; - - if (sbvsc.bottomPosInner.isRelativePos) - { - totalWeight += sbvsc.bottomPosInner.posNumVal.doubleValue; - fixedHeight += sbvsc.bottomPosInner.offsetVal; + pos += subviewEngine.height; + + if (subviewTraits.bottomPosInner.isRelativePos) { + totalWeight += subviewTraits.bottomPosInner.numberVal.doubleValue; + totalHeight += subviewTraits.bottomPosInner.offsetVal; + } else { + totalHeight += subviewTraits.bottomPosInner.measure; + if (subviewTraits.bottomPosInner.measure != 0.0) { + fixedSpacingSubviewCount += 1; + totalFixedSpacing += subviewTraits.bottomPosInner.measure; + } } - else - { - fixedHeight += sbvsc.bottomPosInner.absVal; - - if (sbvsc.bottomPosInner.absVal != 0) - { - fixedSpaceCount += 1; - fixedSpaceHeight += sbvsc.bottomPosInner.absVal; + totalShrink += subviewTraits.bottomPosInner.shrink; + pos += subviewTraits.bottomPosInner.measure; + + if (subviewEngine != context->subviewEngines.lastObject) { + totalHeight += context->vertSpace; + pos += context->vertSpace; + if (context->vertSpace != 0.0) { + fixedSpacingSubviewCount += 1; + totalFixedSpacing += context->vertSpace; } - } - - pos += sbvsc.bottomPosInner.absVal; - - if (sbv != sbs.lastObject) - { - fixedHeight += subviewSpace; - - pos += subviewSpace; - - if (subviewSpace != 0) - { - fixedSpaceCount += 1; - fixedSpaceHeight += subviewSpace; + } + + if (layoutTraits.heightSizeInner.wrapVal) { + if (totalWeight != 0.0) { //在包裹高度且总体比重不为0时则,则需要还原最小的高度,这样就不会使得高度在横竖屏或者多次计算后越来高。 + CGFloat tempLayoutHeight = paddingVert; + if (context->subviewEngines.count > 1) { + tempLayoutHeight += (context->subviewEngines.count - 1) * context->vertSpace; } + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:tempLayoutHeight subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } else { + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:totalHeight + paddingVert subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; } - - sbvmyFrame.frame = rect; + + //如果是高度自适应则不需要压缩。 + totalShrink = 0.0; } - - //在包裹宽度且总体比重不为0时则,则需要还原最小的宽度,这样就不会使得宽度在横竖屏或者多次计算后越来越宽。 - if (lsc.wrapContentHeight && totalWeight != 0) - { - CGFloat tempSelfHeight = paddingVert; - if (sbs.count > 1) - tempSelfHeight += (sbs.count - 1) * subviewSpace; - - selfSize.height = [self myValidMeasure:lsc.heightSizeInner sbv:self calcSize:tempSelfHeight sbvSize:selfSize selfLayoutSize:self.superview.bounds.size]; - + + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:maxLayoutWidth + context->paddingLeading + context->paddingTrailing subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; } //这里需要特殊处理当子视图的尺寸高度大于布局视图的高度的情况。 - - BOOL isWeightShrinkSpace = NO; //是否按比重缩小间距。 - CGFloat weightShrinkSpaceTotalHeight = 0; - CGFloat floatingHeight = selfSize.height - fixedHeight - paddingVert; //剩余的可浮动的高度,那些weight不为0的从这个高度来进行分发 + BOOL isWeightShrinkSpacing = NO; //是否按比重缩小间距。 + CGFloat weightShrinkSpacingTotalHeight = 0; + CGFloat spareHeight = context->selfSize.height - totalHeight - paddingVert; //剩余的可浮动的高度,那些weight不为0的从这个高度来进行分发 //取出shrinkType中的模式和内容类型: - MySubviewsShrinkType sstMode = lsc.shrinkType & 0x0F; //压缩的模式 - MySubviewsShrinkType sstContent = lsc.shrinkType & 0xF0; //压缩内容 - if (_myCGFloatLessOrEqual(floatingHeight, 0)) - { - if (sstMode != MySubviewsShrink_None) - { - if (sstContent == MySubviewsShrink_Size) - {//压缩尺寸 - if (fixedSizeSbs.count > 0 && totalWeight != 0 && floatingHeight < 0 && selfSize.height > 0) - { - if (sstMode == MySubviewsShrink_Average) - {//均分。 - CGFloat averageHeight = floatingHeight / fixedSizeSbs.count; - - for (UIView *fsbv in fixedSizeSbs) - { - fsbv.myFrame.height += averageHeight; + MySubviewsShrinkType sstMode = layoutTraits.shrinkType & 0x0F; //压缩的模式 + MySubviewsShrinkType sstContent = layoutTraits.shrinkType & 0xF0; //压缩内容 + + //如果子视图设置了压缩比重则ssMode不起作用 + if (totalShrink != 0.0) { + sstMode = MySubviewsShrink_None; + } + if (_myCGFloatLessOrEqual(spareHeight, 0)) { + if (sstMode != MySubviewsShrink_None) { + if (sstContent == MySubviewsShrink_Size) { //压缩尺寸 + if (fixedHeightSubviewEngines.count > 0 && spareHeight < 0 && context->selfSize.height > 0) { + if (sstMode == MySubviewsShrink_Average) { //均分。 + CGFloat averageHeight = spareHeight / fixedHeightSubviewEngines.count; + + for (MyLayoutEngine *fixedHeightSubviewEngine in fixedHeightSubviewEngines) { + fixedHeightSubviewEngine.height += averageHeight; } - } - else if (_myCGFloatNotEqual(fixedSizeHeight, 0)) - {//按比例分配。 - for (UIView *fsbv in fixedSizeSbs) - { - fsbv.myFrame.height += floatingHeight * (fsbv.myFrame.height / fixedSizeHeight); + } else if (_myCGFloatNotEqual(totalFixedHeight, 0)) { //按比例分配。 + for (MyLayoutEngine *fixedHeightSubviewEngine in fixedHeightSubviewEngines) { + fixedHeightSubviewEngine.height += spareHeight * (fixedHeightSubviewEngine.height / totalFixedHeight); } - } } - } - else if (sstContent == MySubviewsShrink_Space) - {//压缩间距 - if (fixedSpaceCount > 0 && floatingHeight < 0 && selfSize.height > 0 && fixedSpaceHeight > 0) - { - if (sstMode == MySubviewsShrink_Average) - { - addSpace = floatingHeight / fixedSpaceCount; - } - else if (sstMode == MySubviewsShrink_Weight) - { - isWeightShrinkSpace = YES; - weightShrinkSpaceTotalHeight = floatingHeight; + } else if (sstContent == MySubviewsShrink_Space) { //压缩间距 + if (fixedSpacingSubviewCount > 0 && spareHeight < 0 && context->selfSize.height > 0 && totalFixedSpacing > 0) { + if (sstMode == MySubviewsShrink_Average) { + addSpace = spareHeight / fixedSpacingSubviewCount; + } else if (sstMode == MySubviewsShrink_Weight) { + isWeightShrinkSpacing = YES; + weightShrinkSpacingTotalHeight = spareHeight; } } - - } - else - { - ; } + } + if (totalShrink == 0.0) { + spareHeight = 0.0; } + } else { + //如果不需要压缩则压缩比设置为0 + totalShrink = 0.0; + } - - floatingHeight = 0; + //如果是总的压缩比重不为0则认为固定高度和布局视图高度保持一致。 + if (totalShrink != 0.0) { + totalHeight = context->selfSize.height - paddingVert; } - //如果有浮动尺寸或者有压缩模式 - if (totalWeight != 0 || (sstMode != MySubviewsShrink_None && _myCGFloatLessOrEqual(floatingHeight, 0))) - { - pos = paddingTop; - for (UIView *sbv in sbs) { - - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - - CGFloat topSpace = sbvsc.topPosInner.posNumVal.doubleValue; - CGFloat bottomSpace = sbvsc.bottomPosInner.posNumVal.doubleValue; - CGFloat weight = sbvsc.weight; - CGRect rect = sbvmyFrame.frame; + if (totalWeight != 0.0 || totalShrink != 0.0 || (sstMode != MySubviewsShrink_None && _myCGFloatLessOrEqual(spareHeight, 0)) || context->vertGravity != MyGravity_None || layoutTraits.widthSizeInner.wrapVal) { + maxLayoutWidth = 0.0; + CGFloat incSpacing = 0.0; //间距扩充 + CGFloat incHeight = 0.0; //尺寸扩充 + if (context->vertGravity == MyGravity_Vert_Center) { + pos = (context->selfSize.height - totalHeight - paddingVert) / 2.0 + context->paddingTop; + } else if (context->vertGravity == MyGravity_Vert_Window_Center) { + if (self.window != nil) { + pos = (CGRectGetHeight(self.window.bounds) - totalHeight) / 2.0; + CGPoint point = CGPointMake(0, pos); + pos = [self.window convertPoint:point toView:self].y; + } + } else if (context->vertGravity == MyGravity_Vert_Bottom) { + pos = context->selfSize.height - totalHeight - context->paddingBottom; + } else if (context->vertGravity == MyGravity_Vert_Between) { + pos = context->paddingTop; + + if (context->subviewEngines.count > 1) { + incSpacing = (context->selfSize.height - totalHeight - paddingVert) / (context->subviewEngines.count - 1); + } + } else if (context->vertGravity == MyGravity_Vert_Around) { + //around停靠中如果子视图数量大于1则间距均分,并且首尾子视图和父视图的间距为均分的一半,如果子视图数量为1则一个子视图垂直居中。 + if (context->subviewEngines.count > 1) { + incSpacing = (context->selfSize.height - totalHeight - paddingVert) / context->subviewEngines.count; + pos = context->paddingTop + incSpacing / 2; + } else { + pos = (context->selfSize.height - totalHeight - paddingVert) / 2.0 + context->paddingTop; + } + } else if (context->vertGravity == MyGravity_Vert_Among) { + incSpacing = (context->selfSize.height - totalHeight - paddingVert) / (context->subviewEngines.count + 1); + pos = context->paddingTop + incSpacing; + } else if (context->vertGravity == MyGravity_Vert_Fill || context->vertGravity == MyGravity_Vert_Stretch) { + pos = context->paddingTop; + if (flexHeightSubviewEngines.count > 0) { + incHeight = (context->selfSize.height - totalHeight - paddingVert) / flexHeightSubviewEngines.count; + } + } else { + pos = context->paddingTop; + } + + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + + CGFloat topSpacing = subviewTraits.topPosInner.numberVal.doubleValue; + CGFloat bottomSpacing = subviewTraits.bottomPosInner.numberVal.doubleValue; + CGFloat weight = subviewTraits.weight; + //分别处理相对顶部间距和绝对顶部间距 - if ([self myIsRelativePos:topSpace]) - { - CGFloat topSpaceWeight = topSpace; - topSpace = _myCGFloatRound((topSpaceWeight / totalWeight) * floatingHeight); - floatingHeight -= topSpace; - totalWeight -= topSpaceWeight; - if (_myCGFloatLessOrEqual(topSpace, 0)) - topSpace = 0; - } - else - { - if (topSpace + sbvsc.topPosInner.offsetVal != 0) - { + if (subviewTraits.topPosInner.isRelativePos) { + CGFloat topSpacingWeight = topSpacing; + topSpacing = _myCGFloatRound((topSpacingWeight / totalWeight) * spareHeight); + if (_myCGFloatLessOrEqual(topSpacing, 0.0)) { + topSpacing = 0.0; + } + } else { + if (topSpacing + subviewTraits.topPosInner.offsetVal != 0.0) { pos += addSpace; - - if (isWeightShrinkSpace) - { - pos += weightShrinkSpaceTotalHeight * (topSpace + sbvsc.topPosInner.offsetVal) / fixedSpaceHeight; + if (isWeightShrinkSpacing) { + pos += weightShrinkSpacingTotalHeight * (topSpacing + subviewTraits.topPosInner.offsetVal) / totalFixedSpacing; } } - } - - pos += [self myValidMargin:sbvsc.topPosInner sbv:sbv calcPos:topSpace + sbvsc.topPosInner.offsetVal selfLayoutSize:selfSize]; - rect.origin.y = pos; - + + topSpacing += subviewTraits.topPosInner.offsetVal; + if (totalShrink != 0.0 && subviewTraits.topPosInner.shrink != 0.0) { + topSpacing += (subviewTraits.topPosInner.shrink / totalShrink) * spareHeight; + } + pos += [self myValidMargin:subviewTraits.topPosInner subview:subviewTraits.view calcPos:topSpacing selfLayoutSize:context->selfSize]; + subviewEngine.top = pos; + //分别处理相对高度和绝对高度 - if (weight > 0) - { - CGFloat h = _myCGFloatRound((weight / totalWeight) * floatingHeight); - floatingHeight -= h; - totalWeight -= weight; - if (_myCGFloatLessOrEqual(h, 0)) - h = 0; - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:h sbvSize:rect.size selfLayoutSize:selfSize]; + if (weight != 0) { + CGFloat h = _myCGFloatRound((weight / totalWeight) * spareHeight); + if (_myCGFloatLessOrEqual(h, 0.0)) { + h = 0.0; + } + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview :subviewTraits.view calcSize:h subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; } - - pos += rect.size.height; - + + //加上扩充的宽度。 + if (incHeight != 0 && [flexHeightSubviewEngines containsObject:subviewEngine]) { + subviewEngine.height += incHeight; + } + if (totalShrink != 0.0 && subviewTraits.heightSizeInner.shrink != 0.0) { + subviewEngine.height += (subviewTraits.heightSizeInner.shrink / totalShrink) * spareHeight; + if (subviewEngine.height < 0.0) { + subviewEngine.height = 0.0; + } + } + + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { //特殊处理宽度等于高度的情况 + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; + } + + //计算子视图宽度以及对齐 + CGFloat tempLayoutWidth = [self mySubviewEngine:subviewEngine calcLeadingTrailingWithContext:context]; + + if (layoutTraits.widthSizeInner.wrapVal && + (tempLayoutWidth > maxLayoutWidth) && + (subviewTraits.widthSizeInner.anchorVal == nil || subviewTraits.widthSizeInner.anchorVal != layoutTraits.widthSizeInner) && + !subviewTraits.widthSizeInner.fillVal && + (subviewTraits.leadingPosInner.val == nil || subviewTraits.trailingPosInner.val == nil || subviewTraits.widthSizeInner.val != nil)) { + maxLayoutWidth = tempLayoutWidth; + if (context->selfSize.width < maxLayoutWidth + context->paddingLeading + context->paddingTrailing) + context->selfSize.width = maxLayoutWidth + context->paddingLeading + context->paddingTrailing; + } + + pos += subviewEngine.height; + //分别处理相对底部间距和绝对底部间距 - if ([self myIsRelativePos:bottomSpace]) - { - CGFloat bottomSpaceWeight = bottomSpace; - bottomSpace = _myCGFloatRound((bottomSpaceWeight / totalWeight) * floatingHeight); - floatingHeight -= bottomSpace; - totalWeight -= bottomSpaceWeight; - if ( _myCGFloatLessOrEqual(bottomSpace, 0)) - bottomSpace = 0; - - } - else - { - if (bottomSpace + sbvsc.bottomPosInner.offsetVal != 0) - { + if (subviewTraits.bottomPosInner.isRelativePos) { + CGFloat bottomSpaceWeight = bottomSpacing; + bottomSpacing = _myCGFloatRound((bottomSpaceWeight / totalWeight) * spareHeight); + if (_myCGFloatLessOrEqual(bottomSpacing, 0.0)) { + bottomSpacing = 0.0; + } + } else { + if (bottomSpacing + subviewTraits.bottomPosInner.offsetVal != 0.0) { pos += addSpace; - - if (isWeightShrinkSpace) - { - pos += weightShrinkSpaceTotalHeight * (bottomSpace + sbvsc.bottomPosInner.offsetVal) / fixedSpaceHeight; + if (isWeightShrinkSpacing) { + pos += weightShrinkSpacingTotalHeight * (bottomSpacing + subviewTraits.bottomPosInner.offsetVal) / totalFixedSpacing; } } - } - - pos += [self myValidMargin:sbvsc.bottomPosInner sbv:sbv calcPos:bottomSpace + sbvsc.bottomPosInner.offsetVal selfLayoutSize:selfSize]; - + + bottomSpacing += subviewTraits.bottomPosInner.offsetVal; + if (totalShrink != 0.0 && subviewTraits.bottomPosInner.shrink != 0.0) { + bottomSpacing += (subviewTraits.bottomPosInner.shrink / totalShrink) * spareHeight; + } + pos += [self myValidMargin:subviewTraits.bottomPosInner subview:subviewTraits.view calcPos:bottomSpacing selfLayoutSize:context->selfSize]; + //添加共有的子视图间距 - if (sbv != sbs.lastObject) - { - pos += subviewSpace; - - if (subviewSpace != 0) - { + if (subviewEngine != context->subviewEngines.lastObject) { + pos += context->vertSpace; + if (context->vertSpace != 0.0) { pos += addSpace; - - if (isWeightShrinkSpace) - { - pos += weightShrinkSpaceTotalHeight * subviewSpace / fixedSpaceHeight; + if (isWeightShrinkSpacing) { + pos += weightShrinkSpacingTotalHeight * context->vertSpace / totalFixedSpacing; } } - + pos += incSpacing; //只有mgvert为between才加这个间距拉伸。 } - - sbvmyFrame.frame = rect; } - } - - pos += paddingBottom; - - if (lsc.wrapContentHeight) - { - selfSize.height = pos; + pos += context->paddingBottom; + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = pos; + } + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = maxLayoutWidth + context->paddingLeading + context->paddingTrailing; } - - return selfSize; } --(CGSize)myLayoutSubviewsForHorz:(CGSize)selfSize sbs:(NSArray*)sbs lsc:(MyLinearLayout*)lsc -{ - - CGFloat subviewSpace = lsc.subviewHSpace; - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - CGFloat paddingHorz = paddingLeading + paddingTrailing; - CGFloat paddingVert = paddingTop + paddingBottom; - MyGravity vertGravity = lsc.gravity & MyGravity_Horz_Mask; - - CGFloat fixedWidth = 0; //计算固定部分的高度 - CGFloat floatingWidth = 0; //浮动的高度。 - CGFloat totalWeight = 0; - - CGFloat maxSubviewHeight = 0; - CGFloat addSpace = 0; //用于压缩时的间距压缩增量。 - - //计算出固定的子视图宽度的总和以及宽度比例总和 - - NSMutableArray *fixedSizeSbs = [NSMutableArray new]; - NSMutableArray *flexedSizeSbs = [NSMutableArray new]; - CGFloat fixedSizeWidth = 0; //固定尺寸的宽度 - NSInteger fixedSpaceCount = 0; //固定间距的子视图数量。 - CGFloat fixedSpaceWidth = 0; //固定间距的子视图的宽度。 - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (sbvsc.leadingPosInner.isRelativePos) - { - totalWeight += sbvsc.leadingPosInner.posNumVal.doubleValue; - fixedWidth += sbvsc.leadingPosInner.offsetVal; - } - else - { - fixedWidth += sbvsc.leadingPosInner.absVal; - if (sbvsc.leadingPosInner.absVal != 0) - { - fixedSpaceCount += 1; - fixedSpaceWidth += sbvsc.leadingPosInner.absVal; +- (void)myDoHorzOrientationLayoutWithContext:(MyLayoutContext *)context { + MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)context->layoutViewEngine.currentSizeClass; + CGFloat subviewWidth = [layoutTraits.flexSpace calcMaxMinSubviewSize:context->selfSize.width arrangedCount:context->subviewEngines.count paddingStart:&context->paddingLeading paddingEnd:&context->paddingTrailing space:&context->horzSpace]; + CGFloat totalWidth = 0.0; //计算固定部分的宽度 + CGFloat totalWeight = 0.0; //剩余部分的总比重 + CGFloat totalShrink = 0.0; //总的压缩比重 + CGFloat addSpace = 0.0; //用于压缩时的间距压缩增量。 + CGFloat paddingHorz = context->paddingLeading + context->paddingTrailing; + + //宽度是可伸缩的子视图集合 + NSMutableSet *flexWidthSubviewEngines = [NSMutableSet new]; + //宽度是固定尺寸的子视图集合 + NSMutableArray *fixedWidthSubviewEngines = [NSMutableArray new]; + //左右拉伸宽度尺寸的子视图集合 + NSMutableArray *leftRightFlexWidthSubviewEngines = [NSMutableArray new]; + CGFloat totalFixedWidth = 0.0; //固定尺寸视图的宽度 + NSInteger fixedSpacingSubviewCount = 0; //固定间距的子视图数量。 + CGFloat totalFixedSpacing = 0.0; //固定间距的子视图的宽度。 + CGFloat baselinePos = CGFLOAT_MAX; //保存基线的值。 + CGFloat pos = context->paddingLeading; + CGFloat maxLayoutHeight = 0.0; + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + //计算出固定宽度部分以及weight部分。这里的宽度可能依赖高度。如果不是高度包裹则计算出所有高度。 + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + if (context->horzGravity == MyGravity_Horz_Fill || context->horzGravity == MyGravity_Horz_Stretch) { + BOOL isFlexEngine = YES; + //设置了比重宽度的子视图不伸缩 + if (subviewTraits.weight != 0.0) { + isFlexEngine = NO; + } + //宽度依赖父视图宽度的不伸缩 + if ((subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == layoutTraits.widthSizeInner) || subviewTraits.widthSizeInner.fillVal) { + isFlexEngine = NO; + } + //布局子视图宽度自适应时不伸缩 + if (subviewTraits.widthSizeInner.wrapVal && [subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + isFlexEngine = NO; + } + //子视图宽度最小为自身宽度时不伸缩。 + if (subviewTraits.widthSizeInner.lBoundValInner.wrapVal) { + isFlexEngine = NO; + } + //对于拉伸来说,只要是设置了宽度约束(除了非布局子视图的宽度自适应除外)都不会被伸缩 + if (context->horzGravity == MyGravity_Horz_Stretch && + subviewTraits.widthSizeInner.val != nil && + (!subviewTraits.widthSizeInner.wrapVal || [subviewTraits.view isKindOfClass:[MyBaseLayout class]])) { + isFlexEngine = NO; + } + if (isFlexEngine) { + [flexWidthSubviewEngines addObject:subviewEngine]; + } + //在计算拉伸时,如果没有设置宽度约束则将宽度设置为0 + if (subviewTraits.widthSizeInner.val == nil && !subviewTraits.widthSizeInner.lBoundValInner.wrapVal) { + subviewEngine.width = 0.0; } } - - if (sbvsc.trailingPosInner.isRelativePos) - { - totalWeight += sbvsc.trailingPosInner.posNumVal.doubleValue; - fixedWidth += sbvsc.trailingPosInner.offsetVal; + + //计算子视图的宽度,这里不管是否设置约束以及是否宽度是weight的都是进行计算。 + if (subviewWidth != 0.0) { + subviewEngine.width = subviewWidth; + } else { + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; } - else - { - fixedWidth += sbvsc.trailingPosInner.absVal; - if (sbvsc.trailingPosInner.absVal != 0) - { - fixedSpaceCount += 1; - fixedSpaceWidth += sbvsc.trailingPosInner.absVal; - } + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview :subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { //特殊处理高度等于宽度的情况 + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; } - - if (sbvsc.weight > 0.0) - { - totalWeight += sbvsc.weight; + + //计算子视图高度以及对齐 + CGFloat tempLayoutHeight = [self mySubviewEngine:subviewEngine baselinePos:baselinePos calcTopBottomWithContext:context]; + + if ((tempLayoutHeight > maxLayoutHeight) && + (subviewTraits.heightSizeInner.anchorVal == nil || subviewTraits.heightSizeInner.anchorVal != layoutTraits.heightSizeInner) && + !subviewTraits.heightSizeInner.fillVal && + (subviewTraits.topPosInner.val == nil || subviewTraits.bottomPosInner.val == nil || subviewTraits.heightSizeInner.val != nil)) { + maxLayoutHeight = tempLayoutHeight; } - else - { - CGFloat vWidth = sbvmyFrame.width; - if (sbvsc.widthSizeInner.dimeNumVal != nil) - vWidth = sbvsc.widthSizeInner.measure; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == lsc.widthSizeInner) - vWidth = [sbvsc.widthSizeInner measureWith:selfSize.width - paddingHorz]; - - vWidth = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:vWidth sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - sbvmyFrame.width = vWidth; - fixedWidth += vWidth; - + + //如果垂直方向的对齐方式是基线对齐,那么就以第一个具有基线的视图作为标准位置。 + if (context->vertGravity == MyGravity_Vert_Baseline && baselinePos == CGFLOAT_MAX && self.baselineBaseView == subviewTraits.view) { + UIFont *subviewFont = [subviewTraits.view valueForKey:@"font"]; + //这里要求baselineBaseView必须要具有font属性。 + //得到基线位置。 + baselinePos = subviewEngine.top + (subviewEngine.height - subviewFont.lineHeight) / 2.0 + subviewFont.ascender; + } + + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner && subviewWidth == 0.0) { //特殊处理宽度等于高度的情况 + + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview :subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + + //计算固定宽度尺寸和浮动宽度尺寸部分 + if (subviewTraits.leadingPosInner.isRelativePos) { + totalWeight += subviewTraits.leadingPosInner.numberVal.doubleValue; + + totalWidth += subviewTraits.leadingPosInner.offsetVal; + } else { + totalWidth += subviewTraits.leadingPosInner.measure; + + if (subviewTraits.leadingPosInner.measure != 0.0) { + fixedSpacingSubviewCount += 1; + totalFixedSpacing += subviewTraits.leadingPosInner.measure; + } + } + + totalShrink += subviewTraits.leadingPosInner.shrink; + + pos += subviewTraits.leadingPosInner.measure; + subviewEngine.leading = pos; + + if (subviewTraits.weight != 0.0) { + totalWeight += subviewTraits.weight; + } else { + totalWidth += subviewEngine.width; + totalShrink += subviewTraits.widthSizeInner.shrink; + //如果最小宽度不为自身并且宽度不是包裹的则可以进行缩小。 - if (sbvsc.widthSizeInner.lBoundValInner.dimeSelfVal == nil) - { - fixedSizeWidth += vWidth; - [fixedSizeSbs addObject:sbv]; + if (!subviewTraits.widthSizeInner.lBoundValInner.wrapVal) { + totalFixedWidth += subviewEngine.width; + [fixedWidthSubviewEngines addObject:subviewEngine]; } - - if (sbvsc.widthSizeInner.dimeSelfVal != nil) - { - [flexedSizeSbs addObject:sbv]; + + if (subviewTraits.widthSizeInner.wrapVal) { + [leftRightFlexWidthSubviewEngines addObject:subviewEngine]; } } - - if (sbv != sbs.lastObject) - { - fixedWidth += subviewSpace; - if (subviewSpace != 0) - { - fixedSpaceCount += 1; - fixedSpaceWidth += subviewSpace; + + pos += subviewEngine.width; + + if (subviewTraits.trailingPosInner.isRelativePos) { + totalWeight += subviewTraits.trailingPosInner.numberVal.doubleValue; + totalWidth += subviewTraits.trailingPosInner.offsetVal; + } else { + totalWidth += subviewTraits.trailingPosInner.measure; + + if (subviewTraits.trailingPosInner.measure != 0.0) { + fixedSpacingSubviewCount += 1; + totalFixedSpacing += subviewTraits.trailingPosInner.measure; + } + } + totalShrink += subviewTraits.trailingPosInner.shrink; + pos += subviewTraits.trailingPosInner.measure; + if (subviewEngine != context->subviewEngines.lastObject) { + totalWidth += context->horzSpace; + pos += context->horzSpace; + if (context->horzSpace != 0.0) { + fixedSpacingSubviewCount += 1; + totalFixedSpacing += context->horzSpace; } } } - - //在包裹宽度且总体比重不为0时则,则需要还原最小的宽度,这样就不会使得宽度在横竖屏或者多次计算后越来越宽。 - if (lsc.wrapContentWidth && totalWeight != 0) - { - CGFloat tempSelfWidth = paddingHorz; - if (sbs.count > 1) - tempSelfWidth += (sbs.count - 1) * subviewSpace; - - selfSize.width = [self myValidMeasure:lsc.widthSizeInner sbv:self calcSize:tempSelfWidth sbvSize:selfSize selfLayoutSize:self.superview.bounds.size]; - } - - //剩余的可浮动的宽度,那些weight不为0的从这个高度来进行分发 - BOOL isWeightShrinkSpace = NO; //是否按比重缩小间距。。。 - CGFloat weightShrinkSpaceTotalWidth = 0; - floatingWidth = selfSize.width - fixedWidth - paddingHorz; - if (_myCGFloatLessOrEqual(floatingWidth, 0)) - { - //取出shrinkType中的模式和内容类型: - MySubviewsShrinkType sstMode = lsc.shrinkType & 0x0F; //压缩的模式 - MySubviewsShrinkType sstContent = lsc.shrinkType & 0xF0; //压缩内容 + //在包裹宽度且总体比重不为0时则,则需要还原最小的宽度,这样就不会使得宽度在横竖屏或者多次计算后越来越宽。 + if (layoutTraits.widthSizeInner.wrapVal) { + if (totalWeight != 0.0) { + CGFloat tempSelfWidth = paddingHorz; + if (context->subviewEngines.count > 1) { + tempSelfWidth += (context->subviewEngines.count - 1) * context->horzSpace; + } + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:tempSelfWidth subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } else { + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:totalWidth + paddingHorz subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + + //如果是宽度自适应则不需要压缩。 + totalShrink = 0.0; + } + + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:maxLayoutHeight + context->paddingTop + context->paddingBottom subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + + //这里需要特殊处理当子视图的尺寸宽度大于布局视图的宽度的情况. + //剩余的可浮动的宽度,那些weight不为0的从这个宽度来进行分发 + BOOL isWeightShrinkSpacing = NO; //是否按比重缩小间距。。。 + CGFloat weightShrinkSpacingTotalWidth = 0.0; + CGFloat spareWidth = context->selfSize.width - totalWidth - paddingHorz; + //取出shrinkType中的模式和内容类型: + MySubviewsShrinkType sstMode = layoutTraits.shrinkType & 0x0F; //压缩的模式 + MySubviewsShrinkType sstContent = layoutTraits.shrinkType & 0xF0; //压缩内容 + + //如果子视图设置了压缩比重则ssMode不起作用 + if (totalShrink != 0.0) { + sstMode = MySubviewsShrink_None; + } + if (_myCGFloatLessOrEqual(spareWidth, 0.0)) { //如果压缩方式为自动,但是浮动宽度子视图数量不为2则压缩类型无效。 - if (sstMode == MySubviewsShrink_Auto && flexedSizeSbs.count != 2) + if (sstMode == MySubviewsShrink_Auto && leftRightFlexWidthSubviewEngines.count != 2) { sstMode = MySubviewsShrink_None; - - if (sstMode != MySubviewsShrink_None) - { - if (sstContent == MySubviewsShrink_Size) - { - if (fixedSizeSbs.count > 0 && totalWeight != 0 && floatingWidth < 0 && selfSize.width > 0) - { + } + if (sstMode != MySubviewsShrink_None) { + if (sstContent == MySubviewsShrink_Size) { + if (fixedWidthSubviewEngines.count > 0 && spareWidth < 0 && context->selfSize.width > 0) { //均分。 - if (sstMode == MySubviewsShrink_Average) - { - CGFloat averageWidth = floatingWidth / fixedSizeSbs.count; - - for (UIView *fsbv in fixedSizeSbs) - { - fsbv.myFrame.width += averageWidth; - + if (sstMode == MySubviewsShrink_Average) { + CGFloat averageWidth = spareWidth / fixedWidthSubviewEngines.count; + + for (MyLayoutEngine *fixedWidthSubviewEngine in fixedWidthSubviewEngines) { + fixedWidthSubviewEngine.width += averageWidth; } - } - else if (sstMode == MySubviewsShrink_Auto) - { - - UIView *leadingView = flexedSizeSbs[0]; - UIView *trailingView = flexedSizeSbs[1]; - - CGFloat leadingWidth = leadingView.myFrame.width; - CGFloat trailingWidth = trailingView.myFrame.width; - + } else if (sstMode == MySubviewsShrink_Auto) { + MyLayoutEngine *leadingViewEngine = leftRightFlexWidthSubviewEngines[0]; + MyLayoutEngine *trailingViewEngine = leftRightFlexWidthSubviewEngines[1]; + CGFloat leadingWidth = leadingViewEngine.width; + CGFloat trailingWidth = trailingViewEngine.width; + //如果2个都超过一半则总是一半显示。 //如果1个超过了一半则 如果两个没有超过总宽度则正常显示,如果超过了总宽度则超过一半的视图的宽度等于总宽度减去未超过一半的视图的宽度。 //如果没有一个超过一半。则正常显示 - CGFloat layoutWidth = floatingWidth + leadingWidth + trailingWidth; + CGFloat layoutWidth = spareWidth + leadingWidth + trailingWidth; CGFloat halfLayoutWidth = layoutWidth / 2; - - if (_myCGFloatGreat(leadingWidth, halfLayoutWidth) && _myCGFloatGreat(trailingWidth,halfLayoutWidth)) - { - leadingView.myFrame.width = halfLayoutWidth; - trailingView.myFrame.width = halfLayoutWidth; - } - else if ((_myCGFloatGreat(leadingWidth, halfLayoutWidth) || _myCGFloatGreat(trailingWidth, halfLayoutWidth)) && (_myCGFloatGreat(leadingWidth + trailingWidth, layoutWidth))) - { - - if (_myCGFloatGreat(leadingWidth, halfLayoutWidth)) - { - trailingView.myFrame.width = trailingWidth; - leadingView.myFrame.width = layoutWidth - trailingWidth; - } - else - { - leadingView.myFrame.width = leadingWidth; - trailingView.myFrame.width = layoutWidth - leadingWidth; + + if (_myCGFloatGreat(leadingWidth, halfLayoutWidth) && _myCGFloatGreat(trailingWidth, halfLayoutWidth)) { + leadingViewEngine.width = halfLayoutWidth; + trailingViewEngine.width = halfLayoutWidth; + } else if ((_myCGFloatGreat(leadingWidth, halfLayoutWidth) || _myCGFloatGreat(trailingWidth, halfLayoutWidth)) && (_myCGFloatGreat(leadingWidth + trailingWidth, layoutWidth))) { + + if (_myCGFloatGreat(leadingWidth, halfLayoutWidth)) { + trailingViewEngine.width = trailingWidth; + leadingViewEngine.width = layoutWidth - trailingWidth; + } else { + leadingViewEngine.width = leadingWidth; + trailingViewEngine.width = layoutWidth - leadingWidth; } - } - else ; - - - } - else if (_myCGFloatNotEqual(fixedSizeWidth, 0)) - {//按比例分配。 - for (UIView *fsbv in fixedSizeSbs) - { - fsbv.myFrame.width += floatingWidth * (fsbv.myFrame.width / fixedSizeWidth); + } else if (_myCGFloatNotEqual(totalFixedWidth, 0.0)) { //按比例分配。 + for (MyLayoutEngine *fixedWidthSubviewEngine in fixedWidthSubviewEngines) { + fixedWidthSubviewEngine.width += spareWidth * (fixedWidthSubviewEngine.width / totalFixedWidth); } - } } - } - else if (sstContent == MySubviewsShrink_Space) - { - if (fixedSpaceCount > 0 && floatingWidth < 0 && selfSize.width > 0 && fixedSpaceWidth > 0) - { - if (sstMode == MySubviewsShrink_Average) - { - addSpace = floatingWidth / fixedSpaceCount; - } - else if (sstMode == MySubviewsShrink_Weight) - { - isWeightShrinkSpace = YES; - weightShrinkSpaceTotalWidth = floatingWidth; + } else if (sstContent == MySubviewsShrink_Space) { + if (fixedSpacingSubviewCount > 0 && spareWidth < 0 && context->selfSize.width > 0 && totalFixedSpacing > 0) { + if (sstMode == MySubviewsShrink_Average) { + addSpace = spareWidth / fixedSpacingSubviewCount; + } else if (sstMode == MySubviewsShrink_Weight) { + isWeightShrinkSpacing = YES; + weightShrinkSpacingTotalWidth = spareWidth; } } - } - else - { - ; - } - } - - - floatingWidth = 0; - } - - CGFloat baselinePos = CGFLOAT_MAX; //保存基线的值。 - //调整所有子视图的宽度和高度。 - CGFloat pos = paddingLeading; - for (UIView *sbv in sbs) { - - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - CGFloat leadingSpace = sbvsc.leadingPosInner.posNumVal.doubleValue; - CGFloat trailingSpace = sbvsc.trailingPosInner.posNumVal.doubleValue; - CGFloat weight = sbvsc.weight; - - CGRect rect = sbvmyFrame.frame; - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == lsc.heightSizeInner) - rect.size.height= [sbvsc.heightSizeInner measureWith:selfSize.height - paddingVert]; - - - //计算出先对左边边距和绝对左边边距 - if ([self myIsRelativePos:leadingSpace]) - { - CGFloat leadingSpaceWeight = leadingSpace; - leadingSpace = _myCGFloatRound((leadingSpaceWeight / totalWeight) * floatingWidth); - floatingWidth -= leadingSpace; - totalWeight -= leadingSpaceWeight; - if (_myCGFloatLessOrEqual(leadingSpace, 0)) - leadingSpace = 0; - + if (totalShrink == 0.0) { + spareWidth = 0.0; } - else - { - if (leadingSpace + sbvsc.leadingPosInner.offsetVal != 0) - { - pos += addSpace; - - if (isWeightShrinkSpace) - { - pos += weightShrinkSpaceTotalWidth * (leadingSpace + sbvsc.leadingPosInner.offsetVal) / fixedSpaceWidth; - } + } else { + //如果不需要压缩则压缩比设置为0 + totalShrink = 0.0; + } + + //如果是总的压缩比重不为0则认为固定宽度和布局视图宽度保持一致。 + if (totalShrink != 0.0) { + totalWidth = context->selfSize.width - paddingHorz; + } + //如果有浮动尺寸或者有压缩模式 + if (totalWeight != 0.0 || totalShrink != 0.0 || (sstMode != MySubviewsShrink_None && _myCGFloatLessOrEqual(spareWidth, 0)) || context->horzGravity != MyGravity_None || layoutTraits.heightSizeInner.wrapVal) { + maxLayoutHeight = 0.0; + CGFloat incSpacing = 0.0; //间距扩充 + CGFloat incWidth = 0.0; //尺寸扩充 + if (context->horzGravity == MyGravity_Horz_Center) { + pos = (context->selfSize.width - totalWidth - paddingHorz) / 2.0 + context->paddingLeading; + } else if (context->horzGravity == MyGravity_Horz_Window_Center) { + if (self.window != nil) { + pos = (CGRectGetWidth(self.window.bounds) - totalWidth) / 2.0; + + CGPoint point = CGPointMake(pos, 0); + pos = [self.window convertPoint:point toView:self].x; } - - } - - pos += [self myValidMargin:sbvsc.leadingPosInner sbv:sbv calcPos:leadingSpace + sbvsc.leadingPosInner.offsetVal selfLayoutSize:selfSize]; - - rect.origin.x = pos; - - - if (weight > 0) - { - CGFloat w = _myCGFloatRound((weight / totalWeight) * floatingWidth); - floatingWidth -= w; - totalWeight -= weight; - - if (_myCGFloatLessOrEqual(w, 0)) - w = 0; - - rect.size.width = w; - - } - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - pos += rect.size.width; - - //计算相对的右边边距和绝对的右边边距 - if ([self myIsRelativePos:trailingSpace]) - { - CGFloat trailingSpaceWeight = trailingSpace; - trailingSpace = _myCGFloatRound((trailingSpaceWeight / totalWeight) * floatingWidth); - floatingWidth -= trailingSpace; - totalWeight -= trailingSpaceWeight; - if (_myCGFloatLessOrEqual(trailingSpace, 0)) - trailingSpace = 0; - } - else - { - if (trailingSpace + sbvsc.trailingPosInner.offsetVal != 0) - { - pos += addSpace; - - if (isWeightShrinkSpace) - { - pos += weightShrinkSpaceTotalWidth * (trailingSpace + sbvsc.trailingPosInner.offsetVal) / fixedSpaceWidth; - } + } else if (context->horzGravity == MyGravity_Horz_Trailing) { + pos = context->selfSize.width - totalWidth - context->paddingTrailing; + } else if (context->horzGravity == MyGravity_Horz_Between) { + pos = context->paddingLeading; + + if (context->subviewEngines.count > 1) { + incSpacing = (context->selfSize.width - totalWidth - paddingHorz) / (context->subviewEngines.count - 1); } - } - - pos += [self myValidMargin:sbvsc.trailingPosInner sbv:sbv calcPos:trailingSpace + sbvsc.trailingPosInner.offsetVal selfLayoutSize:selfSize]; - - - if (sbv != sbs.lastObject) - { - pos += subviewSpace; - - if (subviewSpace != 0) - { - pos += addSpace; - - if (isWeightShrinkSpace) - { - pos += weightShrinkSpaceTotalWidth * subviewSpace / fixedSpaceWidth; - } + } else if (context->horzGravity == MyGravity_Horz_Around) { + //around停靠中如果子视图数量大于1则间距均分,并且首尾子视图和父视图的间距为均分的一半,如果子视图数量为1则一个子视图垂直居中。 + if (context->subviewEngines.count > 1) { + incSpacing = (context->selfSize.width - totalWidth - paddingHorz) / context->subviewEngines.count; + pos = context->paddingLeading + incSpacing / 2.0; + } else { + pos = (context->selfSize.width - totalWidth - paddingHorz) / 2.0 + context->paddingLeading; } - } - - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == sbvsc.widthSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width ]; - - - //如果高度是浮动的则需要调整高度。 - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]] && sbvsc.heightSizeInner.dimeRelaVal.view != self) - { - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - } - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - - //计算最高的高度。 - if (lsc.wrapContentHeight) - { - if (sbvsc.heightSizeInner.dimeRelaVal.view != self && (sbvsc.topPosInner.posVal == nil || sbvsc.bottomPosInner.posVal == nil)) - { - maxSubviewHeight = [self myCalcSelfSize:maxSubviewHeight subviewSize:rect.size.height headPos:sbvsc.topPosInner centerPos:sbvsc.centerYPosInner tailPos:sbvsc.bottomPosInner]; + } else if (context->horzGravity == MyGravity_Horz_Among) { + incSpacing = (context->selfSize.width - totalWidth - paddingHorz) / (context->subviewEngines.count + 1); + pos = context->paddingLeading + incSpacing; + } else if (context->horzGravity == MyGravity_Horz_Fill || context->horzGravity == MyGravity_Horz_Stretch) { + pos = context->paddingLeading; + if (flexWidthSubviewEngines.count > 0) { + incWidth = (context->selfSize.width - totalWidth - paddingHorz) / flexWidthSubviewEngines.count; } + } else { + pos = context->paddingLeading; } - else - { - [self myCalcSubviewTopBottomRect:vertGravity - selfSize:selfSize - rect_p:&rect - sbv:sbv - paddingBottom:paddingBottom - paddingTop:paddingTop - baselinePos:baselinePos - sbvsc:sbvsc - lsc:lsc]; - - - //如果垂直方向的对齐方式是基线对齐,那么就以第一个具有基线的视图作为标准位置。 - if (vertGravity == MyGravity_Vert_Baseline && baselinePos == CGFLOAT_MAX && self.baselineBaseView == sbv) - { - UIFont *sbvFont = [sbv valueForKey:@"font"]; - //这里要求baselineBaseView必须要具有font属性。 - //得到基线位置。 - baselinePos = rect.origin.y + (rect.size.height - sbvFont.lineHeight) / 2.0 + sbvFont.ascender; - - } - + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; - } - - sbvmyFrame.frame = rect; - } - - pos += paddingTrailing; - - if (lsc.wrapContentWidth) - { - selfSize.width = pos; - } - - //调整所有子视图的高度。 - if (lsc.wrapContentHeight) - { - selfSize.height = maxSubviewHeight + paddingVert; - baselinePos = CGFLOAT_MAX; - - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - CGRect rect = sbvmyFrame.frame; - - [self myCalcSubviewTopBottomRect:vertGravity - selfSize:selfSize - rect_p:&rect - sbv:sbv - paddingBottom:paddingBottom - paddingTop:paddingTop - baselinePos:baselinePos - sbvsc:sbvsc - lsc:lsc]; - - sbvmyFrame.frame = rect; - - //如果垂直方向的对齐方式是基线对齐,那么就以第一个具有基线的视图作为标准位置。 - if (vertGravity == MyGravity_Vert_Baseline && baselinePos == CGFLOAT_MAX && self.baselineBaseView == sbv) - { - UIFont *sbvFont = [sbv valueForKey:@"font"]; - //这里要求baselineBaseView必须要具有font属性。 - //得到基线位置。 - baselinePos = rect.origin.y + (rect.size.height - sbvFont.lineHeight) / 2.0 + sbvFont.ascender; - - } + CGFloat leadingSpacing = subviewTraits.leadingPosInner.numberVal.doubleValue; + CGFloat trailingSpacing = subviewTraits.trailingPosInner.numberVal.doubleValue; + CGFloat weight = subviewTraits.weight; - - } + //分别处理相对顶部间距和绝对顶部间距 + if (subviewTraits.leadingPosInner.isRelativePos) { + CGFloat topSpaceWeight = leadingSpacing; + leadingSpacing = _myCGFloatRound((topSpaceWeight / totalWeight) * spareWidth); + if (_myCGFloatLessOrEqual(leadingSpacing, 0.0)) { + leadingSpacing = 0.0; + } + } else { + if (leadingSpacing + subviewTraits.leadingPosInner.offsetVal != 0.0) { + pos += addSpace; - } - + if (isWeightShrinkSpacing) { + pos += weightShrinkSpacingTotalWidth * (leadingSpacing + subviewTraits.leadingPosInner.offsetVal) / totalFixedSpacing; + } + } + } + leadingSpacing += subviewTraits.leadingPosInner.offsetVal; + if (totalShrink != 0.0 && subviewTraits.leadingPosInner.shrink != 0.0) { + leadingSpacing += (subviewTraits.leadingPosInner.shrink / totalShrink) * spareWidth; + } - return selfSize; -} + pos += [self myValidMargin:subviewTraits.leadingPosInner subview:subviewTraits.view calcPos:leadingSpacing selfLayoutSize:context->selfSize]; + subviewEngine.leading = pos; + //分别处理相对宽度和绝对宽度 + if (weight != 0.0) { + CGFloat w = _myCGFloatRound((weight / totalWeight) * spareWidth); + if (_myCGFloatLessOrEqual(w, 0.0)) { + w = 0.0; + } + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:w subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + } + //加上扩充的宽度。 + if (incWidth != 0.0 && [flexWidthSubviewEngines containsObject:subviewEngine]) { + subviewEngine.width += incWidth; + } + if (totalShrink != 0.0 && subviewTraits.widthSizeInner.shrink != 0.0) { + subviewEngine.width += (subviewTraits.widthSizeInner.shrink / totalShrink) * spareWidth; + if (subviewEngine.width < 0.0) { + subviewEngine.width = 0.0; + } + } --(CGSize)myLayoutSubviewsForVertGravity:(CGSize)selfSize sbs:(NSArray*)sbs lsc:(MyLinearLayout*)lsc -{ - - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - CGFloat paddingHorz = paddingLeading + paddingTrailing; - CGFloat paddingVert = paddingTop + paddingBottom; - CGFloat subviewSpace = lsc.subviewVSpace; - MyGravity vertGravity = lsc.gravity & MyGravity_Horz_Mask; - MyGravity horzGravity = [self myConvertLeftRightGravityToLeadingTrailing:lsc.gravity & MyGravity_Vert_Mask]; - - CGFloat totalHeight = 0; - if (sbs.count > 1) - totalHeight += (sbs.count - 1) * subviewSpace; - - selfSize = [self myCalcMaxWrapWidth:sbs selfSize:selfSize paddingHorz:paddingHorz lsc:lsc]; - - CGFloat floatingHeight = selfSize.height - paddingVert - totalHeight; - if (_myCGFloatLessOrEqual(floatingHeight, 0)) - floatingHeight = 0; - - //调整子视图的宽度。并根据情况调整子视图的高度。并计算出固定高度和浮动高度。 - NSMutableSet *noWrapsbsSet = [NSMutableSet new]; - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - BOOL canAddToNoWrapSbs = YES; - CGRect rect = sbvmyFrame.frame; - - [self myCalcLeadingTrailingRect:horzGravity - selfSize:selfSize - rect_p:&rect - sbv:sbv - paddingTrailing:paddingTrailing - paddingLeading:paddingLeading - sbvsc:sbvsc - lsc:lsc]; - - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == lsc.heightSizeInner) - { - rect.size.height = [sbvsc.heightSizeInner measureWith:selfSize.height - paddingVert]; - canAddToNoWrapSbs = NO; - } + //特殊处理高度依赖宽度的情况。 + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { //特殊处理高度等于宽度的情况 + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + } - - //高度等于宽度的情况。 - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == sbvsc.widthSizeInner) - { - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width]; - } - - //如果子视图需要调整高度则调整高度 - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]] && sbvsc.weight == 0) - { - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - canAddToNoWrapSbs = NO; - } - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - //如果子视图的最小高度就是自身则也不进行扩展。 - if (sbvsc.heightSizeInner.lBoundValInner.dimeSelfVal != nil) - { - canAddToNoWrapSbs = NO; - } + CGFloat tempLayoutHeight = [self mySubviewEngine:subviewEngine baselinePos:baselinePos calcTopBottomWithContext:context]; - - totalHeight += [self myValidMargin:sbvsc.topPosInner sbv:sbv calcPos:[sbvsc.topPosInner realPosIn:floatingHeight] selfLayoutSize:selfSize]; - - totalHeight += rect.size.height; - - totalHeight += [self myValidMargin:sbvsc.bottomPosInner sbv:sbv calcPos:[sbvsc.bottomPosInner realPosIn:floatingHeight] selfLayoutSize:selfSize]; - - sbvmyFrame.frame = rect; - - //如果子布局视图是wrap属性则不进行扩展。 - if (vertGravity == MyGravity_Vert_Fill && [sbv isKindOfClass:[MyBaseLayout class]]) - { - if (sbvsc.wrapContentHeight) - { - canAddToNoWrapSbs = NO; + if (layoutTraits.heightSizeInner.wrapVal && + (tempLayoutHeight > maxLayoutHeight) && + (subviewTraits.heightSizeInner.anchorVal == nil || subviewTraits.heightSizeInner.anchorVal != layoutTraits.heightSizeInner) && + !subviewTraits.heightSizeInner.fillVal && + (subviewTraits.topPosInner.val == nil || subviewTraits.bottomPosInner.val == nil || subviewTraits.heightSizeInner.val != nil)) { + maxLayoutHeight = tempLayoutHeight; + if (context->selfSize.height < maxLayoutHeight + context->paddingTop + context->paddingBottom) + context->selfSize.height = maxLayoutHeight + context->paddingTop + context->paddingBottom; } - } - - if (canAddToNoWrapSbs) - [noWrapsbsSet addObject:sbv]; - - } - - - //根据对齐的方位来定位子视图的布局对齐 - CGFloat pos = 0; //位置偏移 - CGFloat between = 0; //间距扩充 - CGFloat fill = 0; //尺寸扩充 - if (vertGravity == MyGravity_Vert_Top) - { - pos = paddingTop; - } - else if (vertGravity == MyGravity_Vert_Center) - { - pos = (selfSize.height - totalHeight - paddingVert)/2.0 + paddingTop; - } - else if (vertGravity == MyGravity_Vert_Window_Center) - { - if (self.window != nil) - { - pos = (CGRectGetHeight(self.window.bounds) - totalHeight)/2.0; - - CGPoint pt = CGPointMake(0, pos); - pos = [self.window convertPoint:pt toView:self].y; - - - } - } - else if (vertGravity == MyGravity_Vert_Bottom) - { - pos = selfSize.height - totalHeight - paddingBottom; - } - else if (vertGravity == MyGravity_Vert_Between) - { - pos = paddingTop; - - if (sbs.count > 1) - between = (selfSize.height - totalHeight - paddingVert) / (sbs.count - 1); - } - else if (vertGravity == MyGravity_Vert_Fill) - { - pos = paddingTop; - if (noWrapsbsSet.count > 0) - fill = (selfSize.height - totalHeight - paddingVert) / noWrapsbsSet.count; - } - else - { - pos = paddingTop; - } - - - - for (UIView *sbv in sbs) - { - - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - pos += [self myValidMargin:sbvsc.topPosInner sbv:sbv calcPos:[sbvsc.topPosInner realPosIn:floatingHeight] selfLayoutSize:selfSize]; - - sbvmyFrame.top = pos; - - //加上扩充的宽度。 - if (fill != 0 && [noWrapsbsSet containsObject:sbv]) - sbvmyFrame.height += fill; - - pos += sbvmyFrame.height; - - - pos += [self myValidMargin:sbvsc.bottomPosInner sbv:sbv calcPos:[sbvsc.bottomPosInner realPosIn:floatingHeight] selfLayoutSize:selfSize]; - - if (sbv != sbs.lastObject) - pos += subviewSpace; - - pos += between; //只有mgvert为between才加这个间距拉伸。 - } - - return selfSize; - -} - + pos += subviewEngine.width; --(CGSize)myLayoutSubviewsForHorzGravity:(CGSize)selfSize sbs:(NSArray*)sbs lsc:(MyLinearLayout*)lsc -{ - CGFloat paddingTop = lsc.myLayoutTopPadding; - CGFloat paddingBottom = lsc.myLayoutBottomPadding; - CGFloat paddingLeading = lsc.myLayoutLeadingPadding; - CGFloat paddingTrailing = lsc.myLayoutTrailingPadding; - CGFloat paddingHorz = paddingLeading + paddingTrailing; - CGFloat paddingVert = paddingTop + paddingBottom; - CGFloat subviewSpace = lsc.subviewHSpace; - MyGravity vertGravity = lsc.gravity & MyGravity_Horz_Mask; - MyGravity horzGravity = [self myConvertLeftRightGravityToLeadingTrailing:lsc.gravity & MyGravity_Vert_Mask]; - - - CGFloat totalWidth = 0; - if (sbs.count > 1) - totalWidth += (sbs.count - 1) * subviewSpace; - - - CGFloat floatingWidth = 0; - CGFloat maxSubviewHeight = 0; - - floatingWidth = selfSize.width - paddingHorz - totalWidth; - if (_myCGFloatLessOrEqual(floatingWidth, 0)) - floatingWidth = 0; - - //计算出固定的高度 - NSMutableSet *noWrapsbsSet = [NSMutableSet new]; - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - BOOL canAddToNoWrapSbs = YES; - CGRect rect = sbvmyFrame.frame; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == lsc.widthSizeInner) - { - rect.size.width= [sbvsc.widthSizeInner measureWith:selfSize.width - paddingHorz]; - canAddToNoWrapSbs = NO; - } - - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == lsc.heightSizeInner) - rect.size.height= [sbvsc.heightSizeInner measureWith:selfSize.height - paddingVert]; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == sbvsc.heightSizeInner) - rect.size.width = [sbvsc.widthSizeInner measureWith:rect.size.height]; - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - //如果最小宽度不能被缩小则不加入。 - if (sbvsc.widthSizeInner.lBoundValInner.dimeSelfVal != nil) - { - canAddToNoWrapSbs = NO; - } + //计算相对的右边边距和绝对的右边边距 + if (subviewTraits.trailingPosInner.isRelativePos) { + CGFloat trailingSpaceWeight = trailingSpacing; + trailingSpacing = _myCGFloatRound((trailingSpaceWeight / totalWeight) * spareWidth); + if (_myCGFloatLessOrEqual(trailingSpacing, 0.0)) { + trailingSpacing = 0.0; + } + } else { + if (trailingSpacing + subviewTraits.trailingPosInner.offsetVal != 0.0) { + pos += addSpace; - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == sbvsc.widthSizeInner) - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width]; - - //如果高度是浮动的则需要调整高度。 - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]] && sbvsc.heightSizeInner.dimeRelaVal.view != self) - { - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; - } - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - //计算以子视图为大小的情况 - if (lsc.wrapContentHeight && sbvsc.heightSizeInner.dimeRelaVal.view != self && (sbvsc.topPosInner.posVal == nil || sbvsc.bottomPosInner.posVal == nil)) - { - maxSubviewHeight = [self myCalcSelfSize:maxSubviewHeight subviewSize:rect.size.height headPos:sbvsc.topPosInner centerPos:sbvsc.centerYPosInner tailPos:sbvsc.bottomPosInner]; - } - - - totalWidth += [self myValidMargin:sbvsc.leadingPosInner sbv:sbv calcPos:[sbvsc.leadingPosInner realPosIn:floatingWidth] selfLayoutSize:selfSize]; - - totalWidth += rect.size.width; - - - totalWidth += [self myValidMargin:sbvsc.trailingPosInner sbv:sbv calcPos:[sbvsc.trailingPosInner realPosIn:floatingWidth] selfLayoutSize:selfSize]; - - - sbvmyFrame.frame = rect; - - //如果子视图是包裹属性则也不加入。 - if (horzGravity == MyGravity_Horz_Fill && [sbv isKindOfClass:[MyBaseLayout class]]) - { - if (sbvsc.wrapContentWidth) - { - canAddToNoWrapSbs = NO; + if (isWeightShrinkSpacing) { + pos += weightShrinkSpacingTotalWidth * (trailingSpacing + subviewTraits.trailingPosInner.offsetVal) / totalFixedSpacing; + } + } } - } - - if (canAddToNoWrapSbs) - [noWrapsbsSet addObject:sbv]; - - } - - - //调整自己的高度。 - if (lsc.wrapContentHeight) - { - selfSize.height = maxSubviewHeight + paddingVert; - } - - //根据对齐的方位来定位子视图的布局对齐 - CGFloat pos = 0; - CGFloat between = 0; - CGFloat fill = 0; - - if (horzGravity == MyGravity_Horz_Leading) - { - pos = paddingLeading; - } - else if (horzGravity == MyGravity_Horz_Center) - { - pos = (selfSize.width - totalWidth - paddingHorz)/2.0; - pos += paddingLeading; - } - else if (horzGravity == MyGravity_Horz_Window_Center) - { - if (self.window != nil) - { - pos = (CGRectGetWidth(self.window.bounds) - totalWidth)/2.0; - - CGPoint pt = CGPointMake(pos, 0); - pos = [self.window convertPoint:pt toView:self].x; - - //特殊处理窗口水平居中的场景。 - if ([MyBaseLayout isRTL]) - { - pos += (selfSize.width - CGRectGetWidth(self.window.bounds)); + trailingSpacing += subviewTraits.trailingPosInner.offsetVal; + if (totalShrink != 0.0 && subviewTraits.trailingPosInner.shrink != 0.0) { + trailingSpacing += (subviewTraits.trailingPosInner.shrink / totalShrink) * spareWidth; + } + pos += [self myValidMargin:subviewTraits.trailingPosInner subview:subviewTraits.view calcPos:trailingSpacing selfLayoutSize:context->selfSize]; + //添加共有的子视图间距 + if (subviewEngine != context->subviewEngines.lastObject) { + pos += context->horzSpace; + if (context->horzSpace != 0.0) { + pos += addSpace; + if (isWeightShrinkSpacing) { + pos += weightShrinkSpacingTotalWidth * context->horzSpace / totalFixedSpacing; + } + } + pos += incSpacing; //只有gravity为between或者around才加这个间距拉伸。 } - } } - else if (horzGravity == MyGravity_Horz_Trailing) - { - pos = selfSize.width - totalWidth - paddingTrailing; - } - else if (horzGravity == MyGravity_Horz_Between) - { - pos = paddingLeading; - - if (sbs.count > 1) - between = (selfSize.width - totalWidth - paddingHorz) / (sbs.count - 1); - } - else if (horzGravity == MyGravity_Horz_Fill) - { - pos = paddingLeading; - if (noWrapsbsSet.count > 0) - fill = (selfSize.width - totalWidth - paddingHorz) / noWrapsbsSet.count; + pos += context->paddingTrailing; + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = pos; } - else - { - pos = paddingLeading; + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = maxLayoutHeight + context->paddingTop + context->paddingBottom; } +} - CGFloat baselinePos = CGFLOAT_MAX; //保存基线的值。 - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - pos += [self myValidMargin:sbvsc.leadingPosInner sbv:sbv calcPos:[sbvsc.leadingPosInner realPosIn:floatingWidth] selfLayoutSize:selfSize]; - - - CGRect rect = sbvmyFrame.frame; - - rect.origin.x = pos; - - [self myCalcSubviewTopBottomRect:vertGravity selfSize:selfSize rect_p:&rect sbv:sbv paddingBottom:paddingBottom paddingTop:paddingTop baselinePos:baselinePos sbvsc:sbvsc lsc:lsc]; - - if (fill != 0 && [noWrapsbsSet containsObject:sbv]) - rect.size.width += fill; - - - pos += rect.size.width; - - - pos += [self myValidMargin:sbvsc.trailingPosInner sbv:sbv calcPos:[sbvsc.trailingPosInner realPosIn:floatingWidth] selfLayoutSize:selfSize]; - - sbvmyFrame.frame = rect; - - - if (sbv != sbs.lastObject) - pos += subviewSpace; - - pos += between; //只有mghorz为between才加这个间距拉伸。 - - //如果垂直方向的对齐方式是基线对齐,那么就以第一个具有基线的视图作为标准位置。 - if (vertGravity == MyGravity_Vert_Baseline && baselinePos == CGFLOAT_MAX && self.baselineBaseView == sbv) - { - UIFont *sbvFont = [sbv valueForKey:@"font"]; - //这里要求baselineBaseView必须要具有font属性。 - //得到基线位置。 - baselinePos = rect.origin.y + (rect.size.height - sbvFont.lineHeight) / 2.0 + sbvFont.ascender; - +- (CGFloat)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithContext:(MyLayoutContext *)context { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + UIView *subview = subviewTraits.view; + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; + if (subviewTraits.leadingPosInner.val != nil && subviewTraits.trailingPosInner.val != nil) { + if (subviewTraits.widthSizeInner.val == nil) { + subviewEngine.width = context->selfSize.width - context->paddingLeading - context->paddingTrailing - subviewTraits.leadingPosInner.measure - subviewTraits.trailingPosInner.measure; } } - - return selfSize; -} - - - -- (void)myCalcLeadingTrailingRect:(MyGravity)horzGravity selfSize:(CGSize)selfSize rect_p:(CGRect *)rect_p sbv:(UIView *)sbv paddingTrailing:(CGFloat)paddingTrailing paddingLeading:(CGFloat)paddingLeading sbvsc:(UIView *)sbvsc lsc:(MyLinearLayout*)lsc -{ - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect_p->size.width = sbvsc.widthSizeInner.measure; - - //调整子视图的宽度,如果子视图为matchParent的话 - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == lsc.widthSizeInner) - rect_p->size.width = [sbvsc.widthSizeInner measureWith:selfSize.width - paddingLeading - paddingTrailing]; - - if (sbvsc.leadingPosInner.posVal != nil && sbvsc.trailingPosInner.posVal != nil) - rect_p->size.width = selfSize.width - paddingLeading - paddingTrailing - sbvsc.leadingPosInner.absVal - sbvsc.trailingPosInner.absVal; - - - rect_p->size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect_p->size.width sbvSize:rect_p->size selfLayoutSize:selfSize]; - - - [self myCalcHorzGravity:[self myGetSubviewHorzGravity:sbv sbvsc:sbvsc horzGravity:horzGravity] sbv:sbv sbvsc:sbvsc paddingLeading:paddingLeading paddingTrailing:paddingTrailing selfSize:selfSize pRect:rect_p]; -} + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subview calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; -- (void)myCalcSubviewTopBottomRect:(MyGravity)vertGravity selfSize:(CGSize)selfSize rect_p:(CGRect *)rect_p sbv:(UIView *)sbv paddingBottom:(CGFloat)paddingBottom paddingTop:(CGFloat)paddingTop baselinePos:(CGFloat)baselinePos sbvsc:(UIView *)sbvsc lsc:(MyLinearLayout*)lsc -{ - //计算高度 - if (sbvsc.heightSizeInner.dimeRelaVal != nil && sbvsc.heightSizeInner.dimeRelaVal == lsc.heightSizeInner) - { - rect_p->size.height = [sbvsc.heightSizeInner measureWith:selfSize.height - paddingTop - paddingBottom]; - } - - - if (sbvsc.topPosInner.posVal != nil && sbvsc.bottomPosInner.posVal != nil) - rect_p->size.height = selfSize.height - paddingTop - paddingBottom - sbvsc.topPosInner.absVal - sbvsc.bottomPosInner.absVal; - - - rect_p->size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect_p->size.height sbvSize:rect_p->size selfLayoutSize:selfSize]; - - [self myCalcVertGravity:[self myGetSubviewVertGravity:sbv sbvsc:sbvsc vertGravity:vertGravity] sbv:sbv sbvsc:sbvsc paddingTop:paddingTop paddingBottom:paddingBottom baselinePos:baselinePos selfSize:selfSize pRect:rect_p]; + return [self myCalcSubview:subviewEngine horzGravity:[subviewTraits finalHorzGravityFrom:context->horzGravity] withContext:context]; } --(CGFloat)myCalcSelfSize:(CGFloat)selfSize subviewSize:(CGFloat)subviewSize headPos:(MyLayoutPos*)headPos centerPos:(MyLayoutPos*)centerPos tailPos:(MyLayoutPos*)tailPos -{ - CGFloat totalWeight = 0; - CGFloat tempSize = subviewSize; - CGFloat hm = headPos.posNumVal.doubleValue; - CGFloat cm = centerPos.posNumVal.doubleValue; - CGFloat tm = tailPos.posNumVal.doubleValue; - - //这里是求父视图的最大尺寸,因此如果使用了相对边距的话,最大最小要参与计算。 - if (![self myIsRelativePos:hm]) - tempSize += hm; - else - totalWeight += hm; - - tempSize += headPos.offsetVal; - - - if (![self myIsRelativePos:cm]) - tempSize += cm; - else - totalWeight += cm; - - tempSize += centerPos.offsetVal; - - - if (![self myIsRelativePos:tm]) - tempSize += tm; - else - totalWeight += tm; - - tempSize += tailPos.offsetVal; - - //如果3个比重之和小于等于1则表示按比重分配宽度。 - if (_myCGFloatLessOrEqual(1, totalWeight)) - tempSize = 0; - else - tempSize /=(1 - totalWeight); - - - CGFloat leadingMargin = [self myValidMargin:headPos sbv:headPos.view calcPos:[headPos realPosIn:tempSize] selfLayoutSize:CGSizeZero]; - - CGFloat centerMargin = [self myValidMargin:centerPos sbv:centerPos.view calcPos:[centerPos realPosIn:tempSize] selfLayoutSize:CGSizeZero]; - - CGFloat trailingMargin = [self myValidMargin:tailPos sbv:tailPos.view calcPos:[tailPos realPosIn:tempSize] selfLayoutSize:CGSizeZero]; - - tempSize = subviewSize + leadingMargin + centerMargin + trailingMargin; - if (_myCGFloatGreat(tempSize,selfSize)) - { - selfSize = tempSize; +- (CGFloat)mySubviewEngine:(MyLayoutEngine *)subviewEngine baselinePos:(CGFloat)baselinePos calcTopBottomWithContext:(MyLayoutContext *)context { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + UIView *subview = subviewTraits.view; + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; + if (subviewTraits.topPosInner.val != nil && subviewTraits.bottomPosInner.val != nil) { + if (subviewTraits.heightSizeInner.val == nil) + subviewEngine.height = context->selfSize.height - context->paddingTop - context->paddingBottom - subviewTraits.topPosInner.measure - subviewTraits.bottomPosInner.measure; } - - return selfSize; - -} + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subview calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; --(void)myEqualizeSubviewsForVert:(BOOL)centered withSpace:(CGFloat)margin -{ - - - //如果居中和不居中则拆分出来的片段是不一样的。 - - CGFloat scale; - CGFloat scale2; - NSArray *sbs = [self myGetLayoutSubviews]; - if (margin == CGFLOAT_MAX) - { - CGFloat fragments = centered ? sbs.count * 2 + 1 : sbs.count * 2 - 1; - scale = 1 / fragments; - scale2 = scale; - - } - else - { - scale = 1.0; - scale2 = margin; - } - - - for (UIView *sbv in sbs) - { - UIView *sbvsc = sbv.myCurrentSizeClass; - - [sbvsc.bottomPos __equalTo:@0]; - [sbvsc.topPos __equalTo:@(scale2)]; - sbvsc.weight = scale; - - if (sbv == sbs.firstObject && !centered) - [sbvsc.topPos __equalTo:@0]; - - if (sbv == sbs.lastObject && centered) - [sbvsc.bottomPos __equalTo:@(scale2)]; - } - + return [self myCalcSubview:subviewEngine vertGravity:[subviewTraits finalVertGravityFrom:context->vertGravity] baselinePos:baselinePos withContext:context]; } --(void)myEqualizeSubviewsForHorz:(BOOL)centered withSpace:(CGFloat)space -{ - - - NSArray *sbs = [self myGetLayoutSubviews]; +- (void)myEqualizeSubviewEngines:(NSMutableArray *)subviewEngines orientation:(MyOrientation)orientation centered:(BOOL)centered space:(CGFloat)space { //如果居中和不居中则拆分出来的片段是不一样的。 - CGFloat scale; - CGFloat scale2; - - if (space == CGFLOAT_MAX) - { - CGFloat fragments = centered ? sbs.count * 2 + 1 : sbs.count * 2 - 1; - scale = 1 / fragments; - scale2 = scale; - - } - else - { - scale = 1.0; - scale2 = space; - } - - for (UIView *sbv in sbs) - { - UIView *sbvsc = sbv.myCurrentSizeClass; - - [sbvsc.trailingPos __equalTo:@0]; - [sbvsc.leadingPos __equalTo:@(scale2)]; - sbvsc.weight = scale; - - if (sbv == sbs.firstObject && !centered) - [sbvsc.leadingPos __equalTo:@0]; - - if (sbv == sbs.lastObject && centered) - [sbvsc.trailingPos __equalTo:@(scale2)]; + CGFloat weight; + if (space == CGFLOAT_MAX) { + CGFloat fragments = centered ? subviewEngines.count * 2 + 1 : subviewEngines.count * 2 - 1; + weight = 1 / fragments; + space = weight; + } else { + weight = 1.0; } - -} - - --(void)myEqualizeSubviewsSpaceForVert:(BOOL)centered -{ - - - - //如果居中和不居中则拆分出来的片段是不一样的。 - NSArray *sbs = [self myGetLayoutSubviews]; - CGFloat fragments = centered ? sbs.count + 1 : sbs.count - 1; - CGFloat scale = 1 / fragments; - - for (UIView *sbv in sbs) - { - UIView *sbvsc = sbv.myCurrentSizeClass; - [sbvsc.topPos __equalTo:@(scale)]; + for (MyLayoutEngine *subviewEngine in subviewEngines) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + if (orientation == MyOrientation_Vert) { + [subviewTraits.topPos _myEqualTo:@(space)]; + [subviewTraits.bottomPos _myEqualTo:@0]; + } else { + [subviewTraits.leadingPos _myEqualTo:@(space)]; + [subviewTraits.trailingPos _myEqualTo:@0]; + } - if (sbv == sbs.firstObject && !centered) - [sbvsc.topPos __equalTo:@0]; + subviewTraits.weight = weight; - if (sbv == sbs.lastObject) - [sbvsc.bottomPos __equalTo: centered? @(scale) : @0]; + if (subviewEngine == subviewEngines.firstObject && !centered) { + if (orientation == MyOrientation_Vert) { + [subviewTraits.topPos _myEqualTo:@0]; + } else { + [subviewTraits.leadingPos _myEqualTo:@0]; + } + } + if (subviewEngine == subviewEngines.lastObject && centered) { + if (orientation == MyOrientation_Vert) { + [subviewTraits.bottomPos _myEqualTo:@(space)]; + } else { + [subviewTraits.trailingPos _myEqualTo:@(space)]; + } + } } - - } --(void)myEqualizeSubviewsSpaceForHorz:(BOOL)centered -{ - +- (void)myEqualizeSubviewEngines:(NSArray *)subviewEngines orientation:(MyOrientation)orientation centered:(BOOL)centered { //如果居中和不居中则拆分出来的片段是不一样的。 - NSArray *sbs = [self myGetLayoutSubviews]; - CGFloat fragments = centered ? sbs.count + 1 : sbs.count - 1; - CGFloat scale = 1 / fragments; - - for (UIView *sbv in sbs) - { - UIView *sbvsc = sbv.myCurrentSizeClass; - - [sbvsc.leadingPos __equalTo:@(scale)]; - - if (sbv == sbs.firstObject && !centered) - [sbvsc.leadingPos __equalTo:@0]; - - if (sbv == sbs.lastObject) - [sbvsc.trailingPos __equalTo:centered? @(scale) : @0]; + CGFloat fragments = centered ? subviewEngines.count + 1 : subviewEngines.count - 1; + CGFloat space = 1 / fragments; + for (MyLayoutEngine *subviewEngine in subviewEngines) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + if (orientation == MyOrientation_Vert) { + [subviewTraits.topPos _myEqualTo:@(space)]; + } else { + [subviewTraits.leadingPos _myEqualTo:@(space)]; + } + if (subviewEngine == subviewEngines.firstObject && !centered) { + if (orientation == MyOrientation_Vert) { + [subviewTraits.topPos _myEqualTo:@0]; + } else { + [subviewTraits.leadingPos _myEqualTo:@0]; + } + } + if (subviewEngine == subviewEngines.lastObject) { + if (orientation == MyOrientation_Vert) { + [subviewTraits.bottomPos _myEqualTo:centered ? @(space) : @0]; + } else { + [subviewTraits.trailingPos _myEqualTo:centered ? @(space) : @0]; + } + } } } - - - - - @end diff --git a/MyLayout/Lib/MyMaker.h b/MyLayout/Lib/MyMaker.h index 8903506..4df7756 100644 --- a/MyLayout/Lib/MyMaker.h +++ b/MyLayout/Lib/MyMaker.h @@ -15,102 +15,94 @@ */ @interface MyMaker : NSObject --(MyMaker*)top; --(MyMaker*)left; --(MyMaker*)bottom; --(MyMaker*)right; --(MyMaker*)margin; +- (MyMaker *)top; +- (MyMaker *)left; +- (MyMaker *)bottom; +- (MyMaker *)right; +- (MyMaker *)margin; --(MyMaker*)leading; --(MyMaker*)trailing; +- (MyMaker *)leading; +- (MyMaker *)trailing; +- (MyMaker *)wrapContentHeight; +- (MyMaker *)wrapContentWidth; --(MyMaker*)wrapContentHeight; --(MyMaker*)wrapContentWidth; +- (MyMaker *)height; +- (MyMaker *)width; +- (MyMaker *)useFrame; +- (MyMaker *)noLayout; --(MyMaker*)height; --(MyMaker*)width; --(MyMaker*)useFrame; --(MyMaker*)noLayout; +- (MyMaker *)centerX; +- (MyMaker *)centerY; +- (MyMaker *)center; +- (MyMaker *)baseline; --(MyMaker*)centerX; --(MyMaker*)centerY; --(MyMaker*)center; --(MyMaker*)baseline; - --(MyMaker*)visibility; --(MyMaker*)alignment; - --(MyMaker*)sizeToFit; +- (MyMaker *)visibility; +- (MyMaker *)alignment; +- (MyMaker *)sizeToFit; //布局独有 --(MyMaker*)topPadding; --(MyMaker*)leftPadding; --(MyMaker*)bottomPadding; --(MyMaker*)rightPadding; --(MyMaker*)leadingPadding; --(MyMaker*)trailingPadding; --(MyMaker*)padding; --(MyMaker*)zeroPadding; --(MyMaker*)reverseLayout; --(MyMaker*)vertSpace; --(MyMaker*)horzSpace; --(MyMaker*)space; - - +- (MyMaker *)paddingTop; +- (MyMaker *)paddingLeft; +- (MyMaker *)paddingBottom; +- (MyMaker *)paddingRight; +- (MyMaker *)paddingLeading; +- (MyMaker *)paddingTrailing; +- (MyMaker *)padding; +- (MyMaker *)zeroPadding; +- (MyMaker *)reverseLayout; +- (MyMaker *)vertSpace; +- (MyMaker *)horzSpace; +- (MyMaker *)space; //线性布局和流式布局独有 --(MyMaker*)orientation; --(MyMaker*)gravity; +- (MyMaker *)orientation; +- (MyMaker *)gravity; //线性布局独有 --(MyMaker*)shrinkType; +- (MyMaker *)shrinkType; //流式布局独有 --(MyMaker*)arrangedCount; --(MyMaker*)autoArrange; --(MyMaker*)arrangedGravity; --(MyMaker*)pagedCount; - +- (MyMaker *)arrangedCount; +- (MyMaker *)autoArrange; +- (MyMaker *)arrangedGravity; +- (MyMaker *)pagedCount; //线性布局和浮动布局和流式布局子视图独有 --(MyMaker*)weight; +- (MyMaker *)weight; //浮动布局子视图独有 --(MyMaker*)reverseFloat; --(MyMaker*)clearFloat; - - -//浮动布局独有。 --(MyMaker*)noBoundaryLimit; +- (MyMaker *)reverseFloat; +- (MyMaker *)clearFloat; //赋值操支持NSNumber,UIView,MyLayoutPos,MyLayoutSize, NSArray[MyLayoutSize] --(MyMaker* (^)(id val))equalTo; --(MyMaker* (^)(id val))min; --(MyMaker* (^)(id val))max; - --(MyMaker* (^)(CGFloat val))offset; --(MyMaker* (^)(CGFloat val))multiply; --(MyMaker* (^)(CGFloat val))add; - - +- (MyMaker * (^)(id val))equalTo; +- (MyMaker * (^)(id val))min; +- (MyMaker * (^)(id val))max; +- (MyMaker * (^)(CGFloat val))offset; +- (MyMaker * (^)(CGFloat val))multiply; +- (MyMaker * (^)(CGFloat val))add; @end - -@interface UIView(MyMakerExt) +@interface UIView (MyMakerExt) //对视图进行统一的布局,方便操作,请参考DEMO1中的使用方法。 --(void)makeLayout:(void(^)(MyMaker *make))layoutMaker; +- (void)makeLayout:(void (^)(MyMaker *make))layoutMaker; //布局内所有子视图的布局构造,会影响到有所的子视图。 --(void)allSubviewMakeLayout:(void(^)(MyMaker *make))layoutMaker; - +- (void)allSubviewMakeLayout:(void (^)(MyMaker *make))layoutMaker; @end +//@interface MyMaker(MyDeprecated) +//- (MyMaker *)topPadding MYDEPRECATED("use paddingTop instead"); +//- (MyMaker *)leftPadding MYDEPRECATED("use paddingTop instead"); +//- (MyMaker *)bottomPadding MYDEPRECATED("use paddingTop instead"); +//- (MyMaker *)rightPadding MYDEPRECATED("use paddingTop instead"); +//- (MyMaker *)leadingPadding MYDEPRECATED("use paddingTop instead"); +//- (MyMaker *)trailingPadding MYDEPRECATED("use paddingTop instead"); +//@end #endif - - diff --git a/MyLayout/Lib/MyMaker.m b/MyLayout/Lib/MyMaker.m index 6a951f2..0982111 100644 --- a/MyLayout/Lib/MyMaker.m +++ b/MyLayout/Lib/MyMaker.m @@ -11,509 +11,363 @@ #if TARGET_OS_IPHONE #import "MyLayoutPos.h" -#import "MyLayoutSize.h" #import "MyLayoutPosInner.h" +#import "MyLayoutSize.h" #import "MyLayoutSizeInner.h" -@implementation MyMaker -{ +@implementation MyMaker { NSArray *_myViews; NSMutableArray *_keys; - BOOL _clear; + BOOL _clear; } --(id)initWithView:(NSArray *)v -{ +- (id)initWithView:(NSArray *)v { self = [self init]; - if (self != nil) - { + if (self != nil) { _myViews = v; _keys = [[NSMutableArray alloc] init]; _clear = NO; } - return self; } --(MyMaker*)addMethod:(NSString*)method -{ - if (_clear) +- (MyMaker *)addMethod:(NSString *)method { + if (_clear) { [_keys removeAllObjects]; + } _clear = NO; - + [_keys addObject:method]; return self; } - - --(MyMaker*)top -{ +- (MyMaker *)top { return [self addMethod:@"topPos"]; } --(MyMaker*)left -{ +- (MyMaker *)left { return [self addMethod:@"leftPos"]; } --(MyMaker*)bottom -{ +- (MyMaker *)bottom { return [self addMethod:@"bottomPos"]; } --(MyMaker*)right -{ +- (MyMaker *)right { return [self addMethod:@"rightPos"]; } --(MyMaker*)margin -{ +- (MyMaker *)margin { [self top]; [self left]; [self right]; - return [self bottom]; + return [self bottom]; } --(MyMaker*)leading -{ +- (MyMaker *)leading { return [self addMethod:@"leadingPos"]; - } --(MyMaker*)trailing -{ +- (MyMaker *)trailing { return [self addMethod:@"trailingPos"]; - } - --(MyMaker*)height -{ +- (MyMaker *)height { return [self addMethod:@"heightSize"]; } --(MyMaker*)width -{ +- (MyMaker *)width { return [self addMethod:@"widthSize"]; } --(MyMaker*)useFrame -{ +- (MyMaker *)useFrame { return [self addMethod:@"useFrame"]; } --(MyMaker*)noLayout -{ +- (MyMaker *)noLayout { return [self addMethod:@"noLayout"]; - } - --(MyMaker*)wrapContentHeight -{ +- (MyMaker *)wrapContentHeight { return [self addMethod:@"wrapContentHeight"]; } --(MyMaker*)wrapContentWidth -{ +- (MyMaker *)wrapContentWidth { return [self addMethod:@"wrapContentWidth"]; } --(MyMaker*)reverseLayout -{ +- (MyMaker *)reverseLayout { return [self addMethod:@"reverseLayout"]; } - - --(MyMaker*)weight -{ +- (MyMaker *)weight { return [self addMethod:@"weight"]; - } --(MyMaker*)reverseFloat -{ +- (MyMaker *)reverseFloat { return [self addMethod:@"reverseFloat"]; } --(MyMaker*)clearFloat -{ +- (MyMaker *)clearFloat { return [self addMethod:@"clearFloat"]; } --(MyMaker*)noBoundaryLimit -{ - return [self addMethod:@"noBoundaryLimit"]; +- (MyMaker *)paddingTop { + return [self addMethod:@"paddingTop"]; } --(MyMaker*)topPadding -{ - - return [self addMethod:@"topPadding"]; - +- (MyMaker *)paddingLeft { + return [self addMethod:@"paddingLeft"]; } --(MyMaker*)leftPadding -{ - return [self addMethod:@"leftPadding"]; - +- (MyMaker *)paddingBottom { + return [self addMethod:@"paddingBottom"]; } --(MyMaker*)bottomPadding -{ - - return [self addMethod:@"bottomPadding"]; - +- (MyMaker *)paddingRight { + return [self addMethod:@"paddingRight"]; } --(MyMaker*)rightPadding -{ - return [self addMethod:@"rightPadding"]; - +- (MyMaker *)paddingLeading { + return [self addMethod:@"paddingLeading"]; } --(MyMaker*)leadingPadding -{ - - return [self addMethod:@"leadingPadding"]; - +- (MyMaker *)paddingTrailing { + return [self addMethod:@"paddingTrailing"]; } --(MyMaker*)trailingPadding -{ - return [self addMethod:@"trailingPadding"]; - +- (MyMaker *)padding { + [self addMethod:@"paddingTop"]; + [self addMethod:@"paddingLeft"]; + [self addMethod:@"paddingBottom"]; + return [self addMethod:@"paddingRight"]; } - --(MyMaker*)padding -{ - [self addMethod:@"topPadding"]; - [self addMethod:@"leftPadding"]; - [self addMethod:@"bottomPadding"]; - return [self addMethod:@"rightPadding"]; -} - --(MyMaker*)zeroPadding -{ +- (MyMaker *)zeroPadding { return [self addMethod:@"zeroPadding"]; } --(MyMaker*)orientation -{ +- (MyMaker *)orientation { return [self addMethod:@"orientation"]; - } --(MyMaker*)gravity -{ +- (MyMaker *)gravity { return [self addMethod:@"gravity"]; - } - --(MyMaker*)centerX -{ - return [self addMethod:@"centerXPos"]; +- (MyMaker *)centerX { + return [self addMethod:@"centerXPos"]; } --(MyMaker*)centerY -{ +- (MyMaker *)centerY { return [self addMethod:@"centerYPos"]; } --(MyMaker*)center -{ +- (MyMaker *)center { [self addMethod:@"centerXPos"]; return [self addMethod:@"centerYPos"]; } --(MyMaker*)baseline -{ +- (MyMaker *)baseline { return [self addMethod:@"baselinePos"]; } - --(MyMaker*)visibility -{ - return [self addMethod:@"myVisibility"]; +- (MyMaker *)visibility { + return [self addMethod:@"visibility"]; } --(MyMaker*)alignment -{ - return [self addMethod:@"myAlignment"]; +- (MyMaker *)alignment { + return [self addMethod:@"alignment"]; } - - --(MyMaker*)sizeToFit -{ - for (UIView *myView in _myViews) - { +- (MyMaker *)sizeToFit { + for (UIView *myView in _myViews) { [myView sizeToFit]; } - + return self; } - - --(MyMaker*)space -{ +- (MyMaker *)space { return [self addMethod:@"subviewSpace"]; - } --(MyMaker*)shrinkType -{ +- (MyMaker *)shrinkType { return [self addMethod:@"shrinkType"]; - } - --(MyMaker*)arrangedCount -{ +- (MyMaker *)arrangedCount { return [self addMethod:@"arrangedCount"]; } --(MyMaker*)autoArrange -{ +- (MyMaker *)autoArrange { return [self addMethod:@"autoArrange"]; } --(MyMaker*)arrangedGravity -{ +- (MyMaker *)arrangedGravity { return [self addMethod:@"arrangedGravity"]; - } --(MyMaker*)vertSpace -{ +- (MyMaker *)vertSpace { return [self addMethod:@"subviewVSpace"]; - } --(MyMaker*)horzSpace -{ +- (MyMaker *)horzSpace { return [self addMethod:@"subviewHSpace"]; - } --(MyMaker*)pagedCount -{ +- (MyMaker *)pagedCount { return [self addMethod:@"pagedCount"]; - } - - - --(MyMaker* (^)(id val))equalTo -{ +- (MyMaker * (^)(id val))equalTo { _clear = YES; return ^id(id val) { - - for (NSString *key in _keys) - { - - for (UIView * myView in _myViews) - { - if ([val isKindOfClass:[NSNumber class]]) - { + for (NSString *key in self->_keys) { + for (UIView *myView in self->_myViews) { + if ([val isKindOfClass:[NSNumber class]]) { id oldVal = [myView valueForKey:key]; - if ([oldVal isKindOfClass:[MyLayoutPos class]]) - { - [((MyLayoutPos*)oldVal) __equalTo:val]; - } - else if ([oldVal isKindOfClass:[MyLayoutSize class]]) - { - [((MyLayoutSize*)oldVal) __equalTo:val]; - } - else + if ([oldVal isKindOfClass:[MyLayoutPos class]]) { + ((MyLayoutPos *)oldVal).myEqualTo(val); + } else if ([oldVal isKindOfClass:[MyLayoutSize class]]) { + ((MyLayoutSize *)oldVal).myEqualTo(val); + } else { [myView setValue:val forKey:key]; - } - else if ([val isKindOfClass:[MyLayoutPos class]]) - { - [((MyLayoutPos*)[myView valueForKey:key]) __equalTo:val]; - } - else if ([val isKindOfClass:[MyLayoutSize class]]) - { - [((MyLayoutSize*)[myView valueForKey:key]) __equalTo:val]; - } - else if ([val isKindOfClass:[NSArray class]]) - { - [((MyLayoutSize*)[myView valueForKey:key]) __equalTo:val]; - } - else if ([val isKindOfClass:[UIView class]]) - { - id oldVal = [val valueForKey:key]; - if ([oldVal isKindOfClass:[MyLayoutPos class]]) - { - [((MyLayoutPos*)[myView valueForKey:key]) __equalTo:oldVal]; - } - else if ([oldVal isKindOfClass:[MyLayoutSize class]]) - { - [((MyLayoutSize*)[myView valueForKey:key]) __equalTo:oldVal]; - } - else - { + } else if ([val isKindOfClass:[MyLayoutPos class]]) { + ((MyLayoutPos *)[myView valueForKey:key]).myEqualTo(val); + } else if ([val isKindOfClass:[MyLayoutSize class]]) { + ((MyLayoutSize *)[myView valueForKey:key]).myEqualTo(val); + } else if ([val isKindOfClass:[NSArray class]]) { + ((MyLayoutSize *)[myView valueForKey:key]).myEqualTo(val); + } else if ([val isKindOfClass:[UIView class]]) { + id oldVal = [val valueForKey:key]; + if ([oldVal isKindOfClass:[MyLayoutPos class]]) { + ((MyLayoutPos *)[myView valueForKey:key]).myEqualTo(oldVal); + } else if ([oldVal isKindOfClass:[MyLayoutSize class]]) { + ((MyLayoutSize *)[myView valueForKey:key]).myEqualTo(oldVal); + } else { [myView setValue:oldVal forKey:key]; } } } - } - return self; }; } --(MyMaker* (^)(CGFloat val))offset -{ +- (MyMaker * (^)(CGFloat val))offset { _clear = YES; - return ^id(CGFloat val) { - - for (NSString *key in _keys) - { - for (UIView *myView in _myViews) - { - - [((MyLayoutPos*)[myView valueForKey:key]) __offset:val]; + for (NSString *key in self->_keys) { + for (UIView *myView in self->_myViews) { + [((MyLayoutPos *)[myView valueForKey:key]) _myOffset:val]; } } - return self; }; } --(MyMaker* (^)(CGFloat val))multiply -{ +- (MyMaker * (^)(CGFloat val))multiply { _clear = YES; return ^id(CGFloat val) { - - for (NSString *key in _keys) - { - for (UIView *myView in _myViews) - { - - [((MyLayoutSize*)[myView valueForKey:key]) __multiply:val]; + for (NSString *key in self->_keys) { + for (UIView *myView in self->_myViews) { + [((MyLayoutSize *)[myView valueForKey:key]) _myMultiply:val]; } } return self; }; - } --(MyMaker* (^)(CGFloat val))add -{ +- (MyMaker * (^)(CGFloat val))add { _clear = YES; return ^id(CGFloat val) { - - for (NSString *key in _keys) - { - - for (UIView *myView in _myViews) - { - - [((MyLayoutSize*)[myView valueForKey:key]) __add:val]; + for (NSString *key in self->_keys) { + for (UIView *myView in self->_myViews) { + [((MyLayoutSize *)[myView valueForKey:key]) _myAdd:val]; } } return self; }; - } --(MyMaker* (^)(id val))min -{ +- (MyMaker * (^)(id val))min { _clear = YES; return ^id(id val) { - - for (NSString *key in _keys) - { - - for (UIView *myView in _myViews) - { - - + for (NSString *key in self->_keys) { + for (UIView *myView in self->_myViews) { id val2 = val; - if ([val isKindOfClass:[UIView class]]) + if ([val isKindOfClass:[UIView class]]) { val2 = [val valueForKey:key]; - - id oldVal = [myView valueForKey:key]; - if ([oldVal isKindOfClass:[MyLayoutPos class]]) - { - [((MyLayoutPos*)oldVal) __lBound:val2 offsetVal:0]; } - else if ([oldVal isKindOfClass:[MyLayoutSize class]]) - { - [((MyLayoutSize*)oldVal) __lBound:val2 addVal:0 multiVal:1]; + id oldVal = [myView valueForKey:key]; + if ([oldVal isKindOfClass:[MyLayoutPos class]]) { + [((MyLayoutPos *)oldVal) _myLBound:val2 offsetVal:0]; + } else if ([oldVal isKindOfClass:[MyLayoutSize class]]) { + [((MyLayoutSize *)oldVal) _myLBound:val2 addVal:0 multiVal:1]; } - else - ; } } return self; }; - } --(MyMaker* (^)(id val))max -{ +- (MyMaker * (^)(id val))max { _clear = YES; return ^id(id val) { - - for (NSString *key in _keys) - { - for (UIView *myView in _myViews) - { + for (NSString *key in self->_keys) { + for (UIView *myView in self->_myViews) { id val2 = val; - if ([val isKindOfClass:[UIView class]]) + if ([val isKindOfClass:[UIView class]]) { val2 = [val valueForKey:key]; - - id oldVal = [myView valueForKey:key]; - if ([oldVal isKindOfClass:[MyLayoutPos class]]) - { - [((MyLayoutPos*)oldVal) __uBound:val2 offsetVal:0]; } - else if ([oldVal isKindOfClass:[MyLayoutSize class]]) - { - [((MyLayoutSize*)oldVal) __uBound:val2 addVal:0 multiVal:1]; + id oldVal = [myView valueForKey:key]; + if ([oldVal isKindOfClass:[MyLayoutPos class]]) { + [((MyLayoutPos *)oldVal) _myUBound:val2 offsetVal:0]; + } else if ([oldVal isKindOfClass:[MyLayoutSize class]]) { + [((MyLayoutSize *)oldVal) _myUBound:val2 addVal:0 multiVal:1]; } - else - ; } } return self; }; - } - +#pragma mark -- Dreprecated methods +// +//- (MyMaker *)topPadding { +// return self.paddingTop; +//} +//- (MyMaker *)leftPadding { +// return self.paddingLeft; +//} +//- (MyMaker *)bottomPadding { +// return self.paddingBottom; +//} +//- (MyMaker *)rightPadding { +// return self.paddingRight; +//} +//- (MyMaker *)leadingPadding { +// return self.paddingLeading; +//} +//- (MyMaker *)trailingPadding { +// return self.paddingTrailing; +//} @end +@implementation UIView (MyMakerExt) -@implementation UIView(MyMakerExt) - - --(void)makeLayout:(void(^)(MyMaker *make))layoutMaker -{ - +- (void)makeLayout:(void (^)(MyMaker *make))layoutMaker { MyMaker *myMaker = [[MyMaker alloc] initWithView:@[self]]; layoutMaker(myMaker); } --(void)allSubviewMakeLayout:(void(^)(MyMaker *make))layoutMaker -{ +- (void)allSubviewMakeLayout:(void (^)(MyMaker *make))layoutMaker { MyMaker *myMaker = [[MyMaker alloc] initWithView:self.subviews]; layoutMaker(myMaker); } - @end #endif - diff --git a/MyLayout/Lib/MyPathLayout.h b/MyLayout/Lib/MyPathLayout.h index 9588a13..ec25d02 100644 --- a/MyLayout/Lib/MyPathLayout.h +++ b/MyLayout/Lib/MyPathLayout.h @@ -8,75 +8,66 @@ #import "MyBaseLayout.h" - /** *定义子视图在路径布局中的距离的类型。 */ typedef enum : NSUInteger { - MyPathSpace_Flexed, //浮动距离,子视图之间的距离根据路径布局的尺寸和子视图的数量而确定。 - MyPathSpace_Fixed, //固定距离,就是子视图之间的距离是固定的某个数值。 - MyPathSpace_Count, //固定数量距离,就是子视图之间的距离根据路径布局的尺寸和某个具体的数量而确定。 + MyPathSpace_Flexed, //浮动距离,子视图之间的距离根据路径布局的尺寸和子视图的数量而确定。 + MyPathSpace_Fixed, //固定距离,就是子视图之间的距离是固定的某个数值。 + MyPathSpace_Count, //固定数量距离,就是子视图之间的距离根据路径布局的尺寸和某个具体的数量而确定。 } MyPathSpaceType; - - /** *子视图之间的路径距离类,描述子视图在路径上的间隔距离的类型。 */ @interface MyPathSpace : NSObject /**浮动距离,根据布局视图的尺寸和子视图的数量动态决定*/ -+(id)flexed; ++ (MyPathSpace *)flexed; /**固定距离,len为长度,每个子视图之间的距离都是len*/ -+(id)fixed:(CGFloat)len; ++ (MyPathSpace *)fixed:(CGFloat)len; /**数量距离,根据布局视图的尺寸和指定的数量count动态决定。*/ -+(id)count:(NSInteger)count; - ++ (MyPathSpace *)count:(NSInteger)count; /**距离类型。*/ -@property(nonatomic, assign, readonly) MyPathSpaceType type; +@property (nonatomic, assign, readonly) MyPathSpaceType type; /**距离的值。*/ -@property(nonatomic, assign, readonly) CGFloat value; +@property (nonatomic, assign, readonly) CGFloat value; @end - /** * 坐标轴设置类,用来描述坐标轴的信息。一个坐标轴具有原点、坐标系类型、开始和结束点、坐标轴对应的值这四个方面的内容。 */ @interface MyCoordinateSetting : NSObject - /** *坐标原点的位置,位置是相对位置,默认是(0,0), 假如设置为(0.5,0.5)则在视图的中间。 */ -@property(nonatomic, assign) CGPoint origin; +@property (nonatomic, assign) CGPoint origin; /** * 指定是否是数学坐标系,默认为NO,表示绘图坐标系。 数学坐标系y轴向上为正,向下为负;绘图坐标系则反之。 */ -@property(nonatomic, assign) BOOL isMath; +@property (nonatomic, assign) BOOL isMath; /** *指定是否是y轴和x轴互换,默认为NO,如果设置为YES则方程提供的变量是y轴的值,方程返回的是x轴的值。 */ -@property(nonatomic, assign) BOOL isReverse; - +@property (nonatomic, assign) BOOL isReverse; /**开始位置。如果不设置则根据坐标原点设置以及视图的尺寸自动确定.默认是-CGFLOAT_MAX*/ -@property(nonatomic, assign) CGFloat start; +@property (nonatomic, assign) CGFloat start; /**结束位置。如果不设置则根据坐标原点设置以及视图的尺寸自动确定.默认是CGFLOAT_MAX*/ -@property(nonatomic, assign) CGFloat end; +@property (nonatomic, assign) CGFloat end; -/**恢复默认设置。*/ --(void)reset; +/**将上面的属性恢复默认设置。*/ +- (void)reset; @end - - /** 路径布局类。路径布局通过坐标轴的设置,曲线路径函数方程,子视图之间的距离三个要素来确定其中子视图的位置。因此通过路径布局可以实现一些非常酷炫的布局效果。 @@ -94,75 +85,65 @@ typedef enum : NSUInteger { */ @interface MyPathLayout : MyBaseLayout - /** **下面部分是路径布局的三要素:坐标、函数、距离的定义 **/ - /** * 坐标系设置,您可以调整坐标系的各种参数来完成下列两个方法中的坐标到绘制的映射转换。 */ -@property(nonatomic, strong, readonly) MyCoordinateSetting *coordinateSetting; - +@property (nonatomic, strong, readonly) MyCoordinateSetting *coordinateSetting; /** * 直角坐标普通方程,x是坐标系里面x轴的位置,返回y = f(x)。要求函数在定义域内是连续的,否则结果不确定。如果返回的y无效则函数要返回NAN */ -@property(nonatomic, copy) CGFloat (^rectangularEquation)(CGFloat x); - +@property (nonatomic, copy) CGFloat (^rectangularEquation)(CGFloat x); /** *直角坐标参数方程,t是参数, 返回CGPoint是x轴和y轴的值。要求函数在定义域内是连续的,否则结果不确定。如果返回的点无效,则请返回CGPointMake(NAN,NAN) */ -@property(nonatomic, copy) CGPoint (^parametricEquation)(CGFloat t); +@property (nonatomic, copy) CGPoint (^parametricEquation)(CGFloat t); /** *极坐标方程,angle是极坐标的弧度,返回r半径。要求函数在定义域内是连续的,否则结果不确定。如果返回的点无效,则请返回NAN */ -@property(nonatomic, copy) CGFloat (^polarEquation)(CGFloat angle); - +@property (nonatomic, copy) CGFloat (^polarEquation)(CGFloat angle); /** *设置子视图在路径曲线上的距离的类型,一共有Flexed, Fixed, MaxCount,默认是Flexed, */ -@property(nonatomic, strong) MyPathSpace *spaceType; - +@property (nonatomic, strong) MyPathSpace *spaceType; /** *设置和获取布局视图中的原点视图,默认是nil。如果设置了原点视图则总会将原点视图作为布局视图中的最后一个子视图。原点视图将会显示在路径的坐标原点中心上,因此原点布局是不会参与在路径中的布局的。因为中心原点视图是布局视图中的最后一个子视图,而MyPathLayout重写了AddSubview方法,因此可以正常的使用这个方法来添加子视图。 */ -@property(nonatomic, strong) UIView *originView; - +@property (nonatomic, strong) UIView *originView; /** *返回布局视图中所有在曲线路径中排列的子视图。如果设置了原点视图则返回subviews里面除最后一个子视图外的所有子视图,如果没有原点子视图则返回subviews */ -@property(nonatomic, strong,readonly) NSArray *pathSubviews; - +@property (nonatomic, strong, readonly) NSArray *pathSubviews; /** 得到子视图在曲线路径中定位时的函数的自变量的值。也就是说在函数中当值等于下面的返回值时,这个视图的位置就被确定了。方法如果返回NAN则表示这个子视图没有定位。 @param subview 指定的子视图 @return 返回指定子视图在曲线路径中的自变量值 */ --(CGFloat)argumentFrom:(UIView*)subview; - +- (CGFloat)argumentFrom:(UIView *)subview; /** 下面三个函数用来获取两个子视图之间的曲线路径数据,在调用getSubviewPathPoint方法之前请先调用beginSubviewPathPoint方法,而调用完毕后请调用endSubviewPathPoint方法,否则getSubviewPathPoint返回的结果未可知。 */ - /** 开始获取子视图路径数据的方法 @param full 表示getSubviewPathPoint获取的是否是全部路径点。如果为NO则只会获取子视图的位置的点 */ --(void)beginSubviewPathPoint:(BOOL)full; +- (void)beginSubviewPathPoint:(BOOL)full; /** 结束获取子视图路径数据的方法 */ --(void)endSubviewPathPoint; +- (void)endSubviewPathPoint; /** 创建从某个子视图到另外一个子视图之间的路径点,返回NSValue数组,里面的值是CGPoint。 @@ -170,20 +151,18 @@ typedef enum : NSUInteger { @param toIndex 指定结束的子视图的索引位置。如果有原点子视图时,这两个索引值不能算上原点子视图的索引值。 @return 返回fromIndex到toIndex之间的所有曲线路径点数组 */ --(NSArray*)getSubviewPathPoint:(NSInteger)fromIndex toIndex:(NSInteger)toIndex; +- (NSArray *)getSubviewPathPoint:(NSInteger)fromIndex toIndex:(NSInteger)toIndex; /** 创建布局的曲线的路径。用户需要负责销毁返回的值。调用者可以用这个方法来获得曲线的路径,进行一些绘制的工作。 @param subviewCount 指定这个路径上子视图的数量的个数,如果设置为-1则是按照布局视图的子视图的数量来创建。需要注意的是如果布局视图的spaceType为Flexed,Count的话则这个参数设置无效。 @return 返回指定数量的子视图的曲线路径,用户需要负责销毁返回的对象。 */ --(CGPathRef)createPath:(NSInteger)subviewCount; - +- (CGPathRef)createPath:(NSInteger)subviewCount; /** 设置获取子视图距离的误差值。默认是0.5,误差越小则距离的精确值越大,误差最低值不能<=0。一般不需要调整这个值,只有那些要求精度非常高的场景才需要微调这个值,比如在一些曲线路径较短的情况下,通过调小这个值来子视图之间间距的精确计算。 */ -@property(nonatomic, assign) CGFloat distanceError; - +@property (nonatomic, assign) CGFloat distanceError; @end diff --git a/MyLayout/Lib/MyPathLayout.m b/MyLayout/Lib/MyPathLayout.m index 57963a2..425b578 100644 --- a/MyLayout/Lib/MyPathLayout.m +++ b/MyLayout/Lib/MyPathLayout.m @@ -9,141 +9,124 @@ #import "MyPathLayout.h" #import "MyLayoutInner.h" - @implementation MyPathSpace --(id)initWithType:(MyPathSpaceType)type value:(CGFloat)value -{ +- (MyPathSpace *)initWithSpaceType:(MyPathSpaceType)type value:(CGFloat)value { self = [super init]; - if (self != nil) - { + if (self != nil) { _type = type; _value = value; } - return self; } //浮动距离,根据布局视图的尺寸和子视图的数量动态决定 -+(id)flexed -{ - return [[self alloc] initWithType:MyPathSpace_Flexed value:0]; ++ (MyPathSpace *)flexed { + return [[self alloc] initWithSpaceType:MyPathSpace_Flexed value:0]; } //固定距离,len为长度,每个子视图之间的距离都是len -+(id)fixed:(CGFloat)len -{ - return [[self alloc] initWithType:MyPathSpace_Fixed value:len]; ++ (MyPathSpace *)fixed:(CGFloat)len { + return [[self alloc] initWithSpaceType:MyPathSpace_Fixed value:len]; } //数量距离,根据布局视图的尺寸和指定的数量动态决定。 -+(id)count:(NSInteger)count -{ - return [[self alloc] initWithType:MyPathSpace_Count value:count]; ++ (MyPathSpace *)count:(NSInteger)count { + return [[self alloc] initWithSpaceType:MyPathSpace_Count value:count]; } - - @end +@interface MyCoordinateSetting() +@property (nonatomic, weak) MyPathLayout *pathLayout; +@end @implementation MyCoordinateSetting -{ - __weak MyPathLayout *_pathLayout; -} --(id)initWithPathLayout:(MyPathLayout*)pathLayout -{ +- (MyCoordinateSetting *)initWithPathLayout:(MyPathLayout *)pathLayout { self = [super init]; - if (self != nil) - { + if (self != nil) { _pathLayout = pathLayout; _start = -CGFLOAT_MAX; - _end = CGFLOAT_MAX; + _end = CGFLOAT_MAX; } - return self; } --(void)setIsMath:(BOOL)isMath -{ - if (_isMath != isMath) - { +- (void)setIsMath:(BOOL)isMath { + if (_isMath != isMath) { _isMath = isMath; - [_pathLayout setNeedsLayout]; + [self.pathLayout setNeedsLayout]; } - } --(void)setIsReverse:(BOOL)isReverse -{ - if (_isReverse != isReverse) - { +- (void)setIsReverse:(BOOL)isReverse { + if (_isReverse != isReverse) { _isReverse = isReverse; - [_pathLayout setNeedsLayout]; + [self.pathLayout setNeedsLayout]; } } --(void)setOrigin:(CGPoint)origin -{ - if (!_myCGPointEqual(_origin, origin)) - { +- (void)setOrigin:(CGPoint)origin { + if (!_myCGPointEqual(_origin, origin)) { _origin = origin; - [_pathLayout setNeedsLayout]; + [self.pathLayout setNeedsLayout]; } } --(void)setStart:(CGFloat)start -{ - if (_start != start) - { +- (void)setStart:(CGFloat)start { + if (_start != start) { _start = start; - [_pathLayout setNeedsLayout]; + [self.pathLayout setNeedsLayout]; } - - } --(void)setEnd:(CGFloat)end -{ - if (_end != end) - { +- (void)setEnd:(CGFloat)end { + if (_end != end) { _end = end; - [_pathLayout setNeedsLayout]; - + [self.pathLayout setNeedsLayout]; } } --(void)reset -{ +- (void)reset { _start = -CGFLOAT_MAX; - _end = CGFLOAT_MAX; + _end = CGFLOAT_MAX; _isMath = NO; _isReverse = NO; _origin = CGPointZero; - [_pathLayout setNeedsLayout]; + [self.pathLayout setNeedsLayout]; } @end +@interface MyPathLayout () +/** + 记录路径内所有算出来的点,内容为NSValue(CGPoint) + */ +@property (nonatomic, strong) NSArray *pathPoints; -@interface MyPathLayout() +/** + 记录每个子视图在pathPoints中的索引位置。内容为NSNumber。比如pathPoints有1000个点, + 一共有10个子视图那么pointIndexs的长度就是10,每个元素就是在subviewPoints里面的索引。 + */ +@property (nonatomic, strong) NSArray *pointIndexs; -@property(nonatomic, strong) NSArray *pathPoints; //记录路径内所有算出来的点,内容为NSValue(CGPoint) -@property(nonatomic, strong) NSArray *pointIndexs; //记录每个子视图在pathPoints中的索引位置。内容为NSNumber。比如pathPoints有1000个点,一共有10个子视图那么pointIndexs的长度就是10,每个元素就是在subviewPoints里面的索引。 -@property(nonatomic, strong) NSMutableArray *argumentArray; //记录每个子视图在路径曲线函数中的变量值的数组,数组的内容为NSValue。 +/** + 记录每个子视图在路径曲线函数中的变量值的数组,数组的内容为NSValue。 + */ +@property (nonatomic, strong) NSMutableArray *argumentArray; -@end +@property (nonatomic, assign) BOOL hasOriginView; +@property (nonatomic, strong) MyCoordinateSetting *coordinateSetting; +@end @implementation MyPathLayout -{ - MyCoordinateSetting *_coordinateSetting; - BOOL _hasOriginView; - MyPathSpace *_spaceType; -} +@synthesize spaceType = _spaceType; + +#pragma mark-- Public Methods /* +(Class)layerClass @@ -152,308 +135,276 @@ +(Class)layerClass } */ --(MyCoordinateSetting*)coordinateSetting -{ - if (_coordinateSetting == nil) - { +- (MyCoordinateSetting *)coordinateSetting { + if (_coordinateSetting == nil) { _coordinateSetting = [[MyCoordinateSetting alloc] initWithPathLayout:self]; } - return _coordinateSetting; } --(MyPathSpace*)spaceType -{ - if (_spaceType == nil) - { +- (MyPathSpace *)spaceType { + if (_spaceType == nil) { _spaceType = [MyPathSpace flexed]; } - return _spaceType; } --(void)setSpaceType:(MyPathSpace *)spaceType -{ - if (_spaceType != spaceType) - { +- (void)setSpaceType:(MyPathSpace *)spaceType { + if (_spaceType != spaceType) { _spaceType = spaceType; - [self setNeedsLayout]; } } --(void)setRectangularEquation:(CGFloat (^)(CGFloat))rectangularEquation -{ +- (void)setRectangularEquation:(CGFloat (^)(CGFloat))rectangularEquation { _rectangularEquation = [rectangularEquation copy]; _parametricEquation = nil; _polarEquation = nil; [self setNeedsLayout]; } --(void)setParametricEquation:(CGPoint (^)(CGFloat))parametricEquation -{ +- (void)setParametricEquation:(CGPoint (^)(CGFloat))parametricEquation { _parametricEquation = [parametricEquation copy]; _rectangularEquation = nil; _polarEquation = nil; [self setNeedsLayout]; } --(void)setPolarEquation:(CGFloat (^)(CGFloat))polarEquation -{ +- (void)setPolarEquation:(CGFloat (^)(CGFloat))polarEquation { _polarEquation = [polarEquation copy]; _rectangularEquation = nil; _parametricEquation = nil; [self setNeedsLayout]; } - --(UIView*)originView -{ - if (_hasOriginView && self.subviews.count > 0) +- (UIView *)originView { + if (_hasOriginView && self.subviews.count > 0) { return self.subviews.lastObject; + } return nil; } --(void)setOriginView:(UIView *)originView -{ - if (_hasOriginView) - { - if (originView != nil) - { - if (self.subviews.count > 0) - { - if (self.subviews.lastObject != originView) - { +- (void)setOriginView:(UIView *)originView { + if (_hasOriginView) { + if (originView != nil) { + if (self.subviews.count > 0) { + if (self.subviews.lastObject != originView) { [self.subviews.lastObject removeFromSuperview]; [super addSubview:originView]; } - - } - else - { + } else { [self addSubview:originView]; } - - } - else - { - if (self.subviews.count > 0) + } else { + if (self.subviews.count > 0) { [self.subviews.lastObject removeFromSuperview]; - + } _hasOriginView = NO; } - - } - else - { - if (originView != nil) - { + } else { + if (originView != nil) { [super addSubview:originView]; _hasOriginView = YES; } } } --(NSArray*)pathSubviews -{ - if (_hasOriginView) - { - NSArray *sbs = self.subviews; - if (sbs.count == 0) - return sbs; - - NSMutableArray *pathsbs = [NSMutableArray arrayWithCapacity:self.subviews.count]; - for (UIView *sbv in sbs) - { - if (sbv == sbs.lastObject) +- (NSArray *)pathSubviews { + if (_hasOriginView) { + NSArray *subviews = self.subviews; + if (subviews.count == 0) { + return subviews; + } + NSMutableArray *pathSubviews = [NSMutableArray arrayWithCapacity:self.subviews.count]; + for (UIView *subview in subviews) { + if (subview == subviews.lastObject) { continue; - - [pathsbs addObject:sbv]; + } + [pathSubviews addObject:subview]; } - - return pathsbs; - } - else + + return pathSubviews; + } else { return self.subviews; + } } --(NSMutableArray*)argumentArray -{ - if (_argumentArray == nil) - { +- (NSMutableArray *)argumentArray { + if (_argumentArray == nil) { _argumentArray = [NSMutableArray new]; } - return _argumentArray; } --(CGFloat)argumentFrom:(UIView*)subview -{ +- (CGFloat)argumentFrom:(UIView *)subview { NSUInteger index = [self.subviews indexOfObject:subview]; - if (index == NSNotFound) + if (index == NSNotFound) { return NAN; - - if (self.originView == subview) + } + if (self.originView == subview) { return NAN; - - if (index < self.argumentArray.count) - { - if (self.polarEquation != nil) + } + if (index < self.argumentArray.count) { + if (self.polarEquation != nil) { return [self.argumentArray[index] doubleValue] / 180 * M_PI; - else + } else { return [self.argumentArray[index] doubleValue]; - } - else + } + } else { return NAN; - + } } - //开始和结束子视图之间的路径创建 --(void)beginSubviewPathPoint:(BOOL)full -{ +- (void)beginSubviewPathPoint:(BOOL)full { //这里先把所有点都创建出来。 - if (full) - { + MyPathLayoutTraits *layoutTraits = (MyPathLayoutTraits*)self.myDefaultSizeClass; + + MyLayoutContext context; + context.selfSize = self.bounds.size; + context.paddingTop = layoutTraits.myLayoutPaddingTop; + context.paddingBottom = layoutTraits.myLayoutPaddingBottom; + context.paddingLeading = layoutTraits.myLayoutPaddingLeading; + context.paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + + if (full) { NSMutableArray *pointIndexs = [NSMutableArray new]; - self.pathPoints = [self myCalcPoints:self.pathSubviews path:nil pointIndexArray:pointIndexs lsc:self.myCurrentSizeClass]; + self.pathPoints = [self myCalcPointsOfSubviewCount:self.pathSubviews.count path:nil pointIndexArray:pointIndexs withContext:&context]; self.pointIndexs = pointIndexs; - } - else - { - self.pathPoints = [self myCalcPoints:self.pathSubviews path:nil pointIndexArray:nil lsc:self.myCurrentSizeClass]; + } else { + self.pathPoints = [self myCalcPointsOfSubviewCount:self.pathSubviews.count path:nil pointIndexArray:nil withContext:&context]; self.pointIndexs = nil; } - } --(void)endSubviewPathPoint -{ +- (void)endSubviewPathPoint { self.pathPoints = nil; self.pointIndexs = nil; } --(NSArray*)getSubviewPathPoint:(NSInteger)fromIndex toIndex:(NSInteger)toIndex -{ - if (self.pathPoints == nil) +- (NSArray *)getSubviewPathPoint:(NSInteger)fromIndex toIndex:(NSInteger)toIndex { + if (self.pathPoints == nil) { return nil; - + } NSInteger realFromIndex = fromIndex; NSInteger realToIndex = toIndex; - - if (realFromIndex == realToIndex) + + if (realFromIndex == realToIndex) { return [NSArray new]; - + } //要求外界传递进来的索引要考虑原点视图的索引。 NSMutableArray *retPoints = [NSMutableArray new]; - - if (realFromIndex < realToIndex) - { - NSInteger start; - NSInteger end; - if (self.pointIndexs == nil) - { + NSInteger start; + NSInteger end; + NSInteger indexsCount = self.pointIndexs.count; + + if (realFromIndex < realToIndex) { + if (indexsCount == 0) { start = realFromIndex; end = realToIndex; + } else { + if (realFromIndex >= indexsCount) { + start = [self.pointIndexs[indexsCount - 1] integerValue]; + } else { + start = [self.pointIndexs[realFromIndex] integerValue]; + } + if (realToIndex >= indexsCount) { + end = [self.pointIndexs[indexsCount - 1] integerValue]; + } else { + end = [self.pointIndexs[realToIndex] integerValue]; + } } - else - { - start = [self.pointIndexs[realFromIndex] integerValue]; - end = [self.pointIndexs[realToIndex] integerValue]; - } - - for (NSInteger i = start; i <= end; i++) - { + + for (NSInteger i = start; i <= end; i++) { [retPoints addObject:self.pathPoints[i]]; } - } - else - { - NSInteger end = [self.pointIndexs[realFromIndex] integerValue]; - NSInteger start = [self.pointIndexs[realToIndex] integerValue]; - - for (NSInteger i = end; i >= start; i--) - { + } else { + if (indexsCount == 0) { + start = realToIndex; + end = realFromIndex; + } else { + if (realFromIndex >= indexsCount) { + end = [self.pointIndexs[indexsCount - 1] integerValue]; + } else { + end = [self.pointIndexs[realFromIndex] integerValue]; + } + if (realToIndex >= indexsCount) { + start = [self.pointIndexs[indexsCount - 1] integerValue]; + } else { + start = [self.pointIndexs[realToIndex] integerValue]; + } + } + + for (NSInteger i = end; i >= start; i--) { [retPoints addObject:self.pathPoints[i]]; } } - - return retPoints; - + + return retPoints; } --(CGPathRef)createPath:(NSInteger)subviewCount -{ - CGMutablePathRef retPath = CGPathCreateMutable(); +- (CGPathRef)createPath:(NSInteger)subviewCount { + MyPathLayoutTraits *layoutTraits = (MyPathLayoutTraits*)self.myDefaultSizeClass; - if (self.spaceType.type != MyPathSpace_Fixed) - { - [self myCalcPathPoints:retPath pPathLen:NULL subviewCount:-1 pointIndexArray:nil viewSpace:0 lsc:self.myCurrentSizeClass]; - } - else - { - if (subviewCount == -1) + MyLayoutContext context; + context.selfSize = self.bounds.size; + context.paddingTop = layoutTraits.myLayoutPaddingTop; + context.paddingBottom = layoutTraits.myLayoutPaddingBottom; + context.paddingLeading = layoutTraits.myLayoutPaddingLeading; + context.paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + + CGMutablePathRef retPath = CGPathCreateMutable(); + + if (self.spaceType.type != MyPathSpace_Fixed) { + [self myCalcPathPoints:retPath pPathLen:NULL subviewCount:-1 pointIndexArray:nil viewSpace:0 withContext:&context]; + } else { + if (subviewCount == -1) { subviewCount = self.pathSubviews.count; - - [self myCalcPathPoints:retPath pPathLen:NULL subviewCount:subviewCount pointIndexArray:nil viewSpace:self.spaceType.value lsc:self.myCurrentSizeClass]; + } + [self myCalcPathPoints:retPath pPathLen:NULL subviewCount:subviewCount pointIndexArray:nil viewSpace:self.spaceType.value withContext:&context]; } - return retPath; - } -#pragma mark -- Override Method +#pragma mark-- Override Methods -- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index -{ - if (self.originView != nil) - { - if (index == self.subviews.count) - index -=1; +- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index { + if (self.originView != nil) { + if (index == self.subviews.count) { + index -= 1; + } } - [super insertSubview:view atIndex:index]; } -- (void)addSubview:(UIView *)view -{ - if (self.originView != nil) - { +- (void)addSubview:(UIView *)view { + if (self.originView != nil) { [super insertSubview:view atIndex:self.subviews.count - 1]; - } - else + } else { [super addSubview:view]; + } } - -- (void)sendSubviewToBack:(UIView *)view -{ - if (self.originView == view) +- (void)sendSubviewToBack:(UIView *)view { + if (self.originView == view) { return; - + } [super sendSubviewToBack:view]; - } - -- (void)willRemoveSubview:(UIView *)subview -{ +- (void)willRemoveSubview:(UIView *)subview { [super willRemoveSubview:subview]; - if (_hasOriginView) - { - if (self.subviews.count > 0 && subview == self.subviews.lastObject) - { + if (_hasOriginView) { + if (self.subviews.count > 0 && subview == self.subviews.lastObject) { _hasOriginView = NO; } } } - // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - - /* +/* - (void)drawRect:(CGRect)rect { // Drawing code @@ -518,536 +469,337 @@ - (void)drawRect:(CGRect)rect { */ +- (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { + [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; - --(CGSize)calcLayoutRect:(CGSize)size isEstimate:(BOOL)isEstimate pHasSubLayout:(BOOL*)pHasSubLayout sizeClass:(MySizeClass)sizeClass sbs:(NSMutableArray*)sbs -{ - CGSize selfSize = [super calcLayoutRect:size isEstimate:isEstimate pHasSubLayout:pHasSubLayout sizeClass:sizeClass sbs:sbs]; - - if (sbs == nil) - sbs = [self myGetLayoutSubviews]; - NSArray *sbs2 = sbs; - - MyPathLayout *lsc = self.myCurrentSizeClass; - - for (UIView *sbv in sbs) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (!isEstimate) - { - sbvmyFrame.frame = sbv.bounds; - [self myCalcSizeOfWrapContentSubview:sbv sbvsc:sbvsc sbvmyFrame:sbvmyFrame]; - } - - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - - - if (pHasSubLayout != nil && (sbvsc.wrapContentHeight || sbvsc.wrapContentWidth)) - *pHasSubLayout = YES; - - if (isEstimate && (sbvsc.wrapContentWidth || sbvsc.wrapContentHeight)) - { - [(MyBaseLayout*)sbv sizeThatFits:sbvmyFrame.frame.size inSizeClass:sizeClass]; - if (sbvmyFrame.multiple) - { - sbvmyFrame.sizeClass = [sbv myBestSizeClass:sizeClass]; //因为sizeThatFits执行后会还原,所以这里要重新设置 - sbvsc = sbvmyFrame.sizeClass; - } - } - } + MyPathLayoutTraits *layoutTraits = (MyPathLayoutTraits *)context->layoutViewEngine.currentSizeClass; + context->paddingTop = layoutTraits.myLayoutPaddingTop; + context->paddingBottom = layoutTraits.myLayoutPaddingBottom; + context->paddingLeading = layoutTraits.myLayoutPaddingLeading; + context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + if (context->subviewEngines == nil) { + context->subviewEngines = [layoutTraits filterEngines:subviewEngines]; } - - + + [self myCalcSubviewsWrapContentSize:context withCustomSetting:nil]; + CGFloat minXPos = CGFLOAT_MAX; CGFloat maxXPos = -CGFLOAT_MAX; CGFloat minYPos = CGFLOAT_MAX; CGFloat maxYPos = -CGFLOAT_MAX; + + //这里先把原点子视图的位置删除。因为后续的计算只处理非原点视图的位置和尺寸,原点视图的位置和尺寸单独处理。 + NSInteger originViewIndex = NSNotFound; + MyLayoutEngine *originViewEngine = self.originView.myEngine; + if (originViewEngine != nil) { + for (NSInteger i = 0; i < context->subviewEngines.count; i++) { + if (context->subviewEngines[i] == originViewEngine) { + originViewIndex = i; + [context->subviewEngines removeObjectAtIndex:i]; + break; + } + } + } - //算路径子视图的。 - sbs = [self myGetLayoutSubviewsFrom:self.pathSubviews]; - CGMutablePathRef path = nil; - if ([self.layer isKindOfClass:[CAShapeLayer class]] && !isEstimate) - { + if ([self.layer isKindOfClass:[CAShapeLayer class]] && !context->isEstimate) { path = CGPathCreateMutable(); } - - NSArray *pts = [self myCalcPoints:sbs path:path pointIndexArray:nil lsc:lsc]; - - if (path != nil) - { - CAShapeLayer *slayer = (CAShapeLayer*)self.layer; + NSArray *points = [self myCalcPointsOfSubviewCount:context->subviewEngines.count path:path pointIndexArray:nil withContext:context]; + + if (path != nil) { + CAShapeLayer *slayer = (CAShapeLayer *)self.layer; slayer.path = path; CGPathRelease(path); } - - for (int i = 0; i < sbs.count; i++) - { - UIView *sbv = sbs[i]; - - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - CGPoint pt = CGPointZero; - if (pts.count > i) - { - pt = [pts[i] CGPointValue]; + + for (int i = 0; i < context->subviewEngines.count; i++) { + + MyLayoutEngine *subviewEngine = context->subviewEngines[i]; + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + + CGPoint point = CGPointZero; + if (points.count > i) { + point = [points[i] CGPointValue]; } - //计算得到最大的高度和最大的宽度。 - - CGRect rect = sbvmyFrame.frame; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil) - { - if (sbvsc.widthSizeInner.dimeRelaVal == lsc.widthSizeInner) - { - rect.size.width = [sbvsc.widthSizeInner measureWith:(selfSize.width - lsc.myLayoutLeftPadding - lsc.myLayoutRightPadding) ]; - } - else - { - rect.size.width = [sbvsc.widthSizeInner measureWith: sbvsc.widthSizeInner.dimeRelaVal.view.estimatedRect.size.width ]; - } - } - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - - if (sbvsc.heightSizeInner.dimeRelaVal != nil) - { - if (sbvsc.heightSizeInner.dimeRelaVal == lsc.heightSizeInner) - { - rect.size.height = [sbvsc.heightSizeInner measureWith:(selfSize.height - lsc.myLayoutTopPadding - lsc.myLayoutBottomPadding) ]; - } - else - { - rect.size.height = [sbvsc.heightSizeInner measureWith:sbvsc.heightSizeInner.dimeRelaVal.view.estimatedRect.size.height ]; - } + + subviewEngine.width = [self myWidthSizeValueOfSubviewEngine:subviewEngine withContext:context]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + subviewEngine.height = [self myHeightSizeValueOfSubviewEngine:subviewEngine withContext:context]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + if (subviewTraits.heightSizeInner.anchorVal != nil && subviewTraits.heightSizeInner.anchorVal == subviewTraits.widthSizeInner) { //特殊处理高度等于宽度的情况 + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:subviewEngine.width]; + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; } - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - { - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; + + if (subviewTraits.widthSizeInner.anchorVal != nil && subviewTraits.widthSizeInner.anchorVal == subviewTraits.heightSizeInner) { //特殊处理宽度等于高度的情况 + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.height]; + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; } - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - + //中心点的位置。。 - rect.origin.x = pt.x - rect.size.width * sbv.layer.anchorPoint.x - sbvsc.rightPosInner.absVal + sbvsc.leftPosInner.absVal; - rect.origin.y = pt.y - rect.size.height * sbv.layer.anchorPoint.y - sbvsc.bottomPosInner.absVal + sbvsc.topPosInner.absVal; - - if (_myCGFloatLess(CGRectGetMinY(rect), minYPos)) - { - minYPos = CGRectGetMinY(rect); + subviewEngine.leading = point.x - subviewEngine.width * subviewTraits.view.layer.anchorPoint.x - subviewTraits.rightPosInner.measure + subviewTraits.leftPosInner.measure; + subviewEngine.top = point.y - subviewEngine.height * subviewTraits.view.layer.anchorPoint.y - subviewTraits.bottomPosInner.measure + subviewTraits.topPosInner.measure; + + if (_myCGFloatLess(subviewEngine.top, minYPos)) { + minYPos = subviewEngine.top; } - - if (_myCGFloatGreat(CGRectGetMaxY(rect), maxYPos)) - { - maxYPos = CGRectGetMaxY(rect); + if (_myCGFloatGreat(subviewEngine.top + subviewEngine.height, maxYPos)) { + maxYPos = subviewEngine.top + subviewEngine.height; } - - if (_myCGFloatLess(CGRectGetMinX(rect), minXPos)) - { - minXPos = CGRectGetMinX(rect); + if (_myCGFloatLess(subviewEngine.leading, minXPos)) { + minXPos = subviewEngine.leading; } - - if (_myCGFloatGreat(CGRectGetMaxX(rect), maxXPos)) - { - maxXPos = CGRectGetMaxX(rect); + if (_myCGFloatGreat(subviewEngine.leading + subviewEngine.width, maxXPos)) { + maxXPos = subviewEngine.leading + subviewEngine.width; } - - - sbvmyFrame.frame = rect; - } - + //特殊填充中心视图。 - UIView *sbv = [self originView]; - if (sbv != nil) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - CGRect rect = sbvmyFrame.frame; - - if (sbvsc.widthSizeInner.dimeNumVal != nil) - rect.size.width = sbvsc.widthSizeInner.measure; - - if (sbvsc.widthSizeInner.dimeRelaVal != nil) - { - if (sbvsc.widthSizeInner.dimeRelaVal == lsc.widthSizeInner) - { - rect.size.width = [sbvsc.widthSizeInner measureWith:(selfSize.width - lsc.myLayoutLeftPadding - lsc.myLayoutRightPadding)]; - } - else - { - rect.size.width = [sbvsc.widthSizeInner measureWith:sbvsc.widthSizeInner.dimeRelaVal.view.estimatedRect.size.width]; - } - } - - rect.size.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:rect.size.width sbvSize:rect.size selfLayoutSize:selfSize]; - - if (sbvsc.heightSizeInner.dimeNumVal != nil) - rect.size.height = sbvsc.heightSizeInner.measure; - - - if (sbvsc.heightSizeInner.dimeRelaVal != nil) - { - if (sbvsc.heightSizeInner.dimeRelaVal == lsc.heightSizeInner) - { - rect.size.height = [sbvsc.heightSizeInner measureWith:selfSize.height - lsc.myLayoutTopPadding - lsc.myLayoutBottomPadding]; - } - else if (sbvsc.heightSizeInner.dimeRelaVal == sbvsc.widthSizeInner) - { - rect.size.height = [sbvsc.heightSizeInner measureWith:rect.size.width]; - } - else - { - rect.size.height = [sbvsc.heightSizeInner measureWith:sbvsc.heightSizeInner.dimeRelaVal.view.estimatedRect.size.height]; - } - } - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]) - { - rect.size.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:rect.size.width]; + if (originViewEngine != nil) { + MyViewTraits *originViewTraits = (MyViewTraits *)originViewEngine.currentSizeClass; + + originViewEngine.width = [self myWidthSizeValueOfSubviewEngine:originViewEngine withContext:context]; + originViewEngine.width = [self myValidMeasure:originViewTraits.widthSizeInner subview:originViewTraits.view calcSize:originViewEngine.width subviewSize:originViewEngine.size selfLayoutSize:context->selfSize]; + + originViewEngine.height = [self myHeightSizeValueOfSubviewEngine:originViewEngine withContext:context]; + originViewEngine.height = [self myValidMeasure:originViewTraits.heightSizeInner subview:originViewTraits.view calcSize:originViewEngine.height subviewSize:originViewEngine.size selfLayoutSize:context->selfSize]; + + if (originViewTraits.heightSizeInner.anchorVal != nil && originViewTraits.heightSizeInner.anchorVal == originViewTraits.widthSizeInner) { //特殊处理高度等于宽度的情况 + originViewEngine.height = [originViewTraits.heightSizeInner measureWith:originViewEngine.width]; + originViewEngine.height = [self myValidMeasure:originViewTraits.heightSizeInner subview:originViewTraits.view calcSize:originViewEngine.height subviewSize:originViewEngine.size selfLayoutSize:context->selfSize]; } - - rect.size.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:rect.size.height sbvSize:rect.size selfLayoutSize:selfSize]; - - //计算宽度等于高度的情况。 - if (sbvsc.widthSizeInner.dimeRelaVal != nil && sbvsc.widthSizeInner.dimeRelaVal == sbvsc.heightSizeInner) - { - rect.size.width = [sbvsc.widthSizeInner measureWith:rect.size.height]; + + if (originViewTraits.widthSizeInner.anchorVal != nil && originViewTraits.widthSizeInner.anchorVal == originViewTraits.heightSizeInner) { //特殊处理宽度等于高度的情况 + originViewEngine.width = [originViewTraits.widthSizeInner measureWith:originViewEngine.height]; + originViewEngine.width = [self myValidMeasure:originViewTraits.widthSizeInner subview:originViewTraits.view calcSize:originViewEngine.width subviewSize:originViewEngine.size selfLayoutSize:context->selfSize]; } - + //位置在原点位置。。 - rect.origin.x = (selfSize.width - lsc.myLayoutLeftPadding - lsc.myLayoutRightPadding)*self.coordinateSetting.origin.x - rect.size.width *sbv.layer.anchorPoint.x + sbvsc.leftPosInner.absVal + lsc.myLayoutLeftPadding - sbvsc.rightPosInner.absVal; - rect.origin.y = (selfSize.height - lsc.myLayoutTopPadding - lsc.myLayoutBottomPadding)*self.coordinateSetting.origin.y - rect.size.height * sbv.layer.anchorPoint.y + sbvsc.topPosInner.absVal + lsc.myLayoutTopPadding - sbvsc.bottomPosInner.absVal; - - if (_myCGFloatLess(CGRectGetMinY(rect), minYPos)) - { - minYPos = CGRectGetMinY(rect); + originViewEngine.leading = (context->selfSize.width - context->paddingLeading - context->paddingTrailing) * self.coordinateSetting.origin.x - originViewEngine.width * originViewTraits.view.layer.anchorPoint.x + originViewTraits.leftPosInner.measure + context->paddingLeading - originViewTraits.rightPosInner.measure; + originViewEngine.top = (context->selfSize.height - context->paddingTop - context->paddingBottom) * self.coordinateSetting.origin.y - originViewEngine.height * originViewTraits.view.layer.anchorPoint.y + originViewTraits.topPosInner.measure + context->paddingTop - originViewTraits.bottomPosInner.measure; + + if (_myCGFloatLess(originViewEngine.top, minYPos)) { + minYPos = originViewEngine.top; } - - if (_myCGFloatGreat(CGRectGetMaxY(rect), maxYPos)) - { - maxYPos = CGRectGetMaxY(rect); + if (_myCGFloatGreat(originViewEngine.top + originViewEngine.height, maxYPos)) { + maxYPos = originViewEngine.top + originViewEngine.height; } - - if (_myCGFloatLess(CGRectGetMinX(rect), minXPos)) - { - minXPos = CGRectGetMinX(rect); + if (_myCGFloatLess(originViewEngine.leading, minXPos)) { + minXPos = originViewEngine.leading; } - - if (_myCGFloatGreat(CGRectGetMaxX(rect), maxXPos)) - { - maxXPos = CGRectGetMaxX(rect); + if (_myCGFloatGreat(originViewEngine.leading + originViewEngine.width, maxXPos)) { + maxXPos = originViewEngine.leading + originViewEngine.width; } - - sbvmyFrame.frame = rect; - } - - if (minYPos == CGFLOAT_MAX) - { + + if (minYPos == CGFLOAT_MAX) { minYPos = 0; } - - if (maxYPos == -CGFLOAT_MAX) - { - maxYPos = lsc.myLayoutTopPadding + lsc.myLayoutBottomPadding; + if (maxYPos == -CGFLOAT_MAX) { + maxYPos = context->paddingTop + context->paddingBottom; } - - if (minXPos == CGFLOAT_MAX) - { + if (minXPos == CGFLOAT_MAX) { minXPos = 0; } - - if (maxXPos == -CGFLOAT_MAX) - { - maxXPos = lsc.myLayoutLeftPadding + lsc.myLayoutRightPadding; + if (maxXPos == -CGFLOAT_MAX) { + maxXPos = context->paddingLeading + context->paddingTrailing; } - - - if (lsc.wrapContentWidth) - { - selfSize.width = maxXPos - minXPos; + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = maxXPos - minXPos; } - - if (lsc.wrapContentHeight) - { - selfSize.height = maxYPos - minYPos; + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = maxYPos - minYPos; } - //调整布局视图自己的尺寸。 - [self myAdjustLayoutSelfSize:&selfSize lsc:lsc]; - - - //路径布局不支持RTL。 - // [self myAdjustSubviewsRTLPos:sbs2 selfWidth:selfSize.width]; + //再将原点视图引擎插入到子视图引擎数组中,参加后续的处理。 + if (originViewIndex != NSNotFound) { + [context->subviewEngines insertObject:originViewEngine atIndex:originViewIndex]; + } - return [self myAdjustSizeWhenNoSubviews:selfSize sbs:sbs2 lsc:lsc]; + return [self myAdjustLayoutViewSizeWithContext:context]; } --(id)createSizeClassInstance -{ - return [MyPathLayoutViewSizeClass new]; +- (id)createSizeClassInstance { + return [MyPathLayoutTraits new]; } +#pragma mark-- Private Methods - -#pragma mark -- Private Method - - --(CGFloat)myCalcDistance:(CGPoint)pt1 with:(CGPoint)pt2 -{ - return sqrt(pow(pt1.x - pt2.x, 2) + pow(pt1.y - pt2.y, 2)); +- (CGFloat)myCalcDistance:(CGPoint)pt1 with:(CGPoint)pt2 { + return hypot(pt1.x - pt2.x, pt1.y - pt2.y); } +- (CGPoint)myGetNearestDistancePoint:(CGFloat)startArg lastXY:(CGPoint)lastXY distance:(CGFloat)distance viewSpace:(CGFloat)viewSpace pLastValidArg:(CGFloat *)pLastValidArg func:(CGPoint (^)(CGFloat))func { --(CGPoint)myGetNearestDistancePoint:(CGFloat)startArg lastXY:(CGPoint)lastXY distance:(CGFloat)distance viewSpace:(CGFloat)viewSpace pLastValidArg:(CGFloat*)pLastValidArg func:(CGPoint (^)(CGFloat))func -{ - //判断pLastValidArg和Arg的差距如果超过1则按pLastValidArg + 1作为开始计算的变量。 - if (startArg - *pLastValidArg > 1) + if (startArg - *pLastValidArg > 1) { startArg = *pLastValidArg + 1; - + } CGFloat step = 1; - while(true) - { - - CGFloat arg = startArg - step + step/10; - while (arg - startArg < 0.0001) - { - + while (true) { + CGFloat arg = startArg - step + step / 10; + while (arg - startArg < 0.0001) { CGPoint realXY = func(arg); - if (!isnan(realXY.x) && !isnan(realXY.y)) - { + if (!isnan(realXY.x) && !isnan(realXY.y)) { CGFloat oldDistance = distance; - distance += [self myCalcDistance:realXY with:lastXY]; - if ( _myCGFloatGreatOrEqual(distance, viewSpace)) - { - if (_myCGFloatLessOrEqual(distance - viewSpace, self.distanceError)) - { + distance += [self myCalcDistance:realXY with:lastXY]; + if (_myCGFloatGreatOrEqual(distance, viewSpace)) { + if (_myCGFloatLessOrEqual(distance - viewSpace, self.distanceError)) { *pLastValidArg = arg; return realXY; - } - else - { + } else { distance = oldDistance; break; } } - lastXY = realXY; } - else - { - } - arg += step / 10; } - - if (arg > startArg) - { + + if (arg > startArg) { startArg += 1; step = 1; - } - else - { + } else { startArg = arg; step /= 10; } - if (_myCGFloatLessOrEqual(step, 0.001)) - { - *pLastValidArg = arg; + if (_myCGFloatLessOrEqual(step, 0.001)) { + *pLastValidArg = arg; break; } } - return lastXY; - } +- (void)myCalcPathPointsHelper:(NSMutableArray *)pathPointArray + showPath:(CGMutablePathRef)showPath + pPathLen:(CGFloat *)pPathLen + subviewCount:(NSInteger)subviewCount + pointIndexArray:(NSMutableArray *)pointIndexArray + viewSpace:(CGFloat)viewSpace + startArg:(CGFloat)startArg //定义域最小值 + endArg:(CGFloat)endArg //定义域最大值 + func:(CGPoint (^)(CGFloat))func { --(void)myCalcPathPointsHelper:(NSMutableArray*)pathPointArray - showPath:(CGMutablePathRef)showPath - pPathLen:(CGFloat*)pPathLen - subviewCount:(NSInteger)subviewCount - pointIndexArray:(NSMutableArray*)pointIndexArray - viewSpace:(CGFloat)viewSpace - startArg:(CGFloat)startArg //定义域最小值 - endArg:(CGFloat)endArg //定义域最大值 - func:(CGPoint (^)(CGFloat))func -{ - - if (self.distanceError <= 0) + if (self.distanceError <= 0.0) { self.distanceError = 0.5; - + } [self.argumentArray removeAllObjects]; - - CGFloat distance = 0; + + CGFloat distance = 0.0; CGPoint lastXY = CGPointZero; - + CGFloat arg = startArg; CGFloat lastValidArg = arg; - + //曲线有可能不是连续的,因此这两个标志用来记录是否某段曲线的开始标志以及曲线的段数 BOOL isSegmentStart = YES; int segmentCount = 0; - while (true) - { - - if (subviewCount < 0) - { + while (true) { + if (subviewCount < 0) { //如果曲线变量超过了定义域则退出。 - if (arg - endArg > 0.1) //这里不能用arg > endArg 的原因是有精度问题。 - { + if (arg - endArg > 0.1) //这里不能用arg > endArg 的原因是有精度问题。 break; - } - } - else if (subviewCount == 0) - { + } else if (subviewCount == 0) { break; - } - else - { - if (isSegmentStart && segmentCount >= 1) - { - if (pointIndexArray != nil) + } else { + if (isSegmentStart && segmentCount >= 1) { + if (pointIndexArray != nil) { [pointIndexArray addObject:@(pathPointArray.count)]; - + } [pathPointArray addObject:[NSValue valueWithCGPoint:lastXY]]; - + [self.argumentArray addObject:@(arg)]; - - if (showPath != nil) - { + + if (showPath != nil) { CGPathAddLineToPoint(showPath, NULL, lastXY.x, lastXY.y); } break; } - } - + CGPoint realXY = func(arg); - if (!isnan(realXY.x) && !isnan(realXY.y)) - { - if (isSegmentStart) - { - + if (!isnan(realXY.x) && !isnan(realXY.y)) { + if (isSegmentStart) { isSegmentStart = NO; segmentCount += 1; - if (subviewCount > 0 && segmentCount == 1) - { - subviewCount -=1; + if (subviewCount > 0 && segmentCount == 1) { + subviewCount -= 1; lastValidArg = arg; - - if (pointIndexArray != nil) + + if (pointIndexArray != nil) { [pointIndexArray addObject:@(pathPointArray.count)]; - + } [self.argumentArray addObject:@(arg)]; } - + [pathPointArray addObject:[NSValue valueWithCGPoint:realXY]]; - - if (showPath != nil) - { - CGPathMoveToPoint(showPath,NULL, realXY.x, realXY.y); + + if (showPath != nil) { + CGPathMoveToPoint(showPath, NULL, realXY.x, realXY.y); } - - - } - else - { - if (subviewCount >= 0) - { + } else { + if (subviewCount >= 0) { //点的距离累加 CGFloat oldDistance = distance; distance += [self myCalcDistance:realXY with:lastXY]; - if (_myCGFloatGreatOrEqual(distance, viewSpace) ) - {//如果距离超过间距。则需要缩小自变量步长。以便达到最小的间距误差 - if (_myCGFloatGreatOrEqual(distance - viewSpace, self.distanceError) ) - { + if (_myCGFloatGreatOrEqual(distance, viewSpace)) { //如果距离超过间距。则需要缩小自变量步长。以便达到最小的间距误差 + if (_myCGFloatGreatOrEqual(distance - viewSpace, self.distanceError)) { realXY = [self myGetNearestDistancePoint:arg lastXY:lastXY distance:oldDistance viewSpace:viewSpace pLastValidArg:&lastValidArg func:func]; - } - else - { + } else { lastValidArg = arg; } - - if (pointIndexArray == nil) + if (pointIndexArray == nil) { [pathPointArray addObject:[NSValue valueWithCGPoint:realXY]]; - else + } else { [pointIndexArray addObject:@(pathPointArray.count)]; - + } distance = 0; - subviewCount -=1; + subviewCount -= 1; [self.argumentArray addObject:@(lastValidArg)]; - - } - else if (distance - viewSpace > -1 * self.distanceError) - {//如果距离和间距小于误差则认为是一个视图可以存放的点。 - if (pointIndexArray == nil) + } else if (distance - viewSpace > -1 * self.distanceError) { //如果距离和间距小于误差则认为是一个视图可以存放的点。 + if (pointIndexArray == nil) { [pathPointArray addObject:[NSValue valueWithCGPoint:realXY]]; - else + } else { [pointIndexArray addObject:@(pathPointArray.count)]; - - distance = 0; - subviewCount -=1; + } + distance = 0.0; + subviewCount -= 1; lastValidArg = arg; [self.argumentArray addObject:@(arg)]; - } - else - { + } else { lastValidArg = arg; } - } - else - { - if (pointIndexArray == nil) + } else { + if (pointIndexArray == nil) { [pathPointArray addObject:[NSValue valueWithCGPoint:realXY]]; + } } - - if (pointIndexArray != nil) + + if (pointIndexArray != nil) { [pathPointArray addObject:[NSValue valueWithCGPoint:realXY]]; - - - if (showPath != nil) - { - CGPathAddLineToPoint(showPath,NULL, realXY.x, realXY.y); } - - if (pPathLen != NULL) - { - *pPathLen += [self myCalcDistance:realXY with:lastXY]; + if (showPath != nil) { + CGPathAddLineToPoint(showPath, NULL, realXY.x, realXY.y); + } + if (pPathLen != NULL) { + *pPathLen += [self myCalcDistance:realXY with:lastXY]; } } - lastXY = realXY; - } - else - {//只要自变量和因变量是无效值,则认为是一个新的段的开始。 + } else { //只要自变量和因变量是无效值,则认为是一个新的段的开始。 isSegmentStart = YES; } arg += 1; } - - } - /** 计算曲线路径 @@ -1056,213 +808,188 @@ -(void)myCalcPathPointsHelper:(NSMutableArray*)pathPointArray @param subviewCount 子视图的数量,如果为-1则表示只计算路径的长度不会把每个子视图所在的路径点索引保存 @param pointIndexArray 返回所有子视图在路径点的索引 @param viewSpace 子视图之间的间距 - @param lsc lsc + @param context 布局上下文对象。 @return 返回所有路径点 */ --(NSArray*)myCalcPathPoints:(CGMutablePathRef)showPath - pPathLen:(CGFloat*)pPathLen - subviewCount:(NSInteger)subviewCount - pointIndexArray:(NSMutableArray*)pointIndexArray - viewSpace:(CGFloat)viewSpace - lsc:(MyPathLayout*)lsc -{ +- (NSArray *)myCalcPathPoints:(CGMutablePathRef)showPath + pPathLen:(CGFloat *)pPathLen + subviewCount:(NSInteger)subviewCount + pointIndexArray:(NSMutableArray *)pointIndexArray + viewSpace:(CGFloat)viewSpace + withContext:(MyLayoutContext *)context { NSMutableArray *pathPointArray = [NSMutableArray new]; - - CGFloat selfWidth = CGRectGetWidth(self.bounds) - lsc.myLayoutLeftPadding - lsc.myLayoutRightPadding; - CGFloat selfHeight = CGRectGetHeight(self.bounds) - lsc.myLayoutTopPadding - lsc.myLayoutBottomPadding; - + + CGFloat selfContentWidth = context->selfSize.width - context->paddingLeading - context->paddingTrailing; + CGFloat selfContentHeight = context->selfSize.height - context->paddingTop - context->paddingBottom; + CGFloat originX = selfContentWidth * self.coordinateSetting.origin.x + context->paddingLeading; + CGFloat originY = selfContentHeight * self.coordinateSetting.origin.y + context->paddingTop; + CGFloat startArg; CGFloat endArg; - - if (self.rectangularEquation != nil) - { - startArg = 0 - selfWidth * self.coordinateSetting.origin.x; - if (self.coordinateSetting.isReverse) - { - if (self.coordinateSetting.isMath) - startArg = -1 * selfHeight *(1 - self.coordinateSetting.origin.y); - else - startArg = selfHeight *(0 - self.coordinateSetting.origin.y); + + if (self.rectangularEquation != nil) { + startArg = 0 - selfContentWidth * self.coordinateSetting.origin.x; + if (self.coordinateSetting.isReverse) { + if (self.coordinateSetting.isMath) { + startArg = -1 * selfContentHeight * (1 - self.coordinateSetting.origin.y); + } else { + startArg = selfContentHeight * (0 - self.coordinateSetting.origin.y); + } } - - if (self.coordinateSetting.start != -CGFLOAT_MAX) + + if (self.coordinateSetting.start != -CGFLOAT_MAX) { startArg = self.coordinateSetting.start; - - endArg = selfWidth - selfWidth * self.coordinateSetting.origin.x; - if (self.coordinateSetting.isReverse) - { - if (self.coordinateSetting.isMath) - endArg = -1 * selfHeight *(0 - self.coordinateSetting.origin.y); - else - endArg = selfHeight *(1 - self.coordinateSetting.origin.y); } - if (self.coordinateSetting.end != CGFLOAT_MAX) + endArg = selfContentWidth - selfContentWidth * self.coordinateSetting.origin.x; + if (self.coordinateSetting.isReverse) { + if (self.coordinateSetting.isMath) { + endArg = -1 * selfContentHeight * (0 - self.coordinateSetting.origin.y); + } else { + endArg = selfContentHeight * (1 - self.coordinateSetting.origin.y); + } + } + + if (self.coordinateSetting.end != CGFLOAT_MAX) { endArg = self.coordinateSetting.end; - - [self myCalcPathPointsHelper:pathPointArray showPath:showPath pPathLen:pPathLen subviewCount:subviewCount pointIndexArray:pointIndexArray viewSpace:viewSpace startArg:startArg endArg:endArg func:^(CGFloat arg){ - - CGFloat val = self.rectangularEquation(arg); - if (!isnan(val)) - { - if (self.coordinateSetting.isReverse) - { - return CGPointMake(val + selfWidth * self.coordinateSetting.origin.x + lsc.myLayoutLeftPadding, - (self.coordinateSetting.isMath ? -arg : arg) + selfHeight * self.coordinateSetting.origin.y + lsc.myLayoutTopPadding); - - } - else - { - return CGPointMake(arg + selfWidth * self.coordinateSetting.origin.x + lsc.myLayoutLeftPadding, - (self.coordinateSetting.isMath ? -val : val) + selfHeight * self.coordinateSetting.origin.y + lsc.myLayoutTopPadding); - - } + } + [self myCalcPathPointsHelper:pathPointArray + showPath:showPath + pPathLen:pPathLen + subviewCount:subviewCount + pointIndexArray:pointIndexArray + viewSpace:viewSpace + startArg:startArg + endArg:endArg + func:^(CGFloat arg) { + CGFloat val = self.rectangularEquation(arg); + if (!isnan(val)) { + if (self.coordinateSetting.isReverse) { + return CGPointMake(val + originX, + (self.coordinateSetting.isMath ? -arg : arg) + originY); + } else { + return CGPointMake(arg + originX, + (self.coordinateSetting.isMath ? -val : val) + originY); + } + } else { + return CGPointMake(NAN, NAN); + } + }]; + } else if (self.parametricEquation != nil) { + startArg = 0 - selfContentWidth * self.coordinateSetting.origin.x; + if (self.coordinateSetting.isReverse) { + if (self.coordinateSetting.isMath) { + startArg = -1 * selfContentHeight * (1 - self.coordinateSetting.origin.y); + } else { + startArg = selfContentHeight * (0 - self.coordinateSetting.origin.y); } - else - return CGPointMake(NAN, NAN); - - }]; - } - else if (self.parametricEquation != nil) - { - startArg = 0 - selfWidth * self.coordinateSetting.origin.x; - if (self.coordinateSetting.isReverse) - { - if (self.coordinateSetting.isMath) - startArg = -1 * selfHeight *(1 - self.coordinateSetting.origin.y); - else - startArg = selfHeight *(0 - self.coordinateSetting.origin.y); } - if (self.coordinateSetting.start != -CGFLOAT_MAX) + if (self.coordinateSetting.start != -CGFLOAT_MAX) { startArg = self.coordinateSetting.start; - - endArg = selfWidth - selfWidth * self.coordinateSetting.origin.x; - if (self.coordinateSetting.isReverse) - { - if (self.coordinateSetting.isMath) - endArg = -1 * selfHeight *(0 - self.coordinateSetting.origin.y); - else - endArg = selfHeight *(1 - self.coordinateSetting.origin.y); } - if (self.coordinateSetting.end != CGFLOAT_MAX) - endArg = self.coordinateSetting.end; - - [self myCalcPathPointsHelper:pathPointArray showPath:showPath pPathLen:pPathLen subviewCount:subviewCount pointIndexArray:pointIndexArray viewSpace:viewSpace startArg:startArg endArg:endArg func:^(CGFloat arg){ - - CGPoint val = self.parametricEquation(arg); - if (!isnan(val.x) && !isnan(val.y)) - { - if (self.coordinateSetting.isReverse) - { - return CGPointMake(val.y + selfWidth * self.coordinateSetting.origin.x + lsc.myLayoutLeftPadding, - (self.coordinateSetting.isMath ? -val.x : val.x) + selfHeight * self.coordinateSetting.origin.y + lsc.myLayoutTopPadding); - - } - else - { - return CGPointMake(val.x + selfWidth * self.coordinateSetting.origin.x + lsc.myLayoutLeftPadding, - (self.coordinateSetting.isMath ? -val.y : val.y) + selfHeight * self.coordinateSetting.origin.y + lsc.myLayoutTopPadding); - } + endArg = selfContentWidth - selfContentWidth * self.coordinateSetting.origin.x; + if (self.coordinateSetting.isReverse) { + if (self.coordinateSetting.isMath) { + endArg = -1 * selfContentHeight * (0 - self.coordinateSetting.origin.y); + } else { + endArg = selfContentHeight * (1 - self.coordinateSetting.origin.y); } - else - return CGPointMake(NAN, NAN); - - }]; - - } - else if (self.polarEquation != nil) - { - - startArg = 0; - if (self.coordinateSetting.start != -CGFLOAT_MAX) + } + if (self.coordinateSetting.end != CGFLOAT_MAX) { + endArg = self.coordinateSetting.end; + } + [self myCalcPathPointsHelper:pathPointArray + showPath:showPath + pPathLen:pPathLen + subviewCount:subviewCount + pointIndexArray:pointIndexArray + viewSpace:viewSpace + startArg:startArg + endArg:endArg + func:^(CGFloat arg) { + CGPoint val = self.parametricEquation(arg); + if (!isnan(val.x) && !isnan(val.y)) { + if (self.coordinateSetting.isReverse) { + return CGPointMake(val.y + originX, + (self.coordinateSetting.isMath ? -val.x : val.x) + originY); + } else { + return CGPointMake(val.x + originX, + (self.coordinateSetting.isMath ? -val.y : val.y) + originY); + } + } else { + return CGPointMake(NAN, NAN); + } + }]; + } else if (self.polarEquation != nil) { + startArg = 0.0; + if (self.coordinateSetting.start != -CGFLOAT_MAX) { startArg = self.coordinateSetting.start * 180.0 / M_PI; - - endArg = 360; - if (self.coordinateSetting.end != CGFLOAT_MAX) + } + endArg = 360.0; + if (self.coordinateSetting.end != CGFLOAT_MAX) { endArg = self.coordinateSetting.end * 180.0 / M_PI; - - - [self myCalcPathPointsHelper:pathPointArray showPath:showPath pPathLen:pPathLen subviewCount:subviewCount pointIndexArray:pointIndexArray viewSpace:viewSpace startArg:startArg endArg:endArg func:^(CGFloat arg){ - - //计算用弧度 - CGFloat r = self.polarEquation(arg * M_PI / 180.0); - if (!isnan(r)) - { - if (self.coordinateSetting.isReverse) - { - CGFloat y = r * cos(arg / 180.0 * M_PI); - - return CGPointMake(r * sin(arg / 180.0 * M_PI) + selfWidth * self.coordinateSetting.origin.x + lsc.myLayoutLeftPadding, - (self.coordinateSetting.isMath ? -y : y) + selfHeight * self.coordinateSetting.origin.y + lsc.myLayoutTopPadding); - - } - else - { - - CGFloat y = r * sin(arg / 180 * M_PI); - - return CGPointMake(r * cos(arg / 180 * M_PI) + selfWidth * self.coordinateSetting.origin.x + lsc.myLayoutLeftPadding, - (self.coordinateSetting.isMath ? -y : y) + selfHeight * self.coordinateSetting.origin.y + lsc.myLayoutTopPadding); - } - } - else - return CGPointMake(NAN, NAN); - - }]; - + } + [self myCalcPathPointsHelper:pathPointArray + showPath:showPath + pPathLen:pPathLen + subviewCount:subviewCount + pointIndexArray:pointIndexArray + viewSpace:viewSpace + startArg:startArg + endArg:endArg + func:^(CGFloat arg) { + //计算用弧度 + CGFloat r = self.polarEquation(arg * M_PI / 180.0); + if (!isnan(r)) { + if (self.coordinateSetting.isReverse) { + CGFloat y = r * cos(arg / 180.0 * M_PI); + + return CGPointMake(r * sin(arg / 180.0 * M_PI) + originX, + (self.coordinateSetting.isMath ? -y : y) + originY); + } else { + CGFloat y = r * sin(arg / 180.0 * M_PI); + return CGPointMake(r * cos(arg / 180.0 * M_PI) + originX, + (self.coordinateSetting.isMath ? -y : y) + originY); + } + } else { + return CGPointMake(NAN, NAN); + } + }]; } - return pathPointArray; } --(NSArray*)myCalcPoints:(NSArray*)sbs path:(CGMutablePathRef)path pointIndexArray:(NSMutableArray*)pointIndexArray lsc:(MyPathLayout*)lsc -{ - if (sbs.count > 0) - { - if (self.spaceType.type != MyPathSpace_Fixed) - { +- (NSArray *)myCalcPointsOfSubviewCount:(NSInteger)count path:(CGMutablePathRef)path pointIndexArray:(NSMutableArray *)pointIndexArray withContext:(MyLayoutContext*)context { + if (count > 0) { + if (self.spaceType.type != MyPathSpace_Fixed) { //如果间距不是固定的,那么要先算出路径的长度pathLen,以及是否是封闭曲线标志来算出每个子视图在曲线上的间距viewSpacing - - CGFloat pathLen = 0; //曲线的总长度 - NSArray *tempArray = [self myCalcPathPoints:path pPathLen:&pathLen subviewCount:-1 pointIndexArray:nil viewSpace:0 lsc:lsc]; - + + CGFloat pathLen = 0; //曲线的总长度 + NSArray *tempArray = [self myCalcPathPoints:path pPathLen:&pathLen subviewCount:-1 pointIndexArray:nil viewSpace:0 withContext:context]; + //判断是否是封闭路径,方法为第一个路径点和最后一个之间的距离差小于1 BOOL bClose = NO; - if (tempArray.count > 1) - { + if (tempArray.count > 1) { CGPoint p1 = [tempArray.firstObject CGPointValue]; CGPoint p2 = [tempArray.lastObject CGPointValue]; bClose = [self myCalcDistance:p1 with:p2] <= 1; - } - - - CGFloat viewSpace = 0; //每个视图之间的间距。 - NSInteger sbvcount = sbs.count; - if (self.spaceType.type == MyPathSpace_Count) //如果是固定数量则按固定数量来分配间距 + + CGFloat viewSpace = 0; //每个视图之间的间距。 + NSInteger sbvcount = count; + if (self.spaceType.type == MyPathSpace_Count) { //如果是固定数量则按固定数量来分配间距 sbvcount = (NSInteger)self.spaceType.value; - + } //总长度除视图数量得出子视图的间距。 - if (sbvcount > 1) - { + if (sbvcount > 1) { viewSpace = pathLen / (sbvcount - (bClose ? 0 : 1)); } - //有间距后再重新计算一遍 - return [self myCalcPathPoints:nil pPathLen:NULL subviewCount:sbs.count pointIndexArray:pointIndexArray viewSpace:viewSpace lsc:lsc]; - - } - else - { - return [self myCalcPathPoints:path pPathLen:NULL subviewCount:sbs.count pointIndexArray:pointIndexArray viewSpace:self.spaceType.value lsc:lsc]; + return [self myCalcPathPoints:nil pPathLen:NULL subviewCount:count pointIndexArray:pointIndexArray viewSpace:viewSpace withContext:context]; + } else { + return [self myCalcPathPoints:path pPathLen:NULL subviewCount:count pointIndexArray:pointIndexArray viewSpace:self.spaceType.value withContext:context]; } } - return nil; - } - - - @end - diff --git a/MyLayout/Lib/MyRelativeLayout.h b/MyLayout/Lib/MyRelativeLayout.h index 5457a82..d12e120 100644 --- a/MyLayout/Lib/MyRelativeLayout.h +++ b/MyLayout/Lib/MyRelativeLayout.h @@ -8,8 +8,6 @@ #import "MyBaseLayout.h" - - /** 相对布局是一种里面的子视图通过相互之间的约束和依赖来进行布局和定位的布局视图。 相对布局里面的子视图的布局位置和添加的顺序无关,而是通过设置子视图的相对依赖关系来进行定位和布局的。 @@ -17,17 +15,4 @@ */ @interface MyRelativeLayout : MyBaseLayout - -/** - *这个属性已经无效了,请使用子视图自身的扩展属性myVisibility属性来进行子视图的隐藏和显示的定制化处理。 - */ -@property(nonatomic, assign) BOOL flexOtherViewWidthWhenSubviewHidden MYMETHODDEPRECATED("this property was invalid, please use subview's myVisibility to instead"); - -/** - *这个属性已经无效了,请使用子视图自身的扩展属性myVisibility属性来进行子视图的隐藏和显示的定制化处理。 - */ -@property(nonatomic, assign) BOOL flexOtherViewHeightWhenSubviewHidden MYMETHODDEPRECATED("this property was invalid, please use subview's myVisibility to instead"); -; - - @end diff --git a/MyLayout/Lib/MyRelativeLayout.m b/MyLayout/Lib/MyRelativeLayout.m index 47032dd..a2d07de 100644 --- a/MyLayout/Lib/MyRelativeLayout.m +++ b/MyLayout/Lib/MyRelativeLayout.m @@ -9,1300 +9,959 @@ #import "MyRelativeLayout.h" #import "MyLayoutInner.h" - @implementation MyRelativeLayout -/* - // Only override drawRect: if you perform custom drawing. - // An empty implementation adversely affects performance during animation. - - (void)drawRect:(CGRect)rect { - // Drawing code - } - */ - - --(void)setFlexOtherViewWidthWhenSubviewHidden:(BOOL)flexOtherViewWidthWhenSubviewHidden -{ - NSAssert(0, @"oops!, flexOtherViewWidthWhenSubviewHidden is invalid please use subview's myVisibility to instead!!!"); -} - --(BOOL)flexOtherViewWidthWhenSubviewHidden -{ - return NO; -} - --(void)setFlexOtherViewHeightWhenSubviewHidden:(BOOL)flexOtherViewHeightWhenSubviewHidden -{ - NSAssert(0, @"oops!, flexOtherViewHeightWhenSubviewHidden is invalid please use subview's myVisibility to instead!!!"); -} +#pragma mark-- Override Methods --(BOOL)flexOtherViewHeightWhenSubviewHidden -{ - return NO; -} +- (CGSize)calcLayoutSize:(CGSize)size subviewEngines:(NSMutableArray *)subviewEngines context:(MyLayoutContext *)context { + [super calcLayoutSize:size subviewEngines:subviewEngines context:context]; + + MyRelativeLayoutTraits *layoutTraits = (MyRelativeLayoutTraits *)context->layoutViewEngine.currentSizeClass; + context->paddingTop = layoutTraits.myLayoutPaddingTop; + context->paddingBottom = layoutTraits.myLayoutPaddingBottom; + context->paddingLeading = layoutTraits.myLayoutPaddingLeading; + context->paddingTrailing = layoutTraits.myLayoutPaddingTrailing; + context->subviewEngines = subviewEngines; -#pragma mark -- Override Method + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = [self myValidMeasure:layoutTraits.widthSizeInner subview:self calcSize:0.0 subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = [self myValidMeasure:layoutTraits.heightSizeInner subview:self calcSize:0.0 subviewSize:context->selfSize selfLayoutSize:self.superview.bounds.size]; + } + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; --(CGSize)calcLayoutRect:(CGSize)size isEstimate:(BOOL)isEstimate pHasSubLayout:(BOOL*)pHasSubLayout sizeClass:(MySizeClass)sizeClass sbs:(NSMutableArray*)sbs -{ - CGSize selfSize = [super calcLayoutRect:size isEstimate:isEstimate pHasSubLayout:pHasSubLayout sizeClass:sizeClass sbs:sbs]; - - MyRelativeLayout *lsc = self.myCurrentSizeClass; - - - for (UIView *sbv in self.subviews) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (sbvsc.useFrame) + if (subviewTraits.useFrame) { continue; + } - if (!isEstimate || (pHasSubLayout != nil && (*pHasSubLayout) == YES)) - [sbvmyFrame reset]; - + [subviewEngine reset]; - if ([sbv isKindOfClass:[MyBaseLayout class]]) - { - - if (sbvsc.wrapContentWidth) - { - //只要同时设置了左右边距或者设置了宽度则应该把wrapContentWidth置为NO - if ((sbvsc.leadingPosInner.posVal != nil && sbvsc.trailingPosInner.posVal != nil) || sbvsc.widthSizeInner.dimeVal != nil) - sbvsc.wrapContentWidth = NO; - } - - if (sbvsc.wrapContentHeight) - { - if ((sbvsc.topPosInner.posVal != nil && sbvsc.bottomPosInner.posVal != nil) || sbvsc.heightSizeInner.dimeVal != nil) - sbvsc.wrapContentHeight = NO; - } - - if (pHasSubLayout != nil && (sbvsc.wrapContentHeight || sbvsc.wrapContentWidth)) - *pHasSubLayout = YES; - - if (isEstimate && (sbvsc.wrapContentWidth || sbvsc.wrapContentHeight)) - { - [(MyBaseLayout*)sbv sizeThatFits:sbvmyFrame.frame.size inSizeClass:sizeClass]; - - sbvmyFrame.leading = sbvmyFrame.trailing = sbvmyFrame.top = sbvmyFrame.bottom = CGFLOAT_MAX; - - if (sbvmyFrame.multiple) - { - sbvmyFrame.sizeClass = [sbv myBestSizeClass:sizeClass]; //因为sizeThatFits执行后会还原,所以这里要重新设置 - sbvsc = sbvmyFrame.sizeClass; - } - } + //只要同时设置了左右边距且宽度优先级很低则把宽度值置空 + if (subviewTraits.leadingPosInner.val != nil && + subviewTraits.trailingPosInner.val != nil && + (subviewTraits.widthSizeInner.priority == MyPriority_Low || !layoutTraits.widthSizeInner.wrapVal)) + [subviewTraits.widthSizeInner _myEqualTo:nil]; + + //只要同时设置了上下边距且高度优先级很低则把高度值置空 + if (subviewTraits.topPosInner.val != nil && + subviewTraits.bottomPosInner.val != nil && + (subviewTraits.heightSizeInner.priority == MyPriority_Low || !layoutTraits.heightSizeInner.wrapVal)) { + [subviewTraits.heightSizeInner _myEqualTo:nil]; } } - - - BOOL reCalc = NO; - CGSize maxSize = [self myCalcLayout:&reCalc lsc:lsc selfSize:selfSize]; - - if (lsc.wrapContentWidth || lsc.wrapContentHeight) - { - if (_myCGFloatNotEqual(selfSize.height, maxSize.height) || _myCGFloatNotEqual(selfSize.width, maxSize.width)) - { - - if (lsc.wrapContentWidth) - { - selfSize.width = maxSize.width; - } - - if (lsc.wrapContentHeight) - { - selfSize.height = maxSize.height; + + BOOL reCalcWidth = NO; + BOOL reCalcHeight = NO; + CGSize maxSize = [self myDoLayoutRecalcWidth:&reCalcWidth recalcHeight:&reCalcHeight withContext:context]; + //如果布局视图的尺寸自适应的,这里要调整一下布局视图的尺寸。但是因为有一些子视图的约束是依赖布局视图的而且可能会引发连锁反应,所以这里要再次进行重新布局处理 + if (layoutTraits.widthSizeInner.wrapVal || layoutTraits.heightSizeInner.wrapVal) { + if (_myCGFloatNotEqual(context->selfSize.height, maxSize.height) || _myCGFloatNotEqual(context->selfSize.width, maxSize.width)) { + if (layoutTraits.widthSizeInner.wrapVal) { + context->selfSize.width = maxSize.width; + } + if (layoutTraits.heightSizeInner.wrapVal) { + context->selfSize.height = maxSize.height; } - //如果里面有需要重新计算的就重新计算布局 - if (reCalc) - { - for (UIView *sbv in self.subviews) - { - MyFrame *sbvmyFrame = sbv.myFrame; - //如果是布局视图则不清除尺寸,其他清除。 - if (isEstimate && [sbv isKindOfClass:[MyBaseLayout class]]) - { - sbvmyFrame.leading = sbvmyFrame.trailing = sbvmyFrame.top = sbvmyFrame.bottom = CGFLOAT_MAX; - } - else - [sbvmyFrame reset]; + if (reCalcWidth || reCalcHeight) { + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + [subviewEngine reset]; } - - [self myCalcLayout:NULL lsc:lsc selfSize:selfSize]; + [self myDoLayoutRecalcWidth:NULL recalcHeight:NULL withContext:context]; } } - } - - - //调整布局视图自己的尺寸。 - [self myAdjustLayoutSelfSize:&selfSize lsc:lsc]; - - //如果是反向则调整所有子视图的左右位置。 - NSArray *sbs2 = [self myGetLayoutSubviews]; - - [self myAdjustSubviewsRTLPos:sbs2 selfWidth:selfSize.width]; - - return [self myAdjustSizeWhenNoSubviews:selfSize sbs:sbs2 lsc:lsc]; - -} --(id)createSizeClassInstance -{ - return [MyRelativeLayoutViewSizeClass new]; + context->subviewEngines = [layoutTraits filterEngines:context->subviewEngines]; + return [self myAdjustLayoutViewSizeWithContext:context]; } +- (id)createSizeClassInstance { + return [MyRelativeLayoutTraits new]; +} - -#pragma mark -- Private Method --(void)myCalcSubViewLeadingTrailing:(UIView*)sbv - sbvsc:(UIView*)sbvsc - lsc:(MyRelativeLayout*)lsc - sbvmyFrame:(MyFrame*)sbvmyFrame - selfSize:(CGSize)selfSize -{ - +#pragma mark-- Private Method +- (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcLeadingTrailingWithContext:(MyLayoutContext *)context { + MyRelativeLayoutTraits *layoutTraits = (MyRelativeLayoutTraits *)context->layoutViewEngine.currentSizeClass; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + //确定宽度,如果跟父一样宽则设置宽度和设置左右值,这时候三个参数设置完毕 //如果和其他视图的宽度一样,则先计算其他视图的宽度并返回其他视图的宽度 //如果没有指定宽度 //检测左右设置。 //如果设置了左右值则计算左右值。然后再计算宽度为两者之间的差 //如果没有设置则宽度为width - + //检测是否有centerX,如果设置了centerX的值为父视图则左右值都设置OK,这时候三个参数完毕 //如果不是父视图则计算其他视图的centerX的值。并返回位置,根据宽度设置后,三个参数完毕。 - + //如果没有设置则计算左边的位置。 - + //如果设置了左边,计算左边的值, 右边的值确定。 - + //如果设置右边,则计算右边的值,左边的值确定。 - + //如果都没有设置则,左边为左边距,右边为左边+宽度。 - + //左右和宽度设置完毕。 - - - - if (sbvmyFrame.leading != CGFLOAT_MAX && sbvmyFrame.trailing != CGFLOAT_MAX && sbvmyFrame.width != CGFLOAT_MAX) + + if (subviewEngine.leading != CGFLOAT_MAX && subviewEngine.trailing != CGFLOAT_MAX && subviewEngine.width != CGFLOAT_MAX) { return; - - + } //先检测宽度,如果宽度是父亲的宽度则宽度和左右都确定 - if ([self myCalcWidth:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]) + if ([self mySubviewEngine:subviewEngine calcWidthWithContext:context]) { return; - - - if (sbvsc.centerXPosInner.posRelaVal != nil) - { - UIView *relaView = sbvsc.centerXPosInner.posRelaVal.view; - - sbvmyFrame.leading = [self myCalcSubView:relaView lsc:lsc gravity:sbvsc.centerXPosInner.posRelaVal.pos selfSize:selfSize] - sbvmyFrame.width / 2 + sbvsc.centerXPosInner.absVal; - - if (relaView != nil && relaView != self && [self myIsNoLayoutSubview:relaView]) - { - sbvmyFrame.leading -= sbvsc.centerXPosInner.absVal; - } - - if (sbvmyFrame.leading < 0 && relaView == self && lsc.wrapContentWidth) - sbvmyFrame.leading = 0; - - sbvmyFrame.trailing = sbvmyFrame.leading + sbvmyFrame.width; - } - else if (sbvsc.centerXPosInner.posNumVal != nil) - { - sbvmyFrame.leading = (selfSize.width - lsc.myLayoutLeadingPadding - lsc.myLayoutTrailingPadding - sbvmyFrame.width) / 2 + lsc.myLayoutLeadingPadding + sbvsc.centerXPosInner.absVal; - - if (sbvmyFrame.leading < 0 && lsc.wrapContentWidth) - sbvmyFrame.leading = 0; - - sbvmyFrame.trailing = sbvmyFrame.leading + sbvmyFrame.width; } - else - { - //如果左右都设置了则上上面的calcWidth会直接返回不会进入这个流程。 - if (sbvsc.leadingPosInner.posRelaVal != nil) - { - UIView *relaView = sbvsc.leadingPosInner.posRelaVal.view; - - sbvmyFrame.leading = [self myCalcSubView:relaView lsc:lsc gravity:sbvsc.leadingPosInner.posRelaVal.pos selfSize:selfSize] + sbvsc.leadingPosInner.absVal; - - if (relaView != nil && relaView != self && [self myIsNoLayoutSubview:relaView]) - { - sbvmyFrame.leading -= sbvsc.leadingPosInner.absVal; + + //表示视图数组水平居中 + if (subviewTraits.centerXPosInner.arrayVal != nil) { + //先算出所有关联视图的宽度。再计算出关联视图的左边和右边的绝对值。 + NSArray *anchorArray = subviewTraits.centerXPosInner.arrayVal; + + CGFloat totalWidth = 0.0; + CGFloat totalOffset = 0.0; + + MyLayoutPos *nextAnchor = nil; + for (NSInteger i = anchorArray.count - 1; i >= 0; i--) { + MyLayoutPos *anchor = anchorArray[i]; + MyViewTraits *relativeViewTraits = anchor.view.myEngine.currentSizeClass; + if (![relativeViewTraits invalid]) { + if (totalWidth != 0) { + if (nextAnchor != nil) { + totalOffset += nextAnchor.measure; + } + } + [self mySubviewEngine:anchor.view.myEngine calcWidthWithContext:context]; + totalWidth += anchor.view.myEngine.width; } - - sbvmyFrame.trailing = sbvmyFrame.leading + sbvmyFrame.width; - } - else if (sbvsc.leadingPosInner.posNumVal != nil) - { - sbvmyFrame.leading = sbvsc.leadingPosInner.absVal + lsc.myLayoutLeadingPadding; - sbvmyFrame.trailing = sbvmyFrame.leading + sbvmyFrame.width; + + nextAnchor = anchor; } - else if (sbvsc.trailingPosInner.posRelaVal != nil) - { - UIView *relaView = sbvsc.trailingPosInner.posRelaVal.view; - - - sbvmyFrame.trailing = [self myCalcSubView:relaView lsc:lsc gravity:sbvsc.trailingPosInner.posRelaVal.pos selfSize:selfSize] - sbvsc.trailingPosInner.absVal + sbvsc.leadingPosInner.absVal; - - if (relaView != nil && relaView != self && [self myIsNoLayoutSubview:relaView]) - { - sbvmyFrame.trailing += sbvsc.trailingPosInner.absVal; + + if (![subviewTraits invalid]) { + if (totalWidth != 0.0) { + if (nextAnchor != nil) { + totalOffset += nextAnchor.measure; + } } - - sbvmyFrame.leading = sbvmyFrame.trailing - sbvmyFrame.width; - + + [self mySubviewEngine:subviewEngine calcWidthWithContext:context]; + totalWidth += subviewEngine.width; + totalOffset += subviewTraits.centerXPosInner.measure; } - else if (sbvsc.trailingPosInner.posNumVal != nil) - { - sbvmyFrame.trailing = selfSize.width - lsc.myLayoutTrailingPadding - sbvsc.trailingPosInner.absVal + sbvsc.leadingPosInner.absVal; - sbvmyFrame.leading = sbvmyFrame.trailing - sbvmyFrame.width; + + //所有宽度算出后,再分别设置 + CGFloat leadingOffset = (context->selfSize.width - context->paddingLeading - context->paddingTrailing - totalWidth - totalOffset) / 2; + id prevAxis = @(leadingOffset); + [subviewTraits.leadingPos _myEqualTo:prevAxis]; + prevAxis = subviewTraits.trailingPos; + for (MyLayoutPos *anchor in anchorArray) { + MyViewTraits *relativeViewTraits = anchor.view.myEngine.currentSizeClass; + [[relativeViewTraits.leadingPos _myEqualTo:prevAxis] _myOffset:anchor.measure]; + prevAxis = relativeViewTraits.trailingPos; } - else - { - - sbvmyFrame.leading = sbvsc.leadingPosInner.absVal + lsc.myLayoutLeadingPadding; - sbvmyFrame.trailing = sbvmyFrame.leading + sbvmyFrame.width; + } + + if (subviewTraits.centerXPosInner.anchorVal != nil) { + UIView *relativeView = subviewTraits.centerXPosInner.anchorVal.view; + MyLayoutEngine *relativeViewEngine = relativeView.myEngine; + + subviewEngine.leading = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.centerXPosInner.anchorVal.anchorType withContext:context] - subviewEngine.width / 2 + subviewTraits.centerXPosInner.measure; + + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + subviewEngine.leading -= subviewTraits.centerXPosInner.measure; + } + if (subviewEngine.leading < 0.0 && relativeView == self && layoutTraits.widthSizeInner.wrapVal) { + subviewEngine.leading = 0.0; + } + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width; + } else if (subviewTraits.centerXPosInner.mostVal != nil) { + subviewEngine.leading = subviewTraits.centerXPosInner.mostVal.doubleValue - subviewEngine.width / 2 + subviewTraits.centerXPosInner.measure; + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width; + } else if (subviewTraits.centerXPosInner.numberVal != nil) { + subviewEngine.leading = (context->selfSize.width - context->paddingLeading - context->paddingTrailing - subviewEngine.width) / 2 + context->paddingLeading + subviewTraits.centerXPosInner.measure; + + if (subviewEngine.leading < 0.0 && layoutTraits.widthSizeInner.wrapVal) { + subviewEngine.leading = 0.0; + } + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width; + } else { + //如果左右都设置了则上上面的calcWidth会直接返回不会进入这个流程。 + if (subviewTraits.leadingPosInner.anchorVal != nil) { + UIView *relativeView = subviewTraits.leadingPosInner.anchorVal.view; + MyLayoutEngine *relativeViewEngine = relativeView.myEngine; + + subviewEngine.leading = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.leadingPosInner.anchorVal.anchorType withContext:context] + subviewTraits.leadingPosInner.measure; + + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + subviewEngine.leading -= subviewTraits.leadingPosInner.measure; + } + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width + subviewTraits.trailingPosInner.measure; + } else if (subviewTraits.leadingPosInner.mostVal != nil) { + subviewEngine.leading = subviewTraits.leadingPosInner.mostVal.doubleValue + subviewTraits.leadingPosInner.measure; + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width + subviewTraits.trailingPosInner.measure; + } else if (subviewTraits.leadingPosInner.numberVal != nil) { + subviewEngine.leading = subviewTraits.leadingPosInner.measure + context->paddingLeading; + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width + subviewTraits.trailingPosInner.measure; + } else if (subviewTraits.trailingPosInner.anchorVal != nil) { + UIView *relativeView = subviewTraits.trailingPosInner.anchorVal.view; + MyLayoutEngine *relativeViewEngine = relativeView.myEngine; + subviewEngine.trailing = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.trailingPosInner.anchorVal.anchorType withContext:context] - subviewTraits.trailingPosInner.measure + subviewTraits.leadingPosInner.measure; + + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + subviewEngine.trailing += subviewTraits.trailingPosInner.measure; + } + subviewEngine.leading = subviewEngine.trailing - subviewEngine.width; + } else if (subviewTraits.trailingPosInner.mostVal != nil) { + subviewEngine.trailing = subviewTraits.trailingPosInner.mostVal.doubleValue - subviewTraits.trailingPosInner.measure + subviewTraits.leadingPosInner.measure; + subviewEngine.leading = subviewEngine.trailing - subviewEngine.width; + } else if (subviewTraits.trailingPosInner.numberVal != nil) { + subviewEngine.trailing = context->selfSize.width - subviewTraits.trailingPosInner.measure - context->paddingTrailing; + subviewEngine.leading = subviewEngine.trailing - subviewEngine.width; + } else { + subviewEngine.leading = subviewTraits.leadingPosInner.measure + context->paddingLeading; + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width; } - } - + //这里要更新左边最小和右边最大约束的情况。 - MyLayoutPos *lBoundPos = sbvsc.leadingPosInner.lBoundValInner; - MyLayoutPos *uBoundPos = sbvsc.trailingPosInner.uBoundValInner; - - if (lBoundPos.posRelaVal != nil && uBoundPos.posRelaVal != nil) - { + MyLayoutPos *lBoundAnchor = subviewTraits.leadingPosInner.lBoundValInner; + MyLayoutPos *uBoundAnchor = subviewTraits.trailingPosInner.uBoundValInner; + UIView *lBoundRelativeView = lBoundAnchor.anchorVal.view; + UIView *uBoundRelativeView = uBoundAnchor.anchorVal.view; + MyLayoutEngine *lBoundRelativeViewEngine = lBoundRelativeView.myEngine; + MyLayoutEngine *uBoundRelativeViewEngine = uBoundRelativeView.myEngine; + + if (lBoundAnchor.anchorVal != nil && uBoundAnchor.anchorVal != nil) { //让宽度缩小并在最小和最大的中间排列。 - CGFloat minLeading = [self myCalcSubView:lBoundPos.posRelaVal.view lsc:lsc gravity:lBoundPos.posRelaVal.pos selfSize:selfSize] + lBoundPos.offsetVal; - - CGFloat maxTrailing = [self myCalcSubView:uBoundPos.posRelaVal.view lsc:lsc gravity:uBoundPos.posRelaVal.pos selfSize:selfSize] - uBoundPos.offsetVal; - - //用maxRight减去minLeft得到的宽度再减去视图的宽度,然后让其居中。。如果宽度超过则缩小视图的宽度。 - CGFloat intervalWidth = maxTrailing - minLeading; - if (_myCGFloatLess(intervalWidth, sbvmyFrame.width)) - { - sbvmyFrame.width = intervalWidth; - sbvmyFrame.leading = minLeading; + CGFloat minLeading = [self mySubviewEngine:lBoundRelativeViewEngine getMeasureFromAnchorType:lBoundAnchor.anchorVal.anchorType withContext:context] + lBoundAnchor.offsetVal; + if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentSizeClass invalid]) { + minLeading -= lBoundAnchor.offsetVal; } - else - { - sbvmyFrame.leading = (intervalWidth - sbvmyFrame.width) / 2 + minLeading; + CGFloat maxTrailing = [self mySubviewEngine:uBoundRelativeViewEngine getMeasureFromAnchorType:uBoundAnchor.anchorVal.anchorType withContext:context] - uBoundAnchor.offsetVal; + if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentSizeClass invalid]) { + maxTrailing += uBoundAnchor.offsetVal; } - - sbvmyFrame.trailing = sbvmyFrame.leading + sbvmyFrame.width; - - - } - else if (lBoundPos.posRelaVal != nil) - { + //用maxRight减去minLeft得到的宽度再减去视图的宽度,然后让其居中。。如果宽度超过则缩小视图的宽度。 + CGFloat intervalWidth = maxTrailing - minLeading; + if (_myCGFloatLess(intervalWidth, subviewEngine.width)) { + subviewEngine.width = intervalWidth; + subviewEngine.leading = minLeading; + } else { + subviewEngine.leading = (intervalWidth - subviewEngine.width) / 2 + minLeading; + } + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width; + } else if (lBoundAnchor.anchorVal != nil) { //得到左边的最小位置。如果当前的左边距小于这个位置则缩小视图的宽度。 - CGFloat minLeading = [self myCalcSubView:lBoundPos.posRelaVal.view lsc:lsc gravity:lBoundPos.posRelaVal.pos selfSize:selfSize] + lBoundPos.offsetVal; - - - if (_myCGFloatLess(sbvmyFrame.leading, minLeading)) - { - sbvmyFrame.leading = minLeading; - sbvmyFrame.width = sbvmyFrame.trailing - sbvmyFrame.leading; + CGFloat minLeading = [self mySubviewEngine:lBoundRelativeViewEngine getMeasureFromAnchorType:lBoundAnchor.anchorVal.anchorType withContext:context] + lBoundAnchor.offsetVal; + if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentSizeClass invalid]) { + minLeading -= lBoundAnchor.offsetVal; } - } - else if (uBoundPos.posRelaVal != nil) - { + if (_myCGFloatLess(subviewEngine.leading, minLeading)) { + subviewEngine.leading = minLeading; + subviewEngine.width = subviewEngine.trailing - subviewEngine.leading; + } + } else if (uBoundAnchor.anchorVal != nil) { //得到右边的最大位置。如果当前的右边距大于了这个位置则缩小视图的宽度。 - CGFloat maxTrailing = [self myCalcSubView:uBoundPos.posRelaVal.view lsc:lsc gravity:uBoundPos.posRelaVal.pos selfSize:selfSize] - uBoundPos.offsetVal; - - if (_myCGFloatGreat(sbvmyFrame.trailing, maxTrailing)) - { - sbvmyFrame.trailing = maxTrailing; - sbvmyFrame.width = sbvmyFrame.trailing - sbvmyFrame.leading; + CGFloat maxTrailing = [self mySubviewEngine:uBoundRelativeViewEngine getMeasureFromAnchorType:uBoundAnchor.anchorVal.anchorType withContext:context] - uBoundAnchor.offsetVal; + if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentSizeClass invalid]) { + maxTrailing += uBoundAnchor.offsetVal; + } + if (_myCGFloatGreat(subviewEngine.trailing, maxTrailing)) { + subviewEngine.trailing = maxTrailing; + subviewEngine.width = subviewEngine.trailing - subviewEngine.leading; } } - - } --(void)myCalcSubViewTopBottom:(UIView*)sbv sbvsc:(UIView*)sbvsc lsc:(MyRelativeLayout*)lsc sbvmyFrame:(MyFrame*)sbvmyFrame selfSize:(CGSize)selfSize -{ - +- (void)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcTopBottomWithContext:(MyLayoutContext *)context { + + MyRelativeLayoutTraits *layoutTraits = (MyRelativeLayoutTraits *)context->layoutViewEngine.currentSizeClass; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + - if (sbvmyFrame.top != CGFLOAT_MAX && sbvmyFrame.bottom != CGFLOAT_MAX && sbvmyFrame.height != CGFLOAT_MAX) + if (subviewEngine.top != CGFLOAT_MAX && subviewEngine.bottom != CGFLOAT_MAX && subviewEngine.height != CGFLOAT_MAX) { return; - - + } //先检测高度,如果高度是父亲的高度则高度和上下都确定 - if ([self myCalcHeight:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]) + if ([self mySubviewEngine:subviewEngine calcHeightWithContext:context]) { return; - - if (sbvsc.baselinePosInner.posRelaVal != nil) - { - //得到基线的位置。基线的位置等于top + (子视图的高度 - 字体的高度) / 2 + 字体基线以上的高度。 - UIFont *sbvFont = [self myGetSubviewFont:sbv]; - - if (sbvFont != nil) - { - //得到基线的位置。 - UIView *relaView = sbvsc.baselinePosInner.posRelaVal.view; - sbvmyFrame.top = [self myCalcSubView:relaView lsc:lsc gravity:sbvsc.baselinePosInner.posRelaVal.pos selfSize:selfSize] - sbvFont.ascender - (sbvmyFrame.height - sbvFont.lineHeight) / 2 + sbvsc.baselinePosInner.absVal; - - if (relaView != nil && relaView != self && [self myIsNoLayoutSubview:relaView]) - { - sbvmyFrame.top -= sbvsc.baselinePosInner.absVal; - } - } - else - { - sbvmyFrame.top = lsc.topPadding + sbvsc.baselinePosInner.absVal; - } - - sbvmyFrame.bottom = sbvmyFrame.top + sbvmyFrame.height; - } - else if (sbvsc.baselinePosInner.posNumVal != nil) - { - UIFont *sbvFont = [self myGetSubviewFont:sbv]; - - if (sbvFont != nil) - { - //根据基线位置反退顶部位置。 - sbvmyFrame.top = lsc.topPadding + sbvsc.baselinePosInner.absVal - sbvFont.ascender - (sbvmyFrame.height - sbvFont.lineHeight) / 2; + + //表示视图数组垂直居中 + if (subviewTraits.centerYPosInner.arrayVal != nil) { + NSArray *anchorArray = subviewTraits.centerYPosInner.arrayVal; + + CGFloat totalHeight = 0.0; + CGFloat totalOffset = 0.0; + + MyLayoutPos *nextAnchor = nil; + for (NSInteger i = anchorArray.count - 1; i >= 0; i--) { + MyLayoutPos *anchor = anchorArray[i]; + MyViewTraits *relativeViewTraits = anchor.view.myEngine.currentSizeClass; + if (![relativeViewTraits invalid]) { + if (totalHeight != 0.0) { + if (nextAnchor != nil) { + totalOffset += nextAnchor.measure; + } + } + [self mySubviewEngine:anchor.view.myEngine calcHeightWithContext:context]; + totalHeight += anchor.view.myEngine.height; + } + + nextAnchor = anchor; } - else - { - sbvmyFrame.top = lsc.topPadding + sbvsc.baselinePosInner.absVal; + + if (![subviewTraits invalid]) { + if (totalHeight != 0.0) { + if (nextAnchor != nil) { + totalOffset += nextAnchor.measure; + } + } + [self mySubviewEngine:subviewEngine calcHeightWithContext:context]; + totalHeight += subviewEngine.height; + totalOffset += subviewTraits.centerYPosInner.measure; } - - sbvmyFrame.bottom = sbvmyFrame.top + sbvmyFrame.height; - } - else if (sbvsc.centerYPosInner.posRelaVal != nil) - { - UIView *relaView = sbvsc.centerYPosInner.posRelaVal.view; - - sbvmyFrame.top = [self myCalcSubView:relaView lsc:lsc gravity:sbvsc.centerYPosInner.posRelaVal.pos selfSize:selfSize] - sbvmyFrame.height / 2 + sbvsc.centerYPosInner.absVal; - - - if (relaView != nil && relaView != self && [self myIsNoLayoutSubview:relaView]) - { - sbvmyFrame.top -= sbvsc.centerYPosInner.absVal; + //所有高度算出后,再分别设置 + CGFloat topOffset = (context->selfSize.height - context->paddingTop - context->paddingBottom - totalHeight - totalOffset) / 2; + id prevAxis = @(topOffset); + [subviewTraits.topPos _myEqualTo:prevAxis]; + prevAxis = subviewTraits.bottomPos; + for (MyLayoutPos *anchor in anchorArray) { + MyViewTraits *relativeViewTraits = anchor.view.myEngine.currentSizeClass; + [[relativeViewTraits.topPos _myEqualTo:prevAxis] _myOffset:anchor.measure]; + prevAxis = relativeViewTraits.bottomPos; } - - if (sbvmyFrame.top < 0 && relaView == self && lsc.wrapContentHeight) - sbvmyFrame.top = 0; - - sbvmyFrame.bottom = sbvmyFrame.top + sbvmyFrame.height; - } - else if (sbvsc.centerYPosInner.posNumVal != nil) - { - sbvmyFrame.top = (selfSize.height - lsc.myLayoutTopPadding - lsc.myLayoutBottomPadding - sbvmyFrame.height) / 2 + lsc.myLayoutTopPadding + sbvsc.centerYPosInner.absVal; - - if (sbvmyFrame.top < 0 && lsc.wrapContentHeight) - sbvmyFrame.top = 0; - - sbvmyFrame.bottom = sbvmyFrame.top + sbvmyFrame.height; } - else - { - if (sbvsc.topPosInner.posRelaVal != nil) - { - UIView *relaView = sbvsc.topPosInner.posRelaVal.view; - - sbvmyFrame.top = [self myCalcSubView:relaView lsc:lsc gravity:sbvsc.topPosInner.posRelaVal.pos selfSize:selfSize] + sbvsc.topPosInner.absVal; - - if (relaView != nil && relaView != self && [self myIsNoLayoutSubview:relaView]) - { - sbvmyFrame.top -= sbvsc.topPosInner.absVal; + + if (subviewTraits.baselinePosInner.anchorVal != nil) { + //得到基线的位置。基线的位置等于top + (子视图的高度 - 字体的高度) / 2 + 字体基线以上的高度。 + UIFont *subviewFont = [self myGetSubviewFont:subviewTraits.view]; + + if (subviewFont != nil) { + //得到基线的位置。 + UIView *relativeView = subviewTraits.baselinePosInner.anchorVal.view; + MyLayoutEngine *relativeViewEngine = relativeView.myEngine; + subviewEngine.top = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.baselinePosInner.anchorVal.anchorType withContext:context] - subviewFont.ascender - (subviewEngine.height - subviewFont.lineHeight) / 2 + subviewTraits.baselinePosInner.measure; + + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + subviewEngine.top -= subviewTraits.baselinePosInner.measure; } - - sbvmyFrame.bottom = sbvmyFrame.top + sbvmyFrame.height; + } else { + subviewEngine.top = context->paddingTop + subviewTraits.baselinePosInner.measure; } - else if (sbvsc.topPosInner.posNumVal != nil) - { - sbvmyFrame.top = sbvsc.topPosInner.absVal + lsc.myLayoutTopPadding; - sbvmyFrame.bottom = sbvmyFrame.top + sbvmyFrame.height; + + subviewEngine.bottom = subviewEngine.top + subviewEngine.height; + } else if (subviewTraits.baselinePosInner.mostVal != nil) { + UIFont *subviewFont = [self myGetSubviewFont:subviewTraits.view]; + if (subviewFont != nil) { + subviewEngine.top = subviewTraits.baselinePosInner.mostVal.doubleValue + subviewTraits.baselinePosInner.measure - subviewFont.ascender - (subviewEngine.height - subviewFont.lineHeight) / 2; + } else { + subviewEngine.top = subviewTraits.baselinePosInner.mostVal.doubleValue + subviewTraits.baselinePosInner.measure; } - else if (sbvsc.bottomPosInner.posRelaVal != nil) - { - UIView *relaView = sbvsc.bottomPosInner.posRelaVal.view; - - sbvmyFrame.bottom = [self myCalcSubView:relaView lsc:lsc gravity:sbvsc.bottomPosInner.posRelaVal.pos selfSize:selfSize] - sbvsc.bottomPosInner.absVal + sbvsc.topPosInner.absVal; - - if (relaView != nil && relaView != self && [self myIsNoLayoutSubview:relaView]) - { - sbvmyFrame.bottom += sbvsc.bottomPosInner.absVal; - } - - sbvmyFrame.top = sbvmyFrame.bottom - sbvmyFrame.height; - + + subviewEngine.bottom = subviewEngine.top + subviewEngine.height; + } else if (subviewTraits.baselinePosInner.numberVal != nil) { + UIFont *subviewFont = [self myGetSubviewFont:subviewTraits.view]; + + if (subviewFont != nil) { + //根据基线位置反退顶部位置。 + subviewEngine.top = context->paddingTop + subviewTraits.baselinePosInner.measure - subviewFont.ascender - (subviewEngine.height - subviewFont.lineHeight) / 2; + } else { + subviewEngine.top = context->paddingTop + subviewTraits.baselinePosInner.measure; + } + subviewEngine.bottom = subviewEngine.top + subviewEngine.height; + } else if (subviewTraits.centerYPosInner.anchorVal != nil) { + UIView *relativeView = subviewTraits.centerYPosInner.anchorVal.view; + MyLayoutEngine *relativeViewEngine = relativeView.myEngine; + + subviewEngine.top = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType :subviewTraits.centerYPosInner.anchorVal.anchorType withContext:context] - subviewEngine.height / 2 + subviewTraits.centerYPosInner.measure; + + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + subviewEngine.top -= subviewTraits.centerYPosInner.measure; } - else if (sbvsc.bottomPosInner.posNumVal != nil) - { - if (selfSize.height == 0 && lsc.wrapContentHeight) - { - sbvmyFrame.top = lsc.myLayoutTopPadding; - sbvmyFrame.bottom = sbvmyFrame.top + sbvmyFrame.height; - } - else - { - - sbvmyFrame.bottom = selfSize.height - sbvsc.bottomPosInner.absVal - lsc.myLayoutBottomPadding + sbvsc.topPosInner.absVal; - sbvmyFrame.top = sbvmyFrame.bottom - sbvmyFrame.height; - } + if (subviewEngine.top < 0.0 && relativeView == self && layoutTraits.heightSizeInner.wrapVal) { + subviewEngine.top = 0.0; + } + subviewEngine.bottom = subviewEngine.top + subviewEngine.height; + } else if (subviewTraits.centerYPosInner.mostVal != nil) { + subviewEngine.top = subviewTraits.centerYPosInner.mostVal.doubleValue + subviewTraits.centerYPosInner.measure - subviewEngine.height / 2; + + if (subviewEngine.top < 0.0 && layoutTraits.heightSizeInner.wrapVal) { + subviewEngine.top = 0.0; } - else - { - sbvmyFrame.top = sbvsc.topPosInner.absVal + lsc.myLayoutTopPadding; - sbvmyFrame.bottom = sbvmyFrame.top + sbvmyFrame.height; + subviewEngine.bottom = subviewEngine.top + subviewEngine.height; + } else if (subviewTraits.centerYPosInner.numberVal != nil) { + subviewEngine.top = (context->selfSize.height - context->paddingTop - context->paddingBottom - subviewEngine.height) / 2 + context->paddingTop + subviewTraits.centerYPosInner.measure; + + if (subviewEngine.top < 0.0 && layoutTraits.heightSizeInner.wrapVal) { + subviewEngine.top = 0.0; + } + subviewEngine.bottom = subviewEngine.top + subviewEngine.height; + } else { + if (subviewTraits.topPosInner.anchorVal != nil) { + UIView *relativeView = subviewTraits.topPosInner.anchorVal.view; + MyLayoutEngine *relativeViewEngine = relativeView.myEngine; + subviewEngine.top = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.topPosInner.anchorVal.anchorType withContext:context] + subviewTraits.topPosInner.measure; + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + subviewEngine.top -= subviewTraits.topPosInner.measure; + } + subviewEngine.bottom = subviewEngine.top + subviewEngine.height + subviewTraits.bottomPosInner.measure; + } else if (subviewTraits.topPosInner.mostVal != nil) { + subviewEngine.top = subviewTraits.topPosInner.mostVal.doubleValue + subviewTraits.topPosInner.measure; + subviewEngine.bottom = subviewEngine.top + subviewEngine.height + subviewTraits.bottomPosInner.measure; + } else if (subviewTraits.topPosInner.numberVal != nil) { + subviewEngine.top = subviewTraits.topPosInner.measure + context->paddingTop; + subviewEngine.bottom = subviewEngine.top + subviewEngine.height + subviewTraits.bottomPosInner.measure; + } else if (subviewTraits.bottomPosInner.anchorVal != nil) { + UIView *relativeView = subviewTraits.bottomPosInner.anchorVal.view; + MyLayoutEngine *relativeViewEngine = relativeView.myEngine; + subviewEngine.bottom = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.bottomPosInner.anchorVal.anchorType withContext:context] - subviewTraits.bottomPosInner.measure + subviewTraits.topPosInner.measure; + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + subviewEngine.bottom += subviewTraits.bottomPosInner.measure; + } + subviewEngine.top = subviewEngine.bottom - subviewEngine.height; + } else if (subviewTraits.bottomPosInner.mostVal != nil) { + subviewEngine.bottom = subviewTraits.bottomPosInner.mostVal.doubleValue - subviewTraits.bottomPosInner.measure + subviewTraits.topPosInner.measure; + subviewEngine.top = subviewEngine.bottom - subviewEngine.height; + } else if (subviewTraits.bottomPosInner.numberVal != nil) { + subviewEngine.bottom = context->selfSize.height - subviewTraits.bottomPosInner.measure - context->paddingBottom; + subviewEngine.top = subviewEngine.bottom - subviewEngine.height; + } else { + subviewEngine.top = subviewTraits.topPosInner.measure + context->paddingTop; + subviewEngine.bottom = subviewEngine.top + subviewEngine.height; } } - + //这里要更新上边最小和下边最大约束的情况。 - if (sbvsc.topPosInner.lBoundValInner.posRelaVal != nil && sbvsc.bottomPosInner.uBoundValInner.posRelaVal != nil) - { + MyLayoutPos *lBoundAnchor = subviewTraits.topPosInner.lBoundValInner; + MyLayoutPos *uBoundAnchor = subviewTraits.bottomPosInner.uBoundValInner; + UIView *lBoundRelativeView = lBoundAnchor.anchorVal.view; + UIView *uBoundRelativeView = uBoundAnchor.anchorVal.view; + MyLayoutEngine *lBoundRelativeViewEngine = lBoundRelativeView.myEngine; + MyLayoutEngine *uBoundRelativeViewEngine = uBoundRelativeView.myEngine; + + + if (lBoundAnchor.anchorVal != nil && uBoundAnchor.anchorVal != nil) { //让宽度缩小并在最小和最大的中间排列。 - CGFloat minTop = [self myCalcSubView:sbvsc.topPosInner.lBoundValInner.posRelaVal.view lsc:lsc gravity:sbvsc.topPosInner.lBoundValInner.posRelaVal.pos selfSize:selfSize] + sbvsc.topPosInner.lBoundValInner.offsetVal; - - CGFloat maxBottom = [self myCalcSubView:sbvsc.bottomPosInner.uBoundValInner.posRelaVal.view lsc:lsc gravity:sbvsc.bottomPosInner.uBoundValInner.posRelaVal.pos selfSize:selfSize] - sbvsc.bottomPosInner.uBoundValInner.offsetVal; - - //用maxRight减去minLeft得到的宽度再减去视图的宽度,然后让其居中。。如果宽度超过则缩小视图的宽度。 - if (_myCGFloatLess(maxBottom - minTop, sbvmyFrame.height)) - { - sbvmyFrame.height = maxBottom - minTop; - sbvmyFrame.top = minTop; + CGFloat minTop = [self mySubviewEngine:lBoundRelativeViewEngine getMeasureFromAnchorType:lBoundAnchor.anchorVal.anchorType withContext:context] + lBoundAnchor.offsetVal; + if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentSizeClass invalid]) { + minTop -= lBoundAnchor.offsetVal; } - else - { - sbvmyFrame.top = (maxBottom - minTop - sbvmyFrame.height) / 2 + minTop; + CGFloat maxBottom = [self mySubviewEngine:uBoundRelativeViewEngine getMeasureFromAnchorType:uBoundAnchor.anchorVal.anchorType withContext:context] - uBoundAnchor.offsetVal; + if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentSizeClass invalid]) { + maxBottom += uBoundAnchor.offsetVal; } - - sbvmyFrame.bottom = sbvmyFrame.top + sbvmyFrame.height; - - - } - else if (sbvsc.topPosInner.lBoundValInner.posRelaVal != nil) - { + //用maxRight减去minLeft得到的宽度再减去视图的宽度,然后让其居中。。如果宽度超过则缩小视图的宽度。 + if (_myCGFloatLess(maxBottom - minTop, subviewEngine.height)) { + subviewEngine.height = maxBottom - minTop; + subviewEngine.top = minTop; + } else { + subviewEngine.top = (maxBottom - minTop - subviewEngine.height) / 2 + minTop; + } + subviewEngine.bottom = subviewEngine.top + subviewEngine.height; + } else if (lBoundAnchor.anchorVal != nil) { //得到左边的最小位置。如果当前的左边距小于这个位置则缩小视图的宽度。 - CGFloat minTop = [self myCalcSubView:sbvsc.topPosInner.lBoundValInner.posRelaVal.view lsc:lsc gravity:sbvsc.topPosInner.lBoundValInner.posRelaVal.pos selfSize:selfSize] + sbvsc.topPosInner.lBoundValInner.offsetVal; - - if (_myCGFloatLess(sbvmyFrame.top, minTop)) - { - sbvmyFrame.top = minTop; - sbvmyFrame.height = sbvmyFrame.bottom - sbvmyFrame.top; + CGFloat minTop = [self mySubviewEngine:lBoundRelativeViewEngine getMeasureFromAnchorType:lBoundAnchor.anchorVal.anchorType withContext:context] + lBoundAnchor.offsetVal; + if (lBoundRelativeView != nil && lBoundRelativeView != self && [lBoundRelativeViewEngine.currentSizeClass invalid]) { + minTop -= lBoundAnchor.offsetVal; } - - } - else if (sbvsc.bottomPosInner.uBoundValInner.posRelaVal != nil) - { + if (_myCGFloatLess(subviewEngine.top, minTop)) { + subviewEngine.top = minTop; + subviewEngine.height = subviewEngine.bottom - subviewEngine.top; + } + + } else if (uBoundAnchor.anchorVal != nil) { //得到右边的最大位置。如果当前的右边距大于了这个位置则缩小视图的宽度。 - CGFloat maxBottom = [self myCalcSubView:sbvsc.bottomPosInner.uBoundValInner.posRelaVal.view lsc:lsc gravity:sbvsc.bottomPosInner.uBoundValInner.posRelaVal.pos selfSize:selfSize] - sbvsc.bottomPosInner.uBoundValInner.offsetVal; - if (_myCGFloatGreat(sbvmyFrame.bottom, maxBottom)) - { - sbvmyFrame.bottom = maxBottom; - sbvmyFrame.height = sbvmyFrame.bottom - sbvmyFrame.top; + CGFloat maxBottom = [self mySubviewEngine:uBoundRelativeViewEngine getMeasureFromAnchorType:uBoundAnchor.anchorVal.anchorType withContext:context] - uBoundAnchor.offsetVal; + if (uBoundRelativeView != nil && uBoundRelativeView != self && [uBoundRelativeViewEngine.currentSizeClass invalid]) { + maxBottom += uBoundAnchor.offsetVal; + } + if (_myCGFloatGreat(subviewEngine.bottom, maxBottom)) { + subviewEngine.bottom = maxBottom; + subviewEngine.height = subviewEngine.bottom - subviewEngine.top; } - } - - } - - --(CGFloat)myCalcSubView:(UIView*)sbv lsc:(MyRelativeLayout*)lsc gravity:(MyGravity)gravity selfSize:(CGSize)selfSize -{ - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; +- (CGFloat)mySubviewEngine:(MyLayoutEngine *)subviewEngine getMeasureFromAnchorType:(MyLayoutAnchorType)anchorType withContext:(MyLayoutContext *)context{ - switch (gravity) { - case MyGravity_Horz_Leading: - { - if (sbv == self || sbv == nil) - return lsc.myLayoutLeadingPadding; - - - if (sbvmyFrame.leading != CGFLOAT_MAX) - return sbvmyFrame.leading; - - [self myCalcSubViewLeadingTrailing:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - return sbvmyFrame.leading; - - } - break; - case MyGravity_Horz_Trailing: - { - if (sbv == self || sbv == nil) - return selfSize.width - lsc.myLayoutTrailingPadding; - - if (sbvmyFrame.trailing != CGFLOAT_MAX) - return sbvmyFrame.trailing; - - [self myCalcSubViewLeadingTrailing:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - return sbvmyFrame.trailing; - - } - break; - case MyGravity_Vert_Top: - { - if (sbv == self || sbv == nil) - return lsc.myLayoutTopPadding; - - - if (sbvmyFrame.top != CGFLOAT_MAX) - return sbvmyFrame.top; - - [self myCalcSubViewTopBottom:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - return sbvmyFrame.top; - - } - break; - case MyGravity_Vert_Bottom: - { - if (sbv == self || sbv == nil) - return selfSize.height - lsc.myLayoutBottomPadding; - - - if (sbvmyFrame.bottom != CGFLOAT_MAX) - return sbvmyFrame.bottom; - - [self myCalcSubViewTopBottom:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - return sbvmyFrame.bottom; - } - break; - case MyGravity_Vert_Baseline: - { - if (sbv == self || sbv == nil) - return lsc.topPadding; - - UIFont *sbvFont = [self myGetSubviewFont:sbv]; - if (sbvFont != nil) - { - if (sbvmyFrame.top == CGFLOAT_MAX || sbvmyFrame.height == CGFLOAT_MAX) - [self myCalcSubViewTopBottom:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - //得到基线的位置。 - return sbvmyFrame.top + (sbvmyFrame.height - sbvFont.lineHeight)/2.0 + sbvFont.ascender; - + UIView *relativeView = subviewEngine.currentSizeClass.view; + + switch (anchorType) { + case MyLayoutAnchorType_Leading: { + if (relativeView == self || relativeView == nil) { + return context->paddingLeading; } - else - { - if (sbvmyFrame.top != CGFLOAT_MAX) - return sbvmyFrame.top; - - [self myCalcSubViewTopBottom:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - return sbvmyFrame.top; + if (subviewEngine.leading != CGFLOAT_MAX) { + return subviewEngine.leading; } - - } - break; - case MyGravity_Horz_Fill: - { - if (sbv == self || sbv == nil) - return selfSize.width - lsc.myLayoutLeadingPadding - lsc.myLayoutTrailingPadding; - - - if (sbvmyFrame.width != CGFLOAT_MAX) - return sbvmyFrame.width; - - [self myCalcSubViewLeadingTrailing:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - return sbvmyFrame.width; - - } - break; - case MyGravity_Vert_Fill: - { - if (sbv == self || sbv == nil) - return selfSize.height - lsc.myLayoutTopPadding - lsc.myLayoutBottomPadding; - - - if (sbvmyFrame.height != CGFLOAT_MAX) - return sbvmyFrame.height; - - [self myCalcSubViewTopBottom:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - return sbvmyFrame.height; - } - break; - case MyGravity_Horz_Center: - { - if (sbv == self || sbv == nil) - return (selfSize.width - lsc.myLayoutLeadingPadding - lsc.myLayoutTrailingPadding) / 2 + lsc.myLayoutLeadingPadding; - - if (sbvmyFrame.leading != CGFLOAT_MAX && sbvmyFrame.trailing != CGFLOAT_MAX && sbvmyFrame.width != CGFLOAT_MAX) - return sbvmyFrame.leading + sbvmyFrame.width / 2; - - [self myCalcSubViewLeadingTrailing:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - return sbvmyFrame.leading + sbvmyFrame.width / 2; - - } - break; - - case MyGravity_Vert_Center: - { - if (sbv == self || sbv == nil) - return (selfSize.height - lsc.myLayoutTopPadding - lsc.myLayoutBottomPadding) / 2 + lsc.myLayoutTopPadding; - - if (sbvmyFrame.top != CGFLOAT_MAX && sbvmyFrame.bottom != CGFLOAT_MAX && sbvmyFrame.height != CGFLOAT_MAX) - return sbvmyFrame.top + sbvmyFrame.height / 2; - - [self myCalcSubViewTopBottom:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - return sbvmyFrame.top + sbvmyFrame.height / 2; - } - break; - default: - break; - } - - return 0; -} + [self mySubviewEngine:subviewEngine calcLeadingTrailingWithContext:context]; + return subviewEngine.leading; + } break; + case MyLayoutAnchorType_Trailing: { + if (relativeView == self || relativeView == nil) { + return context->selfSize.width - context->paddingTrailing; + } + if (subviewEngine.trailing != CGFLOAT_MAX) { + return subviewEngine.trailing; + } + [self mySubviewEngine:subviewEngine calcLeadingTrailingWithContext:context]; --(BOOL)myCalcWidth:(UIView*)sbv sbvsc:(UIView*)sbvsc lsc:(MyRelativeLayout*)lsc sbvmyFrame:(MyFrame*)sbvmyFrame selfSize:(CGSize)selfSize -{ - - if (sbvmyFrame.width == CGFLOAT_MAX) - { - - if (sbvsc.widthSizeInner.dimeRelaVal != nil) - { - - sbvmyFrame.width = [sbvsc.widthSizeInner measureWith:[self myCalcSubView:sbvsc.widthSizeInner.dimeRelaVal.view lsc:lsc gravity:sbvsc.widthSizeInner.dimeRelaVal.dime selfSize:selfSize] ]; - - sbvmyFrame.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:sbvmyFrame.width sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - - } - else if (sbvsc.widthSizeInner.dimeNumVal != nil) - { - sbvmyFrame.width = sbvsc.widthSizeInner.measure; - sbvmyFrame.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:sbvmyFrame.width sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - - } - else; - - if ([self myIsNoLayoutSubview:sbv]) - { - sbvmyFrame.width = 0; - } - - if (sbvsc.leadingPosInner.posVal != nil && sbvsc.trailingPosInner.posVal != nil) - { - if (sbvsc.leadingPosInner.posRelaVal != nil) - sbvmyFrame.leading = [self myCalcSubView:sbvsc.leadingPosInner.posRelaVal.view lsc:lsc gravity:sbvsc.leadingPosInner.posRelaVal.pos selfSize:selfSize] + sbvsc.leadingPosInner.absVal; - else - sbvmyFrame.leading = sbvsc.leadingPosInner.absVal + lsc.myLayoutLeadingPadding; - - if (sbvsc.trailingPosInner.posRelaVal != nil) - sbvmyFrame.trailing = [self myCalcSubView:sbvsc.trailingPosInner.posRelaVal.view lsc:lsc gravity:sbvsc.trailingPosInner.posRelaVal.pos selfSize:selfSize] - sbvsc.trailingPosInner.absVal; - else - sbvmyFrame.trailing = selfSize.width - sbvsc.trailingPosInner.absVal - lsc.myLayoutTrailingPadding; - - sbvmyFrame.width = sbvmyFrame.trailing - sbvmyFrame.leading; - sbvmyFrame.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:sbvmyFrame.width sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - - if ([self myIsNoLayoutSubview:sbv]) - { - sbvmyFrame.width = 0; - sbvmyFrame.trailing = sbvmyFrame.leading + sbvmyFrame.width; + return subviewEngine.trailing; + } break; + case MyLayoutAnchorType_Top: { + if (relativeView == self || relativeView == nil) { + return context->paddingTop; } - - - return YES; - - } - - - if (sbvmyFrame.width == CGFLOAT_MAX) - { - sbvmyFrame.width = CGRectGetWidth(sbv.bounds); - sbvmyFrame.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:sbvmyFrame.width sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - } - } - - if ((sbvsc.widthSizeInner.lBoundValInner != nil && sbvsc.widthSizeInner.lBoundValInner.dimeNumVal.doubleValue != -CGFLOAT_MAX) || - (sbvsc.widthSizeInner.uBoundValInner != nil && sbvsc.widthSizeInner.uBoundValInner.dimeNumVal.doubleValue != CGFLOAT_MAX) ) - { - sbvmyFrame.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:sbvmyFrame.width sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - } - - - return NO; -} + if (subviewEngine.top != CGFLOAT_MAX) { + return subviewEngine.top; + } + [self mySubviewEngine:subviewEngine calcTopBottomWithContext:context]; + return subviewEngine.top; --(BOOL)myCalcHeight:(UIView*)sbv sbvsc:(UIView*)sbvsc lsc:(MyRelativeLayout*)lsc sbvmyFrame:(MyFrame*)sbvmyFrame selfSize:(CGSize)selfSize -{ - - if (sbvmyFrame.height == CGFLOAT_MAX) - { - if (sbvsc.heightSizeInner.dimeRelaVal != nil) - { - - sbvmyFrame.height = [sbvsc.heightSizeInner measureWith:[self myCalcSubView:sbvsc.heightSizeInner.dimeRelaVal.view lsc:lsc gravity:sbvsc.heightSizeInner.dimeRelaVal.dime selfSize:selfSize] ]; - - sbvmyFrame.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:sbvmyFrame.height sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - - } - else if (sbvsc.heightSizeInner.dimeNumVal != nil) - { - sbvmyFrame.height = sbvsc.heightSizeInner.measure; - sbvmyFrame.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:sbvmyFrame.height sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - - } - else; - - if ([self myIsNoLayoutSubview:sbv]) - { - sbvmyFrame.height = 0; - } - - - if (sbvsc.topPosInner.posVal != nil && sbvsc.bottomPosInner.posVal != nil) - { - if (sbvsc.topPosInner.posRelaVal != nil) - sbvmyFrame.top = [self myCalcSubView:sbvsc.topPosInner.posRelaVal.view lsc:lsc gravity:sbvsc.topPosInner.posRelaVal.pos selfSize:selfSize] + sbvsc.topPosInner.absVal; - else - sbvmyFrame.top = sbvsc.topPosInner.absVal + lsc.myLayoutTopPadding; - - if (sbvsc.bottomPosInner.posRelaVal != nil) - sbvmyFrame.bottom = [self myCalcSubView:sbvsc.bottomPosInner.posRelaVal.view lsc:lsc gravity:sbvsc.bottomPosInner.posRelaVal.pos selfSize:selfSize] - sbvsc.bottomPosInner.absVal; - else - sbvmyFrame.bottom = selfSize.height - sbvsc.bottomPosInner.absVal - lsc.myLayoutBottomPadding; - - sbvmyFrame.height = sbvmyFrame.bottom - sbvmyFrame.top; - sbvmyFrame.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:sbvmyFrame.height sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - - if ([self myIsNoLayoutSubview:sbv]) - { - sbvmyFrame.height = 0; - sbvmyFrame.bottom = sbvmyFrame.top + sbvmyFrame.height; + } break; + case MyLayoutAnchorType_Bottom: { + if (relativeView == self || relativeView == nil) { + return context->selfSize.height - context->paddingBottom; } - - - return YES; - - } - - - if (sbvmyFrame.height == CGFLOAT_MAX) - { - sbvmyFrame.height = CGRectGetHeight(sbv.bounds); - - if (sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]] && ![self myIsNoLayoutSubview:sbv]) - { - if (sbvmyFrame.width == CGFLOAT_MAX) - [self myCalcWidth:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - sbvmyFrame.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:sbvmyFrame.width]; + if (subviewEngine.bottom != CGFLOAT_MAX) { + return subviewEngine.bottom; } - - sbvmyFrame.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:sbvmyFrame.height sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - - - } - } - - if ( (sbvsc.heightSizeInner.lBoundValInner != nil && sbvsc.heightSizeInner.lBoundValInner.dimeNumVal.doubleValue != -CGFLOAT_MAX) || - (sbvsc.heightSizeInner.uBoundValInner != nil && sbvsc.heightSizeInner.uBoundValInner.dimeNumVal.doubleValue != CGFLOAT_MAX)) - { - sbvmyFrame.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:sbvmyFrame.height sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - } - - return NO; - -} + [self mySubviewEngine:subviewEngine calcTopBottomWithContext:context]; + return subviewEngine.bottom; + } break; + case MyLayoutAnchorType_Baseline: { + if (relativeView == self || relativeView == nil) { + return context->paddingTop; + } + UIFont *subviewFont = [self myGetSubviewFont:relativeView]; + if (subviewFont != nil) { + if (subviewEngine.top == CGFLOAT_MAX || subviewEngine.height == CGFLOAT_MAX) { + [self mySubviewEngine:subviewEngine calcTopBottomWithContext:context]; + } + //得到基线的位置。 + return subviewEngine.top + (subviewEngine.height - subviewFont.lineHeight) / 2.0 + subviewFont.ascender; + } else { + if (subviewEngine.top != CGFLOAT_MAX) { + return subviewEngine.top; + } + [self mySubviewEngine:subviewEngine calcTopBottomWithContext:context]; --(CGSize)myCalcLayout:(BOOL*)pRecalc lsc:(MyRelativeLayout*)lsc selfSize:(CGSize)selfSize -{ - if (pRecalc != NULL) - *pRecalc = NO; - - - //遍历所有子视图,算出所有宽度和高度根据自身内容确定的子视图的尺寸.以及计算出那些有依赖关系的尺寸限制。。。 - for (UIView *sbv in self.subviews) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - - [self myCalcSizeOfWrapContentSubview:sbv sbvsc:sbvsc sbvmyFrame:sbvmyFrame]; - - if (sbvmyFrame.width != CGFLOAT_MAX) - { - if (sbvsc.widthSizeInner.uBoundValInner.dimeRelaVal != nil && sbvsc.widthSizeInner.uBoundValInner.dimeRelaVal.view != self) - { - [self myCalcWidth:sbvsc.widthSizeInner.uBoundValInner.dimeRelaVal.view - sbvsc:sbvsc.widthSizeInner.uBoundValInner.dimeRelaVal.view.myCurrentSizeClass - lsc:lsc - sbvmyFrame:sbvsc.widthSizeInner.uBoundValInner.dimeRelaVal.view.myFrame - selfSize:selfSize]; + return subviewEngine.top; } - - if (sbvsc.widthSizeInner.lBoundValInner.dimeRelaVal != nil && sbvsc.widthSizeInner.lBoundValInner.dimeRelaVal.view != self) - { - [self myCalcWidth:sbvsc.widthSizeInner.lBoundValInner.dimeRelaVal.view - sbvsc:sbvsc.widthSizeInner.lBoundValInner.dimeRelaVal.view.myCurrentSizeClass - lsc:lsc - sbvmyFrame:sbvsc.widthSizeInner.lBoundValInner.dimeRelaVal.view.myFrame - selfSize:selfSize]; + } break; + case MyLayoutAnchorType_Width: { + if (relativeView == self || relativeView == nil) { + return context->selfSize.width - context->paddingLeading - context->paddingTrailing; } - - sbvmyFrame.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:sbvmyFrame.width sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - } - - if (sbvmyFrame.height != CGFLOAT_MAX) - { - if (sbvsc.heightSizeInner.uBoundValInner.dimeRelaVal != nil && sbvsc.heightSizeInner.uBoundValInner.dimeRelaVal.view != self) - { - [self myCalcHeight:sbvsc.heightSizeInner.uBoundValInner.dimeRelaVal.view - sbvsc:sbvsc.heightSizeInner.uBoundValInner.dimeRelaVal.view.myCurrentSizeClass - lsc:lsc - sbvmyFrame:sbvsc.heightSizeInner.uBoundValInner.dimeRelaVal.view.myFrame - selfSize:selfSize]; + if (subviewEngine.width != CGFLOAT_MAX) { + return subviewEngine.width; } - - if (sbvsc.heightSizeInner.lBoundValInner.dimeRelaVal != nil && sbvsc.heightSizeInner.lBoundValInner.dimeRelaVal.view != self) - { - [self myCalcHeight:sbvsc.heightSizeInner.lBoundValInner.dimeRelaVal.view - sbvsc:sbvsc.heightSizeInner.lBoundValInner.dimeRelaVal.view.myCurrentSizeClass - lsc:lsc - sbvmyFrame:sbvsc.heightSizeInner.lBoundValInner.dimeRelaVal.view.myFrame - selfSize:selfSize]; + [self mySubviewEngine:subviewEngine calcLeadingTrailingWithContext:context]; + return subviewEngine.width; + } break; + case MyLayoutAnchorType_Height: { + if (relativeView == self || relativeView == nil) { + return context->selfSize.height - context->paddingTop - context->paddingBottom; } - - sbvmyFrame.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:sbvmyFrame.height sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - } - + if (subviewEngine.height != CGFLOAT_MAX) { + return subviewEngine.height; + } + [self mySubviewEngine:subviewEngine calcTopBottomWithContext:context]; + + return subviewEngine.height; + } break; + case MyLayoutAnchorType_CenterX: { + if (relativeView == self || relativeView == nil) { + return (context->selfSize.width - context->paddingLeading - context->paddingTrailing) / 2 + context->paddingLeading; + } + if (subviewEngine.leading != CGFLOAT_MAX && subviewEngine.trailing != CGFLOAT_MAX && subviewEngine.width != CGFLOAT_MAX) { + return subviewEngine.leading + subviewEngine.width / 2; + } + [self mySubviewEngine:subviewEngine calcLeadingTrailingWithContext:context]; + return subviewEngine.leading + subviewEngine.width / 2; + + } break; + + case MyLayoutAnchorType_CenterY: { + if (relativeView == self || relativeView == nil) { + return (context->selfSize.height - context->paddingTop - context->paddingBottom) / 2 + context->paddingTop; + } + if (subviewEngine.top != CGFLOAT_MAX && subviewEngine.bottom != CGFLOAT_MAX && subviewEngine.height != CGFLOAT_MAX) { + return subviewEngine.top + subviewEngine.height / 2; + } + [self mySubviewEngine:subviewEngine calcTopBottomWithContext:context]; + return subviewEngine.top + subviewEngine.height / 2; + } break; + default: + break; } + + return 0; +} + +- (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcWidthWithContext:(MyLayoutContext *)context { - //均分宽度和高度。把这部分提出来是为了实现不管数组是哪个视图指定都可以。 - for (UIView *sbv in self.subviews) - { - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - - if (sbvsc.widthSizeInner.dimeArrVal != nil) - { - if (pRecalc != NULL) - *pRecalc = YES; - - NSArray *dimeArray = sbvsc.widthSizeInner.dimeArrVal; - - BOOL isViewHidden = [self myIsNoLayoutSubview:sbv]; - CGFloat totalMulti = isViewHidden ? 0 : sbvsc.widthSizeInner.multiVal; - CGFloat totalAdd = isViewHidden ? 0 : sbvsc.widthSizeInner.addVal; - for (MyLayoutSize *dime in dimeArray) - { - - if (dime.isActive) - { - isViewHidden = [self myIsNoLayoutSubview:dime.view]; - if (!isViewHidden) - { - if (dime.dimeVal != nil) - { - [self myCalcWidth:dime.view - sbvsc:dime.view.myCurrentSizeClass - lsc:lsc - sbvmyFrame:dime.view.myFrame - selfSize:selfSize]; - - totalAdd += -1 * dime.view.myFrame.width; - } - else - { - totalMulti += dime.multiVal; - } - - totalAdd += dime.addVal; + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + BOOL hasMargin = NO; + + if (subviewEngine.width == CGFLOAT_MAX) { + if (subviewTraits.widthSizeInner.arrayVal != nil) { + NSArray *anchorArray = subviewTraits.widthSizeInner.arrayVal; + BOOL invalid = [subviewTraits invalid]; + CGFloat totalMultiple = invalid ? 0.0 : subviewTraits.widthSizeInner.multiVal; + CGFloat totalIncrement = invalid ? 0.0 : subviewTraits.widthSizeInner.addVal; + for (MyLayoutSize *anchor in anchorArray) { + if (anchor.isActive) { + MyLayoutEngine *relativeViewEngine = anchor.view.myEngine; + invalid = [relativeViewEngine.currentSizeClass invalid]; + if (!invalid) { + if (anchor.val != nil) { + [self mySubviewEngine:relativeViewEngine calcWidthWithContext:context]; + + totalIncrement += -1 * relativeViewEngine.width; + } else { + totalMultiple += anchor.multiVal; + } + totalIncrement += anchor.addVal; } } - } - - CGFloat floatingWidth = selfSize.width - lsc.myLayoutLeadingPadding - lsc.myLayoutTrailingPadding + totalAdd; - if ( _myCGFloatLessOrEqual(floatingWidth, 0)) - floatingWidth = 0; - - if (totalMulti != 0) - { - CGFloat tempWidth = _myCGFloatRound(floatingWidth * (sbvsc.widthSizeInner.multiVal / totalMulti)); - - sbvmyFrame.width = [self myValidMeasure:sbvsc.widthSizeInner sbv:sbv calcSize:tempWidth sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - - if ([self myIsNoLayoutSubview:sbv]) - { - sbvmyFrame.width = 0; - } - else - { - floatingWidth -= tempWidth; - totalMulti -= sbvsc.widthSizeInner.multiVal; + + CGFloat spareWidth = context->selfSize.width - context->paddingLeading - context->paddingTrailing + totalIncrement; + if (_myCGFloatLessOrEqual(spareWidth, 0.0)) { + spareWidth = 0.0; + } + if (totalMultiple != 0.0) { + CGFloat tempWidth = _myCGFloatRound(spareWidth * (subviewTraits.widthSizeInner.multiVal / totalMultiple)); + subviewEngine.width = tempWidth; + + if ([subviewTraits invalid]) { + subviewEngine.width = 0.0; + } else { + spareWidth -= tempWidth; + totalMultiple -= subviewTraits.widthSizeInner.multiVal; } - - for (MyLayoutSize *dime in dimeArray) - { - if (dime.isActive && ![self myIsNoLayoutSubview:dime.view]) - { - if (dime.dimeVal == nil) - { - tempWidth = _myCGFloatRound(floatingWidth * (dime.multiVal / totalMulti)); - floatingWidth -= tempWidth; - totalMulti -= dime.multiVal; - dime.view.myFrame.width = tempWidth; + for (MyLayoutSize *anchor in anchorArray) { + MyLayoutEngine *relativeViewEngine = anchor.view.myEngine; + if (anchor.isActive && ![relativeViewEngine.currentSizeClass invalid]) { + if (anchor.val == nil) { + tempWidth = _myCGFloatRound(spareWidth * (anchor.multiVal / totalMultiple)); + spareWidth -= tempWidth; + totalMultiple -= anchor.multiVal; + relativeViewEngine.width = tempWidth; } - - dime.view.myFrame.width = [self myValidMeasure:dime.view.widthSize sbv:dime.view calcSize:dime.view.myFrame.width sbvSize:dime.view.myFrame.frame.size selfLayoutSize:selfSize]; - } - else - { - dime.view.myFrame.width = 0; + relativeViewEngine.width = [self myValidMeasure:anchor subview:anchor.view calcSize:relativeViewEngine.width subviewSize:relativeViewEngine.size selfLayoutSize:context->selfSize]; + } else { + relativeViewEngine.width = 0.0; } } } + } else if (subviewTraits.widthSizeInner.anchorVal != nil) { + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:[self mySubviewEngine:subviewTraits.widthSizeInner.anchorVal.view.myEngine getMeasureFromAnchorType:subviewTraits.widthSizeInner.anchorVal.anchorType withContext:context]]; + } else if (subviewTraits.widthSizeInner.numberVal != nil) { + subviewEngine.width = subviewTraits.widthSizeInner.measure; + } else if (subviewTraits.widthSizeInner.fillVal) { + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:context->selfSize.width - context->paddingLeading - context->paddingTrailing]; + } else if (subviewTraits.widthSizeInner.wrapVal) { + //对于非布局视图进行宽度自适应计算。 + if (![subviewTraits.view isKindOfClass:[MyBaseLayout class]]) { + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:[subviewTraits.view sizeThatFits:CGSizeZero].width]; + } else if (context->isEstimate) { + MyBaseLayout *sublayout = (MyBaseLayout*)subviewTraits.view; + subviewEngine.width = [sublayout sizeThatFits:subviewEngine.size inSizeClass:context->sizeClass].width; + subviewEngine.width = [subviewTraits.widthSizeInner measureWith:subviewEngine.width]; + } } - - if (sbvsc.heightSizeInner.dimeArrVal != nil) - { - if (pRecalc != NULL) - *pRecalc = YES; - - NSArray *dimeArray = sbvsc.heightSizeInner.dimeArrVal; - - BOOL isViewHidden = [self myIsNoLayoutSubview:sbv]; - - CGFloat totalMulti = isViewHidden ? 0 : sbvsc.heightSizeInner.multiVal; - CGFloat totalAdd = isViewHidden ? 0 : sbvsc.heightSizeInner.addVal; - for (MyLayoutSize *dime in dimeArray) - { - if (dime.isActive) - { - isViewHidden = [self myIsNoLayoutSubview:dime.view]; - if (!isViewHidden) - { - if (dime.dimeVal != nil) - { - [self myCalcHeight:dime.view - sbvsc:dime.view.myCurrentSizeClass - lsc:lsc - sbvmyFrame:dime.view.myFrame - selfSize:selfSize]; - - totalAdd += -1 * dime.view.myFrame.height; - } - else - totalMulti += dime.multiVal; - - totalAdd += dime.addVal; - } + + if (subviewEngine.width == CGFLOAT_MAX) { + subviewEngine.width = CGRectGetWidth(subviewTraits.view.bounds); + } + + if (subviewTraits.leadingPosInner.val != nil && subviewTraits.trailingPosInner.val != nil) { + if (subviewTraits.leadingPosInner.anchorVal != nil) { + UIView *relaView = subviewTraits.leadingPosInner.anchorVal.view; + MyLayoutEngine *relativeViewEngine = relaView.myEngine; + subviewEngine.leading = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.leadingPosInner.anchorVal.anchorType withContext:context] + subviewTraits.leadingPosInner.measure; + if (relaView != nil && relaView != self && [relativeViewEngine.currentSizeClass invalid]) { + subviewEngine.leading -= subviewTraits.leadingPosInner.measure; } + } else if (subviewTraits.leadingPosInner.mostVal != nil) { + subviewEngine.leading = subviewTraits.leadingPosInner.mostVal.doubleValue + subviewTraits.leadingPosInner.measure; + } else { + subviewEngine.leading = subviewTraits.leadingPosInner.measure + context->paddingLeading; } - - CGFloat floatingHeight = selfSize.height - lsc.myLayoutTopPadding - lsc.myLayoutBottomPadding + totalAdd; - if (_myCGFloatLessOrEqual(floatingHeight, 0)) - floatingHeight = 0; - - if (totalMulti != 0) - { - CGFloat tempHeight = _myCGFloatRound(floatingHeight * (sbvsc.heightSizeInner.multiVal / totalMulti)); - sbvmyFrame.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:tempHeight sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - - if ([self myIsNoLayoutSubview:sbv]) - { - sbvmyFrame.height = 0; - } - else - { - floatingHeight -= tempHeight; - totalMulti -= sbvsc.heightSizeInner.multiVal; + + if (subviewTraits.trailingPosInner.anchorVal != nil) { + UIView *relativeView = subviewTraits.trailingPosInner.anchorVal.view; + MyLayoutEngine *relativeViewEngine = relativeView.myEngine; + subviewEngine.trailing = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.trailingPosInner.anchorVal.anchorType withContext:context] - subviewTraits.trailingPosInner.measure; + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + subviewEngine.trailing += subviewTraits.trailingPosInner.measure; } - - for (MyLayoutSize *dime in dimeArray) - { - if (dime.isActive && ![self myIsNoLayoutSubview:dime.view]) - { - if (dime.dimeVal == nil) - { - tempHeight = _myCGFloatRound(floatingHeight * (dime.multiVal / totalMulti)); - floatingHeight -= tempHeight; - totalMulti -= dime.multiVal; - dime.view.myFrame.height = tempHeight; + } else if (subviewTraits.trailingPosInner.mostVal != nil) { + subviewEngine.trailing = subviewTraits.trailingPosInner.mostVal.doubleValue - subviewTraits.trailingPosInner.measure; + } else { + subviewEngine.trailing = context->selfSize.width - subviewTraits.trailingPosInner.measure - context->paddingTrailing; + } + + //只有在没有设置宽度时才计算宽度。 + if (subviewTraits.widthSizeInner.val == nil) { + subviewEngine.width = subviewEngine.trailing - subviewEngine.leading; + } else { + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width + subviewTraits.trailingPosInner.measure; + } + hasMargin = YES; + } + + //计算有最大和最小约束的情况。 + if (subviewTraits.widthSizeInner.uBoundValInner.anchorVal != nil && subviewTraits.widthSizeInner.uBoundValInner.anchorVal.view != self) { + [self mySubviewEngine:subviewTraits.widthSizeInner.uBoundValInner.anchorVal.view.myEngine calcWidthWithContext:context]; + } + + if (subviewTraits.widthSizeInner.lBoundValInner.anchorVal != nil && subviewTraits.widthSizeInner.lBoundValInner.anchorVal.view != self) { + [self mySubviewEngine:subviewTraits.widthSizeInner.lBoundValInner.anchorVal.view.myEngine calcWidthWithContext:context]; + } + + subviewEngine.width = [self myValidMeasure:subviewTraits.widthSizeInner subview:subviewTraits.view calcSize:subviewEngine.width subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + + if ([subviewTraits invalid]) { + subviewEngine.width = 0.0; + subviewEngine.trailing = subviewEngine.leading + subviewEngine.width; + } + } + + return hasMargin; +} + +- (BOOL)mySubviewEngine:(MyLayoutEngine *)subviewEngine calcHeightWithContext:(MyLayoutContext *)context{ + + MyViewTraits *subviewTraits = subviewEngine.currentSizeClass; + BOOL hasMargin = NO; + + if (subviewEngine.height == CGFLOAT_MAX) { + if (subviewTraits.heightSizeInner.arrayVal != nil) { + NSArray *anchorArray = subviewTraits.heightSizeInner.arrayVal; + + BOOL invalid = [subviewTraits invalid]; + + CGFloat totalMultiple = invalid ? 0.0 : subviewTraits.heightSizeInner.multiVal; + CGFloat totalIncrement = invalid ? 0.0 : subviewTraits.heightSizeInner.addVal; + for (MyLayoutSize *anchor in anchorArray) { + if (anchor.isActive) { + MyLayoutEngine *relativeViewEngine = anchor.view.myEngine; + invalid = [relativeViewEngine.currentSizeClass invalid]; + if (!invalid) { + if (anchor.val != nil) { + [self mySubviewEngine:relativeViewEngine calcHeightWithContext:context]; + + totalIncrement += -1 * relativeViewEngine.height; + } else { + totalMultiple += anchor.multiVal; } - - dime.view.myFrame.height = [self myValidMeasure:dime.view.heightSize sbv:dime.view calcSize:dime.view.myFrame.height sbvSize:dime.view.myFrame.frame.size selfLayoutSize:selfSize]; - - } - else - { - dime.view.myFrame.height = 0; + totalIncrement += anchor.addVal; } } } - } - - - //表示视图数组水平居中 - if (sbvsc.centerXPosInner.posArrVal != nil) - { - //先算出所有关联视图的宽度。再计算出关联视图的左边和右边的绝对值。 - NSArray *centerArray = sbvsc.centerXPosInner.posArrVal; - - CGFloat totalWidth = 0; - CGFloat totalOffset = 0; - - MyLayoutPos *nextPos = nil; - for (NSInteger i = centerArray.count - 1; i >= 0; i--) - { - MyLayoutPos *pos = centerArray[i]; - if (![self myIsNoLayoutSubview:pos.view]) - { - if (totalWidth != 0) - { - if (nextPos != nil) - totalOffset += nextPos.view.centerXPos.absVal; + + CGFloat spareHeight = context->selfSize.height - context->paddingTop - context->paddingBottom + totalIncrement; + if (_myCGFloatLessOrEqual(spareHeight, 0.0)) { + spareHeight = 0.0; + } + if (totalMultiple != 0.0) { + CGFloat tempHeight = _myCGFloatRound(spareHeight * (subviewTraits.heightSizeInner.multiVal / totalMultiple)); + subviewEngine.height = tempHeight; + if ([subviewTraits invalid]) { + subviewEngine.height = 0.0; + } else { + spareHeight -= tempHeight; + totalMultiple -= subviewTraits.heightSizeInner.multiVal; + } + + for (MyLayoutSize *anchor in anchorArray) { + MyLayoutEngine *relativeViewEngine = anchor.view.myEngine; + if (anchor.isActive && ![relativeViewEngine.currentSizeClass invalid]) { + if (anchor.val == nil) { + tempHeight = _myCGFloatRound(spareHeight * (anchor.multiVal / totalMultiple)); + spareHeight -= tempHeight; + totalMultiple -= anchor.multiVal; + relativeViewEngine.height = tempHeight; + } + relativeViewEngine.height = [self myValidMeasure:anchor subview :anchor.view calcSize:relativeViewEngine.height subviewSize:relativeViewEngine.size selfLayoutSize:context->selfSize]; + } else { + relativeViewEngine.height = 0.0; } - - [self myCalcWidth:pos.view sbvsc:pos.view.myCurrentSizeClass lsc:lsc sbvmyFrame:pos.view.myFrame selfSize:selfSize]; - totalWidth += pos.view.myFrame.width; } - - nextPos = pos; } - - if (![self myIsNoLayoutSubview:sbv]) - { - if (totalWidth != 0) - { - if (nextPos != nil) - totalOffset += nextPos.view.centerXPos.absVal; + } else if (subviewTraits.heightSizeInner.anchorVal != nil) { + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:[self mySubviewEngine:subviewTraits.heightSizeInner.anchorVal.view.myEngine getMeasureFromAnchorType:subviewTraits.heightSizeInner.anchorVal.anchorType withContext:context]]; + } else if (subviewTraits.heightSizeInner.numberVal != nil) { + subviewEngine.height = subviewTraits.heightSizeInner.measure; + } else if (subviewTraits.heightSizeInner.fillVal) { + subviewEngine.height = [subviewTraits.heightSizeInner measureWith:context->selfSize.height - context->paddingTop - context->paddingBottom]; + } else if (subviewTraits.heightSizeInner.wrapVal) { + if (subviewEngine.width == CGFLOAT_MAX) { + [self mySubviewEngine:subviewEngine calcWidthWithContext:context]; } - - [self myCalcWidth:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - totalWidth += sbvmyFrame.width; - totalOffset += sbvsc.centerXPosInner.absVal; - } - - - //所有宽度算出后,再分别设置 - CGFloat leadingOffset = (selfSize.width - lsc.myLayoutLeadingPadding - lsc.myLayoutTrailingPadding - totalWidth - totalOffset) / 2; - leadingOffset += lsc.myLayoutLeadingPadding; - id prev = @(leadingOffset); - [sbvsc.leadingPos __equalTo:prev]; - prev = sbvsc.trailingPos; - for (MyLayoutPos *pos in centerArray) - { - [[pos.view.leadingPos __equalTo:prev] __offset:pos.view.centerXPos.absVal]; - prev = pos.view.trailingPos; - } + subviewEngine.height = [self mySubview:subviewTraits wrapHeightSizeFits:subviewEngine.size withContext:context]; } - - //表示视图数组垂直居中 - if (sbvsc.centerYPosInner.posArrVal != nil) - { - NSArray *centerArray = sbvsc.centerYPosInner.posArrVal; - - CGFloat totalHeight = 0; - CGFloat totalOffset = 0; - - MyLayoutPos *nextPos = nil; - for (NSInteger i = centerArray.count - 1; i >= 0; i--) - { - MyLayoutPos *pos = centerArray[i]; - if (![self myIsNoLayoutSubview:pos.view]) - { - if (totalHeight != 0) - { - if (nextPos != nil) - totalOffset += nextPos.view.centerYPos.absVal; - } - - [self myCalcHeight:pos.view sbvsc:pos.view.myCurrentSizeClass lsc:lsc sbvmyFrame:pos.view.myFrame selfSize:selfSize]; - totalHeight += pos.view.myFrame.height; + + if (subviewEngine.height == CGFLOAT_MAX) { + subviewEngine.height = CGRectGetHeight(subviewTraits.view.bounds); + } + if (subviewTraits.topPosInner.val != nil && subviewTraits.bottomPosInner.val != nil) { + if (subviewTraits.topPosInner.anchorVal != nil) { + UIView *relativeView = subviewTraits.topPosInner.anchorVal.view; + MyLayoutEngine *relativeViewEngine = relativeView.myEngine; + subviewEngine.top = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.topPosInner.anchorVal.anchorType withContext:context] + subviewTraits.topPosInner.measure; + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + subviewEngine.top -= subviewTraits.topPosInner.measure; } - - nextPos = pos; + } else if (subviewTraits.topPosInner.mostVal != nil) { + subviewEngine.top = subviewTraits.topPosInner.mostVal.doubleValue + subviewTraits.topPosInner.measure; + } else { + subviewEngine.top = subviewTraits.topPosInner.measure + context->paddingTop; } - - if (![self myIsNoLayoutSubview:sbv]) - { - if (totalHeight != 0) - { - if (nextPos != nil) - totalOffset += nextPos.view.centerYPos.absVal; + + if (subviewTraits.bottomPosInner.anchorVal != nil) { + UIView *relativeView = subviewTraits.bottomPosInner.anchorVal.view; + MyLayoutEngine *relativeViewEngine = relativeView.myEngine; + subviewEngine.bottom = [self mySubviewEngine:relativeViewEngine getMeasureFromAnchorType:subviewTraits.bottomPosInner.anchorVal.anchorType withContext:context] - subviewTraits.bottomPosInner.measure; + if (relativeView != nil && relativeView != self && [relativeViewEngine.currentSizeClass invalid]) { + subviewEngine.bottom += subviewTraits.bottomPosInner.measure; } - - [self myCalcHeight:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - totalHeight += sbvmyFrame.height; - totalOffset += sbvsc.centerYPosInner.absVal; + } else if (subviewTraits.bottomPosInner.mostVal != nil) { + subviewEngine.bottom = subviewTraits.bottomPosInner.mostVal.doubleValue - subviewTraits.bottomPosInner.measure; + } else { + subviewEngine.bottom = context->selfSize.height - subviewTraits.bottomPosInner.measure - context->paddingBottom; } - - - //所有高度算出后,再分别设置 - CGFloat topOffset = (selfSize.height - lsc.myLayoutTopPadding - lsc.myLayoutBottomPadding - totalHeight - totalOffset) / 2; - topOffset += lsc.myLayoutTopPadding; - - id prev = @(topOffset); - [sbvsc.topPos __equalTo:prev]; - prev = sbvsc.bottomPos; - for (MyLayoutPos *pos in centerArray) - { - [[pos.view.topPos __equalTo:prev] __offset:pos.view.centerYPos.absVal]; - prev = pos.view.bottomPos; + + //只有在没有设置高度时才计算高度。 + if (subviewTraits.heightSizeInner.val == nil) { + subviewEngine.height = subviewEngine.bottom - subviewEngine.top; + } else { + subviewEngine.bottom = subviewEngine.top + subviewEngine.height + subviewTraits.bottomPosInner.measure; } - + hasMargin = YES; + } + + if (subviewTraits.heightSizeInner.uBoundValInner.anchorVal != nil && subviewTraits.heightSizeInner.uBoundValInner.anchorVal.view != self) { + [self mySubviewEngine:subviewTraits.heightSizeInner.uBoundValInner.anchorVal.view.myEngine calcHeightWithContext:context]; + } + + if (subviewTraits.heightSizeInner.lBoundValInner.anchorVal != nil && subviewTraits.heightSizeInner.lBoundValInner.anchorVal.view != self) { + [self mySubviewEngine:subviewTraits.heightSizeInner.lBoundValInner.anchorVal.view.myEngine calcHeightWithContext:context]; + } + subviewEngine.height = [self myValidMeasure:subviewTraits.heightSizeInner subview:subviewTraits.view calcSize:subviewEngine.height subviewSize:subviewEngine.size selfLayoutSize:context->selfSize]; + if ([subviewTraits invalid]) { + subviewEngine.height = 0.0; + subviewEngine.bottom = subviewEngine.top + subviewEngine.height; } - - } + return hasMargin; +} + +- (CGSize)myDoLayoutRecalcWidth:(BOOL *)pRecalcWidth recalcHeight:(BOOL *)pRecalcHeight withContext:(MyLayoutContext *)context { + MyRelativeLayoutTraits *layoutTraits = (MyRelativeLayoutTraits *)context->layoutViewEngine.currentSizeClass; + if (pRecalcWidth != NULL) { + *pRecalcWidth = NO; + } + if (pRecalcHeight != NULL) { + *pRecalcHeight = NO; + } //计算最大的宽度和高度 - CGFloat maxWidth = lsc.myLayoutLeadingPadding + lsc.myLayoutTrailingPadding; - CGFloat maxHeight = lsc.myLayoutTopPadding + lsc.myLayoutBottomPadding; - - for (UIView *sbv in self.subviews) - { - - MyFrame *sbvmyFrame = sbv.myFrame; - UIView *sbvsc = [self myCurrentSizeClassFrom:sbvmyFrame]; - BOOL sbvWrapContentHeight = sbvsc.wrapContentHeight && ![sbv isKindOfClass:[MyBaseLayout class]]; - - [self myCalcSubViewLeadingTrailing:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - //特殊处理高度包裹的情况,如果高度包裹时则同时设置顶部和底部将无效。 - if (sbvWrapContentHeight) - { - sbvmyFrame.height = [self myHeightFromFlexedHeightView:sbv sbvsc:sbvsc inWidth:sbvmyFrame.width]; - sbvmyFrame.height = [self myValidMeasure:sbvsc.heightSizeInner sbv:sbv calcSize:sbvmyFrame.height sbvSize:sbvmyFrame.frame.size selfLayoutSize:selfSize]; - } - - [self myCalcSubViewTopBottom:sbv sbvsc:sbvsc lsc:lsc sbvmyFrame:sbvmyFrame selfSize:selfSize]; - - if ([self myIsNoLayoutSubview:sbv]) + CGFloat maxLayoutWidth = context->paddingLeading + context->paddingTrailing; + CGFloat maxLayoutHeight = context->paddingTop + context->paddingBottom; + for (MyLayoutEngine *subviewEngine in context->subviewEngines) { + MyViewTraits *subviewTraits = (MyViewTraits *)subviewEngine.currentSizeClass; + + [self mySubviewEngine:subviewEngine calcLeadingTrailingWithContext:context]; + [self mySubviewEngine:subviewEngine calcTopBottomWithContext:context]; + + if ([subviewTraits invalid]) { continue; - - - if (lsc.wrapContentWidth && pRecalc != NULL) - { + } + if (layoutTraits.widthSizeInner.wrapVal && pRecalcWidth != NULL) { //当有子视图依赖于父视图的一些设置时,需要重新进行布局(设置了右边或者中间的值,或者宽度依赖父视图) - if(sbvsc.trailingPosInner.posNumVal != nil || - sbvsc.trailingPosInner.posRelaVal.view == self || - sbvsc.centerXPosInner.posRelaVal.view == self || - sbvsc.centerXPosInner.posNumVal != nil || - sbvsc.widthSizeInner.dimeRelaVal.view == self - ) - { - *pRecalc = YES; + if (subviewTraits.trailingPosInner.numberVal != nil || + subviewTraits.trailingPosInner.mostVal != nil || + subviewTraits.trailingPosInner.anchorVal.view == self || + subviewTraits.centerXPosInner.anchorVal.view == self || + subviewTraits.centerXPosInner.numberVal != nil || + subviewTraits.widthSizeInner.anchorVal.view == self || + subviewTraits.widthSizeInner.fillVal) { + *pRecalcWidth = YES; } - + //宽度最小是任何一个子视图的左右偏移和外加内边距和。 - if (_myCGFloatLess(maxWidth, sbvsc.leadingPosInner.absVal + sbvsc.trailingPosInner.absVal + lsc.myLayoutLeadingPadding + lsc.myLayoutTrailingPadding)) - { - maxWidth = sbvsc.leadingPosInner.absVal + sbvsc.trailingPosInner.absVal + lsc.myLayoutLeadingPadding + lsc.myLayoutTrailingPadding; + if (_myCGFloatLess(maxLayoutWidth, subviewTraits.leadingPosInner.measure + subviewTraits.trailingPosInner.measure + context->paddingLeading + context->paddingTrailing)) { + maxLayoutWidth = subviewTraits.leadingPosInner.measure + subviewTraits.trailingPosInner.measure + context->paddingLeading + context->paddingTrailing; } - - if (sbvsc.widthSizeInner.dimeRelaVal == nil || sbvsc.widthSizeInner.dimeRelaVal != self.widthSizeInner) - { - if (sbvsc.centerXPosInner.posVal != nil) - { - if (_myCGFloatLess(maxWidth, sbvmyFrame.width + sbvsc.leadingPosInner.absVal + sbvsc.trailingPosInner.absVal + lsc.myLayoutLeadingPadding + lsc.myLayoutTrailingPadding)) - maxWidth = sbvmyFrame.width + sbvsc.leadingPosInner.absVal + sbvsc.trailingPosInner.absVal + lsc.myLayoutLeadingPadding + lsc.myLayoutTrailingPadding; - } - else if (sbvsc.leadingPosInner.posVal != nil && sbvsc.trailingPosInner.posVal != nil) - { - if (_myCGFloatLess(maxWidth, fabs(sbvmyFrame.trailing) + sbvsc.leadingPosInner.absVal + lsc.myLayoutLeadingPadding)) - { - maxWidth = fabs(sbvmyFrame.trailing) + sbvsc.leadingPosInner.absVal + lsc.myLayoutLeadingPadding; + + if ((subviewTraits.widthSizeInner.anchorVal == nil || subviewTraits.widthSizeInner.anchorVal != layoutTraits.widthSizeInner) && !subviewTraits.widthSizeInner.fillVal) { + if (subviewTraits.centerXPosInner.val != nil) { + if (_myCGFloatLess(maxLayoutWidth, subviewEngine.width + subviewTraits.leadingPosInner.measure + subviewTraits.trailingPosInner.measure + context->paddingLeading + context->paddingTrailing)) { + maxLayoutWidth = subviewEngine.width + subviewTraits.leadingPosInner.measure + subviewTraits.trailingPosInner.measure + context->paddingLeading + context->paddingTrailing; + } + } else if (subviewTraits.trailingPosInner.val != nil) { //如果只有右边约束,则可以认为宽度是反过来算的,所以这里是左边的绝对值加上左边的padding来计算最宽宽度。 + if (_myCGFloatLess(maxLayoutWidth, fabs(subviewEngine.leading) + context->paddingLeading)) { + maxLayoutWidth = fabs(subviewEngine.leading) + context->paddingLeading; } - - } - else if (sbvsc.trailingPosInner.posVal != nil) - { - if (_myCGFloatLess(maxWidth, fabs(sbvmyFrame.leading) + lsc.myLayoutLeadingPadding)) - maxWidth = fabs(sbvmyFrame.leading) + lsc.myLayoutLeadingPadding; } - else - { - if (_myCGFloatLess(maxWidth, fabs(sbvmyFrame.trailing) + lsc.myLayoutTrailingPadding)) - maxWidth = fabs(sbvmyFrame.trailing) + lsc.myLayoutTrailingPadding; + + if (_myCGFloatLess(maxLayoutWidth, fabs(subviewEngine.trailing) + context->paddingTrailing)) { + maxLayoutWidth = fabs(subviewEngine.trailing) + context->paddingTrailing; } - - - if (_myCGFloatLess(maxWidth, sbvmyFrame.trailing + sbvsc.trailingPosInner.absVal + lsc.myLayoutTrailingPadding)) - maxWidth = sbvmyFrame.trailing + sbvsc.trailingPosInner.absVal + lsc.myLayoutTrailingPadding; } } - - if (lsc.wrapContentHeight && pRecalc != NULL) - { + + if (layoutTraits.heightSizeInner.wrapVal && pRecalcHeight != NULL) { //当有子视图依赖于父视图的一些设置时,需要重新进行布局(设置了下边或者中间的值,或者高度依赖父视图) - if(sbvsc.bottomPosInner.posNumVal != nil || - sbvsc.bottomPosInner.posRelaVal.view == self || - sbvsc.centerYPosInner.posRelaVal.view == self || - sbvsc.centerYPosInner.posNumVal != nil || - sbvsc.heightSizeInner.dimeRelaVal.view == self - ) - { - *pRecalc = YES; + if (subviewTraits.bottomPosInner.numberVal != nil || + subviewTraits.bottomPosInner.mostVal != nil || + subviewTraits.bottomPosInner.anchorVal.view == self || + subviewTraits.centerYPosInner.anchorVal.view == self || + subviewTraits.centerYPosInner.numberVal != nil || + subviewTraits.heightSizeInner.anchorVal.view == self || + subviewTraits.heightSizeInner.fillVal) { + *pRecalcHeight = YES; } - - if (_myCGFloatLess(maxHeight, sbvsc.topPosInner.absVal + sbvsc.bottomPosInner.absVal + lsc.myLayoutTopPadding + lsc.myLayoutBottomPadding)) - { - maxHeight = sbvsc.topPosInner.absVal + sbvsc.bottomPosInner.absVal + lsc.myLayoutTopPadding + lsc.myLayoutBottomPadding; + + //每个视图最小的高度,在计算时要进行比较。 + if (_myCGFloatLess(maxLayoutHeight, subviewTraits.topPosInner.measure + subviewTraits.bottomPosInner.measure + context->paddingTop + context->paddingBottom)) { + maxLayoutHeight = subviewTraits.topPosInner.measure + subviewTraits.bottomPosInner.measure + context->paddingTop + context->paddingBottom; } - - - //这里加入特殊的条件sbvWrapContentHeight,因为有可能有同时设置顶部和底部位置又同时设置wrapContentHeight的情况,这种情况我们也让其加入最大高度计算行列。 - if (sbvsc.heightSizeInner.dimeRelaVal == nil || sbvsc.heightSizeInner.dimeRelaVal != self.heightSizeInner) - { - - if (sbvsc.centerYPosInner.posVal != nil) - { - if (_myCGFloatLess(maxHeight, sbvmyFrame.height + sbvsc.topPosInner.absVal + sbvsc.bottomPosInner.absVal + lsc.myLayoutTopPadding + lsc.myLayoutBottomPadding)) - maxHeight = sbvmyFrame.height + sbvsc.topPosInner.absVal + sbvsc.bottomPosInner.absVal + lsc.myLayoutTopPadding + lsc.myLayoutBottomPadding; - } - else if (sbvsc.topPosInner.posVal != nil && sbvsc.bottomPosInner.posVal != nil) - { - if (_myCGFloatLess(maxHeight, fabs(sbvmyFrame.bottom) + sbvsc.topPosInner.absVal + lsc.myLayoutTopPadding)) - { - maxHeight = fabs(sbvmyFrame.bottom) + sbvsc.topPosInner.absVal + lsc.myLayoutTopPadding; + + //高度不依赖父视图 + if ((subviewTraits.heightSizeInner.anchorVal == nil || subviewTraits.heightSizeInner.anchorVal != layoutTraits.heightSizeInner) && !subviewTraits.heightSizeInner.fillVal) { + if (subviewTraits.centerYPosInner.val != nil) { + if (_myCGFloatLess(maxLayoutHeight, subviewEngine.height + subviewTraits.topPosInner.measure + subviewTraits.bottomPosInner.measure + context->paddingTop + context->paddingBottom)) { + maxLayoutHeight = subviewEngine.height + subviewTraits.topPosInner.measure + subviewTraits.bottomPosInner.measure + context->paddingTop + context->paddingBottom; + } + } else if (subviewTraits.bottomPosInner.val != nil) { //如果只有底边约束,则可以认为高度是反过来算的,所以这里是上边的绝对值加上上边的padding来计算最高高度 + if (_myCGFloatLess(maxLayoutHeight, fabs(subviewEngine.top) + context->paddingTop)) { + maxLayoutHeight = fabs(subviewEngine.top) + context->paddingTop; } } - else if (sbvsc.bottomPosInner.posVal != nil) - { - if (_myCGFloatLess(maxHeight, fabs(sbvmyFrame.top) + lsc.myLayoutTopPadding)) - maxHeight = fabs(sbvmyFrame.top) + lsc.myLayoutTopPadding; - } - else - { - if (_myCGFloatLess(maxHeight, fabs(sbvmyFrame.bottom) + lsc.myLayoutBottomPadding)) - maxHeight = fabs(sbvmyFrame.bottom) + lsc.myLayoutBottomPadding; + if (_myCGFloatLess(maxLayoutHeight, fabs(subviewEngine.bottom) + context->paddingBottom)) { + maxLayoutHeight = fabs(subviewEngine.bottom) + context->paddingBottom; } - - - if (_myCGFloatLess(maxHeight, sbvmyFrame.bottom + sbvsc.bottomPosInner.absVal + lsc.myLayoutBottomPadding)) - maxHeight = sbvmyFrame.bottom + sbvsc.bottomPosInner.absVal + lsc.myLayoutBottomPadding; - } } } - - - return CGSizeMake(maxWidth, maxHeight); - -} - + return CGSizeMake(maxLayoutWidth, maxLayoutHeight); +} @end diff --git a/MyLayout/Lib/MyTableLayout.h b/MyLayout/Lib/MyTableLayout.h index 1b743af..476375f 100644 --- a/MyLayout/Lib/MyTableLayout.h +++ b/MyLayout/Lib/MyTableLayout.h @@ -8,18 +8,10 @@ #import "MyLinearLayout.h" -//定义特殊的行列尺寸。主要用于表格布局中。请不要再使用如下的宏,而是改用MyLayoutSize中对应的类属性来代替。 -#define MTLSIZE_AVERAGE MyLayoutSize.average -#define MTLSIZE_WRAPCONTENT MyLayoutSize.wrap -#define MTLSIZE_MATCHPARENT MyLayoutSize.fill - - - /** 表格布局行列索引描述扩展对象。 */ -@interface NSIndexPath(MyTableLayoutEx) - +@interface NSIndexPath (MyTableLayoutExt) /** 构建一个单元格索引对象 @@ -28,14 +20,12 @@ @param row 行索引 @return 返回单元格索引实例对象 */ -+(instancetype)indexPathForCol:(NSInteger)col inRow:(NSInteger)row; ++ (instancetype)indexPathForCol:(NSInteger)col inRow:(NSInteger)row; -@property(nonatomic,assign,readonly) NSInteger col; +@property (nonatomic, assign, readonly) NSInteger col; @end - - /** 表格布局是一种里面的子视图可以像表格一样进行多行多列排列的布局视图。子视图添加到表格布局视图前必须先要建立并添加行子视图,然后再将列子视图添加到行子视图里面。 表格里面的行子视图和列子视图的排列方向的概念是相对的,他根据表格布局方向的不同而不同。表格布局根据方向可分为垂直表格布局和水平表格布局。 @@ -46,24 +36,21 @@ */ @interface MyTableLayout : MyLinearLayout - /** 构建一个表格布局视图 @param orientation 表格布局的方向 @return 表格布局实例对象 */ -+(instancetype)tableLayoutWithOrientation:(MyOrientation)orientation; - - ++ (instancetype)tableLayoutWithOrientation:(MyOrientation)orientation; /** 添加一个新行。对于垂直表格来说每一行是从上往下排列的,而水平表格则每一行是从左往右排列的。 @note 行能设置的值: - 1. 一般常数尺寸 表示这行的尺寸为这个固定的数值(垂直表格为行高,水平表格为行宽),列子视图不需要设置尺寸(垂直表格为高度,水平表格为宽度)。 - 2. 特殊尺寸MyLayoutSize.wrap 表示由列子视图决定本行尺寸(垂直表格为行高,水平表格为行宽),每个列子视图都需要自己设置尺寸(垂直表格为高度,水平表格为宽度) + 1. 一般常数尺寸: 表示这行的尺寸为这个固定的数值(垂直表格为行高,水平表格为行宽),列子视图不需要设置尺寸(垂直表格为高度,水平表格为宽度)。 + 2. 特殊尺寸MyLayoutSize.wrap: 表示由列子视图决定本行尺寸(垂直表格为行高,水平表格为行宽),每个列子视图都需要自己设置尺寸(垂直表格为高度,水平表格为宽度) 3. 特殊尺寸MyLayoutSize.average 表示均分尺寸(垂直表格为行高 = 总行高/行数,水平表格为行宽 = 总行宽/行数),列子视图不需要设置尺寸(垂直表格为高度,水平表格为宽度) 4. 不能设置为MyLayoutSize.fill。 @@ -81,8 +68,7 @@ @param colSize 列的尺寸值,请参考上面的列能设置的值。 @return 返回行布局视图对象 */ --(MyLinearLayout*)addRow:(CGFloat)rowSize colSize:(CGFloat)colSize; - +- (MyLinearLayout *)addRow:(CGFloat)rowSize colSize:(CGFloat)colSize; /** 添加行并指定列的数量,这样每列将会平分行的宽度或者高度。 @@ -91,94 +77,75 @@ @param colCount 指定行里面列的数量,必须大于0 @return 返回行布局视图对象。 */ --(MyLinearLayout*)addRow:(CGFloat)rowSize colCount:(NSUInteger)colCount; - +- (MyLinearLayout *)addRow:(CGFloat)rowSize colCount:(NSUInteger)colCount; /** * 在指定的位置插入一个新行 */ --(MyLinearLayout*)insertRow:(CGFloat)rowSize colSize:(CGFloat)colSize atIndex:(NSInteger)rowIndex; --(MyLinearLayout*)insertRow:(CGFloat)rowSize colCount:(NSUInteger)colCount atIndex:(NSInteger)rowIndex; - +- (MyLinearLayout *)insertRow:(CGFloat)rowSize colSize:(CGFloat)colSize atIndex:(NSInteger)rowIndex; +- (MyLinearLayout *)insertRow:(CGFloat)rowSize colCount:(NSUInteger)colCount atIndex:(NSInteger)rowIndex; /** - * 删除一行 + *删除一行 */ --(void)removeRowAt:(NSInteger)rowIndex; +- (void)removeRowAt:(NSInteger)rowIndex; /** * 交换两行的位置 */ --(void)exchangeRowAt:(NSInteger)rowIndex1 withRow:(NSInteger)rowIndex2; +- (void)exchangeRowAt:(NSInteger)rowIndex1 withRow:(NSInteger)rowIndex2; /** *返回行对象 */ --(MyLinearLayout*)viewAtRowIndex:(NSInteger)rowIndex; +- (MyLinearLayout *)viewAtRowIndex:(NSInteger)rowIndex; /** *返回行的数量 */ --(NSUInteger)countOfRow; +- (NSUInteger)countOfRow; /** * 添加一个新的列。再添加一个新的列前必须要先添加行,对于垂直表格来说每一列是从左到右排列的,而对于水平表格来说每一列是从上到下排列的。 * @param colView 列视图 * @param rowIndex 指定要添加列的行的索引 */ --(void)addCol:(UIView*)colView atRow:(NSInteger)rowIndex; +- (void)addCol:(UIView *)colView atRow:(NSInteger)rowIndex; /** * 在指定的indexPath下插入一个新的列indexPath用[NSIndexPath indexPathForCol:inRow:]来构建 */ --(void)insertCol:(UIView*)colView atIndexPath:(NSIndexPath*)indexPath; +- (void)insertCol:(UIView *)colView atIndexPath:(NSIndexPath *)indexPath; /** * 删除一列 */ --(void)removeColAt:(NSIndexPath*)indexPath; +- (void)removeColAt:(NSIndexPath *)indexPath; /** * 交换两个列视图,这两个列视图是可以跨行的 */ --(void)exchangeColAt:(NSIndexPath*)indexPath1 withCol:(NSIndexPath*)indexPath2; +- (void)exchangeColAt:(NSIndexPath *)indexPath1 withCol:(NSIndexPath *)indexPath2; /** * 返回列视图 */ --(UIView*)viewAtIndexPath:(NSIndexPath*)indexPath; +- (UIView *)viewAtIndexPath:(NSIndexPath *)indexPath; /** * 返回某行的列的数量 */ --(NSUInteger)countOfColInRow:(NSInteger)rowIndex; - +- (NSUInteger)countOfColInRow:(NSInteger)rowIndex; /** *表格布局的addSubView被重新定义,是addCol:atRow的精简版本,表示插入当前行的最后一列 */ - (void)addSubview:(UIView *)view; - //不能直接调用如下的函数,否则会崩溃。 - (void)insertSubview:(UIView *)view atIndex:(NSInteger)index; - (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2; - (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview; - (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview; - -@end - - -@interface MyTableLayout(MyTableDeprecated) - - -/** - * 不再单独设置表格的行间距和列间距了,而是复用视图的水平间距和垂直间距。原来表格的行间距和列间距会根据不同的表格方向定义不同而不同,现在统一为水平和垂直间距,不管表格的方向如何,水平间距都是定义左右的间距,垂直间距都是定义上下的间距。 - */ -@property(nonatomic ,assign, getter=subviewVSpace, setter=setSubviewVSpace:) CGFloat rowSpacing MYMETHODDEPRECATED("use subviewVSpace to instead"); -@property(nonatomic, assign, getter=subviewHSpace, setter=setSubviewHSpace:) CGFloat colSpacing MYMETHODDEPRECATED("use subviewHSpace to instead"); - - @end - diff --git a/MyLayout/Lib/MyTableLayout.m b/MyLayout/Lib/MyTableLayout.m index 4dbcfb3..cba4bd9 100644 --- a/MyLayout/Lib/MyTableLayout.m +++ b/MyLayout/Lib/MyTableLayout.m @@ -13,324 +13,265 @@ @interface MyTableRowLayout : MyLinearLayout ++ (MyTableRowLayout *)rowSize:(CGFloat)rowSize colSize:(CGFloat)colSize orientation:(MyOrientation)orientation; -+(id)rowSize:(CGFloat)rowSize colSize:(CGFloat)colSize orientation:(MyOrientation)orientation; - -@property(nonatomic,assign, readonly) CGFloat rowSize; -@property(nonatomic,assign, readonly) CGFloat colSize; +@property (nonatomic, assign) CGFloat rowSize; +@property (nonatomic, assign) CGFloat colSize; @end @implementation MyTableRowLayout -{ - CGFloat _rowSize; - CGFloat _colSize; -} - --(id)initWith:(CGFloat)rowSize colSize:(CGFloat)colSize orientation:(MyOrientation)orientation -{ +- (instancetype)initWith:(CGFloat)rowSize colSize:(CGFloat)colSize orientation:(MyOrientation)orientation { self = [super initWithOrientation:orientation]; - if (self != nil) - { + if (self != nil) { _rowSize = rowSize; _colSize = colSize; - - UIView *lsc = self.myCurrentSizeClass; - - if (rowSize == MyLayoutSize.average) - lsc.weight = 1; - else if (rowSize > 0) - { - if (orientation == MyOrientation_Horz) - lsc.myHeight = rowSize; - else - lsc.myWidth = rowSize; - } - else if (rowSize == MyLayoutSize.wrap) - { - if (orientation == MyOrientation_Horz) - lsc.wrapContentHeight = YES; - else - lsc.wrapContentWidth = YES; - } - else - { + + UIView *layoutTraits = self.myDefaultSizeClass; + + if (rowSize == MyLayoutSize.average) { + layoutTraits.weight = 1; + } else if (rowSize > 0) { + if (orientation == MyOrientation_Horz) { + [layoutTraits.heightSize _myEqualTo:@(rowSize)]; + } else { + [layoutTraits.widthSize _myEqualTo:@(rowSize)]; + } + } else if (rowSize == MyLayoutSize.wrap) { + if (orientation == MyOrientation_Horz) { + [layoutTraits.heightSize _myEqualTo:@(MyLayoutSize.wrap)]; + } else { + [layoutTraits.widthSize _myEqualTo:@(MyLayoutSize.wrap)]; + } + } else { NSCAssert(0, @"Constraint exception !! rowSize can not set to MyLayoutSize.fill"); } - - if (colSize == MyLayoutSize.average || colSize == MyLayoutSize.fill || colSize < sColCountTag) - { - if (orientation == MyOrientation_Horz) - { - lsc.wrapContentWidth = NO; - lsc.myHorzMargin = 0; - } - else - { - lsc.wrapContentHeight = NO; - lsc.myVertMargin = 0; + + if (colSize == MyLayoutSize.average || colSize == MyLayoutSize.fill || colSize < sColCountTag) { + if (orientation == MyOrientation_Horz) { + [layoutTraits.widthSize _myEqualTo:nil]; + [layoutTraits.leadingPos _myEqualTo:@(0)]; + [layoutTraits.trailingPos _myEqualTo:@(0)]; + } else { + [layoutTraits.heightSize _myEqualTo:nil]; + [layoutTraits.topPos _myEqualTo:@(0)]; + [layoutTraits.bottomPos _myEqualTo:@(0)]; } - } } - return self; } -+(id)rowSize:(CGFloat)rowSize colSize:(CGFloat)colSize orientation:(MyOrientation)orientation -{ - return [[self alloc] initWith:rowSize colSize:colSize orientation:orientation]; +- (void)myHookSublayout:(MyBaseLayout *)sublayout borderlineRect:(CGRect *)pRect { + /* + 如果行布局是包裹的,那么意味着里面的列子视图都需要自己指定行的尺寸,这样列子视图就会有不同的尺寸,如果是有智能边界线时就会出现每个列子视图的边界线的长度不一致的情况。 + 有时候我们希望列子视图的边界线能够布满整个行(比如垂直表格中,所有列子视图的的高度都和所在行的行高是一致的)因此我们需要将列子视图的边界线的可显示范围进行调整。 + 因此我们重载这个方法来解决这个问题,这个方法可以将列子视图的边界线的区域进行扩充和调整,目的是为了让列子视图的边界线能够布满整个行布局上。 + */ + + MyLinearLayoutTraits *layoutTraits = (MyLinearLayoutTraits*)self.myEngine.currentSizeClass; + + if (self.rowSize == MyLayoutSize.wrap) { + if (layoutTraits.orientation == MyOrientation_Horz) { + //垂直表格下,行是水平的,所以这里需要将列子视图的y轴的位置和行对齐。 + pRect->origin.y = 0 - CGRectGetMinY(sublayout.frame); + //垂直表格下,行是水平的,所以这里需要将子视图的边界线的高度和行的高度保持一致。 + pRect->size.height = CGRectGetHeight(self.bounds); + } else { + //水平表格下,行是垂直的,所以这里需要将列子视图的x轴的位置和行对齐。 + pRect->origin.x = 0 - CGRectGetMinX(sublayout.frame); + //水平表格下,行是垂直的,所以这里需要将子视图的边界线的宽度和行的宽度保持一致。 + pRect->size.width = CGRectGetWidth(self.bounds); + } + } } ++ (MyTableRowLayout *)rowSize:(CGFloat)rowSize colSize:(CGFloat)colSize orientation:(MyOrientation)orientation { + return [[self alloc] initWith:rowSize colSize:colSize orientation:orientation]; +} @end +@implementation NSIndexPath (MyTableLayoutEx) -@implementation NSIndexPath(MyTableLayoutEx) - -+(instancetype)indexPathForCol:(NSInteger)col inRow:(NSInteger)row -{ ++ (instancetype)indexPathForCol:(NSInteger)col inRow:(NSInteger)row { return [self indexPathForRow:row inSection:col]; } --(NSInteger)col -{ +- (NSInteger)col { return self.section; } @end - @implementation MyTableLayout -+(instancetype)tableLayoutWithOrientation:(MyOrientation)orientation -{ +#pragma mark-- Public Methods + ++ (instancetype)tableLayoutWithOrientation:(MyOrientation)orientation { return [self linearLayoutWithOrientation:orientation]; } - --(MyLinearLayout*)addRow:(CGFloat)rowSize colSize:(CGFloat)colSize -{ +- (MyLinearLayout *)addRow:(CGFloat)rowSize colSize:(CGFloat)colSize { return [self insertRow:rowSize colSize:colSize atIndex:self.countOfRow]; } --(MyLinearLayout*)addRow:(CGFloat)rowSize colCount:(NSUInteger)colCount -{ +- (MyLinearLayout *)addRow:(CGFloat)rowSize colCount:(NSUInteger)colCount { return [self insertRow:rowSize colCount:colCount atIndex:self.countOfRow]; } --(MyLinearLayout*)insertRow:(CGFloat)rowSize colCount:(NSUInteger)colCount atIndex:(NSInteger)rowIndex -{ +- (MyLinearLayout *)insertRow:(CGFloat)rowSize colCount:(NSUInteger)colCount atIndex:(NSInteger)rowIndex { //这里特殊处理用-100000 - colCount 来表示一个特殊的列尺寸。其实是数量。 - return [self insertRow:rowSize colSize:sColCountTag - colCount atIndex:rowIndex]; + return [self insertRow:rowSize colSize:sColCountTag - colCount atIndex:rowIndex]; } +- (MyLinearLayout *)insertRow:(CGFloat)rowSize colSize:(CGFloat)colSize atIndex:(NSInteger)rowIndex { + MyTableLayout *layoutTraits = self.myDefaultSizeClass; --(MyLinearLayout*)insertRow:(CGFloat)rowSize colSize:(CGFloat)colSize atIndex:(NSInteger)rowIndex -{ - MyTableLayout *lsc = self.myCurrentSizeClass; - MyOrientation ori = MyOrientation_Vert; - if (lsc.orientation == MyOrientation_Vert) + if (layoutTraits.orientation == MyOrientation_Vert) { ori = MyOrientation_Horz; - else + } else { ori = MyOrientation_Vert; - - MyTableRowLayout *rowView = [MyTableRowLayout rowSize:rowSize colSize:colSize orientation:ori]; - if (ori == MyOrientation_Horz) - { - rowView.subviewHSpace = lsc.subviewHSpace; } - else - { - rowView.subviewVSpace = lsc.subviewVSpace; + + MyTableRowLayout *rowView = [MyTableRowLayout rowSize:rowSize colSize:colSize orientation:ori]; + if (ori == MyOrientation_Horz) { + rowView.subviewHSpace = layoutTraits.subviewHSpace; + } else { + rowView.subviewVSpace = layoutTraits.subviewVSpace; } rowView.intelligentBorderline = self.intelligentBorderline; [super insertSubview:rowView atIndex:rowIndex]; return rowView; } --(void)removeRowAt:(NSInteger)rowIndex -{ +- (void)removeRowAt:(NSInteger)rowIndex { [[self viewAtRowIndex:rowIndex] removeFromSuperview]; } --(void)exchangeRowAt:(NSInteger)rowIndex1 withRow:(NSInteger)rowIndex2 -{ +- (void)exchangeRowAt:(NSInteger)rowIndex1 withRow:(NSInteger)rowIndex2 { [super exchangeSubviewAtIndex:rowIndex1 withSubviewAtIndex:rowIndex2]; } --(MyLinearLayout*)viewAtRowIndex:(NSInteger)rowIndex; -{ +- (MyLinearLayout *)viewAtRowIndex:(NSInteger)rowIndex { return [self.subviews objectAtIndex:rowIndex]; } --(NSUInteger)countOfRow -{ +- (NSUInteger)countOfRow { return self.subviews.count; } //列操作 --(void)addCol:(UIView*)colView atRow:(NSInteger)rowIndex -{ +- (void)addCol:(UIView *)colView atRow:(NSInteger)rowIndex { [self insertCol:colView atIndexPath:[NSIndexPath indexPathForCol:[self countOfColInRow:rowIndex] inRow:rowIndex]]; } --(void)insertCol:(UIView*)colView atIndexPath:(NSIndexPath*)indexPath -{ - MyTableRowLayout *rowView = (MyTableRowLayout*)[self viewAtRowIndex:indexPath.row]; - - MyLinearLayout *rowsc = rowView.myCurrentSizeClass; - UIView *colsc = colView.myCurrentSizeClass; - - //colSize为0表示均分尺寸,为-1表示由子视图决定尺寸,大于0表示固定尺寸。 - if (rowView.colSize == MyLayoutSize.average) - { - colsc.weight = 1; - } - else if (rowView.colSize < sColCountTag) - { +- (void)insertCol:(UIView *)colView atIndexPath:(NSIndexPath *)indexPath { + MyTableRowLayout *rowView = (MyTableRowLayout *)[self viewAtRowIndex:indexPath.row]; + + MyLinearLayout *rowViewTraits = rowView.myDefaultSizeClass; + UIView *colViewTraits = colView.myDefaultSizeClass; + + if (rowView.colSize == MyLayoutSize.average) { + colViewTraits.weight = 1.0; + } else if (rowView.colSize < sColCountTag) { NSUInteger colCount = sColCountTag - rowView.colSize; - if (rowsc.orientation == MyOrientation_Horz) - colsc.widthSize.equalTo(rowView.widthSize).multiply(1.0 / colCount).add(-1 * rowView.subviewHSpace * (colCount - 1.0)/ colCount); - else - colsc.heightSize.equalTo(rowView.heightSize).multiply(1.0 / colCount).add(-1 * rowView.subviewVSpace * (colCount - 1.0)/ colCount); - } - else if (rowView.colSize > 0) - { - if (rowsc.orientation == MyOrientation_Horz) - colsc.myWidth = rowView.colSize; - else - colsc.myHeight = rowView.colSize; - } - - if (rowsc.orientation == MyOrientation_Horz) - { - if (CGRectGetHeight(colView.bounds) == 0 && colsc.heightSizeInner.dimeVal == nil) - { - if ([colView isKindOfClass:[MyBaseLayout class]]) - { - if (!colsc.wrapContentHeight) - [colsc.heightSize __equalTo:rowsc.heightSize]; - } - else - [colsc.heightSize __equalTo:rowsc.heightSize]; + if (rowViewTraits.orientation == MyOrientation_Horz) { + [[[colViewTraits.widthSize _myEqualTo:rowView.widthSize] _myMultiply:(1.0 / colCount)] _myAdd:-1 * rowView.subviewHSpace * (colCount - 1.0) / colCount]; + } else { + [[[colViewTraits.heightSize _myEqualTo:rowView.heightSize] _myMultiply:(1.0 / colCount)] _myAdd:-1 * rowView.subviewVSpace * (colCount - 1.0) / colCount]; + } + } else if (rowView.colSize > 0) { + if (rowViewTraits.orientation == MyOrientation_Horz) { + [colViewTraits.widthSize _myEqualTo:@(rowView.colSize)]; + } else { + [colViewTraits.heightSize _myEqualTo:@(rowView.colSize)]; } } - else - { - if (CGRectGetWidth(colView.bounds) == 0 && colsc.widthSizeInner.dimeVal == nil) - { - - if ([colView isKindOfClass:[MyBaseLayout class]]) - { - if (!colsc.wrapContentWidth) - [colsc.widthSize __equalTo:rowsc.widthSize]; - } - else - [colsc.widthSize __equalTo:rowsc.widthSize]; + + if (rowViewTraits.orientation == MyOrientation_Horz) { + if (CGRectGetHeight(colView.bounds) == 0 && colViewTraits.heightSizeInner.val == nil) { + [colViewTraits.heightSize _myEqualTo:rowViewTraits.heightSize priority:MyPriority_Low]; + } + } else { + if (CGRectGetWidth(colView.bounds) == 0 && colViewTraits.widthSizeInner.val == nil) { + [colViewTraits.widthSize _myEqualTo:rowViewTraits.widthSize priority:MyPriority_Low]; } - } - - + [rowView insertSubview:colView atIndex:indexPath.col]; } --(void)removeColAt:(NSIndexPath*)indexPath -{ +- (void)removeColAt:(NSIndexPath *)indexPath { [[self viewAtIndexPath:indexPath] removeFromSuperview]; } --(void)exchangeColAt:(NSIndexPath*)indexPath1 withCol:(NSIndexPath*)indexPath2 -{ - UIView * colView1 = [self viewAtIndexPath:indexPath1]; - UIView * colView2 = [self viewAtIndexPath:indexPath2]; - - if (colView1 == colView2) +- (void)exchangeColAt:(NSIndexPath *)indexPath1 withCol:(NSIndexPath *)indexPath2 { + UIView *colView1 = [self viewAtIndexPath:indexPath1]; + UIView *colView2 = [self viewAtIndexPath:indexPath2]; + + if (colView1 == colView2) { return; - - + } [self removeColAt:indexPath1]; [self removeColAt:indexPath2]; - + [self insertCol:colView1 atIndexPath:indexPath2]; [self insertCol:colView2 atIndexPath:indexPath1]; } --(UIView*)viewAtIndexPath:(NSIndexPath*)indexPath -{ +- (UIView *)viewAtIndexPath:(NSIndexPath *)indexPath { return [[self viewAtRowIndex:indexPath.row].subviews objectAtIndex:indexPath.col]; } --(NSUInteger)countOfColInRow:(NSInteger)rowIndex -{ +- (NSUInteger)countOfColInRow:(NSInteger)rowIndex { return [self viewAtRowIndex:rowIndex].subviews.count; } -#pragma mark -- Override Method +#pragma mark-- Override Methods - - --(void)setSubviewVSpace:(CGFloat)subviewVSpace -{ +- (void)setSubviewVSpace:(CGFloat)subviewVSpace { [super setSubviewVSpace:subviewVSpace]; - if (self.orientation == MyOrientation_Horz) - { - for (NSInteger i = 0; i < self.countOfRow; i++) - { + if (self.orientation == MyOrientation_Horz) { + for (NSInteger i = 0; i < self.countOfRow; i++) { [self viewAtRowIndex:i].subviewVSpace = subviewVSpace; } } } --(void)setSubviewHSpace:(CGFloat)subviewHSpace -{ +- (void)setSubviewHSpace:(CGFloat)subviewHSpace { [super setSubviewHSpace:subviewHSpace]; - if (self.orientation == MyOrientation_Vert) - { - for (NSInteger i = 0; i < self.countOfRow; i++) - { + if (self.orientation == MyOrientation_Vert) { + for (NSInteger i = 0; i < self.countOfRow; i++) { [self viewAtRowIndex:i].subviewHSpace = subviewHSpace; } } - } - //不能直接调用如下的函数。 -- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index -{ +- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index { NSCAssert(0, @"Constraint exception!! Can't call insertSubview"); } -- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2 -{ +- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2 { NSCAssert(0, @"Constraint exception!! Can't call exchangeSubviewAtIndex"); } -- (void)addSubview:(UIView *)view -{ +- (void)addSubview:(UIView *)view { [self addCol:view atRow:[self countOfRow] - 1]; } -- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview -{ +- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview { NSCAssert(0, @"Constraint exception!! Can't call insertSubview"); } -- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview -{ +- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview { NSCAssert(0, @"Constraint exception!! Can't call insertSubview"); } - --(id)createSizeClassInstance -{ - return [MyTableLayoutViewSizeClass new]; +- (id)createSizeClassInstance { + return [MyTableLayoutTraits new]; } -#pragma mark -- Deprecated Method - - @end diff --git a/MyLayoutDemo/AllTest10Cell.h b/MyLayoutDemo/AllTest10Cell.h new file mode 100644 index 0000000..3af7cef --- /dev/null +++ b/MyLayoutDemo/AllTest10Cell.h @@ -0,0 +1,15 @@ +// +// AllTest10Cell.h +// FriendListDemo +// +// Created by bsj_mac_2 on 2018/5/7. +// Copyright © 2018年 bsj_mac_2. All rights reserved. +// + +#import +#import "MyLayout.h" + +@interface AllTest10Cell : UITableViewCell + +- (void)setCommentsText:(NSString *)text; +@end diff --git a/MyLayoutDemo/AllTest10Cell.m b/MyLayoutDemo/AllTest10Cell.m new file mode 100644 index 0000000..40f33ab --- /dev/null +++ b/MyLayoutDemo/AllTest10Cell.m @@ -0,0 +1,81 @@ +// +// AllTest10Cell.m +// FriendListDemo +// +// Created by bsj_mac_2 on 2018/5/7. +// Copyright © 2018年 bsj_mac_2. All rights reserved. +// + +#import "AllTest10Cell.h" + +@interface AllTest10Cell () +@property(nonatomic, strong) UILabel *commentsLabel; +@property (strong, nonatomic) MyLinearLayout *rootLayout; +@end + +@implementation AllTest10Cell + +-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self != nil) + { + [self createLinearRootLayout]; + } + + return self; +} + + +//如果您的最低支持是iOS8,那么你可以重载这个方法来动态的评估cell的高度,Autolayout内部是通过这个方法来评估高度的,因此如果用MyLayout实现的话就不需要调用基类的方法,而是调用根布局视图的sizeThatFits来评估获取动态的高度。 +- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority +{ + /* + 通过布局视图的sizeThatFits方法能够评估出UITableViewCell的动态高度。sizeThatFits并不会进行布局而只是评估布局的尺寸。 + 因为cell的高度是自适应的,因此这里通过调用高度为wrap的布局视图的sizeThatFits来获取真实的高度。 + */ + + if (@available(iOS 11.0, *)) { + //如果你的界面要支持横屏的话,因为iPhoneX的横屏左右有44的安全区域,所以这里要减去左右的安全区域的值,来作为布局宽度尺寸的评估值。 + //如果您的界面不需要支持横屏,或者延伸到安全区域外则不需要做这个特殊处理,而直接使用else部分的代码即可。 + return [self.rootLayout sizeThatFits:CGSizeMake(targetSize.width - self.safeAreaInsets.left - self.safeAreaInsets.right, targetSize.height)]; + } else { + return [self.rootLayout sizeThatFits:targetSize]; //如果使用系统自带的分割线,请记得将返回的高度height+1 + } +} + +#pragma mark -- Layout Construction + +//用线性布局来实现UI界面 +-(void)createLinearRootLayout +{ + _rootLayout= [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + _rootLayout.padding = UIEdgeInsetsMake(0, 65, 0, 10); + _rootLayout.cacheEstimatedRect = YES; + _rootLayout.myHorzMargin = MyLayoutPos.safeAreaMargin; + _rootLayout.myHeight = MyLayoutSize.wrap; + //这里设置其他位置有间隔线而最后一行没有下划线。我们可以借助布局视图本身所提供的边界线来代替掉系统默认的cell之间的间隔线,因为布局视图的边界线所提供的能力要大于默认的间隔线。 + MyBorderline *bld = [[MyBorderline alloc] initWithColor:[UIColor whiteColor]]; + self.rootLayout.bottomBorderline = bld; + [self.contentView addSubview:_rootLayout]; + MyLinearLayout *giveLikeLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + giveLikeLayout.padding = UIEdgeInsetsMake(5, 6, 5, 6); + giveLikeLayout.weight = 1; + giveLikeLayout.myHorzMargin = 0; + giveLikeLayout.backgroundColor = [UIColor colorWithRed:240.0/255 green:240.0/255 blue:240.0/255 alpha:1]; + [self.rootLayout addSubview:giveLikeLayout]; + + _commentsLabel = [UILabel new]; + _commentsLabel.text = @"正文"; + _commentsLabel.textColor = [UIColor blueColor]; + _commentsLabel.font = [UIFont systemFontOfSize:12]; + _commentsLabel.myHorzMargin = 0; + _commentsLabel.myHeight = MyLayoutSize.wrap; //如果想让文本的高度是动态的,请在设置明确宽度的情况下将高度设置为自适应 + [giveLikeLayout addSubview:_commentsLabel]; +} + +- (void)setCommentsText:(NSString *)text { + self.commentsLabel.text = text; +} + +@end diff --git a/MyLayoutDemo/AllTest10HeaderView.h b/MyLayoutDemo/AllTest10HeaderView.h new file mode 100644 index 0000000..e3959f6 --- /dev/null +++ b/MyLayoutDemo/AllTest10HeaderView.h @@ -0,0 +1,21 @@ +// +// AllTest10HeaderView.h +// FriendListDemo +// +// Created by 谢海龙 on 2018/5/8. +// Copyright © 2018年 bsj_mac_2. All rights reserved. +// + +#import +#import "MyLayout.h" +@class AllTest10Model; +@class AllTest10HeaderView; +typedef void(^PraiseClickHandler)(AllTest10HeaderView *headerView, AllTest10Model *model); +typedef void(^CommentsClickHandler)(AllTest10HeaderView *headerView, AllTest10Model *model); + +@interface AllTest10HeaderView : UITableViewHeaderFooterView + +@property (strong, nonatomic) AllTest10Model * model; +@property (copy, nonatomic) PraiseClickHandler praiseClickHandler; +@property (copy, nonatomic) CommentsClickHandler commentsClickHandler; +@end diff --git a/MyLayoutDemo/AllTest10HeaderView.m b/MyLayoutDemo/AllTest10HeaderView.m new file mode 100644 index 0000000..1c40f47 --- /dev/null +++ b/MyLayoutDemo/AllTest10HeaderView.m @@ -0,0 +1,215 @@ +// +// AllTest10HeaderView.m +// FriendListDemo +// +// Created by 谢海龙 on 2018/5/8. +// Copyright © 2018年 bsj_mac_2. All rights reserved. +// + +#import "AllTest10HeaderView.h" +#import "AllTest10Model.h" + +@interface AllTest10HeaderView () +@property (strong, nonatomic) MyLinearLayout *rootLayout; +@property(nonatomic, strong) UIImageView *headImageView; +@property(nonatomic, strong) UILabel *nickNameLabel; +@property(nonatomic, strong) UILabel *textMessageLabel; +@property(nonatomic, strong) UILabel *timeLabel; +@property(nonatomic, strong) UILabel *browLabel; +@property(nonatomic, strong) UILabel *giveLikeLabel; +@property (strong, nonatomic) NSArray * nineImageViews; +@property (strong, nonatomic) MyFlowLayout * nineFlowLayout; +@property (strong, nonatomic) UIButton * giveLikeButton; +@property (strong, nonatomic) UIButton * commentsButton; +@property (strong, nonatomic) MyLinearLayout *giveLikeLayout; +@end + +@implementation AllTest10HeaderView + +- (instancetype)initWithReuseIdentifier:(NSString *)reuseIdentifier { + if (self = [super initWithReuseIdentifier:reuseIdentifier]) { + [self createLinearRootLayout]; + } + return self; +} + +#pragma mark -- Layout Construction + +//用线性布局来实现UI界面 +-(void)createLinearRootLayout +{ + _rootLayout= [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + _rootLayout.padding = UIEdgeInsetsMake(10, 10, 0.3, 10); + _rootLayout.backgroundColor = [UIColor whiteColor]; + _rootLayout.myHorzMargin = MyLayoutPos.safeAreaMargin; + _rootLayout.myHeight = MyLayoutSize.wrap; + [self.contentView addSubview:_rootLayout]; + + _headImageView = [UIImageView new]; + _headImageView.myWidth = 50; + _headImageView.myHeight = 50; + _headImageView.backgroundColor = [UIColor colorWithRed:240.0/255 green:240.0/255 blue:240.0/255 alpha:1]; + _headImageView.contentMode = UIViewContentModeScaleAspectFill; + _headImageView.clipsToBounds = YES; + [_rootLayout addSubview:_headImageView]; + + + MyLinearLayout *messageLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + messageLayout.weight = 1; + messageLayout.myLeading = 5; + messageLayout.subviewVSpace = 5; + [_rootLayout addSubview:messageLayout]; + + _nickNameLabel = [UILabel new]; + _nickNameLabel.text = @""; + _nickNameLabel.font = [UIFont systemFontOfSize:14]; + [messageLayout addSubview:_nickNameLabel]; + + [self.nickNameLabel sizeToFit]; + + _textMessageLabel = [UILabel new]; + _textMessageLabel.text = @""; + _textMessageLabel.myLeading = 0; + _textMessageLabel.myTrailing = 0; + _textMessageLabel.myHeight = MyLayoutSize.wrap; + [messageLayout addSubview:_textMessageLabel]; + + _nineFlowLayout = [[MyFlowLayout alloc] initWithOrientation:MyOrientation_Vert arrangedCount:3]; + _nineFlowLayout.gravity = MyGravity_Horz_Fill; + _nineFlowLayout.myHorzMargin = 0; + _nineFlowLayout.subviewHSpace = 5; + _nineFlowLayout.subviewVSpace = 5; + _nineFlowLayout.myHeight = MyLayoutSize.wrap; + [messageLayout addSubview:_nineFlowLayout]; + + _timeLabel = [UILabel new]; + _timeLabel.text = @""; + _timeLabel.font = [UIFont systemFontOfSize:14]; + [messageLayout addSubview:_timeLabel]; + + MyLinearLayout *hLinearLayout = [[MyLinearLayout alloc] initWithOrientation:MyOrientation_Horz]; + hLinearLayout.myHeight = MyLayoutSize.wrap; + hLinearLayout.gravity = MyGravity_Horz_Between | MyGravity_Vert_Center; + hLinearLayout.myHorzMargin = 0; + [messageLayout addSubview:hLinearLayout]; + + _browLabel = [UILabel new]; + _browLabel.text = @""; + _browLabel.font = [UIFont systemFontOfSize:14]; + _browLabel.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); //尺寸自适应。 + [hLinearLayout addSubview:_browLabel]; + + MyLinearLayout *actionshLinearLayout = [[MyLinearLayout alloc] initWithOrientation:MyOrientation_Horz]; + actionshLinearLayout.paddingRight = 5; + actionshLinearLayout.myHeight = MyLayoutSize.wrap; + actionshLinearLayout.gravity = MyGravity_Vert_Center; + actionshLinearLayout.myHorzMargin = 0; + [hLinearLayout addSubview:actionshLinearLayout]; + + _giveLikeButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_giveLikeButton setImage:[UIImage imageNamed:@"friend_zan"] forState:UIControlStateNormal]; + [_giveLikeButton setImage:[UIImage imageNamed:@"zan_select"] forState:UIControlStateSelected]; + _giveLikeButton.mySize = CGSizeMake(30, 30); + [_giveLikeButton addTarget:self action:@selector(praiseClick) forControlEvents:UIControlEventTouchUpInside]; + [_giveLikeButton sizeToFit]; + [actionshLinearLayout addSubview:_giveLikeButton]; + + _commentsButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_commentsButton setImage:[UIImage imageNamed:@"interactive_fill"] forState:UIControlStateNormal]; + [_commentsButton addTarget:self action:@selector(commentsClick) forControlEvents:UIControlEventTouchUpInside]; + _commentsButton.myLeft = 20; + _commentsButton.mySize = CGSizeMake(30, 30); + [_commentsButton sizeToFit]; + [actionshLinearLayout addSubview:_commentsButton]; + + MyLinearLayout *giveLikeLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + giveLikeLayout.padding = UIEdgeInsetsMake(8, 5, 8, 5); + giveLikeLayout.myHorzMargin = 0; + giveLikeLayout.backgroundColor = [UIColor colorWithRed:240.0/255 green:240.0/255 blue:240.0/255 alpha:1]; + [messageLayout addSubview:giveLikeLayout]; + _giveLikeLayout = giveLikeLayout; + _giveLikeLabel = [UILabel new]; + _giveLikeLabel.text = @""; + _giveLikeLabel.textColor = [UIColor blueColor]; + _giveLikeLabel.font = [UIFont systemFontOfSize:12]; + _giveLikeLabel.myHorzMargin = 0; + _giveLikeLabel.myHeight = MyLayoutSize.wrap; + [giveLikeLayout addSubview:_giveLikeLabel]; + +} + +- (void)praiseClick { + if (self.praiseClickHandler) { + self.praiseClickHandler(self,self.model); + } +} + +- (void)commentsClick { + if (self.commentsClickHandler) { + self.commentsClickHandler(self,self.model); + } +} + +- (void)setCountWithImageArray:(NSArray *)array { + [_nineFlowLayout removeAllSubviews]; + __weak typeof(self) weakSelf = self; + [self.nineImageViews enumerateObjectsUsingBlock:^(UIImageView * _Nonnull imageView, NSUInteger idx, BOOL * _Nonnull stop) { + if (idx < array.count) { + imageView.image = [UIImage imageNamed:array[idx]]; + [weakSelf.nineFlowLayout addSubview:imageView]; + } + }]; +} + +- (void)setModel:(AllTest10Model *)model { + _model = model; + + UIImage *image = [UIImage imageNamed:model.icon]; + _headImageView.image = image; + _giveLikeButton.selected = model.isGiveLike; + _nickNameLabel.text = model.name; + [_nickNameLabel sizeToFit]; + _textMessageLabel.text = model.content; + [_textMessageLabel sizeToFit]; + _timeLabel.text = model.time; + [_timeLabel sizeToFit]; + _browLabel.text = [NSString stringWithFormat:@"浏览%ld次",model.browCount]; + [_browLabel sizeToFit]; + + NSString *giveLikeText = model.giveLikeNames.count > 0 ? [NSString stringWithFormat:@"%@等点了👍",[model.giveLikeNames componentsJoinedByString:@"、"]] : @""; + _giveLikeLayout.visibility = model.giveLikeNames.count > 0 ? MyVisibility_Visible : MyVisibility_Gone; + _nineFlowLayout.visibility = model.commentsImageUrls.count > 0 ? MyVisibility_Visible : MyVisibility_Gone; + _giveLikeLabel.text = giveLikeText; + [_giveLikeLabel sizeToFit]; + [self setCountWithImageArray:model.commentsImageUrls]; +} + +//如果您的最低支持是iOS8,那么你可以重载这个方法来动态的评估cell的高度,Autolayout内部是通过这个方法来评估高度的,因此如果用MyLayout实现的话就不需要调用基类的方法,而是调用根布局视图的sizeThatFits来评估获取动态的高度。 +- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority +{ + if (@available(iOS 11.0, *)) { + //如果你的界面要支持横屏的话,因为iPhoneX的横屏左右有44的安全区域,所以这里要减去左右的安全区域的值,来作为布局宽度尺寸的评估值。 + //如果您的界面不需要支持横屏,或者延伸到安全区域外则不需要做这个特殊处理,而直接使用else部分的代码即可。 + return [self.rootLayout sizeThatFits:CGSizeMake(targetSize.width - self.safeAreaInsets.left - self.safeAreaInsets.right, targetSize.height)]; + } else { + return [self.rootLayout sizeThatFits:targetSize]; + } +} + +- (NSArray *)nineImageViews { + if (!_nineImageViews) { + NSMutableArray *imageViewArray = [NSMutableArray array]; + for (int i = 0; i<9; i++) { + UIImageView *imageView = [[UIImageView alloc] init]; + imageView.contentMode = UIViewContentModeScaleAspectFill; + imageView.clipsToBounds = YES; + imageView.backgroundColor = [UIColor colorWithRed:240.0/255 green:240.0/255 blue:240.0/255 alpha:1]; + imageView.heightSize.equalTo(imageView.widthSize); + [imageViewArray addObject:imageView]; + } + _nineImageViews = [imageViewArray copy]; + } + return _nineImageViews; +} + +@end diff --git a/MyLayoutDemo/AllTest10Model.h b/MyLayoutDemo/AllTest10Model.h new file mode 100644 index 0000000..a71008e --- /dev/null +++ b/MyLayoutDemo/AllTest10Model.h @@ -0,0 +1,45 @@ +// +// AllTest10Model.h +// FriendListDemo +// +// Created by bsj_mac_2 on 2018/5/9. +// Copyright © 2018年 bsj_mac_2. All rights reserved. +// + +#import + +@interface AllTest10Model : NSObject + +@property (copy, nonatomic) NSString * name; +@property (copy, nonatomic) NSString * icon; +@property (copy, nonatomic) NSString * content; +@property (copy, nonatomic) NSString * time; +@property (strong, nonatomic) NSArray * commentsImageUrls; +/* + 浏览次数 + */ +@property (assign, nonatomic) NSInteger browCount; +/* + 点赞姓名集合(简洁起见,实际开发中转换为对应) + */ +@property (strong, nonatomic) NSMutableArray * giveLikeNames; +/* + 评论集合 + */ +@property (strong, nonatomic) NSMutableArray * comments; +/* + 是否点赞 + */ +@property (assign, nonatomic) BOOL isGiveLike; + +/* + 模拟评论数据 + */ ++ (NSArray*)getRandTestDatasWithNumber:(NSInteger)modelsNumber; + +/* + 返回单挑模拟评论数据 + */ ++ (NSString *)randTestComments; + +@end diff --git a/MyLayoutDemo/AllTest10Model.m b/MyLayoutDemo/AllTest10Model.m new file mode 100644 index 0000000..b983ed5 --- /dev/null +++ b/MyLayoutDemo/AllTest10Model.m @@ -0,0 +1,83 @@ +// +// AllTest10Model.m +// FriendListDemo +// +// Created by bsj_mac_2 on 2018/5/9. +// Copyright © 2018年 bsj_mac_2. All rights reserved. +// + +#import "AllTest10Model.h" + +@implementation AllTest10Model + ++ (NSString *)randTestComments { + NSArray *textMessages = @[NSLocalizedString(@"A single line text", @""), + NSLocalizedString(@"This Demo is used to introduce the solution when use layout view to realize the UITableViewCell's dynamic height. We only need to use the layout view's sizeThatFits method to evaluate the size of the layout view. and you can touch the Cell to shrink the height when the Cell has a picture.", @""), + NSLocalizedString(@"Through layout view's sizeThatFits method can assess a UITableViewCell dynamic height.EstimateLayoutRect just to evaluate layout size but not set the size of the layout. here don't preach the width of 0 is the cause of the above UITableViewCell set the default width is 320 (regardless of any screen), so if we pass the width of 0 will be according to the width of 320 to evaluate UITableViewCell dynamic height, so when in 375 and 375 the width of the assessment of height will not be right, so here you need to specify the real width dimension;And the height is set to 0 mean height is not a fixed value need to evaluate. you can use all type layout view to realize UITableViewCell.", @""), + NSLocalizedString(@"This section not only has text but also hav picture. and picture below at text, text will wrap", @"") + ]; + return textMessages[arc4random_uniform((uint32_t)textMessages.count)]; +} + ++ (NSArray*)getRandTestDatasWithNumber:(NSInteger)modelsNumber { + + NSArray *headImages = @[@"head1", + @"head2", + @"minions1", + @"minions4" + ]; + + NSArray *nickNames = @[@"欧阳大哥", + @"醉里挑灯看键", + @"张三", + @"李四" + ]; + + NSArray *contentTexts = @[@"", + NSLocalizedString(@"A single line text", @""), + NSLocalizedString(@"This Demo is used to introduce the solution when use layout view to realize the UITableViewCell's dynamic height. We only need to use the layout view's sizeThatFits method to evaluate the size of the layout view. and you can touch the Cell to shrink the height when the Cell has a picture.", @""), + NSLocalizedString(@"Through layout view's sizeThatFits method can assess a UITableViewCell dynamic height.EstimateLayoutRect just to evaluate layout size but not set the size of the layout. here don't preach the width of 0 is the cause of the above UITableViewCell set the default width is 320 (regardless of any screen), so if we pass the width of 0 will be according to the width of 320 to evaluate UITableViewCell dynamic height, so when in 375 and 375 the width of the assessment of height will not be right, so here you need to specify the real width dimension;And the height is set to 0 mean height is not a fixed value need to evaluate. you can use all type layout view to realize UITableViewCell.", @""), + NSLocalizedString(@"This section not only has text but also hav picture. and picture below at text, text will wrap", @"") + ]; + + NSArray *textMessages = @[NSLocalizedString(@"A single line text", @""), + NSLocalizedString(@"This Demo is used to introduce the solution when use layout view to realize the UITableViewCell's dynamic height. We only need to use the layout view's sizeThatFits method to evaluate the size of the layout view. and you can touch the Cell to shrink the height when the Cell has a picture.", @""), + NSLocalizedString(@"Through layout view's sizeThatFits method can assess a UITableViewCell dynamic height.EstimateLayoutRect just to evaluate layout size but not set the size of the layout. here don't preach the width of 0 is the cause of the above UITableViewCell set the default width is 320 (regardless of any screen), so if we pass the width of 0 will be according to the width of 320 to evaluate UITableViewCell dynamic height, so when in 375 and 375 the width of the assessment of height will not be right, so here you need to specify the real width dimension;And the height is set to 0 mean height is not a fixed value need to evaluate. you can use all type layout view to realize UITableViewCell.", @""), + NSLocalizedString(@"This section not only has text but also hav picture. and picture below at text, text will wrap", @"") + ]; + + NSArray *commentsImageUrls = @[@"head1",@"head2",@"bk3",@"image1",@"image2",@"image3",@"image4"]; + + NSMutableArray *arrays = [NSMutableArray array]; + for (int i = 0; i + +@interface AllTest10ViewController : UIViewController + +@end diff --git a/MyLayoutDemo/AllTest10ViewController.m b/MyLayoutDemo/AllTest10ViewController.m new file mode 100644 index 0000000..e9d3edd --- /dev/null +++ b/MyLayoutDemo/AllTest10ViewController.m @@ -0,0 +1,140 @@ +// +// AllTest10ViewController.m +// FriendListDemo +// +// Created by bsj_mac_2 on 2018/5/7. +// Copyright © 2018年 bsj_mac_2. All rights reserved. +// + +#import "AllTest10ViewController.h" +#import "AllTest10Cell.h" +#import "YYFPSLabel.h" +#import "AllTest10HeaderView.h" +#import "AllTest10Model.h" + +@interface AllTest10ViewController () +@property (strong, nonatomic) NSMutableArray * cellDatas; +@property (strong, nonatomic) UITableView * tableView; +@end + +@implementation AllTest10ViewController + +-(void)loadView +{ + MyFrameLayout *rootLayout = [[MyFrameLayout alloc] init]; + rootLayout.backgroundColor = [UIColor whiteColor]; + self.view = rootLayout; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view from its nib. + [self setNavBar]; + [self setupUI]; + [self loadDatas]; +} + +- (void)setNavBar { + self.navigationItem.titleView = [[YYFPSLabel alloc] initWithFrame:CGRectMake(0, 0, 80, 40)]; + self.title = @"朋友圈"; +} + +- (void)setupUI { + self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped]; + self.tableView.myMargin = 0; + self.tableView.delegate = self; + self.tableView.dataSource = self; + self.tableView.sectionFooterHeight = 0.0001; + self.tableView.estimatedRowHeight = 100; + self.tableView.rowHeight = UITableViewAutomaticDimension; + self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension; + self.tableView.estimatedSectionHeaderHeight = 100; + self.tableView.backgroundColor = [UIColor whiteColor]; + self.tableView.separatorInset = UIEdgeInsetsMake(0, 65, 0, 10); + self.tableView.separatorColor = [UIColor clearColor]; + [self.tableView registerClass:[AllTest10Cell class] forCellReuseIdentifier:@"AllTest10Cell"]; + [self.tableView registerClass:[AllTest10HeaderView class] forHeaderFooterViewReuseIdentifier:@"AllTest10HeaderView"]; + [self.view addSubview:self.tableView]; +} + +- (void)loadDatas { + [self.cellDatas addObjectsFromArray:[AllTest10Model getRandTestDatasWithNumber:60]]; + [self.tableView reloadData]; +} + +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { + return self.cellDatas.count; +} + +- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { + AllTest10HeaderView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"AllTest10HeaderView"]; + headerView.model = self.cellDatas[section]; + __weak typeof(self) weakSelf = self; + headerView.praiseClickHandler = ^(AllTest10HeaderView *headerView, AllTest10Model *model) { + model.isGiveLike = !model.isGiveLike; + if (model.isGiveLike) { + [model.giveLikeNames addObject:@"乐天"]; + } else { + [model.giveLikeNames removeObject:@"乐天"]; + } + [weakSelf.tableView reloadData]; + }; + headerView.commentsClickHandler = ^(AllTest10HeaderView *headerView, AllTest10Model *model) { + [model.comments addObject:[AllTest10Model randTestComments]]; + [weakSelf.tableView reloadData]; + }; + return headerView; +} + +- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { + MyBaseLayout *footerView = [[MyBaseLayout alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 10)]; + footerView.backgroundColor = [UIColor whiteColor]; + footerView.myHorzMargin = MyLayoutPos.safeAreaMargin; + MyBorderline *bld = [[MyBorderline alloc] initWithColor:[UIColor colorWithRed:216.0/255 green:214.0/255 blue:216.0/255 alpha:1]]; + footerView.bottomBorderline = bld; + return footerView; +} + +- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { + return 10; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + AllTest10Model *model = self.cellDatas[section]; + return model.comments.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + AllTest10Cell *cell = [tableView dequeueReusableCellWithIdentifier:@"AllTest10Cell"]; + AllTest10Model *model = self.cellDatas[indexPath.section]; + cell.selectionStyle = 0; + [cell setCommentsText:model.comments[indexPath.row]]; + return cell; +} + +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:@"是否删除此条信息" preferredStyle:UIAlertControllerStyleActionSheet]; + __weak typeof(self) weakSelf = self; + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"删除" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) { + AllTest10Model *model = weakSelf.cellDatas[indexPath.section]; + [model.comments removeObjectAtIndex:indexPath.row]; + [UIView performWithoutAnimation:^{ + [weakSelf.tableView reloadData]; + }]; + }]; + UIAlertAction *cancleAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { + + }]; + [alertController addAction:cancleAction]; + [alertController addAction:okAction]; + [self presentViewController:alertController animated:YES completion:nil]; +} + +- (NSMutableArray *)cellDatas { + if (!_cellDatas) { + _cellDatas = [NSMutableArray array]; + } + return _cellDatas; +} + +@end diff --git a/MyLayoutDemo/AllTest11ViewController.h b/MyLayoutDemo/AllTest11ViewController.h new file mode 100644 index 0000000..fe88ad8 --- /dev/null +++ b/MyLayoutDemo/AllTest11ViewController.h @@ -0,0 +1,16 @@ +// +// AllTest11ViewController.h +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import + +/** + *❁5.Subviews layout transform + */ +@interface AllTest11ViewController : UIViewController + +@end diff --git a/MyLayoutDemo/AllTest11ViewController.m b/MyLayoutDemo/AllTest11ViewController.m new file mode 100644 index 0000000..e5c605b --- /dev/null +++ b/MyLayoutDemo/AllTest11ViewController.m @@ -0,0 +1,193 @@ +// +// AllTest11ViewController.m +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import "AllTest11ViewController.h" +#import "MyLayout.h" +#import "CFTool.h" + +@interface AllTest11ViewController () + +@property(nonatomic, strong) MyBaseLayout *contentLayout; //对于布局视图来说,所有种类布局的布局视图都支持layoutTransform属性。 + +@end + +@implementation AllTest11ViewController + +-(void)loadView +{ + /* + + 这个DEMO主要用来演示布局视图的layoutTransform属性的使用和设置方法,这个属性用来对布局视图内所有子视图的位置进行坐标变换。只要你了解CGAffineTransform的设置和使用 + 方法,就可以用他来进行各种布局视图内子视图的整体的坐标变换,比如:平移、缩放、水平反转、垂直反转、旋转等以及一些复合的坐标变换。在下面的例子里面我分别列举了一些常见的布局位置 + 坐标变换的设置方法以及参数。 + + + 当你的布局内所有视图都需要有统一的变换的动画时,你可以借助layoutTransform属性并且配合layoutAnimationWithDuration方法来实现动画效果。 + + */ + + self.edgesForExtendedLayout = UIRectEdgeNone; //设置视图控制器中的视图尺寸不延伸到导航条或者工具条下面。您可以注释这句代码看看效果。 + + + MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + rootLayout.gravity = MyGravity_Horz_Fill; //里面所有子视图的宽度都填充为和父视图一样宽。 + rootLayout.backgroundColor = [UIColor whiteColor]; + self.view = rootLayout; + + //添加操作按钮。 + MyFlowLayout *actionLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3]; + actionLayout.myHeight = MyLayoutSize.wrap; + actionLayout.gravity = MyGravity_Horz_Fill; //所有子视图水平填充,也就是所有子视图的宽度相等。 + actionLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); + actionLayout.subviewHSpace = 5; + actionLayout.subviewVSpace = 5; + [rootLayout addSubview:actionLayout]; + + [actionLayout addSubview:[self createActionButton:NSLocalizedString(@"Identity", @"") + action:@selector(handleIdentityTransform:)]]; + [actionLayout addSubview:[self createActionButton:NSLocalizedString(@"Translation", @"") + action:@selector(handleTranslationTransform:)]]; + [actionLayout addSubview:[self createActionButton:NSLocalizedString(@"Scale", @"") + action:@selector(handleScaleTransform:)]]; + [actionLayout addSubview:[self createActionButton:NSLocalizedString(@"Horz Reflection", @"") + action:@selector(handleHorzReflectionTransform:)]]; + [actionLayout addSubview:[self createActionButton:NSLocalizedString(@"Vert Reflection", @"") + action:@selector(handleVertReflectionTransform:)]]; + [actionLayout addSubview:[self createActionButton:NSLocalizedString(@"Reverse", @"") + action:@selector(handleReverseTransform:)]]; + + + //下面是用于测试的layoutTransform属性的布局视图,本系统中的所有布局视图都支持layoutTransform属性。 + MyFlowLayout *contentLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:4]; + contentLayout.backgroundColor = [CFTool color:5]; + contentLayout.weight = 1.0; //占用线性布局中的剩余高度。 + contentLayout.subviewSpace = 10; + [rootLayout addSubview:contentLayout]; + self.contentLayout = contentLayout; + + for (int i = 0; i < 14; i++) + { + UILabel *label = [UILabel new]; + label.text = [NSString stringWithFormat:@"%d", i]; + label.backgroundColor = [UIColor redColor]; + label.mySize = CGSizeMake(40, 40); + label.textAlignment = NSTextAlignmentCenter; + [contentLayout addSubview:label]; + } + + + +} + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. + +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +#pragma mark -- Layout Construction + +//创建动作操作按钮。 +-(UIButton*)createActionButton:(NSString*)title action:(SEL)action +{ + UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; + [button setTitle:title forState:UIControlStateNormal]; + button.titleLabel.font = [CFTool font:14]; + [button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside]; + button.myHeight = 30; + button.layer.borderColor = [UIColor lightGrayColor].CGColor; + button.layer.borderWidth = 0.5; + + return button; + +} + +#pragma mark -- Handle Method + +-(void)handleIdentityTransform:(id)sender +{ + //还原位置的坐标变换,这也是默认值。 + self.contentLayout.layoutTransform = CGAffineTransformIdentity; + [self.contentLayout layoutAnimationWithDuration:0.3]; +} + +-(void)handleTranslationTransform:(id)sender +{ + //子视图的整体水平和垂直的平移变换。 + static BOOL flag = YES; + if (flag) + self.contentLayout.layoutTransform = CGAffineTransformMakeTranslation(100, 0); + else + self.contentLayout.layoutTransform = CGAffineTransformMakeTranslation(100, 100); + + flag = !flag; + + [self.contentLayout layoutAnimationWithDuration:0.3]; +} + +-(void)handleScaleTransform:(id)sender +{ + //布局内子视图位置的放大和缩小的位置变换。因为缩放是以布局视图的中心点为中心进行缩放的,所以如果是想要以某个点为中心进行缩放。所以在缩放的同时还需要进行位移的调整。 + //下面的例子里面,因为缩放默认是以布局视图的中心点为中心进行缩放,但这里是想以第一个子视图保持不变而进行缩放,所以除了要进行缩放外,还需要调整所有子视图的偏移,因为第一个 + //子视图的中心点的位置按照布局视图中心点原点坐标的话位置是: (20 - 布局视图的宽度/2, 20 - 布局视图的高度/2), 这里的20是因为视图的高度都是40。 + //所以只要保证第一个子视图的中心点在放大后的位置还是一样,就会实现以第一个子视图为中心进行放大的效果。 + + + CGSize size = self.contentLayout.frame.size; + + //缩放因子。 + static CGFloat factor = 2; + self.contentLayout.layoutTransform = CGAffineTransformMake(factor, 0, 0, factor, (1 - factor) * (20 - size.width / 2.0), (1 - factor) * (20 - size.height / 2)); //这里因为要让第一个子视图的位置保持不变,所以tx,ty参数需要进行特殊设置。 + + if (factor == 2) + factor = 0.9; + else + factor = 2; + + [self.contentLayout layoutAnimationWithDuration:0.3]; +} + +-(void)handleHorzReflectionTransform:(id)sender +{ + //布局内所有子视图都进行水平翻转排列,也就是水平镜像的效果。 + self.contentLayout.layoutTransform = CGAffineTransformMake(-1,0,0,1,0,0); + [self.contentLayout layoutAnimationWithDuration:0.3]; +} + +-(void)handleVertReflectionTransform:(id)sender +{ + //布局内所有子视图都进行垂直翻转排列,也就是垂直镜像的效果。 + self.contentLayout.layoutTransform = CGAffineTransformMake(1,0,0,-1,0,0); + [self.contentLayout layoutAnimationWithDuration:0.3]; + +} + + +-(void)handleReverseTransform:(id)sender +{ + //布局内所有子视图整体翻转180度的效果。 + self.contentLayout.layoutTransform = CGAffineTransformMake(-1,0,0,-1,0,0); + [self.contentLayout layoutAnimationWithDuration:0.3]; +} + +@end diff --git a/MyLayoutDemo/AllTest12ViewController.h b/MyLayoutDemo/AllTest12ViewController.h new file mode 100644 index 0000000..c9a0735 --- /dev/null +++ b/MyLayoutDemo/AllTest12ViewController.h @@ -0,0 +1,16 @@ +// +// AllTest11ViewController.h +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import + +/** +*❁6.MyLayout & AutoLayout +*/ +@interface AllTest12ViewController : UIViewController + +@end diff --git a/MyLayoutDemo/AllTest12ViewController.m b/MyLayoutDemo/AllTest12ViewController.m new file mode 100644 index 0000000..cd8e31e --- /dev/null +++ b/MyLayoutDemo/AllTest12ViewController.m @@ -0,0 +1,218 @@ +// +// AllTest11ViewController.m +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import "AllTest12ViewController.h" +#import "MyLayout.h" +#import "CFTool.h" + +@interface AllTest12ViewController () + + +@end + +@implementation AllTest12ViewController + +- (void)viewDidLoad { + self.edgesForExtendedLayout = UIRectEdgeNone; //设置视图控制器中的视图尺寸不延伸到导航条或者工具条下面。您可以注释这句代码看看效果。 + + [super viewDidLoad]; + self.view.backgroundColor = [UIColor whiteColor]; + + // Do any additional setup after loading the view. + /* + 这个DEMO演示MyLayout和AutoLayout的代码结合的例子。因为布局视图也是一个普通的视图,因此可以把一个布局视图添加到现有的其他非布局父视图中并且对布局视图设置 + 约束。 因为布局视图可以设置尺寸自适应,就如UILabel一样因为具有intrinsicContentSize的能力,因此不需要在约束中明确设置宽度或者高度约束。当一个布局视图 + 的高度或者宽度都是由子视图决定的,也就是尺寸设置为自适应时,布局视图也不需要明确的设置宽度或者高度的约束。而且其他视图还可以依赖这种布局视图尺寸的自包含的能力。 + + 本例子由3个子示例组成。 + + */ + + //容器视图包含一个垂直线性布局视图,垂直线性布局视图的尺寸由2个子视图决定,容器视图的尺寸由线性布局视图决定 + [self demo1]; + //一个线性布局视图的宽度为某个具体的约束值,高度由子视图决定。另外一个兄弟视图在线性布局视图的下面,一个兄弟视图在线性布局的右边。 + [self demo2]; + //一个水平线性布局的高度固定,宽度自适应,另外一个兄弟视图在水平线性布局视图的右边。 + [self demo3]; + + +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +-(void)demo1 +{ + //容器视图包含一个垂直线性布局视图,垂直线性布局视图的尺寸由2个子视图决定,容器视图的尺寸由线性布局视图决定 + + UIView *containerView = [UIView new]; + containerView.backgroundColor = [UIColor greenColor]; + containerView.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:containerView]; + + + MyLinearLayout *linelayout = [MyLinearLayout new]; + linelayout.translatesAutoresizingMaskIntoConstraints = NO; + linelayout.backgroundColor = [UIColor redColor]; + [containerView addSubview:linelayout]; + + + UIView *sbv1 = [UIView new]; + sbv1.backgroundColor = [UIColor blueColor]; + [linelayout addSubview:sbv1]; + UIView *sbv2 = [UIView new]; + sbv2.backgroundColor = [UIColor blueColor]; + [linelayout addSubview:sbv2]; + + + + //MyLayout中的约束设置方法 + linelayout.orientation = MyOrientation_Vert; + linelayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + linelayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); + linelayout.subviewSpace = 10; + + sbv1.mySize = CGSizeMake(100, 40); + sbv2.mySize = CGSizeMake(150, 50); + + + + //AutoLayout中的约束设置方法,采用iOS9提供的约束设置方法。 + [containerView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES; + [containerView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES; + //父视图约束依赖子视图约束会产生父视图的尺寸由子视图进行自适应的效果。 + [containerView.bottomAnchor constraintEqualToAnchor:linelayout.bottomAnchor constant:10].active = YES; + [containerView.rightAnchor constraintEqualToAnchor:linelayout.rightAnchor constant:10].active = YES; + + //线性布局视图的宽度和高度自适应,所以不需要设置任何高度和宽度的约束。 + [linelayout.leftAnchor constraintEqualToAnchor:containerView.leftAnchor constant:10].active = YES; + [linelayout.topAnchor constraintEqualToAnchor:containerView.topAnchor constant:10].active = YES; + +} + +-(void)demo2 +{ + //一个线性布局视图的宽度为某个具体的约束值,高度由子视图决定。另外一个兄弟视图在线性布局视图的下面,一个兄弟视图在线性布局的右边。 + + MyLinearLayout *linelayout = [MyLinearLayout new]; + linelayout.translatesAutoresizingMaskIntoConstraints = NO; + linelayout.backgroundColor = [UIColor redColor]; + [self.view addSubview:linelayout]; + + + UIView *sbv1 = [UIView new]; + sbv1.backgroundColor = [UIColor blueColor]; + [linelayout addSubview:sbv1]; + UIView *sbv2 = [UIView new]; + sbv2.backgroundColor = [UIColor blueColor]; + [linelayout addSubview:sbv2]; + + + UIView *brotherView1 = [UIView new]; + brotherView1.translatesAutoresizingMaskIntoConstraints = NO; + brotherView1.backgroundColor = [UIColor greenColor]; + [self.view addSubview:brotherView1]; + + UIView *brotherView2 = [UIView new]; + brotherView2.translatesAutoresizingMaskIntoConstraints = NO; + brotherView2.backgroundColor = [UIColor orangeColor]; + [self.view addSubview:brotherView2]; + + + //MyLayout中的约束设置 + linelayout.orientation = MyOrientation_Vert; + linelayout.myHeight = MyLayoutSize.wrap; + linelayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); + linelayout.subviewSpace = 10; + + sbv1.mySize = CGSizeMake(100, 40); + sbv2.mySize = CGSizeMake(150, 50); + + + //AutoLayout中的约束设置 + //线性布局因为高度是自适应的所以不需要设置高度约束。 + [linelayout.leftAnchor constraintEqualToAnchor:self.view.leftAnchor constant:10].active = YES; + [linelayout.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:180].active = YES; + //当布局视图的高度由子视图自适应且宽度为autolayout约束时,目前只支持对布局视图设置明确宽度约束才有效。 + //同时设置左右边界约束来推导布局视图宽度,并且高度又自适应目前不支持。 + [linelayout.widthAnchor constraintEqualToAnchor:self.view.widthAnchor constant:-150].active = YES; + + [brotherView1.leftAnchor constraintEqualToAnchor:linelayout.leftAnchor].active = YES; + [brotherView1.topAnchor constraintEqualToAnchor:linelayout.bottomAnchor constant:10].active = YES; + [brotherView1.widthAnchor constraintEqualToAnchor:linelayout.widthAnchor].active = YES; + [brotherView1.heightAnchor constraintEqualToAnchor:linelayout.heightAnchor].active = YES; + + + [brotherView2.leftAnchor constraintEqualToAnchor:linelayout.rightAnchor constant:10].active = YES; + [brotherView2.topAnchor constraintEqualToAnchor:linelayout.topAnchor].active = YES; + [brotherView2.widthAnchor constraintEqualToConstant:50].active = YES; + [brotherView2.heightAnchor constraintEqualToAnchor:linelayout.heightAnchor].active = YES; + +} + +-(void)demo3 +{ + MyLinearLayout *linelayout = [MyLinearLayout new]; + linelayout.translatesAutoresizingMaskIntoConstraints = NO; + linelayout.backgroundColor = [UIColor redColor]; + [self.view addSubview:linelayout]; + + + UIView *sbv1 = [UIView new]; + sbv1.backgroundColor = [UIColor blueColor]; + [linelayout addSubview:sbv1]; + UIView *sbv2 = [UIView new]; + sbv2.backgroundColor = [UIColor blueColor]; + [linelayout addSubview:sbv2]; + + + UIView *brotherView1 = [UIView new]; + brotherView1.translatesAutoresizingMaskIntoConstraints = NO; + brotherView1.backgroundColor = [UIColor greenColor]; + [self.view addSubview:brotherView1]; + + + //MyLayout中的约束设置 + linelayout.orientation = MyOrientation_Horz; + linelayout.myWidth = MyLayoutSize.wrap; + linelayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); + linelayout.subviewSpace = 10; + linelayout.gravity = MyGravity_Vert_Fill; + + + sbv1.myWidth = 50; + sbv2.myWidth = 60; + + + //AutoLayout中的约束设置 + [linelayout.leftAnchor constraintEqualToAnchor:self.view.leftAnchor constant:10].active = YES; + [linelayout.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:500].active = YES; + [linelayout.heightAnchor constraintEqualToConstant:60].active = YES; + + [brotherView1.leftAnchor constraintEqualToAnchor:linelayout.rightAnchor constant:10].active = YES; + [brotherView1.topAnchor constraintEqualToAnchor:linelayout.topAnchor].active = YES; + [brotherView1.widthAnchor constraintEqualToAnchor:linelayout.widthAnchor].active = YES; + [brotherView1.heightAnchor constraintEqualToAnchor:linelayout.heightAnchor].active = YES; + + +} + + +@end diff --git a/MyLayoutDemo/AllTest1TableViewCell.h b/MyLayoutDemo/AllTest1TableViewCell.h index eb036a7..852fbe7 100644 --- a/MyLayoutDemo/AllTest1TableViewCell.h +++ b/MyLayoutDemo/AllTest1TableViewCell.h @@ -12,6 +12,7 @@ /** * 动态高度UITableViewCell + * 本例子是将布局视图的高度自适应能力和在不使用AutoLayout的情况下实现UITableViewCell高度自适应的能力 */ @interface AllTest1TableViewCell : UITableViewCell diff --git a/MyLayoutDemo/AllTest1TableViewCell.m b/MyLayoutDemo/AllTest1TableViewCell.m index bcc40c6..ff52df1 100644 --- a/MyLayoutDemo/AllTest1TableViewCell.m +++ b/MyLayoutDemo/AllTest1TableViewCell.m @@ -27,11 +27,13 @@ -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStri self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self != nil) { + self.selectionStyle = UITableViewCellSelectionStyleNone; + /** * 您可以尝试用不同的布局来实现相同的功能。 */ - [self createLinearRootLayout]; - // [self createRelativeRootLayout]; + // [self createLinearRootLayout]; + [self createRelativeRootLayout]; // [self createFloatRootLayout]; @@ -53,7 +55,7 @@ -(void)setModel:(AllTest1DataModel*)model isImageMessageHidden:(BOOL)isImageMess if (model.imageMessage.length == 0) { - self.imageMessageImageView.myVisibility = MyVisibility_Gone; + self.imageMessageImageView.visibility = MyVisibility_Gone; } else { @@ -61,11 +63,11 @@ -(void)setModel:(AllTest1DataModel*)model isImageMessageHidden:(BOOL)isImageMess [self.imageMessageImageView sizeToFit]; if (isImageMessageHidden) { - self.imageMessageImageView.myVisibility = MyVisibility_Gone; + self.imageMessageImageView.visibility = MyVisibility_Gone; } else { - self.imageMessageImageView.myVisibility = MyVisibility_Visible; + self.imageMessageImageView.visibility = MyVisibility_Visible; } } @@ -85,20 +87,22 @@ - (void)setSelected:(BOOL)selected animated:(BOOL)animated { // Configure the view for the selected state } - //如果您的最低支持是iOS8,那么你可以重载这个方法来动态的评估cell的高度,Autolayout内部是通过这个方法来评估高度的,因此如果用MyLayout实现的话就不需要调用基类的方法,而是调用根布局视图的sizeThatFits来评估获取动态的高度。 + /**为了支持cell的高度自适应,您需要重载这个方法来动态的评估cell的高度,Autolayout内部是通过这个方法来评估高度的。因此如果用MyLayout实现的话就不需要调用基类的方法,而是调用根布局视图的sizeThatFits来评估获取动态的高度。 + 当然您也可以不通过重载这个方法来实现高度自适应,而是用AllTest1TableViewCellForAutoLayout类中介绍的方法:通过autolayout+mylayout的组合方式来实现高度自适应的能力,强烈建议采用后者,代码更加简单方便!!! + */ - (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority { /* - 通过布局视图的sizeThatFits方法能够评估出UITableViewCell的动态高度。sizeThatFits并不会进行布局而只是评估布局的尺寸。 - 因为cell的高度是自适应的,因此这里通过调用高度为wrap的布局视图的sizeThatFits来获取真实的高度。 + 通过布局视图的systemLayoutSizeFittingSize方法能够评估出UITableViewCell的动态高度。systemLayoutSizeFittingSize并不会进行布局而只是评估布局的尺寸。 + 因为cell的高度是自适应的,因此这里通过调用高度为wrap的布局视图的systemLayoutSizeFittingSize来获取真实的高度。 */ if (@available(iOS 11.0, *)) { //如果你的界面要支持横屏的话,因为iPhoneX的横屏左右有44的安全区域,所以这里要减去左右的安全区域的值,来作为布局宽度尺寸的评估值。 //如果您的界面不需要支持横屏,或者延伸到安全区域外则不需要做这个特殊处理,而直接使用else部分的代码即可。 - return [self.rootLayout sizeThatFits:CGSizeMake(targetSize.width - self.safeAreaInsets.left - self.safeAreaInsets.right, targetSize.height)]; + return [self.rootLayout systemLayoutSizeFittingSize:CGSizeMake(targetSize.width - self.safeAreaInsets.left - self.safeAreaInsets.right, targetSize.height) withHorizontalFittingPriority:horizontalFittingPriority verticalFittingPriority:verticalFittingPriority]; } else { - return [self.rootLayout sizeThatFits:targetSize]; //如果使用系统自带的分割线,请记得将返回的高度height+1 + return [self.rootLayout systemLayoutSizeFittingSize:targetSize withHorizontalFittingPriority:horizontalFittingPriority verticalFittingPriority:verticalFittingPriority]; //如果使用系统自带的分割线,请记得将返回的高度height+1 } } @@ -109,19 +113,18 @@ - (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPr -(void)createLinearRootLayout { _rootLayout= [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; - _rootLayout.topPadding = 5; - _rootLayout.bottomPadding = 5; - _rootLayout.cacheEstimatedRect = YES; + _rootLayout.paddingTop = 5; + _rootLayout.paddingBottom = 5; + _rootLayout.cacheEstimatedRect = YES; //这个属性只局限于在UITableViewCell中使用,用来优化tableviewcell的高度自适应的性能,其他地方请不要使用!!! /* 在UITableViewCell中使用MyLayout中的布局时请将布局视图作为contentView的子视图。如果我们的UITableViewCell的高度是动态的,请务必在将布局视图添加到contentView之前进行如下设置: _rootLayout.widthSize.equalTo(self.contentView.widthSize); - _rootLayout.wrapContentHeight = YES; + _rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); */ // _rootLayout.widthSize.equalTo(self.contentView.widthSize); _rootLayout.myHorzMargin = MyLayoutPos.safeAreaMargin; - _rootLayout.wrapContentHeight = YES; - _rootLayout.wrapContentWidth = NO; + _rootLayout.myHeight = MyLayoutSize.wrap; [self.contentView addSubview:_rootLayout]; //如果您将布局视图作为子视图添加到UITableViewCell本身,并且同时用了myLeft和myRight来做边界的话,那么有可能最终展示的宽度会不正确。经过试验是因为对UITableViewCell本身的KVO监控所得到的新老尺寸的问题导致的这应该是iOS的一个BUG。所以这里建议最好是把布局视图添加到UITableViewCell的子视图contentView里面去。 @@ -153,7 +156,7 @@ -(void)createLinearRootLayout _textMessageLabel.textColor = [CFTool color:4]; _textMessageLabel.myLeading = 0; _textMessageLabel.myTrailing = 0; //垂直线性布局里面如果同时设置了左右边距则能确定子视图的宽度,这里表示宽度和父视图相等。 - _textMessageLabel.wrapContentHeight = YES; //如果想让文本的高度是动态的,请在设置明确宽度的情况下将wrapContentHeight设置为YES。 + _textMessageLabel.myHeight = MyLayoutSize.wrap; //如果想让文本的高度是动态的,请将高度设置为自适应 [messageLayout addSubview:_textMessageLabel]; @@ -166,17 +169,17 @@ -(void)createLinearRootLayout -(void)createRelativeRootLayout { _rootLayout = [MyRelativeLayout new]; - _rootLayout.topPadding = 5; - _rootLayout.bottomPadding = 5; - _rootLayout.cacheEstimatedRect = YES; + _rootLayout.paddingTop = 5; + _rootLayout.paddingBottom = 5; + _rootLayout.cacheEstimatedRect = YES; //这个属性只局限于在UITableViewCell中使用,用来优化tableviewcell的高度自适应的性能,其他地方请不要使用!!! /* 在UITableViewCell中使用MyLayout中的布局时请将布局视图作为contentView的子视图。如果我们的UITableViewCell的高度是动态的,请务必在将布局视图添加到contentView之前进行如下设置: _rootLayout.widthSize.equalTo(self.contentView.widthSize); - _rootLayout.wrapContentHeight = YES; + _rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); */ _rootLayout.widthSize.equalTo(self.contentView.widthSize); - _rootLayout.wrapContentHeight = YES; + _rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); [self.contentView addSubview:_rootLayout]; @@ -201,7 +204,7 @@ -(void)createRelativeRootLayout _textMessageLabel.leadingPos.equalTo(_headImageView.trailingPos).offset(5); //文本消息的左边在头像视图的右边并偏移5个点。 _textMessageLabel.trailingPos.equalTo(_rootLayout.trailingPos); //文本消息的右边和父布局的右边对齐。上面2行代码也同时确定了文本消息的宽度。 _textMessageLabel.topPos.equalTo(_nickNameLabel.bottomPos).offset(5); //文本消息的顶部在昵称文本的底部并偏移5个点。 - _textMessageLabel.wrapContentHeight = YES; //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将wrapContentHeight设置为YES。 + _textMessageLabel.heightSize.equalTo(@(MyLayoutSize.wrap)); //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将高度设置为自适应 [_rootLayout addSubview:_textMessageLabel]; @@ -216,17 +219,17 @@ -(void)createRelativeRootLayout -(void)createFloatRootLayout { _rootLayout= [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; - _rootLayout.topPadding = 5; - _rootLayout.bottomPadding = 5; - _rootLayout.cacheEstimatedRect = YES; + _rootLayout.paddingTop = 5; + _rootLayout.paddingBottom = 5; + _rootLayout.cacheEstimatedRect = YES; //这个属性只局限于在UITableViewCell中使用,用来优化tableviewcell的高度自适应的性能,其他地方请不要使用!!! /* 在UITableViewCell中使用MyLayout中的布局时请将布局视图作为contentView的子视图。如果我们的UITableViewCell的高度是动态的,请务必在将布局视图添加到contentView之前进行如下设置: _rootLayout.widthSize.equalTo(self.contentView.widthSize); - _rootLayout.wrapContentHeight = YES; + _rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); */ _rootLayout.widthSize.equalTo(self.contentView.widthSize); - _rootLayout.wrapContentHeight = YES; + _rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); [self.contentView addSubview:_rootLayout]; /* @@ -250,7 +253,7 @@ -(void)createFloatRootLayout _textMessageLabel.font = [CFTool font:15]; _textMessageLabel.textColor = [CFTool color:4]; _textMessageLabel.weight = 1; //占用剩余宽度 - _textMessageLabel.wrapContentHeight = YES; //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将wrapContentHeight设置为YES。 + _textMessageLabel.myHeight = MyLayoutSize.wrap; //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将高度设置为自适应。 [_rootLayout addSubview:_textMessageLabel]; _imageMessageImageView = [UIImageView new]; diff --git a/MyLayoutDemo/AllTest1TableViewCellForAutoLayout.h b/MyLayoutDemo/AllTest1TableViewCellForAutoLayout.h new file mode 100644 index 0000000..4906966 --- /dev/null +++ b/MyLayoutDemo/AllTest1TableViewCellForAutoLayout.h @@ -0,0 +1,25 @@ +// +// AllTest1TableViewCellForAutoLayout.h +// MyLayout +// +// Created by apple on 19/5/16. +// Copyright © 2016年 YoungSoft. All rights reserved. +// + +#import +#import "MyLayout.h" +#import "AllTestDataModel.h" + +/** + * 动态高度UITableViewCellForAutoLayout + * 本例子是将布局视图的高度自适应能力和使用Autolayout的情况下实现UITableViewCell高度自适应的能力 + */ +@interface AllTest1TableViewCellForAutoLayout : UITableViewCell + +//对于需要动态评估高度的UITableViewCell来说可以把布局视图暴露出来。用于高度评估和边界线处理。以及事件处理的设置。 +@property(nonatomic, strong, readonly) MyBaseLayout *rootLayout; + + +-(void)setModel:(AllTest1DataModel*)model isImageMessageHidden:(BOOL)isImageMessageHidden; + +@end diff --git a/MyLayoutDemo/AllTest1TableViewCellForAutoLayout.m b/MyLayoutDemo/AllTest1TableViewCellForAutoLayout.m new file mode 100644 index 0000000..2512889 --- /dev/null +++ b/MyLayoutDemo/AllTest1TableViewCellForAutoLayout.m @@ -0,0 +1,239 @@ +// +// AllTest1TableViewCellForAutoLayout.m +// MyLayout +// +// Created by apple on 19/5/16. +// Copyright © 2016年 YoungSoft. All rights reserved. +// + +#import "AllTest1TableViewCellForAutoLayout.h" +#import "CFTool.h" + + +@interface AllTest1TableViewCellForAutoLayout() + +@property(nonatomic, strong) UIImageView *headImageView; +@property(nonatomic, strong) UILabel *nickNameLabel; +@property(nonatomic, strong) UILabel *textMessageLabel; +@property(nonatomic, strong) UIImageView *imageMessageImageView; + +@end + + +@implementation AllTest1TableViewCellForAutoLayout + +-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier +{ + self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; + if (self != nil) + { + /** + * 您可以尝试用不同的布局来实现相同的功能。 + */ + [self createLinearRootLayout]; + // [self createRelativeRootLayout]; + // [self createFloatRootLayout]; + + //如果是代码实现autolayout的话必须要将translatesAutoresizingMaskIntoConstraints 设置为NO。 + _rootLayout.translatesAutoresizingMaskIntoConstraints = NO; + + //设置布局视图的autolayout约束,这里是用iOS9提供的约束设置方法,您也可以用低级版本设置,以及用masonry来进行设置。 + [_rootLayout.leftAnchor constraintEqualToAnchor:self.contentView.leftAnchor].active = YES; + [_rootLayout.topAnchor constraintEqualToAnchor:self.contentView.topAnchor].active = YES; + //目前MyLayout和AutoLayout相结合并且高度根据宽度自适应时只能通过明确设置宽度约束,暂时不支持同时设置左右约束来确定宽度的能力。 + [_rootLayout.widthAnchor constraintEqualToAnchor:self.contentView.widthAnchor].active = YES; + + //这句代码很关键,表明self.contentView的高度随着子视图_rootLayout的高度自适应。 + [self.contentView.bottomAnchor constraintEqualToAnchor:_rootLayout.bottomAnchor constant:0].active = YES; + + + } + + return self; +} + +-(void)setModel:(AllTest1DataModel*)model isImageMessageHidden:(BOOL)isImageMessageHidden +{ + self.headImageView.image = [UIImage imageNamed:model.headImage]; + [self.headImageView sizeToFit]; + + + self.nickNameLabel.text = model.nickName; + [self.nickNameLabel sizeToFit]; + + self.textMessageLabel.text = model.textMessage; + + if (model.imageMessage.length == 0) + { + self.imageMessageImageView.visibility = MyVisibility_Gone; + } + else + { + self.imageMessageImageView.image = [UIImage imageNamed:model.imageMessage]; + [self.imageMessageImageView sizeToFit]; + if (isImageMessageHidden) + { + self.imageMessageImageView.visibility = MyVisibility_Gone; + } + else + { + self.imageMessageImageView.visibility = MyVisibility_Visible; + } + } + +} + + +- (void)awakeFromNib { + // Initialization code + [super awakeFromNib]; + +} + +- (void)setSelected:(BOOL)selected animated:(BOOL)animated { + [super setSelected:selected animated:animated]; + + // Configure the view for the selected state +} + + +#pragma mark -- Layout Construction + +//用线性布局来实现UI界面 +-(void)createLinearRootLayout +{ + _rootLayout= [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + _rootLayout.paddingTop = 5; + _rootLayout.paddingBottom = 5; + //这个属性只局限于在UITableViewCell中使用,用来优化tableviewcell的高度自适应的性能,其他地方请不要使用!!! + _rootLayout.cacheEstimatedRect = YES; + _rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + _rootLayout.widthSize.equalTo(nil); + [self.contentView addSubview:_rootLayout]; + + + + /* + 用线性布局实现时,整体用一个水平线性布局:左边是头像,右边是一个垂直的线性布局。垂直线性布局依次加入昵称、文本消息、图片消息。 + */ + + + _headImageView = [UIImageView new]; + [_rootLayout addSubview:_headImageView]; + + + MyLinearLayout *messageLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + messageLayout.weight = 1; + messageLayout.myLeading = 5; //前面2行代码描述的是垂直布局占用除头像外的所有宽度,并和头像保持5个点的间距。 + messageLayout.subviewVSpace = 5; //垂直布局里面所有子视图都保持5个点的间距。 + [_rootLayout addSubview:messageLayout]; + + + _nickNameLabel = [UILabel new]; + _nickNameLabel.textColor = [CFTool color:3]; + _nickNameLabel.font = [CFTool font:17]; + [messageLayout addSubview:_nickNameLabel]; + + + _textMessageLabel = [UILabel new]; + _textMessageLabel.font = [CFTool font:15]; + _textMessageLabel.textColor = [CFTool color:4]; + _textMessageLabel.myLeading = 0; + _textMessageLabel.myTrailing = 0; //垂直线性布局里面如果同时设置了左右边距则能确定子视图的宽度,这里表示宽度和父视图相等。 + _textMessageLabel.myHeight = MyLayoutSize.wrap; //如果想让文本的高度是动态的,请在设置明确宽度的情况下将高度设置为自适应。 + [messageLayout addSubview:_textMessageLabel]; + + + _imageMessageImageView = [UIImageView new]; + _imageMessageImageView.myCenterX = 0; //图片视图在父布局视图中水平居中。 + [messageLayout addSubview:_imageMessageImageView]; +} + +//用相对布局来实现UI界面 +-(void)createRelativeRootLayout +{ + _rootLayout = [MyRelativeLayout new]; + _rootLayout.paddingTop = 5; + _rootLayout.paddingBottom = 5; + //这个属性只局限于在UITableViewCell中使用,用来优化tableviewcell的高度自适应的性能,其他地方请不要使用!!! + _rootLayout.cacheEstimatedRect = YES; + _rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + [self.contentView addSubview:_rootLayout]; + + + /* + 用相对布局实现时,左边是头像视图,昵称文本在头像视图的右边,文本消息在昵称文本的下面,图片消息在文本消息的下面。 + */ + + _headImageView = [UIImageView new]; + [_rootLayout addSubview:_headImageView]; + + + _nickNameLabel = [UILabel new]; + _nickNameLabel.textColor = [CFTool color:3]; + _nickNameLabel.font = [CFTool font:17]; + _nickNameLabel.leadingPos.equalTo(_headImageView.trailingPos).offset(5); //昵称文本的左边在头像视图的右边并偏移5个点。 + [_rootLayout addSubview:_nickNameLabel]; + + + _textMessageLabel = [UILabel new]; + _textMessageLabel.font = [CFTool font:15]; + _textMessageLabel.textColor = [CFTool color:4]; + _textMessageLabel.leadingPos.equalTo(_headImageView.trailingPos).offset(5); //文本消息的左边在头像视图的右边并偏移5个点。 + _textMessageLabel.trailingPos.equalTo(_rootLayout.trailingPos); //文本消息的右边和父布局的右边对齐。上面2行代码也同时确定了文本消息的宽度。 + _textMessageLabel.topPos.equalTo(_nickNameLabel.bottomPos).offset(5); //文本消息的顶部在昵称文本的底部并偏移5个点。 + _textMessageLabel.heightSize.equalTo(@(MyLayoutSize.wrap)); //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将高度设置为自适应 + [_rootLayout addSubview:_textMessageLabel]; + + + _imageMessageImageView = [UIImageView new]; + _imageMessageImageView.centerXPos.equalTo(@5); //图片消息的水平中心点等于父布局的水平中心点并偏移5个点的位置,这里要偏移5的原因是头像和消息之间需要5个点的间距。 + _imageMessageImageView.topPos.equalTo(_textMessageLabel.bottomPos).offset(5); //图片消息的顶部在文本消息的底部并偏移5个点。 + [_rootLayout addSubview:_imageMessageImageView]; + +} + +//用浮动布局来实现UI界面 +-(void)createFloatRootLayout +{ + _rootLayout= [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; + _rootLayout.paddingTop = 5; + _rootLayout.paddingBottom = 5; + //这个属性只局限于在UITableViewCell中使用,用来优化tableviewcell的高度自适应的性能,其他地方请不要使用!!! + _rootLayout.cacheEstimatedRect = YES; + _rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + [self.contentView addSubview:_rootLayout]; + + /* + 用浮动布局实现时,头像视图浮动到最左边,昵称文本跟在头像视图后面并占用剩余宽度,文本消息也跟在头像视图后面并占用剩余宽度,图片消息不浮动占据所有宽度。 + 要想了解浮动布局的原理,请参考文章:http://www.jianshu.com/p/0c075f2fdab2 中的介绍。 + */ + + + _headImageView = [UIImageView new]; + _headImageView.myTrailing = 5; //右边保留出5个点的视图间距。 + [_rootLayout addSubview:_headImageView]; + + _nickNameLabel = [UILabel new]; + _nickNameLabel.textColor = [CFTool color:3]; + _nickNameLabel.font = [CFTool font:17]; + _nickNameLabel.myBottom = 5; //下边保留出5个点的视图间距。 + _nickNameLabel.weight = 1; //占用剩余宽度。 + [_rootLayout addSubview:_nickNameLabel]; + + _textMessageLabel = [UILabel new]; + _textMessageLabel.font = [CFTool font:15]; + _textMessageLabel.textColor = [CFTool color:4]; + _textMessageLabel.weight = 1; //占用剩余宽度 + _textMessageLabel.myHeight = MyLayoutSize.wrap; //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将高度设置为自适应。 + [_rootLayout addSubview:_textMessageLabel]; + + _imageMessageImageView = [UIImageView new]; + _imageMessageImageView.myTop = 5; + _imageMessageImageView.reverseFloat = YES; //反向浮动 + _imageMessageImageView.weight = 1; //占用剩余空间。 + _imageMessageImageView.contentMode = UIViewContentModeCenter; + [_rootLayout addSubview:_imageMessageImageView]; +} + +@end diff --git a/MyLayoutDemo/AllTest1ViewController.m b/MyLayoutDemo/AllTest1ViewController.m index e023486..3517432 100644 --- a/MyLayoutDemo/AllTest1ViewController.m +++ b/MyLayoutDemo/AllTest1ViewController.m @@ -9,6 +9,7 @@ #import "AllTest1ViewController.h" #import "AllTestDataModel.h" #import "AllTest1TableViewCell.h" +#import "AllTest1TableViewCellForAutoLayout.h" #import "AllTest1TableViewHeaderFooterView.h" #import "MyLayout.h" @@ -49,7 +50,7 @@ -(NSMutableArray*)datas ]; NSArray *textMessages = @[@"", - NSLocalizedString(@"a single line text", @""), + NSLocalizedString(@"A single line text", @""), NSLocalizedString(@"This Demo is used to introduce the solution when use layout view to realize the UITableViewCell's dynamic height. We only need to use the layout view's sizeThatFits method to evaluate the size of the layout view. and you can touch the Cell to shrink the height when the Cell has a picture.", @""), NSLocalizedString(@"Through layout view's sizeThatFits method can assess a UITableViewCell dynamic height.EstimateLayoutRect just to evaluate layout size but not set the size of the layout. here don't preach the width of 0 is the cause of the above UITableViewCell set the default width is 320 (regardless of any screen), so if we pass the width of 0 will be according to the width of 320 to evaluate UITableViewCell dynamic height, so when in 375 and 375 the width of the assessment of height will not be right, so here you need to specify the real width dimension;And the height is set to 0 mean height is not a fixed value need to evaluate. you can use all type layout view to realize UITableViewCell.", @""), NSLocalizedString(@"This section not only has text but also hav picture. and picture below at text, text will wrap", @"") @@ -98,10 +99,11 @@ - (void)viewDidLoad { //设置所有cell的高度为高度自适应,如果cell高度是动态的请这么设置。 如果不同的cell有差异那么可以通过实现协议方法-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath //如果您最低要支持到iOS7那么请您实现-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath方法来代替这个属性的设置。 self.tableView.rowHeight = UITableViewAutomaticDimension; - - + + //MyLayout中的布局视图可以支持UITableViewCell的高度自适应的能力。这里注册两个cell,一个是不和AutoLayout结合的实现,一个是和AutoLayout结合的实现。至于使用哪种方式您可以二选一。 [self.tableView registerClass:[AllTest1TableViewCell class] forCellReuseIdentifier:@"alltest1_cell"]; - + [self.tableView registerClass:[AllTest1TableViewCellForAutoLayout class] forCellReuseIdentifier:@"alltest1_cell_forautolayout"]; + /** @@ -114,7 +116,7 @@ - (void)viewDidLoad { tableHeaderViewLayout.frame = CGRectMake(0, 0, self.tableView.frame.size.width, 100); - 而如果某个布局视图的高度有可能是动态的高度,也就是用了wrapContentHeight为YES时,可以不用指定明确的指定高度,但要指定宽度。而且在布局视图添加到self.tableView.tableHeaderView 之前一定要记得调用: + 而如果某个布局视图的高度有可能是动态的高度,也就是高度自适应,可以不用指定明确的指定高度,但要指定宽度。而且在布局视图添加到self.tableView.tableHeaderView 之前一定要记得调用: [tableHeaderViewLayout layoutIfNeeded] */ @@ -141,7 +143,10 @@ -(void)createTableHeaderView //这个例子用来构建一个动态高度的头部布局视图。 MyLinearLayout *tableHeaderViewLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; tableHeaderViewLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - tableHeaderViewLayout.frame = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), 0); //高度不确定可以设置为0。但是宽度一定要写明确的值。尽量不要在代码中使用kScreenWidth,kScreenHeight,SCREEN_WIDTH。之类这样的宏来设定视图的宽度和高度。要充分利用MyLayout的特性,减少常数的使用。 + /*在iOS14以前的viewDidLoad中self.view或者self.tableView的frame属性是有值的,但是到了iOS14以后其中的frame值就变为了CGRectZero了 + 所以这里要想取到正确的宽度,从iOS14以后起改为了获取self.navigationController.view.bounds。以前的历史版本是self.tableView.bounds + */ + tableHeaderViewLayout.frame = CGRectMake(0, 0, CGRectGetWidth(self.navigationController.view.bounds), 0); //高度不确定可以设置为0。但是宽度一定要写明确的值。尽量不要在代码中使用kScreenWidth,kScreenHeight,SCREEN_WIDTH。之类这样的宏来设定视图的宽度和高度。要充分利用MyLayout的特性,减少常数的使用。 tableHeaderViewLayout.myHorzMargin = 0; //这里注意设置宽度和父布局保持一致。 tableHeaderViewLayout.backgroundImage = [UIImage imageNamed:@"bk1"]; [tableHeaderViewLayout setTarget:self action:@selector(handleTableHeaderViewLayoutClick:)]; @@ -158,11 +163,11 @@ -(void)createTableHeaderView UILabel *label2 = [UILabel new]; - label2.text = NSLocalizedString(@" if you use layout view to realize the dynamic height tableHeaderView, please use frame to set view's width and use wrapContentHeight to set view's height. the layoutIfNeeded method is needed to call before the layout view assignment to the UITableview's tableHeaderView.", @""); + label2.text = NSLocalizedString(@" if you use layout view to realize the dynamic height tableHeaderView, please use frame to set view's width and set view's heightSize to MyLayoutSize.wrap. the layoutIfNeeded method is needed to call before the layout view assignment to the UITableview's tableHeaderView.", @""); label2.textColor = [CFTool color:4]; label2.font = [CFTool font:15]; label2.myHorzMargin = 5; - label2.wrapContentHeight = YES; + label2.myHeight = MyLayoutSize.wrap; label2.myTop = 10; [label2 sizeToFit]; [tableHeaderViewLayout addSubview:label2]; @@ -193,7 +198,10 @@ -(void)createTableFooterView //这个例子用来构建一个固定高度的尾部布局视图。 MyLinearLayout *tableFooterViewLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; tableFooterViewLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - tableFooterViewLayout.frame = CGRectMake(0, 0, CGRectGetWidth(self.tableView.bounds), 80); //这里明确设定高度。 + /*在iOS14以前的viewDidLoad中self.view或者self.tableView的frame属性是有值的,但是到了iOS14以后其中的frame值就变为了CGRectZero了 + 所以这里要想取到正确的宽度,从iOS14以后起改为了获取self.navigationController.view.bounds。以前的历史版本是self.tableView.bounds + */ + tableFooterViewLayout.frame = CGRectMake(0, 0, CGRectGetWidth(self.navigationController.view.bounds), 80); //这里明确设定高度。 tableFooterViewLayout.myHorzMargin = 0; //这里注意设置宽度和父布局保持一致。 tableFooterViewLayout.backgroundColor = [CFTool color:6]; tableFooterViewLayout.gravity = MyGravity_Vert_Center | MyGravity_Horz_Fill; @@ -231,18 +239,12 @@ -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger) -(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - AllTest1TableViewCell *cell; - - if ([UIDevice currentDevice].systemVersion.floatValue < 8) - { - //如果您的系统要求最低支持到iOS7那么需要通过-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 来评估高度,因此请不要使用- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath 这个方法来初始化UITableviewCell,否则可能造成系统崩溃!!! - cell = (AllTest1TableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"alltest1_cell"]; - } - else - { - //如果你最低支持到iOS8那么请用这个方法来初始化一个UITableviewCell,用这个方法要记得调用registerClass来注册UITableviewCell,否则可能会返回nil - cell = (AllTest1TableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"alltest1_cell" forIndexPath:indexPath]; - } + //MyLayout中的布局视图可以支持UITableViewCell的高度自适应的能力。这里注册两个cell,一个是不和AutoLayout结合的实现,一个是和AutoLayout结合的实现。至于使用哪种方式您可以二选一。 + NSString *identifiers[2] = {@"alltest1_cell", @"alltest1_cell_forautolayout"}; + + //如果你最低支持到iOS8那么请用这个方法来初始化一个UITableviewCell,用这个方法要记得调用registerClass来注册UITableviewCell,否则可能会返回nil + //这里因为AllTest1TableViewCell和AllTest1TableViewCellForAutoLayout的方法名相同,所以这里虽然是两个不同的类,但是我们还是可以使用,你可以将下面的代码改为identifiers[1]试试AllTest1TableViewCellForAutoLayout这个cell类。 + AllTest1TableViewCell *cell = (AllTest1TableViewCell*)[tableView dequeueReusableCellWithIdentifier:identifiers[0] forIndexPath:indexPath]; AllTest1DataModel *model = [self.datas objectAtIndex:indexPath.row]; @@ -297,25 +299,7 @@ -(CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSIntege -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - //如果您的系统最低支持到iOS7那么需要实现这个方法来算出每个UITableviewCell的动态高度。如果最低支持到iOS8那么您可以直接返回UITableViewAutomaticDimension,或者不实现这个方法而通过设置UITableView的rowHeight为UITableViewAutomaticDimension就可以了。 - - if ([UIDevice currentDevice].systemVersion.floatValue < 8) - { - //这里注意一下:不是调用tableview的cellForRowAtIndexPath方法!!!!,而是调用的UITableviewDataSource的方法。 - AllTest1TableViewCell *cell = (AllTest1TableViewCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath]; - - //通过布局视图的sizeThatFits函数能够评估出UITableViewCell的动态高度。sizeThatFits并不会进行布局 - //而只是评估布局的尺寸,这里的宽度不传0的原因是上面的UITableViewCell在建立时默认的宽度是320(不管任何尺寸都如此),因此如果我们 - //传递了宽度为0的话则会按320的宽度来评估UITableViewCell的动态高度,这样当在375和414的宽度时评估出来的高度将不会正确,因此这里需要 - //指定出真实的宽度尺寸;而高度设置为0的意思是表示高度不是固定值需要评估出来。 - CGSize size = [cell.rootLayout sizeThatFits:CGSizeMake(tableView.frame.size.width, 0)]; - return size.height; //如果使用系统自带的分割线,请返回size.height+1 - } - else - { - return UITableViewAutomaticDimension; - } - + return UITableViewAutomaticDimension; } @@ -332,10 +316,10 @@ -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath * -(void)handleTableHeaderViewLayoutClick:(MyBaseLayout*)sender { UILabel *label1 = [sender viewWithTag:1000]; - if (label1.myVisibility == MyVisibility_Visible) - label1.myVisibility = MyVisibility_Gone; + if (label1.visibility == MyVisibility_Visible) + label1.visibility = MyVisibility_Gone; else - label1.myVisibility = MyVisibility_Visible; + label1.visibility = MyVisibility_Visible; [UIView animateWithDuration:0.3 animations:^{ diff --git a/MyLayoutDemo/AllTest2TableViewCell.m b/MyLayoutDemo/AllTest2TableViewCell.m index 876d970..ed1b35b 100644 --- a/MyLayoutDemo/AllTest2TableViewCell.m +++ b/MyLayoutDemo/AllTest2TableViewCell.m @@ -31,7 +31,7 @@ -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSStri /** * 您可以尝试用不同的布局来实现相同的功能。 */ - //[self createLinearRootLayout]; + // [self createLinearRootLayout]; [self createRelativeRootLayout]; // [self createFloatRootLayout]; } @@ -80,7 +80,7 @@ -(void)createLinearRootLayout MyBorderline *bld = [[MyBorderline alloc] initWithColor:[UIColor lightGrayColor]]; bld.headIndent = bld.tailIndent = 10; rootLayout.bottomBorderline = bld; - rootLayout.leftPadding = rootLayout.rightPadding = 10; //两边保留10的内边距。 + rootLayout.paddingLeft = rootLayout.paddingRight = 10; //两边保留10的内边距。 rootLayout.gravity = MyGravity_Vert_Center; //整个布局内容垂直居中。 /* @@ -94,7 +94,7 @@ -(void)createLinearRootLayout //中间用户信息是一个垂直线性布局:上部分是姓名,以及一些小图标这部分组成一个水平线性布局。下面是一行长的描述文字。 MyLinearLayout *userInfoLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; - userInfoLayout.wrapContentHeight = YES; //高度由子视图决定 + userInfoLayout.myHeight = MyLayoutSize.wrap; //高度由子视图决定 userInfoLayout.weight = 1; //中间部分的宽度占用整个水平线性布局剩余的空间。 userInfoLayout.gravity = MyGravity_Horz_Fill; //里面的子视图宽度和布局视图相等。 userInfoLayout.subviewVSpace = 5; //子视图间距为5。 @@ -103,8 +103,7 @@ -(void)createLinearRootLayout //姓名信息部分,一个水平线性布局:左边名称,后面两个操作按钮,整体底部对齐。 MyLinearLayout *userNameLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; - userNameLayout.wrapContentHeight = YES; //高度由子视图决定 - userNameLayout.wrapContentWidth = NO; //因为布局的宽度是和父布局相等,因此这里必须要设置为NO!!否则会有约束冲突。 + userNameLayout.myHeight = MyLayoutSize.wrap; //高度由子视图决定 userNameLayout.subviewHSpace = 5; //子视图之间宽度间隔是5 userNameLayout.gravity = MyGravity_Vert_Bottom; //整体垂直底部对齐。 [userInfoLayout addSubview:userNameLayout]; @@ -115,7 +114,7 @@ -(void)createLinearRootLayout //_nameLabel的宽度根据内容自适应,但是最大的宽度是父视图的宽度的1倍,再减去5+14+5+14。这里的5是视图之间的间距,14是后面两个图片的宽度。 //这个设置的意思是_nameLabel的宽可以动态增长,但是不能超过父视图的宽度,并且要保证后面的2个图片视图显示出来。 //您可以通过uBound方法设置尺寸的最大上边界。具体参见对uBound的方法的详细介绍。 - _nameLabel.widthSize.equalTo(_nameLabel.widthSize).uBound(userNameLayout.widthSize, -(5 + 14 + 5 + 14), 1); + _nameLabel.widthSize.equalTo(@(MyLayoutSize.wrap)).uBound(userNameLayout.widthSize, -(5 + 14 + 5 + 14), 1); _nameLabel.heightSize.equalTo(@25); [userNameLayout addSubview:_nameLabel]; @@ -130,7 +129,7 @@ -(void)createLinearRootLayout _descLabel.textColor = [CFTool color:4]; _descLabel.font = [CFTool font:15]; _descLabel.adjustsFontSizeToFitWidth = YES; - _descLabel.wrapContentHeight = YES; //2行高度,高度根据内容确定。 + _descLabel.myHeight = MyLayoutSize.wrap; //2行高度,高度根据内容确定。 _descLabel.numberOfLines = 2; [userInfoLayout addSubview:_descLabel]; @@ -143,7 +142,7 @@ -(void)createLinearRootLayout _priceLabel.textAlignment = NSTextAlignmentRight; _priceLabel.adjustsFontSizeToFitWidth = YES; //宽度最宽为100,注意到这里使用了宏MYDIMESCALEW表示会根据屏幕的宽度来对100进行缩放。这个100是按iPhone6为标准设置的。具体请参考MyDimeScale类。 - _priceLabel.widthSize.equalTo(_priceLabel.widthSize).uBound(@(MYDIMESCALEW(100)), 0, 1).lBound(@(MYDIMESCALEW(50)), 0, 1); + _priceLabel.widthSize.equalTo(@(MyLayoutSize.wrap)).uBound(@(MYDIMESCALEW(100)), 0, 1).lBound(@(MYDIMESCALEW(50)), 0, 1); _priceLabel.heightSize.equalTo(@25); _priceLabel.myLeading = 10; [rootLayout addSubview:_priceLabel]; @@ -165,7 +164,7 @@ -(void)createRelativeRootLayout MyBorderline *bld = [[MyBorderline alloc] initWithColor:[UIColor lightGrayColor]]; bld.headIndent = bld.tailIndent = 10; rootLayout.bottomBorderline = bld; - rootLayout.leftPadding = rootLayout.rightPadding = 10; //两边保留10的内边距。 + rootLayout.paddingLeft = rootLayout.paddingRight = 10; //两边保留10的内边距。 _headImageView = [UIImageView new]; @@ -181,7 +180,7 @@ -(void)createRelativeRootLayout _priceLabel.trailingPos.equalTo(rootLayout.trailingPos); _priceLabel.centerYPos.equalTo(rootLayout.centerYPos); //priceLabel的宽度根据内容自适应,但是最大的宽度是100,最小的宽度是50。注意到这里使用了宏MYDIMESCALEW表示会根据屏幕的宽度来对100进行缩放。这个100是在DEMO中是按iPhone6为标准设置的。具体请参考MyDimeScale类的介绍。 - _priceLabel.widthSize.equalTo(_priceLabel.widthSize).uBound(@(MYDIMESCALEW(100)), 0, 1).lBound(@(MYDIMESCALEW(50)), 0, 1); + _priceLabel.widthSize.equalTo(@(MyLayoutSize.wrap)).uBound(@(MYDIMESCALEW(100)), 0, 1).lBound(@(MYDIMESCALEW(50)), 0, 1); _priceLabel.heightSize.equalTo(@25); [rootLayout addSubview:_priceLabel]; @@ -189,7 +188,7 @@ -(void)createRelativeRootLayout _nameLabel = [UILabel new]; _nameLabel.font = [CFTool font:17]; _nameLabel.textColor = [CFTool color:3]; - _nameLabel.widthSize.equalTo(_nameLabel.widthSize); //视图的宽度由内容包裹 + _nameLabel.widthSize.equalTo(@(MyLayoutSize.wrap)); //视图的宽度由内容包裹 _nameLabel.heightSize.equalTo(@25); _nameLabel.leadingPos.equalTo(_headImageView.trailingPos); //1.3.0版本最新支持。设置_nameLabel的右边距最大是_priceLabel的左边距,再偏移两个小图标和间距的距离。这样当_nameLabel的尺寸超过这个最大的右边距时就会自动的缩小视图的宽度。 @@ -213,7 +212,7 @@ -(void)createRelativeRootLayout _descLabel.textColor = [CFTool color:4]; _descLabel.font = [CFTool font:15]; _descLabel.adjustsFontSizeToFitWidth = YES; - _descLabel.wrapContentHeight = YES; //2行高度,高度根据内容确定。 + _descLabel.myHeight = MyLayoutSize.wrap; //2行高度,高度根据内容确定。 _descLabel.numberOfLines = 2; _descLabel.leadingPos.equalTo(_nameLabel.leadingPos); _descLabel.trailingPos.equalTo(_priceLabel.leadingPos).offset(10); @@ -237,7 +236,7 @@ -(void)createFloatRootLayout MyBorderline *bld = [[MyBorderline alloc] initWithColor:[UIColor lightGrayColor]]; bld.headIndent = bld.tailIndent = 10; rootLayout.bottomBorderline = bld; - rootLayout.leftPadding = rootLayout.rightPadding = 10; //两边保留10的内边距。 + rootLayout.paddingLeft = rootLayout.paddingRight = 10; //两边保留10的内边距。 _headImageView = [UIImageView new]; _headImageView.contentMode = UIViewContentModeScaleAspectFit; @@ -253,7 +252,7 @@ -(void)createFloatRootLayout _priceLabel.reverseFloat = YES; _priceLabel.myLeading = 10; //priceLabel的宽度根据内容自适应,但是最大的宽度是100,最小的宽度是50。注意到这里使用了宏MYDIMESCALEW表示会根据屏幕的宽度来对100进行缩放。这个100是在DEMO中是按iPhone6为标准设置的。具体请参考MyDimeScale类的介绍。 - _priceLabel.widthSize.equalTo(_priceLabel.widthSize).uBound(@(MYDIMESCALEW(100)), 0, 1).lBound(@(MYDIMESCALEW(50)), 0, 1); + _priceLabel.widthSize.equalTo(@(MyLayoutSize.wrap)).uBound(@(MYDIMESCALEW(100)), 0, 1).lBound(@(MYDIMESCALEW(50)), 0, 1); _priceLabel.heightSize.equalTo(rootLayout.heightSize); [rootLayout addSubview:_priceLabel]; @@ -272,7 +271,7 @@ -(void)createFloatRootLayout //_nameLabel的宽度根据内容自适应,但是最大的宽度是父视图的宽度的1倍,再减去5+14+5+14。这里的5是视图之间的间距,14是后面两个图片的宽度。 //这个设置的意思是_nameLabel的宽可以动态增长,但是不能超过父视图的宽度,并且要保证后面的2个图片视图显示出来。 //您可以通过uBound方法设置尺寸的最大上边界。具体参见对uBound的方法的详细介绍。 - _nameLabel.widthSize.equalTo(_nameLabel.widthSize).uBound(userInfoLayout.widthSize, -(5 + 14 + 5 + 14), 1); + _nameLabel.widthSize.equalTo(@(MyLayoutSize.wrap)).uBound(userInfoLayout.widthSize, -(5 + 14 + 5 + 14), 1); _nameLabel.heightSize.equalTo(@25); [userInfoLayout addSubview:_nameLabel]; @@ -286,7 +285,7 @@ -(void)createFloatRootLayout _descLabel.textColor = [CFTool color:4]; _descLabel.font = [CFTool font:15]; _descLabel.adjustsFontSizeToFitWidth = YES; - _descLabel.wrapContentHeight = YES; //2行高度,高度根据内容确定。 + _descLabel.myHeight = MyLayoutSize.wrap; //2行高度,高度根据内容确定。 _descLabel.numberOfLines = 2; _descLabel.clearFloat = YES; _descLabel.weight = 1; diff --git a/MyLayoutDemo/AllTest3ViewController.m b/MyLayoutDemo/AllTest3ViewController.m index 62b3d67..bafdf39 100644 --- a/MyLayoutDemo/AllTest3ViewController.m +++ b/MyLayoutDemo/AllTest3ViewController.m @@ -132,7 +132,7 @@ -(void)addHeaderLayout:(MyLinearLayout *)contentLayout { MyRelativeLayout *headerLayout = [MyRelativeLayout new]; headerLayout.backgroundImage = [UIImage imageNamed:@"bk1"]; //可以为布局直接设备背景图片。 - headerLayout.wrapContentHeight = YES; + headerLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); [contentLayout addSubview:headerLayout]; UIImageView *headerImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"head2"]]; @@ -210,8 +210,8 @@ -(void)addVisibilityTestLayout:(MyLinearLayout *)contentLayout MyLinearLayout *testLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; testLayout.backgroundColor = [UIColor whiteColor]; - testLayout.leftPadding = 10; - testLayout.rightPadding = 10; + testLayout.paddingLeft = 10; + testLayout.paddingRight = 10; testLayout.myHeight = 50; testLayout.gravity = MyGravity_Vert_Fill; testLayout.subviewHSpace = 10; @@ -261,8 +261,8 @@ -(void)addFlexedWidthLayout:(MyLinearLayout*)contentLayout MyLinearLayout *testLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; testLayout.backgroundColor = [UIColor whiteColor]; - testLayout.leftPadding = 10; - testLayout.rightPadding = 10; + testLayout.paddingLeft = 10; + testLayout.paddingRight = 10; testLayout.myHeight = 50; testLayout.gravity = MyGravity_Vert_Fill; testLayout.shrinkType = MySubviewsShrink_Auto; //左右2个子视图会根据自身的宽度自动调整。不会产生覆盖和重叠。 @@ -278,7 +278,7 @@ -(void)addFlexedWidthLayout:(MyLinearLayout*)contentLayout leftLabel.backgroundColor = [CFTool color:5]; leftLabel.font = [CFTool font:14]; leftLabel.trailingPos.equalTo(@0.5).min(0); //右边浮动间距为0.5,最小为0 - leftLabel.wrapContentWidth = YES; //宽度由内容包裹 + leftLabel.widthSize.equalTo(@(MyLayoutSize.wrap)); [testLayout addSubview:leftLabel]; self.leftFlexedLabel = leftLabel; @@ -289,7 +289,7 @@ -(void)addFlexedWidthLayout:(MyLinearLayout*)contentLayout rightLabel.backgroundColor = [CFTool color:6]; rightLabel.font = [CFTool font:14]; rightLabel.leadingPos.equalTo(@0.5).min(0); //左边浮动间距为0.5,最小为0 - rightLabel.wrapContentWidth = YES; //宽度由内容包裹 + rightLabel.widthSize.equalTo(@(MyLayoutSize.wrap)); //宽度由内容包裹 [testLayout addSubview:rightLabel]; self.rightFlexedLabel = rightLabel; @@ -299,7 +299,6 @@ -(void)addFlexedWidthLayout:(MyLinearLayout*)contentLayout //添加一个能伸缩的布局 -(void)addShrinkLayout:(MyLinearLayout*)contentLayout { - //下面两个布局用来测试布局视图的hideSubviewReLayout属性。 MyLinearLayout *switchLayout = [self createSwitchLayout:NSLocalizedString(@"show all switch", @"") action:@selector(handleShrinkSwitch:)]; switchLayout.bottomBorderline = [[MyBorderline alloc] initWithColor:[UIColor redColor]]; //底部边界线设置可以缩进 switchLayout.bottomBorderline.headIndent = 10; @@ -342,14 +341,15 @@ -(void)addActiveLayout:(MyLinearLayout*)contentLayout testLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); testLayout.backgroundColor = [UIColor whiteColor]; testLayout.myTop = 10; - testLayout.myBottom = 50; //这里设置底部间距的原因是登录按钮在最底部。为了使得滚动到底部时不被覆盖。你也可以设置contentLayout的bottomPadding = 50来解决这个问题。 + testLayout.myBottom = 50; //这里设置底部间距的原因是登录按钮在最底部。为了使得滚动到底部时不被覆盖。你也可以设置contentLayout的paddingBottom = 50来解决这个问题。 [contentLayout addSubview:testLayout]; UIButton *testButton = [UIButton buttonWithType:UIButtonTypeSystem]; [testButton setTitle:@"Click me" forState:UIControlStateNormal]; testButton.backgroundColor = [CFTool color:0]; testButton.heightSize.equalTo(@50); - testButton.widthSize.equalTo(testButton.widthSize).add(20); + // testButton.widthSize.equalTo(testButton.widthSize).add(20); + testButton.widthSize.equalTo(@(MyLayoutSize.wrap)).add(20); testButton.leadingPos.equalTo(@10).active = YES; //左边边距是10,设置active为YES表示左边位置对象的设置是生效的。 testButton.trailingPos.equalTo(@10).active = NO; //右边边距是10,设置active为NO表示右边位置对象的设置是不生效的。 @@ -367,9 +367,9 @@ -(MyLinearLayout*)createActionLayout:(NSString*)title action:(SEL)action [actionLayout setTarget:self action:action]; //这里设置布局的触摸事件处理。 //左右内边距都是10,不包裹子视图,整体高度为50,里面的子布局垂直居中对齐。 - actionLayout.leftPadding = 10; - actionLayout.rightPadding = 10; - actionLayout.wrapContentWidth = NO; + actionLayout.paddingLeft = 10; + actionLayout.paddingRight = 10; + actionLayout.widthSize.equalTo(nil); actionLayout.heightSize.equalTo(@50); actionLayout.gravity = MyGravity_Vert_Center; @@ -400,9 +400,9 @@ -(MyLinearLayout*)createSwitchLayout:(NSString*)title action:(SEL)action switchLayout.backgroundColor = [UIColor whiteColor]; //左右边距都是10,不包裹子视图,整体高度为50,里面的子布局垂直居中对齐。 - switchLayout.leftPadding = 10; - switchLayout.rightPadding = 10; - switchLayout.wrapContentWidth = NO; + switchLayout.paddingLeft = 10; + switchLayout.paddingRight = 10; + switchLayout.widthSize.equalTo(nil); switchLayout.heightSize.equalTo(@50); switchLayout.gravity = MyGravity_Vert_Center; @@ -433,8 +433,8 @@ -(MyFloatLayout*)createSegmentedLayout:(SEL)leftAction rightAction:(SEL)rightAct segmentedLayout.backgroundColor = [UIColor whiteColor]; //左右边距都是10,不包裹子视图,整体高度为50,里面的子布局垂直居中对齐。 - segmentedLayout.leftPadding = 10; - segmentedLayout.rightPadding = 10; + segmentedLayout.paddingLeft = 10; + segmentedLayout.paddingRight = 10; segmentedLayout.heightSize.equalTo(@50); segmentedLayout.gravity = MyGravity_Vert_Center; @@ -486,18 +486,18 @@ -(void)handleTouchCancel:(MyBaseLayout*)sender -(void)handleResetShow:(UIButton *)sender { - self.invisibleButton.myVisibility = MyVisibility_Visible; - self.goneButton.myVisibility = MyVisibility_Visible; + self.invisibleButton.visibility = MyVisibility_Visible; + self.goneButton.visibility = MyVisibility_Visible; } -(void)handleInvisible:(UIButton*)sender { - sender.myVisibility = MyVisibility_Invisible; + sender.visibility = MyVisibility_Invisible; } -(void)handleGone:(UIButton*)sender { - sender.myVisibility = MyVisibility_Gone; + sender.visibility = MyVisibility_Gone; } -(void)handleLeftFlexed:(UISegmentedControl*)segmented @@ -540,15 +540,9 @@ -(void)handleRightFlexed:(UISegmentedControl*)segmented -(void)handleShrinkSwitch:(UISwitch *)sender { if (sender.isOn) - { - self.shrinkLayout.heightSize.equalTo(nil); - self.shrinkLayout.wrapContentHeight = YES; - } + self.shrinkLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); else - { self.shrinkLayout.heightSize.equalTo(@50); - self.shrinkLayout.wrapContentHeight = NO; - } [self.shrinkLayout layoutAnimationWithDuration:0.3]; } @@ -593,7 +587,7 @@ -(void)handleShowPopMenu:(MyBaseLayout*)sender itemLayout.gravity = MyGravity_Horz_Fill; itemLayout.subviewHSpace = 10; itemLayout.subviewVSpace = 10; - itemLayout.wrapContentHeight = YES; + itemLayout.myHeight = MyLayoutSize.wrap; [scrollView addSubview:itemLayout]; self.popmenuItemLayout = itemLayout; @@ -717,16 +711,19 @@ -(void)handleActiveTest:(UIButton*)sender { sender.leadingPos.active = YES; sender.trailingPos.active = NO; //按钮将停靠在父布局的左边。 + sender.widthSize.active = YES; } else if (sender.leadingPos.isActive) { sender.leadingPos.active = NO; sender.trailingPos.active = YES; //按钮将停靠在父布局的右边 + sender.widthSize.active = YES; } else if (sender.trailingPos.isActive) { sender.leadingPos.active = YES; sender.trailingPos.active = YES; //按钮的左右边距都生效,并且会拉伸按钮的宽度。 + sender.widthSize.active = NO; } MyLinearLayout *superLayout = (MyLinearLayout*)sender.superview; diff --git a/MyLayoutDemo/AllTest4ViewController.m b/MyLayoutDemo/AllTest4ViewController.m index 1c12c1c..7e559e4 100644 --- a/MyLayoutDemo/AllTest4ViewController.m +++ b/MyLayoutDemo/AllTest4ViewController.m @@ -58,7 +58,7 @@ - (void)viewDidLoad { _rootLayout.gravity = MyGravity_Horz_Fill; //设置垂直线性布局的水平填充值表明布局视图里面的所有子视图的宽度都和布局视图相等。 _rootLayout.widthSize.equalTo(scrollView.widthSize); - _rootLayout.wrapContentHeight = YES; //布局宽度和父视图一致,高度则由内容包裹。这是实现将布局视图加入滚动条视图并垂直滚动的标准方法。 + _rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); //布局宽度和父视图一致,高度则由内容包裹。这是实现将布局视图加入滚动条视图并垂直滚动的标准方法。 [scrollView addSubview:_rootLayout]; self.containerLayouts = [NSMutableArray new]; @@ -134,7 +134,7 @@ -(UIView*)createSupplementaryLayout:(NSString*)sectionTitle -(MyFlowLayout*)createCellContainerLayout:(NSInteger)arrangedCount { MyFlowLayout *containerLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:arrangedCount]; - containerLayout.wrapContentHeight = YES; + containerLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); containerLayout.gravity = MyGravity_Horz_Fill; //平均分配里面每个子视图的宽度或者拉伸子视图的宽度以便填充满整个布局。 containerLayout.subviewHSpace = 5; containerLayout.subviewVSpace = 5; @@ -147,7 +147,6 @@ -(MyFlowLayout*)createCellContainerLayout:(NSInteger)arrangedCount -(UIView*)createCellLayout1:(NSString*)image title:(NSString*)title { MyLinearLayout *cellLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; - cellLayout.wrapContentHeight = NO; cellLayout.gravity = MyGravity_Horz_Fill; //里面所有子视图的宽度都跟父视图保持一致,这样子视图就不需要设置宽度了。 cellLayout.myHeight = 100; cellLayout.subviewVSpace = 5; //设置布局视图里面子视图之间的间距为5个点。 @@ -265,9 +264,12 @@ -(void)handleReverse:(id)sender { //MyBaseLayout的属性reverseLayout可以将子视图按照添加的顺序逆序布局。 + for (MyBaseLayout *layout in self.containerLayouts) { + layout.reverseLayout = !layout.reverseLayout; + [layout layoutAnimationWithDuration:0.3]; } } diff --git a/MyLayoutDemo/AllTest5ViewController.m b/MyLayoutDemo/AllTest5ViewController.m index f357d43..ee29453 100644 --- a/MyLayoutDemo/AllTest5ViewController.m +++ b/MyLayoutDemo/AllTest5ViewController.m @@ -28,7 +28,6 @@ -(void)loadView //默认设置为垂直布局 MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - rootLayout.wrapContentHeight = NO; rootLayout.gravity = MyGravity_Horz_Fill; rootLayout.subviewSpace = 10; rootLayout.backgroundColor = [UIColor whiteColor]; @@ -57,16 +56,16 @@ -(void)loadView [rootLayout addSubview:v3]; //v3视图在其他任何iPhone设备横屏都不参与布局 - [v3 fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact].myVisibility = MyVisibility_Gone; + [v3 fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact].visibility = MyVisibility_Gone; //只有iphone6Plus的横屏才参与布局 - [v3 fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hCompact copyFrom:MySizeClass_wAny | MySizeClass_hAny].myVisibility = MyVisibility_Visible; + [v3 fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hCompact copyFrom:MySizeClass_wAny | MySizeClass_hAny].visibility = MyVisibility_Visible; //针对iPhone设备的所有横屏的高度都是Compact的,而宽度则是任意,因此下面的设置横屏情况下布局变为水平布局。 //虽然fetchLayoutSizeClass方法真实返回的是MyLayoutSize或者其派生类,但是仍然可以用视图以及布局来设置其中的属性 //但是调用lsc.backgroundColor = xx 则会崩溃,因为fetchLayoutSizeClass返回的并不是真的视图对象。 MyLinearLayout *lsc = [rootLayout fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact copyFrom:MySizeClass_wAny | MySizeClass_hAny]; lsc.orientation = MyOrientation_Horz; - lsc.wrapContentWidth = NO; + lsc.widthSize.equalTo(nil); lsc.gravity = MyGravity_Vert_Fill; diff --git a/MyLayoutDemo/AllTest6ViewController.m b/MyLayoutDemo/AllTest6ViewController.m index ca86537..138309a 100644 --- a/MyLayoutDemo/AllTest6ViewController.m +++ b/MyLayoutDemo/AllTest6ViewController.m @@ -27,15 +27,14 @@ -(void)loadView MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; - rootLayout.wrapContentHeight = NO; rootLayout.gravity = MyGravity_Horz_Fill; rootLayout.backgroundColor = [UIColor whiteColor]; self.view = rootLayout; //创建顶部的菜单布局部分。 MyFlowLayout *menuLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3]; - menuLayout.gravity = MyGravity_Fill; //填充所有尺寸。 - menuLayout.wrapContentHeight = YES; + menuLayout.gravity = MyGravity_Horz_Fill; //水平填充所有尺寸。 + menuLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); menuLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); menuLayout.subviewSpace = 10; [rootLayout addSubview:menuLayout]; @@ -46,7 +45,7 @@ -(void)loadView menu1Label.backgroundColor = [CFTool color:5]; menu1Label.font = [CFTool font:16]; menu1Label.heightSize.equalTo(menu1Label.widthSize); - menu1Label.widthSize.equalTo(menu1Label.heightSize); + // menu1Label.widthSize.equalTo(menu1Label.heightSize); [menuLayout addSubview:menu1Label]; UILabel *menu2Label = [UILabel new]; @@ -55,7 +54,7 @@ -(void)loadView menu2Label.backgroundColor = [CFTool color:6]; menu2Label.font = [CFTool font:16]; menu2Label.heightSize.equalTo(menu2Label.widthSize); - menu2Label.widthSize.equalTo(menu2Label.heightSize); + // menu2Label.widthSize.equalTo(menu2Label.heightSize); [menuLayout addSubview:menu2Label]; UILabel *menu3Label = [UILabel new]; @@ -64,7 +63,7 @@ -(void)loadView menu3Label.backgroundColor = [CFTool color:7]; menu3Label.font = [CFTool font:16]; menu3Label.heightSize.equalTo(menu3Label.widthSize); - menu3Label.widthSize.equalTo(menu3Label.heightSize); + // menu3Label.widthSize.equalTo(menu3Label.heightSize); [menuLayout addSubview:menu3Label]; MyRelativeLayout *contentLayout = [MyRelativeLayout new]; @@ -113,7 +112,18 @@ -(void)loadView MyFlowLayout *menuLayoutSC = [menuLayout fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact copyFrom:MySizeClass_hAny | MySizeClass_wAny]; menuLayoutSC.orientation = MyOrientation_Horz; - menuLayoutSC.wrapContentWidth = YES; + menuLayoutSC.gravity = MyGravity_Vert_Fill; + menuLayoutSC.widthSize.equalTo(@(MyLayoutSize.wrap)); + + UILabel *menu1LabelSC = [menu1Label fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; + UILabel *menu2LabelSC = [menu2Label fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; + UILabel *menu3LabelSC = [menu3Label fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; + + menu1LabelSC.widthSize.equalTo(menu1LabelSC.heightSize); + menu2LabelSC.widthSize.equalTo(menu2LabelSC.heightSize); + menu3LabelSC.widthSize.equalTo(menu3LabelSC.heightSize); + + UILabel *func1LabelSC = [func1Label fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact]; @@ -128,12 +138,12 @@ -(void)loadView func3LabelSC.heightSize.equalTo(contentLayout.heightSize); //下面是定义在iPad上设备的横屏的界面布局,因为iPad上的SizeClass都是regular,所以这里要区分横竖屏的方法是使用MySizeClass_Portrait和MySizeClass_Landscape - UILabel *menu1LabelSC = [menu1Label fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; - menu1LabelSC.heightSize.max(200); - UILabel *menu2LabelSC = [menu2Label fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; - menu2LabelSC.heightSize.max(200); - UILabel *menu3LabelSC = [menu3Label fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; - menu3LabelSC.heightSize.max(200); + UILabel *menu1LabelSCForiPad = [menu1Label fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; + menu1LabelSCForiPad.heightSize.max(200); + UILabel *menu2LabelSCForiPad = [menu2Label fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; + menu2LabelSCForiPad.heightSize.max(200); + UILabel *menu3LabelSCForiPad = [menu3Label fetchLayoutSizeClass:MySizeClass_wRegular | MySizeClass_hRegular | MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; + menu3LabelSCForiPad.heightSize.max(200); } diff --git a/MyLayoutDemo/AllTest7ViewController.m b/MyLayoutDemo/AllTest7ViewController.m index 5d6b524..1c8865a 100644 --- a/MyLayoutDemo/AllTest7ViewController.m +++ b/MyLayoutDemo/AllTest7ViewController.m @@ -38,8 +38,7 @@ -(void) loadView tipLabel.font = [CFTool font:16]; tipLabel.textColor = [CFTool color:3]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; - [tipLabel sizeToFit]; + tipLabel.heightSize.equalTo(@(MyLayoutSize.wrap)); [rootLayout addSubview:tipLabel]; [self createDemo1:rootLayout]; @@ -64,7 +63,8 @@ -(void) loadView [self createDemo11:rootLayout]; - + [self createDemo12:rootLayout]; + } @@ -77,18 +77,15 @@ -(void)createDemo1:(MyLinearLayout*)rootLayout tipLabel.text = @"1.下面的例子实现一行内多个子视图从左往右排列。如果在小屏幕下显示则会压缩所有子视图的空间,如果能够被容纳的话则正常显示。您可以分别在横竖屏下测试以及在iPhone4/5/6/6+上测试效果:"; tipLabel.font = [CFTool font:14]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; + tipLabel.myHeight = MyLayoutSize.wrap; tipLabel.myTop = 10; - [tipLabel sizeToFit]; [rootLayout addSubview:tipLabel]; MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; - contentLayout.wrapContentWidth = NO; - contentLayout.wrapContentHeight = YES; + contentLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); contentLayout.subviewHSpace = 5; - contentLayout.shrinkType = MySubviewsShrink_Weight; //这个属性用来设置当子视图的总尺寸大于布局视图的尺寸时如何压缩这些具有固定尺寸的方法为按比例缩小。您可以分别试试设置为:MySubviewsShrink_Weight,MySubviewsShrink_Average,MySubviewsShrink_None,MySubviewsShrink_Auto四种值的效果。 - + contentLayout.backgroundColor = [CFTool color:0]; [rootLayout addSubview:contentLayout]; @@ -98,8 +95,9 @@ -(void)createDemo1:(MyLinearLayout*)rootLayout label1.font = [CFTool font:16]; label1.backgroundColor = [CFTool color:5]; label1.adjustsFontSizeToFitWidth = YES; - label1.wrapContentSize = YES; //尺寸等于视图的内容包裹。 - label1.widthSize.lBound(label1.widthSize,0,1); //并且最小宽度也等于自己,这样设置的话可以保证这个视图永远不会被压缩。您可以注释掉这句看看效果。 + label1.heightSize.equalTo(@(MyLayoutSize.wrap)); + label1.widthSize.equalTo(@(MyLayoutSize.wrap)); + label1.widthSize.shrink = 0; //不压缩! [contentLayout addSubview:label1]; //第二个子视图。 @@ -108,7 +106,9 @@ -(void)createDemo1:(MyLinearLayout*)rootLayout label2.font = [CFTool font:16]; label2.backgroundColor = [CFTool color:6]; label2.adjustsFontSizeToFitWidth = YES; - label2.wrapContentSize = YES; //尺寸等于自身的内容 + label2.heightSize.equalTo(@(MyLayoutSize.wrap)); + label2.widthSize.equalTo(@(MyLayoutSize.wrap)); + label2.widthSize.shrink = 1; //压缩 label2.numberOfLines = 1; //如果你只想要一行,那么要记得在设置包裹后设置行数。。 [contentLayout addSubview:label2]; @@ -118,7 +118,9 @@ -(void)createDemo1:(MyLinearLayout*)rootLayout label3.font = [CFTool font:15]; label3.backgroundColor = [CFTool color:7]; label3.adjustsFontSizeToFitWidth = YES; - label3.wrapContentSize = YES; + label3.heightSize.equalTo(@(MyLayoutSize.wrap)); + label3.widthSize.equalTo(@(MyLayoutSize.wrap)); + label3.widthSize.shrink = 1; //压缩 [contentLayout addSubview:label3]; label3.myTrailing = 0.5; //这句设置非常重要,设置为右间距为相对间距,从而达到如果屏幕小则会缩小固定尺寸,如果大则不会的效果。 @@ -132,14 +134,12 @@ -(void)createDemo2:(MyLinearLayout*)rootLayout tipLabel.text = @"2.下面的例子里面最右边的两个子视图的宽度是固定的,而第一个子视图则占用剩余的空间。您可以分别在横竖屏下测试以及在iPhone4/5/6/6+上测试效果:"; tipLabel.font = [CFTool font:14]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; + tipLabel.myHeight = MyLayoutSize.wrap; tipLabel.myTop = 10; - [tipLabel sizeToFit]; [rootLayout addSubview:tipLabel]; MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; - contentLayout.wrapContentWidth = NO; - contentLayout.wrapContentHeight = YES; + contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); contentLayout.subviewHSpace = 5; contentLayout.backgroundColor = [CFTool color:0]; @@ -151,8 +151,7 @@ -(void)createDemo2:(MyLinearLayout*)rootLayout label1.font = [CFTool font:15]; label1.backgroundColor = [CFTool color:5]; label1.adjustsFontSizeToFitWidth = YES; - [label1 sizeToFit]; - label1.wrapContentHeight = YES; //自动换行。 + label1.myHeight = MyLayoutSize.wrap; label1.weight = 1; //占用剩余的空间。 [contentLayout addSubview:label1]; @@ -198,14 +197,13 @@ -(void)createDemo3:(MyLinearLayout*)rootLayout tipLabel.text = @"3.下面的例子里面最右边的两个子视图的宽度是固定的,而第一个子视图的尺寸动态变化,但是最宽不能超过布局剩余的宽度。您可以分别在横竖屏下测试以及在iPhone4/5/6/6+上分别测试效果:"; tipLabel.font = [CFTool font:14]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; + tipLabel.myHeight = MyLayoutSize.wrap; tipLabel.myTop = 10; [tipLabel sizeToFit]; [rootLayout addSubview:tipLabel]; MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; - contentLayout.wrapContentWidth = NO; - contentLayout.wrapContentHeight = YES; + contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); contentLayout.subviewHSpace = 5; contentLayout.backgroundColor = [CFTool color:0]; @@ -217,7 +215,7 @@ -(void)createDemo3:(MyLinearLayout*)rootLayout label1.font = [CFTool font:14]; label1.backgroundColor = [CFTool color:5]; label1.tag = 1000; //为了测试用。。 - label1.wrapContentSize = YES; //尺寸由内容包裹 + label1.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); //尺寸由内容包裹 label1.myTrailing = 0.5; //设置相对间距 [contentLayout addSubview:label1]; @@ -236,13 +234,13 @@ -(void)createDemo3:(MyLinearLayout*)rootLayout [button3 setTitle:@"Del Click" forState:UIControlStateNormal]; button3.tintColor = [UIColor redColor]; button3.titleLabel.font = [CFTool font:14]; - [button3 sizeToFit]; + button3.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); [contentLayout addSubview:button3]; [button3 addTarget:self action:@selector(handleDel:) forControlEvents:UIControlEventTouchUpInside]; - //因为button2,和button3的宽度是固定的,因此这里面label1的最大宽是父视图的宽度减去2个按钮的宽度之和,外加上所有子视图的间距的和。 - label1.widthSize.uBound(contentLayout.widthSize, -1 *(CGRectGetWidth(button2.bounds) + CGRectGetWidth(button3.bounds) + 2 * contentLayout.subviewHSpace), 1); + //因为button2,和button3的宽度是固定的,而label1的宽度是自适应的,但是不能超过容器视图的宽度,所以这里将宽度尺寸的shrink设置为1表明当宽度超过阈值时会自动压缩宽度值。 + label1.widthSize.shrink = 1; } @@ -271,7 +269,7 @@ -(void)createDemo4:(MyLinearLayout*)rootLayout tipLabel.text = @"4.下面的例子展示左右2个子视图的内容分别向两边延伸,但是不会重叠。这样做的好处就是不会产生空间的浪费。一个具体例子就是UITableviewCell中展示内容时,一部分在左边而一部分在右边,两边的内容长度都不确定,但是不能重叠以及浪费空间。 您可以分别在横竖屏下测试以及在iPhone4/5/6/6+上测试效果:"; tipLabel.font = [CFTool font:14]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; + tipLabel.myHeight = MyLayoutSize.wrap; tipLabel.myTop = 10; [tipLabel sizeToFit]; [rootLayout addSubview:tipLabel]; @@ -283,8 +281,7 @@ -(void)createDemo4:(MyLinearLayout*)rootLayout [changeButton addTarget:self action:@selector(handleChangeText:) forControlEvents:UIControlEventTouchUpInside]; MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; - contentLayout.wrapContentWidth = NO; - contentLayout.wrapContentHeight = YES; + contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); contentLayout.subviewHSpace = 20; contentLayout.shrinkType = MySubviewsShrink_Auto; //为了实现左右两边文本的自动缩放调整,必须要将线性布局的属性设置MySubviewsShrink_Auto。当设置为Auto属性时,必要要满足当前子视图中只有2个子视图的宽度设置为等于自身内容,否则无效。这个属性用来实现左右2个子视图根据内容来占用最佳的空间的例子。 @@ -305,7 +302,7 @@ -(void)createDemo4:(MyLinearLayout*)rootLayout UILabel *leadingLabel = [UILabel new]; leadingLabel.font = [UIFont systemFontOfSize:14]; leadingLabel.textColor = [UIColor redColor]; - leadingLabel.wrapContentSize = YES; + leadingLabel.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); leadingLabel.trailingPos.equalTo(@0.5); //设置右边的相对间距. [contentLayout addSubview:leadingLabel]; leadingLabel.tag = 1000; @@ -315,7 +312,7 @@ -(void)createDemo4:(MyLinearLayout*)rootLayout UILabel *trailingLabel = [UILabel new]; trailingLabel.font = [UIFont systemFontOfSize:14]; trailingLabel.textColor = [UIColor blueColor]; - trailingLabel.wrapContentSize = YES; + trailingLabel.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); trailingLabel.leadingPos.equalTo(@0.5); //设置右边的相对间距. [contentLayout addSubview:trailingLabel]; trailingLabel.tag = 2000; @@ -359,7 +356,7 @@ -(void)createDemo5:(MyLinearLayout*)rootLayout tipLabel.text = @"5.下面的例子中(响应式布局!!),您可以添加按钮来添加多个按钮形成多行多列的布局。在不同的屏幕尺寸下,子视图之间的间距会自动调整以便满足最佳的布局状态。比如多个子视图有规律排列,每个子视图的宽度是固定的,在iPhone4下以及iPhone6下都能放置4个子视图,但是iPhone4中子视图之间的间距要比iPhone6上的小,而在iPhone6+上则因为空间足够可以放置5个子视图。您可以分别在横竖屏下测试以及在iPhone4/5/6/6+上测试效果:"; tipLabel.font = [CFTool font:14]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; + tipLabel.myHeight = MyLayoutSize.wrap; tipLabel.myTop = 10; [tipLabel sizeToFit]; [rootLayout addSubview:tipLabel]; @@ -369,7 +366,7 @@ -(void)createDemo5:(MyLinearLayout*)rootLayout MyFlowLayout *contentLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:0]; contentLayout.backgroundColor = [CFTool color:0]; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - contentLayout.wrapContentHeight = YES; + contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.subviewVSpace = 5; //设置流式布局里面子视图之间的垂直间距。 [contentLayout setSubviewsSize:subviewWidth minSpace:5 maxSpace:10]; //这里面水平间距用浮动间距,浮动间距设置为子视图固定宽度为60,最小的间距为5,最大间距为10。注意这里要求所有子视图的宽度都是60。 [rootLayout addSubview:contentLayout]; @@ -450,7 +447,7 @@ -(void)createDemo6:(MyLinearLayout*)rootLayout tipLabel.text = @"6.下面的例子用来实现子视图依次从左往右添加,并且当空间不够时会自动压缩前面的所有子视图的宽度。而当所有子视图的宽度都到达了最小的阈值时就会自动换行,并继续添加上去。您可以分别在横竖屏下测试以及在iPhone4/5/6/6+上分别测试效果:"; tipLabel.font = [CFTool font:14]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; + tipLabel.myHeight = MyLayoutSize.wrap; tipLabel.myTop = 10; [tipLabel sizeToFit]; [rootLayout addSubview:tipLabel]; @@ -480,14 +477,14 @@ -(void)createDemo7:(MyLinearLayout*)rootLayout tipLabel.text = @"7.下面的例子里面有多个宽度不一致的子视图,但是布局会根据屏幕的大小而智能的排列这些子视图,从而充分的利用好布局视图的空间。您可以分别在横竖屏下测试以及在iPhone4/5/6/6+上分别测试效果:"; tipLabel.font = [CFTool font:14]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; + tipLabel.myHeight = MyLayoutSize.wrap; tipLabel.myTop = 10; [tipLabel sizeToFit]; [rootLayout addSubview:tipLabel]; MyFlowLayout *contentLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:0]; - contentLayout.wrapContentHeight = YES; + contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); contentLayout.subviewSpace = 5; contentLayout.backgroundColor = [CFTool color:0]; @@ -520,7 +517,7 @@ -(void)createDemo8:(MyLinearLayout*)rootLayout tipLabel.text = @"8.下面的例子中如果屏幕足够宽则左边视图居左,中间视图居中,右边视图居右。这时候不会产生滚动,而当屏幕宽度不足时则会压缩中间视图和两边视图之间的间距并且产生滚动效果。这样的例子也可以同样应用在纵向屏幕中:通常在大屏幕设备上中间的部分要居中显示,而在小屏幕上则依次排列产生滚动效果。 您可以分别在iPhone4/5/6/6+上以及横竖屏测试效果:"; tipLabel.font = [CFTool font:14]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; + tipLabel.myHeight = MyLayoutSize.wrap; tipLabel.myTop = 10; [tipLabel sizeToFit]; [rootLayout addSubview:tipLabel]; @@ -537,7 +534,7 @@ -(void)createDemo8:(MyLinearLayout*)rootLayout contentLayout.gravity = MyGravity_Vert_Fill; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); contentLayout.subviewHSpace = 5; - contentLayout.widthSize.lBound(scrollView.widthSize,0,1); //默认水平线性布局的宽度是wrapContentWidth的但是最小的宽度和父视图相等,这样对于一些大尺寸屏幕因为能够容纳内容而不会产生滚动。 + contentLayout.widthSize.lBound(scrollView.widthSize,0,1); //默认水平线性布局的宽度是自适应但是最小的宽度和父视图相等,这样对于一些大尺寸屏幕因为能够容纳内容而不会产生滚动。 [scrollView addSubview:contentLayout]; @@ -555,7 +552,7 @@ -(void)createDemo8:(MyLinearLayout*)rootLayout label2.font = [CFTool font:15]; label2.backgroundColor = [CFTool color:6]; [label2 sizeToFit]; - //中间视图的左边间距是0.5,右边间距是0.5。表明中间视图的左右间距占用剩余的空间而达到居中的效果。这样在屏幕尺寸足够时则会产生居中效果,而屏幕尺寸不足时则会缩小间距,但是这里面最左边的最小间距是0而最右边的最小间距是30,这样布局视图因为具有wrapContentWidth属性所以会扩充宽度而达到滚动的效果。 + //中间视图的左边间距是0.5,右边间距是0.5。表明中间视图的左右间距占用剩余的空间而达到居中的效果。这样在屏幕尺寸足够时则会产生居中效果,而屏幕尺寸不足时则会缩小间距,但是这里面最左边的最小间距是0而最右边的最小间距是30,这样布局视图因为具有宽度自适应属性所以会扩充宽度而达到滚动的效果。 label2.leadingPos.equalTo(@0.5).min(0); label2.trailingPos.equalTo(@0.5).min(30); [contentLayout addSubview:label2]; @@ -578,7 +575,7 @@ -(void)createDemo9:(MyLinearLayout*)rootLayout tipLabel.text = @"9.下面的例子中最右边的视图如果能够被屏幕容纳则放在最右边,否则就会产生滚动效果。这个例子同样也可以应用在纵向屏幕中:很多页面里面最下边需要放一个按钮,如果屏幕尺寸够高则总是放在最底部,如果屏幕尺寸不够则会产生滚动效果。 您可以分别在iPhone4/5/6/6+上以及横竖屏测试效果:"; tipLabel.font = [CFTool font:14]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; + tipLabel.myHeight = MyLayoutSize.wrap; tipLabel.myTop = 10; [tipLabel sizeToFit]; [rootLayout addSubview:tipLabel]; @@ -594,7 +591,7 @@ -(void)createDemo9:(MyLinearLayout*)rootLayout contentLayout.gravity = MyGravity_Vert_Fill; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); contentLayout.subviewHSpace = 5; - contentLayout.widthSize.lBound(scrollView.widthSize,0,1); //默认水平线性布局的宽度是wrapContentWidth的但是最小的宽度和父视图相等,这样对于一些大尺寸屏幕因为能够容纳内容而不会产生滚动。 + contentLayout.widthSize.lBound(scrollView.widthSize,0,1); //默认水平线性布局的宽度是自适应的但是最小的宽度和父视图相等,这样对于一些大尺寸屏幕因为能够容纳内容而不会产生滚动。 [scrollView addSubview:contentLayout]; @@ -620,7 +617,7 @@ -(void)createDemo9:(MyLinearLayout*)rootLayout label3.font = [CFTool font:15]; label3.backgroundColor = [CFTool color:7]; [label3 sizeToFit]; - label3.leadingPos.equalTo(@0.5).min(30); //最后一个视图的左边距占用剩余的空间,但是最低不能小于30。这样设置的意义是如果布局视图够宽则第三个子视图的左边间距是剩余的空间,这样就保证了第三个子视图总是在最右边。而如果剩余空间不够时,则因为这里最小的宽度是30,而布局视图又是wrapContentWidth,所以就会扩充布局视图的宽度,而产生滚动效果。这里的最小值30很重要,也就是第三个子视图和其他子视图的最小间距,具体设置多少就要看UI的界面效果图了。 + label3.leadingPos.equalTo(@0.5).min(30); //最后一个视图的左边距占用剩余的空间,但是最低不能小于30。这样设置的意义是如果布局视图够宽则第三个子视图的左边间距是剩余的空间,这样就保证了第三个子视图总是在最右边。而如果剩余空间不够时,则因为这里最小的宽度是30,而布局视图又是宽度自适应,所以就会扩充布局视图的宽度,而产生滚动效果。这里的最小值30很重要,也就是第三个子视图和其他子视图的最小间距,具体设置多少就要看UI的界面效果图了。 [contentLayout addSubview:label3]; } @@ -646,13 +643,12 @@ -(void)createDemo10:(MyLinearLayout*)rootLayout tipLabel.text = @"10.下面的例子中展示了一行中各个子视图的宽度和间距都将根据屏幕的尺寸来进行拉伸和收缩,这样不管在任何尺寸的屏幕下都能达到完美的适配。在实践中UI人员往往会按某个设备的尺寸给出一张效果图,那么我们只需要按这个效果图中的子视图的宽度来计算好所占用的宽度和间距的比例,然后我们通过对视图的宽度和间距按比例值进行设置,这样就会使得子视图的真实宽度和间距将根据屏幕的尺寸进行拉升和收缩。您可以分别在iPhone4/5/6/6+上以及横竖屏测试效果:"; tipLabel.font = [CFTool font:14]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; + tipLabel.myHeight = MyLayoutSize.wrap; tipLabel.myTop = 10; [tipLabel sizeToFit]; [rootLayout addSubview:tipLabel]; MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; - contentLayout.wrapContentWidth = NO; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); contentLayout.myHeight = 60; //高度为60. contentLayout.gravity = MyGravity_Vert_Center; //内容垂直居中。 @@ -714,13 +710,12 @@ -(void)createDemo11:(MyLinearLayout*)rootLayout tipLabel.text = @"11.下面一个例子展示了当某个布局视图的尺寸能够容纳里面所有子视图的尺寸和间距时就按正常显示,而当尺寸不足以容纳所有子视图的尺寸和间距之和时我们就对子视图之间的间距进行压缩,以便能将所有子视图的内容都显示出来。您可以分别在iPhone4/5/6/6+上以及横竖屏测试效果:"; tipLabel.font = [CFTool font:14]; tipLabel.adjustsFontSizeToFitWidth = YES; - tipLabel.wrapContentHeight = YES; + tipLabel.myHeight = MyLayoutSize.wrap; tipLabel.myTop = 10; [tipLabel sizeToFit]; [rootLayout addSubview:tipLabel]; MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; - contentLayout.wrapContentWidth = NO; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); contentLayout.myHeight = 60; //高度为60. contentLayout.gravity = MyGravity_Vert_Center; //内容垂直居中。 @@ -747,6 +742,53 @@ -(void)createDemo11:(MyLinearLayout*)rootLayout } +-(void)createDemo12:(MyLinearLayout*)rootLayout +{ + //一行内的子视图的间距会根据屏幕尺寸自动缩小。 + UILabel *tipLabel = [UILabel new]; + tipLabel.text = @"12. 当布局视图下有多个子视图时有可能无法将这些子视图显示完全,而是需要将某些子视图的尺寸或者子视图之间的间距进行压缩处理。这里可以通过设置子视图尺寸中的shrink或者间距中的shrink值来进行处理。默认情况下这些值都是0表明不压缩,值越大压缩就越大"; + tipLabel.font = [CFTool font:14]; + tipLabel.adjustsFontSizeToFitWidth = YES; + tipLabel.myHeight = MyLayoutSize.wrap; + tipLabel.myTop = 10; + [tipLabel sizeToFit]; + [rootLayout addSubview:tipLabel]; + + MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); + contentLayout.heightSize.equalTo(@(MyLayoutSize.wrap)).min(60); + contentLayout.gravity = MyGravity_Vert_Center; //内容垂直居中。 + contentLayout.backgroundColor = [CFTool color:0]; + [rootLayout addSubview:contentLayout]; + + UILabel *A = [self createLabel:@"不压缩" color:5]; + A.widthSize.equalTo(@(MyLayoutSize.wrap)); + A.heightSize.equalTo(@(MyLayoutSize.wrap)); + A.widthSize.shrink = 0; + [contentLayout addSubview:A]; + + UILabel *B = [self createLabel:@"压缩1,间距不压缩" color:6]; + B.widthSize.equalTo(@(MyLayoutSize.wrap)); + B.heightSize.equalTo(@(MyLayoutSize.wrap)); + B.widthSize.shrink = 1; + B.leftPos.equalTo(@(20)).shrink = 0; + [contentLayout addSubview:B]; + + UILabel *C = [self createLabel:@"压缩2,间距压缩1" color:7]; + C.widthSize.equalTo(@(MyLayoutSize.wrap)); + C.heightSize.equalTo(@(MyLayoutSize.wrap)); + C.widthSize.shrink = 2; + C.leftPos.equalTo(@(20)).shrink = 1; + [contentLayout addSubview:C]; + + UILabel *D = [self createLabel:@"压缩3,间距压缩2" color:8]; + D.widthSize.equalTo(@(MyLayoutSize.wrap)); + D.heightSize.equalTo(@(MyLayoutSize.wrap)); + D.widthSize.shrink = 3; + D.leftPos.equalTo(@(20)).shrink = 2; + [contentLayout addSubview:D]; +} + - (void)viewDidLoad { diff --git a/MyLayoutDemo/AllTest8ViewController.m b/MyLayoutDemo/AllTest8ViewController.m index 13d7f16..7d9dbc3 100644 --- a/MyLayoutDemo/AllTest8ViewController.m +++ b/MyLayoutDemo/AllTest8ViewController.m @@ -41,7 +41,7 @@ - (void)viewDidLoad { // Do any additional setup after loading the view. /* - 本例子演示当把一个布局视图加入到非布局视图时的各种场景。当把一个布局视图加入到非布局父视图时,因为无法完全对非布局父视图进行控制。所以一些布局视图的属性将不再起作用了,但是基本的视图扩展属性: leftPos,rightPos,topPos,bottomPos,centerXPos,centerYPos,widthSize,heightSize,wrapContentWidth, wrapContentHeight这几个属性仍然有意义,只不过这些属性的equalTo方法能设置的类型有限,而且这些设置都只是基于父视图的。 + 本例子演示当把一个布局视图加入到非布局视图时的各种场景。当把一个布局视图加入到非布局父视图时,因为无法完全对非布局父视图进行控制。所以一些布局视图的属性将不再起作用了,但是基本的视图扩展属性: leftPos,rightPos,topPos,bottomPos,centerXPos,centerYPos,widthSize,heightSize这几个属性仍然有意义,只不过这些属性的equalTo方法能设置的类型有限,而且这些设置都只是基于父视图的。 */ @@ -128,11 +128,9 @@ - (void)viewDidLoad { /* - 这个例子用来演示让一个布局视图在非布局视图中居中,并且其尺寸是由子视图决定的,也就是wrapContentHeight设置为YES。 + 这个例子用来演示让一个布局视图在非布局视图中居中,并且其尺寸是由子视图决定的,也就是尺寸自适应 */ - - -(void)handleDemo1AddText:(UIButton*)sender { //这里可以看出当您调整了文字的长度,不需要编写任何更新布局视图位置的代码,系统会完全帮你自动更新布局,这样就简化了我们开发。 @@ -164,7 +162,6 @@ -(void)handleDemo1:(UIButton*)sender layout.myTrailing = 0.2; //左右边距0.2表示相对边距,也就是左右边距都是父视图总宽度的20%,这样布局视图的宽度就默认为父视图的60%了。 layout.myCenterY = 0; //布局视图在父视图中垂直居中出现。 //layout.myBottom = 0; //布局视图在父视图中底部出现。您可以注释上面居中的代码并解开这句看看效果。 - [self.view addSubview:layout]; //标题 UILabel *titleLabel = [UILabel new]; @@ -177,7 +174,7 @@ -(void)handleDemo1:(UIButton*)sender //文本 UILabel *label = [UILabel new]; - label.wrapContentHeight = YES; + label.myHeight = MyLayoutSize.wrap; label.font = [CFTool font:14]; label.text = @"这是一段具有动态高度的文本,同时他也会影响着布局视图的高度。您可以单击下面的按钮来添加文本来查看效果:"; [layout addSubview:label]; @@ -185,7 +182,6 @@ -(void)handleDemo1:(UIButton*)sender //按钮容器。如果您想让两个按钮水平排列则只需在btnContainer初始化中把方向改为:MyOrientation_Horz 。您可以尝试看看效果。 MyLinearLayout *btnContainer = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert /*MyOrientation_Horz*/]; - btnContainer.wrapContentHeight = YES; //高度由子视图确定。 btnContainer.subviewVSpace = 5; //视图之间的间距设置为5 btnContainer.subviewHSpace = 5; //视图之间的间距设置为5 btnContainer.gravity = MyGravity_Horz_Fill; //里面的子视图的宽度水平填充,如果是垂直线性布局则里面的所有子视图的宽度都和父视图相等。如果是水平线性布局则会均分所有子视图的宽度。 diff --git a/MyLayoutDemo/AllTest9CollectionViewCell.m b/MyLayoutDemo/AllTest9CollectionViewCell.m index 4f63adf..2aca016 100644 --- a/MyLayoutDemo/AllTest9CollectionViewCell.m +++ b/MyLayoutDemo/AllTest9CollectionViewCell.m @@ -28,22 +28,20 @@ -(instancetype)initWithFrame:(CGRect)frame { self.backgroundColor = [CFTool color:(random()%14 + 1)]; _rootLayout= [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; - _rootLayout.topPadding = 5; - _rootLayout.bottomPadding = 5; + _rootLayout.paddingTop = 5; + _rootLayout.paddingBottom = 5; _rootLayout.subviewVSpace = 10; _rootLayout.myHorzMargin = 0; - _rootLayout.wrapContentHeight = YES; - _rootLayout.cacheEstimatedRect = YES; + _rootLayout.gravity = MyGravity_Horz_Fill; [self.contentView addSubview:_rootLayout]; _titleLabel = [UILabel new]; - _titleLabel.widthSize.equalTo(_titleLabel.widthSize).max(100); - _titleLabel.heightSize.equalTo(@30); + _titleLabel.myHeight = MyLayoutSize.wrap; [_rootLayout addSubview:_titleLabel]; _subtitleLabel = [UILabel new]; - _subtitleLabel.myWidth = 70; - _subtitleLabel.wrapContentHeight = YES; + _subtitleLabel.myTop = 10; + _subtitleLabel.myHeight = MyLayoutSize.wrap; [_rootLayout addSubview:_subtitleLabel]; } @@ -51,18 +49,25 @@ -(instancetype)initWithFrame:(CGRect)frame return self; } -- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority -{ - /* - 通过布局视图的sizeThatFits方法能够评估出UITableViewCell的动态高度。sizeThatFits并不会进行布局而只是评估布局的尺寸。 - 因为cell的高度是自适应的,因此这里通过调用高度为wrap的布局视图的sizeThatFits来获取真实的高度。 - */ - return [self.rootLayout sizeThatFits:targetSize];//如果使用系统自带的分割线,请记得将返回的高度height+1 - +- (void)prepareForReuse { + [super prepareForReuse]; } --(void)setModel:(AllTest9DataModel *)model -{ +- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes { + [super applyLayoutAttributes:layoutAttributes]; +} + +- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority { + ///自适应高度处理,需要重载这个方法来指定尺寸和位置。 + CGSize sz = [self.rootLayout systemLayoutSizeFittingSize:targetSize withHorizontalFittingPriority:horizontalFittingPriority verticalFittingPriority:verticalFittingPriority]; + return sz; +} + +- (UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes { + return [super preferredLayoutAttributesFittingAttributes:layoutAttributes]; +} + +-(void)setModel:(AllTest9DataModel *)model { _titleLabel.text = model.title; _subtitleLabel.text = model.subtitle; } diff --git a/MyLayoutDemo/AllTest9ViewController.m b/MyLayoutDemo/AllTest9ViewController.m index db91bfd..7a5dac5 100644 --- a/MyLayoutDemo/AllTest9ViewController.m +++ b/MyLayoutDemo/AllTest9ViewController.m @@ -8,6 +8,7 @@ #import "AllTest9ViewController.h" #import "AllTest9CollectionViewCell.h" +#import "AllTest9WaterFlowLayout.h" @interface AllTest9ViewController () @@ -21,17 +22,21 @@ @implementation AllTest9ViewController -(instancetype)init { - UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; - layout.scrollDirection = UICollectionViewScrollDirectionVertical | UICollectionViewScrollDirectionHorizontal; - - //如果您的cell是需要动态尺寸则需要设置estimatedItemSize的值为非0 - layout.estimatedItemSize = CGSizeMake(100, 100); + AllTest9WaterFlowLayout *layout = [[AllTest9WaterFlowLayout alloc] init]; + layout.numberOfColumn = 2; + layout.vertSpace = 10; + layout.horzSpace = 10; + //您可以试试内置的UICollectionViewFlowLayout效果。 + /* + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.minimumLineSpacing = 10; + layout.minimumInteritemSpacing = 10; + layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize; + */ self = [self initWithCollectionViewLayout:layout]; - if (self != nil) - { + if (self != nil) { } - return self; } @@ -104,6 +109,11 @@ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { #pragma mark +- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { + return CGSizeMake((collectionView.frame.size.width - 10)/2, 0); +} + + - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return 1; } @@ -115,77 +125,9 @@ - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSe - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { AllTest9CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; - - AllTest9DataModel *model = self.datas[indexPath.section * 1 + indexPath.row]; - cell.model = model; - - return cell; } -#pragma mark - -/* -// Uncomment this method to specify if the specified item should be highlighted during tracking -- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath { - return YES; -} -*/ - -/* -// Uncomment this method to specify if the specified item should be selected -- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath { - return YES; -} -*/ - -/* -// Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item -- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath { - return NO; -} - -- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { - return NO; -} - -- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { - -} -*/ - -/* -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath -{ - return CGSizeMake(100, 100); -} - -- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section -{ - return UIEdgeInsetsMake(10, 10, 10, 10); -} - -- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section -{ - return 60; -} - -- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section -{ - return 70; -} - - -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section -{ - -} -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section -{ - -} -*/ - @end diff --git a/MyLayoutDemo/AllTest9WaterFlowLayout.h b/MyLayoutDemo/AllTest9WaterFlowLayout.h new file mode 100644 index 0000000..54b7820 --- /dev/null +++ b/MyLayoutDemo/AllTest9WaterFlowLayout.h @@ -0,0 +1,21 @@ +// +// AllTest9WaterFlowLayout.h +// MyLayoutDemo +// +// Created by oubaiquan on 2022/12/19. +// Copyright © 2022 YoungSoft. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface AllTest9WaterFlowLayout : UICollectionViewLayout + +@property (nonatomic, assign) NSUInteger numberOfColumn; +@property (nonatomic, assign) CGFloat horzSpace; +@property (nonatomic, assign) CGFloat vertSpace; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MyLayoutDemo/AllTest9WaterFlowLayout.m b/MyLayoutDemo/AllTest9WaterFlowLayout.m new file mode 100644 index 0000000..1e6208f --- /dev/null +++ b/MyLayoutDemo/AllTest9WaterFlowLayout.m @@ -0,0 +1,147 @@ +// +// AllTest9WaterFlowLayout.m +// MyLayoutDemo +// +// Created by oubaiquan on 2022/12/19. +// Copyright © 2022 YoungSoft. All rights reserved. +// + +#import "AllTest9WaterFlowLayout.h" + +@interface AllTest9WaterFlowLayout() + +//存放每一列的高度 +@property (nonatomic, strong) NSMutableArray *columnHeightArray; + +//存放 每一个item的 属性 包含 frame以及下标 +@property (nonatomic, strong) NSMutableArray *attributesArray; + + +@end + + +@implementation AllTest9WaterFlowLayout + + +//获取最大值 +- (CGFloat)maxHeight +{ + CGFloat max = 0; + for (NSNumber *height in _columnHeightArray) { + CGFloat h = [height floatValue]; + if (max < h) { + max = h; + } + } + return max; +} + +- (void)invalidateLayout { + [super invalidateLayout]; + _columnHeightArray = nil; + _attributesArray = nil; +} + +-(void)invalidateLayoutWithContext:(UICollectionViewLayoutInvalidationContext *)context { + [super invalidateLayoutWithContext:context]; +} + +//重写父类的布局方法 +- (void)prepareLayout +{ + [super prepareLayout]; + + + if (_columnHeightArray == nil) { + _columnHeightArray = [[NSMutableArray alloc] initWithCapacity:self.numberOfColumn]; + for (int i = 0; i < self.numberOfColumn; i ++) { + [_columnHeightArray addObject:@(self.vertSpace)]; + } + } + + if (_attributesArray == nil) { + _attributesArray = [[NSMutableArray alloc] init]; + + CGFloat totalWidth = self.collectionView.frame.size.width; + NSUInteger itemCount = [self.collectionView numberOfItemsInSection:0]; + CGFloat itemWidth = (totalWidth - self.horzSpace) / self.numberOfColumn; + for (int i = 0; i < itemCount; i ++) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; + UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; + attribute.frame = CGRectMake(-1, 0, itemWidth, 0); + [_attributesArray addObject:attribute]; + } + } +} + + +- (BOOL)shouldInvalidateLayoutForPreferredLayoutAttributes:(UICollectionViewLayoutAttributes *)preferredAttributes withOriginalAttributes:(UICollectionViewLayoutAttributes *)originalAttributes { + + UICollectionViewLayoutAttributes *attributes = _attributesArray[preferredAttributes.indexPath.item]; + if (attributes.frame.origin.x >= 0) { + return NO; + } + + NSInteger minHeightIndex = -1; + CGFloat minHeight = CGFLOAT_MAX; + for (int j = 0; j < self.columnHeightArray.count; j++) { + CGFloat h = [self.columnHeightArray[j] doubleValue]; + if (minHeight > h) { + minHeight = h; + minHeightIndex = j; + } + } + CGFloat x = minHeightIndex * (preferredAttributes.size.width + self.horzSpace); + CGFloat y = minHeight; + attributes.frame = CGRectMake(x, y, preferredAttributes.size.width, preferredAttributes.size.height); + self.columnHeightArray[minHeightIndex] = [NSNumber numberWithFloat:minHeight + preferredAttributes.size.height + self.vertSpace]; + return YES; +} + +//- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { +// BOOL ok = [super shouldInvalidateLayoutForBoundsChange:newBounds]; +// return YES; +//} + +//重写这个方法,可以返回集合视图的总高度 +- (CGSize)collectionViewContentSize { + return CGSizeMake(self.collectionView.frame.size.width, [self maxHeight]); +} + + +- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { + return self.attributesArray; +} + + +//@property(class, nonatomic, readonly) Class layoutAttributesClass; // override this method to provide a custom class to be used when instantiating instances of UICollectionViewLayoutAttributes +//@property(class, nonatomic, readonly) Class invalidationContextClass API_AVAILABLE(ios(7.0)); // override this method to provide a custom class to be used for invalidation contexts +// +//// The collection view calls -prepareLayout once at its first layout as the first message to the layout instance. +//// The collection view calls -prepareLayout again after layout is invalidated and before requerying the layout information. +//// Subclasses should always call super if they override. +//- (void)prepareLayout; +// +//// UICollectionView calls these four methods to determine the layout information. +//// Implement -layoutAttributesForElementsInRect: to return layout attributes for for supplementary or decoration views, or to perform layout in an as-needed-on-screen fashion. +//// Additionally, all layout subclasses should implement -layoutAttributesForItemAtIndexPath: to return layout attributes instances on demand for specific index paths. +//// If the layout supports any supplementary or decoration view types, it should also implement the respective atIndexPath: methods for those types. +//- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect; // return an array layout attributes instances for all the views in the given rect +//- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath; +//- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath; +//- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)elementKind atIndexPath:(NSIndexPath *)indexPath; +// +//- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds; // return YES to cause the collection view to requery the layout for geometry information +//- (UICollectionViewLayoutInvalidationContext *)invalidationContextForBoundsChange:(CGRect)newBounds API_AVAILABLE(ios(7.0)); +// +//- (BOOL)shouldInvalidateLayoutForPreferredLayoutAttributes:(UICollectionViewLayoutAttributes *)preferredAttributes withOriginalAttributes:(UICollectionViewLayoutAttributes *)originalAttributes API_AVAILABLE(ios(8.0)); +//- (UICollectionViewLayoutInvalidationContext *)invalidationContextForPreferredLayoutAttributes:(UICollectionViewLayoutAttributes *)preferredAttributes withOriginalAttributes:(UICollectionViewLayoutAttributes *)originalAttributes API_AVAILABLE(ios(8.0)); +// +//- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity; // return a point at which to rest after scrolling - for layouts that want snap-to-point scrolling behavior +//- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset API_AVAILABLE(ios(7.0)); // a layout can return the content offset to be applied during transition or update animations +// +//@property(nonatomic, readonly) CGSize collectionViewContentSize; // Subclasses must override this method and use it to return the width and height of the collection view’s content. These values represent the width and height of all the content, not just the content that is currently visible. The collection view uses this information to configure its own content size to facilitate scrolling. + + + +@end diff --git a/MyLayoutDemo/AllTestExampleViewController.h b/MyLayoutDemo/AllTestExampleViewController.h new file mode 100644 index 0000000..fcc1809 --- /dev/null +++ b/MyLayoutDemo/AllTestExampleViewController.h @@ -0,0 +1,14 @@ +// +// AllTest11ViewController.h +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import + + +@interface AllTestExampleViewController : UIViewController + +@end diff --git a/MyLayoutDemo/AllTestExampleViewController.m b/MyLayoutDemo/AllTestExampleViewController.m new file mode 100644 index 0000000..aa1409c --- /dev/null +++ b/MyLayoutDemo/AllTestExampleViewController.m @@ -0,0 +1,276 @@ +// +// AllTestExampleViewController.m +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import "AllTestExampleViewController.h" +#import "MyLayout.h" +#import "CFTool.h" + +@interface AllTestExampleViewController () + + +@end + +@implementation AllTestExampleViewController + +- (void)viewDidLoad { + self.edgesForExtendedLayout = UIRectEdgeNone; //设置视图控制器中的视图尺寸不延伸到导航条或者工具条下面。您可以注释这句代码看看效果。 + + [super viewDidLoad]; + self.view.backgroundColor = [UIColor whiteColor]; + + // [self example1]; + // [self example2]; + //[self example3]; + // [self example4]; + [self example5]; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +-(void)example1 +{ + //验证相对布局的其他分支覆盖。 + + MyRelativeLayout *rootLayout = [MyRelativeLayout new]; + rootLayout.myMargin = 0; + [self.view addSubview:rootLayout]; + + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(100, 100); + v1.myTop = 100; + v1.myLeft = 100; + v1.visibility = MyVisibility_Gone; + [rootLayout addSubview:v1]; + + //某个视图的水平居中依赖另外一个视图,另外一个视图隐藏。 + UIView *v2 = [UIView new]; + v2.mySize = CGSizeMake(100, 100); + v2.centerXPos.equalTo(v1.centerXPos).offset(20); + v2.centerYPos.equalTo(v1.centerYPos).offset(20); + [rootLayout addSubview:v2]; + + //某个视图的左边依赖另外一个视图,另外一个视图隐藏。 + UIView *v3 = [UIView new]; + v3.mySize = CGSizeMake(100, 100); + v3.leadingPos.equalTo(v1.leadingPos).offset(20); + v3.bottomPos.equalTo(v1.bottomPos).offset(20); + [rootLayout addSubview:v3]; + + UIView *v4 = [UIView new]; + v4.mySize = CGSizeMake(100, 100); + v4.leadingPos.lBound(v3.leadingPos, 0); + v4.trailingPos.uBound(rootLayout.trailingPos,0); + v4.bottomPos.equalTo(@(10)); + [rootLayout addSubview:v4]; + + UIView *v5 = [UIView new]; + v5.mySize = CGSizeMake(100, 100); + v5.baselinePos.equalTo(v4.baselinePos).offset(20); + [rootLayout addSubview:v5]; + + UILabel *v6 = [UILabel new]; + v6.mySize = CGSizeMake(100, 100); + v6.baselinePos.equalTo(v1.baselinePos).offset(20); + [rootLayout addSubview:v6]; + + UILabel *v7 = [UILabel new]; + v7.mySize = CGSizeMake(100, 100); + v7.baselinePos.equalTo(@(40)); + [rootLayout addSubview:v7]; + +} + +-(void)example2 +{ + MyFrameLayout *rootLayout = [MyFrameLayout new]; + rootLayout.myMargin = 0; + [self.view addSubview:rootLayout]; + + // B 视图 + UIScrollView *scrollview = [[UIScrollView alloc] init]; + scrollview.backgroundColor = [UIColor blueColor]; + scrollview.myHorzMargin = 0; + scrollview.wrapContentHeight = YES; + scrollview.heightSize.max(400).min(280); + [rootLayout addSubview:scrollview]; + + // 内容C视图 + MyLinearLayout * backLinear = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + backLinear.backgroundColor = [UIColor greenColor]; + backLinear.myHorzMargin = 0; + backLinear.heightSize.min(280); + backLinear.gravity = MyGravity_Vert_Bottom; + [scrollview addSubview:backLinear]; + + UIView *v = [UIView new]; + v.myHeight = 500; //调整不同的尺寸得到不同的结果。分别设置为100, 350, 500 + v.myWidth = 100; + v.backgroundColor = [UIColor redColor]; + [backLinear addSubview:v]; + +} + +-(void)example3 +{ + //用链式语法创建一个弹性布局,宽度和父视图一致,高度为100 + MyFlexLayout *layout = MyFlexLayout.new.myFlex + .flex_direction(MyFlexDirection_Row) + .flex_wrap(MyFlexWrap_Wrap) + .align_content(MyFlexGravity_Center) + .align_items(MyFlexGravity_Flex_End) + .vert_space(10) + .horz_space(10) + .padding(UIEdgeInsetsMake(10, 10, 10, 10)) + .margin_top(50) + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .addTo(self.view); + + + UILabel *itemA = UILabel.new.myFlex + .width(MyLayoutSize.fill) + .height(30) + .addTo(layout); + + UILabel *itemB = UILabel.new.myFlex + .flex_grow(1) + .align_self(MyFlexGravity_Flex_Start) + .height(30) + .addTo(layout); + + UILabel *itemC = UILabel.new.myFlex + .flex_grow(1) + .height(40) + .addTo(layout); + + UILabel *itemD = UILabel.new.myFlex + .flex_grow(1) + .height(50) + .addTo(layout); + + + layout.backgroundColor = [UIColor grayColor]; + itemA.text = @"A"; + itemA.backgroundColor = [UIColor redColor]; + itemB.text = @"B"; + itemB.backgroundColor = [UIColor greenColor]; + itemC.text = @"C"; + itemC.backgroundColor = [UIColor blueColor]; + itemD.text = @"D"; + itemD.backgroundColor = [UIColor yellowColor]; + +} + + +-(void)example4 +{ + MyRelativeLayout *_rootLayout = [MyRelativeLayout new]; + + _rootLayout.widthSize.equalTo(self.view.widthSize); + _rootLayout.wrapContentHeight = YES; + _rootLayout.paddingTop = 15; + _rootLayout.paddingBottom = 15; + _rootLayout.backgroundColor = [UIColor whiteColor]; + [self.view addSubview:_rootLayout]; + + + + MyLinearLayout *topLayout = [MyLinearLayout linearLayoutWithOrientation:(MyOrientation_Vert)]; + + topLayout.wrapContentWidth = YES; + topLayout.myHorzMargin = 0; + topLayout.wrapContentHeight = YES; + topLayout.subviewVSpace = 5; + [_rootLayout addSubview:topLayout]; + + + UILabel *topLable = [UILabel new]; + topLable.text = @"宿州学院-皁阳火车站"; + topLable.myTop = 0; + topLable.myLeft = 11; + topLable.wrapContentSize = YES; + [topLayout addSubview:topLable]; + + + + UIButton *checkMarkBtn = [UIButton new]; + [checkMarkBtn setImage:[UIImage imageNamed:@"train_progresscomplete"] forState:(UIControlStateNormal)]; + checkMarkBtn.widthSize.equalTo(@(13)); + checkMarkBtn.heightSize.equalTo(@(13)); + checkMarkBtn.rightPos.equalTo(@(10)); + checkMarkBtn.centerYPos.equalTo(@(0)); + [_rootLayout addSubview:checkMarkBtn]; + + UILabel *contentLabel = [UILabel new]; + contentLabel.text = @"途径:蒙城、嘎县、阜阳师范学院、嘎县、阜阳师范学院、嘎县、阜阳师范学院"; + contentLabel.myLeft = 11; + + contentLabel.wrapContentHeight= YES; + contentLabel.myRight = 27; + contentLabel.hidden = NO; + [topLayout addSubview:contentLabel]; +} + +-(void)example5 +{ + + MyRelativeLayout *rootLayout = [MyRelativeLayout new]; + rootLayout.myHeight = MyLayoutSize.wrap; + rootLayout.myHorzMargin = 0; + rootLayout.padding = UIEdgeInsetsMake(12, 12, 12, 12); + + MyLinearLayout *headerLayout = [MyLinearLayout linearLayoutWithOrientation:(MyOrientation_Horz)]; + headerLayout.topPos.equalTo(rootLayout.topPos); + headerLayout.leftPos.equalTo(rootLayout.leftPos); + headerLayout.wrapContentHeight = YES; + [rootLayout addSubview:headerLayout]; + + UIImageView *headerView = UIImageView.alloc.init; + headerView.mySize = CGSizeMake(32, 32); + [headerLayout addSubview:headerView]; + + UILabel *nameLabel = [UILabel new]; + nameLabel.text = @"欧阳大哥"; + nameLabel.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + nameLabel.alignment = MyGravity_Vert_Center; + nameLabel.myLeft = 5; + [headerLayout addSubview:nameLabel]; + + UILabel *titleLabel = [UILabel new]; + titleLabel.text = @"大师傅阿萨德阿斯蒂芬阿斯蒂芬"; + titleLabel.myHeight = MyLayoutSize.wrap; + titleLabel.leftPos.equalTo(headerLayout.leftPos).offset(32 + 5); + titleLabel.topPos.equalTo(headerLayout.bottomPos).offset(5); + titleLabel.rightPos.equalTo(rootLayout.rightPos); + [rootLayout addSubview:titleLabel]; + + MyLinearLayout *barView = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + barView.myHeight = 20; + barView.leftPos.equalTo(titleLabel.leftPos); + barView.rightPos.equalTo(rootLayout.rightPos); + barView.topPos.equalTo(titleLabel.bottomPos); + [rootLayout addSubview:barView]; + + [self.view addSubview:rootLayout]; +} + +@end diff --git a/MyLayoutDemo/AppDelegate.m b/MyLayoutDemo/AppDelegate.m index 503cf71..f38774b 100644 --- a/MyLayoutDemo/AppDelegate.m +++ b/MyLayoutDemo/AppDelegate.m @@ -10,13 +10,11 @@ #import "ViewController.h" #import "MyLayout.h" + @interface AppDelegate () @end - - - @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { @@ -33,7 +31,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( ViewController *vc = [[ViewController alloc] init]; - UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; + UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; self.window.rootViewController = nav; [self.window makeKeyAndVisible]; @@ -63,19 +61,4 @@ - (void)applicationWillTerminate:(UIApplication *)application { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } --(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event -{ - - [super touchesEnded:touches withEvent:event]; - - //为了测试使用。主要用于方便通过present出来的VC返回用。 - CGPoint pt = [touches.anyObject locationInView:self.window]; - - if (pt.y < 20 && pt.x > 0 && pt.x < 100) - { - [self.window.rootViewController dismissViewControllerAnimated:YES completion:nil]; - } - -} - @end diff --git a/MyLayoutDemo/Base.lproj/FLLTest2ViewController.xib b/MyLayoutDemo/Base.lproj/FLLTest2ViewController.xib index 66cdec6..1cfe82b 100644 --- a/MyLayoutDemo/Base.lproj/FLLTest2ViewController.xib +++ b/MyLayoutDemo/Base.lproj/FLLTest2ViewController.xib @@ -1,9 +1,10 @@ - - + + + - - + + @@ -15,18 +16,18 @@ - + - - + + - + - + @@ -37,7 +38,7 @@ - - + @@ -62,78 +63,108 @@ - - + + - - + - + + + + + + + - + - + - + - + - + @@ -141,10 +172,10 @@ - - + + - + @@ -152,34 +183,29 @@ - + - + - + - + - + - + - - - - - diff --git a/MyLayoutDemo/Base.lproj/FOLTest1ViewController.xib b/MyLayoutDemo/Base.lproj/FOLTest1ViewController.xib index 20b59f1..255c4ad 100644 --- a/MyLayoutDemo/Base.lproj/FOLTest1ViewController.xib +++ b/MyLayoutDemo/Base.lproj/FOLTest1ViewController.xib @@ -1,8 +1,9 @@ - - + + + - - + + @@ -18,14 +19,14 @@ - + - + - + @@ -39,7 +40,7 @@ - - - - + - + - + - + @@ -139,10 +140,10 @@ - + - + @@ -150,19 +151,14 @@ - + - + - - - - - diff --git a/MyLayoutDemo/CFTool.m b/MyLayoutDemo/CFTool.m index 7977c99..553cfb6 100644 --- a/MyLayoutDemo/CFTool.m +++ b/MyLayoutDemo/CFTool.m @@ -37,8 +37,6 @@ +(UIColor*)color:(NSInteger)idx +(UIFont*)font:(CGFloat)size { - //中文显示平方字体,英文显示ding. - // [NSLocale preferredLanguages] UIFont *font = [UIFont fontWithName:@"STHeitiSC-Light" size:size]; return font; diff --git a/MyLayoutDemo/DetailViewController.m b/MyLayoutDemo/DetailViewController.m index a579096..5feec8d 100644 --- a/MyLayoutDemo/DetailViewController.m +++ b/MyLayoutDemo/DetailViewController.m @@ -84,15 +84,32 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N #pragma mark -- UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + [tableView deselectRowAtIndexPath:indexPath animated:YES]; UIViewController *demoVC = [[self.demoVCList[indexPath.row][@"class"] alloc] init]; demoVC.title = self.demoVCList[indexPath.row][@"title"]; - if (self.isPresentVC) - [self.navigationController presentViewController:demoVC animated:YES completion:nil]; - else + if (self.isPresentVC) { + demoVC.modalPresentationStyle = UIModalPresentationOverFullScreen; + [self.navigationController presentViewController:demoVC animated:YES completion:^{ + if (@available(iOS 13.0, *)) { + UIButton *button = [UIButton buttonWithType:UIButtonTypeClose]; + [button addTarget:self action:@selector(handleClose:) forControlEvents:UIControlEventTouchUpInside]; + button.frame = CGRectMake(0, 20, 44, 44); + [self.view.window addSubview:button]; + + + } else { + // Fallback on earlier versions + } + }]; + + + } + else { [self.navigationController pushViewController:demoVC animated:YES]; + } } @@ -105,9 +122,14 @@ -(void)handlePushOrPresent:(UIBarButtonItem*)sender if (self.isPresentVC) { - [[[UIAlertView alloc] initWithTitle:@"" message:NSLocalizedString(@"If you select \"Present\", than you can touch topleft corner of Status bar to dissmis the view controller",@"") delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show]; + [[[UIAlertView alloc] initWithTitle:@"" message:NSLocalizedString(@"If you select \"Present\", then you can touch Close button of topleft corner to dissmis the view controller",@"") delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show]; } } +-(void)handleClose:(UIButton *)sender { + [sender removeFromSuperview]; + [self.view.window.rootViewController dismissViewControllerAnimated:YES completion:nil]; +} + @end diff --git a/MyLayoutDemo/FLLTest1ViewController.m b/MyLayoutDemo/FLLTest1ViewController.m index f3d65a4..22207ba 100644 --- a/MyLayoutDemo/FLLTest1ViewController.m +++ b/MyLayoutDemo/FLLTest1ViewController.m @@ -35,7 +35,7 @@ -(void)loadView //添加操作按钮。 MyFlowLayout *actionLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:2]; - actionLayout.wrapContentHeight = YES; + actionLayout.myHeight = MyLayoutSize.wrap; actionLayout.gravity = MyGravity_Horz_Fill; //所有子视图水平填充,也就是所有子视图的宽度相等。 actionLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); actionLayout.subviewHSpace = 5; @@ -53,14 +53,16 @@ -(void)loadView [actionLayout addSubview:[self createActionButton:NSLocalizedString(@"adjust align", @"") action:@selector(handleAdjustArrangeGravity:)]]; [actionLayout addSubview:[self createActionButton:NSLocalizedString(@"adjust spacing", @"") - action:@selector(handleAdjustMargin:)]]; + action:@selector(handleAdjustSpace:)]]; + [actionLayout addSubview:[self createActionButton:NSLocalizedString(@"adjust gravity policy", @"") + action:@selector(handleAdjustGravityPolicy:)]]; UILabel *flowLayoutSetLabel = [UILabel new]; flowLayoutSetLabel.font = [CFTool font:13]; flowLayoutSetLabel.textColor = [UIColor redColor]; flowLayoutSetLabel.adjustsFontSizeToFitWidth = YES; - flowLayoutSetLabel.wrapContentHeight = YES; - flowLayoutSetLabel.numberOfLines = 5; + flowLayoutSetLabel.myHeight = MyLayoutSize.wrap; + flowLayoutSetLabel.numberOfLines = 6; [rootLayout addSubview:flowLayoutSetLabel]; self.flowLayoutSetLabel = flowLayoutSetLabel; @@ -172,9 +174,18 @@ -(void)handleAdjustVertGravity:(id)sender vertGravity = MyGravity_Vert_Between; break; case MyGravity_Vert_Between: + vertGravity = MyGravity_Vert_Around; + break; + case MyGravity_Vert_Around: + vertGravity = MyGravity_Vert_Among; + break; + case MyGravity_Vert_Among: vertGravity = MyGravity_Vert_Fill; break; case MyGravity_Vert_Fill: + vertGravity = MyGravity_Vert_Stretch; + break; + case MyGravity_Vert_Stretch: { vertGravity = MyGravity_Vert_Top; [self.flowLayout.subviews makeObjectsPerformSelector:@selector(sizeToFit)]; @@ -209,9 +220,18 @@ -(void)handleAdjustHorzGravity:(id)sender horzGravity = MyGravity_Horz_Between; break; case MyGravity_Horz_Between: + horzGravity = MyGravity_Horz_Around; + break; + case MyGravity_Horz_Around: + horzGravity = MyGravity_Horz_Among; + break; + case MyGravity_Horz_Among: horzGravity = MyGravity_Horz_Fill; break; case MyGravity_Horz_Fill: + horzGravity = MyGravity_Horz_Stretch; + break; + case MyGravity_Horz_Stretch: { horzGravity = MyGravity_Horz_Left; [self.flowLayout.subviews makeObjectsPerformSelector:@selector(sizeToFit)]; @@ -248,6 +268,9 @@ -(void)handleAdjustArrangeGravity:(id)sender vertArrangeGravity = MyGravity_Vert_Fill; break; case MyGravity_Vert_Fill: + vertArrangeGravity = MyGravity_Vert_Stretch; + break; + case MyGravity_Vert_Stretch: { vertArrangeGravity = MyGravity_Vert_Top; [self.flowLayout.subviews makeObjectsPerformSelector:@selector(sizeToFit)]; @@ -271,6 +294,8 @@ -(void)handleAdjustArrangeGravity:(id)sender horzArrangeGravity = MyGravity_Horz_Fill; break; case MyGravity_Horz_Fill: + horzArrangeGravity = MyGravity_Horz_Stretch; + case MyGravity_Horz_Stretch: { horzArrangeGravity = MyGravity_Horz_Left; [self.flowLayout.subviews makeObjectsPerformSelector:@selector(sizeToFit)]; @@ -288,7 +313,7 @@ -(void)handleAdjustArrangeGravity:(id)sender } --(void)handleAdjustMargin:(id)sender +-(void)handleAdjustSpace:(id)sender { //调整所有子视图的水平和垂直间距。 if (self.flowLayout.subviewHSpace == 0) @@ -305,6 +330,26 @@ -(void)handleAdjustMargin:(id)sender [self flowlayoutInfo]; } +-(void)handleAdjustGravityPolicy:(id)sender +{ + switch (self.flowLayout.lastlineGravityPolicy) { + case MyGravityPolicy_No: + self.flowLayout.lastlineGravityPolicy = MyGravityPolicy_Always; + break; + case MyGravityPolicy_Always: + self.flowLayout.lastlineGravityPolicy = MyGravityPolicy_Auto; + break; + case MyGravityPolicy_Auto: + self.flowLayout.lastlineGravityPolicy = MyGravityPolicy_No; + default: + break; + } + + [self.flowLayout.subviews makeObjectsPerformSelector:@selector(sizeToFit)]; + [self.flowLayout layoutAnimationWithDuration:0.4]; + [self flowlayoutInfo]; +} + #pragma mark -- Private Method -(void)flowlayoutInfo @@ -323,8 +368,9 @@ -(void)flowlayoutInfo NSString *subviewSpaceStr = [NSString stringWithFormat:@"vert:%.0f,horz:%.0f",self.flowLayout.subviewVSpace, self.flowLayout.subviewHSpace]; + NSString *gravityPolicyStr = [self gravityPolicyInfo:self.flowLayout.lastlineGravityPolicy]; - self.flowLayoutSetLabel.text = [NSString stringWithFormat:@"flowLayout:\norientation=%@, arrangedCount=%@\ngravity=%@\narrangedGravity=%@\nsubviewSpace=(%@)",orientationStr,arrangeCountStr,gravityStr,arrangedGravityStr,subviewSpaceStr]; + self.flowLayoutSetLabel.text = [NSString stringWithFormat:@"flowLayout:\norientation=%@, arrangedCount=%@\ngravity=%@\narrangedGravity=%@\nsubviewSpace=(%@)\n%@",orientationStr,arrangeCountStr,gravityStr,arrangedGravityStr,subviewSpaceStr,gravityPolicyStr]; } -(NSString*)gravityInfo:(MyGravity)gravity @@ -350,6 +396,15 @@ -(NSString*)gravityInfo:(MyGravity)gravity case MyGravity_Vert_Between: vertGravityStr = @"MyGravity_Vert_Between"; break; + case MyGravity_Vert_Around: + vertGravityStr = @"MyGravity_Vert_Around"; + break; + case MyGravity_Vert_Among: + vertGravityStr = @"MyGravity_Vert_Among"; + break; + case MyGravity_Vert_Stretch: + vertGravityStr = @"MyGravity_Vert_Stretch"; + break; case MyGravity_Vert_Window_Center: vertGravityStr = @"MyGravity_Vert_Window_Center"; break; @@ -375,6 +430,15 @@ -(NSString*)gravityInfo:(MyGravity)gravity case MyGravity_Horz_Between: horzGravityStr = @"MyGravity_Horz_Between"; break; + case MyGravity_Horz_Around: + horzGravityStr = @"MyGravity_Horz_Around"; + break; + case MyGravity_Horz_Among: + horzGravityStr = @"MyGravity_Horz_Among"; + break; + case MyGravity_Horz_Stretch: + horzGravityStr = @"MyGravity_Horz_Stretch"; + break; case MyGravity_Horz_Window_Center: horzGravityStr = @"MyGravity_Horz_Window_Center"; break; @@ -388,6 +452,22 @@ -(NSString*)gravityInfo:(MyGravity)gravity } +-(NSString*)gravityPolicyInfo:(MyGravityPolicy)gravityPolicy +{ + switch (gravityPolicy) { + case MyGravityPolicy_No: + return @"policy:No"; + break; + case MyGravityPolicy_Always: + return @"policy:Always"; + case MyGravityPolicy_Auto: + return @"policy:Auto"; + default: + break; + } + + return @""; +} /* #pragma mark - Navigation diff --git a/MyLayoutDemo/FLLTest2ViewController.m b/MyLayoutDemo/FLLTest2ViewController.m index fa7d51a..adcbe14 100644 --- a/MyLayoutDemo/FLLTest2ViewController.m +++ b/MyLayoutDemo/FLLTest2ViewController.m @@ -62,8 +62,8 @@ -(void)createTagButton:(NSString*)text tagButton.backgroundColor = [CFTool color:random()%15]; //这里可以看到尺寸宽度等于自己的尺寸宽度并且再增加10,且最小是40,意思是按钮的宽度是等于自身内容的宽度再加10,但最小的宽度是40 //如果没有这个设置,而是直接调用了sizeToFit则按钮的宽度就是内容的宽度。 - tagButton.widthSize.equalTo(tagButton.widthSize).add(10).min(40); - tagButton.heightSize.equalTo(tagButton.heightSize).add(10); //高度根据自身的内容再增加10 + tagButton.widthSize.equalTo(@(MyLayoutSize.wrap)).add(10).min(40); + tagButton.heightSize.equalTo(@(MyLayoutSize.wrap)).add(10); //高度根据自身的内容再增加10 [tagButton sizeToFit]; [tagButton addTarget:self action:@selector(handleDelTag:) forControlEvents:UIControlEventTouchUpInside]; [self.flowLayout addSubview:tagButton]; @@ -72,7 +72,28 @@ -(void)createTagButton:(NSString*)text #pragma mark -- Handle Method -- (IBAction)handleShrinkMargin:(UISwitch *)sender { +- (IBAction)handleAddTag:(id)sender { + + if (self.tagTextField.text.length == 0) + return; + + [self createTagButton:self.tagTextField.text]; + + self.tagTextField.text = @""; + + [self.flowLayout layoutAnimationWithDuration:0.2]; + +} + +- (IBAction)handleDelTag:(UIButton*)sender { + + [sender removeFromSuperview]; + + [self.flowLayout layoutAnimationWithDuration:0.2]; +} + + +- (IBAction)handleAdjustSpaceChange:(UISwitch *)sender { //间距拉伸 if (sender.isOn) @@ -83,7 +104,7 @@ - (IBAction)handleShrinkMargin:(UISwitch *)sender { [self.flowLayout layoutAnimationWithDuration:0.2]; } -- (IBAction)handleShrinkContent:(UISwitch *)sender { +- (IBAction)handleAdjustSizeChange:(UISwitch *)sender { //内容拉伸 if (sender.isOn) @@ -95,7 +116,7 @@ - (IBAction)handleShrinkContent:(UISwitch *)sender { } -- (IBAction)handleShrinkAuto:(UISwitch *)sender { +- (IBAction)handleAutoArrangeChange:(UISwitch *)sender { //自动调整位置。 if (sender.isOn) @@ -107,25 +128,15 @@ - (IBAction)handleShrinkAuto:(UISwitch *)sender { } -- (IBAction)handleAddTag:(id)sender { - - if (self.tagTextField.text.length == 0) - return; - - [self createTagButton:self.tagTextField.text]; - - self.tagTextField.text = @""; - - [self.flowLayout layoutAnimationWithDuration:0.2]; - +-(IBAction)handleGravityAlwaysChange:(id)sender +{ + if (self.flowLayout.lastlineGravityPolicy == MyGravityPolicy_No) + self.flowLayout.lastlineGravityPolicy = MyGravityPolicy_Always; + else + self.flowLayout.lastlineGravityPolicy = MyGravityPolicy_No; } -- (IBAction)handleDelTag:(UIButton*)sender { - - [sender removeFromSuperview]; - - [self.flowLayout layoutAnimationWithDuration:0.2]; -} + @end diff --git a/MyLayoutDemo/FLLTest3ViewController.m b/MyLayoutDemo/FLLTest3ViewController.m index e7b3b4d..12abf0b 100644 --- a/MyLayoutDemo/FLLTest3ViewController.m +++ b/MyLayoutDemo/FLLTest3ViewController.m @@ -10,15 +10,15 @@ #import "MyLayout.h" #import "CFTool.h" + @interface FLLTest3ViewController () @property(nonatomic, strong) MyFlowLayout *flowLayout; -@property(nonatomic, assign) NSInteger oldIndex; -@property(nonatomic, assign) NSInteger currentIndex; -@property(nonatomic, assign) BOOL hasDrag; @property(nonatomic, strong) UIButton *addButton; +@property(nonatomic, strong) MyLayoutDragger *dragger; + @end @implementation FLLTest3ViewController @@ -33,20 +33,20 @@ -(void)loadView 子视图的扩展属性useFrame表示某个子视图不受布局视图的布局控制,而是用最原始的frame属性设置来实现自定义的位置和尺寸的设定。 子视图的扩展属性noLayout表示某个子视图会参与布局,但是并不会正真的调整布局后的frame值。 + 在新版本1.9.0中将这些能力进行了统一的封装,通过一个拖放类来实现这些功能。 + */ MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; rootLayout.backgroundColor = [UIColor whiteColor]; rootLayout.gravity = MyGravity_Horz_Fill; //垂直线性布局里面的子视图的宽度和布局视图一致。 - rootLayout.wrapContentWidth = NO; - rootLayout.wrapContentHeight = NO; self.view = rootLayout; UILabel *tipLabel = [UILabel new]; tipLabel.font = [CFTool font:13]; tipLabel.text = NSLocalizedString(@" You can drag the following tag to adjust location in layout, MyLayout can use subview's useFrame,noLayout property and layout view's autoresizesSubviews propery to complete some position adjustment and the overall animation features: \n useFrame set to YES indicates subview is not controlled by the layout view but use its own frame to set the location and size instead.\n \n autoresizesSubviews set to NO indicate layout view will not do any layout operation, and will remain in the position and size of all subviews.\n \n noLayout set to YES indicate subview in the layout view just only take up the position and size but not real adjust the position and size when layouting.", @""); tipLabel.textColor = [CFTool color:4]; - tipLabel.wrapContentHeight = YES; //这两个属性结合着使用实现自动换行和文本的动态高度。 + tipLabel.heightSize.equalTo(@(MyLayoutSize.wrap)); //这两个属性结合着使用实现自动换行和文本的动态高度。 tipLabel.topPos.equalTo(self.topLayoutGuide); //子视图不会延伸到导航条屏幕下面。 [rootLayout addSubview:tipLabel]; @@ -75,8 +75,12 @@ - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. - for (NSInteger i = 0; i < 14; i++) - { + //创建一个布局拖动器。 + self.dragger = [self.flowLayout createLayoutDragger]; + self.dragger.animateDuration = 0.2; + + + for (NSInteger i = 0; i < 14; i++){ NSString *label = [NSString stringWithFormat:NSLocalizedString(@"drag me %ld", @""),i]; [self.flowLayout addSubview:[self createTagButton:label]]; } @@ -84,6 +88,7 @@ - (void)viewDidLoad { //最后添加添加按钮。 self.addButton = [self createAddButton]; [self.flowLayout addSubview:self.addButton]; + self.dragger.exclusiveViews = @[self.addButton]; } @@ -105,11 +110,11 @@ -(UIButton*)createTagButton:(NSString*)text tagButton.backgroundColor = [CFTool color:(random()%14 + 1)]; tagButton.heightSize.equalTo(@44); - [tagButton addTarget:self action:@selector(handleTouchDrag:withEvent:) forControlEvents:UIControlEventTouchDragInside]; //注册拖动事件。 - [tagButton addTarget:self action:@selector(handleTouchDrag:withEvent:) forControlEvents:UIControlEventTouchDragOutside]; //注册外面拖动事件。 - [tagButton addTarget:self action:@selector(handleTouchDown:withEvent:) forControlEvents:UIControlEventTouchDown]; //注册按下事件 - [tagButton addTarget:self action:@selector(handleTouchUp:withEvent:) forControlEvents:UIControlEventTouchUpInside]; //注册抬起事件 - [tagButton addTarget:self action:@selector(handleTouchUp:withEvent:) forControlEvents:UIControlEventTouchCancel]; //注册终止事件 + [tagButton addTarget:self.dragger action:@selector(dragginView:withEvent:) forControlEvents:UIControlEventTouchDragInside]; //注册拖动事件。 + [tagButton addTarget:self.dragger action:@selector(dragginView:withEvent:) forControlEvents:UIControlEventTouchDragOutside]; //注册外面拖动事件。 + [tagButton addTarget:self.dragger action:@selector(dragView:withEvent:) forControlEvents:UIControlEventTouchDown]; //注册按下事件 + [tagButton addTarget:self.dragger action:@selector(dropView:withEvent:) forControlEvents:UIControlEventTouchUpInside]; //注册抬起事件 + [tagButton addTarget:self.dragger action:@selector(dropView:withEvent:) forControlEvents:UIControlEventTouchCancel]; //注册终止事件 [tagButton addTarget:self action:@selector(handleTouchDownRepeat:withEvent:) forControlEvents:UIControlEventTouchDownRepeat]; //注册多次点击事件 return tagButton; @@ -144,100 +149,6 @@ -(void)handleAddTagButton:(id)sender } -- (IBAction)handleTouchDown:(UIButton*)sender withEvent:(UIEvent*)event { - - //在按下时记录当前要拖动的控件的索引。 - self.oldIndex = [self.flowLayout.subviews indexOfObject:sender]; - self.currentIndex = self.oldIndex; - self.hasDrag = NO; - -} - -- (IBAction)handleTouchUp:(UIButton*)sender withEvent:(UIEvent*)event { - - if (!self.hasDrag) - return; - - //当抬起时,需要让拖动的子视图调整到正确的顺序,并重新参与布局,因此这里要把拖动的子视图的useFrame设置为NO,同时把布局视图的autoresizesSubviews还原为YES。 - - //调整索引。 - for (NSInteger i = self.flowLayout.subviews.count - 1; i > self.currentIndex; i--) - { - [self.flowLayout exchangeSubviewAtIndex:i withSubviewAtIndex:i - 1]; - } - - sender.useFrame = NO; //让拖动的子视图重新参与布局,将useFrame设置为NO - self.flowLayout.autoresizesSubviews = YES; //让布局视图可以重新激发布局,这里还原为YES。 - - -} - - -- (IBAction)handleTouchDrag:(UIButton*)sender withEvent:(UIEvent*)event { - - self.hasDrag = YES; - - NSLog(@"AAA:%@", event); - //取出拖动时当前的位置点。 - CGPoint pt = [[event touchesForView:sender].anyObject locationInView:self.flowLayout]; - - UIView *sbv2 = nil; //sbv2保存拖动时手指所在的视图。 - //判断当前手指在具体视图的位置。这里要排除self.addButton的位置(因为这个按钮将固定不调整)。 - for (UIView *sbv in self.flowLayout.subviews) - { - if (sbv != sender && sender.useFrame && sbv != self.addButton) - { - CGRect rc1 = sbv.frame; - if (CGRectContainsPoint(rc1, pt)) - { - sbv2 = sbv; - break; - } - } - } - - //如果拖动的控件sender和手指下当前其他的兄弟控件有重合时则意味着需要将当前控件插入到手指下的sbv2所在的位置,并且调整sbv2的位置。 - if (sbv2 != nil) - { - - [self.flowLayout layoutAnimationWithDuration:0.2]; - - //得到要移动的视图的位置索引。 - self.currentIndex = [self.flowLayout.subviews indexOfObjectIdenticalTo:sbv2]; - - if (self.oldIndex != self.currentIndex) - { - self.oldIndex = self.currentIndex; - } - else - { - self.currentIndex = self.oldIndex + 1; - } - - - - //因为sender在bringSubviewToFront后变为了最后一个子视图,因此要调整正确的位置。 - for (NSInteger i = self.flowLayout.subviews.count - 1; i > self.currentIndex; i--) - { - [self.flowLayout exchangeSubviewAtIndex:i withSubviewAtIndex:i - 1]; - } - - //经过上面的sbv2的位置调整完成后,需要重新激发布局视图的布局,因此这里要设置autoresizesSubviews为YES。 - self.flowLayout.autoresizesSubviews = YES; - sender.useFrame = NO; - sender.noLayout = YES; //这里设置为YES表示布局时不会改变sender的真实位置而只是在布局视图中占用一个位置和尺寸,正是因为只是占用位置,因此会调整其他视图的位置。 - [self.flowLayout layoutIfNeeded]; - - } - - //在进行sender的位置调整时,要把sender移动到最顶端,也就子视图数组的的最后。同时布局视图不能激发子视图布局,因此要把autoresizesSubviews设置为NO,同时因为要自定义sender的位置,因此要把useFrame设置为YES,并且恢复noLayout为NO。 - [self.flowLayout bringSubviewToFront:sender]; //把拖动的子视图放在最后,这样这个子视图在移动时就会在所有兄弟视图的上面。 - self.flowLayout.autoresizesSubviews = NO; //在拖动时不要让布局视图激发布局 - sender.useFrame = YES; //因为拖动时,拖动的控件需要自己确定位置,不能被布局约束,因此必须要将useFrame设置为YES下面的center设置才会有效。 - sender.center = pt; //因为useFrame设置为了YES所有这里可以直接调整center,从而实现了位置的自定义设置。 - sender.noLayout = NO; //恢复noLayout为NO。 - -} - (IBAction)handleTouchDownRepeat:(UIButton*)sender withEvent:(UIEvent*)event { diff --git a/MyLayoutDemo/FLLTest4ViewController.m b/MyLayoutDemo/FLLTest4ViewController.m index 4cfe7a7..e3a27b3 100644 --- a/MyLayoutDemo/FLLTest4ViewController.m +++ b/MyLayoutDemo/FLLTest4ViewController.m @@ -21,7 +21,7 @@ @implementation FLLTest4ViewController -(void)loadView { /* - 这个例子主要展示流式布局对子视图weight属性的支持。对于垂直流式布局来说,子视图的weight值用来指定子视图的宽度在当前行剩余空间所占用的比例值,比如某个流式布局的宽度是100,而每行的数量为2个,且假如第一个子视图的宽度为20,则如果第二个子视图的weight设置为1的话则第二个子视图的真实宽度 = (100-20)*1 = 80。而假如第二个子视图的weight设置为0.5的话则第二个子视图的真实宽度 = (100 - 20) * 0.5 = 40。 + 这个例子主要展示流式布局对子视图weight属性的支持以及对特定行进行对齐停靠的定制支持。对于垂直流式布局来说,子视图的weight值用来指定子视图的宽度在当前行剩余空间所占用的比例值,比如某个流式布局的宽度是100,而每行的数量为2个,且假如第一个子视图的宽度为20,则如果第二个子视图的weight设置为1的话则第二个子视图的真实宽度 = (100-20)*1 = 80。而假如第二个子视图的weight设置为0.5的话则第二个子视图的真实宽度 = (100 - 20) * 0.5 = 40。 对于水平流式布局来说weight值用来指定一列内的剩余高度的比重值。 通过对子视图weight值的合理使用,可以很方便的替换掉需要用线性布局来实现的嵌套布局的能力。 @@ -33,7 +33,7 @@ -(void)loadView MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; rootLayout.backgroundColor = [CFTool color:11]; rootLayout.myHorzMargin = 0; - rootLayout.wrapContentHeight = YES; + rootLayout.myHeight = MyLayoutSize.wrap; rootLayout.subviewVSpace = 10; //子视图之间的间距设置为10 rootLayout.gravity = MyGravity_Horz_Fill; //所有子视图的宽度都和自己相等,这样子视图就不再需要设置宽度了。 [scrollView addSubview:rootLayout]; @@ -74,7 +74,7 @@ -(void)createFlowLayout1:(MyLinearLayout*)rootLayout //每行2列的垂直流式布局。 MyFlowLayout *flowLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:2]; flowLayout.backgroundColor = [UIColor whiteColor]; - flowLayout.wrapContentHeight = YES; //高度由子视图决定。 + flowLayout.myHeight = MyLayoutSize.wrap; //高度由子视图决定。 flowLayout.gravity = MyGravity_Horz_Center; //所有子视图整体水平居中 flowLayout.arrangedGravity = MyGravity_Vert_Center; //每行子视图垂直居中对齐。您可以这里尝试设置为:MyGravity_Vert_Top, MyGravity_Vert_Bottom的效果。 flowLayout.padding = UIEdgeInsetsMake(20, 20, 20, 20); //四周内边距设置为20 @@ -129,7 +129,6 @@ -(void)createFlowLayout1:(MyLinearLayout*)rootLayout //the fourth line: forgot password. UIView *placeholderView2 = [UIView new]; //因为流式布局这里面每行两列,所以这里建立一个宽高为0的占位视图。我们可以在流式布局中通过使用占位视图来充满行的数量。 - placeholderView2.weight = 1; [flowLayout addSubview:placeholderView2]; UIButton *forgetPasswordButton = [UIButton buttonWithType:UIButtonTypeSystem]; @@ -143,8 +142,7 @@ -(void)createFlowLayout1:(MyLinearLayout*)rootLayout rememberLabel.text = @"Remember me:"; rememberLabel.textColor = [CFTool color:4]; rememberLabel.font = [CFTool font:15]; - rememberLabel.weight = 1; - rememberLabel.myAlignment = MyGravity_Vert_Bottom; //流式布局通过arrangedGravity设置每行的对齐方式,如果某个子视图不想使用默认的对齐方式则可以通过myAlignment属性来单独设置对齐方式,这个例子中所有都是居中对齐,但是这个标题则是底部对齐。 + rememberLabel.alignment = MyGravity_Vert_Bottom; //流式布局通过arrangedGravity设置每行的对齐方式,如果某个子视图不想使用默认的对齐方式则可以通过alignment属性来单独设置对齐方式,这个例子中所有都是居中对齐,但是这个标题则是底部对齐。 [rememberLabel sizeToFit]; [flowLayout addSubview:rememberLabel]; @@ -167,6 +165,22 @@ -(void)createFlowLayout1:(MyLinearLayout*)rootLayout //第六行因为最后只有一个按钮,所以这里不需要建立占位视图。 + //我们可以通过lineGravity属性来实现为每一行进行不同的对齐方式定制,如果返回MyGravity_None则表示用gravity属性设置的对齐方式来进行处理。 + flowLayout.lineGravity = ^MyGravity(MyFlowLayout *layout, NSInteger lineIndex, NSInteger itemCount, BOOL isLastLine) { + + switch (lineIndex) { + case 3: + return MyGravity_Horz_Right; //第3行右对齐 + break; + case 4: + return MyGravity_Horz_Between; //第4行水平间距拉伸。 + break; + default: + return MyGravity_None; //其他行按gravity属性所指定的停靠对齐方式 + break; + } + }; + } @@ -233,7 +247,7 @@ -(void)createFlowLayout3:(MyLinearLayout *)rootLayout MyFlowLayout *flowLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:0]; flowLayout.backgroundColor = [CFTool color:0]; [rootLayout addSubview:flowLayout]; - flowLayout.wrapContentHeight = YES; + flowLayout.myHeight = MyLayoutSize.wrap; flowLayout.subviewSpace = 10; flowLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); @@ -310,7 +324,7 @@ -(void)createFlowLayout4:(MyLinearLayout *)rootLayout MyFlowLayout *flowLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:2]; flowLayout.backgroundColor = [CFTool color:0]; [rootLayout addSubview:flowLayout]; - flowLayout.wrapContentHeight = YES; + flowLayout.myHeight = MyLayoutSize.wrap; flowLayout.subviewSpace = 10; flowLayout.arrangedGravity = MyGravity_Vert_Center; flowLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); @@ -327,7 +341,7 @@ -(void)createFlowLayout4:(MyLinearLayout *)rootLayout titleLabel.textColor = [CFTool color:5]; titleLabel.font = [CFTool font:14]; titleLabel.weight = 1; //对于指定数量的流式布局来说这个weight的是剩余的占比。 - titleLabel.wrapContentHeight = YES; + titleLabel.myHeight = MyLayoutSize.wrap; [flowLayout addSubview:titleLabel]; //第二行第一个固定,剩余的占据全部 @@ -338,7 +352,7 @@ -(void)createFlowLayout4:(MyLinearLayout *)rootLayout priceLabel.text = @"$123.23 - $200.12"; priceLabel.textColor = [UIColor redColor]; priceLabel.font = [CFTool font:13]; - priceLabel.wrapContentWidth = YES; + priceLabel.myWidth = MyLayoutSize.wrap; priceLabel.myHeight = 30; [flowLayout addSubview:priceLabel]; @@ -346,7 +360,7 @@ -(void)createFlowLayout4:(MyLinearLayout *)rootLayout UILabel *buyButton = [UILabel new]; buyButton.text = @"Buy"; buyButton.font = [CFTool font:12]; - buyButton.wrapContentWidth = YES; + buyButton.myWidth = MyLayoutSize.wrap; buyButton.myLeading = 20; buyButton.myHeight = 30; [flowLayout addSubview:buyButton]; diff --git a/MyLayoutDemo/FLLTest5ViewController.m b/MyLayoutDemo/FLLTest5ViewController.m index 35b9e77..5b40529 100644 --- a/MyLayoutDemo/FLLTest5ViewController.m +++ b/MyLayoutDemo/FLLTest5ViewController.m @@ -12,6 +12,11 @@ @interface FLLTest5ViewController () +@property(nonatomic, strong) UIScrollView *scrollView1; +@property(nonatomic, strong) UIScrollView *scrollView2; +@property(nonatomic, strong) UIScrollView *scrollView3; +@property(nonatomic, strong) UIScrollView *scrollView4; + @end @@ -46,16 +51,16 @@ -(void)loadView //创建一个垂直数量流式布局分页从左到右滚动 [self createVertPagingFlowLayout2:rootLayout]; - - - + //创建一个垂直数量流式布局分页从左到右滚动,这里高度自适应。 + [self createVertPagingFlowLayout3:rootLayout]; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. - + UIBarButtonItem *bar =[[UIBarButtonItem alloc] initWithTitle:@"paging" style:UIBarButtonItemStylePlain target:self action:@selector(handlePagingSwitch:)]; + self.navigationItem.rightBarButtonItem = bar; } @@ -76,6 +81,8 @@ -(void)addAllItemSubviews:(MyFlowLayout*)flowLayout label.textAlignment = NSTextAlignmentCenter; label.backgroundColor = [CFTool color:random() % 14 + 1]; label.text = [NSString stringWithFormat:@"%d",i]; + label.myMargin = 5; //同时带有四边的间距! + label.mySize = CGSizeMake(60, 60); //在分页的情况下一般不需要设置条目视图的尺寸。而且即使设置了也无效。 [flowLayout addSubview:label]; } @@ -100,12 +107,13 @@ -(void)createHorzPagingFlowLayout1:(UIView*)rootLayout scrollView.pagingEnabled = YES; //开启分页滚动模式!!您可以注释这句话看看非分页滚动的布局滚动效果。 scrollView.myHeight = 200; //设置明确的高度为200,因为宽度已经由父线性布局的gravity属性确定了,所以不需要设置了。 [rootLayout addSubview:scrollView]; + self.scrollView1 = scrollView; //建立一个水平数量约束流式布局:每列展示3个子视图,每页展示9个子视图,整体从左往右滚动。 MyFlowLayout *flowLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:3]; flowLayout.pagedCount = 9; //pagedCount设置为非0时表示开始分页展示的功能,这里表示每页展示9个子视图,这个数量必须是arrangedCount的倍数。 - flowLayout.wrapContentWidth = YES; //设置布局视图的宽度由子视图包裹,当水平流式布局的这个属性设置为YES,并和pagedCount搭配使用会产生分页从左到右滚动的效果。 + flowLayout.myWidth = MyLayoutSize.wrap; //设置布局视图的宽度由子视图包裹,当水平流式布局设置宽度自适应,并和pagedCount搭配使用会产生分页从左到右滚动的效果。 flowLayout.heightSize.equalTo(scrollView.heightSize); //因为是分页从左到右滚动,因此布局视图的高度必须设置为和父滚动视图相等。 /* 上面是实现一个水平流式布局分页且从左往右滚动的标准属性设置方法。 @@ -140,12 +148,12 @@ -(void)createHorzPagingFlowLayout2:(UIView*)rootLayout scrollView.pagingEnabled = YES; //开启分页滚动模式!!您可以注释这句话看看非分页滚动的布局滚动效果。 scrollView.myHeight = 250; //设置明确的高度为250,因为宽度已经由父线性布局的gravity属性确定了,所以不需要设置了。 [rootLayout addSubview:scrollView]; - + self.scrollView2 = scrollView; //建立一个水平数量约束流式布局:每列展示3个子视图,每页展示9个子视图,整体从上往下滚动。 MyFlowLayout *flowLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:3]; flowLayout.pagedCount = 9; //pagedCount设置为非0时表示开始分页展示的功能,这里表示每页展示9个子视图,这个数量必须是arrangedCount的倍数。 - flowLayout.wrapContentHeight = YES; //设置布局视图的高度由子视图包裹,当水平流式布局的这个属性设置为YES,并和pagedCount搭配使用会产生分页从上到下滚动的效果。 + flowLayout.myHeight = MyLayoutSize.wrap; //设置布局视图的高度由子视图包裹,当水平流式布局的高度自适应,并和pagedCount搭配使用会产生分页从上到下滚动的效果。 flowLayout.widthSize.equalTo(scrollView.widthSize); //因为是分页从左到右滚动,因此布局视图的宽度必须设置为和父滚动视图相等。 /* 上面是实现一个水平流式布局分页且从上往下滚动的标准属性设置方法。 @@ -182,11 +190,12 @@ -(void)createVertPagingFlowLayout1:(UIView*)rootLayout scrollView.pagingEnabled = YES; //开启分页滚动模式!!您可以注释这句话看看非分页滚动的布局滚动效果。 scrollView.myHeight = 250; //设置明确的高度为250,因为宽度已经由父线性布局的gravity属性确定了,所以不需要设置了。 [rootLayout addSubview:scrollView]; - + self.scrollView3 = scrollView; + //建立一个垂直数量约束流式布局:每列展示3个子视图,每页展示9个子视图,整体从上往下滚动。 MyFlowLayout *flowLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3]; flowLayout.pagedCount = 9; //pagedCount设置为非0时表示开始分页展示的功能,这里表示每页展示9个子视图,这个数量必须是arrangedCount的倍数。 - flowLayout.wrapContentHeight = YES; //设置布局视图的高度由子视图包裹,当垂直流式布局的这个属性设置为YES,并和pagedCount搭配使用会产生分页从上到下滚动的效果。 + flowLayout.myHeight = MyLayoutSize.wrap; //设置布局视图的高度由子视图包裹,当垂直流式布局的高度自适应,并和pagedCount搭配使用会产生分页从上到下滚动的效果。 flowLayout.widthSize.equalTo(scrollView.widthSize); /* 上面是实现一个垂直流式布局分页且从上往下滚动的标准属性设置方法。 @@ -226,11 +235,12 @@ -(void)createVertPagingFlowLayout2:(UIView*)rootLayout scrollView.pagingEnabled = YES; //开启分页滚动模式!!您可以注释这句话看看非分页滚动的布局滚动效果。 scrollView.myHeight = 200; //设置明确的高度为200,因为宽度已经由父线性布局的gravity属性确定了,所以不需要设置了。 [rootLayout addSubview:scrollView]; + self.scrollView4 = scrollView; //建立一个垂直数量约束流式布局:每列展示3个子视图,每页展示9个子视图,整体从左往右滚动。 MyFlowLayout *flowLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3]; flowLayout.pagedCount = 9; //pagedCount设置为非0时表示开始分页展示的功能,这里表示每页展示9个子视图,这个数量必须是arrangedCount的倍数。 - flowLayout.wrapContentWidth = YES; //设置布局视图的宽度由子视图包裹,当垂直流式布局的这个属性设置为YES,并和pagedCount搭配使用会产生分页从左到右滚动的效果。 + flowLayout.myWidth = MyLayoutSize.wrap; //设置布局视图的宽度由子视图包裹,当垂直流式布局的宽度自适应,并和pagedCount搭配使用会产生分页从左到右滚动的效果。 flowLayout.heightSize.equalTo(scrollView.heightSize); /* 上面是实现一个垂直流式布局分页且从左往右滚动的标准属性设置方法。 @@ -252,9 +262,75 @@ -(void)createVertPagingFlowLayout2:(UIView*)rootLayout } +/** + * 创建一个垂直分页从左向右滚动的流式布局,高度自适应。 + */ +-(void)createVertPagingFlowLayout3:(UIView*)rootLayout +{ + UILabel *titleLabel = [UILabel new]; + titleLabel.text = @"垂直流式布局分页从左往右滚动(高度自适应):➡︎"; + [titleLabel sizeToFit]; + [rootLayout addSubview:titleLabel]; + titleLabel.myTop = 20; + + //要开启分页功能,必须要将流式布局加入到一个滚动视图里面作为子视图!!! + UIScrollView *scrollView = [UIScrollView new]; + scrollView.pagingEnabled = YES; //开启分页滚动模式!!您可以注释这句话看看非分页滚动的布局滚动效果。 + scrollView.myHeight = MyLayoutSize.wrap; //高度自适应 + [rootLayout addSubview:scrollView]; + self.scrollView4 = scrollView; + + //建立一个垂直数量约束流式布局:每列展示3个子视图,每页展示9个子视图,整体从左往右滚动。 + MyFlowLayout *flowLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3]; + flowLayout.pagedCount = 9; //pagedCount设置为非0时表示开始分页展示的功能,这里表示每页展示9个子视图,这个数量必须是arrangedCount的倍数。 + + //设置流式布局的高度和宽度都是自适应。 + flowLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + /* + 上面是实现一个垂直流式布局分页且从左往右滚动的标准属性设置方法。 + */ + + + flowLayout.subviewHSpace = 10; + flowLayout.subviewVSpace = 10; //设置子视图的水平和垂直间距。 + flowLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); //布局视图的内边距设置!您可以注释掉这句话看看效果!如果设置内边距且也有分页时请将这个值设置和子视图间距相等。 + [scrollView addSubview:flowLayout]; + flowLayout.backgroundColor = [CFTool color:0]; + + [self addAllItemSubviews:flowLayout]; + + //获取流式布局的横屏size classes,并且设置设备处于横屏时,每排数量由3个变为6个,每页的数量由9个变为18个。您可以注释掉这段代码,然后横竖屏切换看看效果。 + MyFlowLayout *flowLayoutSC = [flowLayout fetchLayoutSizeClass:MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny]; + flowLayoutSC.arrangedCount = 6; + flowLayoutSC.pagedCount = 18; +} + #pragma mark -- Handle Method +-(void)handlePagingSwitch:(UIBarButtonItem*)sender +{ + if ([sender.title isEqualToString:@"paging"]) + { + sender.title = @"scroll"; + } + else + { + self.title = @"paging"; + } + + self.scrollView1.pagingEnabled = !self.scrollView1.pagingEnabled; + self.scrollView2.pagingEnabled = !self.scrollView2.pagingEnabled; + self.scrollView3.pagingEnabled = !self.scrollView3.pagingEnabled; + self.scrollView4.pagingEnabled = !self.scrollView4.pagingEnabled; + + [self.scrollView1.subviews.firstObject setNeedsLayout]; + [self.scrollView2.subviews.firstObject setNeedsLayout]; + [self.scrollView3.subviews.firstObject setNeedsLayout]; + [self.scrollView4.subviews.firstObject setNeedsLayout]; + +} + /* #pragma mark - Navigation diff --git a/MyLayoutDemo/FLLTest6ViewController.m b/MyLayoutDemo/FLLTest6ViewController.m index 3b5332b..498a432 100644 --- a/MyLayoutDemo/FLLTest6ViewController.m +++ b/MyLayoutDemo/FLLTest6ViewController.m @@ -33,7 +33,7 @@ -(void)loadView MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3]; rootLayout.backgroundColor = [CFTool color:0]; rootLayout.pagedCount = 9; - rootLayout.wrapContentHeight = YES; //上下滚动,每页9个。 + rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); //上下滚动,每页9个。 rootLayout.subviewSpace = 10; rootLayout.padding = UIEdgeInsetsMake(10, 5, 10, 5); rootLayout.leftPos.equalTo(@0).active = YES; //active属性用来表示是否让这个属性设置生效。 @@ -71,7 +71,7 @@ -(IBAction)handleTap:(UIButton*)sender self.rootLayout.bottomPos.active = !self.rootLayout.bottomPos.isActive; //当前是多行多列。 - if (self.rootLayout.wrapContentHeight) + if (self.rootLayout.heightSize.isWrap) { //换成单行单列 self.rootLayout.arrangedCount = 1; @@ -91,12 +91,11 @@ -(IBAction)handleTap:(UIButton*)sender } //这里切换水平滚动还是垂直滚动。 - self.rootLayout.wrapContentHeight = !self.rootLayout.wrapContentHeight; - self.rootLayout.wrapContentWidth = !self.rootLayout.wrapContentWidth; + self.rootLayout.heightSize.equalTo(self.rootLayout.heightSize.isWrap? nil : @(MyLayoutSize.wrap)); + self.rootLayout.widthSize.equalTo(self.rootLayout.widthSize.isWrap? nil : @(MyLayoutSize.wrap)); - - BOOL isHorzScroll = self.rootLayout.wrapContentWidth; + BOOL isHorzScroll = self.rootLayout.widthSize.isWrap; [UIView animateWithDuration:0.3 animations:^{ [self.rootLayout layoutIfNeeded]; //上面因为进行布局属性的设置变更,必定会激发重新布局,因此如果想要应用动画时可以在动画块内调用layoutIfNeeded来实现 diff --git a/MyLayoutDemo/FLLTest7ViewController.h b/MyLayoutDemo/FLLTest7ViewController.h new file mode 100644 index 0000000..44ef75f --- /dev/null +++ b/MyLayoutDemo/FLLTest7ViewController.h @@ -0,0 +1,16 @@ +// +// FLLTest7ViewController.h +// MyLayout +// +// Created by apple on 17/2/20. +// Copyright © 2017年 YoungSoft. All rights reserved. +// + +#import + +/** + *6.FlowLayout - Auto Arrange + */ +@interface FLLTest7ViewController : UIViewController + +@end diff --git a/MyLayoutDemo/FLLTest7ViewController.m b/MyLayoutDemo/FLLTest7ViewController.m new file mode 100644 index 0000000..5660d4a --- /dev/null +++ b/MyLayoutDemo/FLLTest7ViewController.m @@ -0,0 +1,144 @@ +// +// FLLTest7ViewController.m +// MyLayout +// +// Created by apple on 17/2/20. +// Copyright © 2017年 YoungSoft. All rights reserved. +// + +#import "FLLTest7ViewController.h" +#import "MyLayout.h" +#import "CFTool.h" + + +@implementation FLLTest7ViewController + + +-(void)createItems:(NSArray*)titles inFlowLayout:(MyFlowLayout*)flowLayout +{ + for (NSString *title in titles) + { + UILabel *label = [UILabel new]; + label.text = title; + label.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + label.backgroundColor = [CFTool color:(random()%14 + 1)]; + label.font = [CFTool font:16]; + [flowLayout addSubview:label]; + } +} + +-(void)loadView +{ + /* + 这个例子主要用来介绍数量约束流式布局的自动排列和紧凑排列的能力,目的是为了实现类似于瀑布流的功能,下面的代码您将能看到水平和垂直两种应用场景。 + 每种应用场景中,我们通过设置autoArrange为YES和arrangedGravity属性为MyGravity_Horz_Between或者MyGravity_Vert_Between来分别实现两种不同的 + 排列策略: + autoArrange: 的策略是让总体的空间达到最高效的利用,但是他会打乱视图添加的顺序。 + arrangedGravity: 的策略则不会打乱视图的添加顺序,因此他的总体空间的利用率可能会不如autoArrange那么高。 + */ + + /* + 需要注意的是,当您的数据量比较小时,我们可以考虑使用流式布局来实现瀑布流,而当你的数据量比较大,并且需要考虑复用,那么为了内存上的考虑建议您还是使用tableView或者collectionView来实现。 + */ + + NSArray *titles = @[@"11111111111111111", @"222222222222222222222222222",@"3333333333333", @"4444444444444444444444", @"55555555", @"6666666666666", @"77777777", @"8888888888888888888888", @"99"]; + + + + self.edgesForExtendedLayout = UIRectEdgeNone; //设置视图控制器中的视图尺寸不延伸到导航条或者工具条下面。您可以注释这句代码看看效果。 + + MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + rootLayout.gravity = MyGravity_Horz_Fill; + rootLayout.subviewSpace = 20; + rootLayout.backgroundColor = [UIColor whiteColor]; + self.view = rootLayout; + + //水平瀑布流1。 + UIScrollView *scrollView1 = [UIScrollView new]; + scrollView1.myHeight = MyLayoutSize.wrap; //这里可以设置滚动视图的高度为包裹属性,表示他的高度依赖于布局视图的高度。 + [rootLayout addSubview:scrollView1]; + + MyFlowLayout *flowLayout1 = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:3]; + flowLayout1.backgroundColor = [CFTool color:5]; + //流式布局的尺寸由里面的子视图的整体尺寸决定。 + flowLayout1.heightSize.equalTo(@(MyLayoutSize.wrap)); + flowLayout1.widthSize.equalTo(@(MyLayoutSize.wrap)).lBound(scrollView1.widthSize, 0, 1); //虽然尺寸是包裹的,但是最小宽度不能小于父视图的宽度 + flowLayout1.autoArrange = YES; //通过将流式布局的autoArrange属性设置为YES可以实现里面的子视图进行紧凑的自动排列。 + flowLayout1.subviewSpace = 10; + flowLayout1.padding = UIEdgeInsetsMake(10, 10, 10, 10); + [scrollView1 addSubview:flowLayout1]; + [self createItems:titles inFlowLayout:flowLayout1]; + + + //水平瀑布流2。 + UIScrollView *scrollView2 = [UIScrollView new]; + scrollView2.myHeight = MyLayoutSize.wrap; + [rootLayout addSubview:scrollView2]; + + MyFlowLayout *flowLayout2 = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:3]; + flowLayout2.backgroundColor = [CFTool color:6]; + flowLayout2.heightSize.equalTo(@(MyLayoutSize.wrap)); + flowLayout2.widthSize.equalTo(@(MyLayoutSize.wrap)).lBound(scrollView2.widthSize, 0, 1); //虽然尺寸是包裹的,但是最小宽度不能小于父视图的宽度 + flowLayout2.subviewSpace = 10; + flowLayout2.padding = UIEdgeInsetsMake(10, 10, 10, 10); + flowLayout2.arrangedGravity = MyGravity_Horz_Between; //通过将水平流式布局的arrangeGravity属性设置为MyGravity_Horz_Between,我们将得到里面的子视图在每行都会被紧凑的排列。大家可以看到和上面的将autoArrange设置为YES的不同的效果。 + [scrollView2 addSubview:flowLayout2]; + [self createItems:titles inFlowLayout:flowLayout2]; + + + //垂直瀑布流1 + UIScrollView *scrollView3 = [UIScrollView new]; + scrollView3.weight = 0.5; //占用父布局剩余的高度的一半 + [rootLayout addSubview:scrollView3]; + + MyFlowLayout *flowLayout3 = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3]; + flowLayout3.backgroundColor = [CFTool color:5]; + flowLayout3.heightSize.equalTo(@(MyLayoutSize.wrap)).lBound(scrollView3.heightSize, 0, 1); //虽然是包裹尺寸,但是最小不能小于父视图的高度。 + flowLayout3.myHorzMargin = 0; + flowLayout3.gravity = MyGravity_Horz_Fill; //均分宽度。 + flowLayout3.autoArrange = YES; //通过将流式布局的autoArrange属性设置为YES可以实现里面的子视图进行紧凑的自动排列。 + flowLayout3.subviewSpace = 10; + flowLayout3.padding = UIEdgeInsetsMake(10, 10, 10, 10); + [scrollView3 addSubview:flowLayout3]; + [self createItems:titles inFlowLayout:flowLayout3]; + + + //垂直瀑布流2 + UIScrollView *scrollView4 = [UIScrollView new]; + scrollView4.weight = 0.5; //占用父布局剩余的高度的一半 + [rootLayout addSubview:scrollView4]; + + MyFlowLayout *flowLayout4 = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3]; + flowLayout4.backgroundColor = [CFTool color:6]; + flowLayout4.heightSize.equalTo(@(MyLayoutSize.wrap)).lBound(scrollView4.heightSize, 0, 1); + flowLayout4.myHorzMargin = 0; + flowLayout4.gravity = MyGravity_Horz_Fill; //均分宽度。 + flowLayout4.arrangedGravity = MyGravity_Vert_Between; //通过将垂直流式布局的arrangeGravity属性设置为MyGravity_Vert_Between,我们将得到里面的子视图在每列都会被紧凑的排列。大家可以看到和上面的将autoArrange设置为YES的不同的效果。 + flowLayout4.subviewSpace = 10; + flowLayout4.padding = UIEdgeInsetsMake(10, 10, 10, 10); + [scrollView4 addSubview:flowLayout4]; + [self createItems:titles inFlowLayout:flowLayout4]; + +} + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +@end diff --git a/MyLayoutDemo/FLLTest8ViewController.h b/MyLayoutDemo/FLLTest8ViewController.h new file mode 100644 index 0000000..c689109 --- /dev/null +++ b/MyLayoutDemo/FLLTest8ViewController.h @@ -0,0 +1,14 @@ +// +// FLLTest8ViewController.h +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import + +/*8.FlowLayout - Flex space*/ +@interface FLLTest8ViewController : UIViewController + +@end diff --git a/MyLayoutDemo/FLLTest8ViewController.m b/MyLayoutDemo/FLLTest8ViewController.m new file mode 100644 index 0000000..e56bc01 --- /dev/null +++ b/MyLayoutDemo/FLLTest8ViewController.m @@ -0,0 +1,192 @@ +// +// FLLTest8ViewController.m +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import "FLLTest8ViewController.h" +#import "MyLayout.h" +#import "CFTool.h" + +@interface FLLTest8ViewController () + +@property(nonatomic, strong) MyFlowLayout *vertLayout; +@property(nonatomic, strong) MyFlowLayout *horzLayout; +@property(nonatomic, strong) MyFlowLayout *vertLayout2; +@property(nonatomic, strong) MyFlowLayout *horzLayout2; + + +@end + +@implementation FLLTest8ViewController + +-(void)loadView +{ + /* + 这个例子主要用于展示流式布局的间距的可伸缩性。当我们布局视图中的子视图的宽度或者高度为固定值,但是其中的间距又希望是可以伸缩的,那么就可以借助流式布局提供的方法 + + -(void)setSubviewsSize:(CGFloat)subviewSize minSpace:(CGFloat)minSpace maxSpace:(CGFloat)maxSpace centered:(BOOL)centered + + 来设置子视图的尺寸,以及最小和最大的间距值。并且当布局视图中子视图的间距小于最小值时会自动调整子视图的尺寸来满足这个最小的间距。 + + 对于垂直流式布局来说,这个方法只用于设置子视图的宽度和水平间距。 + 对于水平流式布局来说,这个方法只用于设置子视图的高度和垂直间距。 + + + 你可以在这个界面中尝试一下屏幕的旋转,看看布局为了支持横屏和竖屏而进行的布局调整,以便达到最完美的布局效果。 + + */ + + self.edgesForExtendedLayout = UIRectEdgeNone; //设置视图控制器中的视图尺寸不延伸到导航条或者工具条下面。您可以注释这句代码看看效果。 + + //这里的根视图是一个每列只有一个子视图的垂直流式布局,效果就相当于垂直线性布局了。 + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:1]; + rootLayout.gravity = MyGravity_Fill; //这里将gravity设置为MyGravity_Fill表明子视图的宽度和高度都将平分这个流式布局,可见一个简单的属性设置就可以很容易的实现子视图的尺寸的设置,而不需要编写太复杂的约束。 + rootLayout.subviewSpace = 10; + rootLayout.backgroundColor = [UIColor whiteColor]; + self.view = rootLayout; + + + MyFlowLayout *vertLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:4]; + vertLayout.padding = UIEdgeInsetsMake(5, 0, 5, 0); + vertLayout.backgroundColor = [CFTool color:5]; + vertLayout.subviewVSpace = 20; + vertLayout.gravity = MyGravity_Vert_Fill; //因为上面setSubviewsSize设置了固定宽度,这个属性设置子视图的高度是填充满子布局视图,因此系统内部会自动设置每个子视图的高度,如果你不设置这个属性,那么你就需要在下面分别为每个子视图设置高度。 + [rootLayout addSubview:vertLayout]; + self.vertLayout = vertLayout; + + for (int i = 0; i < 14; i++) + { + UILabel *label = [UILabel new]; + //label.myHeight = 60; 因为子视图的宽度在布局视图的setSubviewsSize:minSpace:maxSpace:中设置了,你也可以在这里单独为每个子视图设置高度,当然如果你的父布局视图使用了gravity来设置填充属性的话,那么子视图是不需要单独设置高度尺寸的。 + label.text = [NSString stringWithFormat:@"%d", i]; + label.textAlignment = NSTextAlignmentCenter; + label.backgroundColor = [CFTool color:2]; + [vertLayout addSubview:label]; + } + + + + MyFlowLayout *horzLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:4]; + + horzLayout.padding = UIEdgeInsetsMake(0, 5, 0, 5); + horzLayout.backgroundColor = [CFTool color:6]; + horzLayout.subviewHSpace = 20; + horzLayout.gravity = MyGravity_Horz_Fill; //因为上面setSubviewsSize设置了固定高度,这个属性设置子视图的宽度是填充满子布局视图,因此系统内部会自动设置每个子视图的宽度,如果你不设置这个属性,那么你就需要在下面分别为每个子视图设置宽度。 + [rootLayout addSubview:horzLayout]; + self.horzLayout = horzLayout; + + for (int i = 0; i < 14; i++) + { + UILabel *label = [UILabel new]; + //label.myWidth = 60; 因为子视图的高度在布局视图的setSubviewsSize:minSpace:maxSpace:中设置了,你也可以在这里单独为每个子视图设置宽度,当然如果你的父布局视图使用了gravity来设置填充属性的话,那么子视图是不需要单独设置宽度尺寸的。 + label.text = [NSString stringWithFormat:@"%d", i]; + label.textAlignment = NSTextAlignmentCenter; + label.backgroundColor = [CFTool color:3];; + [horzLayout addSubview:label]; + } + + + MyFlowLayout *vertLayout2 = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:0]; + vertLayout2.padding = UIEdgeInsetsMake(5, 0, 5, 0); + vertLayout2.backgroundColor = [CFTool color:5]; + vertLayout2.subviewVSpace = 20; + vertLayout2.gravity = MyGravity_Vert_Fill; //因为上面setSubviewsSize设置了固定宽度,这个属性设置子视图的高度是填充满子布局视图,因此系统内部会自动设置每个子视图的高度,如果你不设置这个属性,那么你就需要在下面分别为每个子视图设置高度。 + [rootLayout addSubview:vertLayout2]; + self.vertLayout2 = vertLayout2; + + for (int i = 0; i < 14; i++) + { + UILabel *label = [UILabel new]; + //label.myHeight = 60; 因为子视图的宽度在布局视图的setSubviewsSize:minSpace:maxSpace:中设置了,你也可以在这里单独为每个子视图设置高度,当然如果你的父布局视图使用了gravity来设置填充属性的话,那么子视图是不需要单独设置高度尺寸的。 + label.text = [NSString stringWithFormat:@"%d", i]; + label.textAlignment = NSTextAlignmentCenter; + label.backgroundColor = [CFTool color:2]; + [vertLayout2 addSubview:label]; + } + + + + MyFlowLayout *horzLayout2 = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:0]; + + horzLayout2.padding = UIEdgeInsetsMake(0, 5, 0, 5); + horzLayout2.backgroundColor = [CFTool color:6]; + horzLayout2.subviewHSpace = 20; + horzLayout2.gravity = MyGravity_Horz_Fill; //因为上面setSubviewsSize设置了固定高度,这个属性设置子视图的宽度是填充满子布局视图,因此系统内部会自动设置每个子视图的宽度,如果你不设置这个属性,那么你就需要在下面分别为每个子视图设置宽度。 + [rootLayout addSubview:horzLayout2]; + self.horzLayout2 = horzLayout2; + + for (int i = 0; i < 14; i++) + { + UILabel *label = [UILabel new]; + //label.myWidth = 60; 因为子视图的高度在布局视图的setSubviewsSize:minSpace:maxSpace:中设置了,你也可以在这里单独为每个子视图设置宽度,当然如果你的父布局视图使用了gravity来设置填充属性的话,那么子视图是不需要单独设置宽度尺寸的。 + label.text = [NSString stringWithFormat:@"%d", i]; + label.textAlignment = NSTextAlignmentCenter; + label.backgroundColor = [CFTool color:3];; + [horzLayout2 addSubview:label]; + } + + + //这个垂直流式布局中,每个子视图之间的水平间距是浮动的,并且子视图的宽度是固定为60。间距最小为10,最大不限制。 + [vertLayout setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:NO]; + + //这个水平流式布局中,每个子视图之间的垂直间距是浮动的,并且子视图的高度是固定为60。间距最小为10,最大不限制。 + [horzLayout setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:NO]; + + //这个垂直流式布局中,每个子视图之间的水平间距是浮动的,并且子视图的宽度是固定为60。间距最小为10,最大不限制。 + [vertLayout2 setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:NO]; + + //这个水平流式布局中,每个子视图之间的垂直间距是浮动的,并且子视图的高度是固定为60。间距最小为10,最大不限制。 + [horzLayout2 setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:NO]; + +} + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. + + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"center off" style:UIBarButtonItemStylePlain target:self action:@selector(handleCenterOnOff:)]; + +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +-(IBAction)handleCenterOnOff:(UIBarButtonItem*)sender { + + if ([sender.title isEqualToString:@"center off"]) { + sender.title = @"center on"; + [self.vertLayout setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:YES]; + [self.horzLayout setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:YES]; + [self.vertLayout2 setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:YES]; + [self.horzLayout2 setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:YES]; + + } else { + sender.title = @"center off"; + [self.vertLayout setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:NO]; + [self.horzLayout setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:NO]; + [self.vertLayout2 setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:NO]; + [self.horzLayout2 setSubviewsSize:60 minSpace:10 maxSpace:CGFLOAT_MAX centered:NO]; + } + + [self.vertLayout layoutAnimationWithDuration:0.3]; + [self.horzLayout layoutAnimationWithDuration:0.3]; + [self.vertLayout2 layoutAnimationWithDuration:0.3]; + [self.horzLayout2 layoutAnimationWithDuration:0.3]; +} + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +@end diff --git a/MyLayoutDemo/FLLTest9ViewController.h b/MyLayoutDemo/FLLTest9ViewController.h new file mode 100644 index 0000000..5582105 --- /dev/null +++ b/MyLayoutDemo/FLLTest9ViewController.h @@ -0,0 +1,14 @@ +// +// FLLTest9ViewController.h +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import + +/*9.FlowLayout - line gravity*/ +@interface FLLTest9ViewController : UIViewController + +@end diff --git a/MyLayoutDemo/FLLTest9ViewController.m b/MyLayoutDemo/FLLTest9ViewController.m new file mode 100644 index 0000000..3d5357a --- /dev/null +++ b/MyLayoutDemo/FLLTest9ViewController.m @@ -0,0 +1,200 @@ +// +// FLLTest9ViewController.m +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import "FLLTest9ViewController.h" +#import "MyLayout.h" +#import "CFTool.h" + +@interface FLLTest9ViewController () + +@property(nonatomic, strong) MyFlowLayout *vertContentLayout; +@property(nonatomic, strong) MyFlowLayout *horzContentLayout; + +@end + +@implementation FLLTest9ViewController + +-(void)loadView +{ + /* + 这个例子主要演示流式布局中进行行内的自定义停靠对齐属性lineGravity的使用方法。 + 在垂直流式布局中,我们可以通过gravity来设置每行的水平停靠对齐特性,并通过arrangedGravity来设置每行行内的垂直停靠对齐特性。 + 在水平流式布局中,我们可以通过gravity来设置每列的垂直停靠对齐特性,并通过arrangedGravity来设置每列列内的水平停靠对齐特性。 + + 而如果我们想自定义某行或者某列的水平和垂直停靠对齐特性时则可以通过lineGravity来实现,lineGravity是一个block方法。这个方法的入参有布局对象、行的索引、行内条目的数量、是否是最后一行标志。而方法的返回则是这一行内的水平和垂直停靠对齐特性。如果某个方向返回MyGravity_None则表明用布局指定的gravity和arrangedGravity设置的值。 + + 比如在一个垂直流式布局中所有行都是右停靠,并且每行内都是垂直居中对齐。但是我们又想将其中的第1行设置为左停靠,并且是底部对齐。那么我们就可以进行如下设置: + layout.gravity = MyGravity_Horz_Right; //整体右停靠 + layout.arrangedGravity = MyGravity_Vert_Center; //每行都是垂直居中对齐 + layout.lineGravity = ^(MyFlowLayout *layout, NSInteger lineIndex, NSInteger itemCount, BOOL isLastLine) + { + if (lineIndex == 1) + return MyGravity_Horz_Left | MyGravity_Vert_Bottom; //第一行左边停靠并且底部对齐 + else + return MyGravity_None; + }; + + */ + + self.edgesForExtendedLayout = UIRectEdgeNone; //设置视图控制器中的视图尺寸不延伸到导航条或者工具条下面。您可以注释这句代码看看效果。 + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:0]; + rootLayout.isFlex = YES; //这个属性设置为YES表明让流式布局兼容flexbox的一些特性。 + rootLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); + rootLayout.subviewSpace = 5; + self.view = rootLayout; + + [self createVertContentLayout:rootLayout]; + + [self createHorzContentLayout:rootLayout]; + +} + +-(void)createVertContentLayout:(MyFlowLayout*)rootLayout +{ + UIButton *vertLayoutItemAddButon = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [vertLayoutItemAddButon addTarget:self action:@selector(handleVertLayoutItemAdd:) forControlEvents:UIControlEventTouchUpInside]; + [vertLayoutItemAddButon setTitle:@"Add" forState:UIControlStateNormal]; + [rootLayout addSubview:vertLayoutItemAddButon]; + vertLayoutItemAddButon.weight = 1.0; //两个按钮的比重为1表明平分宽度。 + vertLayoutItemAddButon.heightSize.equalTo(@(40)); + + UIButton *vertLayoutItemRemoveButon = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [vertLayoutItemRemoveButon addTarget:self action:@selector(handleVertLayoutItemRemove:) forControlEvents:UIControlEventTouchUpInside]; + [vertLayoutItemRemoveButon setTitle:@"Remove" forState:UIControlStateNormal]; + [rootLayout addSubview:vertLayoutItemRemoveButon]; + vertLayoutItemRemoveButon.weight = 1.0; + vertLayoutItemRemoveButon.heightSize.equalTo(@(40)); + + MyFlowLayout *vertContentLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:1]; + [rootLayout addSubview:vertContentLayout]; + vertContentLayout.widthSize.equalTo(rootLayout.widthSize); + vertContentLayout.heightSize.equalTo(vertContentLayout.widthSize).multiply(3.0/5); + vertContentLayout.gravity = MyGravity_Horz_Fill | MyGravity_Vert_Center; //整体水平填充和垂直居中 + //单独设置行内的停靠方向。 + vertContentLayout.lineGravity = ^MyGravity(MyFlowLayout *layout, NSInteger lineIndex, NSInteger itemCount, BOOL isLastLine) { + + //只有当布局视图的子视图数量为3个,并且是最后一行时才水平居中,否则其他返回默认的停靠值 + if (layout.subviews.count == 3 && isLastLine) + return MyGravity_Horz_Center; + else + return MyGravity_None; + }; + + self.vertContentLayout = vertContentLayout; + self.vertContentLayout.backgroundColor = [UIColor lightGrayColor]; +} + +-(void)createHorzContentLayout:(MyFlowLayout*)rootLayout +{ + UIButton *horzLayoutItemAddButon = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [horzLayoutItemAddButon addTarget:self action:@selector(handleHorzLayoutItemAdd:) forControlEvents:UIControlEventTouchUpInside]; + [horzLayoutItemAddButon setTitle:@"Add" forState:UIControlStateNormal]; + [rootLayout addSubview:horzLayoutItemAddButon]; + horzLayoutItemAddButon.weight = 1.0; + horzLayoutItemAddButon.heightSize.equalTo(@(40)); + + UIButton *horzLayoutItemRemoveButon = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [horzLayoutItemRemoveButon addTarget:self action:@selector(handleHorzLayoutItemRemove:) forControlEvents:UIControlEventTouchUpInside]; + [horzLayoutItemRemoveButon setTitle:@"Remove" forState:UIControlStateNormal]; + [rootLayout addSubview:horzLayoutItemRemoveButon]; + horzLayoutItemRemoveButon.weight = 1.0; + horzLayoutItemRemoveButon.heightSize.equalTo(@(40)); + + MyFlowLayout *horzContentLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:1]; + [rootLayout addSubview:horzContentLayout]; + horzContentLayout.widthSize.equalTo(rootLayout.widthSize); + horzContentLayout.heightSize.equalTo(horzContentLayout.widthSize).multiply(3.0/5); + horzContentLayout.gravity = MyGravity_Vert_Fill | MyGravity_Horz_Center; + horzContentLayout.lineGravity = ^MyGravity(MyFlowLayout *layout, NSInteger lineIndex, NSInteger itemCount, BOOL isLastLine) { + + //只有当布局视图的子视图数量为3个,并且是最后一行时才水平居中,否则其他返回默认的停靠值 + if (layout.subviews.count == 3 && isLastLine) + return MyGravity_Vert_Center; + else + return MyGravity_None; + }; + + self.horzContentLayout = horzContentLayout; + self.horzContentLayout.backgroundColor = [UIColor lightGrayColor]; +} + + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +#pragma mark -- Handler + +-(void)handleVertLayoutItemAdd:(id)sender +{ + //这里子视图不需要设置任何宽高约束。 + UILabel *itemView = [UILabel new]; + itemView.backgroundColor = [CFTool color:arc4random_uniform(14) + 1]; + itemView.text = [NSString stringWithFormat:@"%ld", self.vertContentLayout.subviews.count]; + itemView.textAlignment = NSTextAlignmentCenter; + [self.vertContentLayout addSubview:itemView]; + + + //1个就1,小于等于4个就2, 小于等于9个就3, 小于等于16个就4 + //也就是每行的数量是大于子视图数量开根的最小整数。 + self.vertContentLayout.arrangedCount = ceil(sqrt(self.vertContentLayout.subviews.count)); + //这里启用分页功能,这样子视图的高度将会被自动计算出来。 + self.vertContentLayout.pagedCount = self.vertContentLayout.arrangedCount * self.vertContentLayout.arrangedCount; +} + +-(void)handleVertLayoutItemRemove:(id)sender +{ + [self.vertContentLayout.subviews.lastObject removeFromSuperview]; + //1个就1,小于等于4个就2, 小于等于9个就3, 小于等于16个就4 + //也就是每行的数量是大于子视图数量开根的最小整数。 + self.vertContentLayout.arrangedCount = ceil(sqrt(self.vertContentLayout.subviews.count)); + self.vertContentLayout.pagedCount = self.vertContentLayout.arrangedCount * self.vertContentLayout.arrangedCount; +} + +-(void)handleHorzLayoutItemAdd:(id)sender +{ + UILabel *itemView = [UILabel new]; + itemView.backgroundColor = [CFTool color:arc4random_uniform(14) + 1]; + itemView.text = [NSString stringWithFormat:@"%ld", self.horzContentLayout.subviews.count]; + itemView.textAlignment = NSTextAlignmentCenter; + [self.horzContentLayout addSubview:itemView]; + + + //1个就1,小于等于4个就2, 小于等于9个就3, 小于等于16个就4 + //也就是每行的数量是大于子视图数量开根的最小整数。 + self.horzContentLayout.arrangedCount = ceil(sqrt(self.horzContentLayout.subviews.count)); + //这里启用分页功能,这样子视图的高度将会被自动计算出来。 + self.horzContentLayout.pagedCount = self.horzContentLayout.arrangedCount * self.horzContentLayout.arrangedCount; +} + +-(void)handleHorzLayoutItemRemove:(id)sender +{ + [self.horzContentLayout.subviews.lastObject removeFromSuperview]; + //1个就1,小于等于4个就2, 小于等于9个就3, 小于等于16个就4 + //也就是每行的数量是大于子视图数量开根的最小整数。 + self.horzContentLayout.arrangedCount = ceil(sqrt(self.horzContentLayout.subviews.count)); + self.horzContentLayout.pagedCount = self.horzContentLayout.arrangedCount * self.horzContentLayout.arrangedCount; +} + +@end diff --git a/MyLayoutDemo/FLXTest1ViewController.h b/MyLayoutDemo/FLXTest1ViewController.h new file mode 100644 index 0000000..c5bc616 --- /dev/null +++ b/MyLayoutDemo/FLXTest1ViewController.h @@ -0,0 +1,14 @@ +// +// FLXTest1ViewController.h +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import + +/*1.FlexLayout - flex box*/ +@interface FLXTest1ViewController : UIViewController + +@end diff --git a/MyLayoutDemo/FLXTest1ViewController.m b/MyLayoutDemo/FLXTest1ViewController.m new file mode 100644 index 0000000..b7399e9 --- /dev/null +++ b/MyLayoutDemo/FLXTest1ViewController.m @@ -0,0 +1,1016 @@ +// +// FLXTest1ViewController.m +// MyLayoutDemo +// +// Created by oubaiquan on 2018/8/1. +// Copyright © 2018年 YoungSoft. All rights reserved. +// + +#import "FLXTest1ViewController.h" +#import "MyLayout.h" +#import "CFTool.h" + +@interface FLXTest1ViewController () + +@property(nonatomic, strong) UISegmentedControl *flex_directionSeg; +@property(nonatomic, strong) UISegmentedControl *flex_wrapSeg; +@property(nonatomic, strong) UISegmentedControl *justify_contentSeg; +@property(nonatomic, strong) UISegmentedControl *align_itemsSeg; +@property(nonatomic, strong) UISegmentedControl *align_contentSeg; + +@property(nonatomic, strong) UILabel *flexlayoutStyleDescLabel; + +@property(nonatomic, strong) MyFlexLayout *contentLayout; + + +@end + +@implementation FLXTest1ViewController + +-(void)loadView +{ + /* + 我们为MyFlexLayout建立了一套专有的语法糖,用来构建布局。具体可以参考MyFlexLayout.h中的MyFlexBox和MyFlexItem两个接口的定义和说明,前者用于弹性盒布局对象的布局属性设置,后者用于条目对象的布局属性设置。 + + 我们可以借助UIView的myFlex扩展属性来获取语法糖布局对象。对于MyFlexBox和MyFlexItem的语法糖使用,又可以分为用attrs属性的方式进行单独赋值, + 也可以采用链式调用的语法来进行布局属性设置。 下面的方法就是使用语法糖的方式来构建弹性盒布局的代码: + + */ + + //弹性盒布局构建布局的语法糖方式1: + MyFlexLayout *rootLayout = nil; + MyFlexLayout *flexAttrLayout = nil; + UILabel *flexAttrTitleLabel = nil; + UISegmentedControl *flexAttrSeg = nil; + + self.view = MyFlexNew(MyFlexLayout, rootLayout) + .flex_direction(MyFlexDirection_Column) + .vert_space(10) + .addItem(MyFlexNew(MyFlexLayout, flexAttrLayout) + .flex_direction(MyFlexDirection_Row) + .align_items(MyFlexGravity_Center) + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .addItem(MyFlexNew(UILabel, flexAttrTitleLabel) + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + ) + .addItem(MyFlexNew(UISegmentedControl, flexAttrSeg) + .flex_grow(1) + .height(MyLayoutSize.wrap) + ) + ).view; + + + + /* + //弹性盒布局构建布局的语法糖方式2: 你可以将上面部分注释,打开这部分注释,二者的作用是一致的。 + //根视图为flexbox布局视图。 + MyFlexLayout *rootLayout = MyFlexLayout.new.myFlex + .flex_direction(MyFlexDirection_Column) + .vert_space(10) + .view; + self.view = rootLayout; + + //布局属性设置。 + MyFlexLayout *flexAttrLayout = MyFlexLayout.new.myFlex + .flex_direction(MyFlexDirection_Row) + .align_items(MyFlexGravity_Center) + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .addTo(rootLayout); + + UILabel *flexAttrTitleLabel = UILabel.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(flexAttrLayout); + + + UISegmentedControl *flexAttrSeg = UISegmentedControl.new.myFlex + .flex_grow(1) + .height(MyLayoutSize.wrap) + .addTo(flexAttrLayout); + + */ + + + flexAttrTitleLabel.text = @"flex:"; + + [flexAttrSeg insertSegmentWithTitle:@"flex-direction" atIndex:0 animated:NO]; + [flexAttrSeg insertSegmentWithTitle:@"flex-wrap" atIndex:1 animated:NO]; + [flexAttrSeg insertSegmentWithTitle:@"justify-content" atIndex:2 animated:NO]; + [flexAttrSeg insertSegmentWithTitle:@"align_items" atIndex:3 animated:NO]; + [flexAttrSeg insertSegmentWithTitle:@"align_content" atIndex:4 animated:NO]; + [flexAttrSeg addTarget:self action:@selector(handleFlexLayoutAttributeChange:) forControlEvents:UIControlEventValueChanged]; + + UISegmentedControl *flex_directionSeg = UISegmentedControl.new.myFlex + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .addTo(rootLayout); + + + [flex_directionSeg insertSegmentWithTitle:@"row" atIndex:0 animated:NO]; + [flex_directionSeg insertSegmentWithTitle:@"row_reverse" atIndex:1 animated:NO]; + [flex_directionSeg insertSegmentWithTitle:@"column" atIndex:2 animated:NO]; + [flex_directionSeg insertSegmentWithTitle:@"column_reverse" atIndex:3 animated:NO]; + [flex_directionSeg addTarget:self action:@selector(handleFlex_Direction:) forControlEvents:UIControlEventValueChanged]; + self.flex_directionSeg = flex_directionSeg; + + UISegmentedControl *flex_wrapSeg = UISegmentedControl.new.myFlex + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .visibility(MyVisibility_Gone) + .addTo(rootLayout); + + [flex_wrapSeg insertSegmentWithTitle:@"nowrap" atIndex:0 animated:NO]; + [flex_wrapSeg insertSegmentWithTitle:@"wrap" atIndex:1 animated:NO]; + [flex_wrapSeg insertSegmentWithTitle:@"wrap_reverse" atIndex:2 animated:NO]; + [flex_wrapSeg addTarget:self action:@selector(handleFlex_Wrap:) forControlEvents:UIControlEventValueChanged]; + self.flex_wrapSeg = flex_wrapSeg; + + UISegmentedControl *justify_contentSeg = UISegmentedControl.new.myFlex + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .visibility(MyVisibility_Gone) + .addTo(rootLayout); + + [justify_contentSeg insertSegmentWithTitle:@"flex-start" atIndex:0 animated:NO]; + [justify_contentSeg insertSegmentWithTitle:@"flex-end" atIndex:1 animated:NO]; + [justify_contentSeg insertSegmentWithTitle:@"center" atIndex:2 animated:NO]; + [justify_contentSeg insertSegmentWithTitle:@"space-between" atIndex:3 animated:NO]; + [justify_contentSeg insertSegmentWithTitle:@"space-around" atIndex:4 animated:NO]; + [justify_contentSeg addTarget:self action:@selector(handleJustify_Content:) forControlEvents:UIControlEventValueChanged]; + self.justify_contentSeg = justify_contentSeg; + + UISegmentedControl *align_itemsSeg = UISegmentedControl.new.myFlex + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .visibility(MyVisibility_Gone) + .addTo(rootLayout); + + [align_itemsSeg insertSegmentWithTitle:@"flex-start" atIndex:0 animated:NO]; + [align_itemsSeg insertSegmentWithTitle:@"flex-end" atIndex:1 animated:NO]; + [align_itemsSeg insertSegmentWithTitle:@"center" atIndex:2 animated:NO]; + [align_itemsSeg insertSegmentWithTitle:@"stretch" atIndex:3 animated:NO]; + [align_itemsSeg insertSegmentWithTitle:@"baseline" atIndex:4 animated:NO]; + [align_itemsSeg addTarget:self action:@selector(handleAlign_Items:) forControlEvents:UIControlEventValueChanged]; + self.align_itemsSeg = align_itemsSeg; + + + UISegmentedControl *align_contentSeg = UISegmentedControl.new.myFlex + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .visibility(MyVisibility_Gone) + .addTo(rootLayout); + + [align_contentSeg insertSegmentWithTitle:@"flex-start" atIndex:0 animated:NO]; + [align_contentSeg insertSegmentWithTitle:@"flex-end" atIndex:1 animated:NO]; + [align_contentSeg insertSegmentWithTitle:@"center" atIndex:2 animated:NO]; + [align_contentSeg insertSegmentWithTitle:@"space-between" atIndex:3 animated:NO]; + [align_contentSeg insertSegmentWithTitle:@"space-around" atIndex:4 animated:NO]; + [align_contentSeg insertSegmentWithTitle:@"stretch" atIndex:5 animated:NO]; + [align_contentSeg addTarget:self action:@selector(handleAlign_Content:) forControlEvents:UIControlEventValueChanged]; + self.align_contentSeg = align_contentSeg; + + + MyFlexLayout *paddingLayout = MyFlexLayout.new.myFlex + .flex_direction(MyFlexDirection_Row) + .align_items(MyFlexGravity_Center) + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .addTo(rootLayout); + + UILabel *paddingTitleLabel = UILabel.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(paddingLayout); + + paddingTitleLabel.text = @"padding:"; + + + + + + + + + UISwitch *paddingSwitch = UISwitch.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(paddingLayout); + + [paddingSwitch addTarget:self action:@selector(handlePaddingChange:) forControlEvents:UIControlEventValueChanged]; + + MyFlexLayout *spaceLayout = MyFlexLayout.new.myFlex + .flex_direction(MyFlexDirection_Row) + .align_items(MyFlexGravity_Center) + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .addTo(rootLayout); + + UILabel *spaceTitleLabel = UILabel.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(spaceLayout); + + spaceTitleLabel.text = @"space:"; + + UISwitch *spaceSwitch = UISwitch.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(spaceLayout); + + [spaceSwitch addTarget:self action:@selector(handleSpaceChange:) forControlEvents:UIControlEventValueChanged]; + + + UILabel *flexlayoutStyleDescLabel = UILabel.new.myFlex + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .addTo(rootLayout); + + flexlayoutStyleDescLabel.text = @""; + flexlayoutStyleDescLabel.textColor = [CFTool color:6]; + self.flexlayoutStyleDescLabel = flexlayoutStyleDescLabel; + + UIButton *button = UIButton.new.myFlex + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .addTo(rootLayout); + + [button setTitle:@"Add Subview" forState:UIControlStateNormal]; + [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + [button addTarget:self action:@selector(handleAddItem:) forControlEvents:UIControlEventTouchUpInside]; + + + + MyFlexLayout *contentLayout = MyFlexLayout.new.myFlex + .flex_grow(1) + .width(MyLayoutSize.fill) + .addTo(rootLayout); + + contentLayout.backgroundColor = [UIColor lightGrayColor]; + self.contentLayout = contentLayout; + + flexAttrSeg.selectedSegmentIndex = 0; + self.flex_directionSeg.selectedSegmentIndex = 0; + [self handleFlex_Direction:self.flex_directionSeg]; + +} + +- (void)viewDidLoad { + self.edgesForExtendedLayout = UIRectEdgeNone; //设置视图控制器中的视图尺寸不延伸到导航条或者工具条下面。您可以注释这句代码看看效果。 + + [super viewDidLoad]; + // Do any additional setup after loading the view. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +-(void)updateStyleDesc +{ + NSString *strFlexDirection = @""; + switch (self.contentLayout.myFlex.attrs.flex_direction) { + case MyFlexDirection_Row: + strFlexDirection =@"row;"; + break; + case MyFlexDirection_Column: + strFlexDirection = @"column;"; + break; + case MyFlexDirection_Row_Reverse: + strFlexDirection = @"row-reverse;"; + break; + case MyFlexDirection_Column_Reverse: + strFlexDirection = @"column-reverse;"; + break; + default: + break; + } + + NSString *strFlexWrap = @""; + switch (self.contentLayout.myFlex.attrs.flex_wrap) { + case MyFlexWrap_NoWrap: + strFlexWrap = @"nowrap;"; + break; + case MyFlexWrap_Wrap: + strFlexWrap = @"wrap;"; + break; + case MyFlexWrap_Wrap_Reverse: + strFlexWrap = @"wrap-reverse;"; + break; + default: + break; + } + + NSString *strJustifyContent = @""; + switch (self.contentLayout.myFlex.attrs.justify_content) { + case MyFlexGravity_Flex_Start: + strJustifyContent = @"flex-start;"; + break; + case MyFlexGravity_Flex_End: + strJustifyContent = @"flex-end;"; + break; + case MyFlexGravity_Center: + strJustifyContent = @"center;"; + break; + case MyFlexGravity_Space_Between: + strJustifyContent = @"space-between;"; + break; + case MyFlexGravity_Space_Around: + strJustifyContent = @"space-around;"; + default: + break; + } + + NSString *strAlignItems = @""; + switch (self.contentLayout.myFlex.attrs.align_items) { + case MyFlexGravity_Flex_End: + strAlignItems = @"flex-end;"; + break; + case MyFlexGravity_Center: + strAlignItems = @"center;"; + break; + case MyFlexGravity_Baseline: + strAlignItems = @"baseline;"; + break; + case MyFlexGravity_Flex_Start: + strAlignItems = @"flex-start;"; + break; + case MyFlexGravity_Stretch: + strAlignItems = @"stretch;"; + break; + default:; + } + + NSString *strAlignContent = @""; + switch (self.contentLayout.myFlex.attrs.align_content) { + case MyFlexGravity_Flex_End: + strAlignContent = @"flex-end;"; + break; + case MyFlexGravity_Center: + strAlignContent = @"center;"; + break; + case MyFlexGravity_Space_Between: + strAlignContent = @"space-between;"; + break; + case MyFlexGravity_Flex_Start: + strAlignContent = @"flex-start;"; + break; + case MyFlexGravity_Space_Around: + strAlignContent = @"space-around;"; + break; + case MyFlexGravity_Stretch: + strAlignContent = @"stretch;"; + break; + default:; + } + + self.flexlayoutStyleDescLabel.text = [NSString stringWithFormat:@"Style=\"flex-direction:%@ flex-wrap:%@ justify-content:%@ align-items:%@ align-content:%@\"", + strFlexDirection, + strFlexWrap, + strJustifyContent, + strAlignItems, + strAlignContent]; + +} + + +-(void)editFlexItem:(UIView*)itemView +{ + //创建一个 + MyFlexLayout *dialogLayout = MyFlexLayout.new.myFlex + .flex_direction(MyFlexDirection_Row) + .align_items(MyFlexGravity_Center) + .flex_wrap(MyFlexWrap_Wrap) + .padding(UIEdgeInsetsMake(10, 10, 10, 10)) + .vert_space(10) + .horz_space(10) + .width(220) + .height(MyLayoutSize.wrap) + .margin_top(80) + .margin_left((self.view.window.frame.size.width - 200)/2.0) + .addTo(self.view.window); + + dialogLayout.backgroundColor = [CFTool color:2]; + + UILabel *widthLabel = UILabel.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(dialogLayout); + + widthLabel.text = @"width:"; + + UITextField *widthTextField = UITextField.new.myFlex + .flex_grow(1) + .width(100) + .height(30) + .addTo(dialogLayout); + + widthTextField.tag = 100; + widthTextField.borderStyle = UITextBorderStyleRoundedRect; + widthTextField.placeholder = @"数字|wrap|fill|empty"; + widthTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; + + UILabel *heightLabel = UILabel.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(dialogLayout); + + heightLabel.text = @"height:"; + + UITextField *heightTextField = UITextField.new.myFlex + .flex_grow(1) + .width(100) + .height(30) + .addTo(dialogLayout); + + heightTextField.tag = 200; + heightTextField.borderStyle = UITextBorderStyleRoundedRect; + heightTextField.placeholder = @"数字|wrap|fill|empty"; + heightTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; + + + UILabel *orderLabel = UILabel.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(dialogLayout); + + orderLabel.text = @"order:"; + + UITextField *orderTextField = UITextField.new.myFlex + .flex_grow(1) + .width(100) + .height(30) + .addTo(dialogLayout); + + orderTextField.tag = 300; + orderTextField.borderStyle = UITextBorderStyleRoundedRect; + orderTextField.placeholder = @"数字|空"; + + + UILabel *flex_growLabel = UILabel.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(dialogLayout); + + flex_growLabel.text = @"flex-grow:"; + + UITextField *flex_growTextField = UITextField.new.myFlex + .flex_grow(1) + .width(100) + .height(30) + .addTo(dialogLayout); + + flex_growTextField.tag = 400; + flex_growTextField.borderStyle = UITextBorderStyleRoundedRect; + flex_growTextField.placeholder = @"数字|空"; + + + UILabel *flex_shrinkLabel = UILabel.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(dialogLayout); + + flex_shrinkLabel.text = @"flex-shrink:"; + + UITextField *flex_shrinkTextField = UITextField.new.myFlex + .flex_grow(1) + .width(100) + .height(30) + .addTo(dialogLayout); + + flex_shrinkTextField.tag = 500; + flex_shrinkTextField.borderStyle = UITextBorderStyleRoundedRect; + flex_shrinkTextField.placeholder = @"数字|空"; + + + UILabel *flex_basisLabel = UILabel.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(dialogLayout); + + flex_basisLabel.text = @"flex-basis:"; + + UITextField *flex_basisTextField = UITextField.new.myFlex + .flex_grow(1) + .width(100) + .height(30) + .addTo(dialogLayout); + + flex_basisTextField.tag = 600; + flex_basisTextField.borderStyle = UITextBorderStyleRoundedRect; + flex_basisTextField.placeholder = @"数字|空"; + + + UILabel *align_selfLabel = UILabel.new.myFlex + .width(MyLayoutSize.wrap) + .height(MyLayoutSize.wrap) + .addTo(dialogLayout); + + align_selfLabel.text = @"align-self:"; + + UITextField *align_selfTextField = UITextField.new.myFlex + .flex_grow(1) + .width(100) + .height(30) + .addTo(dialogLayout); + + align_selfTextField.tag = 700; + align_selfTextField.borderStyle = UITextBorderStyleRoundedRect; + align_selfTextField.placeholder = @"flex-start|flex-end|center|stretch|baseline|auto"; + + + UIButton *addButton = UIButton.new.myFlex + .flex_grow(1) + .width(50) + .height(30) + .addTo(dialogLayout); + + + if (itemView != nil) + { + addButton.tag = (NSInteger)itemView; + [addButton setTitle:@"Save" forState:UIControlStateNormal]; + } + else + [addButton setTitle:@"Add" forState:UIControlStateNormal]; + + [addButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + [addButton addTarget:self action:@selector(handleSaveItem:) forControlEvents:UIControlEventTouchUpInside]; + + UIButton *closeButton = UIButton.new.myFlex + .flex_grow(1) + .width(50) + .height(30) + .addTo(dialogLayout); + + [closeButton setTitle:@"Close" forState:UIControlStateNormal]; + [closeButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; + + [closeButton addTarget:self action:@selector(handleCloseDialog:) forControlEvents:UIControlEventTouchUpInside]; + + UILabel *tipLabel = UILabel.new.myFlex + .width(MyLayoutSize.fill) + .height(MyLayoutSize.wrap) + .addTo(dialogLayout); + + tipLabel.text = @"添加后单击修改,长按删除"; + + if (itemView != nil) + { + if (itemView.myFlex.attrs.width == MyLayoutSize.wrap) + widthTextField.text = @"wrap"; + else if (itemView.myFlex.attrs.width == MyLayoutSize.fill) + widthTextField.text = @"fill"; + else if (itemView.myFlex.attrs.width == MyLayoutSize.empty) + widthTextField.text = @"empty"; + else if (itemView.myFlex.attrs.width != 0) + widthTextField.text = [@(itemView.myFlex.attrs.width) stringValue]; + else + widthTextField.text = @""; + + + if (itemView.myFlex.attrs.height == MyLayoutSize.wrap) + heightTextField.text = @"wrap"; + else if (itemView.myFlex.attrs.height == MyLayoutSize.fill) + heightTextField.text = @"fill"; + else if (itemView.myFlex.attrs.height == MyLayoutSize.empty) + heightTextField.text = @"empty"; + else if (itemView.myFlex.attrs.height != 0) + heightTextField.text = [@(itemView.myFlex.attrs.height) stringValue]; + else + heightTextField.text = @""; + + + if (itemView.myFlex.attrs.order != 0) + orderTextField.text = [@(itemView.myFlex.attrs.order) stringValue]; + + + if (itemView.myFlex.attrs.flex_grow != 0) + flex_growTextField.text = [@(itemView.myFlex.attrs.flex_grow) stringValue]; + + if (itemView.myFlex.attrs.flex_shrink != 1) + flex_shrinkTextField.text = [@(itemView.myFlex.attrs.flex_shrink) stringValue]; + + if (itemView.myFlex.attrs.flex_basis != MyFlex_Auto) + flex_basisTextField.text = [@(itemView.myFlex.attrs.flex_basis) stringValue]; + + switch (itemView.myFlex.attrs.align_self) { + case MyFlexGravity_Flex_Start: + align_selfTextField.text = @"flex-start"; + break; + case MyFlexGravity_Flex_End: + align_selfTextField.text = @"flex-end"; + break; + case MyFlexGravity_Center: + align_selfTextField.text = @"center"; + break; + case MyFlexGravity_Baseline: + align_selfTextField.text = @"baseline"; + break; + case MyFlexGravity_Stretch: + align_selfTextField.text = @"stretch"; + break; + default: + break; + } + } +} + + +#pragma mark -- Handle Method + +-(IBAction)handleFlexLayoutAttributeChange:(UISegmentedControl*)sender +{ + self.flex_directionSeg.myFlex.visibility(MyVisibility_Gone); + self.flex_wrapSeg.myFlex.visibility(MyVisibility_Gone); + self.justify_contentSeg.myFlex.visibility(MyVisibility_Gone); + self.align_itemsSeg.myFlex.visibility(MyVisibility_Gone); + self.align_contentSeg.myFlex.visibility(MyVisibility_Gone); + + switch (sender.selectedSegmentIndex) { + case 0: + { + self.flex_directionSeg.myFlex.visibility(MyVisibility_Visible); + switch (self.contentLayout.myFlex.attrs.flex_direction) { + case MyFlexDirection_Row: + self.flex_directionSeg.selectedSegmentIndex = 0; + break; + case MyFlexDirection_Column: + self.flex_directionSeg.selectedSegmentIndex = 2; + break; + case MyFlexDirection_Row_Reverse: + self.flex_directionSeg.selectedSegmentIndex = 1; + break; + case MyFlexDirection_Column_Reverse: + self.flex_directionSeg.selectedSegmentIndex = 3; + default: + break; + } + } + break; + case 1: + { + self.flex_wrapSeg.myFlex.visibility(MyVisibility_Visible); + switch (self.contentLayout.myFlex.attrs.flex_wrap) { + case MyFlexWrap_Wrap: + self.flex_wrapSeg.selectedSegmentIndex = 1; + break; + case MyFlexWrap_NoWrap: + self.flex_wrapSeg.selectedSegmentIndex = 0; + break; + case MyFlexWrap_Wrap_Reverse: + self.flex_wrapSeg.selectedSegmentIndex = 2; + break; + default: + break; + } + } + break; + case 2: + { + self.justify_contentSeg.myFlex.visibility(MyVisibility_Visible); + switch (self.contentLayout.myFlex.attrs.justify_content) { + case MyFlexGravity_Flex_Start: + self.justify_contentSeg.selectedSegmentIndex = 0; + break; + case MyFlexGravity_Flex_End: + self.justify_contentSeg.selectedSegmentIndex = 1; + break; + case MyFlexGravity_Center: + self.justify_contentSeg.selectedSegmentIndex = 2; + break; + case MyFlexGravity_Space_Between: + self.justify_contentSeg.selectedSegmentIndex = 3; + break; + case MyFlexGravity_Space_Around: + self.justify_contentSeg.selectedSegmentIndex = 4; + default: + break; + } + } + break; + case 3: + { + self.align_itemsSeg.myFlex.visibility(MyVisibility_Visible); + switch (self.contentLayout.myFlex.attrs.align_items) { + case MyFlexGravity_Flex_Start: + self.align_itemsSeg.selectedSegmentIndex = 0; + break; + case MyFlexGravity_Flex_End: + self.align_itemsSeg.selectedSegmentIndex = 1; + break; + case MyFlexGravity_Center: + self.align_itemsSeg.selectedSegmentIndex = 2; + break; + case MyFlexGravity_Stretch: + self.align_itemsSeg.selectedSegmentIndex = 3; + break; + case MyFlexGravity_Baseline: + self.align_itemsSeg.selectedSegmentIndex = 4; + break; + default: + break; + } + } + break; + case 4: + { + self.align_contentSeg.myFlex.visibility(MyVisibility_Visible); + switch (self.contentLayout.myFlex.attrs.align_content) { + case MyFlexGravity_Flex_Start: + self.align_contentSeg.selectedSegmentIndex = 0; + break; + case MyFlexGravity_Flex_End: + self.align_contentSeg.selectedSegmentIndex = 1; + break; + case MyFlexGravity_Center: + self.align_contentSeg.selectedSegmentIndex = 2; + break; + case MyFlexGravity_Space_Between: + self.align_contentSeg.selectedSegmentIndex = 3; + break; + case MyFlexGravity_Space_Around: + self.align_contentSeg.selectedSegmentIndex = 4; + break; + case MyFlexGravity_Stretch: + self.align_contentSeg.selectedSegmentIndex = 5; + break; + default:; + } + } + break; + default: + break; + } +} + +-(void)handleFlex_Direction:(UISegmentedControl*)sender +{ + switch (sender.selectedSegmentIndex) { + case 0: + self.contentLayout.myFlex.flex_direction(MyFlexDirection_Row); + break; + case 1: + self.contentLayout.myFlex.flex_direction(MyFlexDirection_Row_Reverse); + break; + case 2: + self.contentLayout.myFlex.flex_direction(MyFlexDirection_Column); + break; + case 3: + self.contentLayout.myFlex.flex_direction(MyFlexDirection_Column_Reverse); + case 4: + default: + break; + } + + [self updateStyleDesc]; +} + +-(void)handleFlex_Wrap:(UISegmentedControl*)sender +{ + switch (sender.selectedSegmentIndex) { + case 0: + self.contentLayout.myFlex.flex_wrap(MyFlexWrap_NoWrap); + break; + case 1: + self.contentLayout.myFlex.flex_wrap(MyFlexWrap_Wrap); + break; + case 2: + self.contentLayout.myFlex.flex_wrap(MyFlexWrap_Wrap_Reverse); + break; + default: + break; + } + + [self updateStyleDesc]; +} + +-(void)handleJustify_Content:(UISegmentedControl*)sender +{ + switch (sender.selectedSegmentIndex) { + case 0: + self.contentLayout.myFlex.justify_content(MyFlexGravity_Flex_Start); + break; + case 1: + self.contentLayout.myFlex.justify_content(MyFlexGravity_Flex_End); + break; + case 2: + self.contentLayout.myFlex.justify_content(MyFlexGravity_Center); + break; + case 3: + self.contentLayout.myFlex.justify_content(MyFlexGravity_Space_Between); + break; + case 4: + self.contentLayout.myFlex.justify_content(MyFlexGravity_Space_Around); + break; + default: + break; + } + + [self updateStyleDesc]; +} + +-(void)handleAlign_Items:(UISegmentedControl*)sender +{ + switch (sender.selectedSegmentIndex) { + case 0: + self.contentLayout.myFlex.align_items(MyFlexGravity_Flex_Start); + break; + case 1: + self.contentLayout.myFlex.align_items(MyFlexGravity_Flex_End); + break; + case 2: + self.contentLayout.myFlex.align_items(MyFlexGravity_Center); + break; + case 3: + self.contentLayout.myFlex.align_items(MyFlexGravity_Stretch); + break; + case 4: + self.contentLayout.myFlex.align_items(MyFlexGravity_Baseline); + break; + default: + break; + } + + [self updateStyleDesc]; +} + + +-(void)handleAlign_Content:(UISegmentedControl*)sender +{ + switch (sender.selectedSegmentIndex) { + case 0: + self.contentLayout.myFlex.align_content(MyFlexGravity_Flex_Start); + break; + case 1: + self.contentLayout.myFlex.align_content(MyFlexGravity_Flex_End); + break; + case 2: + self.contentLayout.myFlex.align_content(MyFlexGravity_Center); + break; + case 3: + self.contentLayout.myFlex.align_content(MyFlexGravity_Space_Between); + break; + case 4: + self.contentLayout.myFlex.align_content(MyFlexGravity_Space_Around); + break; + case 5: + self.contentLayout.myFlex.align_content(MyFlexGravity_Stretch); + break; + default: + break; + } + + [self updateStyleDesc]; +} + +-(IBAction)handleSpaceChange:(UISwitch*)sender +{ + if (sender.isOn) + self.contentLayout.myFlex.vert_space(10).horz_space(10); + else + self.contentLayout.myFlex.vert_space(0).horz_space(0); +} + + +-(IBAction)handlePaddingChange:(UISwitch*)sender +{ + if (sender.isOn) + self.contentLayout.myFlex.padding(UIEdgeInsetsMake(20, 20, 20, 20)); + else + self.contentLayout.myFlex.padding(UIEdgeInsetsZero); +} + + +-(IBAction)handleAddItem:(UIButton*)sender +{ + [self editFlexItem:nil]; +} + +-(IBAction)handleCloseDialog:(UIButton*)sender +{ + [sender.superview removeFromSuperview]; +} + +-(IBAction)handleSaveItem:(UIButton*)sender +{ + + UIButton *itemView = nil; + if (sender.tag != 0) + itemView = (__bridge UIButton*)((void*)sender.tag); + + UITextField *widthTextField = [sender.superview viewWithTag:100]; + UITextField *heightTextField = [sender.superview viewWithTag:200]; + UITextField *orderTextField = [sender.superview viewWithTag:300]; + UITextField *flex_growTextField = [sender.superview viewWithTag:400]; + UITextField *flex_shrinkTextField = [sender.superview viewWithTag:500]; + UITextField *flex_basisTextField = [sender.superview viewWithTag:600]; + UITextField *align_selfTextField = [sender.superview viewWithTag:700]; + + if (itemView == nil) + { + itemView = [UIButton new]; + itemView.backgroundColor = [CFTool color:arc4random_uniform(14) + 1]; + [self.contentLayout addSubview:itemView]; + + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleModifyItemView:)]; + [itemView addGestureRecognizer:tapGesture]; + UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleRemoveItemView:)]; + [itemView addGestureRecognizer:longPressGesture]; + + } + + //设置属性!! + if (widthTextField.text.length == 0 && heightTextField.text.length == 0) + return; + + NSString *widthStr = widthTextField.text; + if ([widthStr isEqualToString:@"wrap"]) + itemView.myFlex.attrs.width = MyLayoutSize.wrap; + else if ([widthStr isEqualToString:@"fill"]) + itemView.myFlex.attrs.width = MyLayoutSize.fill; + else if ([widthStr isEqualToString:@"empty"]) + itemView.myFlex.attrs.width = MyLayoutSize.empty; + else if (widthStr.length == 0) + itemView.myFlex.attrs.width = MyLayoutSize.wrap; + else + itemView.myFlex.attrs.width = widthStr.doubleValue; + + NSString *heightStr = heightTextField.text; + if ([heightStr isEqualToString:@"wrap"]) + itemView.myFlex.attrs.height = MyLayoutSize.wrap; + else if ([heightStr isEqualToString:@"fill"]) + itemView.myFlex.attrs.height = MyLayoutSize.fill; + else if ([heightStr isEqualToString:@"empty"]) + itemView.myFlex.attrs.height = MyLayoutSize.empty; + else if (heightStr.length == 0) + itemView.myFlex.attrs.height = MyLayoutSize.wrap; + else + itemView.myFlex.attrs.height = heightStr.doubleValue; + + [itemView setTitle:orderTextField.text forState:UIControlStateNormal]; + itemView.myFlex.attrs.order = orderTextField.text.integerValue; + + + itemView.myFlex.attrs.flex_grow = flex_growTextField.text.doubleValue; + + if (flex_shrinkTextField.text.length > 0) + itemView.myFlex.attrs.flex_shrink = flex_shrinkTextField.text.doubleValue; + else + itemView.myFlex.attrs.flex_shrink = 1; + + if (flex_basisTextField.text.length > 0) + itemView.myFlex.attrs.flex_basis = flex_basisTextField.text.doubleValue; + else + itemView.myFlex.attrs.flex_basis = MyFlex_Auto; + + if (align_selfTextField.text.length > 0) + { + if ([align_selfTextField.text isEqualToString:@"flex-start"]) + itemView.myFlex.attrs.align_self = MyFlexGravity_Flex_Start; + else if ([align_selfTextField.text isEqualToString:@"flex-end"]) + itemView.myFlex.attrs.align_self = MyFlexGravity_Flex_End; + else if ([align_selfTextField.text isEqualToString:@"center"]) + itemView.myFlex.attrs.align_self = MyFlexGravity_Center; + else if ([align_selfTextField.text isEqualToString:@"baseline"]) + itemView.myFlex.attrs.align_self = MyFlexGravity_Baseline; + else if ([align_selfTextField.text isEqualToString:@"stretch"]) + itemView.myFlex.attrs.align_self = MyFlexGravity_Stretch; + else + itemView.myFlex.attrs.align_self = MyFlex_Auto; + } + else + { + itemView.myFlex.attrs.align_self = MyFlex_Auto; + } + + + //销毁对话框。 + [sender.superview removeFromSuperview]; +} + +-(IBAction)handleModifyItemView:(UITapGestureRecognizer*)sender +{ + [self editFlexItem:sender.view]; +} + +-(IBAction)handleRemoveItemView:(UILongPressGestureRecognizer*)sender +{ + [sender.view removeFromSuperview]; +} + + +@end diff --git a/MyLayoutDemo/FOLTest2ViewController.m b/MyLayoutDemo/FOLTest2ViewController.m index 22daad9..7697630 100644 --- a/MyLayoutDemo/FOLTest2ViewController.m +++ b/MyLayoutDemo/FOLTest2ViewController.m @@ -428,7 +428,7 @@ -(void)addSectionLayout:(NSInteger)sectionIndex MyFloatLayout *itemContainerLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; itemContainerLayout.tag = 1122; itemContainerLayout.backgroundColor = [UIColor whiteColor]; - itemContainerLayout.wrapContentHeight = YES; + itemContainerLayout.myHeight = MyLayoutSize.wrap; itemContainerLayout.intelligentBorderline = [[MyBorderline alloc] initWithColor:[UIColor lightGrayColor]]; [self.rootLayout addSubview:itemContainerLayout]; @@ -570,7 +570,7 @@ -(MyFloatLayout*)createItemLayout1_3:(FOLTest2DataModel*)dataModel subTitleLabel.myLeading = 5; subTitleLabel.clearFloat = YES; //清除浮动,另起一行。 subTitleLabel.weight = 1; - subTitleLabel.wrapContentHeight = YES; + subTitleLabel.myHeight = MyLayoutSize.wrap; [subTitleLabel sizeToFit]; [itemLayout addSubview:subTitleLabel]; diff --git a/MyLayoutDemo/FOLTest3ViewController.m b/MyLayoutDemo/FOLTest3ViewController.m index 23e8166..92e1b27 100644 --- a/MyLayoutDemo/FOLTest3ViewController.m +++ b/MyLayoutDemo/FOLTest3ViewController.m @@ -148,7 +148,7 @@ -(UIView*)createPictureNewsItemLayout:(FOLTest3DataModel*)dataModel tag:(NSInteg titleLabel.text = dataModel.title; titleLabel.textColor = [UIColor whiteColor]; titleLabel.weight = 1; //向左浮动,宽度和父视图保持一致。 - titleLabel.wrapContentHeight = YES; //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将wrapContentHeight设置为YES。 + titleLabel.myHeight = MyLayoutSize.wrap; //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将高度设置为自适应 [itemLayout addSubview:titleLabel]; itemLayout.gravity = MyGravity_Vert_Bottom; //将整个布局中的所有子视图垂直居底部。 @@ -173,7 +173,7 @@ -(UIView*)createWholeWidthTextNewsItemLayout:(FOLTest3DataModel*)dataModel tag:( titleLabel.text = dataModel.title; titleLabel.font = [UIFont boldSystemFontOfSize:17]; titleLabel.weight =1; //向左浮动,宽度和父视图保持一致。 - titleLabel.wrapContentHeight = YES; //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将wrapContentHeight设置为YES。 + titleLabel.myHeight = MyLayoutSize.wrap; //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将高度设置为自适应 [itemLayout addSubview:titleLabel]; //来源部分 @@ -208,7 +208,7 @@ -(MyBaseLayout*)createHalfWidthTextNewsItemLayout:(FOLTest3DataModel*)dataModel UILabel *titleLabel = [UILabel new]; titleLabel.text = dataModel.title; titleLabel.weight =1; //向左浮动,宽度和父视图保持一致。 - titleLabel.wrapContentHeight = YES; //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将wrapContentHeight设置为YES。 + titleLabel.myHeight = MyLayoutSize.wrap; //如果想让文本消息的高度是动态的,请在设置明确宽度的情况下将高度设置为自适应 [itemLayout addSubview:titleLabel]; diff --git a/MyLayoutDemo/FOLTest4ViewController.m b/MyLayoutDemo/FOLTest4ViewController.m index 78f613c..25fe339 100644 --- a/MyLayoutDemo/FOLTest4ViewController.m +++ b/MyLayoutDemo/FOLTest4ViewController.m @@ -185,7 +185,7 @@ -(void)style1Layout:(MyLinearLayout*)contentLayout MyFloatLayout *floatLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; floatLayout.backgroundColor = [UIColor whiteColor]; floatLayout.padding = UIEdgeInsetsMake(20, 10, 20, 10); - floatLayout.wrapContentHeight = YES; + floatLayout.myHeight = MyLayoutSize.wrap; floatLayout.subviewHSpace = 30; //设置浮动布局里面子视图之间的水平间距。 floatLayout.subviewVSpace = 10; //设置浮动布局里面子视图之间的垂直间距。 [contentLayout addSubview:floatLayout]; @@ -265,7 +265,7 @@ -(void)style2Layout:(MyLinearLayout*)contentLayout MyFloatLayout *floatLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; floatLayout.backgroundColor = [UIColor whiteColor]; floatLayout.padding = UIEdgeInsetsMake(20, 5, 20, 5); - floatLayout.wrapContentHeight = YES; + floatLayout.myHeight = MyLayoutSize.wrap; floatLayout.subviewVSpace = 10; //设置浮动布局里面子视图之间的垂直间距。 [floatLayout setSubviewsSize:sTagWidth minSpace:8 maxSpace:CGFLOAT_MAX]; //这里面水平间距用浮动间距,浮动间距设置为子视图固定宽度为70,最小的间距为8. [contentLayout addSubview:floatLayout]; @@ -338,7 +338,7 @@ -(UIView*)createActionLayout MyFloatLayout *actionLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; actionLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); actionLayout.subviewHSpace = 5; - actionLayout.wrapContentHeight = YES; + actionLayout.myHeight = MyLayoutSize.wrap; actionLayout.bottomBorderline = [[MyBorderline alloc] initWithColor:[UIColor blackColor]]; @@ -370,7 +370,7 @@ -(UIView*)createSectionView:(NSString*)title image:(NSString*)image { MyLinearLayout *sectionLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; - sectionLayout.wrapContentHeight = NO; + sectionLayout.heightSize.equalTo(nil); sectionLayout.layer.cornerRadius = 5; sectionLayout.layer.borderColor = [UIColor lightGrayColor].CGColor; sectionLayout.layer.borderWidth = 0.5; diff --git a/MyLayoutDemo/FOLTest5ViewController.m b/MyLayoutDemo/FOLTest5ViewController.m index cddc8fd..68562df 100644 --- a/MyLayoutDemo/FOLTest5ViewController.m +++ b/MyLayoutDemo/FOLTest5ViewController.m @@ -52,7 +52,7 @@ - (void)viewDidLoad { MyFloatLayout *rootLayout = [[MyFloatLayout alloc] initWithOrientation:MyOrientation_Vert]; rootLayout.myHorzMargin = 0; //宽度和滚动条视图保持一致。 - rootLayout.wrapContentHeight = YES; + rootLayout.myHeight = MyLayoutSize.wrap; rootLayout.subviewVSpace = 5; [scrollView addSubview:rootLayout]; @@ -70,7 +70,7 @@ - (void)viewDidLoad { label2.font = [CFTool font:15]; label2.weight = 1; //宽度占据全部 label2.clearFloat = YES; - label2.wrapContentHeight = YES; + label2.myHeight = MyLayoutSize.wrap; [rootLayout addSubview:label2]; //添加4个子图片。 @@ -101,7 +101,7 @@ - (void)viewDidLoad { descLabel.textColor = [CFTool color:4]; descLabel.font = [CFTool font:13]; descLabel.weight = 1; //占用剩余的宽度 - descLabel.wrapContentHeight = YES; //多行自动换行。 + descLabel.myHeight = MyLayoutSize.wrap; //多行自动换行。 [descLabel sizeToFit]; [rootLayout addSubview:descLabel]; diff --git a/MyLayoutDemo/FOLTest6ViewController.m b/MyLayoutDemo/FOLTest6ViewController.m index ed1b805..db9cfa8 100644 --- a/MyLayoutDemo/FOLTest6ViewController.m +++ b/MyLayoutDemo/FOLTest6ViewController.m @@ -72,14 +72,13 @@ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { -(void)createUserProfile1Layout:(MyLinearLayout*)rootLayout { - //这个例子建立一个只向上浮动的浮动布局视图,注意这里要想布局高度由子视图包裹的话则必须要同时设置noBoundaryLimit为YES。 - //在常规情况下,如果使用左右浮动布局时,要求必须有明确的宽度,也就是不要用wrapContentWidth。同样使用上下浮动布局时,要求必须要有明确的高度,也就是不要用wrapContentHeight。这样设置明确宽度或者高度的原因是浮动布局需要根据这些宽度或者高度的约束自动换行浮动。但是在实践的场景中,有时候我们在浮动方向上没有尺寸约束限制,而是人为的来控制子视图的换行,并且还要布局视图的宽度和高度具有包裹属性,那么这时候我们就可以用浮动布局的noBoundaryLimit属性来进行控制了。 - //设置noBoundaryLimit为YES时必要同时设置包裹属性。具体情况见属性noBoundaryLimit的说明。 + //这个例子建立一个只向上浮动的浮动布局视图,注意这里要想布局高度由子视图包裹的话则必须要设置布局视图的高度自适应。 + //当在水平浮动布局中设置高度自适应,子视图不能逆向浮动只能向上浮动,并且子视图的weight不能设置为非0. + //当在水平浮动布局中设置高度自适应,需要我们设置某个子视图的clearFloat为YES进行手动换行处理。 MyFloatLayout *contentLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Horz]; contentLayout.backgroundColor = [UIColor whiteColor]; - contentLayout.noBoundaryLimit = YES; - contentLayout.wrapContentHeight = YES; //对于上下浮动布局来说,如果只想向上浮动,而高度又希望是由子视图决定,则必须要设置noBoundaryLimit的值为YES。 + contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.myHorzMargin = 0; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); contentLayout.subviewHSpace = 5; @@ -112,7 +111,7 @@ -(void)createUserProfile1Layout:(MyLinearLayout*)rootLayout addressLabel.text = @"联系地址:中华人民共和国北京市朝阳区盈科中心B座2楼,其他的我就不会再告诉你了。"; addressLabel.font = [CFTool font:15]; addressLabel.textColor = [CFTool color:4]; - addressLabel.wrapContentHeight = YES; + addressLabel.heightSize.equalTo(@(MyLayoutSize.wrap)); addressLabel.widthSize.equalTo(contentLayout.widthSize).add(-45); //40的头像宽度外加5的左右间距。 [addressLabel sizeToFit]; [contentLayout addSubview:addressLabel]; @@ -151,7 +150,7 @@ -(void)createUserProfile2Layout:(MyLinearLayout*)rootLayout MyFloatLayout *contentLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; contentLayout.backgroundColor = [UIColor whiteColor]; - contentLayout.wrapContentHeight = YES; + contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.myHorzMargin = 0; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); [rootLayout addSubview:contentLayout]; @@ -235,11 +234,11 @@ -(void)createUserProfile2Layout:(MyLinearLayout*)rootLayout -(void)createUserProfile3Layout:(MyLinearLayout*)rootLayout { - //这个例子里面上下浮动布局还是可以设置wrapContentHeight的,并且这里用了viewLayoutCompleteBlock来实现一些特殊化处理。 + //这个例用了viewLayoutCompleteBlock来实现一些特殊化处理。 MyFloatLayout *contentLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Horz]; contentLayout.backgroundColor = [UIColor whiteColor]; - contentLayout.wrapContentHeight = YES; //虽然说上下浮动布局一般要明确有高度,但是我们依然可以用wrapContentHeight属性,这时候布局视图的高度就是子视图里面高度最高的子视图了。 + contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.myHorzMargin = 0; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); [rootLayout addSubview:contentLayout]; @@ -277,6 +276,7 @@ -(void)createUserProfile3Layout:(MyLinearLayout*)rootLayout nameLabel.text = @"欧阳大哥"; nameLabel.font = [CFTool font:17]; nameLabel.textColor = [CFTool color:4]; + nameLabel.clearFloat = YES; [nameLabel sizeToFit]; [contentLayout addSubview:nameLabel]; @@ -293,12 +293,9 @@ -(void)createUserProfile3Layout:(MyLinearLayout*)rootLayout detailLabel.textColor = [CFTool color:2]; detailLabel.font = [CFTool font:20]; detailLabel.adjustsFontSizeToFitWidth = YES; - detailLabel.reverseFloat = YES; [detailLabel sizeToFit]; [contentLayout addSubview:detailLabel]; - - } @@ -306,7 +303,7 @@ -(void)createUserProfile4Layout:(MyLinearLayout*)rootLayout { MyFloatLayout *contentLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; contentLayout.backgroundColor = [UIColor whiteColor]; - contentLayout.wrapContentHeight = YES; + contentLayout.myHeight = MyLayoutSize.wrap; contentLayout.myHorzMargin = 0; contentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); [rootLayout addSubview:contentLayout]; @@ -327,8 +324,8 @@ -(void)createUserProfile4Layout:(MyLinearLayout*)rootLayout editButton.textColor = [CFTool color:4]; editButton.layer.cornerRadius = 5; editButton.layer.masksToBounds = YES; - editButton.widthSize.equalTo(editButton.widthSize).add(20); - editButton.heightSize.equalTo(editButton.heightSize).add(4); + editButton.widthSize.equalTo(@(MyLayoutSize.wrap)).add(20); + editButton.heightSize.equalTo(@(MyLayoutSize.wrap)).add(4); editButton.reverseFloat = YES; [contentLayout addSubview:editButton]; diff --git a/MyLayoutDemo/FOLTest7ViewController.h b/MyLayoutDemo/FOLTest7ViewController.h new file mode 100644 index 0000000..a235526 --- /dev/null +++ b/MyLayoutDemo/FOLTest7ViewController.h @@ -0,0 +1,16 @@ +// +// FOLTest7ViewController.h +// MyLayout +// +// Created by oybq on 16/2/19. +// Copyright © 2016年 YoungSoft. All rights reserved. +// + +#import + +/** + *7.FloatLayout - Alignment + */ +@interface FOLTest7ViewController : UIViewController + +@end diff --git a/MyLayoutDemo/FOLTest7ViewController.m b/MyLayoutDemo/FOLTest7ViewController.m new file mode 100644 index 0000000..e6df937 --- /dev/null +++ b/MyLayoutDemo/FOLTest7ViewController.m @@ -0,0 +1,273 @@ +// +// FOLTest7ViewController.m +// MyLayout +// +// Created by oybq on 16/2/19. +// Copyright © 2016年 YoungSoft. All rights reserved. +// + +#import "FOLTest7ViewController.h" +#import "MyLayout.h" +#import "CFTool.h" + +@interface FOLTest7ViewController () + +@end + +@implementation FOLTest7ViewController + + +-(void)loadView +{ + /* + 这个例子主要给大家演示,在浮动布局中也可以支持一行(列)内的子视图的对齐方式的设置了。我们可以借助子视图的alignment属性来设置其在浮动布局行(列)内的对齐方式。 + 这里的对齐的标准都是以当前行(列)内最高(宽)的子视图为参考来进行(列)对齐的。 + + 在垂直浮动布局里面的子视图的行内对齐只能设置MyGravity_Vert_Top, MyGravity_Vert_Center, MyGravity_Vert_Bottom, MyGravity_Vert_Fill这几种对齐方式。 + 在水平浮动布局里面的子视图的列内对齐只能设置MyGravity_Horz_Left, MyGravity_Horz_Center, MyGravity_Horz_Right, MyGravity_Horz_Fill这几种对齐方式。 + + */ + + self.edgesForExtendedLayout = UIRectEdgeNone; + + MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + rootLayout.backgroundColor = [UIColor lightGrayColor]; + rootLayout.gravity = MyGravity_Horz_Fill; //所有子视图的宽度都和自己相等。 + rootLayout.subviewVSpace = 10; + self.view = rootLayout; + + MyFloatLayout *vertLayout = [self createVertFloatLayout:rootLayout]; + vertLayout.backgroundColor = [UIColor whiteColor]; + vertLayout.weight = 0.8; //高度占用80% + [rootLayout addSubview:vertLayout]; + + MyFloatLayout *horzLayout = [self createHorzFloatLayout:rootLayout]; + horzLayout.backgroundColor = [UIColor whiteColor]; + horzLayout.weight = 0.2; //高度占用20% + [rootLayout addSubview:horzLayout]; + +} + + +- (void)viewDidLoad { + + [super viewDidLoad]; + + +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +#pragma mark -- Layout Construction + +-(MyFloatLayout*)createVertFloatLayout:(MyLinearLayout*)rootLayout +{ + MyFloatLayout *floatLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; + + UIImageView *logoImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"p1-12"]]; + logoImageView.layer.borderColor = [CFTool color:4].CGColor; + logoImageView.layer.borderWidth = 1; + logoImageView.alignment = MyGravity_Vert_Center; //在浮动的一行内垂直居中对齐。 + logoImageView.myMargin = 10; //四周的边距都设置为10. + logoImageView.mySize = CGSizeMake(80, 36); + [floatLayout addSubview:logoImageView]; + + UILabel *brandLabel = [UILabel new]; + brandLabel.text = @"千奈美官方旗舰店"; + [brandLabel sizeToFit]; + brandLabel.alignment = MyGravity_Vert_Center; //在浮动的一行内垂直居中对齐。 + brandLabel.myVertMargin = 10; + [floatLayout addSubview:brandLabel]; + + UIButton *attentionButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [attentionButton setTitle:@"关注" forState:UIControlStateNormal]; + [attentionButton sizeToFit]; + attentionButton.reverseFloat = YES; //关注放在右边,所以浮动到右边。 + attentionButton.myMargin = 10; + attentionButton.alignment = MyGravity_Vert_Center; //在浮动的一行内垂直居中对齐。 + [floatLayout addSubview:attentionButton]; + + //单独一行。 + UIView *line1 = [UIView new]; + line1.backgroundColor = [CFTool color:5]; + line1.myHeight = 2; + line1.widthSize.equalTo(floatLayout.widthSize); //宽度和父视图一样宽。 + [floatLayout addSubview:line1]; + + + UIImageView *showImageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"image2"]]; + showImageView1.myMargin = 10; //四周边距是10 + showImageView1.weight = 0.6; //此时父布局的剩余宽度是屏幕,因此这里的宽度就是屏幕宽度的0.6 + showImageView1.heightSize.equalTo(showImageView1.widthSize); //高度等于宽度。 + [floatLayout addSubview:showImageView1]; + + + //绘制线 + UIView *line2 = [UIView new]; + line2.backgroundColor = [CFTool color:5]; + line2.myWidth = 2; + line2.heightSize.equalTo(showImageView1.heightSize).add(22); //高度和showImageView1高度相等,因为showImageView1还有上下分别为10的边距,还有中间横线的高度2,所以这里要增加22的高度。 + [floatLayout addSubview:line2]; + + + //右边上面的小图。 + UIImageView *showImageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"image3"]]; + showImageView2.myMargin = 10; //四周边距是10 + showImageView2.weight = 1.0; //注意这里是剩余宽度的比重,因为这个小图要占用全部的剩余空间,因此这里设置为1。 + showImageView2.heightSize.equalTo(showImageView1.heightSize).multiply(0.5).add(-10); //高度等于大图高度的一半,再减去多余的边距10 + [floatLayout addSubview:showImageView2]; + + + //中间横线。 + UIView *line3 = [UIView new]; + line3.backgroundColor = [CFTool color:5]; + line3.myHeight = 2; + line3.weight = 1.0; + [floatLayout addSubview:line3]; + + //右边下面的小图 + UIImageView *showImageView3 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"image4"]]; + showImageView3.myMargin = 10; + showImageView3.weight = 1.0; + showImageView3.heightSize.equalTo(showImageView1.heightSize).multiply(0.5).add(-10); + [floatLayout addSubview:showImageView3]; + + //绘制下面的横线。 + UIView *line4 = [UIView new]; + line4.backgroundColor = [CFTool color:5]; + line4.myHeight = 2; + line4.weight = 1.0; //因为前面的所有内容都占满一行了,所以这条线是单独一行,这里是占用屏幕的全部空间了。 + line4.myBottom = 10; + [floatLayout addSubview:line4]; + + + UILabel *signatureLabel = [UILabel new]; + signatureLabel.text = @"今日已有137人签到获得好礼"; + signatureLabel.font = [CFTool font:14]; + signatureLabel.textColor = [CFTool color:4]; + [signatureLabel sizeToFit]; + signatureLabel.myHorzMargin = 10; + signatureLabel.alignment = MyGravity_Vert_Center; + [floatLayout addSubview:signatureLabel]; + + UIImageView *moreImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"next"]]; + moreImageView.reverseFloat = YES; + moreImageView.myHorzMargin = 10; + moreImageView.alignment = MyGravity_Vert_Center; + [floatLayout addSubview:moreImageView]; + + + UILabel *moreLabel = [UILabel new]; + moreLabel.text = @"进店看看"; + [moreLabel sizeToFit]; + moreLabel.reverseFloat = YES; + moreLabel.alignment = MyGravity_Vert_Center; + [floatLayout addSubview:moreLabel]; + + + UIView *line5 = [UIView new]; + line5.backgroundColor = [CFTool color:5]; + line5.myHeight = 2; + line5.myVertMargin = 10; + line5.widthSize.equalTo(floatLayout.widthSize); + [floatLayout addSubview:line5]; + + + // + UIImageView *commentImageView1 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"minions4"]]; + commentImageView1.alignment = MyGravity_Vert_Fill; //这里使用填充对齐,表明会和这行里面高度最高的那个子视图的高度保持一致。 + commentImageView1.myLeft = 10; + [floatLayout addSubview:commentImageView1]; + + UIImageView *commentImageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"minions3"]]; + commentImageView2.alignment = MyGravity_Vert_Fill; //这里使用填充对齐,表明会和这行里面高度最高的那个子视图的高度保持一致。 + commentImageView2.myLeft = 10; + [floatLayout addSubview:commentImageView2]; + + UIImageView *commentImageView3 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"minions3"]]; + commentImageView3.myLeft = 10; + [floatLayout addSubview:commentImageView3]; + + + for (int i = 0; i < 4; i++) + { + UIImageView *starImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"section2"]]; + starImageView.mySize = CGSizeMake(20, 20); + starImageView.alignment = MyGravity_Vert_Bottom; //这里底部对齐,表明子视图和一行内最高的子视图保持底部对齐。 + starImageView.myLeft = 5; + [floatLayout addSubview:starImageView]; + } + + + UIView *line6 = [UIView new]; + line6.backgroundColor = [CFTool color:5]; + line6.myHeight = 2; + line6.widthSize.equalTo(floatLayout.widthSize); + [floatLayout addSubview:line6]; + + + + + return floatLayout; + +} + +-(MyFloatLayout*)createHorzFloatLayout:(MyLinearLayout*)rootLayout +{ + MyFloatLayout *floatLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Horz]; + floatLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); + floatLayout.subviewSpace = 10; + + NSArray *names = @[@"minions1",@"minions3",@"minions2",@"minions4",@"p4-23",@"p4-11"]; + for (int i = 0; i < 6; i++) + { + UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:names[i]]]; + imageView.layer.borderWidth = 0.5; + imageView.layer.borderColor = [CFTool color:6].CGColor; + imageView.heightSize.equalTo(floatLayout.heightSize).multiply(0.5).add(-5); //高度等于父视图的高度的一半,因为设置了每个子视图的间距为10,所以这里要减去5。 + if (i % 2 == 0) + {//这句话的意思一列显示两个子视图,所以当索引下标为偶数时就是换列处理。 + imageView.clearFloat = YES; + } + + //水平填充,每列两个子视图,每列的对齐方式都不一样。 + switch (i) { + case 0: + case 1: + imageView.alignment = MyGravity_Horz_Center; + break; + case 2: + case 3: + imageView.alignment = MyGravity_Horz_Right; + break; + case 4: + case 5: + imageView.alignment = MyGravity_Horz_Fill; + default: + break; + } + + [floatLayout addSubview:imageView]; + + } + + + return floatLayout; + + +} + +@end diff --git a/MyLayoutDemo/GLTest1ViewController.m b/MyLayoutDemo/GLTest1ViewController.m index 9740316..acbf72b 100644 --- a/MyLayoutDemo/GLTest1ViewController.m +++ b/MyLayoutDemo/GLTest1ViewController.m @@ -48,7 +48,7 @@ -(void)loadView MyGridLayout *rootLayout = [MyGridLayout new]; rootLayout.myHorzMargin = 0; - rootLayout.wrapContentHeight = YES; + rootLayout.myHeight = MyLayoutSize.wrap; rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); rootLayout.subviewSpace = 10; //直接子栅格之间的间隔。 [scrollView addSubview:rootLayout]; @@ -196,7 +196,7 @@ -(void)loadView [rootLayout addRow:MyLayoutSize.wrap]; UILabel *shrinkLabel = [UILabel new]; - shrinkLabel.text = NSLocalizedString(@"This is a can automatically wrap text.To realize this function, you need to set the clear width, and set the wrapContentHeight to YES.You can try to switch different simulator or different orientation screen to see the effect.", @""); + shrinkLabel.text = NSLocalizedString(@"This is a can automatically wrap text.To realize this function, you need to set the width exact, and set the heightSize to MyLayoutSize.wrap.You can try to switch different simulator or different orientation screen to see the effect.", @""); shrinkLabel.backgroundColor = [CFTool color:2]; shrinkLabel.font = [CFTool font:14]; shrinkLabel.numberOfLines = 0; diff --git a/MyLayoutDemo/GLTest3ViewController.m b/MyLayoutDemo/GLTest3ViewController.m index 8750d8e..25dfc70 100644 --- a/MyLayoutDemo/GLTest3ViewController.m +++ b/MyLayoutDemo/GLTest3ViewController.m @@ -265,7 +265,7 @@ -(void)loadView scrollView.pagingEnabled = YES; MyFlowLayout *scrollFlowLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:1]; scrollFlowLayout.pagedCount = 1; //pagedCount设置为非0时表示开始分页展示的功能,这里表示每页展示9个子视图,这个数量必须是arrangedCount的倍数。 - scrollFlowLayout.wrapContentWidth = YES; //设置布局视图的宽度由子视图包裹,当水平流式布局的这个属性设置为YES,并和pagedCount搭配使用会产生分页从左到右滚动的效果。 + scrollFlowLayout.widthSize.equalTo(@(MyLayoutSize.wrap)); //设置布局视图的宽度由子视图包裹,当水平流式布局的这个属性设置为YES,并和pagedCount搭配使用会产生分页从左到右滚动的效果。 scrollFlowLayout.heightSize.equalTo(scrollView.heightSize); //因为是分页从左到右滚动,因此布局视图的高度必须设置为和父滚动视图相等。 [scrollView addSubview:scrollFlowLayout]; diff --git a/MyLayoutDemo/GLTest4ViewController.m b/MyLayoutDemo/GLTest4ViewController.m index 5da37d8..62547c6 100644 --- a/MyLayoutDemo/GLTest4ViewController.m +++ b/MyLayoutDemo/GLTest4ViewController.m @@ -214,7 +214,7 @@ -(void)bindView scrollView.pagingEnabled = YES; MyFlowLayout *scrollFlowLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:1]; scrollFlowLayout.pagedCount = 1; - scrollFlowLayout.wrapContentWidth = YES; + scrollFlowLayout.widthSize.equalTo(@(MyLayoutSize.wrap)); scrollFlowLayout.heightSize.equalTo(scrollView.heightSize); [scrollView addSubview:scrollFlowLayout]; NSArray *temp = @[@"bk1",@"bk2",@"bk3",@"bk1",@"bk2"]; diff --git a/MyLayoutDemo/Images.xcassets/AppIcon.appiconset/Contents.json b/MyLayoutDemo/Images.xcassets/AppIcon.appiconset/Contents.json index d9e47dd..cdf278d 100644 --- a/MyLayoutDemo/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/MyLayoutDemo/Images.xcassets/AppIcon.appiconset/Contents.json @@ -100,8 +100,9 @@ "scale" : "2x" }, { - "idiom" : "ios-marketing", "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "appicon1024.png", "scale" : "1x" } ], diff --git a/MyLayoutDemo/Images.xcassets/AppIcon.appiconset/appicon1024.png b/MyLayoutDemo/Images.xcassets/AppIcon.appiconset/appicon1024.png new file mode 100644 index 0000000..f797814 Binary files /dev/null and b/MyLayoutDemo/Images.xcassets/AppIcon.appiconset/appicon1024.png differ diff --git a/MyLayoutDemo/Images.xcassets/friend_zan.imageset/Contents.json b/MyLayoutDemo/Images.xcassets/friend_zan.imageset/Contents.json new file mode 100644 index 0000000..5cca9cd --- /dev/null +++ b/MyLayoutDemo/Images.xcassets/friend_zan.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "friend_zan@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "friend_zan@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/MyLayoutDemo/Images.xcassets/friend_zan.imageset/friend_zan@2x.png b/MyLayoutDemo/Images.xcassets/friend_zan.imageset/friend_zan@2x.png new file mode 100644 index 0000000..4f2fac7 Binary files /dev/null and b/MyLayoutDemo/Images.xcassets/friend_zan.imageset/friend_zan@2x.png differ diff --git a/MyLayoutDemo/Images.xcassets/friend_zan.imageset/friend_zan@3x.png b/MyLayoutDemo/Images.xcassets/friend_zan.imageset/friend_zan@3x.png new file mode 100644 index 0000000..f04859b Binary files /dev/null and b/MyLayoutDemo/Images.xcassets/friend_zan.imageset/friend_zan@3x.png differ diff --git a/MyLayoutDemo/Images.xcassets/interactive_fill.imageset/Contents.json b/MyLayoutDemo/Images.xcassets/interactive_fill.imageset/Contents.json new file mode 100644 index 0000000..e2f135a --- /dev/null +++ b/MyLayoutDemo/Images.xcassets/interactive_fill.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "interactive_fill@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "interactive_fill@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/MyLayoutDemo/Images.xcassets/interactive_fill.imageset/interactive_fill@2x.png b/MyLayoutDemo/Images.xcassets/interactive_fill.imageset/interactive_fill@2x.png new file mode 100644 index 0000000..a00864e Binary files /dev/null and b/MyLayoutDemo/Images.xcassets/interactive_fill.imageset/interactive_fill@2x.png differ diff --git a/MyLayoutDemo/Images.xcassets/interactive_fill.imageset/interactive_fill@3x.png b/MyLayoutDemo/Images.xcassets/interactive_fill.imageset/interactive_fill@3x.png new file mode 100644 index 0000000..1e6b393 Binary files /dev/null and b/MyLayoutDemo/Images.xcassets/interactive_fill.imageset/interactive_fill@3x.png differ diff --git a/MyLayoutDemo/Images.xcassets/zan_select.imageset/Contents.json b/MyLayoutDemo/Images.xcassets/zan_select.imageset/Contents.json new file mode 100644 index 0000000..32604f4 --- /dev/null +++ b/MyLayoutDemo/Images.xcassets/zan_select.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "zan_select@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "zan_select@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/MyLayoutDemo/Images.xcassets/zan_select.imageset/zan_select@2x.png b/MyLayoutDemo/Images.xcassets/zan_select.imageset/zan_select@2x.png new file mode 100644 index 0000000..f4a2a9c Binary files /dev/null and b/MyLayoutDemo/Images.xcassets/zan_select.imageset/zan_select@2x.png differ diff --git a/MyLayoutDemo/Images.xcassets/zan_select.imageset/zan_select@3x.png b/MyLayoutDemo/Images.xcassets/zan_select.imageset/zan_select@3x.png new file mode 100644 index 0000000..921a80f Binary files /dev/null and b/MyLayoutDemo/Images.xcassets/zan_select.imageset/zan_select@3x.png differ diff --git a/MyLayoutDemo/Info.plist b/MyLayoutDemo/Info.plist index 141a517..326dd71 100644 --- a/MyLayoutDemo/Info.plist +++ b/MyLayoutDemo/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.5.0 + 1.8.0 CFBundleSignature ???? CFBundleVersion diff --git a/MyLayoutDemo/LLTest1ViewController.m b/MyLayoutDemo/LLTest1ViewController.m index 6ed3764..8cefd7d 100644 --- a/MyLayoutDemo/LLTest1ViewController.m +++ b/MyLayoutDemo/LLTest1ViewController.m @@ -19,13 +19,12 @@ @implementation LLTest1ViewController -(void)loadView { - /* 使用MyLayout时必读的知识点: - 1.布局视图: 就是从MyBaseLayout派生而出的不同的类型的视图,目前MyLayout中一共有:线性布局、框架布局、相对布局、表格布局、流式布局、浮动布局、路径布局7种布局。 布局视图也是一个视图,因为MyBaseLayout也是从UIView派生的。 - 2.非布局视图: 除上面说的7种布局视图外的所有视图和控件。 + 1.布局视图: 就是从MyBaseLayout派生而出的不同的类型的视图,目前MyLayout中一共有:线性布局、框架布局、相对布局、表格布局、流式布局、浮动布局、路径布局、栅格布局、弹性布局9种布局。 布局视图也是一个视图,因为MyBaseLayout也是从UIView派生的。 + 2.非布局视图: 除上面说的9种布局视图外的所有视图和控件。 3.布局父视图: 如果某个视图的父视图是一个布局视图,那么这个父视图就是布局父视图。 4.非布局父视图:如果某个视图的父视图不是一个布局视图,那么这个父视图就是非布局父视图。 5.布局子视图: 如果某个视图的子视图是一个布局视图,那么这个子视图就是布局子视图。 @@ -46,8 +45,8 @@ -(void)loadView 10.非布局父视图中的非布局子视图: myLeft,myRight,myTop,myBottom,myLeading,myTrailing的设置不会起任何作用,因为MyLayout已经无法控制了。 再次强调的是: - 1. 如果同时设置了左右边距就能决定自己的宽度,同时设置左右间距不能决定自己的宽度! - 2. 如果同时设置了上下边距就能决定自己的高度,同时设置上下间距不能决定自己的高度! + 1. 如果同时设置了左右边距并且在尺寸不是自适应时就能决定自己的宽度,同时设置左右间距不能决定自己的宽度! + 2. 如果同时设置了上下边距并且在尺寸不是自适应时就能决定自己的高度,同时设置上下间距不能决定自己的高度! */ @@ -102,7 +101,7 @@ -(void)loadView - MyLinearLayout *vertLayout = [self createVertSubviewLayout];//垂直线性布局的高度默认是由子视图的高度决定的,也就是wrapContentHeight默认设置为YES + MyLinearLayout *vertLayout = [self createVertSubviewLayout];//垂直线性布局的高度默认是由子视图的高度决定的,也就是高度自适应 vertLayout.myHorzMargin = 0; //对于垂直线性布局rootLayout的子视图vertLayout来说,如果同时设置了左右边距为0则表示子视图的宽度和父视图宽度相等。 [rootLayout addSubview:vertLayout]; @@ -123,9 +122,25 @@ -(void)loadView - (void)viewDidLoad { [super viewDidLoad]; + + UIBarButtonItem *RTLBar = [[UIBarButtonItem alloc]initWithTitle:@"RTL" style:UIBarButtonItemStylePlain target:self action:@selector(RTLAction)]; + UIBarButtonItem *LTRBar = [[UIBarButtonItem alloc]initWithTitle:@"LTR" style:UIBarButtonItemStylePlain target:self action:@selector(LTRAction)]; + self.navigationItem.rightBarButtonItems = @[RTLBar,LTRBar]; // Do any additional setup after loading the view. } +// RTL- UI - 更新 +- (void)RTLAction +{ + // 当前view的window + [MyBaseLayout updateRTL:YES inWindow:self.view.window]; +} +- (void)LTRAction +{ + // 这个适合APP 使用appdelegate 中的window + [MyBaseLayout updateRTL:NO inWindow:[UIApplication sharedApplication].delegate.window]; +} + - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. diff --git a/MyLayoutDemo/LLTest2ViewController.m b/MyLayoutDemo/LLTest2ViewController.m index 4d01695..1cbedb9 100644 --- a/MyLayoutDemo/LLTest2ViewController.m +++ b/MyLayoutDemo/LLTest2ViewController.m @@ -24,8 +24,8 @@ @implementation LLTest2ViewController -(void)loadView { /* - 本例子用来实现将一个布局视图嵌入到一个UIScrollView里面的功能。 - 我们可以把一个布局视图作为一个子视图加入到UIScrollView中,布局库内部会根据布局视图的尺寸自动调整UIScrollView的contentSize。如果您不想调整contentSize则请将布局视图的adjustScrollViewContentSizeMode属性设置为MyAdjustScrollViewContentSizeModeNo。 + 本例子用来实现将一个布局视图嵌入到一个UIScrollView里面的功能。 + 我们可以把一个布局视图作为一个子视图加入到UIScrollView中,布局库内部会根据布局视图的尺寸自动调整UIScrollView的contentSize。如果您不想调整contentSize则请将布局视图的adjustScrollViewContentSizeMode属性设置为MyAdjustScrollViewContentSizeModeNo。 */ UIScrollView *scrollView = [UIScrollView new]; @@ -33,67 +33,67 @@ -(void)loadView self.view = scrollView; /* - 这里的contentLayout是非布局视图UIScrollView的子视图。因为同时设置了myHorzMargin为0表示宽度和UIScrollView是保持一致;而高度则因为垂直线性布局的wrapContentHeight属性设置来确定,表示垂直线性布局的高度等于里面的所有子视图高度;而其中的x,y轴的位置则因为没有设置默认是0。 + 这里的contentLayout是非布局视图UIScrollView的子视图。因为同时设置了myHorzMargin为0表示宽度和UIScrollView是保持一致;而高度则因为垂直线性布局的高度默认是自适应,表示垂直线性布局的高度等于里面的所有子视图高度;而其中的x,y轴的位置则因为没有设置默认是0。 */ MyLinearLayout *contentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; contentLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); //设置布局内的子视图离自己的边距. contentLayout.myHorzMargin = 0; //同时指定左右边距为0表示宽度和父视图一样宽 - contentLayout.heightSize.lBound(scrollView.heightSize, 10, 1); //高度虽然是wrapContentHeight的。但是最小的高度不能低于父视图的高度加10. + contentLayout.heightSize.lBound(scrollView.heightSize, 10, 1); //高度虽然是自适应的。但是最小的高度不能低于父视图的高度加10. [scrollView addSubview:contentLayout]; self.contentLayout = contentLayout; /* - 布局视图里面的padding属性用来设置布局视图的内边距。内边距是指布局视图里面的子视图离自己距离。外边距则是视图与父视图之间的距离。 - 内边距是在自己的尺寸内离子视图的距离,而外边距则不是自己尺寸内离其他视图的距离。下面是内边距和外边距的效果图: + 布局视图里面的padding属性用来设置布局视图的内边距。内边距是指布局视图里面的子视图离自己距离。外边距则是视图与父视图之间的距离。 + 内边距是在自己的尺寸内离子视图的距离,而外边距则不是自己尺寸内离其他视图的距离。下面是内边距和外边距的效果图: - ^ - | topMargin - | width - +------------------------------+ - | |------------> - | l r | rightMargin - | e topPadding i | - | f g | - | t +---------------+ h | -<----------| P | | t | - leftMargin| a | | P | - | d | subviews | a | height - | d | content | d | - | i | | d | - | n | | i | - | g +---------------+ n | - | g | - | bottomPadding | - +------------------------------+ - |bottomMargin - | - V + ^ + | margin-top + | width + +------------------------------+ + | |------------> + | p p | margin-right + | a padding-top a | + | d d | + | d +---------------+ d | + <----------| i | | i | + margin-left| n | | n | + | g | subview's | g | height + | | | content | | | + | l | | r | + | e | | i | + | f +---------------+ g | + | t h | + | padding-bottom t | + +------------------------------+ + |margin-bottom + | + V 如果一个布局视图中的每个子视图都离自己有一定的距离就可以通过设置布局视图的内边距来实现,而不需要为每个子视图都设置外边距。 */ - + //垂直线性布局直接添加子视图 [self createSection1:contentLayout]; //垂直线性布局套水平线性布局 [self createSection2:contentLayout]; - + //垂直线性布局套垂直线性布局 [self createSection3:contentLayout]; - + //垂直线性布局套水平线性布局 [self createSection4:contentLayout]; - + //垂直线性布局套水平线性布局,水平线性布局利用相对边距实现左右布局 [self createSection5:contentLayout]; //水平线性布局中的基线对齐 [self createSection6:contentLayout]; - + //对子视图的高度的缩放调整 [self createSection7:contentLayout]; @@ -142,7 +142,7 @@ -(void)createSection1:(MyLinearLayout*)contentLayout -(void)createSection2:(MyLinearLayout*)contentLayout { - //userInfoLayout的myHorzMargin确定了视图的x轴的位置和宽度;wrapContentHeight为YES确定了视图的高度;myTop确定了y轴上离兄弟视图间距20 + //userInfoLayout的myHorzMargin确定了视图的x轴的位置和宽度;myHeight设置为MyLayoutSize.wrap表示高度自适应;myTop确定了y轴上离兄弟视图间距20 MyLinearLayout *userInfoLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; userInfoLayout.layer.borderColor = [UIColor lightGrayColor].CGColor; userInfoLayout.layer.borderWidth = 0.5; @@ -150,15 +150,15 @@ -(void)createSection2:(MyLinearLayout*)contentLayout userInfoLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); userInfoLayout.myTop = 20; userInfoLayout.myHorzMargin = 0; - userInfoLayout.wrapContentHeight = YES; + userInfoLayout.myHeight = MyLayoutSize.wrap; [contentLayout addSubview:userInfoLayout]; //headImageView的sizeToFit确定了视图的尺寸;myCenterY确定了在y轴垂直居中;水平线性布局下的子视图可以自动算出在x轴的位置。 UIImageView *headImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"head1"]]; headImageView.myCenterY = 0; [userInfoLayout addSubview:headImageView]; - - //nameLayout是垂直线性布局因此默认的wrapContentHeight确定了视图的高度;weight=1设置宽度比重值,表示占用父布局infoLayout的剩余宽度;y轴上默认和父布局上边对齐;x轴则根据其在父布局下的顺序自动算出。这个部分也是一个水平线性布局套垂直线性布局的场景。 + + //nameLayout是垂直线性布局因此默认高度自适应;weight=1设置宽度比重值,表示占用父布局infoLayout的剩余宽度;y轴上默认和父布局上边对齐;x轴则根据其在父布局下的顺序自动算出。这个部分也是一个水平线性布局套垂直线性布局的场景。 MyLinearLayout *nameLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; nameLayout.weight = 1.0; nameLayout.myLeading = 10; @@ -170,7 +170,7 @@ -(void)createSection2:(MyLinearLayout*)contentLayout userNameLabel.font = [CFTool font:15]; [userNameLabel sizeToFit]; [nameLayout addSubview:userNameLabel]; - + //nickNameLabel的sizeToFit确定视图的尺寸;x轴位置和父布局左对齐;y轴位置由添加到父布局中的顺序决定。 UILabel *nickNameLabel = [UILabel new]; nickNameLabel.text = NSLocalizedString(@"Nickname:醉里挑灯看键", @""); @@ -183,41 +183,41 @@ -(void)createSection2:(MyLinearLayout*)contentLayout //使用线性布局实现这个功能的一个缺点就是必须使用线性布局嵌套线性布局来完成,这样嵌套层次就可能会比较多,因此您可以尝试改用流式布局局来实现这个功能,从而减少嵌套的问题 /* - MyFlowLayout *userInfoLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:2]; - userInfoLayout.layer.borderColor = [UIColor lightGrayColor].CGColor; - userInfoLayout.layer.borderWidth = 0.5; - userInfoLayout.layer.cornerRadius = 4; - userInfoLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - userInfoLayout.myTop = 20; - userInfoLayout.myLeading = userInfoLayout.myTrailing = 0; - userInfoLayout.subviewHSpace = 10; //子视图的水平间距为10 - userInfoLayout.wrapContentHeight = YES; - userInfoLayout.gravity = MyGravity_Vert_Center; //里面的子视图整体垂直居中。 - [contentLayout addSubview:userInfoLayout]; - - //第一列: 一个头像视图,一个占位视图。 - UIImageView *headImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"head1"]]; - [userInfoLayout addSubview:headImageView]; - - //因为数量约束水平流式布局每列必须要2个所以这里建立一个占位视图填满第一列。 - UIView *placeHolderView = [UIView new]; - [userInfoLayout addSubview:placeHolderView]; - - - //第二列: 姓名视图,昵称视图。 - UILabel *userNameLabel = [UILabel new]; - userNameLabel.text = NSLocalizedString(@"Name:欧阳大哥", @""); - userNameLabel.font = [CFTool font:15]; - [userNameLabel sizeToFit]; - [userInfoLayout addSubview:userNameLabel]; - - UILabel *nickNameLabel = [UILabel new]; - nickNameLabel.text = NSLocalizedString(@"Nickname:醉里挑灯看键", @""); - nickNameLabel.textColor = [CFTool color:4]; - nickNameLabel.font = [CFTool font:14]; - [nickNameLabel sizeToFit]; - [userInfoLayout addSubview:nickNameLabel]; - */ + MyFlowLayout *userInfoLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:2]; + userInfoLayout.layer.borderColor = [UIColor lightGrayColor].CGColor; + userInfoLayout.layer.borderWidth = 0.5; + userInfoLayout.layer.cornerRadius = 4; + userInfoLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); + userInfoLayout.myTop = 20; + userInfoLayout.myLeading = userInfoLayout.myTrailing = 0; + userInfoLayout.subviewHSpace = 10; //子视图的水平间距为10 + userInfoLayout.myHeight = MyLayoutSize.wrap; + userInfoLayout.gravity = MyGravity_Vert_Center; //里面的子视图整体垂直居中。 + [contentLayout addSubview:userInfoLayout]; + + //第一列: 一个头像视图,一个占位视图。 + UIImageView *headImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"head1"]]; + [userInfoLayout addSubview:headImageView]; + + //因为数量约束水平流式布局每列必须要2个所以这里建立一个占位视图填满第一列。 + UIView *placeHolderView = [UIView new]; + [userInfoLayout addSubview:placeHolderView]; + + + //第二列: 姓名视图,昵称视图。 + UILabel *userNameLabel = [UILabel new]; + userNameLabel.text = NSLocalizedString(@"Name:欧阳大哥", @""); + userNameLabel.font = [CFTool font:15]; + [userNameLabel sizeToFit]; + [userInfoLayout addSubview:userNameLabel]; + + UILabel *nickNameLabel = [UILabel new]; + nickNameLabel.text = NSLocalizedString(@"Nickname:醉里挑灯看键", @""); + nickNameLabel.textColor = [CFTool color:4]; + nickNameLabel.font = [CFTool font:14]; + [nickNameLabel sizeToFit]; + [userInfoLayout addSubview:nickNameLabel]; + */ } @@ -225,7 +225,7 @@ -(void)createSection2:(MyLinearLayout*)contentLayout -(void)createSection3:(MyLinearLayout*)contentLayout { - //ageLayout是垂直线性布局默认的wrapContentHeight决定了视图的高度;myHorzMargin决定了视图的x轴的位置和宽度;添加到垂直布局父视图的顺序决定了y轴的位置。 + //ageLayout是垂直线性布局默认高度自适应;myHorzMargin决定了视图的x轴的位置和宽度;添加到垂直布局父视图的顺序决定了y轴的位置。 MyLinearLayout *ageLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; ageLayout.layer.borderColor = [UIColor lightGrayColor].CGColor; ageLayout.layer.borderWidth = 0.5; @@ -246,7 +246,7 @@ -(void)createSection3:(MyLinearLayout*)contentLayout //垂直线性布局套水平线性布局 MyLinearLayout *ageSelectLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; ageSelectLayout.myTop = 5; - ageSelectLayout.wrapContentHeight = YES; + ageSelectLayout.myHeight = MyLayoutSize.wrap; ageSelectLayout.subviewHSpace = 10; //里面所有子视图之间的水平间距。 [ageLayout addSubview:ageSelectLayout]; @@ -268,43 +268,43 @@ -(void)createSection3:(MyLinearLayout*)contentLayout //为实现这个功能,线性布局需要2层嵌套来完成,这无疑增加了代码量,因此您可以改为用一个垂直浮动布局来实现相同的能力。 - /* MyFloatLayout *ageLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; - ageLayout.layer.borderColor = [UIColor lightGrayColor].CGColor; - ageLayout.layer.borderWidth = 0.5; - ageLayout.layer.cornerRadius = 4; - ageLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - ageLayout.myTop = 20; - ageLayout.myLeading = ageLayout.myTrailing = 0; // 宽度和父布局相等 - ageLayout.wrapContentHeight = YES; //高度由子视图包裹。 - ageLayout.subviewVSpace = 5; //所有子视图垂直间距为5 - ageLayout.subviewHSpace = 10; //所有子视图水平间距为10 - [contentLayout addSubview:ageLayout]; - - UILabel *ageTitleLabel = [UILabel new]; - ageTitleLabel.text = NSLocalizedString(@"Age:", @""); - ageTitleLabel.font = [CFTool font:15]; - [ageTitleLabel sizeToFit]; - ageTitleLabel.widthSize.equalTo(ageLayout.widthSize); - [ageLayout addSubview:ageTitleLabel]; - - - for (int i = 0; i < 3; i++) - { - UILabel *ageLabel = [UILabel new]; - ageLabel.text = [NSString stringWithFormat:@"%d", (i+2)*10]; - ageLabel.textAlignment = NSTextAlignmentCenter; - ageLabel.layer.cornerRadius = 15; - ageLabel.layer.borderColor = [CFTool color:3].CGColor; - ageLabel.layer.borderWidth = 0.5; - ageLabel.font = [CFTool font:13]; - ageLabel.heightSize.equalTo(@30); - //宽度这样设置的原因是:3个子视图要平分布局视图的宽度,这里每个子视图的间距是10。 - //因此每个子视图的宽度 = (布局视图宽度 - 2 * 子视图间距)/3 = 布局视图宽度 * 1/3 - 2*子视图间距/3 - //MyLayoutSize中的equalTo方法设置布局宽度,multiply方法用来设置1/3,add方法用来设置2*子视图间距/3. 因此可以进行如下设置: - ageLabel.widthSize.equalTo(ageLayout.widthSize).multiply(1.0/3).add(-1 * 2 * ageLayout.subviewHSpace / 3); - [ageLayout addSubview:ageLabel]; - } - */ + /* MyFloatLayout *ageLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; + ageLayout.layer.borderColor = [UIColor lightGrayColor].CGColor; + ageLayout.layer.borderWidth = 0.5; + ageLayout.layer.cornerRadius = 4; + ageLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); + ageLayout.myTop = 20; + ageLayout.myLeading = ageLayout.myTrailing = 0; // 宽度和父布局相等 + ageLayout.myHeight = MyLayoutSize.wrap; //高度由子视图包裹。 + ageLayout.subviewVSpace = 5; //所有子视图垂直间距为5 + ageLayout.subviewHSpace = 10; //所有子视图水平间距为10 + [contentLayout addSubview:ageLayout]; + + UILabel *ageTitleLabel = [UILabel new]; + ageTitleLabel.text = NSLocalizedString(@"Age:", @""); + ageTitleLabel.font = [CFTool font:15]; + [ageTitleLabel sizeToFit]; + ageTitleLabel.widthSize.equalTo(ageLayout.widthSize); + [ageLayout addSubview:ageTitleLabel]; + + + for (int i = 0; i < 3; i++) + { + UILabel *ageLabel = [UILabel new]; + ageLabel.text = [NSString stringWithFormat:@"%d", (i+2)*10]; + ageLabel.textAlignment = NSTextAlignmentCenter; + ageLabel.layer.cornerRadius = 15; + ageLabel.layer.borderColor = [CFTool color:3].CGColor; + ageLabel.layer.borderWidth = 0.5; + ageLabel.font = [CFTool font:13]; + ageLabel.heightSize.equalTo(@30); + //宽度这样设置的原因是:3个子视图要平分布局视图的宽度,这里每个子视图的间距是10。 + //因此每个子视图的宽度 = (布局视图宽度 - 2 * 子视图间距)/3 = 布局视图宽度 * 1/3 - 2*子视图间距/3 + //MyLayoutSize中的equalTo方法设置布局宽度,multiply方法用来设置1/3,add方法用来设置2*子视图间距/3. 因此可以进行如下设置: + ageLabel.widthSize.equalTo(ageLayout.widthSize).multiply(1.0/3).add(-1 * 2 * ageLayout.subviewHSpace / 3); + [ageLayout addSubview:ageLabel]; + } + */ } @@ -319,7 +319,7 @@ -(void)createSection4:(MyLinearLayout*)contentLayout addressLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); addressLayout.myTop = 20; addressLayout.myLeading = addressLayout.myTrailing = 0; - addressLayout.wrapContentHeight = YES; + addressLayout.myHeight = MyLayoutSize.wrap; [contentLayout addSubview:addressLayout]; @@ -330,14 +330,14 @@ -(void)createSection4:(MyLinearLayout*)contentLayout [addressLayout addSubview:addressTitleLabel]; - //addressLabel的y轴位置和父布局视图上边对齐;x轴的位置则根据添加到父布局的顺序确定;视图的宽度由weight=1表示占用父视图的剩余宽度决定;视图的高度由wrapContentHeight设置为YES表示高度由内容动态决定。 + //addressLabel的y轴位置和父布局视图上边对齐;x轴的位置则根据添加到父布局的顺序确定;视图的宽度由weight=1表示占用父视图的剩余宽度决定;视图的高度根据内容自适应。 UILabel *addressLabel = [UILabel new]; addressLabel.text = NSLocalizedString(@"Winterless Building, West Dawang Road, Chaoyang district CBD, Beijing, People's Republic of China", @""); addressLabel.textColor = [CFTool color:4]; addressLabel.font = [CFTool font:14]; addressLabel.myLeading = 10; addressLabel.weight = 1.0; - addressLabel.wrapContentHeight = YES; //这个属性设置为YES表示视图的高度动态确定。设置这个属性的前提是必须有指定视图的宽度. + addressLabel.myHeight = MyLayoutSize.wrap; //这个属性设置为YES表示视图的高度动态确定。设置这个属性的前提是必须有指定视图的宽度. [addressLayout addSubview:addressLabel]; } @@ -351,7 +351,7 @@ -(void)createSection5:(MyLinearLayout*)contentLayout sexLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); sexLayout.myTop = 20; sexLayout.myLeading = sexLayout.myTrailing = 0; - sexLayout.wrapContentHeight = YES; + sexLayout.myHeight = MyLayoutSize.wrap; [contentLayout addSubview:sexLayout]; @@ -367,7 +367,7 @@ -(void)createSection5:(MyLinearLayout*)contentLayout UISwitch *sexSwitch = [UISwitch new]; sexSwitch.myLeading = 0.5; //线性布局中的子视图的边距如果设置为大于0小于1的值表示的是相对间距,0.5的左边距表示左边是父布局剩余空间的50% [sexLayout addSubview:sexSwitch]; - + } @@ -392,15 +392,15 @@ -(void)createSection6:(MyLinearLayout*)contentLayout UILabel *baselineLabel = [UILabel new]; baselineLabel.text = @"Baseline view"; - baselineLabel.wrapContentSize = YES; + baselineLabel.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); baselineLabel.font = [CFTool font:20]; baselineLabel.backgroundColor = [CFTool color:5]; - baselineLabel.myAlignment = MyGravity_Vert_Center; //标准视图垂直居中。 + baselineLabel.alignment = MyGravity_Vert_Center; //标准视图垂直居中。 [baselineLayout addSubview:baselineLabel]; UILabel *rightLabel = [UILabel new]; rightLabel.text = @"Right view"; - rightLabel.wrapContentSize = YES; + rightLabel.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); rightLabel.font = [CFTool font:32]; rightLabel.backgroundColor = [CFTool color:6]; [baselineLayout addSubview:rightLabel]; @@ -413,7 +413,7 @@ -(void)createSection6:(MyLinearLayout*)contentLayout -(void)createSection7:(MyLinearLayout*)contentLayout { UILabel *shrinkLabel = [UILabel new]; - shrinkLabel.text = NSLocalizedString(@"This is a can automatically wrap text.To realize this function, you need to set the clear width, and set the wrapContentHeight to YES.You can try to switch different simulator or different orientation screen to see the effect.", @""); + shrinkLabel.text = NSLocalizedString(@"This is a can automatically wrap text.To realize this function, you need to set the width exact, and set the heightSize to MyLayoutSize.wrap.You can try to switch different simulator or different orientation screen to see the effect.", @""); shrinkLabel.backgroundColor = [CFTool color:2]; shrinkLabel.font = [CFTool font:14]; shrinkLabel.myTop = 20; @@ -421,8 +421,7 @@ -(void)createSection7:(MyLinearLayout*)contentLayout //下面四个属性配合一起简单的实现文本的收起和展开。 shrinkLabel.clipsToBounds = YES; //为了实现文本可缩放,需要将这个标志设置为YES,否则效果无法实现。但要慎重使用这个标志,因为如果设置YES的话会影响性能。 - shrinkLabel.myHeight = 0; //这里设置高度为0,而下面设置wrapContentHeight为YES的优先级比较高。所以当wrapContentHeight = NO时这个高度才起作用。这两个属性搭配使用非常容易实现UILabel的收起和展开 - shrinkLabel.wrapContentHeight = YES; //这个属性会控制在固定宽度下自动调整视图的高度。 + shrinkLabel.myHeight = MyLayoutSize.wrap; [contentLayout addSubview:shrinkLabel]; self.shrinkLabel = shrinkLabel; @@ -451,47 +450,44 @@ -(void)createSection8:(MyLinearLayout*)contentLayout UIView *hiddenView = [UIView new]; hiddenView.backgroundColor = [CFTool color:3]; - hiddenView.myVisibility = MyVisibility_Gone; + hiddenView.visibility = MyVisibility_Gone; hiddenView.myTop = 20; hiddenView.myLeading = hiddenView.myTrailing = 0; hiddenView.myHeight = 800; [contentLayout addSubview:hiddenView]; self.hiddenView = hiddenView; - + } - - #pragma mark -- Handle Method -(void)handleLabelShrink:(UIButton*)sender { - //因为self.shrinkLabel设置了wrapContentHeight来实现动态的文本高度。因此这里可以通过这个标志来实现文本伸缩功能。 - if (self.shrinkLabel.wrapContentHeight) + //通过设置文本的高度在自适应和0之间切换来实现文本的伸缩显示功能。 + if (self.shrinkLabel.myHeight == MyLayoutSize.wrap) { - self.shrinkLabel.wrapContentHeight = NO; //当设置为NO时,视图的myHeight将起作用,这边高度就变为了0 + self.shrinkLabel.myHeight = 0; //当设置为NO时,视图的myHeight将起作用,这边高度就变为了0 } else { - self.shrinkLabel.wrapContentHeight = YES; //当设置为YES时,视图的myHeight将不起作用,这样高度就由内容包裹。 + self.shrinkLabel.myHeight = MyLayoutSize.wrap; //当设置为YES时,视图的myHeight将不起作用,这样高度就由内容包裹。 } [self.contentLayout layoutAnimationWithDuration:0.3]; - } -(void)handleHideAndShowMore:(UIButton*)sender { - if (self.hiddenView.myVisibility == MyVisibility_Visible) + if (self.hiddenView.visibility == MyVisibility_Visible) { - self.hiddenView.myVisibility = MyVisibility_Gone; + self.hiddenView.visibility = MyVisibility_Gone; [sender setTitle:NSLocalizedString(@"Show more》", @"") forState:UIControlStateNormal]; [sender sizeToFit]; } else { - self.hiddenView.myVisibility = MyVisibility_Visible; + self.hiddenView.visibility = MyVisibility_Visible; [sender setTitle:NSLocalizedString(@"Close up《", @"") forState:UIControlStateNormal]; [sender sizeToFit]; } @@ -501,13 +497,13 @@ -(void)handleHideAndShowMore:(UIButton*)sender /* -#pragma mark - Navigation - -// In a storyboard-based application, you will often want to do a little preparation before navigation -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. -} -*/ + #pragma mark - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. + } + */ @end diff --git a/MyLayoutDemo/LLTest3ViewController.m b/MyLayoutDemo/LLTest3ViewController.m index 49b78f6..83eb942 100644 --- a/MyLayoutDemo/LLTest3ViewController.m +++ b/MyLayoutDemo/LLTest3ViewController.m @@ -32,12 +32,15 @@ -(void)loadView 3.假设某个垂直线性布局有A,B,C三个子视图,并且我们希望这三个子视图整体垂直居中。第一个方法是为设置A的myTop=0.5和设置C的myBottom=0.5;第二个方法是只需要设置布局视图的gravity的值为MyGravity_Vert_Center就可以实现了。 4.假设某个垂直线性布局有A,B,C三个子视图,我们希望这三个子视图整体居中。我们就只需要将布局视图的gravity值设置为MyGravity_Center就可以了。 5.假设某个垂直线性布局有A,B,C三个子视图,我们希望这三个子视图的宽度都和布局视图一样宽。第一个方法是分别为每个子视图的myLeft和myRight设置为0;第二个方法是只需要设置布局视图的gravity的值为MyGravity_Horz_Fill就可以了。 + 6.假设某个垂直线性布局有A,B,C三个子视图,我们希望这三个子视图高度不变但是垂直间距填充满整个布局视图。解决的方法是将布局视图的gravity的值设置为MyGravity_Vert_Between就可以实现了。 + 7.假设某个垂直线性布局有A,B,C三个子视图,我们希望这三个子视图高度不变但是垂直间距填充满整个布局视图,并且第一个子视图的上间距和最后一个子视图的下间距是间距的一半。解决的方法是将布局视图的gravity的值设置为MyGravity_Vert_Around就可以实现了。 + 通过上面的几个场景我们可以看出gravity属性的设置可以在很大程度上简化布局视图里面的子视图的布局属性的设置的,通过gravity属性的设置我们可以控制布局视图里面所有子视图的整体停靠方向和填充的尺寸。在使用gravity属性时需要明确如下几个条件: 1.当使用gravity属性时意味着布局视图必须要有明确的尺寸才有意义,因为有确定的尺寸才能决定里面的子视图的停靠的方位。 - 2.布局视图的wrapContentHeight设置为YES时是和gravity上设置垂直停靠方向以及垂直填充是互斥的;而布局视图的wrapContentWidth设置为YES时是和gravity上设置水平停靠方向和水平填充是互斥的。 - 3.布局视图的gravity的属性的优先级要高于子视图的停靠和尺寸设置。 + 2.布局视图的高度自适应设置是和gravity上设置垂直停靠方向以及垂直填充是互斥的;而布局视图的宽度自适应是和gravity上设置水平停靠方向和水平填充是互斥的。(也有特殊情况就是布局视图设置了最小尺寸限制时才有效) + 3.布局视图的gravity的属性的优先级要高于子视图的边距和尺寸设置,但是低于子视图的alignment属性的设置。 */ @@ -105,14 +108,14 @@ -(void)createVertLayoutGravityActionLayout:(MyLinearLayout*)contentLayout actionTitleLabel.text = NSLocalizedString(@"Vertical layout gravity control, you can click the following different button to show the effect:", @""); actionTitleLabel.textColor = [CFTool color:4]; actionTitleLabel.adjustsFontSizeToFitWidth = YES; - actionTitleLabel.wrapContentHeight = YES; + actionTitleLabel.myHeight = MyLayoutSize.wrap; actionTitleLabel.myTop = 10; [contentLayout addSubview:actionTitleLabel]; //用流式布局来创建动作菜单布局。流式布局请参考后面关于流式布局的DEMO。 MyFlowLayout *actionLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3]; - actionLayout.wrapContentHeight = YES; + actionLayout.myHeight = MyLayoutSize.wrap; actionLayout.gravity = MyGravity_Horz_Fill; //平均分配里面所有子视图的宽度 actionLayout.subviewHSpace = 5; actionLayout.subviewVSpace = 5; //设置里面子视图的水平和垂直间距。 @@ -129,8 +132,12 @@ -(void)createVertLayoutGravityActionLayout:(MyLinearLayout*)contentLayout NSLocalizedString(@"screen vert center",@""), NSLocalizedString(@"screen horz center",@""), NSLocalizedString(@"between",@""), + NSLocalizedString(@"around",@""), + NSLocalizedString(@"among",@""), NSLocalizedString(@"horz fill",@""), - NSLocalizedString(@"vert fill", @"")]; + NSLocalizedString(@"vert fill", @""), + NSLocalizedString(@"horz stretch", @""), + NSLocalizedString(@"vert stretch", @"")]; for (NSInteger i = 0; i < actions.count; i++) { @@ -153,7 +160,6 @@ -(void)createVertGravityLayout:(MyLinearLayout*)contentLayout MyLinearLayout *vertGravityLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; - vertGravityLayout.wrapContentHeight = NO; vertGravityLayout.backgroundColor = [CFTool color:0]; vertGravityLayout.myHeight = 160; vertGravityLayout.myTop = 10; @@ -169,7 +175,7 @@ -(void)createVertGravityLayout:(MyLinearLayout*)contentLayout UILabel *v2 = [self createLabel:NSLocalizedString(@"always alignment to left", @"") backgroundColor:[CFTool color:6]]; v2.myHeight = 20; - v2.myAlignment = MyGravity_Horz_Left; //对于垂直布局里面的子视图可以通过这个属性来设置水平对齐的方位,这样即使布局视图设置了gravity属性,这个视图的对齐都不会受到影响。 + v2.alignment = MyGravity_Horz_Left; //对于垂直布局里面的子视图可以通过这个属性来设置水平对齐的方位,这样即使布局视图设置了gravity属性,这个视图的对齐都不会受到影响。 [self.vertGravityLayout addSubview:v2]; UILabel *v3 = [self createLabel:NSLocalizedString(@"test text3 test text3 test text3", @"") backgroundColor:[CFTool color:7]]; @@ -194,14 +200,14 @@ -(void)createHorzLayoutGravityActionLayout:(MyLinearLayout*)contentLayout actionTitleLabel.textColor = [CFTool color:4]; actionTitleLabel.text = NSLocalizedString(@"Horizontal layout gravity control, you can click the following different button to show the effect:", @""); actionTitleLabel.adjustsFontSizeToFitWidth = YES; - actionTitleLabel.wrapContentHeight = YES; + actionTitleLabel.myHeight = MyLayoutSize.wrap; actionTitleLabel.myTop = 10; [contentLayout addSubview:actionTitleLabel]; //用流式布局来创建动作菜单布局。流式布局请参考后面关于流式布局的DEMO。 MyFlowLayout *actionLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3]; - actionLayout.wrapContentHeight = YES; + actionLayout.myHeight = MyLayoutSize.wrap; actionLayout.gravity = MyGravity_Horz_Fill; //平均分配里面所有子视图的宽度 actionLayout.subviewHSpace = 5; actionLayout.subviewVSpace = 5; //设置里面子视图的水平和垂直间距。 @@ -217,8 +223,12 @@ -(void)createHorzLayoutGravityActionLayout:(MyLinearLayout*)contentLayout NSLocalizedString(@"screen vert center",@""), NSLocalizedString(@"screen horz center",@""), NSLocalizedString(@"between",@""), + NSLocalizedString(@"around",@""), + NSLocalizedString(@"among",@""), NSLocalizedString(@"horz fill",@""), - NSLocalizedString(@"vert fill", @"")]; + NSLocalizedString(@"vert fill", @""), + NSLocalizedString(@"horz stretch",@""), + NSLocalizedString(@"vert stretch", @"")]; for (NSInteger i = 0; i < actions.count; i++) { @@ -241,7 +251,6 @@ -(void)createHorzGravityLayout:(MyLinearLayout*)contentLayout MyLinearLayout *horzGravityLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; horzGravityLayout.backgroundColor = [CFTool color:0]; - horzGravityLayout.wrapContentWidth = NO; //因为默认水平线性布局的宽度由子视图包裹,但这里的宽度由父布局决定的,所有这里必须设置为NO。 horzGravityLayout.myHeight = 200; horzGravityLayout.myTop = 10; horzGravityLayout.myLeading = 20; @@ -250,24 +259,24 @@ -(void)createHorzGravityLayout:(MyLinearLayout*)contentLayout self.horzGravityLayout = horzGravityLayout; UILabel *v1 = [self createLabel:NSLocalizedString(@"test text1", @"") backgroundColor:[CFTool color:5]]; - v1.wrapContentHeight = YES; + v1.myHeight = MyLayoutSize.wrap; v1.myWidth = 60; [self.horzGravityLayout addSubview:v1]; UILabel *v2 = [self createLabel:NSLocalizedString(@"always alignment to top", @"") backgroundColor:[CFTool color:6]]; - v2.wrapContentHeight = YES; + v2.myHeight = MyLayoutSize.wrap; v2.myWidth = 60; - v2.myAlignment = MyGravity_Vert_Top; //对于水平布局里面的子视图可以通过这个属性来设置垂直对齐的方位,这样即使布局视图设置了gravity属性,这个视图的对齐都不会受到影响。 + v2.alignment = MyGravity_Vert_Top; //对于水平布局里面的子视图可以通过这个属性来设置垂直对齐的方位,这样即使布局视图设置了gravity属性,这个视图的对齐都不会受到影响。 [self.horzGravityLayout addSubview:v2]; UILabel *v3 = [self createLabel:NSLocalizedString(@"test text3 test text3 test text3", @"") backgroundColor:[CFTool color:7]]; - v3.wrapContentHeight = YES; + v3.myHeight = MyLayoutSize.wrap; v3.myWidth = 60; [self.horzGravityLayout addSubview:v3]; UILabel *v4 = [self createLabel:NSLocalizedString(@"set top and bottom margin to determine height", @"") backgroundColor:[CFTool color:8]]; - v4.wrapContentHeight = YES; + v4.numberOfLines = 0; v4.myTop = 20; v4.myBottom = 10; v4.myWidth = 60; @@ -329,12 +338,24 @@ -(void)handleVertLayoutGravity:(UIButton*)button case 9: //垂直间距拉伸 vertGravity = MyGravity_Vert_Between; break; - case 10: //水平填充 + case 10: //垂直间距环绕 + vertGravity = MyGravity_Vert_Around; + break; + case 11: //垂直间距等分 + vertGravity = MyGravity_Vert_Among; + break; + case 12: //水平填充 horzGravity = MyGravity_Horz_Fill; break; - case 11: //垂直填充 + case 13: //垂直填充 vertGravity = MyGravity_Vert_Fill; //这里模拟器顶部出现黑线,真机是不会出现的。。 break; + case 14: //水平拉伸 + horzGravity = MyGravity_Horz_Stretch; + break; + case 15: //垂直拉伸 + vertGravity = MyGravity_Vert_Stretch; //这里模拟器顶部出现黑线,真机是不会出现的。。 + break; default: break; } @@ -382,12 +403,24 @@ -(void)handleHorzLayoutGravity:(UIButton*)button case 9: //水平间距拉伸 horzGravity = MyGravity_Horz_Between; break; - case 10: //水平填充 + case 10: //水平间距环绕 + horzGravity = MyGravity_Horz_Around; + break; + case 11: //水平间距等分 + horzGravity = MyGravity_Horz_Among; + break; + case 12: //水平填充 horzGravity = MyGravity_Horz_Fill; break; - case 11: + case 13: vertGravity = MyGravity_Vert_Fill; break; + case 14: //水平拉伸 + horzGravity = MyGravity_Horz_Stretch; + break; + case 15: //垂直拉伸 + vertGravity = MyGravity_Vert_Stretch; + break; default: break; } @@ -405,8 +438,7 @@ -(void)handleHorzLayoutGravity:(UIButton*)button -(void)handleNavigationTitleCentre:(id)sender { MyLinearLayout *navigationItemLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; - navigationItemLayout.wrapContentHeight = NO; - navigationItemLayout.wrapContentWidth = NO; //将线性布局放入navigationItem.titleView需要把包裹属性设置为NO。 + navigationItemLayout.heightSize.equalTo(nil); //将布局放入navigationItem的titleView时不要设置高度约束 //通过MyGravity_Horz_Window_Center的设置总是保证在窗口的中间而不是布局视图的中间。 navigationItemLayout.gravity = MyGravity_Horz_Window_Center | MyGravity_Vert_Center; @@ -467,9 +499,18 @@ -(NSString*)gravityText:(MyGravity)gravity prefixText:(NSString*)prefixText case MyGravity_Vert_Fill: vertGravityStr = @"MyGravity_Vert_Fill"; break; + case MyGravity_Vert_Stretch: + vertGravityStr = @"MyGravity_Vert_Stretch"; + break; case MyGravity_Vert_Between: vertGravityStr = @"MyGravity_Vert_Between"; break; + case MyGravity_Vert_Around: + vertGravityStr = @"MyGravity_Vert_Around"; + break; + case MyGravity_Vert_Among: + vertGravityStr = @"MyGravity_Vert_Among"; + break; case MyGravity_Vert_Window_Center: vertGravityStr = @"MyGravity_Vert_Window_Center"; break; @@ -492,9 +533,18 @@ -(NSString*)gravityText:(MyGravity)gravity prefixText:(NSString*)prefixText case MyGravity_Horz_Fill: horzGravityStr = @"MyGravity_Horz_Fill"; break; + case MyGravity_Horz_Stretch: + horzGravityStr = @"MyGravity_Horz_Stretch"; + break; case MyGravity_Horz_Between: horzGravityStr = @"MyGravity_Horz_Between"; break; + case MyGravity_Horz_Around: + horzGravityStr = @"MyGravity_Horz_Around"; + break; + case MyGravity_Horz_Among: + horzGravityStr = @"MyGravity_Horz_Among"; + break; case MyGravity_Horz_Window_Center: horzGravityStr = @"MyGravity_Horz_Window_Center"; break; diff --git a/MyLayoutDemo/LLTest4ViewController.m b/MyLayoutDemo/LLTest4ViewController.m index 85c6eab..f480ae3 100644 --- a/MyLayoutDemo/LLTest4ViewController.m +++ b/MyLayoutDemo/LLTest4ViewController.m @@ -21,7 +21,7 @@ @implementation LLTest4ViewController -(void)loadView { /* - 这个例子详细说明wrapContentHeight和wrapContentWidth的包裹属性的设置、以及边界线性的设定、以及布局中可局部缩放背景图片的设定方法。 + 这个例子详细说明高度和宽度的自适应设置、以及边界线性的设定、以及布局中可局部缩放背景图片的设定方法。 */ self.edgesForExtendedLayout = UIRectEdgeNone; //设置视图控制器中的视图尺寸不延伸到导航条或者工具条下面。您可以注释这句代码看看效果。 @@ -29,19 +29,24 @@ -(void)loadView [super loadView]; self.view.backgroundColor = [UIColor whiteColor]; - + UIView *contentView = [UIView new]; contentView.backgroundColor = [CFTool color:5]; [self.view addSubview:contentView]; - contentView.wrapContentWidth = YES; - contentView.wrapContentHeight = YES; //如果一个非布局父视图里面有布局子视图,那么这个非布局父视图也是可以设置wrapContentHeight和wrapContentWidth的,他表示的意义是这个非布局父视图的尺寸由里面的布局子视图的尺寸来决定的。这个功能是在1.3.3版本支持的。 还有一个场景是非布局父视图是一个UIScrollView。他是左右滚动的,但是滚动视图的高度是由里面的布局子视图确定的,而宽度则是和窗口保持一致。这样只需要将滚动视图的宽度设置为和屏幕保持一致,高度设置为wrapContentHeight,并且把一个水平线性布局添加到滚动视图即可。 + + + /* + 如果一个非布局父视图里面有布局子视图,那么这个非布局父视图也是可以设置高度和宽度自适应,他表示的意义是这个非布局父视图的尺寸由里面的布局子视图的尺寸来决定的。 + 还有一个场景是非布局父视图是一个UIScrollView。他是左右滚动的,但是滚动视图的高度是由里面的布局子视图确定的,而宽度则是和窗口保持一致。 + 这样只需要将滚动视图的宽度设置为和屏幕保持一致,高度设置为自适应,并且把一个水平线性布局添加到滚动视图即可。 + */ + contentView.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); self.rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; self.rootLayout.layer.borderWidth = 1; self.rootLayout.layer.borderColor = [UIColor lightGrayColor].CGColor; - self.rootLayout.wrapContentHeight = YES; - self.rootLayout.wrapContentWidth = YES; //布局的高度和宽度由子视图决定 + self.rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); self.rootLayout.myTop = 10; self.rootLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); self.rootLayout.zeroPadding = NO; //这个属性设置为NO时表示当布局视图的尺寸是wrap也就是由子视图决定时并且在没有任何子视图是不会参与布局视图高度的计算的。您可以在这个DEMO的测试中将所有子视图都删除掉,看看效果,然后注释掉这句代码看看效果。 @@ -70,11 +75,10 @@ -(MyLinearLayout*)addWrapContentLayout { MyLinearLayout *wrapContentLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; - wrapContentLayout.wrapContentHeight = YES; - wrapContentLayout.wrapContentWidth = YES; //布局的高度和宽度由子视图决定 + wrapContentLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); //布局的高度和宽度由子视图决定 wrapContentLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); wrapContentLayout.subviewHSpace = 5; - + /* 布局视图的backgroundImage的属性的内部实现是通过设置视图的layer的content属性来实现的。因此如果我们希望实现具有拉升效果的 @@ -104,8 +108,7 @@ -(MyLinearLayout*)addWrapContentLayout actionLayout.layer.borderColor = [CFTool color:9].CGColor; actionLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); actionLayout.subviewVSpace = 5; - actionLayout.wrapContentWidth = YES; - actionLayout.wrapContentHeight = YES; //布局的高度和宽度由子视图决定 + actionLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); //布局的高度和宽度由子视图决定 [wrapContentLayout addSubview:actionLayout]; @@ -155,7 +158,7 @@ -(void)handleAction:(UIButton*)sender } else if (sender.tag == 300) { - MyLinearLayout*actionLayout = (MyLinearLayout*)sender.superview; + MyLinearLayout*actionLayout = (MyLinearLayout*)sender.superview; [actionLayout removeFromSuperview]; } else if (sender.tag == 400) @@ -172,13 +175,12 @@ -(void)handleAction:(UIButton*)sender /* -#pragma mark - Navigation - -// In a storyboard-based application, you will often want to do a little preparation before navigation -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. -} -*/ + #pragma mark - Navigation + // In a storyboard-based application, you will often want to do a little preparation before navigation + - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. + } + */ @end diff --git a/MyLayoutDemo/LLTest5ViewController.m b/MyLayoutDemo/LLTest5ViewController.m index 067feb3..54447da 100644 --- a/MyLayoutDemo/LLTest5ViewController.m +++ b/MyLayoutDemo/LLTest5ViewController.m @@ -65,15 +65,13 @@ -(void)loadView 如果布局视图里面的子视图使用了相对尺寸和相对间距我们必须要满足如下的条件: - 1.垂直线性布局里面如果有子视图设置了weight或者指定了垂直相对间距,则wrapContentHeight设置将失效;水平线性布局里面如果有子视图设置为weight或者指定了水平相对间距,则wrapContentWidth设置将失效。 + 1.垂直线性布局里面如果有子视图设置了weight,则高度约束设置将失效;水平线性布局里面如果有子视图设置为weight,则宽度约束设置将失效。 2.如果布局视图里面的子视图使用了相对间距和相对尺寸则必须要明确指定布局视图的宽度或者高度,否则相对设置可能会失效。 */ MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; rootLayout.backgroundColor = [CFTool color:0]; - rootLayout.wrapContentWidth = NO; - rootLayout.wrapContentHeight = NO; self.view = rootLayout; diff --git a/MyLayoutDemo/LLTest6ViewController.m b/MyLayoutDemo/LLTest6ViewController.m index c2b86ab..c582f73 100644 --- a/MyLayoutDemo/LLTest6ViewController.m +++ b/MyLayoutDemo/LLTest6ViewController.m @@ -57,7 +57,6 @@ -(void)loadView } MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; - rootLayout.wrapContentHeight = NO; rootLayout.backgroundColor = [UIColor whiteColor]; [rootLayout setTarget:self action:@selector(handleHideKeyboard:)]; //设置布局上的触摸事件。布局视图支持触摸事件的设置,可以使用setTarget方法来实现。 @@ -131,12 +130,12 @@ -(void)loadView textView.delegate = self; //左右边距为布局的10%,距离底部间距为65%,高度自适应,但高度最高为300,最低为30 - //wrapContentHeight和max,min的结合能做到一些完美的自动伸缩功能。 + //高度自适应设置和max,min的结合能做到一些完美的自动伸缩功能。 textView.myLeading = 0.05; textView.myTrailing = 0.05; textView.myBottom = 0.65; - textView.wrapContentHeight = YES; - textView.heightSize.max(300).min(60); //虽然wrapContentHeight属性设置了视图的高度为动态高度,但是仍然不能超过300的高度以及不能小于60的高度。 + //虽然高度自适应,但是仍然不能超过300的高度以及不能小于60的高度。 + textView.heightSize.equalTo(@(MyLayoutSize.wrap)).max(300).min(60); [rootLayout addSubview:textView]; diff --git a/MyLayoutDemo/LLTest7ViewController.m b/MyLayoutDemo/LLTest7ViewController.m index c3d2926..4a57099 100644 --- a/MyLayoutDemo/LLTest7ViewController.m +++ b/MyLayoutDemo/LLTest7ViewController.m @@ -42,14 +42,12 @@ -(void)loadView rootLayout.backgroundColor = [UIColor whiteColor]; rootLayout.gravity = MyGravity_Horz_Fill; //设置里面所有子视图的宽度填充布局视图的宽度。 rootLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); - rootLayout.wrapContentHeight = NO; - rootLayout.wrapContentWidth = NO; rootLayout.subviewVSpace = 5; self.view = rootLayout; //创建动作布局 MyLinearLayout *action1Layout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; - action1Layout.wrapContentHeight = YES; + action1Layout.heightSize.equalTo(@(MyLayoutSize.wrap)); action1Layout.topPos.equalTo(self.topLayoutGuide); [rootLayout addSubview:action1Layout]; @@ -60,7 +58,7 @@ -(void)loadView //创建动作布局 MyLinearLayout *action2Layout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; - action2Layout.wrapContentHeight = YES; + action2Layout.heightSize.equalTo(@(MyLayoutSize.wrap)); [rootLayout addSubview:action2Layout]; [action2Layout addSubview:[self createActionButton:NSLocalizedString(@"average size centered",@"") tag:400]]; @@ -72,11 +70,9 @@ -(void)loadView self.testLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; self.testLayout.backgroundColor = [CFTool color:0]; self.testLayout.gravity = MyGravity_Horz_Fill; //所有子视图水平宽度充满布局,这样就不需要分别设置每个子视图的宽度了。 - self.testLayout.wrapContentHeight = NO; - self.testLayout.wrapContentWidth = NO; self.testLayout.weight = 1.0; - self.testLayout.leftPadding = 10; - self.testLayout.rightPadding = 10; + self.testLayout.paddingLeft = 10; + self.testLayout.paddingRight = 10; [rootLayout addSubview:self.testLayout]; //这里用到了sizeclass的支持。您可以切换横竖屏看看效果。默认是垂直布局,横屏时是水平布局,并且垂直填充。 @@ -128,8 +124,7 @@ -(UIButton*)createActionButton:(NSString*)title tag:(NSInteger)tag button.titleLabel.adjustsFontSizeToFitWidth = YES; button.titleLabel.textAlignment = NSTextAlignmentCenter; button.titleLabel.font = [CFTool font:14]; - [button sizeToFit]; - button.heightSize.equalTo(button.heightSize).add(20); //高度等于内容的高度再加20 + button.heightSize.equalTo(@(MyLayoutSize.wrap)).add(20); //高度自适应外加20的 button.layer.borderColor = [UIColor lightGrayColor].CGColor; button.layer.borderWidth = 0.5; button.layer.cornerRadius = 4; diff --git a/MyLayoutDemo/LLTest8ViewController.h b/MyLayoutDemo/LLTest8ViewController.h new file mode 100644 index 0000000..229bc0b --- /dev/null +++ b/MyLayoutDemo/LLTest8ViewController.h @@ -0,0 +1,16 @@ +// +// LLTest8ViewController.h +// MyLayout +// +// Created by oybq on 15/7/9. +// Copyright (c) 2015年 YoungSoft. All rights reserved. +// + +#import + +/** + * 8.LinearLayout - Scroll&Dock + */ +@interface LLTest8ViewController : UIViewController + +@end diff --git a/MyLayoutDemo/LLTest8ViewController.m b/MyLayoutDemo/LLTest8ViewController.m new file mode 100644 index 0000000..dcb05e2 --- /dev/null +++ b/MyLayoutDemo/LLTest8ViewController.m @@ -0,0 +1,199 @@ +// +// LLTest8ViewController.m +// MyLayout +// +// Created by oybq on 15/7/9. +// Copyright (c) 2015年 YoungSoft. All rights reserved. +// + +#import "LLTest8ViewController.h" +#import "MyLayout.h" +#import "CFTool.h" + + +@interface DockViewInfo : NSObject + +@property (nonatomic, strong) UIView *dockView; +@property (nonatomic, strong) UIView *placeHolderView; + +@end + +@implementation DockViewInfo + +@end + +@interface LLTest8ViewController () + + +@property (nonatomic, strong) NSMutableArray< DockViewInfo *> *dockViewInfos; + +@property(nonatomic, weak) MyLinearLayout *rootLayout; + + +@end + +@implementation LLTest8ViewController + + +-(UILabel*)createLabel:(NSString*)title backgroundColor:(UIColor*)color +{ + UILabel *v = [UILabel new]; + v.text = title; + v.backgroundColor = color; + v.textColor = [CFTool color:0]; + v.font = [CFTool font:17]; + v.numberOfLines = 0; + return v; +} + +-(void)loadView +{ + /* + 这个例子用来介绍布局和滚动视图的结合,以及布局内子视图在滑动时停靠的实现方法。停靠功能通过子视图的属性useFrame以及占位视图来实现。 + */ + + self.edgesForExtendedLayout = UIRectEdgeNone; //设置视图控制器中的视图尺寸不延伸到导航条或者工具条下面。您可以注释这句代码看看效果。 + + + self.dockViewInfos = [NSMutableArray new]; + + UIScrollView *scrollView = [UIScrollView new]; + scrollView.delegate = self; + scrollView.backgroundColor = [UIColor whiteColor]; + self.view = scrollView; + + if (@available(iOS 11.0, *)) { + scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; + } else { + // Fallback on earlier versions + } + + + MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + rootLayout.insetsPaddingFromSafeArea = ~UIRectEdgeBottom; //为了防止拉到底部时iPhoneX设备的抖动发生,不能将底部安全区叠加到padding中去。 + rootLayout.widthSize.equalTo(scrollView.widthSize); + rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); + rootLayout.gravity = MyGravity_Horz_Fill; + [scrollView addSubview:rootLayout]; + self.rootLayout = rootLayout; + + + //添加色块。 + UILabel *v1 = [self createLabel:NSLocalizedString(@"Scroll the view please", @"") backgroundColor:[CFTool color:1]]; + v1.heightSize.equalTo(@80); + [rootLayout addSubview:v1]; + + //这是一个停靠的视图。 + UIView *v2 = [self createLabel:@"This is a dock view1" backgroundColor:[CFTool color:2]]; + v2.heightSize.equalTo(@60); + [rootLayout addSubview:v2]; + DockViewInfo *dockViewInfo1 = [DockViewInfo new]; + dockViewInfo1.dockView = v2; + [self.dockViewInfos addObject:dockViewInfo1]; + + UIView *v3 = [self createLabel:@"This is scrolled view ................" backgroundColor:[CFTool color:3]]; + v3.heightSize.equalTo(@100); + [rootLayout addSubview:v3]; + + + UIView *v4 = [self createLabel:@"This is a dock view2" backgroundColor:[CFTool color:4]]; + v4.heightSize.equalTo(@40); + [rootLayout addSubview:v4]; + + DockViewInfo *dockViewInfo2 = [DockViewInfo new]; + dockViewInfo2.dockView = v4; + [self.dockViewInfos addObject:dockViewInfo2]; + + UIView *v5 = [self createLabel:@"This is scrolled view ................" backgroundColor:[CFTool color:5]]; + v5.heightSize.equalTo(@100); + [rootLayout addSubview:v5]; + + + UIView *v6 = [self createLabel:@"This is a dock view3" backgroundColor:[CFTool color:6]]; + v6.heightSize.equalTo(@40); + [rootLayout addSubview:v6]; + + DockViewInfo *dockViewInfo3 = [DockViewInfo new]; + dockViewInfo3.dockView = v6; + [self.dockViewInfos addObject:dockViewInfo3]; + + + UILabel *v7 = [self createLabel:NSLocalizedString(@"This is scrolled view ................", @"") backgroundColor:[CFTool color:7]]; + v7.heightSize.equalTo(@800); + [rootLayout addSubview:v7]; + + UILabel *v8 = [self createLabel:NSLocalizedString(@"This is last view", @"") backgroundColor:[CFTool color:8]]; + v8.heightSize.equalTo(@80); + [rootLayout addSubview:v8]; + +} + + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +#pragma mark -- UIScrollViewDelegate +- (void)scrollViewDidScroll:(UIScrollView *)scrollView +{ + CGFloat totalDockedHeight = 0.0; + DockViewInfo *prevDockViewInfo = nil; + for (DockViewInfo *dockViewInfo in self.dockViewInfos) { + + totalDockedHeight += CGRectGetHeight(prevDockViewInfo.placeHolderView.frame); + + CGFloat y = 0; + if (dockViewInfo.dockView.useFrame) { + y = CGRectGetMinY(dockViewInfo.placeHolderView.frame); + } else { + y = CGRectGetMinY(dockViewInfo.dockView.frame); + } + y -= totalDockedHeight; + + if (scrollView.contentOffset.y >= y) { + if (!dockViewInfo.dockView.useFrame) { + //创建一个占位视图,占位视图的布局特性和将要停靠的视图的布局特性以及位置要保持一致。同时需要把要停靠的视图放到最顶端的层级,这样剩余视图在滑动时就不会覆盖停靠的视图。 + UIView *placeholderView = [UIView new]; + id traits = [dockViewInfo.dockView fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hAny]; + [placeholderView setLayoutSizeClass:MySizeClass_wAny | MySizeClass_hAny withValue:[traits copy]]; + [self.rootLayout insertSubview:placeholderView belowSubview:dockViewInfo.dockView]; + [self.rootLayout bringSubviewToFront:dockViewInfo.dockView]; + dockViewInfo.dockView.useFrame = YES; + dockViewInfo.placeHolderView = placeholderView; + } + + CGRect rect = dockViewInfo.dockView.frame; + dockViewInfo.dockView.frame = CGRectMake(rect.origin.x, scrollView.contentOffset.y + totalDockedHeight, rect.size.width, rect.size.height); + } else { + if (dockViewInfo.dockView.useFrame) { + dockViewInfo.dockView.useFrame = NO; + [self.rootLayout insertSubview:dockViewInfo.dockView belowSubview:dockViewInfo.placeHolderView]; + [dockViewInfo.placeHolderView removeFromSuperview]; + dockViewInfo.placeHolderView = nil; + } + } + + prevDockViewInfo = dockViewInfo; + } + +} + + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +@end diff --git a/MyLayoutDemo/PLTest2ViewController.m b/MyLayoutDemo/PLTest2ViewController.m index 6dd873b..15e7c7f 100644 --- a/MyLayoutDemo/PLTest2ViewController.m +++ b/MyLayoutDemo/PLTest2ViewController.m @@ -58,10 +58,9 @@ -(void)loadView self.view = scrollView; _pathLayout = [MyTestPathLayou new]; - _pathLayout.wrapContentHeight = YES; - _pathLayout.wrapContentWidth = YES; //这里面的布局视图的宽度和高度都是wrap属性,因此这个例子里面出现不同函数切换时不会立即得到真实的位置,而是要在添加或者删除子视图后才会更新真实的高度和宽度,这个并不是一个BUG。 - _pathLayout.widthSize.lBound(scrollView.widthSize, 0, 1); - _pathLayout.heightSize.lBound(scrollView.heightSize, 0, 1); //设置最小的宽度和高度和父视图保持一致。 +//这里面的布局视图的宽度和高度都是wrap属性,因此这个例子里面出现不同函数切换时不会立即得到真实的位置,而是要在添加或者删除子视图后才会更新真实的高度和宽度,这个并不是一个BUG。 + _pathLayout.widthSize.equalTo(@(MyLayoutSize.wrap)).lBound(scrollView.widthSize, 0, 1); + _pathLayout.heightSize.equalTo(@(MyLayoutSize.wrap)).lBound(scrollView.heightSize, 0, 1); //设置最小的宽度和高度和父视图保持一致。 _pathLayout.padding = UIEdgeInsetsMake(20, 20, 20, 20); [scrollView addSubview:_pathLayout]; diff --git a/MyLayoutDemo/PLTest3ViewController.m b/MyLayoutDemo/PLTest3ViewController.m index 7c3abc0..76125f2 100644 --- a/MyLayoutDemo/PLTest3ViewController.m +++ b/MyLayoutDemo/PLTest3ViewController.m @@ -33,8 +33,8 @@ -(id)initWithImage:(NSString*)image title:(NSString*)title index:(NSInteger)inde self = [super init]; if (self != nil) { - self.wrapContentHeight = YES; - self.wrapContentWidth = YES; + self.widthSize.equalTo(@(MyLayoutSize.wrap)); + self.heightSize.equalTo(@(MyLayoutSize.wrap)); _circleView = [MyPathLayout new]; //这里这是为路径布局的原因是其中的numLabel是跟随园所在的位置而确定的。 _circleView.widthSize.equalTo(@60); @@ -129,12 +129,13 @@ -(void)loadView centerButton.titleLabel.font = [CFTool font:20]; centerButton.layer.borderWidth = 5; centerButton.layer.borderColor = [UIColor whiteColor].CGColor; - centerButton.widthSize.equalTo(_pathLayout.widthSize).multiply(0.5).add(-30); //宽度是父视图宽度的一半再减去30 + centerButton.widthSize.equalTo(@[_pathLayout.widthSize, _pathLayout.heightSize].myMinSize).multiply(0.5).add(-30); + //宽度是父视图宽度或者高度最小者的一半再减去30 + centerButton.heightSize.equalTo(centerButton.widthSize); //高度等于宽度。 centerButton.viewLayoutCompleteBlock = ^(MyBaseLayout *layout, UIView *sbv) { //viewLayoutCompleteBlock是在1.2.3中添加的新功能,目的是给完成了布局的子视图一个机会进行一些特殊的处理,viewLayoutCompleteBlock只会在子视图布局完成后调用一次.其中的sbv就是子视图自己,而layout则是父布局视图。因为这个block是完成布局后执行的。所以这时候子视图的frame值已经被计算出来,因此您可以在这里设置一些和frame关联的属性。 sbv.layer.cornerRadius = sbv.frame.size.width / 2; //这里取子视图的圆角半径为宽度的一半,也就是形成了圆形按钮。 - sbv.heightSize.equalTo(@(sbv.frame.size.width)); //这里取子视图的高度等于他的宽度。 }; [centerButton addTarget:self action:@selector(handleClick:) forControlEvents:UIControlEventTouchUpInside]; diff --git a/MyLayoutDemo/PLTest4ViewController.m b/MyLayoutDemo/PLTest4ViewController.m index 7f75ef4..c0aa524 100644 --- a/MyLayoutDemo/PLTest4ViewController.m +++ b/MyLayoutDemo/PLTest4ViewController.m @@ -27,7 +27,7 @@ -(void)loadView self.view = _pathLayout; _pathLayout.backgroundColor = [CFTool color:0]; - _pathLayout.leftPadding = 20; + _pathLayout.paddingLeft = 20; _pathLayout.coordinateSetting.origin = CGPointMake(0, 0.5); //原点坐标在(0,0.5)的位置。 _pathLayout.coordinateSetting.start = -60.0 / 180.0 * M_PI; //从-60度到60度 _pathLayout.coordinateSetting.end = 60.0 / 180.0 * M_PI; diff --git a/MyLayoutDemo/PLTest5ViewController.m b/MyLayoutDemo/PLTest5ViewController.m index e7e7669..a37ccee 100644 --- a/MyLayoutDemo/PLTest5ViewController.m +++ b/MyLayoutDemo/PLTest5ViewController.m @@ -182,7 +182,7 @@ -(void)handleSwipe:(UISwipeGestureRecognizer*)sender [UIView animateWithDuration:0.08 animations:^{ - [_pathLayout layoutIfNeeded]; + [self.pathLayout layoutIfNeeded]; } completion:^(BOOL finished) { @@ -190,7 +190,7 @@ -(void)handleSwipe:(UISwipeGestureRecognizer*)sender [UIView animateWithDuration:0.08 animations:^{ - [_pathLayout layoutIfNeeded]; + [self.pathLayout layoutIfNeeded]; } completion:^(BOOL finished) { @@ -198,7 +198,7 @@ -(void)handleSwipe:(UISwipeGestureRecognizer*)sender [UIView animateWithDuration:0.08 animations:^{ - [_pathLayout layoutIfNeeded]; + [self.pathLayout layoutIfNeeded]; }]; diff --git a/MyLayoutDemo/RLTest1ViewController.m b/MyLayoutDemo/RLTest1ViewController.m index 28452a3..5adae64 100644 --- a/MyLayoutDemo/RLTest1ViewController.m +++ b/MyLayoutDemo/RLTest1ViewController.m @@ -247,6 +247,17 @@ -(void)loadView }]; */ + UIView *squareView = [UIView new]; + squareView.backgroundColor = [CFTool color:9]; + //宽度是父布局宽度和高度二者之间的最小值的1/5, 高度等于宽度。 + //这里面用到了数组的一个扩展属性myMinSize。要求数组中的元素必须是MyLayoutSize类型,而且这些值也必须在本视图约束计算前已经有明确的值。 + squareView.widthSize.equalTo(@[rootLayout.widthSize, rootLayout.heightSize].myMinSize).multiply(0.2); + squareView.heightSize.equalTo(squareView.widthSize); + squareView.centerXPos.equalTo(rootLayout.centerXPos); + squareView.centerYPos.equalTo(rootLayout.centerYPos).offset(40); + [rootLayout addSubview:squareView]; + + /* 左下角区域部分。 */ @@ -255,7 +266,7 @@ -(void)loadView bottomHalfCircleView.layer.cornerRadius = 25; bottomHalfCircleView.widthSize.equalTo(@50); //宽度固定为50 bottomHalfCircleView.heightSize.equalTo(bottomHalfCircleView.widthSize); //高度等于宽度 - bottomHalfCircleView.centerYPos.equalTo(rootLayout.bottomPos).offset(10); //垂直中心点和父布局的底部对齐,并且往下偏移10个点。 因为rootLayout设置了bottomPadding为10,所以这里要偏移10,否则不需要设置偏移。 + bottomHalfCircleView.centerYPos.equalTo(rootLayout.bottomPos).offset(10); //垂直中心点和父布局的底部对齐,并且往下偏移10个点。 因为rootLayout设置了paddingBottom为10,所以这里要偏移10,否则不需要设置偏移。 bottomHalfCircleView.leadingPos.equalTo(rootLayout.leadingPos).offset(50); //左边父布局左对齐,并且向右偏移50个点。 [rootLayout addSubview:bottomHalfCircleView]; /* diff --git a/MyLayoutDemo/RLTest2ViewController.m b/MyLayoutDemo/RLTest2ViewController.m index 9ec3a25..0c8ffba 100644 --- a/MyLayoutDemo/RLTest2ViewController.m +++ b/MyLayoutDemo/RLTest2ViewController.m @@ -48,7 +48,7 @@ -(void)loadView MyRelativeLayout *rootLayout = [MyRelativeLayout new]; rootLayout.backgroundColor = [UIColor whiteColor]; - rootLayout.trailingPadding = 10; + rootLayout.paddingTrailing = 10; self.view = rootLayout; UISwitch *visibilitySwitch = [UISwitch new]; @@ -222,18 +222,18 @@ -(void)handleHidden:(UIButton*)sender { if (self.visibilitySwitch.isOn) { - self.visibilityButton.myVisibility = MyVisibility_Gone; + self.visibilityButton.visibility = MyVisibility_Gone; } else { - self.visibilityButton.myVisibility = MyVisibility_Invisible; + self.visibilityButton.visibility = MyVisibility_Invisible; } } -(void)handleShow:(UIButton*)sender { - self.visibilityButton.myVisibility = MyVisibility_Visible; + self.visibilityButton.visibility = MyVisibility_Visible; } /* diff --git a/MyLayoutDemo/RLTest4ViewController.h b/MyLayoutDemo/RLTest4ViewController.h index 6a2cc27..aada27a 100644 --- a/MyLayoutDemo/RLTest4ViewController.h +++ b/MyLayoutDemo/RLTest4ViewController.h @@ -1,15 +1,15 @@ // -// RLTest4ViewController.h +// RLTest5ViewController.h // MyLayout // -// Created by oybq on 15/7/9. -// Copyright (c) 2015年 YoungSoft. All rights reserved. +// Created by oybq on 16/12/19. +// Copyright (c) 2016年 YoungSoft. All rights reserved. // #import /** - * 4.RelativeLayout - Scroll&Dock + * 4.RelativeLayout - Boundary limit */ @interface RLTest4ViewController : UIViewController diff --git a/MyLayoutDemo/RLTest4ViewController.m b/MyLayoutDemo/RLTest4ViewController.m index b84800f..3d1a476 100644 --- a/MyLayoutDemo/RLTest4ViewController.m +++ b/MyLayoutDemo/RLTest4ViewController.m @@ -2,8 +2,8 @@ // RLTest4ViewController.m // MyLayout // -// Created by oybq on 15/7/9. -// Copyright (c) 2015年 YoungSoft. All rights reserved. +// Created by oybq on 16/12/19. +// Copyright (c) 2016年 YoungSoft. All rights reserved. // #import "RLTest4ViewController.h" @@ -13,8 +13,6 @@ @interface RLTest4ViewController () -@property(nonatomic, weak) UIView *testTopDockView; -@property(nonatomic, weak) UIView *testView1; @end @@ -22,147 +20,254 @@ @interface RLTest4ViewController () @implementation RLTest4ViewController --(UILabel*)createLabel:(NSString*)title backgroundColor:(UIColor*)color -{ - UILabel *v = [UILabel new]; - v.text = title; - v.backgroundColor = color; - v.textColor = [CFTool color:0]; - v.font = [CFTool font:17]; - v.numberOfLines = 0; - return v; -} - -(void)loadView { /* - 这个例子用来介绍相对布局和滚动视图的结合,来实现滚动以及子视图的停靠的实现,其中主要的方式是通过子视图的属性noLayout来简单的实现这个功能。 - - 这里之所以用相对布局来实现滚动和停靠的原因是,线性布局、流式布局、浮动布局这几种布局都是根据添加的顺序来排列的。一般情况下,前面添加的子视图会显示在底部,而后面添加的子视图则会显示在顶部,所以一旦我们出现这种滚动,且某个子视图固定停靠时,我们一般要求这个停靠的子视图要放在最上面,也就是最后一个。 + 本例子是要用来介绍在相对布局中的子视图可以使用MyLayoutPos对象的uBound,lBound设置来实现最大和最小边界约束。 */ - self.edgesForExtendedLayout = UIRectEdgeNone; //设置视图控制器中的视图尺寸不延伸到导航条或者工具条下面。您可以注释这句代码看看效果。 - UIScrollView *scrollView = [UIScrollView new]; - scrollView.delegate = self; scrollView.backgroundColor = [UIColor whiteColor]; self.view = scrollView; - if (@available(iOS 11.0, *)) { - scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; - } else { - // Fallback on earlier versions - } - - - MyRelativeLayout *rootLayout = [MyRelativeLayout new]; - rootLayout.widthSize.equalTo(scrollView.widthSize); - rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - rootLayout.wrapContentHeight = YES; + MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + rootLayout.leadingPos.equalTo(@0); + rootLayout.trailingPos.equalTo(@0); + rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + rootLayout.subviewVSpace = 10; + rootLayout.gravity = MyGravity_Horz_Fill; [scrollView addSubview:rootLayout]; + //最小最大间距限制例子。segment的简易实现。 + [self createDemo1:rootLayout]; - //添加色块。 - UILabel *v1 = [self createLabel:NSLocalizedString(@"Scroll the view please", @"") backgroundColor:[CFTool color:1]]; - v1.widthSize.equalTo(rootLayout.widthSize); - v1.heightSize.equalTo(@80); - [rootLayout addSubview:v1]; - self.testView1 = v1; + //右边边距限制的例子。 + [self createDemo2:rootLayout]; + + //左边边距限制和上下边距限制的例子。 + [self createDemo3:rootLayout]; + +} + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +-(void)handleButtonSelect:(UIButton*)button +{ + if (button.isSelected) + return; + + MyRelativeLayout *containerLayout = (MyRelativeLayout*)button.superview; - UIView *v2 = [self createLabel:@"" backgroundColor:[CFTool color:2]]; - v2.widthSize.equalTo(rootLayout.widthSize); - v2.heightSize.equalTo(@200); - [rootLayout addSubview:v2]; + UIButton *leadingButton = [containerLayout viewWithTag:1]; + UIButton *trailingButton = [containerLayout viewWithTag:2]; + UIView *underLineView = [containerLayout viewWithTag:3]; + NSInteger tag = button.tag; + leadingButton.selected = (tag == 1); + trailingButton.selected = (tag == 2); - UIView *v3 = [self createLabel:@"" backgroundColor:[CFTool color:3]]; - v3.widthSize.equalTo(rootLayout.widthSize); - v3.heightSize.equalTo(@800); - v3.topPos.equalTo(v2.bottomPos); - [rootLayout addSubview:v3]; + //调整underLineView的位置。 + underLineView.leadingPos.equalTo(button.leadingPos); + underLineView.widthSize.equalTo(button.widthSize); - //这里最后一个加入的子视图作为滚动时的停靠视图。。 - UILabel *v4 = [self createLabel:NSLocalizedString(@"This view will Dock to top when scroll", @"") backgroundColor:[CFTool color:4]]; - v4.widthSize.equalTo(rootLayout.widthSize); - v4.heightSize.equalTo(@80); - v4.topPos.equalTo(v1.bottomPos); - [rootLayout addSubview:v4]; - self.testTopDockView = v4; + [containerLayout layoutAnimationWithDuration:0.3]; - v2.topPos.equalTo(v4.bottomPos); +} + +-(void)createDemo1:(UIView*)rootLayout +{ + /* + 本例子实现一个带动画效果的segment的简单实现。只有在相对布局中的子视图的MyLayoutPos位置对象才支持lBound和uBound方法。 + 通过这个方法能设置子视图的最小和最大的边界值。 + */ + MyRelativeLayout *containerLayout = [MyRelativeLayout new]; + containerLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + containerLayout.paddingTop = 6; + containerLayout.paddingBottom = 6; + containerLayout.backgroundColor = [CFTool color:0]; + [rootLayout addSubview:containerLayout]; - //当v4设置为noLayout时,出现了屏幕的旋转这时候是无法更新v4的frame值的,所以这里需要实现布局视图的这个block来再屏幕旋转是更新v4的frame值。 - __weak UIView *weakV4 = v4; - __weak UIScrollView *weakScrollView = scrollView; - rootLayout.rotationToDeviceOrientationBlock = ^(MyBaseLayout *layout, BOOL isFirst, BOOL isPortrait){ + UIButton *leadingButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [leadingButton setTitle:@"Leading Button" forState:UIControlStateNormal]; + [leadingButton setTitleColor:[CFTool color:4] forState:UIControlStateNormal]; + [leadingButton setTitleColor:[CFTool color:7] forState:UIControlStateSelected]; + leadingButton.tag = 1; + [leadingButton addTarget:self action:@selector(handleButtonSelect:) forControlEvents:UIControlEventTouchUpInside]; + [leadingButton sizeToFit]; //根据内容得到高度和宽度 + leadingButton.leadingPos.lBound(containerLayout.leadingPos, 0); //左边最小边界是父视图左边偏移0 + leadingButton.trailingPos.uBound(containerLayout.centerXPos, 0); //右边最大的边界是父视图中心点偏移0 + //在相对布局中子视图可以不设置左右边距而是设置最小和最大的边界值,就可以让子视图在指定的边界范围内居中,并且如果宽度超过最小和最大的边界设定时会自动压缩子视图的宽度。在这个例子中leadingButton始终在父视图的左边和父视图的水平中心这个边界内居中显示。 + [containerLayout addSubview:leadingButton]; + leadingButton.selected = YES; - if (weakV4.noLayout && !isFirst) - { - //如果V4不实际布局且不是第一次布局完成则这里要调整V4的frame。 - CGRect rect = weakV4.frame; - weakV4.frame = CGRectMake(rect.origin.x, weakScrollView.contentOffset.y, layout.frame.size.width - 20, rect.size.height); - } - - }; + UIButton *trailingButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [trailingButton setTitle:@"Trailing Button" forState:UIControlStateNormal]; + [trailingButton setTitleColor:[CFTool color:4] forState:UIControlStateNormal]; + [trailingButton setTitleColor:[CFTool color:7] forState:UIControlStateSelected]; + trailingButton.tag = 2; + [trailingButton addTarget:self action:@selector(handleButtonSelect:) forControlEvents:UIControlEventTouchUpInside]; + [trailingButton sizeToFit]; //根据内容得到高度和宽度 + trailingButton.leadingPos.lBound(containerLayout.centerXPos, 0); //左边最小边界是父视图中心点偏移0 + trailingButton.trailingPos.uBound(containerLayout.trailingPos, 0); //右边最大边界是父视图右边偏移0 + //在相对布局中子视图可以不设置左右边距而是设置最小和最大的边界值,就可以让子视图在指定的边界范围内居中,并且如果宽度超过最小和最大的边界设定时会自动压缩子视图的宽度。在这个例子中trailingButton始终在父视图的水平中心和父视图的右边这个边界内居中显示。 + [containerLayout addSubview:trailingButton]; + //添加下划线视图。 + UIView *underLineView = [UIView new]; + underLineView.backgroundColor = [CFTool color:7]; + underLineView.tag = 3; + underLineView.heightSize.equalTo(@1); + underLineView.widthSize.equalTo(leadingButton.widthSize); + underLineView.leadingPos.equalTo(leadingButton.leadingPos); + underLineView.topPos.equalTo(leadingButton.bottomPos).offset(6); + [containerLayout addSubview:underLineView]; -} - -- (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view. } -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} -#pragma mark -- UIScrollViewDelegate -- (void)scrollViewDidScroll:(UIScrollView *)scrollView +-(void)createDemo2:(UIView*)rootLayout { + /* + 这个例子通常用于UITableViewCell中的某些元素的最大尺寸的限制,您可以横竖屏切换,看看效果。 + 对于某些布局场景中:子视图尺寸是自适应的,但又不能无限制的延生而会受到某些边界的约束控制,因此可以用如下的方法来进行视图的尺寸设置。 + */ + + MyRelativeLayout *containerLayout = [MyRelativeLayout new]; + containerLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + containerLayout.padding = UIEdgeInsetsMake(6, 6, 6, 6); + containerLayout.backgroundColor = [CFTool color:0]; + [rootLayout addSubview:containerLayout]; + + /* + 这个例子中,水平方向一共有leadingImageView,flexedLabel,editImageView,trailingLabel四个子视图水平排列。其中leadingImageView在最左边且宽度固定,flexedLabel则跟在leadingImageView的右边但是宽度是不确定的,editImageView则是跟在flexedLabel的后面宽度是固定的,trailingLabel则总是在屏幕的右边且宽度是固定的,但是其中的flexedLabel的宽度最宽不能无限制的扩展,且不能和trailingLabel进行重叠。 + */ + + NSArray *images = @[@"minions1",@"minions2",@"minions3",@"minions4"]; + NSArray *texts = @[@"这是一段很长的文本,目的是为了实现最大限度的利用整个空间而不出现多余的缝隙", + @"您好", + @"北京市朝阳区三里屯SOHO城", + @"我是醉里挑灯看键", + @"欧阳大哥", + @"MyLayout是一套功能强大的综合界面布局库"]; + + NSArray *trailingTexts = @[@"100.00",@"1000.00",@"10000.00",@"100000.00"]; - //testTopDockView的上面视图是testView1。所以这里如果偏移超过testView1的最大则开始固定testTopDockView了 - if (scrollView.contentOffset.y > CGRectGetMaxY(self.testView1.frame)) + for (int i = 0; i < images.count; i++) { + UIImageView *leadingImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:images[arc4random()%4]]]; + leadingImageView.topPos.equalTo(@(90*i)); + [containerLayout addSubview:leadingImageView]; - /* - 当滚动条偏移的位置大于某个值后,我们将特定的子视图的noLayout设置为YES,表示特定的子视图虽然会参与布局,但是在布局完成后不会更新frame值。 - 因为参与了布局,所以不会影响到依赖这个视图的其他视图,所以整体布局结构是保持不变,这时候虽然设置为了noLayout的视图留出了一个空挡,但是却可以通过frame值来进行任意的定位而不受到布局的控制。 + UILabel *flexedLabel = [UILabel new]; + flexedLabel.text = texts[arc4random()%6]; + flexedLabel.font = [CFTool font:17]; + flexedLabel.textColor = [CFTool color:4]; + flexedLabel.lineBreakMode = NSLineBreakByCharWrapping; + flexedLabel.heightSize.equalTo(@(MyLayoutSize.wrap)); //高度自动计算。 + flexedLabel.leadingPos.equalTo(leadingImageView.trailingPos).offset(5); //左边等于leadingImageView的右边 + flexedLabel.topPos.equalTo(leadingImageView.topPos); //顶部和leadingImageView相等。 + [containerLayout addSubview:flexedLabel]; - 上面的代码中我们可以看到v4视图的位置和尺寸设置如下: - v4.widthSize.equalTo(rootLayout.widthSize); //宽度和父视图相等。 - v4.heightSize.equalTo(@80); //高度等于80。 - v4.topPos.equalTo(v1.bottomPos); //总是位于v1的下面。 - 。。。。 - v2.topPos.equalTo(v4.bottomPos); //v2则总是位于v4的下面。 - - 而当我们将v4的noLayout设置为了YES后,这时候v4仍然会参与布局,也就是说v4的那块区域和位置是保持不变的,v2还是会在v4的下面。但是v4却可以通过frame值进行任意位置和尺寸的改变。 这样就实现了当滚动时我们调整v4的真实frame值来达到悬停的功能,但是v2却保持了不变,还是向往常一样保持在那个v4假位置的下面,而随着滚动条滚动而滚动。 - - ***需要注意的是这个特定的子视图一定要最后加入到布局视图中去。*** - */ + UIImageView *editImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"edit"]]; + editImageView.leadingPos.equalTo(flexedLabel.trailingPos); //这个图片总是跟随在flexedLabel的后面。 + editImageView.topPos.equalTo(leadingImageView.topPos).offset(4); + [containerLayout addSubview:editImageView]; - self.testTopDockView.noLayout = YES; - CGRect rect = self.testTopDockView.frame; - self.testTopDockView.frame = CGRectMake(rect.origin.x, scrollView.contentOffset.y, rect.size.width, rect.size.height); //这里可以自由设置位置和尺寸了。 + UILabel *trailingLabel = [UILabel new]; + trailingLabel.text = trailingTexts[arc4random()%4]; + trailingLabel.font = [CFTool font:15]; + trailingLabel.textColor = [CFTool color:7]; + [trailingLabel sizeToFit]; //尺寸自适应 + trailingLabel.trailingPos.equalTo(containerLayout.trailingPos); //右边等于父视图的右边,也就是现实在最右边。 + trailingLabel.topPos.equalTo(leadingImageView.topPos).offset(4); + [containerLayout addSubview:trailingLabel]; + flexedLabel.widthSize.equalTo(@(MyLayoutSize.wrap)); //宽度自适应 + flexedLabel.trailingPos.uBound(trailingLabel.leadingPos, editImageView.frame.size.width + 10); //右边的最大的边界就等于trailingLabel的最左边再减去editImageView的尺寸外加上10,这里的10是视图之间的间距,为了让视图之间保持有足够的间距。这样当flexedLabel的宽度超过这个最大的右边界时,系统自动会缩小flexedLabel的宽度,以便来满足右边界的限制。 这个场景非常适合某个UITableViewCell里面的两个子视图之间有尺寸长度约束的情况。 } - else - { - - //当滚动的偏移小于90后,我们将testTopDocView的noLayout设置回NO,这样这个视图就又会根据所设置的约束而受到布局视图的约束和控制了,这时候frame的设置将不再起作用了。 - self.testTopDockView.noLayout = NO; - } + + } +-(void)handleClick:(UITapGestureRecognizer*)sender +{ + UILabel *label = (UILabel*)sender.view; + NSString *text = label.text; + label.text = [text stringByAppendingString:@"+++"]; + +} + +-(void)createDemo3:(UIView*)rootLayout +{ + /* + 这个例子用来了解上下边距的约束和左边边距的约束的场景。这些约束的设置特别适合那些有尺寸依赖以及位置依赖的UITableViewCell的场景。 + */ + + + MyRelativeLayout *containerLayout = [MyRelativeLayout new]; + containerLayout.heightSize.equalTo(@150); + containerLayout.padding = UIEdgeInsetsMake(6, 6, 6, 6); + containerLayout.backgroundColor = [CFTool color:0]; + [rootLayout addSubview:containerLayout]; + + + //左边文字居中并且根据内容变化。 + UILabel *leadingLabel = [UILabel new]; + leadingLabel.backgroundColor = [CFTool color:5]; + leadingLabel.text = @"Click me:"; + leadingLabel.textColor = [CFTool color:4]; + leadingLabel.widthSize.equalTo(@100); //宽度固定为100 + leadingLabel.heightSize.equalTo(@(MyLayoutSize.wrap)); //高度由子视图的内容确定,自动计算高度。 + leadingLabel.topPos.lBound(containerLayout.topPos,0); //最小的上边界是父布局的顶部。 + leadingLabel.bottomPos.uBound(containerLayout.bottomPos, 0); //最大的下边界是父布局的底部 + //通过这两个位置的最小最大边界设置,视图leadingLabel将会在这个范围内垂直居中显示,并且当高度超过这个边界时,会自动的压缩子视图的高度。 + [containerLayout addSubview:leadingLabel]; + + //添加手势处理。 + UITapGestureRecognizer *leadingLabelTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleClick:)]; + leadingLabel.userInteractionEnabled = YES; + [leadingLabel addGestureRecognizer:leadingLabelTapGesture]; + + + + //右边按钮 + UILabel *trailingLabel = [UILabel new]; + trailingLabel.backgroundColor = [CFTool color:6]; + trailingLabel.text = @"Click me:"; + trailingLabel.textColor = [CFTool color:4]; + trailingLabel.trailingPos.equalTo(containerLayout.trailingPos); //和父布局视图右对齐。 + trailingLabel.centerYPos.equalTo(leadingLabel.centerYPos); //和左边视图垂直居中对齐。 + trailingLabel.leadingPos.lBound(leadingLabel.trailingPos, 10); //右边视图的最小边界是等于左边视图的右边再偏移10,这样当右边视图的宽度超过这个最小边界时则会自动压缩视图的宽度。 + trailingLabel.widthSize.equalTo(@(MyLayoutSize.wrap)); //宽度等于自身的宽度。这个设置和上面的leadingPos.lBound方法配合使用实现子视图宽度的压缩。 + trailingLabel.heightSize.equalTo(@(MyLayoutSize.wrap)).uBound(containerLayout.heightSize, 0, 1); //但是最大的高度等于父布局视图的高度(注意这里内部自动减去了padding的值) + [containerLayout addSubview:trailingLabel]; + + //添加手势处理。 + UITapGestureRecognizer *trailingLabelTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleClick:)]; + trailingLabel.userInteractionEnabled = YES; + [trailingLabel addGestureRecognizer:trailingLabelTapGesture]; + + + + +} + /* #pragma mark - Navigation diff --git a/MyLayoutDemo/RLTest5ViewController.h b/MyLayoutDemo/RLTest5ViewController.h index c823fd7..40d3b9c 100644 --- a/MyLayoutDemo/RLTest5ViewController.h +++ b/MyLayoutDemo/RLTest5ViewController.h @@ -9,7 +9,7 @@ #import /** - * 5.RelativeLayout - Boundary limit + * 5.RelativeLayout - MostSize&MostPos */ @interface RLTest5ViewController : UIViewController diff --git a/MyLayoutDemo/RLTest5ViewController.m b/MyLayoutDemo/RLTest5ViewController.m index 26c58da..b09b5df 100644 --- a/MyLayoutDemo/RLTest5ViewController.m +++ b/MyLayoutDemo/RLTest5ViewController.m @@ -20,260 +20,174 @@ @interface RLTest5ViewController () @implementation RLTest5ViewController --(void)loadView -{ - /* - 本例子是要用来介绍在相对布局中的子视图可以使用MyLayoutPos对象的uBound,lBound设置来实现最大和最小边界约束。 - */ - - UIScrollView *scrollView = [UIScrollView new]; - scrollView.backgroundColor = [UIColor whiteColor]; - self.view = scrollView; - - MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; - rootLayout.leadingPos.equalTo(@0); - rootLayout.trailingPos.equalTo(@0); - rootLayout.wrapContentHeight = YES; - rootLayout.subviewVSpace = 10; - [scrollView addSubview:rootLayout]; - - //最小最大间距限制例子。segment的简易实现。 - [self createDemo1:rootLayout]; - - //右边边距限制的例子。 - [self createDemo2:rootLayout]; - - //左边边距限制和上下边距限制的例子。 - [self createDemo3:rootLayout]; - - -} - - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. -} - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} + self.edgesForExtendedLayout = UIRectEdgeNone; //设置视图控制器中的视图尺寸不延伸到导航条或者工具条下面。您可以注释这句代码看看效果。 --(void)handleButtonSelect:(UIButton*)button -{ - if (button.isSelected) - return; - - MyRelativeLayout *containerLayout = (MyRelativeLayout*)button.superview; - - UIButton *leadingButton = [containerLayout viewWithTag:1]; - UIButton *trailingButton = [containerLayout viewWithTag:2]; - UIView *underLineView = [containerLayout viewWithTag:3]; - - NSInteger tag = button.tag; - leadingButton.selected = (tag == 1); - trailingButton.selected = (tag == 2); - - //调整underLineView的位置。 - underLineView.leadingPos.equalTo(button.leadingPos); - underLineView.widthSize.equalTo(button.widthSize); - - - [containerLayout layoutAnimationWithDuration:0.3]; - -} - --(void)createDemo1:(UIView*)rootLayout -{ /* - 本例子实现一个带动画效果的segment的简单实现。只有在相对布局中的子视图的MyLayoutPos位置对象才支持lBound和uBound方法。 - 通过这个方法能设置子视图的最小和最大的边界值。 + 本示例用来介绍最值约束的使用方式。什么是最值约束呢?在一些场景中我们希望一个视图的宽度或者高度是某一些宽度或者高度集合中的最大值或者最小值,同样在一些场景中 + 我们希望某个视图的某个方位的位置值是一些位置值集合中的最大或者最小值。这种尺寸或者位置是一个集合中的尺寸或者位置中的最大最小值的约束就称之为最值约束。 + + 为了实现最值约束我们对MyLayoutSize以及MyLayoutPos的equalTo方法的参数中分别添加了对MyLayoutMostSize以及MyLayoutMostPos类型值的支持。我们不需要关心MyLayoutMostSize以及MyLayoutMostPos的内部具体实现,只需要知道这两种类型的值是分别从NSArray数组的分类方法:myMaxSize,myMinSize,myMaxPos,myMinPos中获取得到。而对于NSArray数组中的元素类型,则要求必须是NSNumber类型或者MyLayoutSize或者MyLayoutPos类型。下面举例来说: + + 1.视图D的高度是: 视图A的宽度、视图B的高度、视图C的高度的一半、50 这四个值中的最大值。那么视图D的高度约束可以如下设置: + D.heightSize.equalTo(@[A.widthSize, B.heightSize, C.heightSize.clone(0, 0.5), @50].myMaxSize); + + 2.视图D的右边距是:视图A的左边距、视图B的右边距、视图C的左边距偏移20、50这四个值中的最小值。那么视图D的右边距约束可以如下设置: + D.rightPos.equalTo(@[A.leftPos,B.rightPos,C.leftPos.clone(20), @50].myMinPos); + + 3.视图C的宽度是:自身宽度、视图B的宽度-20、30这三个值的最小值。 + C.widthSize.equalTo(@[@(MyLayoutSize.wrap),B.widthSize.clone(-20,1),@30)].myMinSize); + + 需要注意的是在使用最值约束时要求数组中的元素的约束必须是在当前约束计算前就已经完成约束计算,否则结果未可知。就以上面的例子来说在计算D的高度时,要求A的宽度,B的高度,C的高度都是已经完成了约束计算才有效。 + + 需要注意的是只有相对布局中的子视图的位置约束才支持位置的最值约束设置,其他布局中的子视图不支持!而尺寸最值约束则所有布局中的子视图都支持。 */ - MyRelativeLayout *containerLayout = [MyRelativeLayout new]; - containerLayout.leadingPos.equalTo(rootLayout.leadingPos); - containerLayout.trailingPos.equalTo(rootLayout.trailingPos); - containerLayout.wrapContentHeight = YES; - containerLayout.topPadding = 6; - containerLayout.bottomPadding = 6; - containerLayout.backgroundColor = [CFTool color:0]; - [rootLayout addSubview:containerLayout]; - - UIButton *leadingButton = [UIButton buttonWithType:UIButtonTypeCustom]; - [leadingButton setTitle:@"Leading Button" forState:UIControlStateNormal]; - [leadingButton setTitleColor:[CFTool color:4] forState:UIControlStateNormal]; - [leadingButton setTitleColor:[CFTool color:7] forState:UIControlStateSelected]; - leadingButton.tag = 1; - [leadingButton addTarget:self action:@selector(handleButtonSelect:) forControlEvents:UIControlEventTouchUpInside]; - [leadingButton sizeToFit]; //根据内容得到高度和宽度 - leadingButton.leadingPos.lBound(containerLayout.leadingPos, 0); //左边最小边界是父视图左边偏移0 - leadingButton.trailingPos.uBound(containerLayout.centerXPos, 0); //右边最大的边界是父视图中心点偏移0 - //在相对布局中子视图可以不设置左右边距而是设置最小和最大的边界值,就可以让子视图在指定的边界范围内居中,并且如果宽度超过最小和最大的边界设定时会自动压缩子视图的宽度。在这个例子中leadingButton始终在父视图的左边和父视图的水平中心这个边界内居中显示。 - [containerLayout addSubview:leadingButton]; - leadingButton.selected = YES; - - UIButton *trailingButton = [UIButton buttonWithType:UIButtonTypeCustom]; - [trailingButton setTitle:@"Trailing Button" forState:UIControlStateNormal]; - [trailingButton setTitleColor:[CFTool color:4] forState:UIControlStateNormal]; - [trailingButton setTitleColor:[CFTool color:7] forState:UIControlStateSelected]; - trailingButton.tag = 2; - [trailingButton addTarget:self action:@selector(handleButtonSelect:) forControlEvents:UIControlEventTouchUpInside]; - [trailingButton sizeToFit]; //根据内容得到高度和宽度 - trailingButton.leadingPos.lBound(containerLayout.centerXPos, 0); //左边最小边界是父视图中心点偏移0 - trailingButton.trailingPos.uBound(containerLayout.trailingPos, 0); //右边最大边界是父视图右边偏移0 - //在相对布局中子视图可以不设置左右边距而是设置最小和最大的边界值,就可以让子视图在指定的边界范围内居中,并且如果宽度超过最小和最大的边界设定时会自动压缩子视图的宽度。在这个例子中trailingButton始终在父视图的水平中心和父视图的右边这个边界内居中显示。 - [containerLayout addSubview:trailingButton]; + MyRelativeLayout *rootLayout = [MyRelativeLayout new]; + rootLayout.myMargin = 0; + [self.view addSubview:rootLayout]; + //最值约束例子1 + MyRelativeLayout *layout1 = [self createLayout1]; + layout1.topPos.equalTo(rootLayout.topPos); + layout1.leftPos.equalTo(rootLayout.leftPos); + layout1.backgroundColor = [CFTool color:7]; + [rootLayout addSubview:layout1]; - //添加下划线视图。 - UIView *underLineView = [UIView new]; - underLineView.backgroundColor = [CFTool color:7]; - underLineView.tag = 3; - underLineView.heightSize.equalTo(@1); - underLineView.widthSize.equalTo(leadingButton.widthSize); - underLineView.leadingPos.equalTo(leadingButton.leadingPos); - underLineView.topPos.equalTo(leadingButton.bottomPos).offset(6); - [containerLayout addSubview:underLineView]; + //最值约束例子2 + MyRelativeLayout *layout2 = [self createLayout2]; + layout2.topPos.equalTo(layout1.bottomPos).offset(20); + layout2.leftPos.equalTo(rootLayout.leftPos); + layout2.backgroundColor = [CFTool color:8]; + [rootLayout addSubview:layout2]; - } +-(MyRelativeLayout*)createLayout1 +{ + //这个例子用来演示位置的最值约束设置。通过位置的最值约束我们设置某个位置的值为一批位置中的最大或者最小值。位置最值约束只能用于相对布局。 + MyRelativeLayout *rootLayout = [MyRelativeLayout new]; + rootLayout.widthSize.equalTo(@(MyLayoutSize.wrap)); + rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); + + //名字控件,尺寸是自适应的。 + UILabel *nameLabel = [UILabel new]; + nameLabel.text = @"欧阳大哥"; + nameLabel.font = [CFTool font:20]; + nameLabel.widthSize.equalTo(@(MyLayoutSize.wrap)); + nameLabel.heightSize.equalTo(@(MyLayoutSize.wrap)); + [rootLayout addSubview:nameLabel]; + + //详情控件,尺寸是自适应的,但是最宽是屏幕宽度减20,这里减20的原因是因为布局视图设置了左右padding为10。 + UILabel *detailLabel = [UILabel new]; + detailLabel.text = @"继续!"; + detailLabel.backgroundColor = [CFTool color:9]; + //宽度自适应,最宽是屏幕的宽度减去20 + detailLabel.widthSize.equalTo(@(MyLayoutSize.wrap)).uBound(self.view.widthSize, -20, 1); + detailLabel.heightSize.equalTo(@(MyLayoutSize.wrap)); + detailLabel.topPos.equalTo(nameLabel.bottomPos).offset(10); + detailLabel.leftPos.equalTo(nameLabel.leftPos); + [rootLayout addSubview:detailLabel]; + + //按钮控件,右边和详情控件对齐,但是当详情控件的内容太少时,起码要和名字控件最小的距离是40 + UIButton *clickButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [clickButton setTitle:@"Click me" forState:UIControlStateNormal]; + clickButton.backgroundColor = [CFTool color:10]; + [clickButton sizeToFit]; //这里直接计算出clickButton的size是自适应的 + clickButton.topPos.equalTo(nameLabel.topPos); + //最新版本的相对布局可以让子视图的位置设置为某些视图位置中的最大或者最小值,也就是最值,我们可以对一个数组调用扩展分类的方法myMaxPos或者myMinPos来获取 + //到数组中元素的最大或者最小位置值。使用最大最小值的前提是在计算当前位置的约束时,要求数组中的元素的约束都是已经计算好了的,否则得到的结果是未可知,就如本例中 + //在计算clickButton的右边距时,detailLabel以及nameLabel的右边距都是已经计算好了的。 + clickButton.rightPos.equalTo(@[detailLabel.rightPos, nameLabel.rightPos.clone(-1 *(40+clickButton.frame.size.width))].myMaxPos); + [rootLayout addSubview:clickButton]; + + [clickButton addTarget:self action:@selector(handleClick:) forControlEvents:UIControlEventTouchUpInside]; + + + //这个控件的高度取nameLabel和detailLabel二个高度中的最小高度值。 + //这个控件的宽度取nameLabel和detailLabel二个宽度中的最大宽度值。 + //在设置最值尺寸约束时,要求数组中的约束尺寸是已经计算完毕了的,否则结果未可知。 + UILabel *subDetailLabel = [UILabel new]; + subDetailLabel.text = @"这是一句很长的文字,随着detail的增长而显示越多"; + subDetailLabel.backgroundColor = [CFTool color:10]; + subDetailLabel.topPos.equalTo(detailLabel.bottomPos).offset(10); + subDetailLabel.leftPos.equalTo(nameLabel.leftPos); + subDetailLabel.heightSize.equalTo(@[nameLabel.heightSize, detailLabel.heightSize].myMinSize); + subDetailLabel.widthSize.equalTo(@[nameLabel.widthSize, detailLabel.widthSize].myMaxSize); + [rootLayout addSubview:subDetailLabel]; + + return rootLayout; +} --(void)createDemo2:(UIView*)rootLayout +-(MyRelativeLayout*)createLayout2 { - /* - 这个例子通常用于UITableViewCell中的某些元素的最大尺寸的限制,您可以横竖屏切换,看看效果。 - 对于某些布局场景中,某个子视图的尺寸是不确定的,因此你不能设置一个固定的值,同时这个尺寸又不能无限制的延生而会受到某些边界的约束控制。因此可以用如下的方法来进行视图的尺寸设置。 - */ - - MyRelativeLayout *containerLayout = [MyRelativeLayout new]; - containerLayout.leadingPos.equalTo(rootLayout.leadingPos); - containerLayout.trailingPos.equalTo(rootLayout.trailingPos); - containerLayout.wrapContentHeight = YES; - containerLayout.padding = UIEdgeInsetsMake(6, 6, 6, 6); - containerLayout.backgroundColor = [CFTool color:0]; - [rootLayout addSubview:containerLayout]; - - /* - 这个例子中,水平方向一共有leadingImageView,flexedLabel,editImageView,trailingLabel四个子视图水平排列。其中leadingImageView在最左边且宽度固定,flexedLabel则跟在leadingImageView的右边但是宽度是不确定的,editImageView则是跟在flexedLabel的后面宽度是固定的,trailingLabel则总是在屏幕的右边且宽度是固定的,但是其中的flexedLabel的宽度最宽不能无限制的延生,且不能和trailingLabel进行重叠。 - */ - - NSArray *images = @[@"minions1",@"minions2",@"minions3",@"minions4"]; - NSArray *texts = @[@"这是一段很长的文本,目的是为了实现最大限度的利用整个空间而不出现多余的缝隙", - @"您好", - @"北京市朝阳区三里屯SOHO城", - @"我是醉里挑灯看键", - @"欧阳大哥", - @"MyLayout是一套功能强大的综合界面布局库"]; - - NSArray *trailingTexts = @[@"100.00",@"1000.00",@"10000.00",@"100000.00"]; - - for (int i = 0; i < images.count; i++) - { - UIImageView *leadingImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:images[arc4random()%4]]]; - leadingImageView.topPos.equalTo(@(90*i)); - [containerLayout addSubview:leadingImageView]; - - UILabel *flexedLabel = [UILabel new]; - flexedLabel.text = texts[arc4random()%6]; - flexedLabel.font = [CFTool font:17]; - flexedLabel.textColor = [CFTool color:4]; - flexedLabel.lineBreakMode = NSLineBreakByCharWrapping; - flexedLabel.wrapContentHeight = YES; //高度自动计算。 - flexedLabel.leadingPos.equalTo(leadingImageView.trailingPos).offset(5); //左边等于leadingImageView的右边 - flexedLabel.topPos.equalTo(leadingImageView.topPos); //顶部和leadingImageView相等。 - [containerLayout addSubview:flexedLabel]; - - UIImageView *editImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"edit"]]; - editImageView.leadingPos.equalTo(flexedLabel.trailingPos); //这个图片总是跟随在flexedLabel的后面。 - editImageView.topPos.equalTo(leadingImageView.topPos).offset(4); - [containerLayout addSubview:editImageView]; - - UILabel *trailingLabel = [UILabel new]; - trailingLabel.text = trailingTexts[arc4random()%4]; - trailingLabel.font = [CFTool font:15]; - trailingLabel.textColor = [CFTool color:7]; - [trailingLabel sizeToFit]; //尺寸固定。 - trailingLabel.trailingPos.equalTo(containerLayout.trailingPos); //右边等于父视图的右边,也就是现实在最右边。 - trailingLabel.topPos.equalTo(leadingImageView.topPos).offset(4); - [containerLayout addSubview:trailingLabel]; - - flexedLabel.widthSize.equalTo(flexedLabel.widthSize); //宽度等于自身的宽度 - flexedLabel.trailingPos.uBound(trailingLabel.leadingPos, editImageView.frame.size.width + 10); //右边的最大的边界就等于trailingLabel的最左边再减去editImageView的尺寸外加上10,这里的10是视图之间的间距,为了让视图之间保持有足够的间距。这样当flexedLabel的宽度超过这个最大的右边界时,系统自动会缩小flexedLabel的宽度,以便来满足右边界的限制。 这个场景非常适合某个UITableViewCell里面的两个子视图之间有尺寸长度约束的情况。 - - - } - - + MyRelativeLayout *rootLayout = [MyRelativeLayout new]; + rootLayout.widthSize.equalTo(@(MyLayoutSize.wrap)); + rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); + + //名字控件,尺寸是自适应的。 + UILabel *nameLabel = [UILabel new]; + nameLabel.text = @"欧阳大哥"; + nameLabel.widthSize.equalTo(@(MyLayoutSize.wrap)); + nameLabel.heightSize.equalTo(@(MyLayoutSize.wrap)); + [rootLayout addSubview:nameLabel]; + + UILabel *detailLabel = [UILabel new]; + detailLabel.text = @"继续!"; + detailLabel.backgroundColor = [CFTool color:9]; + //宽度自适应,最宽是屏幕的宽度减去20 + detailLabel.widthSize.equalTo(@(90)); + detailLabel.heightSize.equalTo(@(MyLayoutSize.wrap)); + detailLabel.topPos.equalTo(nameLabel.bottomPos).offset(10); + detailLabel.leftPos.equalTo(nameLabel.leftPos); + [rootLayout addSubview:detailLabel]; + + //按钮控件,右边和详情控件对齐,但是当详情控件的内容太少时,起码要和名字控件最小的距离是40 + UIButton *clickButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + [clickButton setTitle:@"Click me" forState:UIControlStateNormal]; + clickButton.backgroundColor = [CFTool color:10]; + [clickButton sizeToFit]; //这里直接计算出date的size是自适应 + clickButton.leftPos.equalTo(detailLabel.rightPos).offset(20); + //按钮的底部位置取100和detailLabel底部位置二者的最小值。 + //在这个例子中随着detailLabel的高度增加底部越来越大,但是clickButton的底部如果超过100则最终会取值100 + clickButton.bottomPos.equalTo(@[@100,detailLabel.bottomPos].myMinPos); + [rootLayout addSubview:clickButton]; + [clickButton addTarget:self action:@selector(handleClick:) forControlEvents:UIControlEventTouchUpInside]; + + + //这个控件的高度取50和detailLabel二个高度中的最小高度值。 + //这个控件的宽度取nameLabel宽度的一半加10,detailLabel高度的3/5,100三个宽度中的最大宽度值。 + //在设置最值尺寸约束时,要求数组中的约束尺寸是已经计算完毕了的,否则结果未可知。 + UILabel *subDetailLabel = [UILabel new]; + subDetailLabel.text = @"这是一句很长的文字,随着detail的增长而显示越多"; + subDetailLabel.backgroundColor = [CFTool color:10]; + subDetailLabel.topPos.equalTo(clickButton.bottomPos).offset(10); + subDetailLabel.leftPos.equalTo(detailLabel.rightPos).offset(10); + subDetailLabel.heightSize.equalTo(@[@50, detailLabel.heightSize].myMinSize); + subDetailLabel.widthSize.equalTo(@[nameLabel.widthSize.clone(10, 0.5), detailLabel.heightSize.clone(0,0.6), @(100)].myMaxSize); + [rootLayout addSubview:subDetailLabel]; + + + return rootLayout; } --(void)handleClick:(UITapGestureRecognizer*)sender -{ - UILabel *label = (UILabel*)sender.view; - NSString *text = label.text; - label.text = [text stringByAppendingString:@"+++"]; - +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. } --(void)createDemo3:(UIView*)rootLayout +-(IBAction)handleClick:(UIButton*)sender { - /* - 这个例子用来了解上下边距的约束和左边边距的约束的场景。这些约束的设置特别适合那些有尺寸依赖以及位置依赖的UITableViewCell的场景。 - */ - - - MyRelativeLayout *containerLayout = [MyRelativeLayout new]; - containerLayout.leadingPos.equalTo(rootLayout.leadingPos); - containerLayout.trailingPos.equalTo(rootLayout.trailingPos); - containerLayout.heightSize.equalTo(@150); - containerLayout.padding = UIEdgeInsetsMake(6, 6, 6, 6); - containerLayout.backgroundColor = [CFTool color:0]; - [rootLayout addSubview:containerLayout]; - - - //左边文字居中并且根据内容变化。 - UILabel *leadingLabel = [UILabel new]; - leadingLabel.backgroundColor = [CFTool color:5]; - leadingLabel.text = @"Click me:"; - leadingLabel.textColor = [CFTool color:4]; - leadingLabel.widthSize.equalTo(@100); //宽度固定为100 - leadingLabel.wrapContentHeight = YES; //高度由子视图的内容确定,自动计算高度。 - leadingLabel.topPos.lBound(containerLayout.topPos,0); //最小的上边界是父布局的顶部。 - leadingLabel.bottomPos.uBound(containerLayout.bottomPos, 0); //最大的下边界是父布局的底部 - //通过这两个位置的最小最大边界设置,视图leadingLabel将会在这个范围内垂直居中显示,并且当高度超过这个边界时,会自动的压缩子视图的高度。 - [containerLayout addSubview:leadingLabel]; - - //添加手势处理。 - UITapGestureRecognizer *leadingLabelTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleClick:)]; - leadingLabel.userInteractionEnabled = YES; - [leadingLabel addGestureRecognizer:leadingLabelTapGesture]; - - - - //右边按钮 - UILabel *trailingLabel = [UILabel new]; - trailingLabel.backgroundColor = [CFTool color:6]; - trailingLabel.text = @"Click me:"; - trailingLabel.textColor = [CFTool color:4]; - trailingLabel.trailingPos.equalTo(containerLayout.trailingPos); //和父布局视图右对齐。 - trailingLabel.centerYPos.equalTo(leadingLabel.centerYPos); //和左边视图垂直居中对齐。 - trailingLabel.leadingPos.lBound(leadingLabel.trailingPos, 10); //右边视图的最小边界是等于左边视图的右边再偏移10,这样当右边视图的宽度超过这个最小边界时则会自动压缩视图的宽度。 - trailingLabel.widthSize.equalTo(trailingLabel.widthSize); //宽度等于自身的宽度。这个设置和上面的leadingPos.lBound方法配合使用实现子视图宽度的压缩。 - trailingLabel.wrapContentHeight = YES; //高度动态调整 - trailingLabel.heightSize.uBound(containerLayout.heightSize, 0, 1); //但是最大的高度等于父布局视图的高度(注意这里内部自动减去了padding的值) - [containerLayout addSubview:trailingLabel]; - - //添加手势处理。 - UITapGestureRecognizer *trailingLabelTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleClick:)]; - trailingLabel.userInteractionEnabled = YES; - [trailingLabel addGestureRecognizer:trailingLabelTapGesture]; - - - - + UILabel *label = (UILabel*)sender.superview.subviews[1]; + label.text = [NSString stringWithFormat:@"继续! %@", label.text]; } + /* #pragma mark - Navigation diff --git a/MyLayoutDemo/TLTest2ViewController.m b/MyLayoutDemo/TLTest2ViewController.m index bf2ed68..098cad9 100644 --- a/MyLayoutDemo/TLTest2ViewController.m +++ b/MyLayoutDemo/TLTest2ViewController.m @@ -36,8 +36,7 @@ -(void)loadView rootLayout.padding = UIEdgeInsetsMake(5, 5, 5, 5); //分别设置表格布局里面的行间距、列间距、内部padding边距。 rootLayout.widthSize.equalTo(scrollView.widthSize); - rootLayout.wrapContentHeight = YES; //布局宽度和父视图一致,高度则由内容包裹。这是实现将布局视图加入滚动条视图并垂直滚动的标准方法。 - rootLayout.wrapContentWidth = NO; + rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap));//布局宽度和父视图一致,高度则由内容包裹。这是实现将布局视图加入滚动条视图并垂直滚动的标准方法。 [scrollView addSubview:rootLayout]; self.rootLayout = rootLayout; @@ -73,7 +72,6 @@ -(UIView*)createColLayout:(NSString*)image title:(NSString*)title { MyLinearLayout *colLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; colLayout.gravity = MyGravity_Horz_Fill; //里面所有子视图的宽度都跟父视图保持一致,这样子视图就不需要设置宽度了。 - colLayout.wrapContentHeight = YES; colLayout.subviewVSpace = 5; //设置布局视图里面子视图之间的间距为5个点。 colLayout.backgroundColor = [CFTool color:0]; colLayout.highlightedOpacity = 0.3; //设置触摸事件按下时的不透明度,来响应按下状态。 @@ -81,7 +79,7 @@ -(UIView*)createColLayout:(NSString*)image title:(NSString*)title UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:image]]; - imageView.wrapContentHeight = YES; //这个属性重点注意!! 对于UIImageView来说,如果我们设置了这个属性为YES的话,表示视图的高度会根据视图的宽度进行等比例的缩放来确定,从而防止图片显示时出现变形的情况。 + imageView.myHeight = MyLayoutSize.wrap; //这个属性重点注意!! 对于UIImageView来说,如果我们设置了高度自适应的话,表示视图的高度会根据视图的宽度进行等比例的缩放来确定,从而防止图片显示时出现变形的情况。 [colLayout addSubview:imageView]; UILabel *titleLabel = [UILabel new]; diff --git a/MyLayoutDemo/TLTest4ViewController.h b/MyLayoutDemo/TLTest4ViewController.h new file mode 100644 index 0000000..a6ad9ed --- /dev/null +++ b/MyLayoutDemo/TLTest4ViewController.h @@ -0,0 +1,16 @@ +// +// TLTest4ViewController.h +// MyLayout +// +// Created by oybq on 15/7/18. +// Copyright (c) 2015年 YoungSoft. All rights reserved. +// + +#import + +/** + 4.TableLayout - Style&Alignment + */ +@interface TLTest4ViewController : UIViewController + +@end diff --git a/MyLayoutDemo/TLTest4ViewController.m b/MyLayoutDemo/TLTest4ViewController.m new file mode 100644 index 0000000..0dd4bc2 --- /dev/null +++ b/MyLayoutDemo/TLTest4ViewController.m @@ -0,0 +1,158 @@ +// +// TLTest4ViewController.m +// MyLayout +// +// Created by oybq on 15/7/18. +// Copyright (c) 2015年 YoungSoft. All rights reserved. +// + +#import "TLTest4ViewController.h" +#import "MyLayout.h" +#import "CFTool.h" + +@interface TLTest4ViewController () + +@end + +@implementation TLTest4ViewController + +-(void)loadView +{ + /* + 这个例子是将表格布局和智能边界线以及对齐和动态高度的综合应用结合,实现一个表格界面。 + + */ + + + UIScrollView *scrollView = [UIScrollView new]; + scrollView.backgroundColor = [UIColor whiteColor]; + scrollView.contentInset = UIEdgeInsetsMake(0, 0, 10, 0); + self.view = scrollView; + + if (@available(iOS 11.0, *)) { + } else { + // Fallback on earlier versions + self.edgesForExtendedLayout = UIRectEdgeNone; + } + + + MyTableLayout *tableLayout = [MyTableLayout tableLayoutWithOrientation:MyOrientation_Vert]; + tableLayout.leadingPos.equalTo(@(MyLayoutPos.safeAreaMargin)).offset(10); + tableLayout.trailingPos.equalTo(@(MyLayoutPos.safeAreaMargin)).offset(10); + tableLayout.topPos.equalTo(@(MyLayoutPos.safeAreaMargin)).offset(10); + tableLayout.boundBorderline = [[MyBorderline alloc] initWithColor:[UIColor blackColor] thick:3]; + tableLayout.intelligentBorderline = [[MyBorderline alloc] initWithColor:[UIColor lightGrayColor]]; + [self.view addSubview:tableLayout]; + + NSArray *titles = @[@"身高(Height)(cm)", @"体重(Weight)(kg)", @"胸围(Bust)(cm)",@"腰围(Waist)(cm)",@"臀围(Hip)(cm)",@"鞋码(Shoes Size)(欧码)"]; + NSArray *values = @[@"177",@"54",@"88",@"88",@"88",@"43"]; + + + //第一行 + MyLinearLayout *row1 = [tableLayout addRow:MyLayoutSize.wrap colCount:titles.count]; + row1.gravity = MyGravity_Vert_Top; + row1.backgroundColor = [CFTool color:8]; + for (NSString *title in titles) + { + [tableLayout addSubview:[self itemFrom:title alignment:NSTextAlignmentCenter isFitWidth:NO]]; + } + + //第二行 + MyLinearLayout *row2 = [tableLayout addRow:MyLayoutSize.wrap colCount:titles.count]; + row2.gravity = MyGravity_Vert_Center; + for (NSString *title in titles) + { + [tableLayout addSubview:[self itemFrom:title alignment:NSTextAlignmentCenter isFitWidth:NO]]; + } + + //第三行 + MyLinearLayout *row3 = [tableLayout addRow:MyLayoutSize.wrap colCount:titles.count]; + row3.gravity = MyGravity_Vert_Bottom; + for (NSString *title in titles) + { + [tableLayout addSubview:[self itemFrom:title alignment:NSTextAlignmentCenter isFitWidth:NO]]; + } + + //第四行 + /*MyLinearLayout *row4 =*/ [tableLayout addRow:50 colCount:titles.count]; + for (NSString *title in titles) + { + [tableLayout addSubview:[self itemFrom:title alignment:NSTextAlignmentCenter isFitWidth:YES]]; + } + + + //第五行 + /*MyLinearLayout *row5 =*/ [tableLayout addRow:MyLayoutSize.wrap colCount:values.count]; + for (NSString *value in values) + { + [tableLayout addSubview:[self itemFrom:value alignment:NSTextAlignmentLeft isFitWidth:NO]]; + } + + //第六行 + /*MyLinearLayout *row6 =*/ [tableLayout addRow:MyLayoutSize.wrap colCount:values.count]; + for (NSString *value in values) + { + [tableLayout addSubview:[self itemFrom:value alignment:NSTextAlignmentCenter isFitWidth:NO]]; + } + + //第7行 + /*MyLinearLayout *row7 =*/ [tableLayout addRow:MyLayoutSize.wrap colCount:values.count]; + for (NSString *value in values) + { + [tableLayout addSubview:[self itemFrom:value alignment:NSTextAlignmentRight isFitWidth:NO]]; + } +} + + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view. + +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +#pragma mark -- Layout Construction + +-(MyBaseLayout*)itemFrom:(NSString*)itemString alignment:(NSTextAlignment)alignment isFitWidth:(BOOL)isFitWidth +{ + MyFrameLayout *itemLayout = [MyFrameLayout new]; + itemLayout.paddingTop = itemLayout.paddingBottom = 10; + itemLayout.paddingLeft = itemLayout.paddingRight = 5; + UILabel *label = [UILabel new]; + label.text = itemString; + label.textAlignment = alignment; + label.textColor = [CFTool color:4]; + [itemLayout addSubview:label]; + + if (isFitWidth) + { + itemLayout.gravity = MyGravity_Fill; + label.adjustsFontSizeToFitWidth = YES; + label.numberOfLines = 0; + } + else + { + itemLayout.gravity = MyGravity_Horz_Fill; + itemLayout.myHeight = MyLayoutSize.wrap; + label.myHeight = MyLayoutSize.wrap; + } + + + return itemLayout; +} + +/* +#pragma mark - Navigation + +// In a storyboard-based application, you will often want to do a little preparation before navigation +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { + // Get the new view controller using [segue destinationViewController]. + // Pass the selected object to the new view controller. +} +*/ + +@end diff --git a/MyLayoutDemo/ViewController.m b/MyLayoutDemo/ViewController.m index d3bc3d3..6e17fba 100644 --- a/MyLayoutDemo/ViewController.m +++ b/MyLayoutDemo/ViewController.m @@ -18,6 +18,7 @@ #import "LLTest5ViewController.h" #import "LLTest6ViewController.h" #import "LLTest7ViewController.h" +#import "LLTest8ViewController.h" #import "FLTest1ViewController.h" #import "FLTest2ViewController.h" @@ -28,10 +29,11 @@ #import "RLTest4ViewController.h" #import "RLTest5ViewController.h" - #import "TLTest1ViewController.h" #import "TLTest2ViewController.h" #import "TLTest3ViewController.h" +#import "TLTest4ViewController.h" + #import "FLLTest1ViewController.h" #import "FLLTest2ViewController.h" @@ -39,6 +41,11 @@ #import "FLLTest4ViewController.h" #import "FLLTest5ViewController.h" #import "FLLTest6ViewController.h" +#import "FLLTest7ViewController.h" +#import "FLLTest8ViewController.h" +#import "FLLTest9ViewController.h" + +#import "FLXTest1ViewController.h" #import "AllTest1ViewController.h" @@ -50,6 +57,10 @@ #import "AllTest7ViewController.h" #import "AllTest8ViewController.h" #import "AllTest9ViewController.h" +#import "AllTest10ViewController.h" +#import "AllTest11ViewController.h" +#import "AllTest12ViewController.h" +#import "AllTestExampleViewController.h" #import "FOLTest1ViewController.h" @@ -58,6 +69,8 @@ #import "FOLTest4ViewController.h" #import "FOLTest5ViewController.h" #import "FOLTest6ViewController.h" +#import "FOLTest7ViewController.h" + #import "PLTest1ViewController.h" #import "PLTest2ViewController.h" @@ -107,6 +120,9 @@ -(NSArray*)demoTypeList }, @{@"title":NSLocalizedString(@"7.LinearLayout - Average size&space", @""), @"class":[LLTest7ViewController class] + }, + @{@"title":NSLocalizedString(@"8.LinearLayout - Scroll&Dock", @""), + @"class":[LLTest8ViewController class] } ] }, @@ -133,10 +149,10 @@ -(NSArray*)demoTypeList @{@"title":NSLocalizedString(@"3.RelativeLayout - Centered", @""), @"class":[RLTest3ViewController class] }, - @{@"title":NSLocalizedString(@"4.RelativeLayout - Scroll&Dock", @""), + @{@"title":NSLocalizedString(@"4.RelativeLayout - Boundary limit", @""), @"class":[RLTest4ViewController class] }, - @{@"title":NSLocalizedString(@"5.RelativeLayout - Boundary limit", @""), + @{@"title":NSLocalizedString(@"5.RelativeLayout - MostSize&MostPos", @""), @"class":[RLTest5ViewController class] } ] @@ -152,6 +168,9 @@ -(NSArray*)demoTypeList }, @{@"title":NSLocalizedString(@"3.TableLayout - Intelligent Borderline", @""), @"class":[TLTest3ViewController class] + }, + @{@"title":NSLocalizedString(@"4.TableLayout - Style&Alignment", @""), + @"class":[TLTest4ViewController class] } ] }, @@ -175,10 +194,25 @@ -(NSArray*)demoTypeList }, @{@"title":NSLocalizedString(@"6.FlowLayout - Scroll", @""), @"class":[FLLTest6ViewController class] + }, + @{@"title":NSLocalizedString(@"7.FlowLayout - Auto Arrange", @""), + @"class":[FLLTest7ViewController class] + }, + @{@"title":NSLocalizedString(@"8.FlowLayout - Flex space", @""), + @"class":[FLLTest8ViewController class] + }, + @{@"title":NSLocalizedString(@"9.FlowLayout - Line gravity", @""), + @"class":[FLLTest9ViewController class] + } + ] + }, + @{@"type_title":@"Flex布局(FlexLayout)", + @"type_desc":@"fll.png", + @"type_vclist":@[@{@"title":NSLocalizedString(@"1.FlexLayout - Flexlayout", @""), + @"class":[FLXTest1ViewController class] } ] }, - @{@"type_title":@"浮动布局(FloatLayout)", @"type_desc":@"flo.png", @"type_vclist":@[@{@"title":NSLocalizedString(@"1.FloatLayout - Float", @""), @@ -198,6 +232,9 @@ -(NSArray*)demoTypeList }, @{@"title":NSLocalizedString(@"6.FloatLayout - User Profiles", @""), @"class":[FOLTest6ViewController class] + }, + @{@"title":NSLocalizedString(@"7.FloatLayout - Alignment", @""), + @"class":[FOLTest7ViewController class] } ] }, @@ -270,6 +307,18 @@ -(NSArray*)demoTypeList }, @{@"title":NSLocalizedString(@"❁3.UICollectionView height self-adaption", @""), @"class":[AllTest9ViewController class] + }, + @{@"title":NSLocalizedString(@"❁4.Circle of friends height self-adaption", @""), + @"class":[AllTest10ViewController class] + }, + @{@"title":NSLocalizedString(@"❁5.Subviews layout transform", @""), + @"class":[AllTest11ViewController class] + }, + @{@"title":NSLocalizedString(@"❁6.MyLayout & AutoLayout", @""), + @"class":[AllTest12ViewController class] + }, + @{@"title":NSLocalizedString(@"❁7.example", @""), + @"class":[AllTestExampleViewController class] } ] } diff --git a/MyLayoutDemo/zh-Hans.lproj/FLLTest2ViewController.xib b/MyLayoutDemo/zh-Hans.lproj/FLLTest2ViewController.xib index b7f4a63..6250e17 100644 --- a/MyLayoutDemo/zh-Hans.lproj/FLLTest2ViewController.xib +++ b/MyLayoutDemo/zh-Hans.lproj/FLLTest2ViewController.xib @@ -1,9 +1,8 @@ - - + + - - - + + @@ -15,18 +14,18 @@ - + - - + + - + - + @@ -37,7 +36,7 @@ - - + @@ -62,78 +61,108 @@ - + - - + - + + + + + + + - + - + - + - + - + @@ -141,10 +170,10 @@ - - + + - + @@ -152,34 +181,29 @@ - + - + - + - + - + - + - - - - - diff --git a/MyLayoutDemo/zh-Hans.lproj/FOLTest1ViewController.xib b/MyLayoutDemo/zh-Hans.lproj/FOLTest1ViewController.xib index 25e3afa..711eed5 100644 --- a/MyLayoutDemo/zh-Hans.lproj/FOLTest1ViewController.xib +++ b/MyLayoutDemo/zh-Hans.lproj/FOLTest1ViewController.xib @@ -1,8 +1,9 @@ - - + + + - - + + @@ -18,14 +19,14 @@ - + - + - + @@ -39,7 +40,7 @@ - - - - + - + - + - + @@ -139,10 +140,10 @@ - + - + @@ -150,19 +151,14 @@ - + - + - - - - - diff --git a/MyLayoutDemo/zh-Hans.lproj/Localizable.strings b/MyLayoutDemo/zh-Hans.lproj/Localizable.strings index 15c45b3..ef4106e 100644 --- a/MyLayoutDemo/zh-Hans.lproj/Localizable.strings +++ b/MyLayoutDemo/zh-Hans.lproj/Localizable.strings @@ -8,7 +8,7 @@ //Category "Category"="目录"; -"If you select \"Present\", than you can touch topleft corner of Status bar to dissmis the view controller" ="如果你选择了弹出则可以点击状态栏的左上角区域退出视图控制器"; +"If you select \"Present\", then you can touch Close button of topleft corner to dissmis the view controller" ="如果你选择了弹出则可以点击屏幕左上角区域关闭按钮退出视图控制器"; "1.LinearLayout - Vert&Horz" = "1.线性布局-垂直布局和水平布局"; "2.LinearLayout - Combine with UIScrollView" = "2.线性布局-和UIScrollView的结合"; "3.LinearLayout - Gravity&Fill" = "3.线性布局-子视图的停靠和填充"; @@ -16,28 +16,34 @@ "5.LinearLayout - Weight & Relative margin" = "5.线性布局-子视图尺寸由布局决定"; "6.LinearLayout - Size limit & Flexed margin" = "6.线性布局-子视图之间的浮动间距"; "7.LinearLayout - Average size&space" = "7.线性布局-均分子视图尺寸和间距"; +"8.LinearLayout - Scroll&Dock" = "8.线性布局-滚动和悬浮停靠"; "1.FrameLayout - Gravity&Fill" = "1.框架布局-子视图在布局各方位停靠"; "2.FrameLayout - Complex UI" = "2.框架布局-复杂的界面布局"; "1.RelativeLayout - Constraint&Dependence" = "1.相对布局-子视图之间的约束依赖"; "2.RelativeLayout - Prorate size" = "2.相对布局-子视图的尺寸按比例分配"; "3.RelativeLayout - Centered" = "3.相对布局-子视图整体居中"; -"4.RelativeLayout - Scroll&Dock" = "4.相对布局-滚动和停靠"; -"5.RelativeLayout - Boundary limit" = "5.相对布局-边界约束"; +"4.RelativeLayout - Boundary limit" = "4.相对布局-边界约束"; +"5.RelativeLayout - MostSize&MostPos" = "5.相对布局-最值约束"; "1.TableLayout - Vert" = "1.表格布局-垂直表格"; "2.TableLayout - Waterfall(Horz)" = "2.表格布局-水平表格实现瀑布流"; "3.TableLayout - Intelligent Borderline" = "3.表格布局-表格和智能边界线"; +"4.TableLayout - Style&Alignment" = "4.表格布局-多种样式和对齐"; "1.FlowLayout - Regular arrangement" = "1.流式布局-有规律的子视图排列"; "2.FlowLayout - Tag cloud" = "2.流式布局-标签流"; "3.FlowLayout - Drag" = "3.流式布局-子视图的拖拽调整功能"; "4.FlowLayout - Weight" = "4.流式布局-对线性布局的替代方案"; "5.FlowLayout - Paging" = "5.流式布局-对分页滚动的支持"; "6.FlowLayout - Scroll" = "6.流式布局-不同方向的滚动"; +"7.FlowLayout - Auto Arrange" = "7.流式布局-自动布局和对瀑布流的支持"; +"8.FlowLayout - Flex space" = "8.流式布局-浮动间距"; +"9.FlowLayout - Line gravity" = "9.流式布局-自定义行停靠对齐"; "1.FloatLayout - Float" = "1.浮动布局-浮动效果的演示"; "2.FloatLayout - Jagged" = "2.浮动布局-仿天猫淘宝首页实现" ; "3.FloatLayout - Card news" = "3.浮动布局-仿ZAKER今日头条实现"; "4.FloatLayout - Tag cloud" = "4.浮动布局-标签流"; "5.FloatLayout - Title & Description" = "5.浮动布局-左右排列的文本"; "6.FloatLayout - User Profiles"="6.浮动布局-各种用户配置的实现"; +"7.FloatLayout - Alignment"="7.浮动布局-行内对齐"; "1.PathLayout - Animations" = "1.路径布局-各种动画效果"; "2.PathLayout - Curves"="2.路径布局-各种函数曲线"; "3.PathLayout - Menu in Circle"="3.路径布局-圆环型菜单"; @@ -57,7 +63,9 @@ "❁1.Screen perfect fit - Demo1" = "❁1.界面的完美适配DEMO"; "❁2.Screen perfect fit - Demo2" = "❁2.界面的完美适配DEMO2"; "❁3.UICollectionView height self-adaption" = "❁3.UICollectionView 高度自适应"; - +"❁4.Circle of friends height self-adaption" = "❁4.朋友圈 高度自适应"; +"❁5.Subviews layout transform" = "❁5.布局内子视图的整体变换"; +"❁6.MyLayout & AutoLayout" = "❁6.MyLayout和AutoLayout结合"; //LLTest1ViewController "vertical(from top to bottom)" = "垂直布局(从上到下)"; "left margin" = "左边边距"; @@ -82,7 +90,7 @@ "Show&Hide the Text"="点击按钮显示隐藏文本"; "Show more》"="点击查看更多》"; "Close up《" ="点击收起《"; -"This is a can automatically wrap text.To realize this function, you need to set the clear width, and set the wrapContentHeight to YES.You can try to switch different simulator or different orientation screen to see the effect."="这是一段可以自动换行的文本。为了实现这个功能,您需要设置明确的宽度,并且把属性wrapContentHeight设置为YES。您可以尝试着切换不同尺寸的模拟器或者横竖屏来看看效果。"; +"This is a can automatically wrap text.To realize this function, you need to set the width exact, and set the heightSize to MyLayoutSize.wrap.You can try to switch different simulator or different orientation screen to see the effect."="这是一段可以自动换行的文本。为了实现这个功能,您需要设置明确的宽度,并且把heightSize设置为MyLayoutSize.wrap。您可以尝试着切换不同尺寸的模拟器或者横竖屏来看看效果。"; //LLTest3ViewController "centered title" = "标题居中"; @@ -97,10 +105,14 @@ "right" = "右停靠"; "horz fill"="水平填充"; "vert fill"="垂直填充"; +"horz stretch"="水平拉伸"; +"vert stretch"="垂直拉伸"; "screen vert center" = "屏幕垂直居中"; "screen horz center" ="屏幕水平居中"; "space" = "间距调整"; "between" = "间距拉伸"; +"around" = "间距环绕"; +"among" = "间距等分"; "test text1"="测试文本1"; "always alignment to left"="总是停靠在左边"; "always alignment to top"="总是停靠在上边"; @@ -170,7 +182,8 @@ "adjust horz gravity"="填充水平停靠"; "adjust vert gravity"="调整垂直停靠"; "adjust align"="调整对齐方式"; -"adjust spacing"="调整间隔"; +"adjust spacing"="调整间距"; +"adjust gravity policy"="调整拉伸策略"; //FLLTest2ViewController "click to remove tag"="点击标签可删除"; @@ -220,7 +233,7 @@ //AllTest1ViewController "add tableHeaderView(please touch me)"="设置tableHeaderView(点我试试)"; -" if you use layout view to realize the dynamic height tableHeaderView, please use frame to set view's width and use wrapContentHeight to set view's height. the layoutIfNeeded method is needed to call before the layout view assignment to the UITableview's tableHeaderView."=" 布局视图实现具有动态高度的tableHeaderView时,宽度需要用frame指定,高度则设置wrapContentHeight为YES,并且在赋值给UITableview的tableHeaderView之前需要调用layoutIfNeeded方法来明确高度。"; +" if you use layout view to realize the dynamic height tableHeaderView, please use frame to set view's width and set view's heightSize to MyLayoutSize.wrap. the layoutIfNeeded method is needed to call before the layout view assignment to the UITableview's tableHeaderView."=" 布局视图实现具有动态高度的tableHeaderView时,宽度需要用frame指定,高度heightSize设置为MyLayoutSize.wrap,并且在赋值给UITableview的tableHeaderView之前需要调用layoutIfNeeded方法来明确高度。"; "add tableFooterView"="设置tableFooterView"; "the layoutIfNeeded is not need to call when you use frame to set layout view's size"="如果通过frame设置了明确的尺寸则不需要调用layoutIfNeeded"; @@ -268,3 +281,14 @@ //AllTest8ViewController "Pop layoutview at center"="居中弹出布局视图"; "Title"="标题"; + +//AllTest11ViewController +"Identity"="正常"; +"Translation" = "平移"; +"Scale" = "缩放"; +"Horz Reflection" = "水平翻转"; +"Vert Reflection" = "垂直翻转"; +"Reverse" = "对角翻转"; + + + diff --git a/MyLayoutTests/MyFloatLayoutTestCase.m b/MyLayoutTests/MyFloatLayoutTestCase.m index 5c7042e..de9f557 100644 --- a/MyLayoutTests/MyFloatLayoutTestCase.m +++ b/MyLayoutTests/MyFloatLayoutTestCase.m @@ -7,7 +7,7 @@ // #import "MyLayoutTestCaseBase.h" -#import "FOLTest2ViewController.h" + @interface MyFloatLayoutTestCase : MyLayoutTestCaseBase @@ -28,21 +28,141 @@ - (void)tearDown { - (void)testExample { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. - FOLTest2ViewController *vc = [FOLTest2ViewController new]; - - [self startClock]; - UIView *v1 = vc.view; - [self endClock:@"load view:"]; //>60ms + for (int i = 1; i <= 7; i++) + { + Class cls = NSClassFromString([NSString stringWithFormat:@"FOLTest%dViewController", i]); + UIViewController *vc = [[cls alloc] init]; + UIView *v = vc.view; + [v setNeedsLayout]; + [v layoutIfNeeded]; + } - [self startClock]; +} + + +-(void)testSubviewSizeDependent +{//测试子视图尺寸依赖 - [v1 layoutIfNeeded]; + //垂直线性布局 + { + MyFloatLayout *rootLayout1 = [[MyFloatLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 667) orientation:MyOrientation_Vert]; + //1. 子视图宽度等于自身高度 + UILabel *label1 = [UILabel new]; + label1.myHeight = 100; + label1.widthSize.equalTo(label1.heightSize); + [rootLayout1 addSubview:label1]; + + + //2. 子视图高度等于自身宽度 + UILabel *label2 = [UILabel new]; + label2.widthSize.equalTo(@(MyLayoutSize.wrap)); + label2.heightSize.equalTo(label2.widthSize); + label2.text = @"hello World"; + [rootLayout1 addSubview:label2]; + + //3. 子视图高度等于兄弟视图高度, 子视图宽度等于兄弟视图宽度 + UILabel *label3 = [UILabel new]; + label3.heightSize.equalTo(label1.heightSize); + label3.widthSize.equalTo(label1.widthSize); + [rootLayout1 addSubview:label3]; + + //4. 子视图高度等于兄弟视图宽度, 子视图宽度等于兄弟视图高度 + UILabel *label4 = [UILabel new]; + label4.widthSize.equalTo(label2.heightSize).add(20); + label4.heightSize.equalTo(label2.widthSize).add(10).multiply(0.5); + [rootLayout1 addSubview:label4]; + + //5. 子视图宽度等于父视图宽度,高度等于宽度 + UILabel *label5 = [UILabel new]; + label5.widthSize.equalTo(rootLayout1.widthSize).add(-20); + label5.heightSize.equalTo(label5.widthSize); + [rootLayout1 addSubview:label5]; + + //6. 子视图高度等于父视图高度,宽度等于高度 + UILabel *label6 = [UILabel new]; + label6.heightSize.equalTo(rootLayout1.heightSize).multiply(0.5); + label6.widthSize.equalTo(label6.heightSize); + [rootLayout1 addSubview:label6]; + + [rootLayout1 layoutIfNeeded]; + + XCTAssertTrue(label1.frame.size.width == label1.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label1.frame)); + XCTAssertTrue(label2.frame.size.width == label2.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label2.frame)); + XCTAssertTrue(label3.frame.size.width == label1.frame.size.width && + label3.frame.size.height == label1.frame.size.height, @"label3.frame = %@",NSStringFromCGRect(label3.frame)); + XCTAssertTrue(label4.frame.size.width == (label2.frame.size.height + 20) && + label4.frame.size.height == (label2.frame.size.width * 0.5 + 10), @"label4.frame = %@",NSStringFromCGRect(label4.frame)); + + XCTAssertTrue(label5.frame.size.width == (rootLayout1.frame.size.width - 20) && + label5.frame.size.height == label5.frame.size.width, @"label5.frame = %@",NSStringFromCGRect(label5.frame)); + + XCTAssertTrue(label6.frame.size.width == label6.frame.size.height && + label6.frame.size.height == rootLayout1.frame.size.height * 0.5, @"label6.frame = %@",NSStringFromCGRect(label6.frame)); + } - [self endClock:@"layout:"]; + //水平线性布局 + { + MyFloatLayout *rootLayout1 = [[MyFloatLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 667) orientation:MyOrientation_Horz]; + //1. 子视图宽度等于自身高度 + UILabel *label1 = [UILabel new]; + label1.myHeight = 100; + label1.widthSize.equalTo(label1.heightSize); + [rootLayout1 addSubview:label1]; + + + //2. 子视图高度等于自身宽度 + UILabel *label2 = [UILabel new]; + label2.widthSize.equalTo(@(MyLayoutSize.wrap)); + label2.heightSize.equalTo(label2.widthSize); + label2.text = @"hello World"; + [rootLayout1 addSubview:label2]; + + //3. 子视图高度等于兄弟视图高度, 子视图宽度等于兄弟视图宽度 + UILabel *label3 = [UILabel new]; + label3.heightSize.equalTo(label1.heightSize); + label3.widthSize.equalTo(label1.widthSize); + [rootLayout1 addSubview:label3]; + + //4. 子视图高度等于兄弟视图宽度, 子视图宽度等于兄弟视图高度 + UILabel *label4 = [UILabel new]; + label4.widthSize.equalTo(label2.heightSize).add(20); + label4.heightSize.equalTo(label2.widthSize).add(10).multiply(0.5); + [rootLayout1 addSubview:label4]; + + //5. 子视图宽度等于父视图宽度,高度等于宽度 + UILabel *label5 = [UILabel new]; + label5.widthSize.equalTo(rootLayout1.widthSize).add(-20); + label5.heightSize.equalTo(label5.widthSize); + [rootLayout1 addSubview:label5]; + + //6. 子视图高度等于父视图高度,宽度等于高度 + UILabel *label6 = [UILabel new]; + label6.heightSize.equalTo(rootLayout1.heightSize).multiply(0.5); + label6.widthSize.equalTo(label6.heightSize); + [rootLayout1 addSubview:label6]; + + + [rootLayout1 layoutIfNeeded]; + + XCTAssertTrue(label1.frame.size.width == label1.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label1.frame)); + XCTAssertTrue(label2.frame.size.width == label2.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label2.frame)); + XCTAssertTrue(label3.frame.size.width == label1.frame.size.width && + label3.frame.size.height == label1.frame.size.height, @"label3.frame = %@",NSStringFromCGRect(label3.frame)); + XCTAssertTrue(label4.frame.size.width == (label2.frame.size.height + 20) && + label4.frame.size.height == (label2.frame.size.width * 0.5 + 10), @"label4.frame = %@",NSStringFromCGRect(label4.frame)); + + XCTAssertTrue(label5.frame.size.width == (rootLayout1.frame.size.width - 20) && + label5.frame.size.height == label5.frame.size.width, @"label5.frame = %@",NSStringFromCGRect(label5.frame)); + + XCTAssertTrue(label6.frame.size.width == label6.frame.size.height && + label6.frame.size.height == rootLayout1.frame.size.height * 0.5, @"label6.frame = %@",NSStringFromCGRect(label6.frame)); + + } } + -(void)testExample2 { MyFloatLayout *dectorInfoFloatLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; @@ -145,6 +265,206 @@ -(void)testExample3 } +-(void)testWrapAndGravity +{ + //测试尺寸自适应,但是又设置了停靠的例子。 + { + MyFloatLayout *rootLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; + rootLayout.mySize = CGSizeMake(100, MyLayoutSize.wrap); + rootLayout.frame = CGRectMake(0, 0, 100, 0); + rootLayout.gravity = MyGravity_Vert_Center; + rootLayout.heightSize.min(90); + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(40, 40); + [rootLayout addSubview:v1]; + + UIView *v2 = [UIView new]; + v2.reverseFloat = YES; + v2.mySize = CGSizeMake(50, 50); + [rootLayout addSubview:v2]; + + UIView *v3 = [UIView new]; + v3.mySize = CGSizeMake(10, 10); + [rootLayout addSubview:v3]; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + MyRectAssert(rootLayout, CGRectMake(0, 0, 100, 90)); + MyRectAssert(v1, CGRectMake(0, 20, 40, 40)); + MyRectAssert(v2, CGRectMake(50, 20, 50, 50)); + MyRectAssert(v3, CGRectMake(40, 20, 10, 10)); + + + v3.mySize = CGSizeMake(20, 20); + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + MyRectAssert(rootLayout, CGRectMake(0, 0, 100, 90)); + MyRectAssert(v1, CGRectMake(0, 15, 40, 40)); + MyRectAssert(v2, CGRectMake(50, 15, 50, 50)); + MyRectAssert(v3, CGRectMake(0, 55, 20, 20)); + + + + v3.mySize = CGSizeMake(60, 60); + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + MyRectAssert(rootLayout, CGRectMake(0, 0, 100, 110)); + MyRectAssert(v1, CGRectMake(0, 0, 40, 40)); + MyRectAssert(v2, CGRectMake(50, 0, 50, 50)); + MyRectAssert(v3, CGRectMake(0, 50, 60, 60)); + + rootLayout.gravity = MyGravity_Vert_Bottom; + v3.mySize = CGSizeMake(10, 10); + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + MyRectAssert(rootLayout, CGRectMake(0, 0, 100, 90)); + MyRectAssert(v1, CGRectMake(0, 40, 40, 40)); + MyRectAssert(v2, CGRectMake(50, 40, 50, 50)); + MyRectAssert(v3, CGRectMake(40,40,10,10)); + } + + { + MyFloatLayout *rootLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Horz]; + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap,100); + rootLayout.frame = CGRectMake(0, 0, 0,100); + rootLayout.gravity = MyGravity_Horz_Center; + rootLayout.widthSize.min(90); + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(40, 40); + [rootLayout addSubview:v1]; + + UIView *v2 = [UIView new]; + v2.reverseFloat = YES; + v2.mySize = CGSizeMake(50, 50); + [rootLayout addSubview:v2]; + + UIView *v3 = [UIView new]; + v3.mySize = CGSizeMake(10, 10); + [rootLayout addSubview:v3]; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + MyRectAssert(rootLayout, CGRectMake(0, 0, 90, 100)); + MyRectAssert(v1, CGRectMake(20,0, 40, 40)); + MyRectAssert(v2, CGRectMake(20,50, 50, 50)); + MyRectAssert(v3, CGRectMake(20,40, 10, 10)); + + + v3.mySize = CGSizeMake(20, 20); + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + MyRectAssert(rootLayout, CGRectMake(0, 0, 90, 100)); + MyRectAssert(v1, CGRectMake(15,0, 40, 40)); + MyRectAssert(v2, CGRectMake(15,50, 50, 50)); + MyRectAssert(v3, CGRectMake(55, 0,20, 20)); + + + + v3.mySize = CGSizeMake(60, 60); + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + MyRectAssert(rootLayout, CGRectMake(0, 0, 110, 100)); + MyRectAssert(v1, CGRectMake(0, 0, 40, 40)); + MyRectAssert(v2, CGRectMake(0,50, 50, 50)); + MyRectAssert(v3, CGRectMake(50,0, 60, 60)); + + rootLayout.gravity = MyGravity_Horz_Trailing; + v3.mySize = CGSizeMake(10, 10); + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + MyRectAssert(rootLayout, CGRectMake(0, 0, 90, 100)); + MyRectAssert(v1, CGRectMake(40,0, 40, 40)); + MyRectAssert(v2, CGRectMake(40,50, 50, 50)); + MyRectAssert(v3, CGRectMake(40,40,10,10)); + } +} + +-(void)testWrapAndMaxMinLimit +{ + { + MyFloatLayout *rootLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + rootLayout.widthSize.max(80); + rootLayout.padding = UIEdgeInsetsMake(10, 5, 5, 10); + rootLayout.subviewSpace = 20; + + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 15, 15)); + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(30, 30); + [rootLayout addSubview:v1]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 5+30+10, 10+30+5)); + + UIView *v2 = [UIView new]; + v2.mySize = CGSizeMake(10, 10); + [rootLayout addSubview:v2]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 5+30+20+10+10, 10+30+5)); + + UIView *v3 = [UIView new]; + v3.clearFloat = YES; + v3.mySize = CGSizeMake(50, 50); + [rootLayout addSubview:v3]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 5+30+20+10+10, 10+30+20+50+5)); + + UIView *v4 = [UIView new]; + v4.mySize = CGSizeMake(50, 50); + [rootLayout addSubview:v4]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 80, 10+30+20+50+20+50+5)); + } + + { + MyFloatLayout *rootLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Horz]; + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + rootLayout.heightSize.max(80); + rootLayout.padding = UIEdgeInsetsMake(5, 10, 10, 5); + rootLayout.subviewSpace = 20; + + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 15, 15)); + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(30, 30); + [rootLayout addSubview:v1]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 10+30+5,5+30+10)); + + UIView *v2 = [UIView new]; + v2.mySize = CGSizeMake(10, 10); + [rootLayout addSubview:v2]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 10+30+5,5+30+20+10+10)); + + UIView *v3 = [UIView new]; + v3.clearFloat = YES; + v3.mySize = CGSizeMake(50, 50); + [rootLayout addSubview:v3]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0,10+30+20+50+5,5+30+20+10+10)); + + UIView *v4 = [UIView new]; + v4.mySize = CGSizeMake(50, 50); + [rootLayout addSubview:v4]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 10+30+20+50+20+50+5,80)); + } + +} + - (void)testPerformanceExample { // This is an example of a performance test case. diff --git a/MyLayoutTests/MyFlowLayoutTestCase.m b/MyLayoutTests/MyFlowLayoutTestCase.m new file mode 100644 index 0000000..8be2237 --- /dev/null +++ b/MyLayoutTests/MyFlowLayoutTestCase.m @@ -0,0 +1,1357 @@ +// +// MyFlowLayoutTestCase.m +// MyLayout +// +// Created by apple on 17/4/26. +// Copyright © 2017年 YoungSoft. All rights reserved. +// + +#import "MyLayoutTestCaseBase.h" + +@interface MyFlowLayoutTestCase : MyLayoutTestCaseBase + +@end + +@implementation MyFlowLayoutTestCase + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + + for (int i = 1; i <= 9; i++) + { + Class cls = NSClassFromString([NSString stringWithFormat:@"FLLTest%dViewController", i]); + UIViewController *vc = [[cls alloc] init]; + UIView *v = vc.view; + [v setNeedsLayout]; + [v layoutIfNeeded]; + } + +} + +-(void)testWrapContentSize +{ + //测试内容约束布局的宽度自适应和高度自适应的设置。 + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:0]; + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap,MyLayoutSize.wrap); + rootLayout.padding = UIEdgeInsetsMake(10, 20, 30, 40); + rootLayout.subviewHSpace = 5; + rootLayout.subviewVSpace = 5; + + for (int i = 0; i < 3; i++) + { + UIView *v = [UIView new]; + v.mySize = CGSizeMake(100, 100 * (i+1)); + [rootLayout addSubview:v]; + } + + [rootLayout layoutIfNeeded]; + + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,20+40+3*100 + 2*5 ,10+30+300)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + + rootLayout.orientation = MyOrientation_Horz; + + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,20+40+100, 10+30+100+200+300+2*5)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + +} + + +-(void)testSubviewSizeDependent +{//测试子视图尺寸依赖 + + //垂直线性布局 + { + MyFlowLayout *rootLayout1 = [[MyFlowLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 667) orientation:MyOrientation_Vert arrangedCount:4]; + rootLayout1.heightSize.equalTo(nil); + //1. 子视图宽度等于自身高度 + UILabel *label1 = [UILabel new]; + label1.myHeight = 100; + label1.widthSize.equalTo(label1.heightSize); + [rootLayout1 addSubview:label1]; + + + //2. 子视图高度等于自身宽度 + UILabel *label2 = [UILabel new]; + label2.widthSize.equalTo(@(MyLayoutSize.wrap)); + label2.heightSize.equalTo(label2.widthSize); + label2.text = @"hello World"; + [rootLayout1 addSubview:label2]; + + //3. 子视图高度等于兄弟视图高度, 子视图宽度等于兄弟视图宽度 + UILabel *label3 = [UILabel new]; + label3.heightSize.equalTo(label1.heightSize); + label3.widthSize.equalTo(label1.widthSize); + [rootLayout1 addSubview:label3]; + + //4. 子视图高度等于兄弟视图宽度, 子视图宽度等于兄弟视图高度 + UILabel *label4 = [UILabel new]; + label4.widthSize.equalTo(label2.heightSize).add(20); + label4.heightSize.equalTo(label2.widthSize).add(10).multiply(0.5); + [rootLayout1 addSubview:label4]; + + //5. 子视图宽度等于父视图宽度,高度等于宽度 + UILabel *label5 = [UILabel new]; + label5.widthSize.equalTo(rootLayout1.widthSize).add(-20); + label5.heightSize.equalTo(label5.widthSize); + [rootLayout1 addSubview:label5]; + + //6. 子视图高度等于父视图高度,宽度等于高度 + UILabel *label6 = [UILabel new]; + label6.heightSize.equalTo(rootLayout1.heightSize).multiply(0.5); + label6.widthSize.equalTo(label6.heightSize); + [rootLayout1 addSubview:label6]; + + [rootLayout1 layoutIfNeeded]; + + XCTAssertTrue(label1.frame.size.width == label1.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label1.frame)); + XCTAssertTrue(label2.frame.size.width == label2.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label2.frame)); + XCTAssertTrue(label3.frame.size.width == label1.frame.size.width && + label3.frame.size.height == label1.frame.size.height, @"label3.frame = %@",NSStringFromCGRect(label3.frame)); + XCTAssertTrue(label4.frame.size.width == (label2.frame.size.height + 20) && + label4.frame.size.height == (label2.frame.size.width * 0.5 + 10), @"label4.frame = %@",NSStringFromCGRect(label4.frame)); + + XCTAssertTrue(label5.frame.size.width == (rootLayout1.frame.size.width - 20) && + label5.frame.size.height == label5.frame.size.width, @"label5.frame = %@",NSStringFromCGRect(label5.frame)); + + XCTAssertTrue(label6.frame.size.width == label6.frame.size.height && + label6.frame.size.height == rootLayout1.frame.size.height * 0.5, @"label6.frame = %@",NSStringFromCGRect(label6.frame)); + } + + + //水平线性布局 + { + MyFlowLayout *rootLayout1 = [[MyFlowLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 667) orientation:MyOrientation_Horz arrangedCount:4]; + //1. 子视图宽度等于自身高度 + UILabel *label1 = [UILabel new]; + label1.myHeight = 100; + label1.widthSize.equalTo(label1.heightSize); + [rootLayout1 addSubview:label1]; + + + //2. 子视图高度等于自身宽度 + UILabel *label2 = [UILabel new]; + label2.widthSize.equalTo(@(MyLayoutSize.wrap)); + label2.heightSize.equalTo(label2.widthSize); + label2.text = @"hello World"; + [rootLayout1 addSubview:label2]; + + //3. 子视图高度等于兄弟视图高度, 子视图宽度等于兄弟视图宽度 + UILabel *label3 = [UILabel new]; + label3.heightSize.equalTo(label1.heightSize); + label3.widthSize.equalTo(label1.widthSize); + [rootLayout1 addSubview:label3]; + + //4. 子视图高度等于兄弟视图宽度, 子视图宽度等于兄弟视图高度 + UILabel *label4 = [UILabel new]; + label4.widthSize.equalTo(label2.heightSize).add(20); + label4.heightSize.equalTo(label2.widthSize).add(10).multiply(0.5); + [rootLayout1 addSubview:label4]; + + //5. 子视图宽度等于父视图宽度,高度等于宽度 + UILabel *label5 = [UILabel new]; + label5.widthSize.equalTo(rootLayout1.widthSize).add(-20); + label5.heightSize.equalTo(label5.widthSize); + [rootLayout1 addSubview:label5]; + + //6. 子视图高度等于父视图高度,宽度等于高度 + UILabel *label6 = [UILabel new]; + label6.heightSize.equalTo(rootLayout1.heightSize).multiply(0.5); + label6.widthSize.equalTo(label6.heightSize); + [rootLayout1 addSubview:label6]; + + + [rootLayout1 layoutIfNeeded]; + + XCTAssertTrue(label1.frame.size.width == label1.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label1.frame)); + XCTAssertTrue(label2.frame.size.width == label2.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label2.frame)); + XCTAssertTrue(label3.frame.size.width == label1.frame.size.width && + label3.frame.size.height == label1.frame.size.height, @"label3.frame = %@",NSStringFromCGRect(label3.frame)); + XCTAssertTrue(label4.frame.size.width == (label2.frame.size.height + 20) && + label4.frame.size.height == (label2.frame.size.width * 0.5 + 10), @"label4.frame = %@",NSStringFromCGRect(label4.frame)); + + XCTAssertTrue(label5.frame.size.width == (rootLayout1.frame.size.width - 20) && + label5.frame.size.height == label5.frame.size.width, @"label5.frame = %@",NSStringFromCGRect(label5.frame)); + + XCTAssertTrue(label6.frame.size.width == label6.frame.size.height && + label6.frame.size.height == rootLayout1.frame.size.height * 0.5, @"label6.frame = %@",NSStringFromCGRect(label6.frame)); + + } + + //垂直线性布局 + { + MyFlowLayout *rootLayout1 = [[MyFlowLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 667) orientation:MyOrientation_Vert arrangedCount:0]; + //1. 子视图宽度等于自身高度 + UILabel *label1 = [UILabel new]; + label1.myHeight = 100; + label1.widthSize.equalTo(label1.heightSize); + [rootLayout1 addSubview:label1]; + + + //2. 子视图高度等于自身宽度 + UILabel *label2 = [UILabel new]; + label2.widthSize.equalTo(@(MyLayoutSize.wrap)); + label2.heightSize.equalTo(label2.widthSize); + label2.text = @"hello World"; + [rootLayout1 addSubview:label2]; + + //3. 子视图高度等于兄弟视图高度, 子视图宽度等于兄弟视图宽度 + UILabel *label3 = [UILabel new]; + label3.heightSize.equalTo(label1.heightSize); + label3.widthSize.equalTo(label1.widthSize); + [rootLayout1 addSubview:label3]; + + //4. 子视图高度等于兄弟视图宽度, 子视图宽度等于兄弟视图高度 + UILabel *label4 = [UILabel new]; + label4.widthSize.equalTo(label2.heightSize).add(20); + label4.heightSize.equalTo(label2.widthSize).add(10).multiply(0.5); + [rootLayout1 addSubview:label4]; + + //5. 子视图宽度等于父视图宽度,高度等于宽度 + UILabel *label5 = [UILabel new]; + label5.widthSize.equalTo(rootLayout1.widthSize).add(-20); + label5.heightSize.equalTo(label5.widthSize); + [rootLayout1 addSubview:label5]; + + //6. 子视图高度等于父视图高度,宽度等于高度 + UILabel *label6 = [UILabel new]; + label6.heightSize.equalTo(rootLayout1.heightSize).multiply(0.5); + label6.widthSize.equalTo(label6.heightSize); + [rootLayout1 addSubview:label6]; + + [rootLayout1 layoutIfNeeded]; + + XCTAssertTrue(label1.frame.size.width == label1.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label1.frame)); + XCTAssertTrue(label2.frame.size.width == label2.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label2.frame)); + XCTAssertTrue(label3.frame.size.width == label1.frame.size.width && + label3.frame.size.height == label1.frame.size.height, @"label3.frame = %@",NSStringFromCGRect(label3.frame)); + XCTAssertTrue(label4.frame.size.width == (label2.frame.size.height + 20) && + label4.frame.size.height == (label2.frame.size.width * 0.5 + 10), @"label4.frame = %@",NSStringFromCGRect(label4.frame)); + + XCTAssertTrue(label5.frame.size.width == (rootLayout1.frame.size.width - 20) && + label5.frame.size.height == label5.frame.size.width, @"label5.frame = %@",NSStringFromCGRect(label5.frame)); + + XCTAssertTrue(label6.frame.size.width == label6.frame.size.height && + label6.frame.size.height == rootLayout1.frame.size.height * 0.5, @"label6.frame = %@",NSStringFromCGRect(label6.frame)); + } + + + //水平线性布局 + { + MyFlowLayout *rootLayout1 = [[MyFlowLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 667) orientation:MyOrientation_Horz arrangedCount:0]; + //1. 子视图宽度等于自身高度 + UILabel *label1 = [UILabel new]; + label1.myHeight = 100; + label1.widthSize.equalTo(label1.heightSize); + [rootLayout1 addSubview:label1]; + + + //2. 子视图高度等于自身宽度 + UILabel *label2 = [UILabel new]; + label2.widthSize.equalTo(@(MyLayoutSize.wrap)); + label2.heightSize.equalTo(label2.widthSize); + label2.text = @"hello World"; + [rootLayout1 addSubview:label2]; + + //3. 子视图高度等于兄弟视图高度, 子视图宽度等于兄弟视图宽度 + UILabel *label3 = [UILabel new]; + label3.heightSize.equalTo(label1.heightSize); + label3.widthSize.equalTo(label1.widthSize); + [rootLayout1 addSubview:label3]; + + //4. 子视图高度等于兄弟视图宽度, 子视图宽度等于兄弟视图高度 + UILabel *label4 = [UILabel new]; + label4.widthSize.equalTo(label2.heightSize).add(20); + label4.heightSize.equalTo(label2.widthSize).add(10).multiply(0.5); + [rootLayout1 addSubview:label4]; + + //5. 子视图宽度等于父视图宽度,高度等于宽度 + UILabel *label5 = [UILabel new]; + label5.widthSize.equalTo(rootLayout1.widthSize).add(-20); + label5.heightSize.equalTo(label5.widthSize); + [rootLayout1 addSubview:label5]; + + //6. 子视图高度等于父视图高度,宽度等于高度 + UILabel *label6 = [UILabel new]; + label6.heightSize.equalTo(rootLayout1.heightSize).multiply(0.5); + label6.widthSize.equalTo(label6.heightSize); + [rootLayout1 addSubview:label6]; + + + [rootLayout1 layoutIfNeeded]; + + XCTAssertTrue(label1.frame.size.width == label1.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label1.frame)); + XCTAssertTrue(label2.frame.size.width == label2.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label2.frame)); + XCTAssertTrue(label3.frame.size.width == label1.frame.size.width && + label3.frame.size.height == label1.frame.size.height, @"label3.frame = %@",NSStringFromCGRect(label3.frame)); + XCTAssertTrue(label4.frame.size.width == (label2.frame.size.height + 20) && + label4.frame.size.height == (label2.frame.size.width * 0.5 + 10), @"label4.frame = %@",NSStringFromCGRect(label4.frame)); + + XCTAssertTrue(label5.frame.size.width == (rootLayout1.frame.size.width - 20) && + label5.frame.size.height == label5.frame.size.width, @"label5.frame = %@",NSStringFromCGRect(label5.frame)); + + XCTAssertTrue(label6.frame.size.width == label6.frame.size.height && + label6.frame.size.height == rootLayout1.frame.size.height * 0.5, @"label6.frame = %@",NSStringFromCGRect(label6.frame)); + + } + +} + +-(void)testWrapContentSize2 +{ + //测试一个布局宽度固定,高度包裹,然后子视图的高度依赖宽度,或者子视图高度自适应,然后又有压缩的场景,看是否会导致行高不对,或者整个行高不对。 + { + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:0]; + rootLayout.frame = CGRectMake(0, 0, 110, 0); + rootLayout.padding = UIEdgeInsetsMake(10, 10, 5, 5); + rootLayout.mySize = CGSizeMake(110, MyLayoutSize.wrap); + rootLayout.subviewSpace = 6; + rootLayout.gravity = MyGravity_Horz_Fill; + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(10, 20); + v1.myMargin = 5; + [rootLayout addSubview:v1]; + + UIView *v2 = [UIView new]; + v2.myWidth = 30; + v2.heightSize.equalTo(v2.widthSize); + v2.myMargin = 6; + [rootLayout addSubview:v2]; + + + UIView *v3 = [UIView new]; + v3.mySize = CGSizeMake(10, 50); + v3.myMargin = 2; + [rootLayout addSubview:v3]; + + UIView *v4 = [UIView new]; + v4.myWidth = 20; + v4.heightSize.equalTo(v4.widthSize).multiply(0.5).add(10); + v4.myMargin = 3; + [rootLayout addSubview:v4]; + + UILabel *v5 = [UILabel new]; + v5.text = @"测试测试测试"; + v5.frame = CGRectMake(0, 0, 20, 0); + v5.heightSize.equalTo(@(MyLayoutSize.wrap)); + [rootLayout addSubview:v5]; + + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,110,197)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(15,15,12.5,20)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(44.5,16,32.5,32.5)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(90.5,12,12.5,50)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(13,73,20,20)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(42,70,20,122)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + rootLayout.isFlex = YES; + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,110,136)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(15,15,12.5,20)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(44.5,16,32.5,32.5)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(90.5,12,12.5,50)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(13,73,41.5,31)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(63.5,70,41.5,61)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + } + + { + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:0]; + rootLayout.frame = CGRectMake(0, 0, 0, 110); + rootLayout.padding = UIEdgeInsetsMake(10, 10, 5, 5); + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap, 110); + rootLayout.subviewSpace = 6; + rootLayout.gravity = MyGravity_Vert_Fill; + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(20, 10); + v1.myMargin = 5; + [rootLayout addSubview:v1]; + + UIView *v2 = [UIView new]; + v2.myHeight = 30; + v2.widthSize.equalTo(v2.heightSize); + v2.myMargin = 6; + [rootLayout addSubview:v2]; + + + UIView *v3 = [UIView new]; + v3.mySize = CGSizeMake(50, 10); + v3.myMargin = 2; + [rootLayout addSubview:v3]; + + UIView *v4 = [UIView new]; + v4.myHeight = 20; + v4.widthSize.equalTo(v4.heightSize).multiply(0.5).add(10); + v4.myMargin = 3; + [rootLayout addSubview:v4]; + + UILabel *v5 = [UILabel new]; + v5.text = @"测试测试测试"; + v5.frame = CGRectMake(0, 0, 0, 20); + v5.widthSize.equalTo(@(MyLayoutSize.wrap)); + [rootLayout addSubview:v5]; + + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,179,110)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(15,15,20,12.5)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(16,44.5,32.5,32.5)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(12,90.5,50,12.5)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(73,13,20,20)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(70,42,104,20)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + rootLayout.isFlex = YES; + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,179,110)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(15,15,20,12.5)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(16,44.5,32.5,32.5)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(12,90.5,50,12.5)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(73,13,31,41.5)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(70,63.5,104,41.5)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + } +} + +-(void)testWrapContentSize3 +{ + { + MyFlowLayout *layout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:4]; + layout.frame = CGRectMake(0, 0, 0, 0); + layout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + layout.subviewHSpace = 5; + layout.subviewVSpace = 5; + + for (int i = 0; i < 1; i++) + { + UIView *sbv = [UIView new]; + sbv.mySize = CGSizeMake(50, 50); + [layout addSubview:sbv]; + } + + [layout layoutIfNeeded]; + MyRectAssert(layout, CGRectMake(0, 0, 50, 50)); + + for (int i = 0; i < 1; i++) + { + UIView *sbv = [UIView new]; + sbv.mySize = CGSizeMake(50, 50); + [layout addSubview:sbv]; + + } + [layout layoutIfNeeded]; + MyRectAssert(layout, CGRectMake(0, 0, 105, 50)); + + for (int i =0; i < 4; i++) + { + UIView *sbv = [UIView new]; + sbv.mySize = CGSizeMake(50, 50); + [layout addSubview:sbv]; + } + + [layout layoutIfNeeded]; + MyRectAssert(layout, CGRectMake(0, 0, 215, 105)); + } + + { + MyFlowLayout *layout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:4]; + layout.frame = CGRectMake(0, 0, 0, 0); + layout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + layout.subviewHSpace = 5; + layout.subviewVSpace = 5; + + for (int i = 0; i < 1; i++) + { + UIView *sbv = [UIView new]; + sbv.mySize = CGSizeMake(50, 50); + [layout addSubview:sbv]; + + } + + [layout layoutIfNeeded]; + MyRectAssert(layout, CGRectMake(0, 0, 50, 50)); + + for (int i = 0; i < 1; i++) + { + UIView *sbv = [UIView new]; + sbv.mySize = CGSizeMake(50, 50); + [layout addSubview:sbv]; + + } + [layout layoutIfNeeded]; + MyRectAssert(layout, CGRectMake(0, 0, 50, 105)); + + for (int i =0; i < 4; i++) + { + UIView *sbv = [UIView new]; + sbv.mySize = CGSizeMake(50, 50); + [layout addSubview:sbv]; + + } + + [layout layoutIfNeeded]; + MyRectAssert(layout, CGRectMake(0, 0, 105, 215)); + } +} + + +-(void)testWeight +{ + //测试内容约束布局中weight刚好在边界的情况。 + { + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:0]; + rootLayout.frame = CGRectMake(0, 0, 152, 100); + rootLayout.padding = UIEdgeInsetsMake(10, 10, 5, 5); + rootLayout.subviewSpace = 6; + rootLayout.myHeight = MyLayoutSize.wrap; + + UIView *v1 = [UIView new]; + v1.myHorzMargin = 4; + v1.mySize = CGSizeMake(20, 20); + [rootLayout addSubview:v1]; // 10+4+20+4+6 = 44 + + UIView *v2 = [UIView new]; + v2.myHorzMargin = 5; + v2.mySize = CGSizeMake(30, 30); + [rootLayout addSubview:v2]; //10+4+20+4+6+5+30+5+6 = 90 + + UIView *v3 = [UIView new]; + v3.myHorzMargin = 6; + v3.mySize = CGSizeMake(40, 40); + [rootLayout addSubview:v3]; //10+4+20+4+6+5+30+5+6+ 6+40+6+ 6 + 5 = 153 + + UIView *v4 = [UIView new]; + v4.myHeight = 50; + v4.weight = 0.6; + [rootLayout addSubview:v4]; + + UIView *v5 = [UIView new]; + v5.mySize = CGSizeMake(20, 20); + [rootLayout addSubview:v5]; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(10,56,82,50)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(98,56,20,20)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,152,111)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + rootLayout.frame = CGRectMake(0, 0, 153, 0); + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(10,56,83,50)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(99,56,20,20)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,153,111)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + + rootLayout.frame = CGRectMake(0, 0, 154, 100); + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(148,10,0.5,50)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(10,66,20,20)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,154,91)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + rootLayout.frame = CGRectMake(0, 0, 152, 100); + v4.weight = 1; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(10,56,137,50)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(10,112,20,20)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,152,137)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + rootLayout.isFlex = YES; + v4.weight = 1; + v5.weight = 1; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(10,56,55.5,50)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(71.5,56,75.5,20)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,152,111)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + } + + //测试内容约束布局中weight刚好在边界的情况。 + { + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:0]; + rootLayout.frame = CGRectMake(0, 0, 100, 152); + rootLayout.padding = UIEdgeInsetsMake(10, 10, 5, 5); + rootLayout.subviewSpace = 6; + rootLayout.myWidth = MyLayoutSize.wrap; + + UIView *v1 = [UIView new]; + v1.myVertMargin = 4; + v1.mySize = CGSizeMake(20, 20); + [rootLayout addSubview:v1]; // 10+4+20+4+6 = 44 + + UIView *v2 = [UIView new]; + v2.myVertMargin = 5; + v2.mySize = CGSizeMake(30, 30); + [rootLayout addSubview:v2]; //10+4+20+4+6+5+30+5+6 = 90 + + UIView *v3 = [UIView new]; + v3.myVertMargin = 6; + v3.mySize = CGSizeMake(40, 40); + [rootLayout addSubview:v3]; //10+4+20+4+6+5+30+5+6+ 6+40+6+ 6 + 5 = 153 + + UIView *v4 = [UIView new]; + v4.myWidth = 50; + v4.weight = 0.6; + [rootLayout addSubview:v4]; + + UIView *v5 = [UIView new]; + v5.mySize = CGSizeMake(20, 20); + [rootLayout addSubview:v5]; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(56,10,50,82)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(56,98,20,20)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,111,152)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + rootLayout.frame = CGRectMake(0, 0, 0, 153); + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(56,10,50,83)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(56,99,20,20)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,111,153)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + + rootLayout.frame = CGRectMake(0, 0, 100, 154); + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(10,148,50,0.5)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(66,10,20,20)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,91,154)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + rootLayout.frame = CGRectMake(0, 0, 100, 152); + v4.weight = 1; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(56,10,50,137)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(112,10,20,20)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,137,152)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + rootLayout.isFlex = YES; + v4.weight = 1; + v5.weight = 1; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(56,10,50,55.5)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(56,71.5,20,75.5)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,111,152)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + } + +} + +-(void)testWrapAndWeightAndShrink +{ + //测试数量约束布局的宽度和高度是自适应,并且有最大,最小值约束下的用例。 + { + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:1]; + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + rootLayout.padding = UIEdgeInsetsMake(10, 5, 5, 10); + rootLayout.subviewHSpace = 20; + rootLayout.subviewVSpace = 20; + + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 15, 15)); + + { + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(30, 30); + [rootLayout addSubview:v1]; + } + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 15+30, 15+30)); + + { + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(30, 30); + [rootLayout addSubview:v1]; + } + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 15+30, 15+30+20+30)); + + rootLayout.arrangedCount = 2; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 10+30+20+30+5, 10+30+5)); + + { + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(30, 30); + [rootLayout addSubview:v1]; + } + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 10+30+20+30+5, 10+30+20+30+5)); + + { + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(40, 40); + [rootLayout addSubview:v1]; + } + rootLayout.subviews.firstObject.weight = 1; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 10+30+20+40+5, 10+30+20+40+5)); + MyRectAssert(rootLayout.subviews.firstObject, CGRectMake(5,10, 40, 30)); + + rootLayout.widthSize.max(70); + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 70, 10+30+20+40+5)); + MyRectAssert(rootLayout.subviews.firstObject, CGRectMake(5,10, 30, 30)); + MyRectAssert(rootLayout.subviews.lastObject, CGRectMake(5+30+20, 10+30+20, 40, 40)); + + rootLayout.subviews.lastObject.widthSize.shrink = 1; + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout.subviews.lastObject, CGRectMake(5+30+20, 10+30+20, 5, 40)); + } + + { + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:1]; + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + rootLayout.padding = UIEdgeInsetsMake(5, 10, 10, 5); + rootLayout.subviewHSpace = 20; + rootLayout.subviewVSpace = 20; + + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 15, 15)); + + { + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(30, 30); + [rootLayout addSubview:v1]; + } + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 15+30, 15+30)); + + { + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(30, 30); + [rootLayout addSubview:v1]; + } + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 15+30+20+30, 15+30)); + + rootLayout.arrangedCount = 2; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0,10+30+5,10+30+20+30+5)); + + { + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(30, 30); + [rootLayout addSubview:v1]; + } + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 10+30+20+30+5, 10+30+20+30+5)); + + { + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(40, 40); + [rootLayout addSubview:v1]; + } + rootLayout.subviews.firstObject.weight = 1; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 10+30+20+40+5,10+30+20+40+5)); + MyRectAssert(rootLayout.subviews.firstObject, CGRectMake(10,5,30,40)); + + rootLayout.heightSize.max(70); + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 10+30+20+40+5, 70)); + MyRectAssert(rootLayout.subviews.firstObject, CGRectMake(10,5,30, 30)); + MyRectAssert(rootLayout.subviews.lastObject, CGRectMake(10+30+20,5+30+20,40, 40)); + + rootLayout.subviews.lastObject.heightSize.shrink = 1; + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout.subviews.lastObject, CGRectMake(10+30+20,5+30+20,40,5)); + } +} + +-(void)testWrapAndMinSize +{ + //测试自适应并且是最小高宽约束的情况。 + { + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:2]; + rootLayout.widthSize.equalTo(@(MyLayoutSize.wrap)).min(50); + rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0,0,50,0)); + + UIView *v = [UIView new]; + v.mySize = CGSizeMake(20, 20); + [rootLayout addSubview:v]; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0,0,50,20)); + + rootLayout.subviews.firstObject.weight = 1; + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0,0,50,20)); + MyRectAssert(rootLayout.subviews.firstObject, CGRectMake(0,0,50,20)); + + UIView *v2 = [UIView new]; + v2.mySize = CGSizeMake(40, 40); + [rootLayout addSubview:v2]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0,0,20+40,40)); + MyRectAssert(rootLayout.subviews.firstObject, CGRectMake(0,0,20,20)); + } + { + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:2]; + rootLayout.widthSize.equalTo(@(MyLayoutSize.wrap)); + rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)).min(50); + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0,0,0,50)); + + UIView *v = [UIView new]; + v.mySize = CGSizeMake(20, 20); + [rootLayout addSubview:v]; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0,0,20,50)); + + rootLayout.subviews.firstObject.weight = 1; + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0,0,20,50)); + MyRectAssert(rootLayout.subviews.firstObject, CGRectMake(0,0,20,50)); + + UIView *v2 = [UIView new]; + v2.mySize = CGSizeMake(40, 40); + [rootLayout addSubview:v2]; + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0,0,40,20+40)); + MyRectAssert(rootLayout.subviews.firstObject, CGRectMake(0,0,20,20)); + } + +} + +-(void)testRightAndBottomGravity +{ + //测试 + { + MyFlowLayout *layout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:4]; + layout.frame = CGRectMake(0, 0, 200, 200); + layout.gravity = MyGravity_Horz_Right; + layout.subviewHSpace = 5; + layout.subviewVSpace = 5; + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(50, 50); + [layout addSubview:v1]; + + [layout layoutIfNeeded]; + + MyRectAssert(v1, CGRectMake(150, 0, 50, 50)); + + UIView *v2 = [UIView new]; + v2.mySize = CGSizeMake(50, 50); + [layout addSubview:v2]; + [layout layoutIfNeeded]; + + MyRectAssert(v2, CGRectMake(150, 0, 50, 50)); + + + UIView *v3 = [UIView new]; + v3.mySize = CGSizeMake(50, 50); + [layout addSubview:v3]; + [layout layoutIfNeeded]; + + UIView *v4 = [UIView new]; + v4.mySize = CGSizeMake(50, 50); + [layout addSubview:v4]; + [layout layoutIfNeeded]; + + UIView *v5 = [UIView new]; + v5.mySize = CGSizeMake(50, 50); + [layout addSubview:v5]; + [layout layoutIfNeeded]; + + UIView *v6 = [UIView new]; + v6.mySize = CGSizeMake(50, 50); + [layout addSubview:v6]; + [layout layoutIfNeeded]; + + MyRectAssert(v6, CGRectMake(150, 55, 50, 50)); + + } + { + MyFlowLayout *layout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:4]; + layout.frame = CGRectMake(0, 0, 200, 200); + layout.gravity = MyGravity_Vert_Bottom; + layout.subviewHSpace = 5; + layout.subviewVSpace = 5; + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(50, 50); + [layout addSubview:v1]; + + [layout layoutIfNeeded]; + + MyRectAssert(v1, CGRectMake(0, 150, 50, 50)); + + UIView *v2 = [UIView new]; + v2.mySize = CGSizeMake(50, 50); + [layout addSubview:v2]; + [layout layoutIfNeeded]; + + MyRectAssert(v2, CGRectMake(0, 150, 50, 50)); + + + UIView *v3 = [UIView new]; + v3.mySize = CGSizeMake(50, 50); + [layout addSubview:v3]; + [layout layoutIfNeeded]; + + UIView *v4 = [UIView new]; + v4.mySize = CGSizeMake(50, 50); + [layout addSubview:v4]; + [layout layoutIfNeeded]; + + UIView *v5 = [UIView new]; + v5.mySize = CGSizeMake(50, 50); + [layout addSubview:v5]; + [layout layoutIfNeeded]; + + UIView *v6 = [UIView new]; + v6.mySize = CGSizeMake(50, 50); + [layout addSubview:v6]; + [layout layoutIfNeeded]; + + MyRectAssert(v6, CGRectMake(55, 150, 50, 50)); + } + + //测试 + { + MyFlowLayout *layout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:0]; + layout.frame = CGRectMake(0, 0, 200, 200); + layout.gravity = MyGravity_Horz_Right; + layout.subviewHSpace = 5; + layout.subviewVSpace = 5; + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(50, 50); + [layout addSubview:v1]; + + [layout layoutIfNeeded]; + + MyRectAssert(v1, CGRectMake(150, 0, 50, 50)); + + UIView *v2 = [UIView new]; + v2.mySize = CGSizeMake(50, 50); + [layout addSubview:v2]; + [layout layoutIfNeeded]; + + MyRectAssert(v2, CGRectMake(150, 0, 50, 50)); + + + UIView *v3 = [UIView new]; + v3.mySize = CGSizeMake(50, 50); + [layout addSubview:v3]; + [layout layoutIfNeeded]; + + UIView *v4 = [UIView new]; + v4.mySize = CGSizeMake(50, 50); + [layout addSubview:v4]; + [layout layoutIfNeeded]; + + UIView *v5 = [UIView new]; + v5.mySize = CGSizeMake(50, 50); + [layout addSubview:v5]; + [layout layoutIfNeeded]; + + UIView *v6 = [UIView new]; + v6.mySize = CGSizeMake(50, 50); + [layout addSubview:v6]; + [layout layoutIfNeeded]; + + MyRectAssert(v6, CGRectMake(150, 55, 50, 50)); + + } + { + MyFlowLayout *layout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:0]; + layout.frame = CGRectMake(0, 0, 200, 200); + layout.gravity = MyGravity_Vert_Bottom; + layout.subviewHSpace = 5; + layout.subviewVSpace = 5; + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(50, 50); + [layout addSubview:v1]; + + [layout layoutIfNeeded]; + + MyRectAssert(v1, CGRectMake(0, 150, 50, 50)); + + UIView *v2 = [UIView new]; + v2.mySize = CGSizeMake(50, 50); + [layout addSubview:v2]; + [layout layoutIfNeeded]; + + MyRectAssert(v2, CGRectMake(0, 150, 50, 50)); + + + UIView *v3 = [UIView new]; + v3.mySize = CGSizeMake(50, 50); + [layout addSubview:v3]; + [layout layoutIfNeeded]; + + UIView *v4 = [UIView new]; + v4.mySize = CGSizeMake(50, 50); + [layout addSubview:v4]; + [layout layoutIfNeeded]; + + UIView *v5 = [UIView new]; + v5.mySize = CGSizeMake(50, 50); + [layout addSubview:v5]; + [layout layoutIfNeeded]; + + UIView *v6 = [UIView new]; + v6.mySize = CGSizeMake(50, 50); + [layout addSubview:v6]; + [layout layoutIfNeeded]; + + MyRectAssert(v6, CGRectMake(55, 150, 50, 50)); + } +} + +-(void)testFillAndStretch +{ + //测试填充和拉伸。垂直数量约束流式布局的行内拉升和填充。以及整体 + + //垂直流式布局主要是高度的填充和拉伸。 + { + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:4]; + rootLayout.arrangedGravity = MyGravity_Vert_Fill; + rootLayout.frame = CGRectMake(0, 0, 200, 200); + + //一个子视图高度有约束, 一个高度为自适应, 一个没有设置任何高度约束。一个标杆高度视图。 + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(40, 40); + [rootLayout addSubview:v1]; + + UILabel *v2 = [UILabel new]; + v2.text = @"hello"; + v2.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + [rootLayout addSubview:v2]; + + UIView *v3 = [UIView new]; + v3.frame = CGRectMake(0, 0, 30, 30); + [rootLayout addSubview:v3]; + + UIView *v4 = [UIView new]; + v4.mySize = CGSizeMake(50, 50); + [rootLayout addSubview:v4]; + + UIView *v5 = [UIView new]; + v5.mySize = CGSizeMake(60, 60); + [rootLayout addSubview:v5]; + + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 200, 200)); + + MyRectAssert(v1, CGRectMake(0, 0, 40, 50)); + MyRectAssert(v2, CGRectMake(40, 0, 36.5, 50)); + MyRectAssert(v3, CGRectMake(76.5, 0, 30, 50)); + MyRectAssert(v4, CGRectMake(106.5, 0, 50, 50)); + MyRectAssert(v5, CGRectMake(0, 50, 60, 60)); + + rootLayout.arrangedGravity = MyGravity_Vert_Stretch; + [rootLayout layoutIfNeeded]; + MyRectAssert(v1, CGRectMake(0, 0, 40, 40)); + MyRectAssert(v2, CGRectMake(40, 0, 36.5, 50)); + MyRectAssert(v3, CGRectMake(76.5, 0, 30, 50)); + MyRectAssert(v4, CGRectMake(106.5, 0, 50, 50)); + MyRectAssert(v5, CGRectMake(0, 50, 60, 60)); + + + rootLayout.gravity = MyGravity_Vert_Fill; + [rootLayout layoutIfNeeded]; + MyRectAssert(v1, CGRectMake(0, 0, 40, 85)); + MyRectAssert(v2, CGRectMake(40, 0, 36.5, 95)); + MyRectAssert(v3, CGRectMake(76.5, 0, 30, 95)); + MyRectAssert(v4, CGRectMake(106.5, 0, 50, 95)); + MyRectAssert(v5, CGRectMake(0, 95, 60, 105)); + + + rootLayout.gravity = MyGravity_Vert_Stretch; + [rootLayout layoutIfNeeded]; + MyRectAssert(v1, CGRectMake(0, 0, 40, 40)); + MyRectAssert(v2, CGRectMake(40, 0, 36.5, 95)); + MyRectAssert(v3, CGRectMake(76.5, 0, 30, 95)); + MyRectAssert(v4, CGRectMake(106.5, 0, 50, 50)); + MyRectAssert(v5, CGRectMake(0, 95, 60, 60)); + } + + //水平流式布局主要是宽度和填充和拉伸 + { + MyFlowLayout *rootLayout = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Horz arrangedCount:4]; + rootLayout.arrangedGravity = MyGravity_Horz_Fill; + rootLayout.frame = CGRectMake(0, 0, 200, 200); + + //一个子视图高度有约束, 一个高度为自适应, 一个没有设置任何高度约束。一个标杆高度视图。 + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(40, 40); + [rootLayout addSubview:v1]; + + UILabel *v2 = [UILabel new]; + v2.text = @"hello"; + v2.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + [rootLayout addSubview:v2]; + + UIView *v3 = [UIView new]; + v3.frame = CGRectMake(0, 0, 30, 30); + [rootLayout addSubview:v3]; + + UIView *v4 = [UIView new]; + v4.mySize = CGSizeMake(50, 50); + [rootLayout addSubview:v4]; + + UIView *v5 = [UIView new]; + v5.mySize = CGSizeMake(60, 60); + [rootLayout addSubview:v5]; + + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 200, 200)); + + MyRectAssert(v1, CGRectMake(0, 0, 50, 40)); + MyRectAssert(v2, CGRectMake(0, 40,50,20.5)); + MyRectAssert(v3, CGRectMake(0,60.5, 50, 30)); + MyRectAssert(v4, CGRectMake(0,90.5, 50, 50)); + MyRectAssert(v5, CGRectMake(50, 0, 60, 60)); + + rootLayout.arrangedGravity = MyGravity_Horz_Stretch; + [rootLayout layoutIfNeeded]; + MyRectAssert(v1, CGRectMake(0, 0, 40, 40)); + MyRectAssert(v2, CGRectMake(0, 40, 50,20.5)); + MyRectAssert(v3, CGRectMake(0,60.5, 50, 30)); + MyRectAssert(v4, CGRectMake(0,90.5, 50, 50)); + MyRectAssert(v5, CGRectMake(50, 0, 60, 60)); + + + rootLayout.gravity = MyGravity_Horz_Fill; + [rootLayout layoutIfNeeded]; + MyRectAssert(v1, CGRectMake(0, 0, 85, 40)); + MyRectAssert(v2, CGRectMake(0, 40, 95, 20.5)); + MyRectAssert(v3, CGRectMake(0,60.5, 50+45, 30)); + MyRectAssert(v4, CGRectMake(0,90.5, 50+45, 50)); + MyRectAssert(v5, CGRectMake(50+45, 0, 60+45, 60)); + + rootLayout.gravity = MyGravity_Horz_Stretch; + [rootLayout layoutIfNeeded]; + MyRectAssert(v1, CGRectMake(0, 0, 40, 40)); + MyRectAssert(v2, CGRectMake(0, 40, 95, 20.5)); + MyRectAssert(v3, CGRectMake(0,60.5, 50+45, 30)); + MyRectAssert(v4, CGRectMake(0,90.5, 50, 50)); + MyRectAssert(v5, CGRectMake(95, 0, 60, 60)); + } + + +} + +-(void)testFlex1 +{ + //测试内容约束布局下的尺寸自适应,以及最大最小值设置的场景。 + + { + MyFlexLayout *flexLayout = MyFlexLayout.new.myFlex + .align_items(MyFlexGravity_Center) + .justify_content(MyFlexGravity_Center) + .margin(0) + .view; + + flexLayout.frame = CGRectMake(0, 0, 300, 300); + flexLayout.backgroundColor = [UIColor redColor]; + + + MyFlexLayout *contentLayout = MyFlexLayout.new.myFlex + .flex_wrap(MyFlexWrap_Wrap) + .max_width(100) + .addTo(flexLayout); + + contentLayout.backgroundColor = [UIColor greenColor]; + + UILabel *lb1 = UILabel.new.myFlex + .width(30) + .height(30) + .addTo(contentLayout); + + lb1.backgroundColor = [UIColor blueColor]; + + UILabel *lb2 = UILabel.new.myFlex + .width(40) + .height(40) + .addTo(contentLayout); + + lb2.backgroundColor = [UIColor yellowColor]; + + UILabel *lb3 = UILabel.new.myFlex + .width(50) + .height(50) + .addTo(contentLayout); + + lb3.backgroundColor = [UIColor grayColor]; + + [flexLayout layoutIfNeeded]; + [contentLayout layoutIfNeeded]; + + MyRectAssert(contentLayout, CGRectMake((300-100)/2.0, (300-90)/2.0, 100, 90)); + MyRectAssert(lb3, CGRectMake(0, 40, 50, 50)); + + contentLayout.myFlex.height(30); + [flexLayout setNeedsLayout]; + [contentLayout setNeedsLayout]; + [contentLayout layoutIfNeeded]; + [flexLayout layoutIfNeeded]; + + MyRectAssert(contentLayout, CGRectMake((300-100)/2.0, (300-30)/2.0, 100, 30)); + MyRectAssert(lb3, CGRectMake(0, 40, 50, 50)); + + } + + { + MyFlexLayout *flexLayout = MyFlexLayout.new.myFlex + .align_items(MyFlexGravity_Center) + .justify_content(MyFlexGravity_Center) + .margin(0) + .view; + + flexLayout.frame = CGRectMake(0, 0, 300, 300); + flexLayout.backgroundColor = [UIColor redColor]; + + + MyFlexLayout *contentLayout = MyFlexLayout.new.myFlex + .flex_wrap(MyFlexWrap_Wrap) + .flex_direction(MyFlexDirection_Column) + .width(MyLayoutSize.wrap) + .max_height(100) + .height(MyLayoutSize.wrap) + .addTo(flexLayout); + + contentLayout.backgroundColor = [UIColor greenColor]; + + UILabel *lb1 = UILabel.new.myFlex + .width(30) + .height(30) + .addTo(contentLayout); + + lb1.backgroundColor = [UIColor blueColor]; + + UILabel *lb2 = UILabel.new.myFlex + .width(40) + .height(40) + .addTo(contentLayout); + + lb2.backgroundColor = [UIColor yellowColor]; + + UILabel *lb3 = UILabel.new.myFlex + .width(50) + .height(50) + .addTo(contentLayout); + + lb3.backgroundColor = [UIColor grayColor]; + + [flexLayout layoutIfNeeded]; + [contentLayout layoutIfNeeded]; + + MyRectAssert(contentLayout, CGRectMake((300-90)/2.0,(300-100)/2.0,90,100)); + MyRectAssert(lb3, CGRectMake(40, 0, 50, 50)); + + contentLayout.myFlex.width(30); + [flexLayout setNeedsLayout]; + [contentLayout setNeedsLayout]; + [contentLayout layoutIfNeeded]; + [flexLayout layoutIfNeeded]; + + MyRectAssert(contentLayout, CGRectMake((300-30)/2.0, (300-100)/2.0, 30, 100)); + MyRectAssert(lb3, CGRectMake(40, 0, 50, 50)); + } + +} + +@end diff --git a/MyLayoutTests/MyFrameLayoutTestCase.m b/MyLayoutTests/MyFrameLayoutTestCase.m index f3c3a23..838f1b6 100644 --- a/MyLayoutTests/MyFrameLayoutTestCase.m +++ b/MyLayoutTests/MyFrameLayoutTestCase.m @@ -7,6 +7,8 @@ // #import "MyLayoutTestCaseBase.h" +#import "FLTest1ViewController.h" +#import "FLTest2ViewController.h" @interface MyFrameLayoutTestCase : MyLayoutTestCaseBase @@ -24,6 +26,19 @@ - (void)tearDown { [super tearDown]; } +-(void)testExample +{ + + for (int i = 1; i <=2; i++) + { + Class cls = NSClassFromString([NSString stringWithFormat:@"FLTest%dViewController", i]); + UIViewController *vc = [cls new]; + UIView *v = vc.view; + [v layoutIfNeeded]; + } + +} + -(void)testblurred { //测试位置偏移不正确或者尺寸不正确将会产生模糊显示的效果。 @@ -38,7 +53,7 @@ -(void)testblurred label1.font = [UIFont systemFontOfSize:17]; label1.myCenterY = 0; label1.myLeft = 10; - label1.wrapContentSize = YES; + label1.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); [frameLayout addSubview:label1]; @@ -48,7 +63,7 @@ -(void)testblurred label2.font = [UIFont systemFontOfSize:17]; label2.myTop = 200; label2.myLeft = 10; - label2.wrapContentSize = YES; + label2.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); [frameLayout addSubview:label2]; @@ -75,7 +90,7 @@ - (void)testWrapContent { MyFrameLayout *frameLayout = [MyFrameLayout new]; frameLayout.padding = UIEdgeInsetsMake(20, 10, 5, 6); - frameLayout.wrapContentSize = YES; + frameLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); frameLayout.zeroPadding = NO; @@ -162,6 +177,199 @@ -(void)testSizeDependent } +-(void)testWrapContent2 +{ + MyFrameLayout * dataview = [[MyFrameLayout alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 0)]; + dataview.heightSize.equalTo(@(MyLayoutSize.wrap)).min([UIScreen mainScreen].bounds.size.height); + //上面在滚动视图下。 + + MyLinearLayout * dataContentV = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + dataContentV.paddingTop = 64; + dataContentV.paddingBottom = 64; // + 10; + dataContentV.myCenterX = 0; + dataContentV.myCenterY = 0; + dataContentV.myHeight = MyLayoutSize.wrap; + dataContentV.myHorzMargin = 0; + [dataview addSubview:dataContentV]; + + + MyLinearLayout * subrootview1 = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + subrootview1.gravity = MyGravity_Horz_Fill; + subrootview1.myHeight = MyLayoutSize.wrap; + subrootview1.myHorzMargin = 0; + subrootview1.padding = UIEdgeInsetsMake(0, 10, 0, 10); + [dataContentV addSubview:subrootview1]; + + + UILabel * view1 = [UILabel new]; + view1.myTop = 120; + view1.myHeight = 140 + 180; + [subrootview1 addSubview:view1]; + + + UILabel * view2 = [UILabel new]; + view2.myHeight = 160 + 200; + [subrootview1 addSubview:view2]; + + + UILabel * subrootview2 = [UILabel new]; + subrootview2.myHeight = 150; + subrootview2.myHorzMargin = 0; + subrootview2.text = @"subrootview2"; + [dataContentV addSubview:subrootview2]; + + view2.hidden = YES; + subrootview2.hidden = YES; + + [dataview layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(dataview.frame, CGRectMake(0,0,375,667)), @"dataview rect is:%@", NSStringFromCGRect(dataview.frame)); + XCTAssertTrue(CGRectEqualToRect(dataContentV.frame, CGRectMake(0,49.5,375,568)), @"dataContentV rect is:%@", NSStringFromCGRect(dataContentV.frame)); + + + subrootview2.hidden = NO; + + [dataview layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(dataview.frame, CGRectMake(0,0,375,718)), @"dataview rect is:%@", NSStringFromCGRect(dataview.frame)); + XCTAssertTrue(CGRectEqualToRect(dataContentV.frame, CGRectMake(0,0,375,718)), @"dataContentV rect is:%@", NSStringFromCGRect(dataContentV.frame)); + + + view2.hidden = NO; + subrootview2.hidden = YES; + + [dataview layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(dataview.frame, CGRectMake(0,0,375,928)), @"dataview rect is:%@", NSStringFromCGRect(dataview.frame)); + XCTAssertTrue(CGRectEqualToRect(dataContentV.frame, CGRectMake(0,0,375,928)), @"dataContentV rect is:%@", NSStringFromCGRect(dataContentV.frame)); + + + view2.hidden = YES; + subrootview2.hidden = YES; + + [dataview layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(dataview.frame, CGRectMake(0,0,375,667)), @"dataview rect is:%@", NSStringFromCGRect(dataview.frame)); + XCTAssertTrue(CGRectEqualToRect(dataContentV.frame, CGRectMake(0,49.5,375,568)), @"dataContentV rect is:%@", NSStringFromCGRect(dataContentV.frame)); + +} + +-(void)testWrapContent3 +{ + //测试一个布局视图的尺寸是包裹的,同时里面的子视图设置了宽高。 + MyFrameLayout *rootLayout = [MyFrameLayout new]; + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + rootLayout.padding = UIEdgeInsetsMake(10, 20, 30, 40); + + //1.同时设置了上下和左右。没有设置高度。 + UIView *v1 = [UIView new]; + v1.myLeft = 10; + v1.myRight = 20; + v1.myTop = 30; + v1.myBottom = 40; + [rootLayout addSubview:v1]; + + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,90,110)), @"rootLayout rect is:%@", NSStringFromCGRect(rootLayout.frame)); + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(30,40,0,0)), @"v1 rect is:%@", NSStringFromCGRect(v1.frame)); + + + + UIView *v2 = [UIView new]; + v2.myLeft = 40; + v2.myRight = 30; + v2.myTop = 20; + v2.myBottom = 10; + v2.myWidth = 100; + v2.myHeight = 200; + [rootLayout addSubview:v2]; + + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,230,270)), @"rootLayout rect is:%@", NSStringFromCGRect(rootLayout.frame)); + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(30,40,140,160)), @"v1 rect is:%@", NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(60,30,100,200)), @"v2 rect is:%@", NSStringFromCGRect(v2.frame)); + + [v1 removeFromSuperview]; + [v2 removeFromSuperview]; + + UILabel *label = [UILabel new]; + label.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + label.text = @"您好!"; + label.myMargin = 20; + CGSize lablesz = [label sizeThatFits:CGSizeZero]; + [rootLayout addSubview:label]; + + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,60+40+lablesz.width,40+40+lablesz.height)), @"rootLayout rect is:%@", NSStringFromCGRect(rootLayout.frame)); + XCTAssertTrue(CGRectEqualToRect(label.frame, CGRectMake(20+20,10+20,lablesz.width,lablesz.height)), @"label rect is:%@", NSStringFromCGRect(label.frame)); + + + UIView *v3 = [UIView new]; + v3.myRight = 30; + v3.myBottom = 30; + v3.mySize = CGSizeMake(100, 100); + [rootLayout addSubview:v3]; + + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,20+40+30+100,10+30+30+100)), @"rootLayout rect is:%@", NSStringFromCGRect(rootLayout.frame)); + + XCTAssertTrue(CGRectEqualToRect(label.frame, CGRectMake(20+20,10+20,lablesz.width,lablesz.height)), @"label rect is:%@", NSStringFromCGRect(label.frame)); + + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(rootLayout.frame.size.width - 40 - 30 - 100 ,rootLayout.frame.size.height - 30 - 30 - 100, 100,100)), @"v3 rect is:%@", NSStringFromCGRect(v3.frame)); + + +} + +-(void)testWrapContent4 +{ + //没有约束,原生的frame值设置。 + MyFrameLayout *rootLayout = [MyFrameLayout new]; + rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + + UIView *v = [UIView new]; + v.frame = CGRectMake(0, 0, 150, 150); + [rootLayout addSubview:v]; + + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 170, 170)); + +} + +-(void)testWrapContent5 +{ + MyFrameLayout* _layoutRoot = [[MyFrameLayout alloc] init]; + _layoutRoot.myTop = + _layoutRoot.myLeft = + _layoutRoot.myRight = 0; + _layoutRoot.wrapContentHeight = YES; + _layoutRoot.paddingBottom = 12; + // _layoutRoot.backgroundColor = [UIColor whiteColor]; + //_layoutRoot.clipsToBounds = YES; + _layoutRoot.frame = CGRectMake(0, 0, 100, 0); + + UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)]; + imageView.myLeft = + imageView.myRight = 0; + imageView.myBottom = -12; + [_layoutRoot addSubview:imageView]; + + [_layoutRoot layoutIfNeeded]; + + MyRectAssert(_layoutRoot, CGRectMake(0, 0, 100, 50)); + + + [_layoutRoot setNeedsLayout]; + [_layoutRoot layoutIfNeeded]; + + MyRectAssert(_layoutRoot, CGRectMake(0, 0, 100, 50)); + +} + -(void)testPerformanceExample { [self measureBlock:^{ diff --git a/MyLayoutTests/MyGridLayoutTestCase.m b/MyLayoutTests/MyGridLayoutTestCase.m new file mode 100644 index 0000000..7d32a48 --- /dev/null +++ b/MyLayoutTests/MyGridLayoutTestCase.m @@ -0,0 +1,57 @@ +// +// MyFloatLayoutTestCase.m +// MyLayout +// +// Created by apple on 17/4/26. +// Copyright © 2017年 YoungSoft. All rights reserved. +// + +#import "MyLayoutTestCaseBase.h" +#import "GLTest1ViewController.h" +#import "GLTest2ViewController.h" +#import "GLTest3ViewController.h" +#import "GLTest4ViewController.h" +#import "GLTest5ViewController.h" + + +@interface MyGridLayoutTestCase : MyLayoutTestCaseBase + +@end + +@implementation MyGridLayoutTestCase + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + for (int i = 1; i <= 5; i++) + { + Class cls = NSClassFromString([NSString stringWithFormat:@"GLTest%dViewController", i]); + UIViewController *vc = [[cls alloc] init]; + UIView *v = vc.view; + [v setNeedsLayout]; + [v layoutIfNeeded]; + } + +} + + +- (void)testPerformanceExample { + // This is an example of a performance test case. + + [self measureBlock:^{ + // Put the code you want to measure the time of here. + + }]; +} + +@end diff --git a/MyLayoutTests/MyLayoutPerformanceTestCase.m b/MyLayoutTests/MyLayoutPerformanceTestCase.m index 3f83c64..c4d0c86 100644 --- a/MyLayoutTests/MyLayoutPerformanceTestCase.m +++ b/MyLayoutTests/MyLayoutPerformanceTestCase.m @@ -7,6 +7,8 @@ // #import "MyLayoutTestCaseBase.h" +//#import "FlexBoxLayout.h" +//#import "FBFeedView.h" //对SDAutoLayout和Masonry的测试请单独引入这两个第三方库进行测试。 @@ -151,7 +153,7 @@ -(UIView*)linearlayout_createFrame -(UIView*)linearlayout_createMyLayout { MyLinearLayout *containerView = [[MyLinearLayout alloc] initWithFrame:CGRectMake(0, 0, 100, 100) orientation:MyOrientation_Vert]; - containerView.wrapContentHeight = NO; + containerView.heightSize.equalTo(nil); for (int i = 0; i < self.counts; i++) { @@ -1900,6 +1902,207 @@ - (void)testPerformance_linearlayout_layoutUIStackView { }]; } +-(void)testPerformance_flexlayout { + + [self measureBlock:^{ + + UIScrollView *contentView = [UIScrollView new]; + contentView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-44); + + + MyFlexLayout *root = MyFlexLayout.new.myFlex + .justify_content(MyFlexGravity_Space_Around) + .align_items(MyFlexGravity_Center) + .width([UIScreen mainScreen].bounds.size.width) + .height([UIScreen mainScreen].bounds.size.height) + .addTo(contentView); + + + MyFlexLayout *div1 = MyFlexLayout.new.myFlex + .flex_direction(MyFlexDirection_Column) + .justify_content(MyFlexGravity_Space_Between) + .align_items(MyFlexGravity_Center) + .width(150) + .margin_top(20) + .addTo(root); + + UIView *child1 = UIView.new.myFlex + .width(100) + .height(100) + .addTo(div1); + + child1.backgroundColor = [UIColor blueColor]; + + + UIView *child2 = UIView.new.myFlex + .width(100) + .height(100) + .addTo(div1); + + child2.backgroundColor = [UIColor greenColor]; + + UILabel *child3 = UILabel.new.myFlex + .addTo(div1); + + child3.numberOfLines = 0; + child3.backgroundColor = [UIColor yellowColor]; + [child3 setAttributedText:[[NSAttributedString alloc] initWithString:@"testfdsfdsfdsfdsfdsfdsafdsafdsafasdkkk" attributes:@{NSFontAttributeName :[UIFont systemFontOfSize:18]}] ]; + + + + MyFlexLayout *div2 = MyFlexLayout.new.myFlex + .flex_direction(MyFlexDirection_Column) + .justify_content(MyFlexGravity_Space_Around) + .align_items(MyFlexGravity_Center) + .width(150) + .margin_top(20) + .addTo(root); + + UIView *child5 = UIView.new.myFlex + .flex_grow(1) + .width(50) + .height(50) + .margin_bottom(10) + .addTo(div2); + + child5.backgroundColor = [UIColor blueColor]; + + + + UIView *child6 = UIView.new.myFlex + .flex_grow(2) + .width(50) + .height(50) + .margin(10) + .addTo(div2); + + child6.backgroundColor = [UIColor greenColor]; + + + UIView *child7 = UIView.new.myFlex + .flex_grow(1) + .width(50) + .height(50) + .margin_bottom(10) + .addTo(div2); + + child7.backgroundColor = [UIColor yellowColor]; + + + UIView *child8 = UIView.new.myFlex + .flex_grow(1) + .width(50) + .height(50) + .margin_bottom(10) + .addTo(div2); + + child8.backgroundColor = [UIColor blackColor]; + + [root layoutIfNeeded]; + + }]; + +} + +//-(void)testPerformance_yoga { +// +// [self measureBlock:^{ +// UIScrollView *contentView = [UIScrollView new]; +// contentView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-44); +// +// +// UIView *child1 = [UIView new]; +// child1.backgroundColor = [UIColor blueColor]; +// +// [child1 fb_makeLayout:^(FBLayout *layout) { +// layout.width.height.equalTo(@100); +// }]; +// +// UIView *child2 = [UIView new]; +// child2.backgroundColor = [UIColor greenColor]; +// [child2 fb_makeLayout:^(FBLayout *layout) { +// layout.equalTo(child1); +// }]; +// +// +// UILabel *child3 = [UILabel new]; +// child3.numberOfLines = 0; +// child3.backgroundColor = [UIColor yellowColor]; +// [child3 fb_wrapContent]; +// [child3 setAttributedText:[[NSAttributedString alloc] initWithString:@"testfdsfdsfdsfdsfdsfdsafdsafdsafasdkkk" attributes:@{NSFontAttributeName :[UIFont systemFontOfSize:18]}] ]; +// +// [contentView addSubview:child1]; +// [contentView addSubview:child2]; +// [contentView addSubview:child3]; +// +// +// FBLayoutDiv *div1 = [FBLayoutDiv layoutDivWithFlexDirection:FBFlexDirectionColumn +// justifyContent:FBJustifySpaceBetween +// alignItems:FBAlignCenter +// children:@[child1, child2,child3]]; +// +// +// +// [div1 fb_makeLayout:^(FBLayout *layout) { +// layout.margin.equalToEdgeInsets(UIEdgeInsetsMake(20, 0, 0, 0)); +// layout.width.equalTo(@(150)); +// }]; +// +// +// UIView *child5 = [UIView new]; +// child5.backgroundColor = [UIColor blueColor]; +// [child5 fb_makeLayout:^(FBLayout *layout) { +// layout.width.height.equalTo(@(50)).margin.equalToEdgeInsets(UIEdgeInsetsMake(0, 0, 10, 0)).flexGrow.equalTo(@1.0); +// }]; +// +// UIView *child6 = [UIView new]; +// child6.backgroundColor = [UIColor greenColor]; +// [child6 fb_makeLayout:^(FBLayout *layout) { +// layout.equalTo(child5); +// layout.flexGrow.equalTo(@(2.0)); +// layout.margin.equalToEdgeInsets(UIEdgeInsetsMake(10, 10, 10, 10)); +// }]; +// +// +// UIView *child7 = [UIView new]; +// child7.backgroundColor = [UIColor yellowColor]; +// [child7 fb_makeLayout:^(FBLayout *layout) { +// layout.equalTo(child5); +// }]; +// +// UIView *child8 = [UIView new]; +// child8.backgroundColor = [UIColor blackColor]; +// +// [child8 fb_makeLayout:^(FBLayout *layout) { +// layout.equalTo(child5); +// }]; +// +// FBLayoutDiv *div2 =[FBLayoutDiv layoutDivWithFlexDirection:FBFlexDirectionColumn +// justifyContent:FBJustifySpaceAround +// alignItems:FBAlignCenter +// children:@[child5,child6,child7,child8]]; +// [div2 fb_makeLayout:^(FBLayout *layout) { +// layout.margin.equalToEdgeInsets(UIEdgeInsetsMake(20, 0, 0, 0)); +// layout.width.equalTo(@(150)); +// }]; +// +// [contentView addSubview:child5]; +// [contentView addSubview:child6]; +// [contentView addSubview:child7]; +// [contentView addSubview:child8]; +// +// FBLayoutDiv *root = [FBLayoutDiv layoutDivWithFlexDirection:FBFlexDirectionRow +// justifyContent:FBJustifySpaceAround +// alignItems:FBAlignCenter +// children:@[div1,div2]]; +// +// contentView.fb_contentDiv = root; +// +// [root fb_applyLayouWithSize:[UIScreen mainScreen].bounds.size]; +// }]; +// +//} + @end diff --git a/MyLayoutTests/MyLayoutTestCaseBase.h b/MyLayoutTests/MyLayoutTestCaseBase.h index 3243aff..9ba3b12 100644 --- a/MyLayoutTests/MyLayoutTestCaseBase.h +++ b/MyLayoutTests/MyLayoutTestCaseBase.h @@ -9,11 +9,18 @@ #import #import "MyLayout.h" + +#define MyRectAssert(v, rc) XCTAssert(CGRectEqualToRect(v.frame, rc), @"the real frame = %@",NSStringFromCGRect(v.frame)); +#define MyRectAssert2(v,rc1, rc2) XCTAssert(CGRectEqualToRect(rc1, rc2), @"the real frame = %@",NSStringFromCGRect(rc1)); +#define MySizeAssert(v, sz1, sz2) XCTAssert(CGSizeEqualToSize(sz1, sz2), @"the real size = %@",NSStringFromCGSize(sz1)); + + @interface MyLayoutTestCaseBase : XCTestCase -(void)startClock; -(void)endClock:(NSString*)text; + @end diff --git a/MyLayoutTests/MyLayoutTestCaseBase.m b/MyLayoutTests/MyLayoutTestCaseBase.m index a5fbdd1..26e57b4 100644 --- a/MyLayoutTests/MyLayoutTestCaseBase.m +++ b/MyLayoutTests/MyLayoutTestCaseBase.m @@ -26,6 +26,8 @@ -(void)endClock:(NSString*)text } + + - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. diff --git a/MyLayoutTests/MyLinearLayoutTestCase.m b/MyLayoutTests/MyLinearLayoutTestCase.m index d634ffa..4c8db8a 100644 --- a/MyLayoutTests/MyLinearLayoutTestCase.m +++ b/MyLayoutTests/MyLinearLayoutTestCase.m @@ -7,7 +7,19 @@ // #import "MyLayoutTestCaseBase.h" +#import "LLTest1ViewController.h" +#import "LLTest2ViewController.h" +#import "LLTest3ViewController.h" +#import "LLTest4ViewController.h" +#import "LLTest5ViewController.h" +#import "LLTest6ViewController.h" +#import "LLTest7ViewController.h" + +#import "TLTest1ViewController.h" +#import "TLTest2ViewController.h" #import "TLTest3ViewController.h" +#import "TLTest4ViewController.h" + @interface MyLinearLayoutTestCase : MyLayoutTestCaseBase @@ -28,6 +40,31 @@ - (void)tearDown { - (void)testExample { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. + + for (int i = 1; i <=7; i++) + { + Class cls = NSClassFromString([NSString stringWithFormat:@"LLTest%dViewController", i]); + UIViewController *vc = [cls new]; + UIView *v = vc.view; + [v layoutIfNeeded]; + } + + for (int i = 1; i <=3; i++) + { + Class cls = NSClassFromString([NSString stringWithFormat:@"TLTest%dViewController", i]); + UIViewController *vc = [cls new]; + UIView *v = vc.view; + [v layoutIfNeeded]; + } + + for (int i = 1; i <=12; i++) + { + Class cls = NSClassFromString([NSString stringWithFormat:@"AllTest%dViewController", i]); + UIViewController *vc = [cls new]; + UIView *v = vc.view; + [v layoutIfNeeded]; + } + } -(void)testWrapContentHeight @@ -36,23 +73,175 @@ -(void)testWrapContentHeight } -- (void)testTable1 { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. + +-(void)testSubviewSizeDependent +{//测试子视图尺寸依赖 - TLTest3ViewController *vc = [TLTest3ViewController new]; + //垂直线性布局 + { + MyLinearLayout *rootLayout1 = [[MyLinearLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 667) orientation:MyOrientation_Vert]; + rootLayout1.heightSize.equalTo(nil); + //1. 子视图宽度等于自身高度 + UILabel *label1 = [UILabel new]; + label1.myHeight = 100; + label1.widthSize.equalTo(label1.heightSize); + [rootLayout1 addSubview:label1]; + + + //2. 子视图高度等于自身宽度 + UILabel *label2 = [UILabel new]; + label2.widthSize.equalTo(@(MyLayoutSize.wrap)); + label2.heightSize.equalTo(label2.widthSize); + label2.text = @"hello World"; + [rootLayout1 addSubview:label2]; + + //3. 子视图高度等于兄弟视图高度, 子视图宽度等于兄弟视图宽度 + UILabel *label3 = [UILabel new]; + label3.heightSize.equalTo(label1.heightSize); + label3.widthSize.equalTo(label1.widthSize); + [rootLayout1 addSubview:label3]; + + //4. 子视图高度等于兄弟视图宽度, 子视图宽度等于兄弟视图高度 + UILabel *label4 = [UILabel new]; + label4.widthSize.equalTo(label2.heightSize).add(20); + label4.heightSize.equalTo(label2.widthSize).add(10).multiply(0.5); + [rootLayout1 addSubview:label4]; + + //5. 子视图宽度等于父视图宽度,高度等于宽度 + UILabel *label5 = [UILabel new]; + label5.widthSize.equalTo(rootLayout1.widthSize).add(-20); + label5.heightSize.equalTo(label5.widthSize); + [rootLayout1 addSubview:label5]; + + //6. 子视图高度等于父视图高度,宽度等于高度 + UILabel *label6 = [UILabel new]; + label6.heightSize.equalTo(rootLayout1.heightSize).multiply(0.5); + label6.widthSize.equalTo(label6.heightSize); + [rootLayout1 addSubview:label6]; + + [rootLayout1 layoutIfNeeded]; + + XCTAssertTrue(label1.frame.size.width == label1.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label1.frame)); + XCTAssertTrue(label2.frame.size.width == label2.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label2.frame)); + XCTAssertTrue(label3.frame.size.width == label1.frame.size.width && + label3.frame.size.height == label1.frame.size.height, @"label3.frame = %@",NSStringFromCGRect(label3.frame)); + XCTAssertTrue(label4.frame.size.width == (label2.frame.size.height + 20) && + label4.frame.size.height == (label2.frame.size.width * 0.5 + 10), @"label4.frame = %@",NSStringFromCGRect(label4.frame)); + + XCTAssertTrue(label5.frame.size.width == (rootLayout1.frame.size.width - 20) && + label5.frame.size.height == label5.frame.size.width, @"label5.frame = %@",NSStringFromCGRect(label5.frame)); + + XCTAssertTrue(label6.frame.size.width == label6.frame.size.height && + label6.frame.size.height == rootLayout1.frame.size.height * 0.5, @"label6.frame = %@",NSStringFromCGRect(label6.frame)); + } + - [self startClock]; + //水平线性布局 + { + MyLinearLayout *rootLayout1 = [[MyLinearLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 667) orientation:MyOrientation_Horz]; + rootLayout1.widthSize.equalTo(nil); + //1. 子视图宽度等于自身高度 + UILabel *label1 = [UILabel new]; + label1.myHeight = 100; + label1.widthSize.equalTo(label1.heightSize); + [rootLayout1 addSubview:label1]; + + + //2. 子视图高度等于自身宽度 + UILabel *label2 = [UILabel new]; + label2.widthSize.equalTo(@(MyLayoutSize.wrap)); + label2.heightSize.equalTo(label2.widthSize); + label2.text = @"hello World"; + [rootLayout1 addSubview:label2]; + + //3. 子视图高度等于兄弟视图高度, 子视图宽度等于兄弟视图宽度 + UILabel *label3 = [UILabel new]; + label3.heightSize.equalTo(label1.heightSize); + label3.widthSize.equalTo(label1.widthSize); + [rootLayout1 addSubview:label3]; + + //4. 子视图高度等于兄弟视图宽度, 子视图宽度等于兄弟视图高度 + UILabel *label4 = [UILabel new]; + label4.widthSize.equalTo(label2.heightSize).add(20); + label4.heightSize.equalTo(label2.widthSize).add(10).multiply(0.5); + [rootLayout1 addSubview:label4]; + + //5. 子视图宽度等于父视图宽度,高度等于宽度 + UILabel *label5 = [UILabel new]; + label5.widthSize.equalTo(rootLayout1.widthSize).add(-20); + label5.heightSize.equalTo(label5.widthSize); + [rootLayout1 addSubview:label5]; + + //6. 子视图高度等于父视图高度,宽度等于高度 + UILabel *label6 = [UILabel new]; + label6.heightSize.equalTo(rootLayout1.heightSize).multiply(0.5); + label6.widthSize.equalTo(label6.heightSize); + [rootLayout1 addSubview:label6]; + + + [rootLayout1 layoutIfNeeded]; + + XCTAssertTrue(label1.frame.size.width == label1.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label1.frame)); + XCTAssertTrue(label2.frame.size.width == label2.frame.size.height, @"label1.frame = %@",NSStringFromCGRect(label2.frame)); + XCTAssertTrue(label3.frame.size.width == label1.frame.size.width && + label3.frame.size.height == label1.frame.size.height, @"label3.frame = %@",NSStringFromCGRect(label3.frame)); + XCTAssertTrue(label4.frame.size.width == (label2.frame.size.height + 20) && + label4.frame.size.height == (label2.frame.size.width * 0.5 + 10), @"label4.frame = %@",NSStringFromCGRect(label4.frame)); + + XCTAssertTrue(label5.frame.size.width == (rootLayout1.frame.size.width - 20) && + label5.frame.size.height == label5.frame.size.width, @"label5.frame = %@",NSStringFromCGRect(label5.frame)); + + XCTAssertTrue(label6.frame.size.width == label6.frame.size.height && + label6.frame.size.height == rootLayout1.frame.size.height * 0.5, @"label6.frame = %@",NSStringFromCGRect(label6.frame)); + + } - UIView *view = vc.view; +} + +-(void)testSubviewSizeDependent2 +{ + //测试隐藏、比重、尺寸依赖。 + + MyLinearLayout *layout2 = [[MyLinearLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 200) orientation:MyOrientation_Horz]; + layout2.widthSize.equalTo(nil); + + + UIButton *zoneBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + zoneBtn.titleLabel.font = [UIFont systemFontOfSize:13]; + zoneBtn.titleLabel.textAlignment = NSTextAlignmentCenter; + + UILabel *nameLab = [[UILabel alloc] initWithFrame:CGRectZero]; + nameLab.numberOfLines = 1.0; + nameLab.font = [UIFont systemFontOfSize:18]; + nameLab.textAlignment = NSTextAlignmentCenter; - [self endClock:@"vc loadview"]; //>=20ms + UIButton *placeholderBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - [self startClock]; + zoneBtn.myLeft = 20; + zoneBtn.myCenterY = 0; + zoneBtn.widthSize.equalTo(zoneBtn.widthSize).add(20); + zoneBtn.heightSize.equalTo(zoneBtn.heightSize).add(3); - [view layoutIfNeeded]; + nameLab.myCenterY = 0; + nameLab.weight = 1.0; + nameLab.heightSize.equalTo(nameLab.heightSize); - [self endClock:@"view layout"]; + placeholderBtn.myCenterY = 0; + placeholderBtn.myRight = 20; + placeholderBtn.widthSize.equalTo(zoneBtn.widthSize); + placeholderBtn.heightSize.equalTo(zoneBtn.heightSize); + + [layout2 addSubview:zoneBtn]; + [layout2 addSubview:nameLab]; + [layout2 addSubview:placeholderBtn]; + + zoneBtn.visibility = MyVisibility_Gone; + placeholderBtn.visibility = MyVisibility_Gone; + + [layout2 layoutIfNeeded]; + + XCTAssertTrue(nameLab.frame.size.width == layout2.frame.size.width && + nameLab.center.y == layout2.frame.size.height * 0.5, @"nameLab.frame = %@",NSStringFromCGRect(nameLab.frame)); } @@ -62,7 +251,7 @@ -(void)testWeight MyLinearLayout* rootLayout1 = [[MyLinearLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 667) orientation:MyOrientation_Horz]; // rootLayout1.myMargin = 0; rootLayout1.backgroundColor = [UIColor redColor]; - rootLayout1.wrapContentWidth = NO; + rootLayout1.widthSize.equalTo(nil); for (int i = 0; i < 4; i++) { @@ -76,7 +265,7 @@ -(void)testWeight { button.backgroundColor = [UIColor greenColor]; } - [button setTitle:[NSString stringWithFormat:@"%zi",i] forState:UIControlStateNormal]; + [button setTitle:[NSString stringWithFormat:@"%i",i] forState:UIControlStateNormal]; button.heightSize.equalTo(rootLayout1.heightSize); [rootLayout1 addSubview:button]; } @@ -84,24 +273,665 @@ -(void)testWeight [rootLayout1 layoutIfNeeded]; XCTAssertTrue(CGRectEqualToRect(rootLayout1.subviews[0].frame, CGRectMake(0,0,94,667)), @"the button0.frame = %@",NSStringFromCGRect(rootLayout1.subviews[0].frame)); - XCTAssertTrue(CGRectEqualToRect(rootLayout1.subviews[1].frame, CGRectMake(94,0,93.5,667)), @"the button0.frame = %@",NSStringFromCGRect(rootLayout1.subviews[1].frame)); - XCTAssertTrue(CGRectEqualToRect(rootLayout1.subviews[2].frame, CGRectMake(187.5,0,94,667)), @"the button0.frame = %@",NSStringFromCGRect(rootLayout1.subviews[2].frame)); - XCTAssertTrue(CGRectEqualToRect(rootLayout1.subviews[3].frame, CGRectMake(281.5,0,93.5,667)), @"the button0.frame = %@",NSStringFromCGRect(rootLayout1.subviews[3].frame)); + XCTAssertTrue(CGRectEqualToRect(rootLayout1.subviews[1].frame, CGRectMake(94,0,94,667)), @"the button0.frame = %@",NSStringFromCGRect(rootLayout1.subviews[1].frame)); + XCTAssertTrue(CGRectEqualToRect(rootLayout1.subviews[2].frame, CGRectMake(188,0,94,667)), @"the button0.frame = %@",NSStringFromCGRect(rootLayout1.subviews[2].frame)); + XCTAssertTrue(CGRectEqualToRect(rootLayout1.subviews[3].frame, CGRectMake(282,0,94,667)), @"the button0.frame = %@",NSStringFromCGRect(rootLayout1.subviews[3].frame)); + + +} + +-(void)testWrapContentHeight2 +{ + MyLinearLayout *layout = [[MyLinearLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 0) orientation:MyOrientation_Vert]; + layout.backgroundColor = [UIColor redColor]; + layout.gravity = MyGravity_Horz_Center; + layout.padding = UIEdgeInsetsMake(15, 0, 0, 0); + layout.subviewVSpace = 12; + + + UILabel *timeLabel = UILabel.new; + timeLabel.myHorzMargin = 0; + timeLabel.myHeight = MyLayoutSize.wrap; + timeLabel.textAlignment = NSTextAlignmentCenter; + timeLabel.text = @"剩余时间为"; + [layout addSubview:timeLabel]; + + // 汇率 + MyLinearLayout *convertLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + convertLayout.backgroundColor = [UIColor greenColor]; + convertLayout.myHeight = MyLayoutSize.wrap; + convertLayout.gravity = MyGravity_Vert_Center; + convertLayout.subviewHSpace = 7; + [layout addSubview:convertLayout]; + + NSArray *items = @[@"$454", @"^sdsf"]; + for (int i=0; i"; + [convertLayout addSubview:btn]; + } + } + + [layout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(layout.frame, CGRectMake(0,0,375,81)), @"the layout.frame = %@",NSStringFromCGRect(layout.frame)); + XCTAssertTrue(CGRectEqualToRect(convertLayout.frame, CGRectMake(105,47.5,165,33.5)), @"the convertLayout.frame = %@",NSStringFromCGRect(convertLayout.frame)); + + XCTAssertTrue(CGRectEqualToRect(timeLabel.frame, CGRectMake(0,15,375,20.5)), @"the timeLabel.frame = %@",NSStringFromCGRect(timeLabel.frame)); + XCTAssertTrue(CGRectEqualToRect(convertLayout.subviews[1].frame, CGRectMake(76,6.5,18.5,20.5)), @"the convertLayout.subviews[1].frame = %@",NSStringFromCGRect(convertLayout.subviews[1].frame)); } +-(void)testWrapGravityMinMaxShrink +{ + //测试自适应,停靠,最大和最小。 + MyLinearLayout *testLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + testLayout.widthSize.min(200).max(300); + testLayout.heightSize.equalTo(@(MyLayoutSize.wrap)).min(40).max(100); + testLayout.shrinkType = MySubviewsShrink_Average; + testLayout.subviewHSpace = 10; + testLayout.gravity = MyGravity_Center; + + + UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; + [button setTitle:@"点我" forState:UIControlStateNormal]; + button.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + [testLayout addSubview:button]; + + + UILabel *label1 = [[UILabel alloc] init]; + label1.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + label1.text = @"文本1"; + label1.backgroundColor = [UIColor grayColor]; + [testLayout addSubview:label1]; + + UILabel *label2 = [[UILabel alloc] init]; + label2.text = @"文本2"; + label2.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + label2.backgroundColor = [UIColor cyanColor]; + [testLayout addSubview:label2]; + + [testLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(testLayout.frame, CGRectMake(0,0,200,40)), @"the testLayout.frame = %@",NSStringFromCGRect(testLayout.frame)); + XCTAssertTrue(CGRectEqualToRect(button.frame, CGRectMake(28,3,37,34)), @"the button.frame = %@",NSStringFromCGRect(button.frame)); + XCTAssertTrue(CGRectEqualToRect(label1.frame, CGRectMake(75,10,42.5,20.5)), @"the label1.frame = %@",NSStringFromCGRect(label1.frame)); -- (void)testPerformanceExample { - // This is an example of a performance test case. + //字体高度扩高。文字超长。 + button.titleLabel.font = [UIFont systemFontOfSize:30]; + label1.text = @"文本111111111111"; + [testLayout setNeedsLayout]; + [testLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(testLayout.frame, CGRectMake(0,0,251.5,48)), @"the testLayout.frame = %@",NSStringFromCGRect(testLayout.frame)); + XCTAssertTrue(CGRectEqualToRect(button.frame, CGRectMake(0,0,61,48)), @"the button.frame = %@",NSStringFromCGRect(button.frame)); + XCTAssertTrue(CGRectEqualToRect(label1.frame, CGRectMake(71,14,125.5,20.5)), @"the label1.frame = %@",NSStringFromCGRect(label1.frame)); + - [self measureBlock:^{ - // Put the code you want to measure the time of here. +// label1.text = @"文本111111111111111111111111111"; +// label1.mySize = CGSizeMake(400, 20.5); +// +// [testLayout setNeedsLayout]; +// [testLayout layoutIfNeeded]; +// +// XCTAssertTrue(CGRectEqualToRect(testLayout.frame, CGRectMake(0,0,330,48)), @"the testLayout.frame = %@",NSStringFromCGRect(testLayout.frame)); +// XCTAssertTrue(CGRectEqualToRect(button.frame, CGRectMake(0,0,44.5,48)), @"the button.frame = %@",NSStringFromCGRect(button.frame)); +// XCTAssertTrue(CGRectEqualToRect(label1.frame, CGRectMake(54.5,14,237,20.5)), @"the label1.frame = %@",NSStringFromCGRect(label1.frame)); + +} + +-(void)testBetweenAndAround +{ + //测试布局视图的gravity属性的between和around特性。 + + MyLinearLayout *layout = [[MyLinearLayout alloc] initWithFrame:CGRectMake(0, 0, 100, 100) orientation:MyOrientation_Vert]; + layout.heightSize.equalTo(nil); + + //一个子视图下的垂直和水平的差异。 + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(30, 30); + [layout addSubview:v1]; + + layout.orientation = MyOrientation_Vert; + layout.gravity = MyGravity_Vert_Between; + [layout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,0,30,30)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + + + layout.orientation = MyOrientation_Horz; + layout.gravity = MyGravity_Horz_Between; + [layout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,0,30,30)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + + + layout.orientation = MyOrientation_Vert; + layout.gravity = MyGravity_Vert_Around; + [layout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,35,30,30)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + + + layout.orientation = MyOrientation_Horz; + layout.gravity = MyGravity_Horz_Around; + [layout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(35,0,30,30)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + + + //两个子视图 + UIView *v2 = [UIView new]; + v2.mySize = CGSizeMake(30, 30); + [layout addSubview:v2]; + + layout.orientation = MyOrientation_Vert; + layout.gravity = MyGravity_Vert_Between; + [layout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(0,70,30,30)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + + + layout.orientation = MyOrientation_Horz; + layout.gravity = MyGravity_Horz_Between; + [layout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(70,0,30,30)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + + + layout.orientation = MyOrientation_Vert; + layout.gravity = MyGravity_Vert_Around; + [layout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(0,60,30,30)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + + + layout.orientation = MyOrientation_Horz; + layout.gravity = MyGravity_Horz_Around; + [layout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(60,0,30,30)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + +} + +-(void)testFillAndStretch +{ + //子视图有几个设置了约束,有几个没有设置约束。分别展现Fill 和Stretch的差异。 + if (1){ + MyLinearLayout *rootLayout = [[MyLinearLayout alloc] initWithFrame:CGRectMake(0, 0, 0, 400) orientation:MyOrientation_Vert]; + rootLayout.padding = UIEdgeInsetsMake(10, 10, 20, 20); + rootLayout.widthSize.equalTo(@(MyLayoutSize.wrap)); + rootLayout.heightSize.equalTo(nil); + + //视图1宽度依赖父视图,高度设置。 + UILabel *v1 = [UILabel new]; + v1.text = @"你好"; //iPhone8模拟器 + v1.myHorzMargin = 10; + v1.myHeight = MyLayoutSize.wrap; + [rootLayout addSubview:v1]; + + //视图2宽度定值,右边对齐,高度不设置。 + UIView *v2 = [UIView new]; + v2.myRight = 20; + v2.myWidth = 50; + [rootLayout addSubview:v2]; + + //视图3宽度定值,居中对齐,高度设置。 + UIView *v3 = [UIView new]; + v3.widthSize.equalTo(v2.widthSize).add(20); + v3.myCenterX = 0; + v3.myHeight = 40; + [rootLayout addSubview:v3]; + + //视图4设置左右边距,并且宽度定值,高度不设置。 + UIView *v4 = [UIView new]; + v4.myHorzMargin = 30; + v4.myWidth = 100; + [rootLayout addSubview:v4]; + + //视图5设置宽度依赖父视图,高度设置。 + UIView *v5 = [UIView new]; + v5.widthSize.equalTo(rootLayout.widthSize).multiply(0.5); + v5.myHeight = 40; + [rootLayout addSubview:v5]; - }]; + rootLayout.gravity = MyGravity_Vert_Fill; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,190,400)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(20,10,140,74.5)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(100,84.5,50,54)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(55,138.5,70,94)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(40,232,100,54)), @"the v4 .frame = %@",NSStringFromCGRect(v4.frame)); + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(10,286,80,94)), @"the v5 .frame = %@",NSStringFromCGRect(v5.frame)); + } + + if (1){ + MyLinearLayout *rootLayout = [[MyLinearLayout alloc] initWithFrame:CGRectMake(0, 0, 0, 400) orientation:MyOrientation_Vert]; + rootLayout.padding = UIEdgeInsetsMake(10, 10, 20, 20); + rootLayout.widthSize.equalTo(@(MyLayoutSize.wrap)); + rootLayout.heightSize.equalTo(nil); + + //视图1宽度依赖父视图,高度设置。 + UILabel *v1 = [UILabel new]; + v1.text = @"你好"; //iPhone8模拟器 + v1.myHorzMargin = 10; + v1.myHeight = MyLayoutSize.wrap; + [rootLayout addSubview:v1]; + + //视图2宽度定值,右边对齐,高度不设置。 + UIView *v2 = [UIView new]; + v2.myRight = 20; + v2.myWidth = 50; + [rootLayout addSubview:v2]; + + //视图3宽度定值,居中对齐,高度设置。 + UIView *v3 = [UIView new]; + v3.widthSize.equalTo(v2.widthSize).add(20); + v3.myCenterX = 0; + v3.myHeight = 40; + [rootLayout addSubview:v3]; + + //视图4设置左右边距,并且宽度定值,高度不设置。 + UIView *v4 = [UIView new]; + v4.myHorzMargin = 30; + v4.myWidth = 100; + [rootLayout addSubview:v4]; + + //视图5设置宽度依赖父视图,高度设置。 + UIView *v5 = [UIView new]; + v5.widthSize.equalTo(rootLayout.widthSize).multiply(0.5); + v5.myHeight = 40; + [rootLayout addSubview:v5]; + + rootLayout.gravity = MyGravity_Vert_Stretch; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,190,400)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(20,10,140,110.5)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(100,120.5,50,90)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(55,210,70,40)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(40,250,100,90)), @"the v4 .frame = %@",NSStringFromCGRect(v4.frame)); + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(10,340,80,40)), @"the v5 .frame = %@",NSStringFromCGRect(v5.frame)); + } + + if (1){ + MyLinearLayout *rootLayout = [[MyLinearLayout alloc] initWithFrame:CGRectMake(0, 0, 400, 0) orientation:MyOrientation_Horz]; + rootLayout.padding = UIEdgeInsetsMake(10, 10, 20, 20); + rootLayout.widthSize.equalTo(nil); + rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + + //视图1高度依赖父视图,宽度设置。 + UILabel *v1 = [UILabel new]; + v1.text = @"你好"; //iPhone8模拟器 + v1.myVertMargin = 10; + v1.myWidth = MyLayoutSize.wrap; + [rootLayout addSubview:v1]; + + //视图2高度定值,下边对齐,宽度不设置。 + UIView *v2 = [UIView new]; + v2.myBottom = 20; + v2.myHeight = 50; + [rootLayout addSubview:v2]; + + //视图3高度定值,居中对齐,宽度设置。 + UIView *v3 = [UIView new]; + v3.heightSize.equalTo(v2.heightSize).add(20); + v3.myCenterY = 0; + v3.myWidth = 40; + [rootLayout addSubview:v3]; + + //视图4设置上下边距,并且高度定值,宽度不设置。 + UIView *v4 = [UIView new]; + v4.myVertMargin = 30; + v4.myHeight = 100; + [rootLayout addSubview:v4]; + + //视图5设置高度依赖父视图,宽度设置。 + UIView *v5 = [UIView new]; + v5.heightSize.equalTo(rootLayout.heightSize).multiply(0.5); + v5.myWidth = 40; + [rootLayout addSubview:v5]; + + rootLayout.gravity = MyGravity_Horz_Fill; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,400,190)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(10,20,86,140)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(96,100,51,50)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(147,55,91,70)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(238,40,51,100)), @"the v4 .frame = %@",NSStringFromCGRect(v4.frame)); + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(289,10,91,80)), @"the v5 .frame = %@",NSStringFromCGRect(v5.frame)); + } + + if (1){ + MyLinearLayout *rootLayout = [[MyLinearLayout alloc] initWithFrame:CGRectMake(0, 0, 400, 0) orientation:MyOrientation_Horz]; + rootLayout.padding = UIEdgeInsetsMake(10, 10, 20, 20); + rootLayout.widthSize.equalTo(nil); + rootLayout.heightSize.equalTo(@(MyLayoutSize.wrap)); + + //视图1高度依赖父视图,宽度设置。 + UILabel *v1 = [UILabel new]; + v1.text = @"你好"; //iPhone8模拟器 + v1.myVertMargin = 10; + v1.myWidth = MyLayoutSize.wrap; + [rootLayout addSubview:v1]; + + //视图2高度定值,下边对齐,宽度不设置。 + UIView *v2 = [UIView new]; + v2.myBottom = 20; + v2.myHeight = 50; + [rootLayout addSubview:v2]; + + //视图3高度定值,居中对齐,宽度设置。 + UIView *v3 = [UIView new]; + v3.heightSize.equalTo(v2.heightSize).add(20); + v3.myCenterY = 0; + v3.myWidth = 40; + [rootLayout addSubview:v3]; + + //视图4设置上下边距,并且高度定值,宽度不设置。 + UIView *v4 = [UIView new]; + v4.myVertMargin = 30; + v4.myHeight = 100; + [rootLayout addSubview:v4]; + + //视图5设置高度依赖父视图,宽度设置。 + UIView *v5 = [UIView new]; + v5.heightSize.equalTo(rootLayout.heightSize).multiply(0.5); + v5.myWidth = 40; + [rootLayout addSubview:v5]; + + rootLayout.gravity = MyGravity_Horz_Stretch; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(rootLayout.frame, CGRectMake(0,0,400,190)), @"the rootLayout.frame = %@",NSStringFromCGRect(rootLayout.frame)); + + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(10,20,120,140)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(130,100,85,50)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(215,55,40,70)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(255,40,85,100)), @"the v4 .frame = %@",NSStringFromCGRect(v4.frame)); + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(340,10,40,80)), @"the v5 .frame = %@",NSStringFromCGRect(v5.frame)); + } + + + + +} + +-(void)testequalizeSubviews +{ + //测试均分视图的能力。 + { + MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + rootLayout.heightSize.equalTo(@300); + rootLayout.frame = CGRectMake(0, 0, 100, 300); + rootLayout.gravity = MyGravity_Horz_Fill; + + UIView *v1 = [UIView new]; + v1.myHeight = 10; + UIView *v2 = [UIView new]; + v2.myHeight = 10; + UIView *v3 = [UIView new]; + v3.myHeight = 10; + + [rootLayout addSubview:v1]; + [rootLayout addSubview:v2]; + [rootLayout addSubview:v3]; + + [rootLayout equalizeSubviews:YES]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,43,100,43)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(0,129,100,43)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(0,215,100,43)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + + [rootLayout equalizeSubviews:NO]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,0,100,60)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(0,120,100,60)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(0,240,100,60)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + + v1.weight = 0; + v2.weight = 0; + v3.weight = 0; + [rootLayout equalizeSubviewsSpace:YES]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,67.5,100,10)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(0,67.5+10+67.5,100,10)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(0,67.5*3+10*2,100,10)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + v1.myTop = 0; + v1.myBottom = 0; + v2.myTop = 0; + v2.myBottom = 0; + v3.myTop = 0; + v3.myBottom = 0; + [rootLayout equalizeSubviewsSpace:NO]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,0,100,10)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(0,135+10,100,10)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(0,135+10+135+10,100,10)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + v1.weight = 0; + v2.weight = 0; + v3.weight = 0; + v1.myTop = 0; + v1.myBottom = 0; + v2.myTop = 0; + v2.myBottom = 0; + v3.myTop = 0; + v3.myBottom = 0; + [rootLayout equalizeSubviews:YES withSpace:10]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,10,100,86.5)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(0,106.5,100,86.5)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(0,203,100,86.5)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + v1.weight = 0; + v2.weight = 0; + v3.weight = 0; + [rootLayout equalizeSubviews:NO withSpace:10]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,0,100,93.5)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(0,103.5,100,93.5)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(0,207,100,93.5)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + } + + + { + MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + rootLayout.widthSize.equalTo(@300); + rootLayout.frame = CGRectMake(0, 0, 300, 100); + rootLayout.gravity = MyGravity_Vert_Fill; + + UIView *v1 = [UIView new]; + v1.myWidth = 10; + UIView *v2 = [UIView new]; + v2.myWidth = 10; + UIView *v3 = [UIView new]; + v3.myWidth = 10; + + [rootLayout addSubview:v1]; + [rootLayout addSubview:v2]; + [rootLayout addSubview:v3]; + + [rootLayout equalizeSubviews:YES]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(43,0,43,100)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(129,0,43,100)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(215,0,43,100)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + + + [rootLayout equalizeSubviews:NO]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,0,60,100)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(120,0,60,100)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(240,0,60,100)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + v1.weight = 0; + v2.weight = 0; + v3.weight = 0; + [rootLayout equalizeSubviewsSpace:YES]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(67.5,0,10,100)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(67.5+10+67.5,0,10,100)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(67.5*3+10*2,0,10,100)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + v1.myLeading = 0; + v1.myTrailing = 0; + v2.myLeading = 0; + v2.myTrailing = 0; + v3.myLeading = 0; + v3.myTrailing = 0; + [rootLayout equalizeSubviewsSpace:NO]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,0,10,100)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(135+10,0,10,100)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(135+10+135+10,0,10,100)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + v1.weight = 0; + v2.weight = 0; + v3.weight = 0; + v1.myLeading = 0; + v1.myTrailing = 0; + v2.myLeading = 0; + v2.myTrailing = 0; + v3.myLeading = 0; + v3.myTrailing = 0; + [rootLayout equalizeSubviews:YES withSpace:10]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(10,0,86.5,100)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(106.5,0,86.5,100)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(203,0,86.5,100)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + + v1.weight = 0; + v2.weight = 0; + v3.weight = 0; + [rootLayout equalizeSubviews:NO withSpace:10]; + [rootLayout layoutIfNeeded]; + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(0,0,93.5,100)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(103.5,0,93.5,100)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(207,0,93.5,100)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + } + +} + +-(void)testMaxAndMin +{ + MyFrameLayout *rootLayout = [MyFrameLayout new]; + rootLayout.frame = CGRectMake(0, 0, 375, 667); + + // B 视图 + UIScrollView *scrollview = [[UIScrollView alloc] init]; + scrollview.backgroundColor = [UIColor blueColor]; + scrollview.myHorzMargin = 0; + scrollview.heightSize.equalTo(@(MyLayoutSize.wrap)).min(280).uBound(rootLayout.heightSize, 66.5, 0.5); + [rootLayout addSubview:scrollview]; + + // 内容C视图 + MyLinearLayout * backLinear = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + backLinear.backgroundColor = [UIColor greenColor]; + backLinear.myHorzMargin = 0; + backLinear.heightSize.equalTo(@(MyLayoutSize.wrap)).min(280); + backLinear.gravity = MyGravity_Vert_Bottom; + [scrollview addSubview:backLinear]; + + UIView *v = [UIView new]; + v.myHeight = 100; + v.myWidth = 100; + v.backgroundColor = [UIColor redColor]; + [backLinear addSubview:v]; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(scrollview.frame, CGRectMake(0,0,375,280)), @"the scrollview.frame = %@",NSStringFromCGRect(scrollview.frame)); + XCTAssertTrue(CGSizeEqualToSize(scrollview.contentSize, CGSizeMake(375, 280)), @"the scrollview.contentSize = %@",NSStringFromCGSize(scrollview.contentSize)); + XCTAssertTrue(CGRectEqualToRect(backLinear.frame, CGRectMake(0,0,375,280)), @"the backLinear.frame = %@",NSStringFromCGRect(backLinear.frame)); + XCTAssertTrue(CGRectEqualToRect(v.frame, CGRectMake(0,180,100,100)), @"the v.frame = %@",NSStringFromCGRect(v.frame)); + + + v.myHeight = 350; + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(scrollview.frame, CGRectMake(0,0,375,350)), @"the scrollview.frame = %@",NSStringFromCGRect(scrollview.frame)); + XCTAssertTrue(CGSizeEqualToSize(scrollview.contentSize, CGSizeMake(375, 350)), @"the scrollview.contentSize = %@",NSStringFromCGSize(scrollview.contentSize)); + XCTAssertTrue(CGRectEqualToRect(backLinear.frame, CGRectMake(0,0,375,350)), @"the backLinear.frame = %@",NSStringFromCGRect(backLinear.frame)); + XCTAssertTrue(CGRectEqualToRect(v.frame, CGRectMake(0,0,100,350)), @"the v.frame = %@",NSStringFromCGRect(v.frame)); + + v.myHeight = 500; + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + + XCTAssertTrue(CGRectEqualToRect(scrollview.frame, CGRectMake(0,0,375,400)), @"the scrollview.frame = %@",NSStringFromCGRect(scrollview.frame)); + XCTAssertTrue(CGSizeEqualToSize(scrollview.contentSize, CGSizeMake(375, 500)), @"the scrollview.contentSize = %@",NSStringFromCGSize(scrollview.contentSize)); + XCTAssertTrue(CGRectEqualToRect(backLinear.frame, CGRectMake(0,0,375,500)), @"the backLinear.frame = %@",NSStringFromCGRect(backLinear.frame)); + XCTAssertTrue(CGRectEqualToRect(v.frame, CGRectMake(0,0,100,500)), @"the v.frame = %@",NSStringFromCGRect(v.frame)); + +} + +-(void)testNoLayoutConstraint1 +{ + //这用来测试没有设置约束,直接通过frame来得到布局尺寸自适应的情况。 + { + MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + + UIView *v1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + [rootLayout addSubview:v1]; + + UIView *v2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)]; + [rootLayout addSubview:v2]; + + UIView *v3 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)]; + [rootLayout addSubview:v3]; + + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 300, 100+200+300)); + } + + { + MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + + UIView *v1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + [rootLayout addSubview:v1]; + + UIView *v2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)]; + [rootLayout addSubview:v2]; + + UIView *v3 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)]; + [rootLayout addSubview:v3]; + + [rootLayout layoutIfNeeded]; + MyRectAssert(rootLayout, CGRectMake(0, 0, 100+200+300,300)); + } + + + +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + + +// [self measureBlock:^{ +// // Put the code you want to measure the time of here. +// +// }]; +// } diff --git a/MyLayoutTests/MyNoLayoutSuperviewTestCase.m b/MyLayoutTests/MyNoLayoutSuperviewTestCase.m index 97e2ddf..a4d1ae8 100644 --- a/MyLayoutTests/MyNoLayoutSuperviewTestCase.m +++ b/MyLayoutTests/MyNoLayoutSuperviewTestCase.m @@ -28,7 +28,7 @@ - (void)testExample1 { //测试停靠到非布局父视图上的布局。 - [MyBaseLayout setIsRTL:YES]; + [MyBaseLayout setIsRTL:NO]; UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 400)]; @@ -64,8 +64,7 @@ - (void)testExample1 { MyFrameLayout *layout6 = [MyFrameLayout new]; - layout6.wrapContentWidth = YES; - layout6.wrapContentHeight = YES; + layout6.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); layout6.myCenterX = 20; layout6.myBottom = 20; [containerView addSubview:layout6]; @@ -76,6 +75,12 @@ - (void)testExample1 { v1.mySize = CGSizeMake(60, 60); [layout6 addSubview:v1]; [layout6 layoutIfNeeded]; + + MyFrameLayout *layout7 = [MyFrameLayout new]; + layout7.mySize = CGSizeMake(40, 40); + layout7.myMargin = 20; + [containerView addSubview:layout7]; + if ([MyBaseLayout isRTL]) @@ -86,6 +91,7 @@ - (void)testExample1 { XCTAssertTrue(CGRectEqualToRect(layout4.frame, CGRectMake(70, 60, 40, 300)), @"layout4 frame=%@", NSStringFromCGRect(layout4.frame)); XCTAssertTrue(CGRectEqualToRect(layout5.frame, CGRectMake(100, 200, 40, 80)), @"layout5 frame=%@", NSStringFromCGRect(layout5.frame)); XCTAssertTrue(CGRectEqualToRect(layout6.frame, CGRectMake(45, 310, 70, 70)), @"layout6 frame=%@", NSStringFromCGRect(layout6.frame)); + XCTAssertTrue(CGRectEqualToRect(layout7.frame, CGRectMake(140, 20, 40, 40)), @"layout7 frame=%@", NSStringFromCGRect(layout7.frame)); } else @@ -97,6 +103,8 @@ - (void)testExample1 { XCTAssertTrue(CGRectEqualToRect(layout4.frame, CGRectMake(90, 60, 40, 300)), @"layout4 frame=%@", NSStringFromCGRect(layout4.frame)); XCTAssertTrue(CGRectEqualToRect(layout5.frame, CGRectMake(100, 200, 40, 80)), @"layout5 frame=%@", NSStringFromCGRect(layout5.frame)); XCTAssertTrue(CGRectEqualToRect(layout6.frame, CGRectMake(85, 310, 70, 70)), @"layout6 frame=%@", NSStringFromCGRect(layout6.frame)); + XCTAssertTrue(CGRectEqualToRect(layout7.frame, CGRectMake(20, 20, 40, 40)), @"layout7 frame=%@", NSStringFromCGRect(layout7.frame)); + } diff --git a/MyLayoutTests/MyPathLayoutTestCase.m b/MyLayoutTests/MyPathLayoutTestCase.m new file mode 100644 index 0000000..287ce34 --- /dev/null +++ b/MyLayoutTests/MyPathLayoutTestCase.m @@ -0,0 +1,50 @@ +// +// MyFloatLayoutTestCase.m +// MyLayout +// +// Created by apple on 17/4/26. +// Copyright © 2017年 YoungSoft. All rights reserved. +// + +#import "MyLayoutTestCaseBase.h" + +@interface MyPathLayoutTestCase : MyLayoutTestCaseBase + +@end + +@implementation MyPathLayoutTestCase + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + for (int i = 1; i <= 5; i++) + { + Class cls = NSClassFromString([NSString stringWithFormat:@"PLTest%dViewController", i]); + UIViewController *vc = [[cls alloc] init]; + UIView *v = vc.view; + [v setNeedsLayout]; + [v layoutIfNeeded]; + } +} + + +- (void)testPerformanceExample { + // This is an example of a performance test case. + + [self measureBlock:^{ + // Put the code you want to measure the time of here. + + }]; +} + +@end diff --git a/MyLayoutTests/MyRelativeLayoutTestCase.m b/MyLayoutTests/MyRelativeLayoutTestCase.m index fba316d..2d7bec1 100644 --- a/MyLayoutTests/MyRelativeLayoutTestCase.m +++ b/MyLayoutTests/MyRelativeLayoutTestCase.m @@ -8,6 +8,10 @@ #import "MyLayoutTestCaseBase.h" #import "RLTest1ViewController.h" +#import "RLTest2ViewController.h" +#import "RLTest3ViewController.h" +#import "RLTest4ViewController.h" +#import "RLTest5ViewController.h" @interface MyRelativeLayoutTestCase : MyLayoutTestCaseBase @@ -21,22 +25,25 @@ @implementation MyRelativeLayoutTestCase - (void)setUp { [super setUp]; // Put setup code here. This method is called before the invocation of each test method in the class. - self.vc = [RLTest1ViewController new]; } - (void)tearDown { // Put teardown code here. This method is called after the invocation of each test method in the class. - self.vc = nil; [super tearDown]; } -(void)testRLTest1VC { - - UIView *v = self.vc.view; - [self startClock]; - [v layoutIfNeeded]; - [self endClock:@"RLTest1"]; + + for (int i = 1; i <= 6; i++) + { + Class cls = NSClassFromString([NSString stringWithFormat:@"RLTest%dViewController", i]); + UIViewController *vc = [[cls alloc] init]; + UIView *v = vc.view; + [v setNeedsLayout]; + [v layoutIfNeeded]; + } + } @@ -50,7 +57,7 @@ - (void)testHidden { MyRelativeLayout *rootLayout = [MyRelativeLayout new]; rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - rootLayout.wrapContentHeight = YES; + rootLayout.myHeight = MyLayoutSize.wrap; UILabel *shopNameLabel = [UILabel new]; shopNameLabel.topPos.equalTo(rootLayout.topPos).offset(10); @@ -104,11 +111,11 @@ - (void)testHidden { -(void)testWrapContentHeight1 { MyRelativeLayout *rootLayout = [MyRelativeLayout new]; - rootLayout.wrapContentHeight = YES; - rootLayout.bottomPadding = 20; - rootLayout.topPadding = 10; - rootLayout.leadingPadding = 10; - rootLayout.trailingPadding = 10; + rootLayout.myHeight = MyLayoutSize.wrap; + rootLayout.paddingBottom = 20; + rootLayout.paddingTop = 10; + rootLayout.paddingLeading = 10; + rootLayout.paddingTrailing = 10; CGSize size0 = [rootLayout sizeThatFits:CGSizeMake(320, 0)]; @@ -168,8 +175,7 @@ -(void)testWrapContentHeight1 //125 MyLinearLayout * lineLayout=[[MyLinearLayout alloc]initWithOrientation:MyOrientation_Vert]; - lineLayout.wrapContentHeight=YES; - lineLayout.wrapContentWidth=NO; + lineLayout.myHeight = MyLayoutSize.wrap; lineLayout.topPos.equalTo(orderLabel.bottomPos).offset(5); lineLayout.myLeft=lineLayout.myRight=10; [rootLayout addSubview:lineLayout]; @@ -201,8 +207,8 @@ -(void)testWrapContentHeight1 for (int i = 0; i < 3; i++) { MyLinearLayout *horzLayout=[[MyLinearLayout alloc]initWithOrientation:MyOrientation_Horz]; - horzLayout.wrapContentHeight=YES; - horzLayout.wrapContentWidth=NO; + horzLayout.myHeight = MyLayoutSize.wrap; + horzLayout.widthSize.equalTo(nil); horzLayout.myLeft=horzLayout.myRight=0; UILabel *v1 = [UILabel new]; @@ -284,7 +290,7 @@ -(void)testWrapContentHeight1 -(void)testWrapContentHeight2 { MyRelativeLayout *rootRelativeView = [MyRelativeLayout new]; - rootRelativeView.wrapContentHeight = YES; + rootRelativeView.myHeight = MyLayoutSize.wrap; UIView *verticalLine = [UIView new]; verticalLine.leftPos.equalTo(rootRelativeView.leftPos).offset(16); @@ -307,7 +313,7 @@ -(void)testWrapContentHeight2 [rootRelativeView addSubview:dateLabel]; UILabel *newsTitleLabel = [UILabel new]; - newsTitleLabel.wrapContentHeight = YES; + newsTitleLabel.myHeight = MyLayoutSize.wrap; newsTitleLabel.topPos.equalTo(dateLabel.bottomPos).offset(5); newsTitleLabel.leftPos.equalTo(dateLabel.leftPos); newsTitleLabel.rightPos.equalTo(dateLabel.rightPos); @@ -336,7 +342,7 @@ -(void)testWrapContentHeight2 -(void)testWrapContentHeight3 { MyRelativeLayout * midView = [MyRelativeLayout new]; - midView.wrapContentSize = YES; + midView.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); UILabel * cartypeLabel = [UILabel new]; cartypeLabel.mySize = CGSizeMake(90, 30); @@ -354,7 +360,7 @@ -(void)testWrapContentHeight3 leftView.heightSize.equalTo(@8); leftView.widthSize.equalTo(@8); leftView.leftPos.equalTo(@0); - leftView.centerYPos.equalTo(line.centerYPos); //118,37 + leftView.centerYPos.equalTo(line.centerYPos); //118,41 [midView addSubview:leftView]; line.leftPos.equalTo(leftView.rightPos); @@ -365,7 +371,7 @@ -(void)testWrapContentHeight3 rightView.widthSize.equalTo(@8); rightView.leftPos.equalTo(line.rightPos); rightView.centerYPos.equalTo(line.centerYPos); - [midView addSubview:rightView]; //126, 37 + [midView addSubview:rightView]; //126, 41 UILabel * numLabel = [UILabel new]; numLabel.mySize = CGSizeMake(40, 20); @@ -376,18 +382,17 @@ -(void)testWrapContentHeight3 CGSize sz = [midView sizeThatFits:CGSizeZero]; NSLog(@"%@",NSStringFromCGSize(sz)); - + MySizeAssert(midView, sz, CGSizeMake(126, 62)); } -(void)testWrapContentWidth1 { MyRelativeLayout *rootLayout = [MyRelativeLayout new]; - rootLayout.wrapContentHeight = YES; - rootLayout.wrapContentWidth = YES; - rootLayout.bottomPadding = 20; - rootLayout.topPadding = 10; - rootLayout.leadingPadding = 10; - rootLayout.trailingPadding = 10; + rootLayout.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + rootLayout.paddingBottom = 20; + rootLayout.paddingTop = 10; + rootLayout.paddingLeading = 10; + rootLayout.paddingTrailing = 10; XCTAssertTrue(CGSizeEqualToSize([rootLayout sizeThatFits:CGSizeMake(0, 0)], CGSizeMake(20, 30)), @"size is:%@", NSStringFromCGSize([rootLayout sizeThatFits:CGSizeMake(0, 0)])); @@ -406,10 +411,10 @@ -(void)testWrapContentWidth1 rightLabel.rightPos.equalTo(rootLayout.rightPos).offset(1); [rootLayout addSubview:rightLabel]; - titleLabel.rightPos.equalTo(rightLabel.leftPos).offset(10); + rightLabel.leftPos.equalTo(titleLabel.rightPos).offset(10); - XCTAssertTrue(CGSizeEqualToSize([rootLayout sizeThatFits:CGSizeMake(0, 0)], CGSizeMake(86, 50)), @"size is:%@", NSStringFromCGSize([rootLayout sizeThatFits:CGSizeMake(0, 0)])); + XCTAssertTrue(CGSizeEqualToSize([rootLayout sizeThatFits:CGSizeMake(0, 0)], CGSizeMake(176, 50)), @"size is:%@", NSStringFromCGSize([rootLayout sizeThatFits:CGSizeMake(0, 0)])); UILabel *allMoneyLabel = [UILabel new]; @@ -446,7 +451,7 @@ -(void)testWrapContentWidth1 [rootLayout addSubview:orderLabel]; - XCTAssertTrue(CGSizeEqualToSize([rootLayout sizeThatFits:CGSizeMake(0, 0)], CGSizeMake(86, 125)), @"size is:%@", NSStringFromCGSize([rootLayout sizeThatFits:CGSizeMake(0, 0)])); + XCTAssertTrue(CGSizeEqualToSize([rootLayout sizeThatFits:CGSizeMake(0, 0)], CGSizeMake(176, 125)), @"size is:%@", NSStringFromCGSize([rootLayout sizeThatFits:CGSizeMake(0, 0)])); } @@ -474,7 +479,7 @@ -(void)testWrapContentWidth2 MyRelativeLayout *rootLayout = [MyRelativeLayout new]; - rootLayout.wrapContentWidth = YES; + rootLayout.myWidth = MyLayoutSize.wrap; //随机生成N个数字保存到数组里面。 @@ -666,28 +671,24 @@ -(void)testDemo1 MyRelativeLayout *_layoutRoot = nil; MyRelativeLayout* relative = [MyRelativeLayout new]; - // relative.widthSize.equalTo(self.contentView.widthSize); - relative.wrapContentHeight = YES; + // relative.widthSize.equalTo(self.contentView.widthSize); + relative.myHeight = MyLayoutSize.wrap; + relative.myHorzMargin = 0; _layoutRoot = relative; + // [self.view addSubview:_layoutRoot]; // 头像区域 MyLinearLayout* linear = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; - linear.wrapContentSize = YES; + linear.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); linear.padding = UIEdgeInsetsMake(12, 15, 12, 15); linear.myLeading = 0.0f; [_layoutRoot addSubview:linear]; MyLinearLayout *_layoutPortrait = linear; // 头像 - CGFloat pWidth = 40.0f; UIImageView* imgv = [UIImageView new]; - [imgv setClipsToBounds:YES]; - [imgv setContentMode:UIViewContentModeScaleAspectFit]; - imgv.myWidth = pWidth; - imgv.myHeight = pWidth; - imgv.layer.masksToBounds = YES; - imgv.layer.cornerRadius = pWidth/2.0f; + imgv.mySize = CGSizeMake(40,40); [_layoutPortrait addSubview:imgv]; CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width; @@ -696,7 +697,7 @@ -(void)testDemo1 linear.heightSize.equalTo(_layoutRoot.heightSize); linear.widthSize.equalTo(@(screenWidth)).multiply(0.24).min(90.0f); linear.myTrailing = 0.0f; - linear.trailingPadding = 15.0f; + linear.paddingTrailing = 15.0f; linear.subviewVSpace = 8.0f; linear.gravity = MyGravity_Horz_Right|MyGravity_Vert_Center; [_layoutRoot addSubview:linear]; @@ -709,15 +710,14 @@ -(void)testDemo1 lbl.font = [UIFont systemFontOfSize:12]; [lbl setMinimumScaleFactor:10.0f]; [lbl setTextColor:[UIColor greenColor]]; - lbl.wrapContentSize = YES; - lbl.widthSize.equalTo(lbl.widthSize).uBound(_layoutAccessory.widthSize,-2,1); + lbl.heightSize.equalTo(@(MyLayoutSize.wrap)); + lbl.widthSize.equalTo(@(MyLayoutSize.wrap)).uBound(_layoutAccessory.widthSize,-2,1); [lbl setNumberOfLines:1]; [_layoutAccessory addSubview:lbl]; // 附加区域底部 linear = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; linear.gravity = MyGravity_Vert_Center|MyGravity_Horz_Right; - linear.wrapContentHeight = NO; linear.widthSize.equalTo(_layoutAccessory.widthSize); linear.myHeight = 20.0f; linear.subviewHSpace = 4.0f; @@ -727,17 +727,17 @@ -(void)testDemo1 // 勿扰图标 UIImage* image = [UIImage imageNamed:@"edit"]; imgv = [[UIImageView alloc] initWithImage:image]; - [imgv sizeToFit]; - [imgv setHidden:YES]; + imgv.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + imgv.visibility = MyVisibility_Gone; [_layoutAccessoryBottom addSubview:imgv]; + // UIView *hideView1 = imgv; // 勿扰状态下,有新消息时的小红点提示 UIView* view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 6.0f, 6.0f)]; [view setBackgroundColor:[UIColor greenColor]]; - [view setHidden:YES]; - view.layer.masksToBounds = YES; - view.layer.cornerRadius = 3.0f; + view.visibility = MyVisibility_Gone; [_layoutAccessoryBottom addSubview:view]; + // UIView *hideView2 = view; // 非勿扰状态下未读消息数 lbl = [UILabel new]; @@ -750,16 +750,11 @@ -(void)testDemo1 [lbl setNumberOfLines:1]; lbl.heightSize.equalTo(lbl.heightSize).add(4.0f).lBound(@16,0,1); lbl.widthSize.equalTo(lbl.widthSize).add(8.0f).lBound(@16,0,1).uBound(_layoutAccessory.widthSize,0,1); - lbl.layer.masksToBounds = YES; - lbl.layer.cornerRadius = 8.0f; - // [lbl setHidden:YES]; // 中间区域 linear = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; - linear.wrapContentWidth = NO; - linear.wrapContentHeight = YES; linear.myVertMargin = 0.0f; - linear.heightSize.min(67.0f); + linear.heightSize.equalTo(@(MyLayoutSize.wrap)).min(67.0f); linear.leadingPos.equalTo(_layoutPortrait.trailingPos); linear.trailingPos.equalTo(_layoutAccessory.leadingPos); linear.gravity = MyGravity_Vert_Center; @@ -770,7 +765,7 @@ -(void)testDemo1 // 标题区域 relative = [MyRelativeLayout new]; relative.widthSize.equalTo(_layoutContent.widthSize); - relative.wrapContentHeight = YES; + relative.myHeight = MyLayoutSize.wrap; [_layoutContent addSubview:relative]; MyRelativeLayout * _layoutTitle = relative; @@ -778,12 +773,13 @@ -(void)testDemo1 lbl = [UILabel new]; lbl.text = @"asdfklajdf爱的色放就爱"; lbl.myLeading = 0.0f; - lbl.wrapContentSize = YES; + lbl.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); [lbl setFont:[UIFont systemFontOfSize:17]]; [lbl setTextColor:[UIColor greenColor]]; lbl.numberOfLines = 1; [_layoutTitle addSubview:lbl]; UILabel *_lblTitle = lbl; + UIView *hideView4 = _lblTitle; // 名称右侧的标记(例如,选题、部门等,作用是标记会话的类型) lbl = [UILabel new]; @@ -804,7 +800,7 @@ -(void)testDemo1 // 摘要 linear = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; linear.widthSize.equalTo(_layoutContent.widthSize); - linear.wrapContentHeight = YES; + linear.myHeight = MyLayoutSize.wrap; linear.subviewHSpace = 2.0f; [_layoutContent addSubview:linear]; MyLinearLayout *_layoutSummary = linear; @@ -812,35 +808,438 @@ -(void)testDemo1 // 发送状态(例如:发送中,发送失败) imgv = [UIImageView new]; [imgv setContentMode:UIViewContentModeScaleAspectFit]; - imgv.wrapContentSize = YES; - [imgv setHidden:YES]; + imgv.mySize = CGSizeMake(50,50); + imgv.visibility = MyVisibility_Gone; [_layoutSummary addSubview:imgv]; + UIView *hideView3 = imgv; // 勿扰状态下,在摘要显示的消息数 lbl = [UILabel new]; [lbl setTextAlignment:NSTextAlignmentCenter]; [lbl setFont:[UIFont systemFontOfSize:12]]; - lbl.wrapContentSize = YES; + lbl.mySize = CGSizeMake(5, 5); [lbl setNumberOfLines:1]; - [lbl setHidden:YES]; + lbl.visibility = MyVisibility_Gone; [_layoutSummary addSubview:lbl]; // 状态(包括:未读,已读,草稿,有人@) lbl = [UILabel new]; [lbl setFont:[UIFont systemFontOfSize:12]]; - lbl.wrapContentSize = YES; + lbl.mySize = CGSizeMake(5,5); [lbl setNumberOfLines:1]; - [lbl setHidden:YES]; + lbl.visibility = MyVisibility_Gone; [_layoutSummary addSubview:lbl]; // 摘要内容 lbl = [UILabel new]; [lbl setFont:[UIFont systemFontOfSize:12]]; lbl.weight = 1.0f; - lbl.wrapContentSize = YES; + lbl.mySize = CGSizeMake(70, 40); [lbl setNumberOfLines:1]; [_layoutSummary addSubview:lbl]; + + //hideView1 + //hideView2 + //hideView3 + CGSize sz = [_layoutRoot sizeThatFits:CGSizeMake(375, 0)]; + MySizeAssert(midView, sz, CGSizeMake(375, 68.5)); + + hideView3.visibility = MyVisibility_Visible; + + sz = [_layoutRoot sizeThatFits:CGSizeMake(375, 0)]; + MySizeAssert(midView, sz, CGSizeMake(375, 78.5)); + + hideView3.visibility = MyVisibility_Gone; + hideView4.visibility = MyVisibility_Gone; + + sz = [_layoutRoot sizeThatFits:CGSizeMake(375, 0)]; + MySizeAssert(midView, sz, CGSizeMake(375, 67)); + +} +-(void)testDemo2 +{ + MyRelativeLayout *_rootLayout = [MyRelativeLayout new]; + + _rootLayout.wrapContentHeight = YES; + _rootLayout.paddingTop = 15; + _rootLayout.paddingBottom = 15; + _rootLayout.backgroundColor = [UIColor whiteColor]; + + + + MyLinearLayout *topLayout = [MyLinearLayout linearLayoutWithOrientation:(MyOrientation_Vert)]; + + topLayout.wrapContentWidth = YES; + topLayout.myHorzMargin = 0; + topLayout.wrapContentHeight = YES; + topLayout.subviewVSpace = 5; + [_rootLayout addSubview:topLayout]; + + + UILabel *topLable = [UILabel new]; + topLable.text = @"宿州学院-皁阳火车站"; + topLable.myTop = 0; + topLable.myLeft = 11; + topLable.wrapContentSize = YES; + [topLayout addSubview:topLable]; + + + + UIButton *checkMarkBtn = [UIButton new]; + [checkMarkBtn setImage:[UIImage imageNamed:@"train_progresscomplete"] forState:(UIControlStateNormal)]; + checkMarkBtn.widthSize.equalTo(@(13)); + checkMarkBtn.heightSize.equalTo(@(13)); + checkMarkBtn.rightPos.equalTo(@(10)); + checkMarkBtn.centerYPos.equalTo(@(0)); + [_rootLayout addSubview:checkMarkBtn]; + + UILabel *contentLabel = [UILabel new]; + contentLabel.text = @"途径:蒙城、嘎县、阜阳师范学院、嘎县、阜阳师范学院、嘎县、阜阳师范学院"; + contentLabel.myLeft = 11; + + contentLabel.wrapContentHeight= YES; + contentLabel.myRight = 27; + contentLabel.hidden = NO; + [topLayout addSubview:contentLabel]; + + CGSize sz = [_rootLayout sizeThatFits:CGSizeMake(375, 0)]; + MySizeAssert(midView, sz, CGSizeMake(375, 96.5)); +} + +-(void)testDemo3 +{ + MyRelativeLayout *rootLayout = [MyRelativeLayout new]; + rootLayout.myWidth = MyLayoutSize.wrap; + rootLayout.myHeight = MyLayoutSize.wrap; + rootLayout.padding = UIEdgeInsetsMake(12, 12, 12, 12); + + MyLinearLayout *headerLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + headerLayout.topPos.equalTo(rootLayout.topPos); + headerLayout.leftPos.equalTo(rootLayout.leftPos); + headerLayout.myHeight = MyLayoutSize.wrap; + [rootLayout addSubview:headerLayout]; + + UIView *headerView = [UIView new]; + headerView.mySize = CGSizeMake(32, 32); + [headerLayout addSubview:headerView]; + + UILabel *nameLabel = [UILabel new]; + nameLabel.mySize = CGSizeMake(60, 20); + nameLabel.alignment = MyGravity_Vert_Center; + nameLabel.myLeft = 5; + [headerLayout addSubview:nameLabel]; + + UILabel *titleLabel = [UILabel new]; + titleLabel.myHeight = 50; + titleLabel.leftPos.equalTo(headerLayout.leftPos); + titleLabel.topPos.equalTo(headerLayout.bottomPos).offset(5); + titleLabel.rightPos.equalTo(rootLayout.rightPos); + [rootLayout addSubview:titleLabel]; + + UIView *barView = [UIView new]; + barView.myHeight = 30; + barView.leftPos.equalTo(titleLabel.leftPos); + barView.rightPos.equalTo(titleLabel.rightPos); + barView.topPos.equalTo(titleLabel.bottomPos); + [rootLayout addSubview:barView]; + + CGSize sz = [rootLayout sizeThatFits:CGSizeMake(0, 0)]; + MySizeAssert(rootLayout, sz, CGSizeMake(121,141)); +} + +- (UILabel *)textLabel:(NSString *)text fontSize:(CGFloat)fontSize +{ + UILabel *label = [[UILabel alloc] init]; + label.font = [UIFont systemFontOfSize:fontSize]; + label.text = text; + [label sizeToFit]; + return label; +} + +- (UILabel *)amountLabel:(NSString *)text fontSize:(CGFloat)fontSize +{ + UILabel *label = [[UILabel alloc] init]; + label.font = [UIFont boldSystemFontOfSize:fontSize]; + label.text = text; + [label sizeToFit]; + return label; +} + +-(void)testDemo4 +{ + MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + rootLayout.myHorzMargin = 0; + rootLayout.myHeight = MyLayoutSize.wrap; + + MyLinearLayout *baseLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + [rootLayout addSubview:baseLayout]; + baseLayout.widthSize.equalTo(rootLayout); + + ///背景布局 + CGFloat screen_width = [UIScreen mainScreen].bounds.size.width; + CGFloat ten_margin = 10; + MyLinearLayout *bgLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + bgLayout.widthSize.equalTo(baseLayout.widthSize).add(-2 * ten_margin); + bgLayout.myLeft = ten_margin; + bgLayout.myTop = ten_margin; + bgLayout.padding = UIEdgeInsetsMake(2 * ten_margin, 0, 2 * ten_margin, 0); + [baseLayout addSubview:bgLayout]; + bgLayout.backgroundColor = [UIColor lightGrayColor]; + + ///资产布局 + MyRelativeLayout *assetsLayout = [[MyRelativeLayout alloc] init]; + [bgLayout addSubview:assetsLayout]; + assetsLayout.myHorzMargin = 0; + assetsLayout.paddingLeft = 1.8 * ten_margin; + assetsLayout.wrapContentHeight = YES; + + ///总资产Lab + UILabel *assetsLab = [UILabel new]; + assetsLab.text = @"TotalAssetsBracketsYuan"; + [assetsLab sizeToFit]; + assetsLab.font = [UIFont systemFontOfSize:11]; + [assetsLayout addSubview:assetsLab]; + + ///总资产金额Lab + UILabel *assetsAmountLab = [UILabel new]; + assetsAmountLab.text = @"0.00"; + assetsAmountLab.font = [UIFont systemFontOfSize:19]; + assetsAmountLab.leftPos.equalTo(assetsLab); + assetsAmountLab.topPos.equalTo(assetsLab.bottomPos).offset(0.3 * ten_margin); + [assetsLayout addSubview:assetsAmountLab]; + + /*************************************************/ + + ///本金布局 + MyFloatLayout *principalLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; + principalLayout.myHorzMargin = 0; + principalLayout.paddingTop = 1.5 * ten_margin; + principalLayout.paddingLeft = 1.8 * ten_margin; + principalLayout.wrapContentHeight = YES; + [bgLayout addSubview:principalLayout]; + + ///待收本金 + UILabel *principalLab = [UILabel new]; + principalLab.text = @"PendingPrincipalBracketsYuan"; + principalLab.font = [UIFont systemFontOfSize:10]; + [principalLab sizeToFit]; + principalLab.weight = 0.33 ; + [principalLayout addSubview:principalLab]; + + ///锁定资金金额 + UILabel *lockLab = [UILabel new]; + lockLab.text = @"LockFundsBracketsYuan"; + lockLab.font = [UIFont systemFontOfSize:10]; + [lockLab sizeToFit]; + lockLab.weight = 0.5; + [principalLayout addSubview:lockLab]; + + ///可用余额 + UILabel *balanceLab = [UILabel new]; + balanceLab.text = @"AvailableBalanceBracketsYuan"; + balanceLab.font = [UIFont systemFontOfSize:10]; + [balanceLab sizeToFit]; + balanceLab.weight = 1; + [principalLayout addSubview:balanceLab]; + +#define QDLabel UILabel + ///待收本金金额 + QDLabel *principalAmountLab = [self amountLabel:@"0.00" fontSize:15]; + principalAmountLab.weight = 0.33 ; + [principalLayout addSubview:principalAmountLab]; + + ///锁定资金金额 + QDLabel *lockAmountLab = [self amountLabel:@"0.00" fontSize:15]; + lockAmountLab.weight = 0.5; + [principalLayout addSubview:lockAmountLab]; + + ///可用余额金额 + QDLabel *balanceAmountLab = [self amountLabel:@"0.00" fontSize:15]; + balanceAmountLab.weight = 1; + [principalLayout addSubview:balanceAmountLab]; + + ///可伸缩布局 + MyLinearLayout *animLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + [bgLayout addSubview:animLayout]; + animLayout.myHorzMargin = 0; + + UIView *topLine = [[UIView alloc] init]; + [animLayout addSubview:topLine]; + topLine.myTop = 2 * ten_margin; + topLine.myHorzMargin = 0; + topLine.heightSize.equalTo(@(1)); + + ///收益布局 + MyFloatLayout *incomeLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; + incomeLayout.myHorzMargin = 0; + incomeLayout.paddingTop = 2 * ten_margin; + incomeLayout.paddingLeft = 1.8 * ten_margin; + incomeLayout.wrapContentHeight = YES; + [animLayout addSubview:incomeLayout]; + + ///待收收益 + QDLabel *pendingIncomeLab = [self textLabel:@"PendingIncomeBracketsYuan" fontSize:10]; + pendingIncomeLab.weight = 0.33 ; + [incomeLayout addSubview:pendingIncomeLab]; + + ///累计收益 + QDLabel *cumulativeIncomeLab = [self textLabel:@"CumulativeIncomeBracketsYuan" fontSize:10]; + cumulativeIncomeLab.weight = 1; + [incomeLayout addSubview:cumulativeIncomeLab]; + + ///待收收益金额 + QDLabel *pendingIncomeAmountLab = [self amountLabel:@"0.00" fontSize:15]; + pendingIncomeAmountLab.weight = 0.33 ; + [incomeLayout addSubview:pendingIncomeAmountLab]; + + ///累计收益金额 + QDLabel *cumulativeIncomeAmountLab = [self amountLabel:@"0.00" fontSize:15]; + cumulativeIncomeAmountLab.weight = 1; + [incomeLayout addSubview:cumulativeIncomeAmountLab]; + + UIView *bttomLine = [[UIView alloc] init]; + [animLayout addSubview:bttomLine]; + bttomLine.myTop = 2 * ten_margin; + bttomLine.myHorzMargin = 0; + bttomLine.heightSize.equalTo(@(1)); + + //出借总额布局 + MyFloatLayout *totalLoanLayout = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; + totalLoanLayout.myHorzMargin = 0; + totalLoanLayout.paddingTop = 2 * ten_margin; + totalLoanLayout.paddingLeft = 1.8 * ten_margin; + totalLoanLayout.wrapContentHeight = YES; + [animLayout addSubview:totalLoanLayout]; + + ///累积出借总额 + QDLabel *totalLoanLab = [self textLabel:@"TotalAccumulatedLoanBracketsYuan" fontSize:10]; + totalLoanLab.weight = 1 ; + [totalLoanLayout addSubview:totalLoanLab]; + + ///累计收益金额 + QDLabel *totalLoanAmountLab = [self amountLabel:@"0.00" fontSize:15]; + totalLoanAmountLab.weight = 1; + [totalLoanLayout addSubview:totalLoanAmountLab]; + CGRect animRect = [baseLayout subview:animLayout estimatedRectInLayoutSize:CGSizeMake(screen_width - 2 * ten_margin, 100)]; + animLayout.frame = CGRectMake(animRect.origin.x, animRect.origin.y, animRect.size.width, animRect.size.height); + animLayout.visibility = MyVisibility_Visible; + + ///点击布局 + MyLinearLayout *stretchLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + stretchLayout.wrapContentHeight = YES; + // stretchLayout.heightSize.equalTo(@100); + stretchLayout.widthSize.equalTo(rootLayout); + stretchLayout.paddingTop = stretchLayout.paddingBottom = 1.2 * ten_margin; + stretchLayout.gravity = MyGravity_Horz_Center; + [baseLayout addSubview:stretchLayout]; + + UILabel *arrowBtn = [UILabel new]; + arrowBtn.backgroundColor = [UIColor redColor]; + arrowBtn.mySize = CGSizeMake(40, 40); + [stretchLayout addSubview:arrowBtn]; + // self.arrowBtn = arrowBtn; + + CGSize sz = [rootLayout sizeThatFits:CGSizeMake(screen_width, 0)]; + MySizeAssert(rootLayout, sz, CGSizeMake(screen_width,324.5)); + + animLayout.visibility = MyVisibility_Gone; + + sz = [rootLayout sizeThatFits:CGSizeMake(screen_width, 0)]; + MySizeAssert(rootLayout, sz, CGSizeMake(screen_width,182.5)); + +} + + +-(void)testhideandubound +{ + //测试视图隐藏以及 + { + UIButton *zoneBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + zoneBtn.titleLabel.font = [UIFont systemFontOfSize:13]; + zoneBtn.titleLabel.textAlignment = NSTextAlignmentCenter; + + UILabel *nameLab = [[UILabel alloc] initWithFrame:CGRectZero]; + nameLab.numberOfLines = 1.0; + nameLab.font = [UIFont systemFontOfSize:18]; + nameLab.textAlignment = NSTextAlignmentCenter; + + UIButton *placeholderBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + + MyRelativeLayout *layout2 = [[MyRelativeLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 100)]; + + zoneBtn.myLeft = 20; + zoneBtn.myCenterY = 0; + zoneBtn.widthSize.equalTo(zoneBtn.widthSize).add(20); + zoneBtn.heightSize.equalTo(zoneBtn.heightSize).add(3); + + nameLab.myCenterY = 0; + nameLab.leftPos.equalTo(zoneBtn.rightPos).offset(15); + nameLab.rightPos.equalTo(placeholderBtn.leftPos).offset(15); + nameLab.heightSize.equalTo(nameLab.heightSize); + + placeholderBtn.myCenterY = 0; + placeholderBtn.myRight = 20; + placeholderBtn.widthSize.equalTo(zoneBtn.widthSize); + placeholderBtn.heightSize.equalTo(zoneBtn.heightSize); + + [layout2 addSubview:zoneBtn]; + [layout2 addSubview:nameLab]; + [layout2 addSubview:placeholderBtn]; + + zoneBtn.visibility = MyVisibility_Gone; + placeholderBtn.visibility = MyVisibility_Gone; + + [layout2 layoutIfNeeded]; + + XCTAssertTrue(nameLab.frame.size.width == layout2.frame.size.width - 40 && + nameLab.center.y == layout2.frame.size.height * 0.5, @"nameLab.frame = %@",NSStringFromCGRect(nameLab.frame)); + } + + { + UIButton *zoneBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + zoneBtn.titleLabel.font = [UIFont systemFontOfSize:13]; + zoneBtn.titleLabel.textAlignment = NSTextAlignmentCenter; + + UILabel *nameLab = [[UILabel alloc] initWithFrame:CGRectZero]; + nameLab.numberOfLines = 1.0; + nameLab.font = [UIFont systemFontOfSize:18]; + nameLab.textAlignment = NSTextAlignmentCenter; + + UIButton *placeholderBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + + MyRelativeLayout *layout2 = [[MyRelativeLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 100)]; + + zoneBtn.myLeft = 20; + zoneBtn.myCenterY = 0; + zoneBtn.widthSize.equalTo(zoneBtn.widthSize).add(20); + zoneBtn.heightSize.equalTo(zoneBtn.heightSize).add(3); + + nameLab.myCenterY = 0; + nameLab.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + nameLab.leftPos.lBound(zoneBtn.rightPos, 15); + nameLab.rightPos.uBound(placeholderBtn.leftPos, 15); + nameLab.text = @"这是阿斯蒂芬阿道夫阿斯蒂芬安防安防阿斯蒂芬安防大师傅阿斯蒂芬阿斯蒂芬安防"; + + placeholderBtn.myCenterY = 0; + placeholderBtn.myRight = 20; + placeholderBtn.widthSize.equalTo(zoneBtn.widthSize); + placeholderBtn.heightSize.equalTo(zoneBtn.heightSize); + + [layout2 addSubview:zoneBtn]; + [layout2 addSubview:nameLab]; + [layout2 addSubview:placeholderBtn]; + + zoneBtn.visibility = MyVisibility_Gone; + placeholderBtn.visibility = MyVisibility_Gone; + + [layout2 layoutIfNeeded]; + + XCTAssertTrue(nameLab.frame.size.width == layout2.frame.size.width - 40 && + nameLab.center.y == layout2.frame.size.height * 0.5, @"nameLab.frame = %@",NSStringFromCGRect(nameLab.frame)); + + } + } -(void)testBaseline @@ -877,6 +1276,554 @@ -(void)testBaseline } +-(void)testMaxHeightAndWidth +{ + MyRelativeLayout *rellayout = [MyRelativeLayout new]; + + UIView *vv = [UIView new]; + [rellayout addSubview:vv]; + + UILabel *label = [UILabel new]; + [rellayout addSubview:label]; + + label.myLeft = 10; + label.widthSize.equalTo(@(MyLayoutSize.wrap)).max(100); + label.heightSize.equalTo(@(MyLayoutSize.wrap)); + + + vv.topPos.equalTo(label.topPos); + vv.bottomPos.equalTo(label.bottomPos); + vv.leftPos.equalTo(label.leftPos); + vv.rightPos.equalTo(label.rightPos); + + + label.text = @"测试"; + vv.backgroundColor = [UIColor redColor]; + + [rellayout sizeThatFits:CGSizeZero]; + + XCTAssertTrue(CGSizeEqualToSize(label.estimatedRect.size, CGSizeMake(35, 20.5))); + XCTAssertTrue(CGSizeEqualToSize(vv.estimatedRect.size, CGSizeMake(35, 20.5))); + XCTAssertTrue(CGSizeEqualToSize(vv.estimatedRect.size, label.estimatedRect.size)); + + label.text = @"测试12345660392034323"; + + [rellayout sizeThatFits:CGSizeZero]; + + XCTAssertTrue(CGSizeEqualToSize(label.estimatedRect.size, CGSizeMake(100, 61))); + XCTAssertTrue(CGSizeEqualToSize(vv.estimatedRect.size, CGSizeMake(100, 61))); + XCTAssertTrue(CGSizeEqualToSize(vv.estimatedRect.size, label.estimatedRect.size)); + +} + + +-(void)testExample1 +{ + MyRelativeLayout *rootLayout = [[MyRelativeLayout alloc] initWithFrame:CGRectMake(0, 0, 375, 603)]; + + + UIView *v1 = [UIView new]; + v1.mySize = CGSizeMake(100, 100); + v1.myTop = 100; + v1.myLeft = 100; + v1.visibility = MyVisibility_Gone; + [rootLayout addSubview:v1]; + + //某个视图的水平居中依赖另外一个视图,另外一个视图隐藏。 + UIView *v2 = [UIView new]; + v2.mySize = CGSizeMake(100, 100); + v2.centerXPos.equalTo(v1.centerXPos).offset(20); + v2.centerYPos.equalTo(v1.centerYPos).offset(20); + [rootLayout addSubview:v2]; + + //某个视图的左边依赖另外一个视图,另外一个视图隐藏。 + UIView *v3 = [UIView new]; + v3.mySize = CGSizeMake(100, 100); + v3.leadingPos.equalTo(v1.leadingPos).offset(20); + v3.bottomPos.equalTo(v1.bottomPos).offset(20); + [rootLayout addSubview:v3]; + + UIView *v4 = [UIView new]; + v4.mySize = CGSizeMake(100, 100); + v4.leadingPos.lBound(v3.leadingPos, 0); + v4.trailingPos.uBound(rootLayout.trailingPos,0); + v4.bottomPos.equalTo(@(10)); + [rootLayout addSubview:v4]; + + UIView *v5 = [UIView new]; + v5.mySize = CGSizeMake(100, 100); + v5.baselinePos.equalTo(v4.baselinePos).offset(20); + [rootLayout addSubview:v5]; + + UILabel *v6 = [UILabel new]; + v6.mySize = CGSizeMake(100, 100); + v6.baselinePos.equalTo(v1.baselinePos).offset(20); + [rootLayout addSubview:v6]; + + UILabel *v7 = [UILabel new]; + v7.mySize = CGSizeMake(100, 100); + v7.baselinePos.equalTo(@(40)); + [rootLayout addSubview:v7]; + + [rootLayout setNeedsLayout]; + [rootLayout layoutIfNeeded]; + + XCTAssertTrue(CGRectEqualToRect(v1.frame, CGRectMake(100,100,0,0)), @"the v1.frame = %@",NSStringFromCGRect(v1.frame)); + XCTAssertTrue(CGRectEqualToRect(v2.frame, CGRectMake(50,50,100,100)), @"the v2.frame = %@",NSStringFromCGRect(v2.frame)); + XCTAssertTrue(CGRectEqualToRect(v3.frame, CGRectMake(100,0,100,100)), @"the v3.frame = %@",NSStringFromCGRect(v3.frame)); + XCTAssertTrue(CGRectEqualToRect(v4.frame, CGRectMake(187.5,493,100,100)), @"the v4.frame = %@",NSStringFromCGRect(v4.frame)); + XCTAssertTrue(CGRectEqualToRect(v5.frame, CGRectMake(0,20,100,100)), @"the v5.frame = %@",NSStringFromCGRect(v5.frame)); + XCTAssertTrue(CGRectEqualToRect(v6.frame, CGRectMake(0,44,100,100)), @"the v6.frame = %@",NSStringFromCGRect(v6.frame)); + XCTAssertTrue(CGRectEqualToRect(v7.frame, CGRectMake(0,-16,100,100)), @"the v7.frame = %@",NSStringFromCGRect(v7.frame)); +} + +-(void)testWrapContentHeight4 +{ + MyRelativeLayout *rootLayout = [MyRelativeLayout new]; + rootLayout.myHeight = MyLayoutSize.wrap; + rootLayout.padding = UIEdgeInsetsMake(12, 12, 12, 12); + + MyLinearLayout *headerLayout = [MyLinearLayout linearLayoutWithOrientation:(MyOrientation_Horz)]; + headerLayout.topPos.equalTo(rootLayout.topPos); + headerLayout.leftPos.equalTo(rootLayout.leftPos); + headerLayout.wrapContentHeight = YES; + [rootLayout addSubview:headerLayout]; + + UIImageView *headerView = UIImageView.alloc.init; + headerView.mySize = CGSizeMake(32, 32); + [headerLayout addSubview:headerView]; + + UILabel *nameLabel = [UILabel new]; + nameLabel.text = @"欧阳大哥"; + nameLabel.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + nameLabel.alignment = MyGravity_Vert_Center; + nameLabel.myLeft = 5; + [headerLayout addSubview:nameLabel]; + + UILabel *titleLabel = [UILabel new]; + titleLabel.text = @"大师傅阿萨德阿斯蒂芬阿斯蒂芬"; + titleLabel.myHeight = MyLayoutSize.wrap; + titleLabel.leftPos.equalTo(headerLayout.leftPos).offset(32 + 5); + titleLabel.topPos.equalTo(headerLayout.bottomPos).offset(5); + titleLabel.rightPos.equalTo(rootLayout.rightPos); + [rootLayout addSubview:titleLabel]; + + MyLinearLayout *barView = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + barView.myHeight = 20; + barView.leftPos.equalTo(titleLabel.leftPos); + barView.rightPos.equalTo(rootLayout.rightPos); + barView.topPos.equalTo(titleLabel.bottomPos); + [rootLayout addSubview:barView]; + + CGSize sz = [rootLayout sizeThatFits:CGSizeMake(375, 0)]; + MySizeAssert(rootLayout,sz,CGSizeMake(375, 101.5)); + //MyRectAssert(rootLayout, CGRectMake(0, 0, sz.width, sz.height)); +} + +-(void)testWrapContentHeight5 +{ + MyRelativeLayout *rootLayout = [MyRelativeLayout new]; + rootLayout.myWidth = MyLayoutSize.wrap; + rootLayout.myHeight = MyLayoutSize.wrap; + + //放一个高宽都是wrap的子布局视图。 + MyRelativeLayout *layout1 = [MyRelativeLayout new]; + layout1.myWidth = MyLayoutSize.wrap; + layout1.myHeight = MyLayoutSize.wrap; + layout1.myMargin = 10; + [rootLayout addSubview:layout1]; + + MyRelativeLayout *layout11 = [MyRelativeLayout new]; + layout11.myWidth = MyLayoutSize.wrap; + layout11.myHeight = MyLayoutSize.wrap; + layout11.myMargin = 10; + [layout1 addSubview:layout11]; + + UIView *v111 = [UIView new]; + v111.mySize = CGSizeMake(10, 10); + v111.myMargin = 10; + [layout11 addSubview:v111]; + + CGSize sz = [rootLayout sizeThatFits:CGSizeMake(0, 0)]; + MySizeAssert(rootLayout,sz,CGSizeMake(70, 70)); + + v111.visibility = MyVisibility_Gone; + sz = [rootLayout sizeThatFits:CGSizeMake(0, 0)]; + MySizeAssert(rootLayout,sz,CGSizeMake(40, 40)); + + v111.visibility = MyVisibility_Visible; + + + MyFlowLayout *layout2 = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:0]; + layout2.widthSize.equalTo(@(MyLayoutSize.wrap)).max(80); + layout2.myHeight = MyLayoutSize.wrap; + layout2.topPos.equalTo(layout1.centerYPos); + layout2.leftPos.equalTo(layout1.rightPos); + layout2.rightPos.equalTo(rootLayout.rightPos).offset(20); + [rootLayout addSubview:layout2]; + + UIView *v21 = [UIView new]; + v21.mySize = CGSizeMake(60, 60); + [layout2 addSubview:v21]; + + sz = [rootLayout sizeThatFits:CGSizeMake(0, 0)]; + MySizeAssert(rootLayout,sz,CGSizeMake(150, 95)); + + + UIView *v22 = [UIView new]; + v22.mySize = CGSizeMake(60, 60); + [layout2 addSubview:v22]; + + sz = [rootLayout sizeThatFits:CGSizeMake(0, 0)]; + MySizeAssert(rootLayout,sz,CGSizeMake(170, 35+60+60)); + + +} + +-(void)testWrapContentHeight6 +{ + MyRelativeLayout* _layoutRoot = [[MyRelativeLayout alloc] init]; + _layoutRoot.myTop = + _layoutRoot.myLeft = + _layoutRoot.myRight = 0; + _layoutRoot.wrapContentHeight = YES; + _layoutRoot.paddingBottom = 12; + // _layoutRoot.backgroundColor = [UIColor whiteColor]; + //_layoutRoot.clipsToBounds = YES; + _layoutRoot.frame = CGRectMake(0, 0, 100, 0); + + UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)]; + imageView.myLeft = + imageView.myRight = 0; + imageView.myBottom = -12; + [_layoutRoot addSubview:imageView]; + + [_layoutRoot layoutIfNeeded]; + + MyRectAssert(_layoutRoot, CGRectMake(0, 0, 100, 50)); + + + [_layoutRoot setNeedsLayout]; + [_layoutRoot layoutIfNeeded]; + + MyRectAssert(_layoutRoot, CGRectMake(0, 0, 100, 50)); + + +} + +- (void)testWrapContentHeight7 { + + MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz]; + rootLayout.myHorzMargin = 10; + rootLayout.frame = CGRectMake(0, 0, 375-20, 0); + rootLayout.myWidth = 375-20; + rootLayout.myHeight = MyLayoutSize.wrap; + + + UIView *leftImageView = [UIView new]; + leftImageView.myHeight = 60; + leftImageView.myWidth = 60; + leftImageView.myLeft = 5; + leftImageView.myRight = 5; + leftImageView.myCenterY = 0; + [rootLayout addSubview:leftImageView]; + + MyLinearLayout *centerLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + centerLayout.weight = 1; + centerLayout.myHeight = MyLayoutSize.wrap; + centerLayout.gravity = MyGravity_Horz_Fill;//里面的子视图宽度和布局视图相等。 + centerLayout.backgroundColor = [UIColor whiteColor]; + [rootLayout addSubview:centerLayout]; + + UILabel *titleLabel = [UILabel new]; + titleLabel.myHeight = MyLayoutSize.wrap; + titleLabel.myTop = 5; + titleLabel.text = @"刘艳辉提交的技术支撑技术职称"; + [centerLayout addSubview:titleLabel]; + + UILabel *projectLabel = [UILabel new]; + projectLabel.myHeight = MyLayoutSize.wrap; + projectLabel.myTop = 10; + projectLabel.text = @"支撑项目:大寿阿斯蒂芬阿斯蒂芬爱上阿斯蒂芬"; + [centerLayout addSubview:projectLabel]; + + UILabel *contentLabel = [UILabel new]; + contentLabel.myHeight = MyLayoutSize.wrap; + contentLabel.myTop = 4; + contentLabel.text = @"支撑方式:业务"; + [centerLayout addSubview:contentLabel]; + + UILabel *timeLabel = [UILabel new]; + timeLabel.myHeight = MyLayoutSize.wrap; + timeLabel.myTop = 4; + timeLabel.myBottom = 5; + timeLabel.text = @"时间:2019-12-32"; + [centerLayout addSubview:timeLabel]; + + MyRelativeLayout *stateLayout = [MyRelativeLayout new]; + stateLayout.myWidth = MyLayoutSize.wrap; + stateLayout.myVertMargin = 0; + stateLayout.backgroundColor = [UIColor whiteColor]; + [rootLayout addSubview:stateLayout]; + + UILabel *createTimeLabel = [UILabel new]; + createTimeLabel.myTop = 5; + createTimeLabel.myRight = 5; + createTimeLabel.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + createTimeLabel.text = @"2019-12-31"; + + [stateLayout addSubview:createTimeLabel]; + + UILabel *stateLabel = [UILabel new]; + stateLabel.myRight = 5; + stateLabel.myCenterY = 0; + stateLabel.mySize = CGSizeMake(MyLayoutSize.wrap, MyLayoutSize.wrap); + stateLabel.text = @"审批中"; + [stateLayout addSubview:stateLabel]; + + // [rootLayout setNeedsLayout]; + // [rootLayout layoutIfNeeded]; + + CGSize sz1 = [rootLayout sizeThatFits:CGSizeMake(355, 0)]; + CGSize sz2 = centerLayout.estimatedRect.size; //[centerLayout sizeThatFits:CGSizeMake(355, 0)]; + CGSize sz3 = stateLayout.estimatedRect.size; //[stateLayout sizeThatFits:CGSizeMake(355, 0)]; + + //355,130 + + MySizeAssert(rootLayout, sz1, CGSizeMake(355, 151)); + MySizeAssert(centerLayout, sz2, CGSizeMake(191, 151)); + MySizeAssert(stateLayout, sz3, CGSizeMake(94, 151)); +} + +-(void)testCenterXY { + { + MyRelativeLayout *rootLayout = [[MyRelativeLayout alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + rootLayout.padding = UIEdgeInsetsMake(20, 10, 10, 10); + rootLayout.backgroundColor = [UIColor grayColor]; + + + UIView *v1 = UIView.alloc.init; + v1.backgroundColor = UIColor.redColor; + v1.mySize = CGSizeMake(30, 20); + [rootLayout addSubview:v1]; + + UIView *v2 = UIView.alloc.init; + v2.mySize = CGSizeMake(70, 20); + v2.backgroundColor = UIColor.orangeColor; + [rootLayout addSubview:v2]; + + v2.leftPos.equalTo(v1.leftPos); + v1.centerYPos.equalTo(@[v2.centerYPos]); + [rootLayout layoutIfNeeded]; + + MyRectAssert(v1, CGRectMake(10, 35, 30, 20)); + MyRectAssert(v2, CGRectMake(10, 55, 70, 20)); + + } + + { + MyRelativeLayout *rootLayout = [[MyRelativeLayout alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; + rootLayout.mySize = CGSizeMake(100, 100); + rootLayout.myTop = 100; + rootLayout.padding = UIEdgeInsetsMake(20, 10, 10, 10); + rootLayout.backgroundColor = [UIColor grayColor]; + + + UIView *v1 = UIView.alloc.init; + v1.backgroundColor = UIColor.redColor; + v1.mySize = CGSizeMake(20, 30); + [rootLayout addSubview:v1]; + + UIView *v2 = UIView.alloc.init; + v2.mySize = CGSizeMake(20, 70); + v2.backgroundColor = UIColor.orangeColor; + [rootLayout addSubview:v2]; + + v2.topPos.equalTo(v1.topPos); + v1.centerXPos.equalTo(@[v2.centerXPos]); + [rootLayout layoutIfNeeded]; + + MyRectAssert(v1, CGRectMake(30, 20, 20, 30)); + MyRectAssert(v2, CGRectMake(50, 20, 20, 70)); + + } + +} + +- (void)testWidthEqualToHeight { + MyRelativeLayout *rootLayout= [[MyRelativeLayout alloc] init]; + rootLayout.myHeight = MyLayoutSize.wrap; + rootLayout.widthSize.equalTo(@(414)); + rootLayout.backgroundColor = UIColor.whiteColor; + + // 头像 3个排序lab 3个名称 + UILabel *titleLab = [[UILabel alloc] init]; + titleLab.numberOfLines = -1; + titleLab.textAlignment = NSTextAlignmentCenter; + titleLab.textColor = UIColor.whiteColor; + titleLab.font = [UIFont systemFontOfSize:13]; + + UIImageView *avatarImv = [[UIImageView alloc] init]; + avatarImv.image = [UIImage imageNamed:@"head1"]; + + UILabel *oneLab = [[UILabel alloc] init]; + oneLab.text = @"1、"; + oneLab.font = [UIFont systemFontOfSize:10]; + + UILabel *twoLab = [[UILabel alloc] init]; + twoLab.text = @"2、"; + twoLab.font = [UIFont systemFontOfSize:10]; + + UILabel *threeLab = [[UILabel alloc] init]; + threeLab.text = @"3、"; + threeLab.font = [UIFont systemFontOfSize:10]; + + UILabel *firstLab = [[UILabel alloc] init]; + firstLab.text = @"第一"; + firstLab.font = [UIFont systemFontOfSize:10]; + + UILabel *secondLab = [[UILabel alloc] init]; + secondLab.text = @"第二"; + secondLab.font = [UIFont systemFontOfSize:10]; + + UILabel *thirdLab = [[UILabel alloc] init]; + thirdLab.text = @"第三"; + thirdLab.font = [UIFont systemFontOfSize:10]; + + // 布局 + titleLab.leftPos.equalTo(avatarImv.leftPos).offset(3); + titleLab.rightPos.equalTo(avatarImv.rightPos).offset(3); + titleLab.topPos.equalTo(avatarImv.topPos); + titleLab.bottomPos.equalTo(avatarImv.bottomPos); + + avatarImv.myVertMargin = 0; + avatarImv.myLeft = 5; + avatarImv.widthSize.equalTo(avatarImv.heightSize); // TODO: 临时设置宽度为 48,正确的应为 avatarImv.heightSize + + oneLab.myHeight = MyLayoutSize.wrap; + oneLab.myTop = 5.0; + oneLab.leftPos.equalTo(avatarImv.rightPos).offset(5); + oneLab.widthSize.equalTo(threeLab.widthSize); + + twoLab.myHeight = MyLayoutSize.wrap; + twoLab.topPos.equalTo(oneLab.bottomPos).offset(6); + twoLab.leftPos.equalTo(oneLab.leftPos); + twoLab.widthSize.equalTo(threeLab.widthSize); + + threeLab.myWidth = MyLayoutSize.wrap; + threeLab.myHeight = MyLayoutSize.wrap; + threeLab.topPos.equalTo(twoLab.bottomPos).offset(6); + threeLab.leftPos.equalTo(oneLab.leftPos); + threeLab.myBottom = 5; + + firstLab.myHeight = MyLayoutSize.wrap; + firstLab.topPos.equalTo(oneLab.topPos); + firstLab.leftPos.equalTo(oneLab.rightPos); + firstLab.rightPos.equalTo(rootLayout.rightPos); + + secondLab.myHeight = MyLayoutSize.wrap; + secondLab.leftPos.equalTo(firstLab.leftPos); + secondLab.rightPos.equalTo(firstLab.rightPos); + secondLab.topPos.equalTo(twoLab.topPos); + + thirdLab.myHeight = MyLayoutSize.wrap; + thirdLab.leftPos.equalTo(firstLab.leftPos); + thirdLab.rightPos.equalTo(firstLab.rightPos); + thirdLab.topPos.equalTo(threeLab.topPos); + + // 添加视图 + [rootLayout addSubview:avatarImv]; + [rootLayout addSubview:titleLab]; + [rootLayout addSubview:oneLab]; + [rootLayout addSubview:twoLab]; + [rootLayout addSubview:threeLab]; + [rootLayout addSubview:firstLab]; + [rootLayout addSubview:secondLab]; + [rootLayout addSubview:thirdLab]; + + + // 添加视图 + // [rootLayout addSubview:avatarImv]; + + + CGSize sz = [rootLayout sizeThatFits:CGSizeMake(414, 0)]; + + MySizeAssert(rootLayout, sz, CGSizeMake(414, 58)); + MyRectAssert2(titleLab, titleLab.estimatedRect, CGRectMake(8, 0, 52, 58)); + MyRectAssert2(titleLab, titleLab.estimatedRect, CGRectMake(8, 0, 52, 58)); + MyRectAssert2(oneLab, oneLab.estimatedRect, CGRectMake(68, 5, 17, 12)); + MyRectAssert2(twoLab, twoLab.estimatedRect, CGRectMake(68, 23, 17, 12)); + MyRectAssert2(threeLab, threeLab.estimatedRect, CGRectMake(68, 41, 17, 12)); + MyRectAssert2(avatarImv, avatarImv.estimatedRect, CGRectMake(5, 0, 58, 58)); + // NSLog(@"aa"); + +} + +-(void)testSizeThatFit { + { + MyRelativeLayout *v = MyRelativeLayout.alloc.init; + v.myWidth = v.myHeight = 200; + CGSize vsize = [v sizeThatFits:(CGSizeZero)]; + MySizeAssert(v, vsize, CGSizeMake(200, 200)); + } + { + MyLinearLayout *v = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; + v.myWidth = v.myHeight = 200; + CGSize vsize = [v sizeThatFits:(CGSizeZero)]; + MySizeAssert(v, vsize, CGSizeMake(200, 200)); + } + { + MyFlowLayout *v = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:4]; + v.myWidth = v.myHeight = 200; + CGSize vsize = [v sizeThatFits:(CGSizeZero)]; + MySizeAssert(v, vsize, CGSizeMake(200, 200)); + } + + { + MyFloatLayout *v = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; + v.myWidth = v.myHeight = 200; + CGSize vsize = [v sizeThatFits:(CGSizeZero)]; + MySizeAssert(v, vsize, CGSizeMake(200, 200)); + } + + { + MyPathLayout *v = [MyPathLayout new]; + v.myWidth = v.myHeight = 200; + CGSize vsize = [v sizeThatFits:(CGSizeZero)]; + MySizeAssert(v, vsize, CGSizeMake(200, 200)); + } + + { + MyFrameLayout *v = [MyFrameLayout new]; + v.myWidth = v.myHeight = 200; + CGSize vsize = [v sizeThatFits:(CGSizeZero)]; + MySizeAssert(v, vsize, CGSizeMake(200, 200)); + } + + + { + MyGridLayout *v = [MyGridLayout new]; + v.myWidth = v.myHeight = 200; + CGSize vsize = [v sizeThatFits:(CGSizeZero)]; + MySizeAssert(v, vsize, CGSizeMake(200, 200)); + } + + { + MyTableLayout *v = [MyTableLayout tableLayoutWithOrientation:MyOrientation_Vert]; + v.myWidth = v.myHeight = 200; + CGSize vsize = [v sizeThatFits:(CGSizeZero)]; + MySizeAssert(v, vsize, CGSizeMake(200, 200)); + } + + { + MyFlexLayout *v = MyFlexLayout.new; + v.myWidth = v.myHeight = 200; + CGSize vsize = [v sizeThatFits:(CGSizeZero)]; + MySizeAssert(v, vsize, CGSizeMake(200, 200)); + } + +} + - (void)testPerformanceExample { // This is an example of a performance test case. [self measureBlock:^{ diff --git a/README.md b/README.md index dbd5df4..635f7fa 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![License](https://img.shields.io/cocoapods/l/MyLayout.svg?style=flat)](http://cocoapods.org/pods/MyLayout) [![Platform](https://img.shields.io/cocoapods/p/MyLayout.svg?style=flat)](http://cocoapods.org/pods/MyLayout) -[![Support](https://img.shields.io/badge/support-iOS%205%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/) +[![Support](https://img.shields.io/badge/support-iOS%208%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/) [![Weibo](https://img.shields.io/badge/Sina微博-@欧阳大哥2013-yellow.svg?style=flat)](http://weibo.com/1411091507) [![QQ](https://img.shields.io/badge/QQ-156355113-yellow.svg?style=flat)]() [![GitHub stars](https://img.shields.io/github/stars/youngsoft/MyLinearLayout.svg)](https://github.com/youngsoft/MyLinearLayout/stargazers) @@ -109,8 +109,7 @@ Sample code: ```objective-c --(void)loadView -{ +-(void)loadView { [super loadView]; MyLinearLayout *S = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; @@ -118,23 +117,22 @@ Sample code: S.subviewSpace = 10; UIView *A = [UIView new]; - A.myLeft = A.myRight = 5; + A.myHorzMargin = 5; A.myHeight = 40; [S addSubview:A]; UIView *B = [UIView new]; B.myLeft = 20; - B.myWidth = B.myHeight = 40; + B.mySize = CGSizeMake(40,40); [S addSubview:B]; UIView *C = [UIView new]; C.myRight = 40; - C.myWidth = 50; - C.myHeight = 40; + C.mySize = CGSizeMake(50,40); [S addSubview:C]; UIView *D = [UIView new]; - D.myLeft = D.myRight = 10; + D.myHorzMargin = 10; D.myHeight = 40; [S addSubview:D]; @@ -160,8 +158,7 @@ Sample code: ```objective-c --(void)loadView -{ +-(void)loadView { [super loadView]; MyRelativeLayout *S = [MyRelativeLayout new]; @@ -212,7 +209,6 @@ Sample code: D.backgroundColor = [UIColor cyanColor]; E.backgroundColor = [UIColor magentaColor]; } - ``` @@ -227,8 +223,7 @@ Sample code: ```objective-c - -(void)loadView -{ + -(void)loadView { [super loadView]; MyFrameLayout *S = [MyFrameLayout new]; @@ -262,7 +257,6 @@ Sample code: C.backgroundColor = [UIColor orangeColor]; D.backgroundColor = [UIColor cyanColor]; } - ``` @@ -277,16 +271,15 @@ Sample code: ```objective-c - -(void)loadView -{ + -(void)loadView { [super loadView]; MyTableLayout *S = [MyTableLayout tableLayoutWithOrientation:MyOrientation_Vert]; - S.wrapContentWidth = YES; + S.myWidth = MyLayoutSize.wrap; S.subviewHSpace = 10; S.subviewVSpace = 10; - [S addRow:MTLSIZE_WRAPCONTENT colSize:MTLSIZE_WRAPCONTENT]; +    [S addRow:MyLayoutSize.wrap colSize:MyLayoutSize.wrap]; UIView *A = [UIView new]; A.mySize = CGSizeMake(50,40); @@ -300,7 +293,7 @@ Sample code: C.mySize = CGSizeMake(30,40); [S addSubview:C]; - [S addRow:MTLSIZE_WRAPCONTENT colSize:MTLSIZE_WRAPCONTENT]; + [S addRow:MyLayoutSize.wrap colSize:MyLayoutSize.wrap]; UIView *D = [UIView new]; D.mySize = CGSizeMake(200,40); @@ -308,7 +301,6 @@ Sample code: //...E,F - [self.view addSubview:S]; S.backgroundColor = [UIColor redColor]; A.backgroundColor = [UIColor greenColor]; @@ -316,7 +308,6 @@ Sample code: C.backgroundColor = [UIColor orangeColor]; D.backgroundColor = [UIColor cyanColor]; } - ``` @@ -331,36 +322,28 @@ Sample code: ```objective-c - - -(void)loadView -{ + -(void)loadView { [super loadView]; MyFlowLayout *S = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:4]; - S.wrapContentHeight = YES; + S.myHeight = MyLayoutSize.wrap; S.myWidth = 300; S.padding = UIEdgeInsetsMake(10, 10, 10, 10); S.gravity = MyGravity_Horz_Fill; S.subviewSpace = 10; - for (int i = 0; i < 10; i++) - { + for (int i = 0; i < 10; i++) { UIView *A = [UIView new]; A.heightSize.equalTo(A.widthSize); [S addSubview:A]; A.backgroundColor = [UIColor greenColor]; - } [self.view addSubview:S]; S.backgroundColor = [UIColor redColor]; } - - - - ``` @@ -376,15 +359,14 @@ Sample code: ```objective-c - -(void)loadView -{ + -(void)loadView { [super loadView]; MyFloatLayout *S = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; - S.wrapContentHeight = YES; S.padding = UIEdgeInsetsMake(10, 10, 10, 10); S.subviewSpace = 10; S.myWidth = 300; + S.myHeight = MyLayoutSize.wrap; UIView *A = [UIView new]; A.mySize = CGSizeMake(80,70); @@ -421,7 +403,6 @@ Sample code: E.backgroundColor = [UIColor blackColor]; F.backgroundColor = [UIColor whiteColor]; } - ``` @@ -436,8 +417,7 @@ Sample code: ```objective-c - -(void)loadView -{ +-(void)loadView { [super loadView]; MyPathLayout *S = [MyPathLayout new]; @@ -445,13 +425,11 @@ Sample code: S.coordinateSetting.isReverse = YES; S.coordinateSetting.origin = CGPointMake(0.5, 0.2); - S.polarEquation = ^(CGFloat angle) - { + S.polarEquation = ^(CGFloat angle) { return 80 * (1 + cos(angle)); }; - for (int i = 0; i < 4; i++) - { + for (int i = 0; i < 4; i++) { UIView *A = [UIView new]; A.mySize = CGSizeMake(40,40); [S addSubview:A]; @@ -462,7 +440,6 @@ Sample code: [self.view addSubview:S]; S.backgroundColor = [UIColor redColor]; } - ``` @@ -478,8 +455,7 @@ Grid layout is a view to a rectangular area according to the row or column divid Sample code: ```objective-c - -(void)loadView -{ +-(void)loadView { [super loadView]; MyGridLayout *S = [MyGridLayout new]; @@ -521,7 +497,40 @@ Grid layout is a view to a rectangular area according to the row or column divid ``` - +### MyFlexLayout +Flex is a layout defined in the W3C specification. This layout allows simple, fast, responsive implementation of various layout pages, and is intended to replace "position+display+float". MyFlexLayout implements a subset of the flex specification while extending some of the capabilities in the flex specification. Since MyFlowLayout also provides similar elastic layout capabilities, but the syntax is not compatible with the flex specification, MyFlexLayout provides a new layout writing syntax derived from MyFlowLayout. Syntax candy allows you to use the syntax defined by the flex specification to implement the layout of the code. + +![演示效果图](https://raw.githubusercontent.com/youngsoft/MyLinearLayout/master/MyLayout/fll.png) + +Sample code: + +```objective-c + + -(void)loadView { + [super loadView]; + + MyFlexLayout *S = MyFlexLayout.new.myFlex + .flex_direction(MyFlexDirection_Row) + .flex_wrap(MyFlexWrap_Wrap) + .vert_space(10) + .horz_space(10) + .padding(UIEdgeInsetsMake(10, 10, 10, 10)) + .width(300) + .height(MyLayoutSize.wrap) + .addTo(self.view); + + for (int i = 0; i < 10; i++) { + UIView *A = UIView.new.myFlex + .width(60) + .height(50) + .addTo(S); + + A.backgroundColor = [UIColor greenColor]; + } + } +``` + + ### MySizeClass > Is equivalent to: Size Classes of iOS. @@ -541,18 +550,16 @@ to set Size Classes Characteristics like below: //default is all Size Classes MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - rootLayout.wrapContentHeight = NO; + rootLayout.myHeight = MyLayoutSize.empty; rootLayout.gravity = MyGravity_Horz_Fill; //MySizeClass_wAny | MySizeClass_hCompact is iPhone landscape orientation. MyLinearLayout *lsc = [rootLayout fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact copyFrom:MySizeClass_wAny | MySizeClass_hAny]; lsc.orientation = MyOrientation_Horz; - lsc.wrapContentWidth = NO; + lsc.myWidth = MyLayoutSize.empty; lsc.gravity = MyGravity_Vert_Fill; - - ``` @@ -612,9 +619,9 @@ To integrate MyLayout into your Xcode project using CocoaPods, specify it in you ``` source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '7.0' +platform :ios, '9.0' -pod 'MyLayout', '~> 1.5.0' +pod 'MyLayout' ``` Then, run the following command: diff --git a/README.zh.md b/README.zh.md index 6e7d295..556f9d0 100644 --- a/README.zh.md +++ b/README.zh.md @@ -2,7 +2,7 @@ [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![License](https://img.shields.io/cocoapods/l/MyLayout.svg?style=flat)](http://cocoapods.org/pods/MyLayout) [![Platform](https://img.shields.io/cocoapods/p/MyLayout.svg?style=flat)](http://cocoapods.org/pods/MyLayout) -[![Support](https://img.shields.io/badge/support-iOS%205%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/) +[![Support](https://img.shields.io/badge/support-iOS%208%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/) [![Weibo](https://img.shields.io/badge/Sina微博-@欧阳大哥2013-yellow.svg?style=flat)](http://weibo.com/1411091507) [![QQ](https://img.shields.io/badge/QQ-156355113-yellow.svg?style=flat)]() [![GitHub stars](https://img.shields.io/github/stars/youngsoft/MyLinearLayout.svg)](https://github.com/youngsoft/MyLinearLayout/stargazers) @@ -28,7 +28,7 @@ MyLayout是一套iOS界面视图布局框架。MyLayout的内核是基于对UIVi [http://www.jianshu.com/p/fbeb376584ed](http://www.jianshu.com/p/fbeb376584ed) 流式布局 [http://www.jianshu.com/p/0c075f2fdab2](http://www.jianshu.com/p/0c075f2fdab2) 浮动布局 [http://www.jianshu.com/p/4ac229057396](http://www.jianshu.com/p/4ac229057396) 路径布局 -[http://bicyclering.com/2017/09/01/IOS-UIViewLayout-MyLinearLayout/#more](http://bicyclering.com/2017/09/01/IOS-UIViewLayout-MyLinearLayout/#more) 栅格布局 +[https://bicyclering.github.io/](https://bicyclering.github.io/2017/09/01/IOS/(UIViewLayout)%E5%B8%83%E5%B1%80/MyLinearLayout/#more) 栅格布局 ### MyLayout的优势 * MyLayout的实现内核是基于frame的设置,而不是对AutoLayout的封装。因此在使用上不会受到任何操作系统版本的限制。 @@ -158,8 +158,7 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 示例代码: ```objective-c --(void)loadView -{ +-(void)loadView { [super loadView]; MyLinearLayout *S = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; @@ -167,23 +166,22 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 S.subviewSpace = 10; UIView *A = [UIView new]; - A.myLeft = A.myRight = 5; + A.myHorzMargin = 5; A.myHeight = 40; [S addSubview:A]; UIView *B = [UIView new]; B.myLeft = 20; - B.myWidth = B.myHeight = 40; + B.mySize = CGSizeMake(40,40); [S addSubview:B]; UIView *C = [UIView new]; C.myRight = 40; - C.myWidth = 50; - C.myHeight = 40; + C.mySize = CGSizeMake(50,40); [S addSubview:C]; UIView *D = [UIView new]; - D.myLeft = D.myRight = 10; + D.myHorzMargin = 10; D.myHeight = 40; [S addSubview:D]; @@ -194,7 +192,6 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 C.backgroundColor = [UIColor orangeColor]; D.backgroundColor = [UIColor cyanColor]; } - ``` @@ -208,8 +205,7 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 示例代码: ```objective-c --(void)loadView -{ +-(void)loadView { [super loadView]; MyRelativeLayout *S = [MyRelativeLayout new]; @@ -275,8 +271,7 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 示例代码: ```objective-c - -(void)loadView -{ + -(void)loadView { [super loadView]; MyFrameLayout *S = [MyFrameLayout new]; @@ -325,16 +320,15 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 示例代码: ```objective-c - -(void)loadView -{ +-(void)loadView { [super loadView]; MyTableLayout *S = [MyTableLayout tableLayoutWithOrientation:MyOrientation_Vert]; - S.wrapContentWidth = YES; + S.myWidth = MyLayoutSize.wrap; S.subviewHSpace = 10; S.subviewVSpace = 10; - [S addRow:MTLSIZE_WRAPCONTENT colSize:MTLSIZE_WRAPCONTENT]; + [S addRow:MyLayoutSize.wrap colSize:MyLayoutSize.wrap]; UIView *A = [UIView new]; A.mySize = CGSizeMake(50,40); @@ -348,7 +342,7 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 C.mySize = CGSizeMake(30,40); [S addSubview:C]; - [S addRow:MTLSIZE_WRAPCONTENT colSize:MTLSIZE_WRAPCONTENT]; + [S addRow:MyLayoutSize.wrap colSize:MyLayoutSize.wrap]; UIView *D = [UIView new]; D.mySize = CGSizeMake(200,40); @@ -381,12 +375,11 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 ```objective-c - -(void)loadView -{ + -(void)loadView { [super loadView]; MyFlowLayout *S = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:4]; - S.wrapContentHeight = YES; + S.myHeight = MyLayoutSize.wrap; S.myWidth = 300; S.padding = UIEdgeInsetsMake(10, 10, 10, 10); S.gravity = MyGravity_Horz_Fill; @@ -406,10 +399,6 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 [self.view addSubview:S]; S.backgroundColor = [UIColor redColor]; } - - - - ``` @@ -424,15 +413,14 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 示例代码: ```objective-c - -(void)loadView -{ + -(void)loadView { [super loadView]; MyFloatLayout *S = [MyFloatLayout floatLayoutWithOrientation:MyOrientation_Vert]; - S.wrapContentHeight = YES; + S.myHeight = MyLayoutSize.wrap; + S.myWidth = 300; S.padding = UIEdgeInsetsMake(10, 10, 10, 10); S.subviewSpace = 10; - S.myWidth = 300; UIView *A = [UIView new]; A.mySize = CGSizeMake(80,70); @@ -469,8 +457,7 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 E.backgroundColor = [UIColor blackColor]; F.backgroundColor = [UIColor whiteColor]; } - - + ``` @@ -486,8 +473,7 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 示例代码: ```objective-c - -(void)loadView -{ + -(void)loadView { [super loadView]; MyPathLayout *S = [MyPathLayout new]; @@ -495,13 +481,11 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 S.coordinateSetting.isReverse = YES; S.coordinateSetting.origin = CGPointMake(0.5, 0.2); - S.polarEquation = ^(CGFloat angle) - { + S.polarEquation = ^(CGFloat angle) { return 80 * (1 + cos(angle)); }; - for (int i = 0; i < 4; i++) - { + for (int i = 0; i < 4; i++) { UIView *A = [UIView new]; A.mySize = CGSizeMake(40,40); [S addSubview:A]; @@ -526,8 +510,7 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 示例代码: ```objective-c - -(void)loadView -{ + -(void)loadView { [super loadView]; MyGridLayout *S = [MyGridLayout new]; @@ -569,6 +552,39 @@ MyLayoutSize类是用来描述一个视图的尺寸的类。UIView中扩展出 ``` +### 弹性布局MyFlexLayout +弹性布局是W3C规范中定义的一种布局,又称为flex布局。这种布局可以简单、快速、响应的实现各种布局页面,其目的是为了取代“position+display+float”。MyFlexLayout实现了flex规范中的子集,同时又扩展了flex规范中的一些能力。由于MyFlowLayout也提供了类似弹性布局的能力,但是语法和flex规范不兼容,因此MyFlexLayout在从MyFlowLayout派生的基础上提供了一套新的布局编写语法糖,语法糖使得我们可以用和flex规范定义的语法来实现代码的布局。 + +![演示效果图](https://raw.githubusercontent.com/youngsoft/MyLinearLayout/master/MyLayout/fll.png) + +示例代码: + +```objective-c + + -(void)loadView { + [super loadView]; + + MyFlexLayout *S = MyFlexLayout.new.myFlex + .flex_direction(MyFlexDirection_Row) + .flex_wrap(MyFlexWrap_Wrap) + .vert_space(10) + .horz_space(10) + .padding(UIEdgeInsetsMake(10, 10, 10, 10)) + .width(300) + .height(MyLayoutSize.wrap) + .addTo(self.view); + + for (int i = 0; i < 10; i++) { + UIView *A = UIView.new.myFlex + .width(60) + .height(50) + .addTo(S); + + A.backgroundColor = [UIColor greenColor]; + } + } +``` + ### SizeClass的支持 > 等价于iOS的Size Classes @@ -588,14 +604,14 @@ MyLayout布局体系为了实现对不同屏幕尺寸的设备进行适配,提 //默认所有设备的设置。 MyLinearLayout *rootLayout = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Vert]; rootLayout.padding = UIEdgeInsetsMake(10, 10, 10, 10); - rootLayout.wrapContentHeight = NO; + rootLayout.myHeight = MyLayoutSize.empty; rootLayout.gravity = MyGravity_Horz_Fill; //MySizeClass_wAny | MySizeClass_hCompact 表明的是iPhone设备的横屏. MyLinearLayout *lsc = [rootLayout fetchLayoutSizeClass:MySizeClass_wAny | MySizeClass_hCompact copyFrom:MySizeClass_wAny | MySizeClass_hAny]; lsc.orientation = MyOrientation_Horz; - lsc.wrapContentWidth = NO; + lsc.myWidth = MyLayoutSize.empty; lsc.gravity = MyGravity_Vert_Fill; @@ -620,9 +636,9 @@ $ gem install cocoapods ``` source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '7.0' +platform :ios, '9.0' -pod 'MyLayout', '~> 1.5.0' +pod 'MyLayout' ``` 然后运行如下命令: @@ -650,7 +666,19 @@ $ pod install $(SRCROOT)/Carthage/Build/iOS/MyLayout.framework + +## 问题列表 +1. 布局完成后如何得到布局视图的尺寸尤其是高度? +MyLayout的布局设置和AutoLayout的约束设置是一样的,并不会立即进行布局。因此设置完约束后立即读取frame值将不是想要的结果。为了获取布局设置后的视图尺寸值一共有4个方法: + * 对布局视图调用尺寸评估方法: `sizeThatFits:` 并传递CGSizeMake(明确的宽度值,0) 作为参数,系统将返回布局评估后所得到的尺寸,你可以从返回值中得到布局评估后的高度值。如果你想获得布局视图里面子视图的评估尺寸和位置时可以通过`estimatedRect`属性来获取。 + * 可以调用布局视图的`layoutIfNeeded`方法来进行立即的布局处理,然后通过布局视图和子视图的frame属性就可以得到真实的视图尺寸和位置值。在使用layoutIfNeeded方法时,要求布局视图的frame属性值必须要设置明确的宽度值,否则布局计算的结果将不一定正确。这个方法和上面方法类似前者是评估后者是真实计算,共同点是都要求指定明确的宽度值。 + * 通过KVO的方式来监视布局视图的isMyLayouting属性来获取每次布局完成后的布局视图以及子视图的真实frame值。 + * 通过为布局视图设置endLayoutBlock或者为子视图设置viewLayoutCompleteBlock来得到一次布局完成后布局视图或者子视图的frame值。这个方法得到的frame有可能会不准确,因此它只是第一次布局的结果。 + + +2. 布局后运行时出现CPU的100%占用并最终崩溃在layoutSubviews方法中。这个问题的原因就是约束冲突了,最常见的就是布局视图依赖子视图的尺寸,而子视图又依赖父视图的尺寸导致的布局尺寸循环计算更新。解决的方法就是检查父子视图之间的约束是否存在相互依赖的情况并取消相互依赖即可。 +3. 在线性布局MyLinearLayout中的子视图的MyLayoutPos属性值设置为大于0小于1的小数时,它所表示的意思并不是一个绝对间距或者边距而是一个相对间距和边距值。这样的设定是由于历史原因导致的不合理的设定。比如在垂直线性布局中有a,b,c,d四个子视图,我们希望c,d两个子视图落地布局视图的底端而不是跟在b的后面。这时候我们只需要设置c.myTop为从大于0到小于1之间的任何小数都可以实现这个功能,虽然这样的设置方法和奇葩!。同时我们也可以通过设定b.myBottom=0.5以及c.myTop=0.5这样的方式来达到目的,而这样的设定将会更加容易理解一些。 ## 版本历史