关于UIButton中edgeInsets的一些小细节

文章迁移自简书 2016.09.04 14:35:01 著swift

# UIButton的相关属性

UIButton提供的设置titleimage的方法,默认是图片在左文字在右的样式.以下图:bash

而咱们在开发过程当中经常要调整为文字在左图片在右,或者文字在上图片在下等样式.ide

系统同时提供了设置图片和文字位置的edgeInsets的属性ui

@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 标记的属性都支持经过外观代理来定制。atom

这样就咱们就能够经过设置 titleEdgeInsetsimageEdgeInsets 来调整图片和文字的相对位置了;spa

# 咱们来看一下 UIEdgeInsets 的解释

  1. 类型代理

    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;
    复制代码
  2. 设置各个值得含义code

    //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 的外部轻松设置想要的样式orm

# 开始实现

咱们如今把相关的设置抽出来作成一个 extension 的方法(一下以文字在左图片在右进行设置)cdn

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*/ 的前面, 始终得不到正确的效果.

# 存疑&解惑

此处存疑: 为何先获取 titleWidthtitleWidth 值始终未 0.0 (能获取到 titleLabel.texttitleLabel.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")
    }

}
复制代码

试试打印的结果

  1. 先获取 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
    复制代码
  2. 先获取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 语句去除

相关文章
相关标签/搜索