iOS一步步實(shí)現(xiàn)一個(gè)高度自定義UIButton控件

=-=.jpg

需求背景

日常開發(fā)中UIButton的圖片與標(biāo)題默認(rèn)的布局是固定的铝宵,是在水平方向左右排列捉超。但是我們會(huì)經(jīng)常需要更改imagetitle的位置來實(shí)現(xiàn)需求,這是個(gè)很常見的需求就不多說了况芒。所以下面就來談?wù)勅绾我徊讲降膶?shí)現(xiàn)一個(gè)高度自定義的UIButton控件绝骚。

實(shí)現(xiàn)思路

默認(rèn)情況下祠够,在button有固定的寬高值的時(shí)候古瓤,imagetitle是以相對(duì)左右排列落君,整體居中于button來顯示的,如果沒有固定的寬高值皮获,即大小自適應(yīng)的情況下洒宝,整個(gè)UIButton將自動(dòng)縮放到剛好可以容納imagetitle的大小。如圖所示:

自適應(yīng)大小.png

了解UIButton的各個(gè)屬性
在準(zhǔn)備自定義之前宏浩,我們需要了解UIButton的各個(gè)屬性都是怎么運(yùn)用和實(shí)現(xiàn)的绘闷,因?yàn)橐薷?code>title和image的位置與這些屬性是密不可分的印蔗。
因?yàn)檫@些都是基本的開發(fā)知識(shí)华嘹,我就不再過多敘述耙厚,分別以圖片來展示效果:

UIControlContentVerticalAlignment

UIControlContentVerticalAlignment各種效果.png

UIControlContentHorizontalAlignment

UIControlContentHorizontalAlignment各種效果.png

通過上面兩張圖可以清楚地看到UIControlContentVerticalAlignmentUIControlContentHorizontalAlignment在不同值下的效果薛躬,灰色背景的就是一個(gè)button的實(shí)際大小型宝,center就是系統(tǒng)默認(rèn)的值趴酣,明顯的在兩種fill值下岖寞,圖片都出現(xiàn)了拉伸的情況柜蜈,而且在水平fill下淑履,圖片并沒有像垂直情況下水平鋪滿整個(gè)控件,imagetitle還重疊到了一起去岁疼。

UIEdgeInsets

UIButton的另一個(gè)重要的屬性就是這個(gè)了捷绒,稱之為偏移量暖侨,他分別有contentEdgeInsetsimageEdgeInsets京郑,titleEdgeInsets三個(gè)相關(guān)屬性些举。
默認(rèn)情況下:

  • contentEdgeInsetstop户魏、left叼丑、bottom扛门、right都是相對(duì)于button本身论寨,控制著imagetitle整體的偏移量;
  • imageEdgeInsetstopleft沮明、bottom相對(duì)于button窍奋,right相對(duì)于title琳袄,控制著image的相對(duì)偏移量;
  • titleEdgeInsetstop址否、bottom碎紊、right相對(duì)于button樊诺,left相對(duì)于image词爬,控制著title的相對(duì)偏移量权均;

我用一張圖來解釋一下:


偏移量.png

上圖的正負(fù)值就代表偏移方向

我們想要的效果
寫到這里恋沃,我們需要考慮一下我們的需求蛇尚,我們并不是來分析button的實(shí)現(xiàn)原理取劫,而是要實(shí)現(xiàn)一個(gè)自定義的button,自定義imagetitle的相對(duì)位置是最起碼的要求炮捧。相信看到這里大家也知道我們只需要修改imageEdgeInsetstitleEdgeInsets的值就可以隨意布局imagetitle的相對(duì)位置咆课。比如左titleimage书蚪,上imagetitle迅栅。
要實(shí)現(xiàn)這個(gè)需求有兩種方式:

  • 新建一個(gè)UIButton的分類UIButton + xx读存,在layoutSubviews里修改imageEdgeInsetstitleEdgeInsets的值让簿。這種方式可以簡單實(shí)現(xiàn)我們的基本需求,但如果想要添加更多的自定義屬性還需要通過runtime來實(shí)現(xiàn)莲祸,好處就是擁有調(diào)用系統(tǒng)API的舒爽虫给,直接UIButton調(diào)用抹估,沒什么代碼侵入性。
  • 封裝一個(gè)繼承自UIButtonCustomButton瓷式,可以自由添加自定義方法贸典、屬性廊驼,在layoutSubviews里重置imagetitleframe來實(shí)現(xiàn)不同的布局方式惋砂。

其實(shí)這兩種方式都可以實(shí)現(xiàn)自定義的效果西饵,具體選用哪個(gè)就看你自己的需求了,我這里就第二種方式來實(shí)現(xiàn)一下期虾。

上面所說到的contentEdgeInsets镶苞,imageEdgeInsets茂蚓,titleEdgeInsets默認(rèn)值都是zero谢澈,但是我們現(xiàn)在假設(shè)他們都有一個(gè)默認(rèn)值x锥忿;

button簡易圖示.png

這里實(shí)現(xiàn)的思路就是通過self.bounds減去四周的偏移量先獲取整個(gè)content的實(shí)際size敬鬓,再由contentSize減去imagetitle的對(duì)應(yīng)偏移量來獲取imagetitle的實(shí)際size钉答。總之就是先獲取imagetitle的實(shí)際大小仑性,然后根據(jù)不同的布局重置imagetitlex诊杆、y坐標(biāo)和bound晨汹,從而得到對(duì)應(yīng)的frame贷盲,就可以實(shí)現(xiàn)自由布局了巩剖。

上面所說的是button有固定寬高值的情況,如果button的寬高自適應(yīng)氧骤,即調(diào)用sizeToFit方法時(shí)筹陵,我們需要在- (CGSize)sizeThatFits:(CGSize)size內(nèi)針對(duì)不同情況重新計(jì)算出buttonsize朦佩,不然的話庐氮,系統(tǒng)會(huì)根據(jù)imagetitle的大小默認(rèn)返回它們左右排列的size弄砍,此時(shí)的size是錯(cuò)誤的,如圖:

沒做適配的結(jié)果.png

具體的布局分析思路就是這些了慨畸,因?yàn)榇a太多寸士,就不在這里粘貼詳細(xì)代碼了弱卡,如果需要代碼的可以在文章底部找到demo的下載鏈接,demo里面也有詳細(xì)的注釋說明瓮具。

但是搭综,到這里我們只是自定義了imagetitle的相對(duì)布局划栓,我們的目的是自定義整個(gè)UIButton忠荞,所以系統(tǒng)默認(rèn)的點(diǎn)擊效果,CALayer的所有默認(rèn)動(dòng)畫都需要移除掉堂油,替換成我們自定義的layer效果碧绞。比如說系統(tǒng)button的默認(rèn)高亮狀態(tài)下圖片顏色也會(huì)加深讥邻,這個(gè)其實(shí)很惡心,所以我們應(yīng)該移除掉系宜,就像圖下所示:

系統(tǒng)button高亮狀態(tài).png

ok盹牧,現(xiàn)在我們來整理一下需要的常用屬性汰寓,分別為normal苹粟、highlighteddisabled這幾種狀態(tài)下的背景色俺孙,透明度變化睛榄,圖片的tintColor想帅,邊框線的顏色,我們就針對(duì)這幾個(gè)點(diǎn)進(jìn)行修改旨剥。
下面粘貼幾塊代碼段大概展示一下:
highlighted邏輯

- (void)setHighlighted:(BOOL)highlighted {
    [super setHighlighted:highlighted];
    
    if (highlighted && !self.originBorderColor) {
        // 手指按在按鈕上會(huì)不斷觸發(fā)setHighlighted:轨帜,所以這里做了保護(hù)蚌父,設(shè)置過一次就不用再設(shè)置了
        self.originBorderColor = [UIColor colorWithCGColor:self.layer.borderColor];
    }
    
    // 渲染背景色
    if (self.highlightedBackgroundColor || self.highlightedBorderColor) {
        [self adjustsButtonHighlighted];
    }
    // 如果此時(shí)是disabled毛萌,則disabled的樣式優(yōu)先
    if (!self.enabled) {
        return;
    }
    // 自定義highlighted樣式
    if (self.adjustsButtonWhenHighlighted) {
        if (highlighted) {
            self.alpha = 0.5f;
        } else {
            [UIView animateWithDuration:0.25f animations:^{
                self.alpha = 1;
            }];
        }
    }
}

enabled邏輯

- (void)setEnabled:(BOOL)enabled {
    [super setEnabled:enabled];
    if (!enabled && self.adjustsButtonWhenDisabled) {
        self.alpha = 0.5f;
    } else {
        [UIView animateWithDuration:0.25f animations:^{
            self.alpha = 1;
        }];
    }
}

移除系統(tǒng)layer阁将,添加自定義layer

- (void)adjustsButtonHighlighted {
    if (self.highlightedBackgroundColor) {
        if (!self.highlightedBackgroundLayer) {
            self.highlightedBackgroundLayer = [CALayer layer];
            [self.highlightedBackgroundLayer FS_removeDefaultAnimations];
            [self.layer insertSublayer:self.highlightedBackgroundLayer atIndex:0];
        }
        self.highlightedBackgroundLayer.frame = self.bounds;
        self.highlightedBackgroundLayer.cornerRadius = self.layer.cornerRadius;
        self.highlightedBackgroundLayer.backgroundColor = self.highlighted ? self.highlightedBackgroundColor.CGColor : [UIColor colorWithRed:1 green:1 blue:1 alpha:0].CGColor;
    }
    
    if (self.highlightedBorderColor) {
        self.layer.borderColor = self.highlighted ? self.highlightedBorderColor.CGColor : self.originBorderColor.CGColor;
    }
}

因?yàn)樾枰罅康淖远x屬性來代替系統(tǒng)默認(rèn)屬性缤削,雖然我很想在這里解釋每個(gè)屬性的用處吹榴,但是太麻煩了,所以還是建議直接下載demo吨拗,配合代碼看文章劝篷,代碼有詳細(xì)的注釋

這里就直接展示一下demo的效果圖:

FSCustomButtonDemo.gif

以前項(xiàng)目用到的時(shí)候娇妓,我也是直接網(wǎng)上找的一個(gè)庫活鹰,不過那個(gè)庫包含內(nèi)容太多,很多都沒用着绷,所以我將其中的部分代碼抽離了出來直接在項(xiàng)目中運(yùn)用荠医,效果還可以很穩(wěn)定,所以最近抽時(shí)間將代碼從項(xiàng)目中抽離封裝了一下兼贡,寫了一個(gè)demo上傳在github遍希,需要的可以直接前往下載:
FSCustomButtonDemo

文章和demo中涉及到的知識(shí)點(diǎn):
有條線叫“一個(gè)像素”
CALayer
關(guān)于UIButton的UIEdgeInsets屬性

如果對(duì)你有所幫助里烦,就點(diǎn)個(gè)喜歡吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末招驴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子虱饿,更是在濱河造成了極大的恐慌触趴,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爽冕,死亡現(xiàn)場離奇詭異颈畸,居然都是意外死亡眯娱,警方通過查閱死者的電腦和手機(jī)爬凑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門于样,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蚤蔓,你說我怎么就攤上這事携御∽纳玻” “怎么了誓军?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵昵时,是天一觀的道長壹甥。 經(jīng)常有香客問我句柠,道長棒假,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮妻枕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鹰贵。我一直安慰自己碉输,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布枝哄。 她就那樣靜靜地躺著挠锥,像睡著了一般蓖租。 火紅的嫁衣襯著肌膚如雪羊壹。 梳的紋絲不亂的頭發(fā)上油猫,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天哄辣,我揣著相機(jī)與錄音窑滞,去河邊找鬼。 笑死料睛,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的雏蛮。 我是一名探鬼主播挑秉,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼犀概,長吁一口氣:“原來是場噩夢啊……” “哼姻灶!你這毒婦竟也來了产喉?” 一聲冷哼從身側(cè)響起曾沈,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤塞俱,失蹤者是張志新(化名)和其女友劉穎障涯,沒想到半個(gè)月后罐旗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唯蝶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年九秀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片生棍。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡颤霎,死狀恐怖媳谁,靈堂內(nèi)的尸體忽然破棺而出涂滴,到底是詐尸還是另有隱情,我是刑警寧澤晴音,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布柔纵,位于F島的核電站锤躁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏椒振。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望填渠。 院中可真熱鬧横浑,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春茫孔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人妇菱。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像彻舰,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尚胞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容