contentVerticalAlignment 和 contentHorizontalAlignment
它们分别表示 button 的 content 在垂直、水平方向的对齐方式。
默认情况下,把 Button 的 title 和 image 当作整体,进行垂直、水平居中
titleEdgeInsets 和 imageEdgeInsets
Apple文档上表述:分别表示按钮文本、图片周围矩形的插入或起始边距。个人理解:就是指 image、title 的上下左右边缘离开原来的位置(布局后的位置)。默认值是:UIEdgeInsetsZero
修改 titleEdgeInsets、imageEdgeInsets 不会改变 Button 的大小,可能会显示在button的外部。
contentAlignment 对 edgeInsets 的影响
详细可以看看 简书 S型身材的猪,下面直接上验证结果:
1、先进行初始参数设置:
1 2 3
| button.size = (80, 80), button.contentAlignment = center; image.frame = (8.333333333333336, 30.0, 20.0, 20.0); title.frame = (28.666666666666664, 31.66666666666667, 43.0, 17.0);
|
2、修改 imageEdgeInsets.left = 10,那么 image.x == 18.333333333333336 ? 看结果:
1 2 3
| btn.imageEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0) print(btn.imageView?.frame)
|
3、image.x 怎么不是 18.333333333333336 ???
imageEdgeInsets
和 titleEdgeInsets
的 top、left、bottom、right
都是相对于 button 的 contentRect 而言。而当 button 的 contentEdgeInsets 值为 UIEdgeInsets.zero 时,它们显示的范围为 button.bounds。
由于 button.contentAlignment = center;
计算时是把 title.width + image.width
一起居中显示布局的,然后 imageEdgeInsets.left = 10
,所以 title + image
显示的范围为 (70, 80) 来进行居中布局,所以,image.x = (80 - 10 - 20 - 43) / 2 + 10 = 13.5 ≈ 13.333333333333336。
总结:
当 UIButton 的 frame 确定时,imageEdgeInsets
和 titleEdgeInsets
的值只是确定了 image 和 title 显示的范围,然后再联合 contentVerticalAlignment 和contentHorizontalAlignment 进行对齐。
如果真的需要调整 image 向右移动 10,则需要设置 right = -10,这样 image.x = (80 - 10 + 10 - 20 - 43) / 2 + 10 = 18.5 ≈ 18.333333333333336。
1
| btn.imageEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: -10)
|
方式一:titleEdgeInsets 和 imageEdgeInsets 调整 button 的 content
当 btn.contentAlignment = center
1 2
| let imageFrame = btn.imageView?.frame ?? CGRect.zero let titleFrame = btn.titleLabel?.frame ?? CGRect.zero
|
1.1、默认图片在左,文字在右,添加间距 10
1 2
| btn.imageEdgeInsets = UIEdgeInsets(top: 0, left: -5, bottom: 0, right: 5) btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: -5)
|
1.2、文字在左,图片在右,间距为 10
1 2 3
| let space: CGFloat = 5 btn.imageEdgeInsets = UIEdgeInsets(top: 0, left: titleFrame.width + space, bottom: 0, right: -(titleFrame.width + space)) btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -(imageFrame.width + space), bottom: 0, right: imageFrame.width + space)
|
1.3、文字在下,图片在上,间距为10
先把文字、图片都居中:
1 2
| btn.imageEdgeInsets = UIEdgeInsets(top: 0, left: titleFrame.width / 2, bottom: 0, right: -titleFrame.width / 2) btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageFrame.width / 2, bottom: 0, right: imageFrame.width / 2)
|
1 2 3
| let space: CGFloat = 10 btn.imageEdgeInsets = UIEdgeInsets(top: -(titleFrame.height + space) / 2, left: titleFrame.width / 2, bottom: (titleFrame.height + space) / 2, right: -titleFrame.width / 2) btn.titleEdgeInsets = UIEdgeInsets(top: (imageFrame.height + space) / 2, left: -imageFrame.width / 2, bottom: -(imageFrame.height + space) / 2, right: imageFrame.width / 2)
|
1.4、文字在上,图片在下,间距为10
1 2 3
| let space: CGFloat = 10 btn.imageEdgeInsets = UIEdgeInsets(top: (titleFrame.height + space) / 2, left: titleFrame.width / 2, bottom: -(titleFrame.height + space) / 2, right: -titleFrame.width / 2) btn.titleEdgeInsets = UIEdgeInsets(top: -(imageFrame.height + space) / 2, left: -imageFrame.width / 2, bottom: (imageFrame.height + space) / 2, right: imageFrame.width / 2)
|
方式二:重写系统的方法
titleRect(contentRect: CGRect) 和 imageRect(contentRect: CGRect) 在设置 title 和 image 时分别会被调用;调用 layoutSubviews 时会被调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| class ZMButton: UIButton { enum ImageLayout { case left, right, top, bottom } var space: CGFloat = 0.0 var layoutStyle = ImageLayout.left override func imageRect(forContentRect contentRect: CGRect) -> CGRect { let imageRect = super.imageRect(forContentRect: contentRect) let titleRect = super.titleRect(forContentRect: contentRect) var x: CGFloat = 0, y: CGFloat = 0 switch layoutStyle { case .left: x = imageRect.origin.x y = imageRect.origin.y case .right: x = (contentRect.width - imageRect.width - titleRect.width - space) / 2 + space + titleRect.width y = (contentRect.height - imageRect.height) / 2 case .top: x = (contentRect.width - imageRect.width) / 2 y = (contentRect.height - imageRect.height - titleRect.height - space) / 2 default: x = (contentRect.width - imageRect.width) / 2 y = (contentRect.height - imageRect.height - titleRect.height - space) / 2 + space + titleRect.height } return CGRect(x: x, y: y, width: imageRect.width, height: imageRect.height) } override func titleRect(forContentRect contentRect: CGRect) -> CGRect { let imageRect = super.imageRect(forContentRect: contentRect) let titleRect = super.titleRect(forContentRect: contentRect) var x: CGFloat = 0, y: CGFloat = 0 switch layoutStyle { case .left: x = titleRect.origin.x y = titleRect.origin.y case .right: x = (contentRect.width - imageRect.width - titleRect.width - space) / 2 y = (contentRect.height - titleRect.height) / 2 case .top: x = (contentRect.width - titleRect.width) / 2 y = (contentRect.height - imageRect.height - titleRect.height - space) / 2 + space + imageRect.height default: x = (contentRect.width - titleRect.width) / 2 y = (contentRect.height - imageRect.height - titleRect.height - space) / 2 } return CGRect(x: x, y: y, width: titleRect.width, height: titleRect.height) } }
|
总结:
方式一 修改 image 和 title,必须 image 和 title 都有值,button 的 frame 确定
方式二 直接初始化 button 时设置参数即可,如页面已显示,设置后则要进行 btn.layoutSubviews()
更新
方式一:
1 2 3 4 5 6 7 8 9 10
| @objc func clickGreenBtn(button: UIButton) { button.isEnabled = false DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) { button.isEnabled = true } }
|
方式二:
来自吴品诚的技术博客
1 2 3 4 5 6 7
| func getPtr<T>(_ value: inout T) -> String { let ptr = withUnsafePointer(to: &value) { UnsafeRawPointer($0) } let address = ptr.load(as: UInt.self) let ptr2 = UnsafeRawPointer(bitPattern: address) return ptr2 == nil ? "\(ptr2!)" : "null" } `
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| extension UIButton { private static var REPEATTIMEINTERVAL_KEY: Void? private static var ENABLEDREJECTREPEATTAP_KEY: Void? private static var EVENTDICTIONARY_KEY: Void? var repeatTimeInterval: Double { get { if let value = objc_getAssociatedObject(self, &UIButton.REPEATTIMEINTERVAL_KEY) as? Double { return value } objc_setAssociatedObject(self, &UIButton.REPEATTIMEINTERVAL_KEY, 0.5, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) return 0.5 } set { objc_setAssociatedObject(self, &UIButton.REPEATTIMEINTERVAL_KEY, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } var enabledRejectRepeatTap: Bool { get { if let value = objc_getAssociatedObject(self, &UIButton.ENABLEDREJECTREPEATTAP_KEY) as? Bool { return value } objc_setAssociatedObject(self, &UIButton.ENABLEDREJECTREPEATTAP_KEY, true, .OBJC_ASSOCIATION_ASSIGN) return true } set { objc_setAssociatedObject(self, &UIButton.ENABLEDREJECTREPEATTAP_KEY, newValue, .OBJC_ASSOCIATION_ASSIGN) } } var eventDictionary: Dictionary<String, Double> { get { if let value = objc_getAssociatedObject(self, &UIButton.EVENTDICTIONARY_KEY) as? Dictionary<String, Double> { return value } let defaultValue = Dictionary<String, Double>() objc_setAssociatedObject(self, &UIButton.EVENTDICTIONARY_KEY, defaultValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) return defaultValue } set { objc_setAssociatedObject(self, &UIButton.EVENTDICTIONARY_KEY, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } static let swizzleUIButtonOnce: Void = { let cls = UIButton.self zm_swizzleMethod(cls, #selector(UIButton.sendAction(_:to:for:)), #selector(UIButton.zm_sendAction(_:to:for:))) }() @objc dynamic func zm_sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) { guard let type = event?.type, type == UIEvent.EventType.touches, enabledRejectRepeatTap else { return } var act = action, tar = target let key = getPtr(&act) + getPtr(&tar) let lasttimeTapTime = eventDictionary[key] ?? 0 let currentTime = NSDate.init().timeIntervalSince1970 if lasttimeTapTime > 0.0 { let interval = currentTime - lasttimeTapTime if interval <= self.repeatTimeInterval { return; } } eventDictionary[key] = currentTime zm_sendAction(action, to: target, for: event) } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class ZMButton: UIButton { var stretchInsets: UIEdgeInsets = UIEdgeInsets.zero override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { var actionBounds = bounds actionBounds.origin.x -= stretchInsets.left actionBounds.size.width += stretchInsets.right + stretchInsets.left actionBounds.origin.y -= stretchInsets.top actionBounds.size.height += stretchInsets.bottom + stretchInsets.top print(actionBounds.contains(point), actionBounds, point) return actionBounds.contains(point) } }
|
学习博客:
意林的小站
简书 S型身材的猪
LQRelayoutButton
雪_晟