摘要
拿來即用短時間效率雖然挺高的,但是拿來的東西沒有消化一次,就無法得心應手的使用它。
這次的探索思路就是,查詢官方文檔,設置不同的值測試單個方法中參數的變化,之后測試兩個方法的執行順序,處理的思路,最后思考總結。
在總結方法的處理邏輯時,使用偽代碼的方式梳理方法的執行思路。避免解釋文本太多,增加理解的成本。
最近在學習小程序開發,接觸到 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文本?你想.你細想...