在咱們在作按鈕相關需求時,咱們常常會遇到一種狀況就是:圖片和文字的排版問題. 咱們在開發的時候常常會遇到的狀況會有:bash
可是呢系統默認的按鈕設計是左邊圖片右邊文字,因此對此咱們須要對此進行適配調整了.ide
這裏咱們對按鈕進行簡單的配置,而後獲取到對應的子視圖位置打下測試
func setupBaseBtn() {
btn.setTitleColor(.white, for: .normal)
btn.setTitle(btnTitle, for: .normal)
btn.setImage(btnImg, for: .normal)
btn.backgroundColor = .lightGray
btn.setBackgroundImage(UIImage(imageLiteralResourceName: "backColor.jpg"), for: .normal)
}
func setupBtnFrame() {
let offfsetY: CGFloat = 12.0
let offsetX: CGFloat = 10.0
// imgSize: (32, 32)
// 36.0
let textWidth: CGFloat = CGFloat(btn.titleLabel!.font.pointSize) * (CGFloat)(btnTitle.count)
// 56.0
let btnHeight: CGFloat = (CGFloat)(offfsetY * 2.0) + (CGFloat)(btnImg.size.height)
// 88.0
let btnWidth: CGFloat = (CGFloat)(btnImg.size.width) + textWidth + 2 * offsetX
btn.frame = CGRect(x: (self.view.bounds.width - btnWidth) / 2.0, y: 350, width: btnWidth, height: btnHeight)
/*
<OverrideBounds.OverrideBoundsButton: 0x7fda67c18590; baseClass = UIButton; frame = (163 350; 88 56); opaque = NO; layer = <CALayer: 0x600000ac5360>>
<UIImageView: 0x7fda67e28c10; frame = (9.5 12; 32 32); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600000ac19c0>>
<UIButtonLabel: 0x7fda67e11ca0; frame = (41.5 17.5; 37 21.5); text = '下載'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000029a0550>>
*/
}
複製代碼
效果圖:ui
爲何說是正常方式呢,由於大部分狀況下這對於調整其餘視圖的子視圖來講是能夠實現其位置的調整,然而UIButton
卻不行.這裏咱們對子視圖進行微調,而後子視圖位置大小卻並不會進行變動.spa
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// 這樣的設置無用
btn.imageView?.frame = CGRect(x: 0, y: 12, width: 32, height: 32)
btn.titleLabel?.frame = CGRect(x: 42, y: 17.5, width: 37, height: 21.5)
/*
<UIImageView: 0x7fceaf4154b0; frame = (9.5 12; 32 32); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6000018c3d60>>
<UIButtonLabel: 0x7fceaf518070; frame = (41.5 17.5; 37 21.5); text = '下載'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600003bcc7d0>>
*/
}
複製代碼
系統提供contentVerticalAlignment
和contentHorizontalAlignment
參數,咱們能夠對內容設置對齊方式.設計
open var contentVerticalAlignment: UIControl.ContentVerticalAlignment // how to position content vertically inside control. default is center
open var contentHorizontalAlignment: UIControl.ContentHorizontalAlignment // how to position content horizontally inside control. default is center
複製代碼
內容對齊方式枚舉3d
public enum ContentVerticalAlignment : Int {
case center
case top
case bottom
case fill
}
public enum ContentHorizontalAlignment : Int {
case center
case left
case right
case fill
@available(iOS 11.0, *)
case leading
@available(iOS 11.0, *)
case trailing
}
複製代碼
系統默認設置爲內容均爲居中顯示,咱們這邊設置內容居上居左對齊code
// 內容展現始終爲左圖片右文字
// 內容默認橫縱居中顯示
// UIControl: 設置內容對齊方式
btn.contentVerticalAlignment = .top;
btn.contentHorizontalAlignment = .left;
/*
<UIImageView: 0x7f9bc3e18b30; frame = (0 0; 32 32); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6000038ec000>>
<UIButtonLabel: 0x7f9bc3d05e30; frame = (32 0; 37 21.5); text = '下載'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600001ba7160>>
*/
複製代碼
效果圖:orm
這裏須要注意的是fill
這個參數比較特別,很差猜想其具體實現.cdn
// 圖片和文本高度撐滿
// 圖片被拉伸,圖片會變形
// label.x爲圖片的寬
// 水平對齊內容以填充內容矩形; 文本能夠換行,圖像能夠拉伸。
btn.contentHorizontalAlignment = .fill
// 垂直對齊內容以填充內容矩形; 圖像可能會被拉伸。
btn.contentVerticalAlignment = .fill
/*
<UIImageView: 0x7f85f0414c00; frame = (0 0; 51.316 56); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600003094ac0>>
<UIButtonLabel: 0x7f85f04046f0; frame = (32 0; 37 56); text = '下載'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000013f6170>>
*/
複製代碼
效果圖:
這裏咱們能夠知道,這兩個枚舉方法這能是調整內容的對齊方式而沒辦法實現咱們的上述需求即圖片和文本的位置變化. 因此咱們須要另外的解決方式.
系統提供了contentEdgeInsets
、titleEdgeInsets
、imageEdgeInsets
三個參數來讓咱們對視圖進行調整.
open var contentEdgeInsets: UIEdgeInsets // default is UIEdgeInsetsZero. On tvOS 10 or later, default is nonzero except for custom buttons.
open var titleEdgeInsets: UIEdgeInsets // default is UIEdgeInsetsZero
open var imageEdgeInsets: UIEdgeInsets // default is UIEdgeInsetsZero
複製代碼
這裏有一篇文章寫得很詳細理解UIButton的imageEdgeInsets和titleEdgeInsets能夠參考下,咱們這裏就簡單介紹下不展開討論.
這裏咱們簡單的調整下imageEdgeInsets
和titleEdgeInsets
會發現子視圖的位置更改了.
btn.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
btn.titleEdgeInsets = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0)
/*
// 新位置
<UIImageView: 0x7ff2b6c12dc0; frame = (4.5 12; 32 32); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x60000069e740>>
<UIButtonLabel: 0x7ff2b6e1aaf0; frame = (41.5 22.5; 37 21.5); text = '下載'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x6000025b2fd0>>
// 原位置
<UIImageView: 0x7fceaf4154b0; frame = (9.5 12; 32 32); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6000018c3d60>>
<UIButtonLabel: 0x7fceaf518070; frame = (41.5 17.5; 37 21.5); text = '下載'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600003bcc7d0>>
*/
複製代碼
看到結果後咱們會發現,這裏的數值變化不是那麼直接,對於某些數值的微調來講是一個很好的選擇也能夠實現咱們上述的需求,但會顯得不那麼直接,會稍微麻煩.下面咱們介紹另外一種方法來實現.
系統提供了backgroundRect
、contentRect
、titleRect
、imageRect
來重寫子視圖位置大小.
// 設置背景圖片位置大小
func backgroundRect(forBounds: CGRect) -> CGRect
Returns the rectangle in which the receiver draws its background.
// 設置內容位置大小
func contentRect(forBounds: CGRect) -> CGRect
Returns the rectangle in which the receiver draws its entire content.
// 設置文本內容大小
func titleRect(forContentRect: CGRect) -> CGRect
Returns the rectangle in which the receiver draws its title.
// 設置圖片內容大小
func imageRect(forContentRect: CGRect) -> CGRect
Returns the rectangle in which the receiver draws its image.
複製代碼
代碼及效果圖展現:
btn.imageRect = CGRect(x: 12, y: 15, width: 36, height: 36)
btn.titleRect = CGRect(x: 45, y: 20, width: 40, height: 24)
/*
<UIImageView: 0x7fb28a415670; frame = (12 15; 36 36); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6000010046a0>>
<UIButtonLabel: 0x7fb28a705f80; frame = (45 20; 40 24); text = '下載'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600003347070>>
*/
btn.backgroundRect = CGRect(x: 10, y: 10, width: 40, height: 40)
/*
<UIImageView: 0x7fc31e629740; frame = (10 10; 40 40); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600003e6cae0>>
*/
複製代碼
效果圖:
這裏須要注意的是:imageRect
和titleRect
與contentRect
會有衝突,是不能一塊兒設置的. imageRect
,titleRect
不設置的話,將被contentRect
所限制 設置內容視圖:
// imageRect,titleRect不設置的話,將被contentRect所限制
// btn.contentRect = CGRect(x: 0, y: 20, width: 60, height: 30)
/*
<UIImageView: 0x7fc731e05550; frame = (4.5 20; 32 30); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6000000195c0>>
<UIButtonLabel: 0x7fc731c1dd60; frame = (37 24.5; 18.5 21.5); text = '下載'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60000237aa30>>
*/
複製代碼
效果圖:
理解UIButton的imageEdgeInsets和titleEdgeInsets
iOS button的imageEdgeInsets和titleEdgeInsets原理
這兩篇文章有對imageEdgeInsets
和titleEdgeInsets
進行詳細的講解,有興趣的小夥伴能夠前往查看閱讀.
UITextField
呢跟UIButton
對於控件的設置是有些相似的,這裏咱們介紹下系統提供給咱們關於子控件的位置大小的重寫方法.
// drawing and positioning overrides
// boarder...沒發現什麼內容=。=
open func borderRect(forBounds bounds: CGRect) -> CGRect
// _UITextFieldContentView視圖的影響
open func textRect(forBounds bounds: CGRect) -> CGRect
// UITextFieldLabel視圖的影響
open func placeholderRect(forBounds bounds: CGRect) -> CGRect
// UIFieldEditor視圖的影響
open func editingRect(forBounds bounds: CGRect) -> CGRect
// clearBtn視圖的影響
open func clearButtonRect(forBounds bounds: CGRect) -> CGRect
// leftView視圖的影響
open func leftViewRect(forBounds bounds: CGRect) -> CGRect
// rightView視圖的影響
open func rightViewRect(forBounds bounds: CGRect) -> CGRect
複製代碼
tf.borderStyle = .roundedRect
tf.text = "測試文本測試文本"
tf.placeholder = "placeholder文本"
tf.clearButtonMode = .always
tf.leftViewMode = .always
tf.rightViewMode = .always
tf.leftView = UIImageView(image: UIImage(imageLiteralResourceName: "download.png"))
let rightIV = UIImageView(image: UIImage(imageLiteralResourceName: "right.png"))
rightIV.frame = CGRect(x: 0, y: 0, width: 32, height: 32)
tf.rightView = rightIV
/*
text:
<_UITextFieldContentView: 0x7f8ab75189f0; frame = (39 -3; 122 37); opaque = NO; userInteractionEnabled = NO; layer = <__UITextTiledLayer: 0x600003accba0>>
leftView:
<UIImageView: 0x7feff760b350; frame = (0 -1; 32 32); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600001acc440>>
editText:
<UIFieldEditor: 0x7feff9031400; frame = (39 2; 127 26); text = 'Jk'; clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x6000014d1bc0>; layer = <CALayer: 0x600001a95e60>; contentOffset: {0, 0}; contentSize: {127, 26}; adjustedContentInset: {0, 0, 0, 0}>
<_UITextFieldContentView: 0x7feff743dcc0; frame = (0 0; 127 26); opaque = NO; userInteractionEnabled = NO; layer = <__UITextTiledLayer: 0x600003e93b40>>
clearBtn:
<UIButton: 0x7feff760f750; frame = (176 6; 19 19); opaque = NO; layer = <CALayer: 0x600001accd00>>
placeholder:
<UITextFieldLabel: 0x7feff98061b0; frame = (39 4; 127 20.5); text = 'placeholder文本'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600003987160>>
<UIFieldEditor: 0x7feff9031400; frame = (39 2; 127 26); text = ''; clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x6000014d1bc0>; layer = <CALayer: 0x600001a95e60>; contentOffset: {0, 0}; contentSize: {127, 26}; adjustedContentInset: {0, 0, 0, 0}>
<_UITextFieldContentView: 0x7feff743dcc0; frame = (0 0; 127 26); opaque = NO; userInteractionEnabled = NO; layer = <__UITextTiledLayer: 0x600003e93b40>>
rightView:
<UIImageView: 0x7fa2efe404d0; frame = (168 -1; 32 32); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600000906ac0>>
*/
複製代碼
效果圖:
這裏須要注意
rightView
與clearButton
是不能同時存在的.這裏是分別去掉其中一項後跑了兩次獲得的結果._UITextFieldContentView
沒有UITextFieldLabel
和UIFieldEditor
控件UIFieldEditor
和_UITextFieldContentView
控件,而且_UITextFieldContentView
是UIFieldEditor
的子控件UITextFieldLabel
、UIFieldEditor
、_UITextFieldContentView
會同時存在,而且UITextFieldLabel
在他們中的最底層tf.leftViewRect = CGRect(x: 5, y: 5, width: 32, height: 32)
tf.rightViewRect = CGRect(x: 160, y: 0, width: 35, height: 35)
tf.clearButtonRect = CGRect(x: 180, y: 10, width: 24, height: 24)
tf.textRect = CGRect(x: 50, y: 5, width: 100, height: 90)
tf.placeholderRect = CGRect(x: 40, y: 6, width: 120, height: 24)
tf.editingRect = CGRect(x: 42, y: 4, width: 128, height: 30)
/*
leftView:
<UIImageView: 0x7fe891e03b30; frame = (5 5; 32 32); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x60000275f5c0>>
*/
/*
placeholder:
<UITextFieldLabel: 0x7f9ecf438140; frame = (40 6; 120 24); text = 'placeholder文本'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600003957070>>
edit:
<UIFieldEditor: 0x7f9ecf865600; frame = (42 4; 128 30); text = ''; clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x600001424de0>; layer = <CALayer: 0x600001a2e000>; contentOffset: {0, 0}; contentSize: {128, 30}; adjustedContentInset: {0, 0, 0, 0}>
<_UITextFieldContentView: 0x7f9ecf701900; frame = (0 0; 128 30); opaque = NO; userInteractionEnabled = NO; layer = <__UITextTiledLayer: 0x600003e76ca0>>
*/
/*
clearButton:
<UIButton: 0x7fdf23d28170; frame = (180 10; 24 24); opaque = NO; layer = <CALayer: 0x60000043dfc0>>
*/
/*
rightView:
<UIImageView: 0x7f9a88f223a0; frame = (160 0; 35 35); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x600003138fa0>>
*/
/*
<_UITextFieldContentView: 0x7fdbf8715ae0; frame = (50 0; 100 101); opaque = NO; userInteractionEnabled = NO; layer = <__UITextTiledLayer: 0x600001fd5e00>>
*/
複製代碼
須要注意的是_UITextFieldContentView
只是會根據textRect
設置的值來變化,並不會直接拿過來用,這裏能夠看到,只有x
和width
是一致.textRect
值的設定會有必定的考究,搞不清楚原理...還有borderRect
實在不清楚有何用處... 有知道的同窗能夠一塊兒賜教下😀
經過對於UIButton
和UITextField
的子控件位置設置來看,蘋果的優雅設計大概的思路是經過insets
來對子視圖進行微調,經過重寫方法來對子控件的位置進行大的調整.經過mode
來控制子視圖的展現.