Autolayout
Auto Layout Guide
Content Compression Resistance和Content Hugging -- 感觉笨笨讲的更好
都是相较 intrinsic size
Content Compression Resistance -- 反压缩
Content Hugging -- 反扩张
有趣的Autolayout示例-Masonry实现123
Autolayout的第一次亲密接触 -- 不错不错
(1)IntrinsicContentSize -- 自己根据内容调整大小
自定义的View拥有默认Size -> 重写intrinsicContentSize。
ContentHugging:表示View的宽度和高度紧靠内容,不让其扩展的力量。-- 对自身 CompressionResistance:优先级越高的,显示的内容越完整 -- 两个对象相互
(2)流程:
对于同一个View,ContentHugging和CompressionResistance不会同时起作用。当一个Label有文字的时候,label会存在一个内容的Size。
<1> 如果有外力让其size扩张,ContentHugging会起作用,外力大于ContentHugging的力量,label的size由外力决定,反之,label的Size由内容决定。
<2> 如果有外力让其size压缩,CompressionResistance会起作用,外力大于CompressionResistance的力量,label的size由外力决定,反之,label的Size由内容决定。
所以先判断对自身来说是扩张还是压缩,如果content Hugging = 1000,那不可能被扩张。
(3)Priorities -- 4种 -- 1000,750,250, 50
(4)调试:
<1> masonry重写了constriant的-description方法
<2> Debug View Hierarchy
(5)Autolayout深层次的探索
<1> 什么时候计算frame
根据最初的介绍,Autolayout是在设置constraint的时候,将constraints存放在View的属性中,在真正布局的时候去计算出view的frame,完成布局。
由于Autolayout不通过frame布局,而是直接设置center和bounds。
由于Autolayout是延迟布局的,并不是约束更新之后就立刻布局
Autolayout是在父view的-layoutSubview中更新Frame的 --setNeedsLayout,layoutIfNeeded
(6)当View约束发生变化时,是怎么调整布局的
view1 <-> view2 -> view3
view2 -> view1的setNeedsLayout,如果自身要改变 -> 父View setNeedsLayout...
如果没改变 -> 自身layoutSubviews(中改变view2布局)
-> view2布局改变 -> view2的layoutSubviews(中改变view3没改变,所停止,否则继续调用view3的layoutSubviews)
(7)Autolayout生效之前使用frame -- 系统会根据约束重新计算Frame。
Autolayout生效之后 -- setFrame
(8)初始化constraint的代码放在viewDidLoad等初始化方法中更好。
updateConstraints方法仅用于提升性能。
AutoLayout Tips
UIScrollview与Autolayout的那点事 -- 自己理解才最重要啊。。。
UIScrollView依靠与其subViews之间的约束来确定ContentSize的大小
这是因为UIScrollView是个非常特殊的view UIScrollView与其subView之间相对位置的约束并不会直接用于frame的计算 而是会转化为对ContentSize的计算
scrollView自身只是一个窗口,设定x,y,w,h(frame)就好。但是,它中的subViews的frame并不是基于scrollView固定的frame来确定的。它的subViews之间的相互约束来共同确定contentSize。所以subViews不能只是设定frame,还要设定top,left,bottom,right。因为contentSize是动态的。
纯代码 和 IB 中都先给scrollView加一个contentView(container View)来避免这种在各个subView之间要协同确定contentSize的繁琐且容易出错的方式。
Masonry介绍与使用实践(快速上手Autolayout)
1. Masonry中能够添加autolayout约束有三个函数
(1)- (NSArray*)mas_makeConstraints:(void(^)(MASConstraintMaker *make))block;
(2)- (NSArray*)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block;
(3)- (NSArray*)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block;
mas_makeConstraints只负责新增约束 Autolayout不能同时存在两条针对于同一对象的约束 否则会报错
mas_updateConstraints针对上面的情况 会更新在block中出现的约束 不会导致出现两个相同约束的情况
mas_remakeConstraints则会清除之前的所有约束 仅保留最新的约束
2. 其次 equalTo 和 mas_equalTo的区别在哪里呢? 其实 mas_equalTo是一个MACRO mas_equalTo只是对其参数进行了一个BOX操作(装箱)。所支持的类型 除了NSNumber支持的那些数值类型之外 就只支持CGPointCGSizeUIEdgeInsets
mas_equalTo适用数值元素,equalTo适合多属性的,比如make.left.and.right.equalTo(self.view)
对于top,left,bottom,right来说,top 和 left 能分别确定 y 和 x 的坐标,而left 和 right 共同决定 width,top 和 bottom共同确定着 height。
设置约束前,请先加入view。
3. UIScrollView && AutoLayout
(1) 正常设置scrollView,通过 edgeInsets
(2) 加一个containerView,用于方便计算 contentSize,edgeInsets + w + h。
(3) 造subView加入containerView,设置 left,right,height,top约束
(4) 设置 containerView的bottom为lastSubView的bottoms,为了计算contentSize。
4. 横向或者纵向等间隙的排列一组view
Category: -- 横纵向同理
(1) n 个View有n + 1个间隙,初始化 n + 1个间隙,设置w,h相等
(2) 第一个间隙 left, centerY。间隙与View相互设置约束(left = right之类)
(3) last间隙 = UIView.right。
追求Masonry -- 源码源码
(1)
MASConstraintMaker -- 22种Attribute类型
MASConstraint -- 19种Attribute
MAS_VIEW(UIView) -- 与MASConstraint一样,加了mas_前缀防止冲突。
(2)
insets: 用来设置left, right, top, bottom。接受MASEdgeInsets类型值
sizeOffset: 用来设置width, height。接受CGSize类型的值
centerOffset: 用来设置centerX, centerY。接受CGPoint类型的值
offset: 可以用来设置所有的东西。接受CGFloat类型的值
(3)小技巧(源码做了兼容) -- 这里的小技巧或许就是casa说的业务工程师乱来吧,本身很好的可读性非要炫技毁了一切,反正我是老老实实的敲全。
(4) mas_key
批量设置的宏 MASAttachKeys(self.view,view1);
(5) Macros
MAS_SHORTHAND && MAS_SHORTHAND_GLOBALS
// 定义这个常量,就可以在使用Masonry不必总带着前缀 `mas_`:
#define MAS_SHORTHAND
// 定义这个常量,以支持在 Masonry 语法中自动将基本类型转换为 object 类型:
#define MAS_SHORTHAND_GLOBALS
(6) Masonry一共13个类,源码确实解析的不错!
AutoLayout框架Masonry使用心得 -- 一般般
(1)setContentHuggingPriority: forAxis:
setContentCompressionResistancePriority: forAxis:
multipliedBy()
(2)AutoLayout下UILabel设置多行计算需要设置preferredMaxLayoutWidth
label.preferredMaxWidth= [UIScreenmainScreen].bounds.size.width- margin - padding;
(3)
preferredMaxLayoutWidth用来制定最大的宽,一般用在多行的UILabel中
systemLayoutSizeFittingSize方法能够获得view的高度
iOS7有两个很有用的属性,topLayoutGuide和bottomLayoutGuide,这个两个主要是方便获取UINavigationController和UITabBarController的头部视图区域和底部视图区域。
masonry:make.top.equalTo(self.mas_topLayoutGuide);
(4)AutoLayout情况如何计算UITableView的变高高度
每次拿model填充单例cell,然后通过cell的systemLayoutSizeFittingSizes:返回高度。和FD思路一样。
实时显示iOS编写UI代码效果
Classy
Instruments -- CocoaLayout
Caller:哪一个类和方法触发了一个约束的改变
Constraint:这一栏是回溯(trace)真正的血肉组成。通过约束(Constraint)的内存地址或者通过标识符属性(如果可用的话),你能够辨别出哪个约束被影响了。Constraint栏也包含了VFL字符串,这个字符串阐述了坐标轴所对应的约束线和约束定义的关系。
Constant:定义约束的常量值。
Event:详细说明某个约束被加载了什么样的操作。这个约束可能被创建,移除,或者添加到window上面了,或者属性例如标识符(identifier)被修改或者移除了。这些改变事件包括:
AutoLayout好多好多不懂的 终于明白了 -- UIStackView,AutoLayout与Frame,动画
先进的自动布局工具箱 -- objc的部分文章不适合入门
布局过程
更新约束(自下而上(从子视图到父视图)) (updating constraints) 和布局视图 (自上而下(从父视图到子视图))(laying out views),更新约束 -> 布局视图 -> 显示。
为自定义视图启动自动布局
Intrinsic Content Size
为了在自定义视图中实现固有内容尺寸,你需要做两件事:重写intrinsicContentSize为内容返回恰当的大小,无论何时有任何会影响固有内容尺寸的改变发生时,调用invalidateIntrinsicContentSize。如果这个视图只有一个方向的尺寸设置了固有尺寸,那么为另一个方向的尺寸返回UIViewNoIntrinsicMetric/NSViewNoIntrinsicMetric。
Compression Resistance && Content Hugging
Frame 和 Alignment Rect
在大多数情况下,你仅需要重写alignmentRectInsets方法,这个方法允许你返回相对于 frame 的 edge insets。如果你需要更多控制权,你可以重写alignmentRectForFrame:和frameForAlignmentRect:。如果你不想减去固定的 insets,而是计算基于当前 frame 的 alignment rect,那么这两个方法将会非常有用。但是你需要确保这两个方法是互为可逆的。
基线对齐 (Baseline Alignment)
在 iOS 中,可以通过实现viewForBaselineLayout来激活基线对齐。在这里返回的视图底边缘将会作为 基线。默认实现只是简单的返回自己,然而自定义的实现可以返回任何子视图。
控制布局
本地约束
最好通过实现requiresConstraintBasedLayout返回 YES 明确这个依赖。
添加本地约束的地方是updateConstraints。确保在你的实现中增加任何你需要布局子视图的约束条件之后,调用一下[super updateConstraints]。
控制子视图布局
你可以进一步在 iOS 里重写layoutSubviews,如果你仍然想使用约束条件布局子视图,你需要调用[super layoutSubviews],然后对布局进行微调。
多行文本的固有内容尺寸
UILabel 和 NSTextField -- 文本的高度取决于行的宽度 --preferredMaxLayoutWidth
动画
两个不同的基本策略:约束条件自身动态化 && UIView + layoutIfNeeded
note that:Core Animation 和 Auto Layout 一起产生视图动画时,自己不要接触视图的 frame。一旦视图使用自动布局,那么你已经将设置 frame 的责任交给了布局系统。你的干扰将造成怪异的行为。
如果你想使用 transform 来产生视图动画或者直接使它的 frame 动态化,最干净利索的技术是将这个视图嵌入到一个视图容器内,然后你可以在容器内重写 layoutSubviews,要么选择完全脱离自动布局,要么仅仅调整它的结果。
调试
当你在不可满足的约束条件错误信息中看到NSLayoutResizingMaskConstraints时,你肯定忘了为你某一个视图设定translatesAutoResizingMaskIntoConstraints为 NO。
(1)如果不是很明确是哪个视图导致的问题,你就需要通过内存地址来辨认视图。(或者用masonry的mas_key)。 po,recursiveDescription
(2)你可以改变它的背景颜色:
(lldb) expr ((UIView*)0x7731880).backgroundColor= [UIColorpurpleColor]
(lldb) expr [(UIView*)0x7731880setBackgroundColor:[UIColorpurpleColor]]
(3) 另一种方法是使用 Instrument 的 allocation 模板,根据图表分析。错误消息(内存地址)Instrument 的详细视图切换到 Objects List 页面,并且用 Cmd-F 搜索那个内存地址。
(4) 我们可以在一个 category 中重写NSLayoutConstraint的描述,并且将视图的 tags 包含进去:-- 用关联对象添加对象的tag,用于标识View,全是Masonry的思路。
(5) swizzle UIView 的 addConstraint:/addConstraints:方法,以及布局约束的description方法。
有歧义的布局
(1)UIView 提供三种方式来查明有歧义的布局: hasAmbiguousLayout,exerciseAmbiguityInLayout,和私有方法_autolayoutTrace。
由于这个方法是私有的,确保正式产品里面不要包含调用这个方法的任何代码。为了防止你犯这种错误,你可以在视图的category中这样做:
#ifdef DEBUG
NSLog(@"%@", [selfperformSelector:@selector(_autolayoutTrace)]);
#endif
(2) 另一个标识出有歧义布局更直观的方法就是使用exerciseAmbiguityInLayout。
NSUserDefault选项
UIViewShowAlignmentRects,NSDoubleLocalizedStrings
约束条件代码
一定要记得将 translatesAutoResizingMaskIntoConstraints 设置为 NO。
VFL:constraintsWithVisualFormat:options:metrics:views:方法有一个很有用的option参数。
性能
你可以读这两个博客帖子了解更多的细节
关于 Cocoa Auto Layout,你需要知道10件事 -- mark
当我们写约束时,应该多留意Debugger控制台。我发现Apple关于模棱两可的约束或未满足的约束的错误日志总是可以帮助我们快速定位问题。这个可以参考Apple’s debugging tips in the Cocoa Auto Layout Guide
iOS 8 Size Classes初探
总的来说,iOS对UI这块的改动是跨时代性的,Autolayout的出现使得布局的复杂度减少到了View与View的关系上,再由根 View(也就是屏幕)指定frame,随后所有子View相对布局,把frame的概念归一化到根View的frame上;但有了Size Class后,根视图的frame概念也被移除了,这下整个app的UI和frame这个单词已然脱离关系,这也正是apple想要达到的目的。
iOS 8 AutoLayout与Size Class自悟 -- 图图图
WWDC 2014 Session笔记 - iOS界面开发的大一统
UITraitCollection 和 UITraitEnvironment:
(1)UIScreen,UIWindow,UIViewController和UIView 都实现了UITraitEnvironment.traitCollection == UITraitCollection
(2)和 UIKit 中的响应者链正好相反,traitCollection将会在 view hierarchy 中自上而下地进行传递。
(3)在实际操作时,我们往往会在 ViewController 中重写-traitCollectionDidChange:或者-willTransitionToTraitCollection:withTransitionCoordinator:方法 (对于 ViewController 来说的话,后者也许是更好的选择,因为提供了转场上下文方便进行动画;但是对于普通的 View 来说就只有前面一个方法了)
UIViewController 的表现方式
UISplitViewController
UIPresentationController -> UIAlertController && UISearchController
这种很轻很明确的使用逻辑,block handler 才是最好的选择