Swift設置UILabel內邊距的實例代碼

摘要

拿來即用短時間效率雖然挺高的,但是拿來的東西沒有消化一次,就無法得心應手的使用它。

這次的探索思路就是,查詢官方文檔,設置不同的值測試單個方法中參數的變化,之後測試兩個方法的執行順序,處理的思路,最後思考總結。

在總結方法的處理邏輯時,使用偽代碼的方式梳理方法的執行思路。避免解釋文本太多,增加理解的成本。

最近在學習小程序開發,接觸到 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方法,不管設置還是不設置內邊距也總是被調用,會不會影響性能啊?

這裡提供兩個方法解決:

  1. 可操作性的,就是盡量考慮需求,在不得不用的時候再使用
  2. 心理安慰性質的,那就是放下。細想一下,這兩個方法都是重寫的方法,重寫的本質是什麼?那就是不執行自己的方法,執行重寫的方法。換句話說,就算系統不走重寫的方法,也要走自己的方法。而這些代碼對性能的影響,不值一提。

新發現

突然之間,有沒有發現,咱們似乎也明白瞭,為什麼UILabel不用固定它的height,它就可以自己確定高度,完全展示 text文本?你想.你細想…

總結

到此這篇關於Swift設置UILabel內邊距的文章就介紹到這瞭,更多相關Swift設置UILabel內邊距內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: