我来这个派对也有点晚了,但我想我有一些有用的补充:o)。
我创建了一个UIButton子类,它的目的是能够选择按钮的图像在哪里布局,无论是垂直还是水平。
这意味着你可以做出这样的按钮:
下面是如何用我的类创建这些按钮的细节:
func makeButton (imageVerticalAlignment:LayoutableButton.VerticalAlignment, imageHorizontalAlignment:LayoutableButton.HorizontalAlignment, title:String) -> LayoutableButton {
let button = LayoutableButton ()
button.imageVerticalAlignment = imageVerticalAlignment
button.imageHorizontalAlignment = imageHorizontalAlignment
button.setTitle(title, for: .normal)
// add image, border, ...
return button
}
let button1 = makeButton(imageVerticalAlignment: .center, imageHorizontalAlignment: .left, title: "button1")
let button2 = makeButton(imageVerticalAlignment: .center, imageHorizontalAlignment: .right, title: "button2")
let button3 = makeButton(imageVerticalAlignment: .top, imageHorizontalAlignment: .center, title: "button3")
let button4 = makeButton(imageVerticalAlignment: .bottom, imageHorizontalAlignment: .center, title: "button4")
let button5 = makeButton(imageVerticalAlignment: .bottom, imageHorizontalAlignment: .center, title: "button5")
button5.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
为此,我添加了2个属性:imageVerticalAlignment和imageHorizontalAlignment。当然,如果你的按钮只有一个图片或标题…不要使用这个类!
我还添加了一个名为imagetotitlespation的属性,它允许你调整标题和图像之间的空间。
如果你想直接或结合新的布局属性使用imageEdgeInsets, titleEdgeInsets和contenttedgeinsets,这个类会尽量兼容。
正如@ravron解释的那样,我尽我最大的努力使按钮内容边缘正确(正如你可以看到的红色边界)。
你也可以在Interface Builder中使用它:
创建一个UIButton
更改按钮类
使用“center”,“top”,“bottom”,“left”或“right”调整可布局属性
以下是代码(要点):
@IBDesignable
class LayoutableButton: UIButton {
enum VerticalAlignment : String {
case center, top, bottom, unset
}
enum HorizontalAlignment : String {
case center, left, right, unset
}
@IBInspectable
var imageToTitleSpacing: CGFloat = 8.0 {
didSet {
setNeedsLayout()
}
}
var imageVerticalAlignment: VerticalAlignment = .unset {
didSet {
setNeedsLayout()
}
}
var imageHorizontalAlignment: HorizontalAlignment = .unset {
didSet {
setNeedsLayout()
}
}
@available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageVerticalAlignment' instead.")
@IBInspectable
var imageVerticalAlignmentName: String {
get {
return imageVerticalAlignment.rawValue
}
set {
if let value = VerticalAlignment(rawValue: newValue) {
imageVerticalAlignment = value
} else {
imageVerticalAlignment = .unset
}
}
}
@available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageHorizontalAlignment' instead.")
@IBInspectable
var imageHorizontalAlignmentName: String {
get {
return imageHorizontalAlignment.rawValue
}
set {
if let value = HorizontalAlignment(rawValue: newValue) {
imageHorizontalAlignment = value
} else {
imageHorizontalAlignment = .unset
}
}
}
var extraContentEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero
override var contentEdgeInsets: UIEdgeInsets {
get {
return super.contentEdgeInsets
}
set {
super.contentEdgeInsets = newValue
self.extraContentEdgeInsets = newValue
}
}
var extraImageEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero
override var imageEdgeInsets: UIEdgeInsets {
get {
return super.imageEdgeInsets
}
set {
super.imageEdgeInsets = newValue
self.extraImageEdgeInsets = newValue
}
}
var extraTitleEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero
override var titleEdgeInsets: UIEdgeInsets {
get {
return super.titleEdgeInsets
}
set {
super.titleEdgeInsets = newValue
self.extraTitleEdgeInsets = newValue
}
}
//Needed to avoid IB crash during autolayout
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.imageEdgeInsets = super.imageEdgeInsets
self.titleEdgeInsets = super.titleEdgeInsets
self.contentEdgeInsets = super.contentEdgeInsets
}
override func layoutSubviews() {
if let imageSize = self.imageView?.image?.size,
let font = self.titleLabel?.font,
let textSize = self.titleLabel?.attributedText?.size() ?? self.titleLabel?.text?.size(attributes: [NSFontAttributeName: font]) {
var _imageEdgeInsets = UIEdgeInsets.zero
var _titleEdgeInsets = UIEdgeInsets.zero
var _contentEdgeInsets = UIEdgeInsets.zero
let halfImageToTitleSpacing = imageToTitleSpacing / 2.0
switch imageVerticalAlignment {
case .bottom:
_imageEdgeInsets.top = (textSize.height + imageToTitleSpacing) / 2.0
_imageEdgeInsets.bottom = (-textSize.height - imageToTitleSpacing) / 2.0
_titleEdgeInsets.top = (-imageSize.height - imageToTitleSpacing) / 2.0
_titleEdgeInsets.bottom = (imageSize.height + imageToTitleSpacing) / 2.0
_contentEdgeInsets.top = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
_contentEdgeInsets.bottom = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
//only works with contentVerticalAlignment = .center
contentVerticalAlignment = .center
case .top:
_imageEdgeInsets.top = (-textSize.height - imageToTitleSpacing) / 2.0
_imageEdgeInsets.bottom = (textSize.height + imageToTitleSpacing) / 2.0
_titleEdgeInsets.top = (imageSize.height + imageToTitleSpacing) / 2.0
_titleEdgeInsets.bottom = (-imageSize.height - imageToTitleSpacing) / 2.0
_contentEdgeInsets.top = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
_contentEdgeInsets.bottom = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
//only works with contentVerticalAlignment = .center
contentVerticalAlignment = .center
case .center:
//only works with contentVerticalAlignment = .center
contentVerticalAlignment = .center
break
case .unset:
break
}
switch imageHorizontalAlignment {
case .left:
_imageEdgeInsets.left = -halfImageToTitleSpacing
_imageEdgeInsets.right = halfImageToTitleSpacing
_titleEdgeInsets.left = halfImageToTitleSpacing
_titleEdgeInsets.right = -halfImageToTitleSpacing
_contentEdgeInsets.left = halfImageToTitleSpacing
_contentEdgeInsets.right = halfImageToTitleSpacing
case .right:
_imageEdgeInsets.left = textSize.width + halfImageToTitleSpacing
_imageEdgeInsets.right = -textSize.width - halfImageToTitleSpacing
_titleEdgeInsets.left = -imageSize.width - halfImageToTitleSpacing
_titleEdgeInsets.right = imageSize.width + halfImageToTitleSpacing
_contentEdgeInsets.left = halfImageToTitleSpacing
_contentEdgeInsets.right = halfImageToTitleSpacing
case .center:
_imageEdgeInsets.left = textSize.width / 2.0
_imageEdgeInsets.right = -textSize.width / 2.0
_titleEdgeInsets.left = -imageSize.width / 2.0
_titleEdgeInsets.right = imageSize.width / 2.0
_contentEdgeInsets.left = -((imageSize.width + textSize.width) - max (imageSize.width, textSize.width)) / 2.0
_contentEdgeInsets.right = -((imageSize.width + textSize.width) - max (imageSize.width, textSize.width)) / 2.0
case .unset:
break
}
_contentEdgeInsets.top += extraContentEdgeInsets.top
_contentEdgeInsets.bottom += extraContentEdgeInsets.bottom
_contentEdgeInsets.left += extraContentEdgeInsets.left
_contentEdgeInsets.right += extraContentEdgeInsets.right
_imageEdgeInsets.top += extraImageEdgeInsets.top
_imageEdgeInsets.bottom += extraImageEdgeInsets.bottom
_imageEdgeInsets.left += extraImageEdgeInsets.left
_imageEdgeInsets.right += extraImageEdgeInsets.right
_titleEdgeInsets.top += extraTitleEdgeInsets.top
_titleEdgeInsets.bottom += extraTitleEdgeInsets.bottom
_titleEdgeInsets.left += extraTitleEdgeInsets.left
_titleEdgeInsets.right += extraTitleEdgeInsets.right
super.imageEdgeInsets = _imageEdgeInsets
super.titleEdgeInsets = _titleEdgeInsets
super.contentEdgeInsets = _contentEdgeInsets
} else {
super.imageEdgeInsets = extraImageEdgeInsets
super.titleEdgeInsets = extraTitleEdgeInsets
super.contentEdgeInsets = extraContentEdgeInsets
}
super.layoutSubviews()
}
}