在咱们在作按钮相关需求时,咱们常常会遇到一种状况就是:图片和文字的排版问题. 咱们在开发的时候常常会遇到的状况会有: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
来控制子视图的展现.