UIView是UIResponder的四大直接子类之一,是UI两大类之一。为构建完整的iOS系统知识框架,下文整理UIView的没用过的或不知道的或似懂非懂的属性和方法,并逐个吃透。目标通读Apple api文档。
isOpaque、tintColor、mask、isExclusiveTouch、window、isDescendant(of:)、didMoveToSuperview、didMoveToWindow、directionalLayoutMargins、layoutMargins、preservesSuperviewLayoutMargins、layoutMarginsDidChange、safeAreaInsets、safeAreaLayoutGuide、insetsLayoutMarginsFromSafeArea、constraints、addConstraint、layoutGuides、intrinsicContentSize
1、isOpaque
This property provides a hint to the drawing system as to how it should treat the view. If set to true, the drawing system treats the view as fully opaque, which allows the drawing system to optimize some drawing operations and improve performance. If set to false, the drawing system composites the view normally with other content. The default value of this property is true.
An opaque view is expected to fill its bounds with entirely opaque content—that is, the content should have an alpha value of `1.0`. If the view is opaque and either does not fill its bounds or contains wholly or partially transparent content, the results are unpredictable. You should always set the value of this property to false if the view is fully or partially transparent.
You only need to set a value for the opaque property in subclasses of `UIView` that draw their own content using the `draw(_:)` method. The opaque property has no effect in system-provided classes such as `UIButton`, `UILabel`, `UITableViewCell`, and so on.
isOpaque属性的真实用处是给绘图系统提供一个性能优化开关!如果了解 opaque,需要点屏幕绘制的知识,屏幕上的每个像素点都是通过 RGBA 值(Red、Green、Blue 三原色再配上 Alpha 透明度)表示的,当纹理(UIView 在绘图系统中对应的表示项)出现重叠时,GPU 会按照 Result = Source + Destination * (1 - SourceAlpha)公式计算重叠部分的像素。
Result 是结果 RGB 值,Source 为处在重叠顶部纹理的 RGB值,Destination 为处在重叠底部纹理的 RGB 值。
当 SourceAlpha 为 1 时,绘图系统认为下面的颜色全部被遮盖住了,Result = Source,如果 Source 的 Alpha 不为 0,上下层颜色就会进行合成,所以 opaque 默认设置 YES,提升绘制性能,如果开发中 UIView 是不透明的,opaque 设置为YES, 如果 opaque 设置NO,那么Alpha应该小于1。
总结:isOpaque是一个性能开关,=false会走混合计算逻辑R=S+D*(1-SA)。alpha是一个显示属性,也会走混合计算逻辑。自定义UIView的时候在draw方法内使用,系统提供的类设置该属性无效。
思考:直接设置alpha就行了,opaque用处是什么?自定义UIView的时候在draw方法内使用,系统提供的类UILabel等设置该属性无效。
参考:UIView的 alpha、hidden、opaque属性之间的关系和区别
2、tintColor
If the system cannot find a nondefault color in the hierarchy, this property’s value is a system-defined color instead.
If the view’s `tintAdjustmentMode` property’s value is `UIView.TintAdjustmentMode.dimmed`, then the `tintColor`property value is automatically dimmed.
To refresh subview rendering when this property changes, override the `tintColorDidChange()` method.
Colors that are pattern colors (as described in `UIColor`) are not supported.
If you attempt to use a pattern color as a tint color, the system raises an exception.
具有传递性。举个例子,当我们用xib或者storyboard拖一个UIButton进入面板后,会发现UIButton的颜色为蓝色,可能有人会问为什么是这个颜色呢? 这是因为,默认情况下,一个视图的tintColor为nil,它会使用父视图tintColor属性的值。当我们为指定视图tintColor属性赋值以后,这个色值会自动传播到视图层次结构(以当前视图为根视图)中所有的子视图上。如果在视图层次结构中没有找到一个非默认的tintColor值,则会使用系统定义的颜色值(蓝色,RGB值为[0,0.478431,1]),所以这就是为什么我们拖一个button后显示的是蓝色了。对一个视图来说, 如果没有设置它的tintColor,那么它会默认使用父视图的tintColor,如果设置了这个视图的tintColor, 那么它就会把这个tintColor传递给没有设置tintColor的所有子视图。
tintColorDidChange方法会在视图的tintColor或tintAdjustmentMode属性改变时自动调用。另外,如果当前视图的父视图的tintColor或tintAdjustmentMode属性改变时,也会调用这个方法。
模式颜色:夜间模式的颜色等
思考:用处?UIImageView使用tintColor、APP设置全局TintColor
参考:小谈iOS UIView的tintColor属性
3、clearsContextBeforeDrawing
???
假定您的PNG始终填充整个UIImageView,则应使用以下方法获得最佳性能:opaque = YES,clearsContextBeforeDrawing = NO.在此模式下,backgroundColor无关紧要.像素只需替换为新的图像数据即可.
对于单色背景上的透明PNG,最快的是:opaque = YES,clearsContextBeforeDrawing = YES和backgroundColor匹配您需要的任何内容.在这种情况下[UIColor whiteColor].
4、mask
UIView.mask和layer.mask原理相同,重叠部分显示,其他部分丢掉,重叠部分,可以这样理解,是将maskView每个point的alpha赋值给View的重叠部分相对应的point,这样view的重叠每个point都有个alpha值了,view重叠部分就可能显示多种透明色。
应用:UILabel渐进式显示(歌词同步)、UIImageView部分展示(切圆角或多图阶段展示)。mask 可以配合CAGradientLayer、CAShapeLayer 使用。可以实现蒙层透明度、显示不同形状图层、图案镂空、文字变色等等功能。mask在动画中使用,可以产生很好的动画效果。
参考:UIView的 maskView 属性
5、layerClass
提供了对外的override属性,可以替换view.layer为其他layer,如CATiledLayer 、AVPlayerLayer、TBPaperLayer等。不过可以通过另一种方式:自定义View,新加一个属性XXLayer。
layer的出现是为了mac OS 和 iOS的解耦和跨平台。
6、userInteractionEnabled
在一次动画执行流程中,动画包含的所有UIView都会被临时禁止用户交互,而不管每个UIView本身userInteractionEnabled此时的属性值是YES还是NO。但是在配置动画时,通过添加allowUserInteraction选项可以禁止这种行为的发生,使UIView即使是在执行动画期间依然能响应用户事件。userInteractionEnabled属性默认值为YES,但UIView的一些子类中对该属性进行了覆盖,并将其默认值设置为了NO,其中UIImageView和UILabel就是这样的类
7、isMultipleTouchEnabled
一个view的多指触控开关,不包含子view。不能通过设置父view=false实现两个子view不能同时点击。不影响手势。
8、isExclusiveTouch
阻止向同一window中的其他视图传递触摸事件
9、frame
frame改变将会自动重新显示,且不调用draw方法。想让调用draw方法可以设置contentMode为redraw。该属性可以动画化(alpha也可以)。如果transform包含非恒等变换时要用center和bound代替frame,否则会出问题frame和transform的坑
10、transform
可以动画化。
改变frame的时候,不要用frame属性,用center和bound。
改anchor point的时候用CALayer的anchorPoint属性。
iOS8之后transform不能影响自动布局,AutoLayout布局与Transform的冲突
11、window
每一个view都有一个该属性,当view被add到父view上的时候,该值不在是nil,而是当前window。每个view都是事件响应者链中的一个节点,相应地,每一个节点view都有一个根节点window的引用。
12、addSubview
给next responder赋值为superview。
给window赋值。
继承父view的alpha。
view只能有一个superview,所以一个view不能加载两个view上。
13、removeFromSuperview
注意判空。
不要在视图的draw(_:)方法中调用这个方法。
14、isDescendant
是否是subview或他本身。是视图层次关系的判断。注意isMember是继承关系的判断,不一样。
15、didAddSubview
有子view才会调用。任何adding a subview 的方法都会调用该方法,默认无实现,子类可重写实现额外的操作。UIView的生命周期还有willRemoveSubview、willMove(toSuperview)、didMoveToSuperview、willMove(toWindow)、didMoveToWindow等。
// add view的时候
init
didMoveToSuperview
didMoveToWindow
layoutSubviews
draw
remove的时候也会调用didMoveToSuperview和didMoveToWindow,只是顺序不同
16、directionalLayoutMargins
iOS11之后的,考虑到当前的语言方向(阿拉伯语是从右到左)的内容布局边距。默认为8,也可自行设置。preservesSuperviewLayoutMargins是否保护父视图边距,默认false。
17、addLayoutGuide
引入UILayoutGuide系统的一系列功能性方法之一。UILayoutGuide是 iOS 9 中增加的帮助开发者在使用auto layout布局时的一个虚拟占位对象。所有需要一个虚拟View帮助的事情都可以交给UILayoutGuide来做。它更轻量、更快速、更高效。并没有真正的创建一个View,只是创建了一个矩形空间,只在进行auto layout时参与进来计算。
18、systemLayoutSizeFitting(_ targetSize: CGSize)
该方法为视图返回一个大小值,该值最优地满足视图的当前约束,并且尽可能接近targetSize参数中的值。这个方法实际上并没有改变视图的大小。参数size可以设置layoutFittingCompressedSize或layoutFittingExpandedSize获得一个尽可能小或大的size。还可以设置fittingSizeLevel或low或high的约束优先级来返回需要的最接近或高度优先或宽度优先的大小。
思考:和sizeThatFit有什么区别?可以在layout异步布局之前拿到frame吗?
用处:可以获取自动填充的静态cell的高度。可以在autolayout时拿到size(也可以用layoutIfNeeded)。
19、intrinsicContentSize
只读属性,可通过重写来更改相应控件的内置尺寸大小,告知系统值已改变。在AutoLayout中,它作为UIView的属性(不是语法上的属性),意思就是说我知道自己的大小,如果你没有为我指定大小,我就按照这个大小来。比如:大家都知道在使用AutoLayout的时候,UILabel是不用指定尺寸大小的,只需指定位置即可,就是因为,只要确定了文字内容,字体等信息,它自己就能计算出大小来。同样的UILabel,UIImageView,UIButton等这些组件及某些包含它们的系统组件都有 Intrinsic Content Size 属性,也就说他们都有自己计算size的能力。
使用场景:自定义视图重写该方法。如在MBProgressHUD添加自定义视图时,若给定视图为不确定大小的图片,展示出来的效果就很差强人意,这时就需要更改对应View中的内置大小来适配合适的尺寸。
invalidateIntrinsicContentSize():当自定义视图中的某些内容发生变化,使其固有内容大小失效时调用此函数。这允许基于约束的布局系统在下一个布局过程中考虑新的内在内容大小。重写intrinsicContentSize时可主动调用该方法通知系统内容大小变化了。另外还有详解 intrinsicContentSize 及 约束优先级/content Hugging/content Compression Resistance
20、alignmentRect
Autolayout系统的布局操作是基于alignment rect而非frame。绝大部分情况下它们是一样的,但是当你设置了alignmentRectInsets或者重写了alignmentRectForFrame:和frameForAlignmentRect:时就需要注意两者的差异。
alignmentRec + alignmentRectInsets = frame
forFirstBaselineLayout:使用baseLine做约束的时候autolayout会调用该方法返回的view。重写此属性以返回基于文本的子视图(例如,UILabel或非滚动的UITextView)。返回的视图必须是接收方的子视图。
应用?自定义view,重写方法返回一个view,然后设置其他基于baseline的约束
21、Debugging Auto Layout
constraintsAffectingLayout:返回影响x/y轴的视图布局的约束
hasAmbiguousLayout:视图是否有模糊(不完全确定)约束
exerciseAmbiguityInLayout:在不同的有效值之间以模糊的布局随机改变视图的约束,用于检测有效约束和必要约束
22、sizeToFit与sizeThatFits
sizeThatFits主要用于计算,返回一个最佳size,不更改view的实际大小。
sizeToFit不应该在子类中被重写,应该重写sizeThatFits,调用sizeToFit会自动调用sizeThatFits方法;
sizeToFit和sizeThatFits方法都没有递归,对subviews也不负责,只负责自己
23、setNeedsLayout
标记下一个更新周期(1/60s)中触发布局更新。由于此方法不强制立即更新,而是等待下一个更新周期,因此您可以使用它在更新任何视图之前使多个视图的布局失效。这种行为允许您将所有布局更新合并到一个更新周期中,这通常有利于提高性能
。layoutIfNeeded:如果布局更新正在等待,则立即布局子视图。常用来在snp或masonry布局的时候获取view的frame
23、requiresConstraintBasedLayout
如果你在updateConstraints这个方法里面给自定义的控件更新控件的constraint,那么需要重写requiresConstraintBasedLayout方法,并且返回YES.否则的话,就不会显示该控件.直接在init方法中设置自定义控件的constraint,那么则不需要重写也可以显示。
translatesAutoresizingMaskIntoConstraints:将 frame 布局 自动转化为 约束布局,转化的结果是为这个视图自动添加所有需要的约束,如果我们这时给视图添加自己创建的约束就会约束冲突。
24、overrideUserInterfaceStyle
iOS13新增的设置light/dark模式。设置view之后会影响该view所在的vc的所有view(不包含child vc),设置window之后会影响window的所有view和vc。
semanticContentAttribute:设置左右布局翻转。一般用在语言翻转。也可用在按钮图片文字的左右翻转。
25、addInteraction
主要涉及UIInteraction。
UIContextMenuInteraction:iOS13可用,上下文菜单功能。如系统相册列表长按图片,弹出一个带可操作菜单的预览界面。
UIDragInteraction+UIDropInteraction:iOS11可用。在mac上可以将图片直接拖入聊天软件进行发送,可以将文档、音乐、视频文件等文件拖入相应应用程序直接进行使用。在iPhone上能应用内使用。iPad上能跨应用使用。
UIIndirectScribbleInteraction:iOS14可用。在ipad上使用apple pencil手写输入文本。
UILargeContentViewerInteraction:iOS13可用。在超大字体(辅助功能)的情况下,内容视图展示文案会很大,但是一些系统的bar button如tabbar item之类的仍然很小,这里提供一种补偿方式,手指按住的时候会弹出一个大内容视图查看器。
UIPencilInteraction:iOS12.1可用。Apple Pencil 双击的处理对象
UIPointerInteraction:iOS13.4可用。指针交互??
UIScribbleInteraction:iOS14可用。接受或抑制手写输入文本
UISpringLoadedInteraction:iOS11可用。弹簧加载交互??
UITextInteraction:iOS13可用。??
26、draw
当这个方法被调用时,UIKit已经为你的视图配置了适当的绘图环境,但是不要建立对图形上下文的强引用,因为它会在调用draw(_:)方法之间发生变化。如果你直接子类化UIView不需要调用super。如果子类化一个不同的视图类应该在你的实现中调用super。调用setNeedsDisplay方法会调用draw重新绘制。
27、viewPrintFormatter
UIViewPrintFormatter??
28、canBecomeFocused
默认值为false。此属性通知焦点引擎
视图是否能够被聚焦。有时即使一个视图返回true,一个视图可能因为以下原因而不能被聚焦:视图是隐藏的。视图将alpha设置为0。视图将userInteractionEnabled设置为false。视图当前不在视图层次结构中。
inheritedAnimationDuration:在一个UIView animation block中获取duration的值。
29、addMotionEffect
UIMotionEffect:随着手机的倾斜微移动view
30、restorationIdentifier
视图恢复用
31、snapshotView
截图用。可用场景:添加购物车,移动交换Cel,点击放大图片。多window截图可以先逐个截图然后合并图片。直播截图是opengl实现的,要用drawHierarchy方法。
32、viewWithTag
会先遍历自己,再遍历子views。如果view和subview1是一个tag,找到的是view,而不是subview1。
33、convert(_ point
获取cell的位置。要先获取cell在列表中的位置,再获取列表在view中的位置。
34、hitTest和point(inside
返回触碰点的最佳的view。内部实现如下:
// 1.判断自己能否接收触摸事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
// 2.判断触摸点在不在自己范围内
if (![self pointInside:point withEvent:event]) return nil;
// 3.从后往前遍历自己的子控件,看是否有子控件更适合响应此事件
35、endEditing(_ force)
让subviews中的first responder的文本控件失去响应
36、动画
demo和效果
总结:费劲弄完,效果甚微。很多api看了文档描述也不知道是什么效果和怎么使用。重点:效果、使用场景、demo。接下来计划-常用的大体都过下。不知道怎么使用的查外网或看wwdc。