摘要
拿來即用短時間效率雖然挺高的,但是拿來的東西沒有消化一次,就無法得心應手的使用它。
這次的探索思路就是,查詢官方文件,設定不同的值測試單個方法中引數的變化,之後測試兩個方法的執行順序,處理的思路,最後思考總結。
在總結方法的處理邏輯時,使用虛擬碼的方式梳理方法的執行思路。避免解釋文字太多,增加理解的成本。
最近在學習小程式開發,接觸到 flex 方式佈局,很喜歡這種快速和方便的方式。所以當遇到一個頁面上居中顯示文字的需求的時候,就想直接在 UIlabel 上處理,然後在UIlabel上設定它的內邊距(類似 flex 佈局)。而不是先放一個 View。然後在這個view 上放置一個 UILabel 控制元件,通過設定 UILabel 控制元件距離父 View 的距離實現。
先看程式碼實現,下面的程式碼,是搜尋之後的解決方式,如果只是拿去使用,直接複製到專案中即可。需要在設定text前設定textInsets
class SHLabel: UILabel {
var textInsets: UIEdgeInsets = .zero
override func drawText(in rect: CGRect) {
super.drawText(in: rect.inset(by: textInsets))
}
override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
let insets = textInsets
var rect = super.textRect(forBounds: bounds.inset(by: insets), limitedToNumberOfLines: numberOfLines)
rect.origin.x -= insets.left
rect.origin.y -= insets.top
rect.size.width += (insets.left + insets.right)
rect.size.height += (insets.top + insets.bottom)
return rect
}
}
為什麼這種方式可以實現內邊距?
接下來是梳理一下,為什麼這樣實現。首先檢視開發者文件,看程式碼塊中這兩個方法是做什麼的
函式 | drawText(in rect: CGRect) |
textRect(forBounds bounds:, limitedToNumberOfLines numberOfLines) -> CGRect |
---|---|---|
標題 | 在rect的區域中繪製文字或者陰影 | 返回文字的繪製的 rect 區域 |
詳細 | 如果需要修改 label 中的繪圖行為,需要重寫這個方法。這個方法已經配置用於繪圖的預設環境和文字顏色,在重寫的方法中,可以自定義繪製方法,然後呼叫super或者自己進行繪圖。 | 在系統執行其他文字計算之前重寫這個方法(這個太難理解),如果呼叫 sizeToFit() 和sizeThatFits(_:) 會觸發這個方法 |
連結 | https://developer.apple.com/documentation/uikit/uilabel/1620527-drawtext | https://developer.apple.com/documentation/uikit/uilabel/1620545-textrect |
之後驗證這兩個方法的執行順序,和各自的作用時,發現當 UILabel 的 text 賦值時,會首先呼叫textRect
方法,之後drawText
方法被呼叫。
textRect
在當文字rect的實際寬度大於設定UILabel的實際寬度時,會再次被呼叫,當然drawText
也是在textRect
兩次呼叫之後被呼叫。
textRect
的作用
看到這裡,似乎可以理解開發者文件中提到的在系統執行其他文字計算之前重寫這個方法了。這個方法的作用就是先獲取 UILabel 的 bounds 和 text 的行數,通過呼叫 super 方法計算出 text 的 rect 區域,返回給系統。
經過多次測試驗證發現執行邏輯(虛擬碼):
// frame 是設定 UIlabel 時的 frame
if numberOfLines == 1 {
textRect 被呼叫
return retc 的 width = text 的 widht
} else {
if text 文字的 width < frame 的 width {
text Rect 被呼叫
return retc 的 width = text 的 widht
} else {
text Rect 被呼叫兩次後
以 frame 的 wdith 為限制,計算出 text 的 height
return rect 的 size = (frame 的 width,text 的 height)
}
}
drawText
的作用
看drawText
中的rect引數,就是textRect
方法返回的rect。text文字的實際繪製區域就通過重寫drawText
方法,並在其中呼叫它的super方法實現。
經過多次驗證,這裡的rect
並不完全是textRect
方法中返回的rect。它們之間的關係是(虛擬碼):
// frame 是設定 UILabel 的 frame
// rect 是 `textRect` 返回的
dx = frame.x
dy = frame.y
if frame.width 確定不變 {
dwidth = frame.width
} else {
dwidth = rect.width
}
if frame.height 確定不變 {
dheight = frame.height
} else {
dheight = rect.width
}
return drawText 中的 rect(dx,dy,dwidth,dheight)
再問:為什麼用這種方式實現內邊距?
耐心看完這兩個方法之後,對題目中的問題,多少有些思路了。那麼就理順一下這個思路。
首先確定一個共識,就是設定UILabel的內邊距,是確定UILabel的frame區域裡面,調整text的顯示區域。有了這個共識,接下來就好辦了。
第一步就要用
textRect
方法獲取到text的顯示區域,預設text的顯示區域和UILabel的bounds區域是一樣的那就需要和咱們自己設定的內邊距值計算獲取到新的text文字的rect區域。
最後就用
drawText
方法重新繪製一下text的rect區域顯示。
那麼為什麼要用這種方式實現呢?因為目前只有這兩個方法和 text 文字直接有關係。
優化
理論搞了這麼多,到了輸出一些乾貨的時候了。
如果,UILabel的frame已經確定了,重要的是width和height確定了。那麼textRect
方法就可以不用重寫。
class SHLabel: UILabel {
var textInsets: UIEdgeInsets = .zero
override func drawText(in rect: CGRect) {
super.drawText(in: rect.inset(by: textInsets))
}
}
這裡就可以看出,當UILabel的height不確定時,重寫textRect
來幫忙確定UILabel的高度。經過驗證下來,這個方法中的 x 和 y 也是不用處理的,什麼時候會用到它?我目前還沒有遇到。
class SHLabel: UILabel {
var textInsets: UIEdgeInsets = .zero
override func drawText(in rect: CGRect) {
super.drawText(in: rect.inset(by: textInsets))
}
override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
let insets = textInsets
var rect = super.textRect(forBounds: bounds.inset(by: insets), limitedToNumberOfLines: numberOfLines)
// rect.origin.x -= insets.left
// rect.origin.y -= insets.top
rect.size.width += (insets.left + insets.right)
rect.size.height += (insets.top + insets.bottom)
return rect
}
}
擴充套件
文章到這裡,就結束了。如果你是一個細節控,感覺textRect
在需要還是不需要的時候都被呼叫。drawText
方法,不管設定還是不設定內邊距也總是被呼叫,會不會影響效能啊?
這裡提供兩個方法解決:
可操作性的,就是儘量考慮需求,在不得不用的時候再使用
心理安慰性質的,那就是放下。細想一下,這兩個方法都是重寫的方法,重寫的本質是什麼?那就是不執行自己的方法,執行重寫的方法。換句話說,就算系統不走重寫的方法,也要走自己的方法。而這些程式碼對效能的影響,不值一提。
新發現
突然之間,有沒有發現,咱們似乎也明白了,為什麼UILabel不用固定它的height,它就可以自己確定高度,完全展示 text文字?你想.你細想...