基于 UIControl 打造一个 Button

首发于公众号

UIKit 提供的 UIButton 在普通场景下已经很好用了,但是也有缺点,就是定制能力不强,开发者往往需要子类化定制自己的风格才能满足 UI 的需求。

UI 对于 Button 有以下几个需求:

  1. 左边图片,右边文字
  2. 左边文字,右边图片
  3. 上面图片,下面文字
  4. 上面文字,下面图片

不仅如此,还要求图片和文字之间还要求有一定的间隔。

系统的 UIButton 默认情况下只能支持第 1 种场景,其他情况都需要自己定制化,然而基于 UIButton 定制也有一些麻烦要处理,比如在自动布局机制下能自适应大小,而且对于上面列举的场景来说,仅仅只有显示风格不一样而已,UIButton 要是有一个接口直接设置风格就方便多了。

基于以上分析,我决定基于 UIControl 来重新实现一个 Button,基本使用接口保持和 UIButton 一致,而且支持定制布局风格,只需要简单设置一个参数就行了。

图片和文字的排列组合一共有 4 种,两种横行排列,两种竖向排列,通过一个枚举类型定义如下:

public enum SFButtonDirection: Int {
    case row
    case rowReverse
    case column
    case columnReverse
}

横向的排列叫 row,竖向的排列叫 column,分别对应之前提到的 4 种场景:

  1. 左边图片,右边文字 (row)
  2. 左边文字,右边图片 (rowReverse)
  3. 上面图片,下面文字 (column)
  4. 上面文字,下面图片 (columnReverse)

图片和文字之间还要能设置间距,再增加一个属性:space。

有时候 UI 要求 Button 的大小需要由内容来决定,而且上下左右还要有一定的间距,满足这个场景只需要再增加一个属性:contentInset。

有了以上 3 个关键属性,就可以满足大多数的应用场景了,而且使用起来也非常简单:
创建一个 Button

func makeButton() -> SFButton {
    let btnNormalBGImage = UIImage(named: "button_normal")!.resizableImage(withCapInsets: UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15))
    let btnHighlightedBGImage = UIImage(named: "button_highlighted")!.resizableImage(withCapInsets: UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15))

    let button = SFButton(frame: .zero)
    button.setImage(UIImage(named: "icon-test"), for: .normal)
    button.setBackgroundImage(btnNormalBGImage, for: .normal)
    button.setBackgroundImage(btnHighlightedBGImage, for: .highlighted)

    return button
}
  • 图片左,文字右
let button = makeButton()
button.space = 10
button.contentInset = UIEdgeInsets(top: 10, left: 5, bottom: 10, right: 5)
button.setTitle("direction: row", for: .normal)
  • 文字左,图片右
let button2 = makeButton()
button2.space = 10
button2.contentInset = UIEdgeInsets(top: 10, left: 5, bottom: 10, right: 5)
button2.direction = .rowReverse
button2.setTitle("direction: rowReverse", for: .normal)
  • 图片上,文字下
let button3 = makeButton()
button3.space = 5
button3.contentInset = UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)
button3.direction = .column
button3.setTitle("direction: column", for: .normal)
  • 文字上,图片下
let button4 = makeButton()
button4.space = 5
button4.contentInset = UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)
button4.direction = .columnReverse
button4.setTitle("direction: columnReverse", for: .normal)

以下是运行效果,完整实现代码请到我的 github:
SFButton

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342