Swift設(shè)置UILabel內(nèi)邊距的實例代碼
摘要
拿來即用短時間效率雖然挺高的,但是拿來的東西沒有消化一次,就無法得心應(yīng)手的使用它。
這次的探索思路就是,查詢官方文檔,設(shè)置不同的值測試單個方法中參數(shù)的變化,之后測試兩個方法的執(zhí)行順序,處理的思路,最后思考總結(jié)。
在總結(jié)方法的處理邏輯時,使用偽代碼的方式梳理方法的執(zhí)行思路。避免解釋文本太多,增加理解的成本。
最近在學(xué)習(xí)小程序開發(fā),接觸到 flex 方式布局,很喜歡這種快速和方便的方式。所以當(dāng)遇到一個頁面上居中顯示文本的需求的時候,就想直接在 UIlabel 上處理,然后在UIlabel上設(shè)置它的內(nèi)邊距(類似 flex 布局)。而不是先放一個 View。然后在這個view 上放置一個 UILabel 控件,通過設(shè)置 UILabel 控件距離父 View 的距離實現(xiàn)。
先看代碼實現(xiàn),下面的代碼,是搜索之后的解決方式,如果只是拿去使用,直接復(fù)制到項目中即可。需要在設(shè)置text前設(shè)置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 } }
為什么這種方式可以實現(xiàn)內(nèi)邊距?
接下來是梳理一下,為什么這樣實現(xiàn)。首先查看開發(fā)者文檔,看代碼塊中這兩個方法是做什么的
函數(shù) | drawText(in rect: CGRect) | textRect(forBounds bounds:, limitedToNumberOfLines numberOfLines) -> CGRect |
---|---|---|
標(biāo)題 | 在rect的區(qū)域中繪制文本或者陰影 | 返回文本的繪制的 rect 區(qū)域 |
詳細(xì) | 如果需要修改 label 中的繪圖行為,需要重寫這個方法。這個方法已經(jīng)配置用于繪圖的默認(rèn)環(huán)境和文本顏色,在重寫的方法中,可以自定義繪制方法,然后調(diào)用super或者自己進(jìn)行繪圖。 | 在系統(tǒng)執(zhí)行其他文本計算之前重寫這個方法(這個太難理解),如果調(diào)用 sizeToFit()和sizeThatFits(_:)會觸發(fā)這個方法 |
鏈接 | https://developer.apple.com/documentation/uikit/uilabel/1620527-drawtext | https://developer.apple.com/documentation/uikit/uilabel/1620545-textrect |
之后驗證這兩個方法的執(zhí)行順序,和各自的作用時,發(fā)現(xiàn)當(dāng) UILabel 的 text 賦值時,會首先調(diào)用textRect方法,之后drawText方法被調(diào)用。
textRect在當(dāng)文本rect的實際寬度大于設(shè)置UILabel的實際寬度時,會再次被調(diào)用,當(dāng)然drawText也是在textRect兩次調(diào)用之后被調(diào)用。
textRect 的作用
看到這里,似乎可以理解開發(fā)者文檔中提到的在系統(tǒng)執(zhí)行其他文本計算之前重寫這個方法了。這個方法的作用就是先獲取 UILabel 的 bounds 和 text 的行數(shù),通過調(diào)用 super 方法計算出 text 的 rect 區(qū)域,返回給系統(tǒng)。
經(jīng)過多次測試驗證發(fā)現(xiàn)執(zhí)行邏輯(偽代碼):
// frame 是設(shè)置 UIlabel 時的 frame if numberOfLines == 1 { textRect 被調(diào)用 return retc 的 width = text 的 widht } else { if text 文本的 width < frame 的 width { text Rect 被調(diào)用 return retc 的 width = text 的 widht } else { text Rect 被調(diào)用兩次后 以 frame 的 wdith 為限制,計算出 text 的 height return rect 的 size = (frame 的 width,text 的 height) } }
drawText 的作用
看drawText中的rect參數(shù),就是textRect方法返回的rect。text文本的實際繪制區(qū)域就通過重寫drawText方法,并在其中調(diào)用它的super方法實現(xiàn)。
經(jīng)過多次驗證,這里的rect并不完全是textRect方法中返回的rect。它們之間的關(guān)系是(偽代碼):
// frame 是設(shè)置 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)
再問:為什么用這種方式實現(xiàn)內(nèi)邊距?
耐心看完這兩個方法之后,對題目中的問題,多少有些思路了。那么就理順一下這個思路。
首先確定一個共識,就是設(shè)置UILabel的內(nèi)邊距,是確定UILabel的frame區(qū)域里面,調(diào)整text的顯示區(qū)域。有了這個共識,接下來就好辦了。
- 第一步就要用textRect方法獲取到text的顯示區(qū)域,默認(rèn)text的顯示區(qū)域和UILabel的bounds區(qū)域是一樣的
- 那就需要和咱們自己設(shè)置的內(nèi)邊距值計算獲取到新的text文本的rect區(qū)域。
- 最后就用drawText方法重新繪制一下text的rect區(qū)域顯示。
那么為什么要用這種方式實現(xiàn)呢?因為目前只有這兩個方法和 text 文本直接有關(guān)系。
優(yōu)化
理論搞了這么多,到了輸出一些干貨的時候了。
如果,UILabel的frame已經(jīng)確定了,重要的是width和height確定了。那么textRect方法就可以不用重寫。
class SHLabel: UILabel { var textInsets: UIEdgeInsets = .zero override func drawText(in rect: CGRect) { super.drawText(in: rect.inset(by: textInsets)) } }
這里就可以看出,當(dāng)UILabel的height不確定時,重寫textRect來幫忙確定UILabel的高度。經(jīng)過驗證下來,這個方法中的 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 } }
擴(kuò)展
文章到這里,就結(jié)束了。如果你是一個細(xì)節(jié)控,感覺textRect在需要還是不需要的時候都被調(diào)用。drawText方法,不管設(shè)置還是不設(shè)置內(nèi)邊距也總是被調(diào)用,會不會影響性能???
這里提供兩個方法解決:
- 可操作性的,就是盡量考慮需求,在不得不用的時候再使用
- 心理安慰性質(zhì)的,那就是放下。細(xì)想一下,這兩個方法都是重寫的方法,重寫的本質(zhì)是什么?那就是不執(zhí)行自己的方法,執(zhí)行重寫的方法。換句話說,就算系統(tǒng)不走重寫的方法,也要走自己的方法。而這些代碼對性能的影響,不值一提。
新發(fā)現(xiàn)
突然之間,有沒有發(fā)現(xiàn),咱們似乎也明白了,為什么UILabel不用固定它的height,它就可以自己確定高度,完全展示 text文本?你想.你細(xì)想...
總結(jié)
到此這篇關(guān)于Swift設(shè)置UILabel內(nèi)邊距的文章就介紹到這了,更多相關(guān)Swift設(shè)置UILabel內(nèi)邊距內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
iPhone與iWatch連接、控制、數(shù)據(jù)傳遞(Swift)的方法
這篇文章主要介紹了iPhone與iWatch連接、控制、數(shù)據(jù)傳遞(Swift)的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-03-03Swift簡單快速的動態(tài)更換app圖標(biāo)AppIcon方法示例
這篇文章主要為大家介紹了Swift動態(tài)更換app圖標(biāo)AppIcon的簡單快速方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06在SpringBoot中實現(xiàn)適配器模式的兩種方式
這篇文章主要介紹了在SpringBoot中實現(xiàn)適配器模式的兩種方式,通過實現(xiàn)類定義類型字段實現(xiàn)和以動態(tài)service名稱的方式實現(xiàn),并且通過代碼示例講解的非常詳細(xì),具有一定的參考價值,需要的朋友可以參考下2024-03-03Swift 3.0基礎(chǔ)學(xué)習(xí)之類與結(jié)構(gòu)體
最近在學(xué)swift 3.0,主要看的是蘋果的官方文檔,這里只是根據(jù)自己看官方文檔的理解所做的一些記錄,不是完整的翻譯,希望也對你有所幫助。下面這篇文章主要介紹了Swift 3.0基礎(chǔ)學(xué)習(xí)之類與結(jié)構(gòu)體的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-03-03