文章迁移自简书 2016.09.04 14:35:01 著
# UIButton的相关属性
UIButton提供的设置title和image的方法,默认是图片在左文字在右的样式.如下图:
而我们在开发过程中常常要调整为文字在左图片在右,或者文字在上图片在下等样式.
系统同时提供了设置图片和文字位置的edgeInsets的属性
@property(nonatomic) UIEdgeInsets contentEdgeInsets UI_APPEARANCE_SELECTOR; // default is UIEdgeInsetsZero
@property(nonatomic) UIEdgeInsets titleEdgeInsets; // default is UIEdgeInsetsZero
@property(nonatomic) UIEdgeInsets imageEdgeInsets; // default is UIEdgeInsetsZero
提示:UI_APPEARANCE_SELECTOR 标记的属性都支持通过外观代理来定制。
这样就我们就可以通过设置 titleEdgeInsets 和 imageEdgeInsets 来调整图片和文字的相对位置了;
# 我们来看一下 UIEdgeInsets 的解释
-
类型
typedef struct UIEdgeInsets { CGFloat top, left, bottom, right; // specify amount to inset (positive) for each of the edges. values can be negative to 'outset' } UIEdgeInsets; -
设置各个值得含义
//UIEdgeInsetsMake(<#T##top: CGFloat##CGFloat#>, <#T##left: CGFloat##CGFloat#>, <#T##bottom: CGFloat##CGFloat#>, <#T##right: CGFloat##CGFloat#>) testBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 10, 40, 10); //0 表示据原来的顶部 为0 //10 表示左边框右移 10 (同理 -10 表示左边框左移 10) //40 表示下边框上移 40 //10 表示右边框左移 10 (同理 -10 表示右边框右移 10)总之:这些参数 正值都是表示 向相反的方向移动相应的距离(例如:对
top正值表示向下移动,负值表示向上移动) 这样我们就可以在UIButton的外部轻松设置想要的样式
# 开始实现
我们现在把相关的设置抽出来做成一个 extension 的方法(一下以文字在左图片在右进行设置)
1.新建一个 swift 文件命名为 Button_Extension , 添加代码如下;
import UIKit
extension UIButton {
/// 设置文字在左图片在右,图文间距默认为`0.0`
func setupImageAtRight(space: CGFloat = 0.0) {
/*1*/
DRPrint("获取imageWidth")
guard let imageWidth = imageView?.frame.size.width else {
DRPrint("noImage")
return
}
/*2*/
DRPrint("获取titleWidth")
guard let titleWidth = titleLabel?.frame.size.width else {
DRPrint("noTitle")
return
}
//(备注:`button`不作为`navigationItem.titleView`是默认`titleLabel`和`imageView`距离边界是有一个空隙的)
titleEdgeInsets = UIEdgeInsetsMake(0.0, -(imageWidth + space * 0.5), 0, imageWidth + space * 0.5)
imageEdgeInsets = UIEdgeInsetsMake(0.0, titleWidth + space * 0.5, 0.0, -(space * 0.5 + titleWidth))
//打开注释就能看到普通的`button`默认`titleLabel`和`imageView`距离边界为`0`
//backgroundColor = UIColor.greenColor()
}
}
上述代码为全部操作, 但是在第一次封装中把 /*2*/ 写在了 /*1*/ 的前面, 始终得不到正确的效果.
# 存疑&解惑
此处存疑: 为什么先获取
titleWidth则titleWidth值始终未0.0(能获取到titleLabel.text和titleLabel.font,字符串计算width能正确获得),但是当先获取imageWidth时,则能获取到正确的titleWidth?
经过研究发现:
通过自定义一个
DRButton并重写-layoutSubviews方法发现, 调用imageView?.frame时会先调用-layoutSubviews,才能获取到imageWidth,而titleLabel?.frame不会调用-layoutSubviews,因此先获取titleWidth不会得到正确的值只会得到0.0.
你也可以自己写一下
import UIKit
class TestBtn: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
DRPrint("button.layoutSubiews")
}
}
试试打印的结果
-
先获取
imageWidth的打印结果Button_extension.swift-setupImageAtRight-14:获取imageWidth TestBtn.swift-layoutSubviews()-15:button.layoutSubiews TestBtn.swift-layoutSubviews()-15:button.layoutSubiews Button_extension.swift-setupImageAtRight-19:获取titleWidth TestBtn.swift-layoutSubviews()-15:button.layoutSubiews TestBtn.swift-layoutSubviews()-15:button.layoutSubiews -
先获取
titleWidth的打印结果Button_extension.swift-setupImageAtRight-14:获取titleWidth Button_extension.swift-setupImageAtRight-19:获取imageWidth TestBtn.swift-layoutSubviews()-15:button.layoutSubiews TestBtn.swift-layoutSubviews()-15:button.layoutSubiews TestBtn.swift-layoutSubviews()-15:button.layoutSubiews TestBtn.swift-layoutSubviews()-15:button.layoutSubiews
# 调用
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
addButton()
//addButton("testTitle")
//addButton("testLongLongTitle")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
// MARK:- 设置UI
extension ViewController {
func addButton(title: String = "78987fded") {
view.backgroundColor = UIColor.lightGrayColor()
let button = TestBtn()
button.setTitle(title, forState: .Normal)
button.setImage(UIImage(named: "navigationbar_arrow_down"), forState: .Normal)
button.setImage(UIImage(named: "navigationbar_arrow_up"), forState: .Selected)
//button.setTitleColor(UIColor.grayColor(), forState: .Normal)
button.titleLabel?.font = UIFont.systemFontOfSize(14);
button.sizeToFit()
button.center = view.center
//设置图片和标题的位置
button.setupImageAtRight()
button.backgroundColor = UIColor.greenColor()
view.addSubview(button)
}
}
效果图:
# 最后附上带有注释的全部代码
/// 设置图片在右,图文间隔默认为`0.0`
func setupImageAtRight(space: CGFloat = 0.0) {
//第一种方法:通过计算字符串长度来调整
/*
let titleString = currentTitle! as NSString
let size = CGSizeMake(CGFloat(MAXFLOAT), bounds.size.height)
let atttibutes = [NSFontAttributeName:titleLabel!.font]
let titleWidth = titleString.boundingRectWithSize(size, options: .UsesLineFragmentOrigin, attributes: atttibutes, context: nil).size.width
*/
/*
此处存疑: 为什么先获取`titleWidth`则`titleWidth`值始终未`0.0`(能获取到`titleLabel.text`和`titleLabel.font`,字符串计算`width`能正确获得),但是当先获取`imageWidth`时,则能获取到正确的`titleWidth`🐷
疑惑解答: 通过自定义一个`DRButton`并重写`-layoutSubviews`方法发现,调用`imageView?.frame`时会先调用`-layoutSubviews`,才能获取到`imageWidth`,而`titleLabel?.frame`不会调用`-layoutSubviews`,因此先获取`titleWidth`不会得到正确的值只会得到`0.0`
*/
//通过获取`titleLabel`的`width`来调整
guard let imageWidth = imageView?.frame.size.width else {
return
}
guard let titleWidth = titleLabel?.frame.size.width else {
return
}
/*
DRPrint("titleWidth = \(titleWidth)")
DRPrint("bounds.size = \(bounds.size)")
DRPrint("调整前_ titleLabel.frame = \(titleLabel!.frame)")
DRPrint("调整前_ imageView.frame = \(imageView!.frame)")
DRPrint(titleLabel?.frame.size.width)
*/
/*
testBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 10, 40, 10);
0 表示据原来的顶部 为0
10 表示左边框右移 10 (同理 -10 表示左边框左移 10)
40 表示下边框上移 40
10 表示右边框左移 10 (同理 -10 表示右边框右移 10)
备注 这些参数 正值都是表示 向相反的方向移动相应的距离
*/
titleEdgeInsets = UIEdgeInsetsMake(0.0, -(imageWidth + space * 0.5), 0, imageWidth + space * 0.5)
imageEdgeInsets = UIEdgeInsetsMake(0.0, titleWidth + space * 0.5, 0.0, -(space * 0.5 + titleWidth))
}
// MARK:- 简单打印
func DRPrint<T>(message: T,file: NSString = #file,lineNum: Int = #line,funcName:String = #function) -> Void {
#if DEBUG
let fileName = (file as NSString).lastPathComponent
print("\(fileName)-\(funcName)-\(lineNum):\(message)")
#endif
}
不出打印信息的请把 #if 语句去除