iOS心法-UIView

UIView是UIResponder的四大直接子類之一,是UI兩大類之一。為構(gòu)建完整的iOS系統(tǒng)知識(shí)框架,下文整理UIView的沒(méi)用過(guò)的或不知道的或似懂非懂的屬性和方法,并逐個(gè)吃透。目標(biāo)通讀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屬性的真實(shí)用處是給繪圖系統(tǒng)提供一個(gè)性能優(yōu)化開(kāi)關(guān)!如果了解 opaque,需要點(diǎn)屏幕繪制的知識(shí),屏幕上的每個(gè)像素點(diǎn)都是通過(guò) RGBA 值(Red、Green、Blue 三原色再配上 Alpha 透明度)表示的,當(dāng)紋理(UIView 在繪圖系統(tǒng)中對(duì)應(yīng)的表示項(xiàng))出現(xiàn)重疊時(shí),GPU 會(huì)按照 Result = Source + Destination * (1 - SourceAlpha)公式計(jì)算重疊部分的像素。
Result 是結(jié)果 RGB 值,Source 為處在重疊頂部紋理的 RGB值,Destination 為處在重疊底部紋理的 RGB 值。
當(dāng) SourceAlpha 為 1 時(shí),繪圖系統(tǒng)認(rèn)為下面的顏色全部被遮蓋住了,Result = Source,如果 Source 的 Alpha 不為 0,上下層顏色就會(huì)進(jìn)行合成,所以 opaque 默認(rèn)設(shè)置 YES,提升繪制性能,如果開(kāi)發(fā)中 UIView 是不透明的,opaque 設(shè)置為YES, 如果 opaque 設(shè)置NO,那么Alpha應(yīng)該小于1。
總結(jié):isOpaque是一個(gè)性能開(kāi)關(guān),=false會(huì)走混合計(jì)算邏輯R=S+D*(1-SA)。alpha是一個(gè)顯示屬性,也會(huì)走混合計(jì)算邏輯。自定義UIView的時(shí)候在draw方法內(nèi)使用,系統(tǒng)提供的類設(shè)置該屬性無(wú)效。

思考:直接設(shè)置alpha就行了,opaque用處是什么?自定義UIView的時(shí)候在draw方法內(nèi)使用,系統(tǒng)提供的類UILabel等設(shè)置該屬性無(wú)效。
參考:UIView的 alpha、hidden、opaque屬性之間的關(guān)系和區(qū)別

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.

具有傳遞性。舉個(gè)例子,當(dāng)我們用xib或者storyboard拖一個(gè)UIButton進(jìn)入面板后,會(huì)發(fā)現(xiàn)UIButton的顏色為藍(lán)色,可能有人會(huì)問(wèn)為什么是這個(gè)顏色呢? 這是因?yàn)?,默認(rèn)情況下,一個(gè)視圖的tintColor為nil,它會(huì)使用父視圖tintColor屬性的值。當(dāng)我們?yōu)橹付ㄒ晥DtintColor屬性賦值以后,這個(gè)色值會(huì)自動(dòng)傳播到視圖層次結(jié)構(gòu)(以當(dāng)前視圖為根視圖)中所有的子視圖上。如果在視圖層次結(jié)構(gòu)中沒(méi)有找到一個(gè)非默認(rèn)的tintColor值,則會(huì)使用系統(tǒng)定義的顏色值(藍(lán)色,RGB值為[0,0.478431,1]),所以這就是為什么我們拖一個(gè)button后顯示的是藍(lán)色了。對(duì)一個(gè)視圖來(lái)說(shuō), 如果沒(méi)有設(shè)置它的tintColor,那么它會(huì)默認(rèn)使用父視圖的tintColor,如果設(shè)置了這個(gè)視圖的tintColor, 那么它就會(huì)把這個(gè)tintColor傳遞給沒(méi)有設(shè)置tintColor的所有子視圖。

tintColorDidChange方法會(huì)在視圖的tintColor或tintAdjustmentMode屬性改變時(shí)自動(dòng)調(diào)用。另外,如果當(dāng)前視圖的父視圖的tintColor或tintAdjustmentMode屬性改變時(shí),也會(huì)調(diào)用這個(gè)方法。

模式顏色:夜間模式的顏色等

思考:用處?UIImageView使用tintColor、APP設(shè)置全局TintColor
參考:小談iOS UIView的tintColor屬性

3、clearsContextBeforeDrawing
???

假定您的PNG始終填充整個(gè)UIImageView,則應(yīng)使用以下方法獲得最佳性能:opaque = YES,clearsContextBeforeDrawing = NO.在此模式下,backgroundColor無(wú)關(guān)緊要.像素只需替換為新的圖像數(shù)據(jù)即可.
對(duì)于單色背景上的透明PNG,最快的是:opaque = YES,clearsContextBeforeDrawing = YES和backgroundColor匹配您需要的任何內(nèi)容.在這種情況下[UIColor whiteColor].

4、mask
UIView.mask和layer.mask原理相同,重疊部分顯示,其他部分丟掉,重疊部分,可以這樣理解,是將maskView每個(gè)point的alpha賦值給View的重疊部分相對(duì)應(yīng)的point,這樣view的重疊每個(gè)point都有個(gè)alpha值了,view重疊部分就可能顯示多種透明色。
應(yīng)用:UILabel漸進(jìn)式顯示(歌詞同步)、UIImageView部分展示(切圓角或多圖階段展示)。mask 可以配合CAGradientLayer、CAShapeLayer 使用。可以實(shí)現(xiàn)蒙層透明度、顯示不同形狀圖層、圖案鏤空、文字變色等等功能。mask在動(dòng)畫中使用,可以產(chǎn)生很好的動(dòng)畫效果。
參考:UIView的 maskView 屬性

5、layerClass
提供了對(duì)外的override屬性,可以替換view.layer為其他layer,如CATiledLayer 、AVPlayerLayer、TBPaperLayer等。不過(guò)可以通過(guò)另一種方式:自定義View,新加一個(gè)屬性XXLayer。
layer的出現(xiàn)是為了mac OS 和 iOS的解耦和跨平臺(tái)。

6、userInteractionEnabled
在一次動(dòng)畫執(zhí)行流程中,動(dòng)畫包含的所有UIView都會(huì)被臨時(shí)禁止用戶交互,而不管每個(gè)UIView本身userInteractionEnabled此時(shí)的屬性值是YES還是NO。但是在配置動(dòng)畫時(shí),通過(guò)添加allowUserInteraction選項(xiàng)可以禁止這種行為的發(fā)生,使UIView即使是在執(zhí)行動(dòng)畫期間依然能響應(yīng)用戶事件。userInteractionEnabled屬性默認(rèn)值為YES,但UIView的一些子類中對(duì)該屬性進(jìn)行了覆蓋,并將其默認(rèn)值設(shè)置為了NO,其中UIImageView和UILabel就是這樣的類

7、isMultipleTouchEnabled
一個(gè)view的多指觸控開(kāi)關(guān),不包含子view。不能通過(guò)設(shè)置父view=false實(shí)現(xiàn)兩個(gè)子view不能同時(shí)點(diǎn)擊。不影響手勢(shì)。

8、isExclusiveTouch
阻止向同一window中的其他視圖傳遞觸摸事件

9、frame
frame改變將會(huì)自動(dòng)重新顯示,且不調(diào)用draw方法。想讓調(diào)用draw方法可以設(shè)置contentMode為redraw。該屬性可以動(dòng)畫化(alpha也可以)。如果transform包含非恒等變換時(shí)要用center和bound代替frame,否則會(huì)出問(wèn)題frame和transform的坑

10、transform
可以動(dòng)畫化。
改變frame的時(shí)候,不要用frame屬性,用center和bound。
改anchor point的時(shí)候用CALayer的anchorPoint屬性。
iOS8之后transform不能影響自動(dòng)布局,AutoLayout布局與Transform的沖突

11、window
每一個(gè)view都有一個(gè)該屬性,當(dāng)view被add到父view上的時(shí)候,該值不在是nil,而是當(dāng)前window。每個(gè)view都是事件響應(yīng)者鏈中的一個(gè)節(jié)點(diǎn),相應(yīng)地,每一個(gè)節(jié)點(diǎn)view都有一個(gè)根節(jié)點(diǎn)window的引用。

12、addSubview
給next responder賦值為superview。
給window賦值。
繼承父view的alpha。
view只能有一個(gè)superview,所以一個(gè)view不能加載兩個(gè)view上。

13、removeFromSuperview
注意判空。
不要在視圖的draw(_:)方法中調(diào)用這個(gè)方法。

14、isDescendant
是否是subview或他本身。是視圖層次關(guān)系的判斷。注意isMember是繼承關(guān)系的判斷,不一樣。

15、didAddSubview
有子view才會(huì)調(diào)用。任何adding a subview 的方法都會(huì)調(diào)用該方法,默認(rèn)無(wú)實(shí)現(xiàn),子類可重寫實(shí)現(xiàn)額外的操作。UIView的生命周期還有willRemoveSubview、willMove(toSuperview)、didMoveToSuperview、willMove(toWindow)、didMoveToWindow等。

// add view的時(shí)候
init
didMoveToSuperview
didMoveToWindow
layoutSubviews
draw

remove的時(shí)候也會(huì)調(diào)用didMoveToSuperview和didMoveToWindow,只是順序不同

16、directionalLayoutMargins
iOS11之后的,考慮到當(dāng)前的語(yǔ)言方向(阿拉伯語(yǔ)是從右到左)的內(nèi)容布局邊距。默認(rèn)為8,也可自行設(shè)置。preservesSuperviewLayoutMargins是否保護(hù)父視圖邊距,默認(rèn)false。

17、addLayoutGuide
引入U(xiǎn)ILayoutGuide系統(tǒng)的一系列功能性方法之一。UILayoutGuide是 iOS 9 中增加的幫助開(kāi)發(fā)者在使用auto layout布局時(shí)的一個(gè)虛擬占位對(duì)象。所有需要一個(gè)虛擬View幫助的事情都可以交給UILayoutGuide來(lái)做。它更輕量、更快速、更高效。并沒(méi)有真正的創(chuàng)建一個(gè)View,只是創(chuàng)建了一個(gè)矩形空間,只在進(jìn)行auto layout時(shí)參與進(jìn)來(lái)計(jì)算。

18、systemLayoutSizeFitting(_ targetSize: CGSize)
該方法為視圖返回一個(gè)大小值,該值最優(yōu)地滿足視圖的當(dāng)前約束,并且盡可能接近targetSize參數(shù)中的值。這個(gè)方法實(shí)際上并沒(méi)有改變視圖的大小。參數(shù)size可以設(shè)置layoutFittingCompressedSize或layoutFittingExpandedSize獲得一個(gè)盡可能小或大的size。還可以設(shè)置fittingSizeLevel或low或high的約束優(yōu)先級(jí)來(lái)返回需要的最接近或高度優(yōu)先或?qū)挾葍?yōu)先的大小。
思考:和sizeThatFit有什么區(qū)別?可以在layout異步布局之前拿到frame嗎?
用處:可以獲取自動(dòng)填充的靜態(tài)cell的高度??梢栽赼utolayout時(shí)拿到size(也可以用layoutIfNeeded)。

19、intrinsicContentSize
只讀屬性,可通過(guò)重寫來(lái)更改相應(yīng)控件的內(nèi)置尺寸大小,告知系統(tǒng)值已改變。在AutoLayout中,它作為UIView的屬性(不是語(yǔ)法上的屬性),意思就是說(shuō)我知道自己的大小,如果你沒(méi)有為我指定大小,我就按照這個(gè)大小來(lái)。比如:大家都知道在使用AutoLayout的時(shí)候,UILabel是不用指定尺寸大小的,只需指定位置即可,就是因?yàn)?,只要確定了文字內(nèi)容,字體等信息,它自己就能計(jì)算出大小來(lái)。同樣的UILabel,UIImageView,UIButton等這些組件及某些包含它們的系統(tǒng)組件都有 Intrinsic Content Size 屬性,也就說(shuō)他們都有自己計(jì)算size的能力。
使用場(chǎng)景:自定義視圖重寫該方法。如在MBProgressHUD添加自定義視圖時(shí),若給定視圖為不確定大小的圖片,展示出來(lái)的效果就很差強(qiáng)人意,這時(shí)就需要更改對(duì)應(yīng)View中的內(nèi)置大小來(lái)適配合適的尺寸。
invalidateIntrinsicContentSize():當(dāng)自定義視圖中的某些內(nèi)容發(fā)生變化,使其固有內(nèi)容大小失效時(shí)調(diào)用此函數(shù)。這允許基于約束的布局系統(tǒng)在下一個(gè)布局過(guò)程中考慮新的內(nèi)在內(nèi)容大小。重寫intrinsicContentSize時(shí)可主動(dòng)調(diào)用該方法通知系統(tǒng)內(nèi)容大小變化了。另外還有詳解 intrinsicContentSize 及 約束優(yōu)先級(jí)/content Hugging/content Compression Resistance

20、alignmentRect
Autolayout系統(tǒng)的布局操作是基于alignment rect而非frame。絕大部分情況下它們是一樣的,但是當(dāng)你設(shè)置了alignmentRectInsets或者重寫了alignmentRectForFrame:和frameForAlignmentRect:時(shí)就需要注意兩者的差異。
alignmentRec + alignmentRectInsets = frame


image.png

forFirstBaselineLayout:使用baseLine做約束的時(shí)候autolayout會(huì)調(diào)用該方法返回的view。重寫此屬性以返回基于文本的子視圖(例如,UILabel或非滾動(dòng)的UITextView)。返回的視圖必須是接收方的子視圖。
應(yīng)用?自定義view,重寫方法返回一個(gè)view,然后設(shè)置其他基于baseline的約束

21、Debugging Auto Layout
constraintsAffectingLayout:返回影響x/y軸的視圖布局的約束
hasAmbiguousLayout:視圖是否有模糊(不完全確定)約束
exerciseAmbiguityInLayout:在不同的有效值之間以模糊的布局隨機(jī)改變視圖的約束,用于檢測(cè)有效約束和必要約束

22、sizeToFit與sizeThatFits
sizeThatFits主要用于計(jì)算,返回一個(gè)最佳size,不更改view的實(shí)際大小。
sizeToFit不應(yīng)該在子類中被重寫,應(yīng)該重寫sizeThatFits,調(diào)用sizeToFit會(huì)自動(dòng)調(diào)用sizeThatFits方法;
sizeToFit和sizeThatFits方法都沒(méi)有遞歸,對(duì)subviews也不負(fù)責(zé),只負(fù)責(zé)自己

23、setNeedsLayout
標(biāo)記下一個(gè)更新周期(1/60s)中觸發(fā)布局更新。由于此方法不強(qiáng)制立即更新,而是等待下一個(gè)更新周期,因此您可以使用它在更新任何視圖之前使多個(gè)視圖的布局失效。這種行為允許您將所有布局更新合并到一個(gè)更新周期中,這通常有利于提高性能。layoutIfNeeded:如果布局更新正在等待,則立即布局子視圖。常用來(lái)在snp或masonry布局的時(shí)候獲取view的frame

23、requiresConstraintBasedLayout
如果你在updateConstraints這個(gè)方法里面給自定義的控件更新控件的constraint,那么需要重寫requiresConstraintBasedLayout方法,并且返回YES.否則的話,就不會(huì)顯示該控件.直接在init方法中設(shè)置自定義控件的constraint,那么則不需要重寫也可以顯示。
translatesAutoresizingMaskIntoConstraints:將 frame 布局 自動(dòng)轉(zhuǎn)化為 約束布局,轉(zhuǎn)化的結(jié)果是為這個(gè)視圖自動(dòng)添加所有需要的約束,如果我們這時(shí)給視圖添加自己創(chuàng)建的約束就會(huì)約束沖突。

24、overrideUserInterfaceStyle
iOS13新增的設(shè)置light/dark模式。設(shè)置view之后會(huì)影響該view所在的vc的所有view(不包含child vc),設(shè)置window之后會(huì)影響window的所有view和vc。
semanticContentAttribute:設(shè)置左右布局翻轉(zhuǎn)。一般用在語(yǔ)言翻轉(zhuǎn)。也可用在按鈕圖片文字的左右翻轉(zhuǎn)。

25、addInteraction
主要涉及UIInteraction。
UIContextMenuInteraction:iOS13可用,上下文菜單功能。如系統(tǒng)相冊(cè)列表長(zhǎng)按圖片,彈出一個(gè)帶可操作菜單的預(yù)覽界面。
UIDragInteraction+UIDropInteraction:iOS11可用。在mac上可以將圖片直接拖入聊天軟件進(jìn)行發(fā)送,可以將文檔、音樂(lè)、視頻文件等文件拖入相應(yīng)應(yīng)用程序直接進(jìn)行使用。在iPhone上能應(yīng)用內(nèi)使用。iPad上能跨應(yīng)用使用。
UIIndirectScribbleInteraction:iOS14可用。在ipad上使用apple pencil手寫輸入文本。
UILargeContentViewerInteraction:iOS13可用。在超大字體(輔助功能)的情況下,內(nèi)容視圖展示文案會(huì)很大,但是一些系統(tǒng)的bar button如tabbar item之類的仍然很小,這里提供一種補(bǔ)償方式,手指按住的時(shí)候會(huì)彈出一個(gè)大內(nèi)容視圖查看器。
UIPencilInteraction:iOS12.1可用。Apple Pencil 雙擊的處理對(duì)象
UIPointerInteraction:iOS13.4可用。指針交互??
UIScribbleInteraction:iOS14可用。接受或抑制手寫輸入文本
UISpringLoadedInteraction:iOS11可用。彈簧加載交互??
UITextInteraction:iOS13可用。??

26、draw
當(dāng)這個(gè)方法被調(diào)用時(shí),UIKit已經(jīng)為你的視圖配置了適當(dāng)?shù)睦L圖環(huán)境,但是不要建立對(duì)圖形上下文的強(qiáng)引用,因?yàn)樗鼤?huì)在調(diào)用draw(_:)方法之間發(fā)生變化。如果你直接子類化UIView不需要調(diào)用super。如果子類化一個(gè)不同的視圖類應(yīng)該在你的實(shí)現(xiàn)中調(diào)用super。調(diào)用setNeedsDisplay方法會(huì)調(diào)用draw重新繪制。

27、viewPrintFormatter
UIViewPrintFormatter??

28、canBecomeFocused
默認(rèn)值為false。此屬性通知焦點(diǎn)引擎視圖是否能夠被聚焦。有時(shí)即使一個(gè)視圖返回true,一個(gè)視圖可能因?yàn)橐韵略蚨荒鼙痪劢?視圖是隱藏的。視圖將alpha設(shè)置為0。視圖將userInteractionEnabled設(shè)置為false。視圖當(dāng)前不在視圖層次結(jié)構(gòu)中。
inheritedAnimationDuration:在一個(gè)UIView animation block中獲取duration的值。

29、addMotionEffect
UIMotionEffect:隨著手機(jī)的傾斜微移動(dòng)view

30、restorationIdentifier
視圖恢復(fù)用

31、snapshotView
截圖用。可用場(chǎng)景:添加購(gòu)物車,移動(dòng)交換Cel,點(diǎn)擊放大圖片。多window截圖可以先逐個(gè)截圖然后合并圖片。直播截圖是opengl實(shí)現(xiàn)的,要用drawHierarchy方法。

32、viewWithTag
會(huì)先遍歷自己,再遍歷子views。如果view和subview1是一個(gè)tag,找到的是view,而不是subview1。

33、convert(_ point
獲取cell的位置。要先獲取cell在列表中的位置,再獲取列表在view中的位置。

34、hitTest和point(inside
返回觸碰點(diǎn)的最佳的view。內(nèi)部實(shí)現(xiàn)如下:
// 1.判斷自己能否接收觸摸事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
// 2.判斷觸摸點(diǎn)在不在自己范圍內(nèi)
if (![self pointInside:point withEvent:event]) return nil;
// 3.從后往前遍歷自己的子控件,看是否有子控件更適合響應(yīng)此事件

35、endEditing(_ force)
讓subviews中的first responder的文本控件失去響應(yīng)

36、動(dòng)畫
demo和效果

總結(jié):費(fèi)勁弄完,效果甚微。很多api看了文檔描述也不知道是什么效果和怎么使用。重點(diǎn):效果、使用場(chǎng)景、demo。接下來(lái)計(jì)劃-常用的大體都過(guò)下。不知道怎么使用的查外網(wǎng)或看wwdc。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • UIView可以說(shuō)是我們?nèi)粘9ぷ髦薪佑|最多的一個(gè)對(duì)象、是所有視圖控件(不包括視圖控制器)的基類。主要的功能包括視圖...
    kirito_song閱讀 4,179評(píng)論 1 33
  • 一、初始化方法 1、- initWithFrame: UIView *view = [[UIView alloc]...
    默默_David閱讀 2,732評(píng)論 1 3
  • 本文獻(xiàn)給所有想學(xué)習(xí)iOS的開(kāi)發(fā)人員(PS:像提高技術(shù)水平還得靠自己多多實(shí)踐) @property(nonatomi...
    賣萌的提莫閱讀 2,267評(píng)論 1 4
  • 從上圖我們可以看出UIView繼承于UIResponder 慣例,我們?cè)诳匆粋€(gè)類的時(shí)候首先要看的是他的初始化方法:...
    西河老伯閱讀 1,217評(píng)論 0 3
  • 概述 UIView是所有控件的父類,UIView的api則相對(duì)比較底層了,繪圖,自動(dòng)布局,動(dòng)畫。這章有點(diǎn)費(fèi)勁,有些...
    guaker閱讀 3,151評(píng)論 0 7

友情鏈接更多精彩內(nèi)容