iOS button的imageEdgeInsets和titleEdgeInsets原理

demo地址: SPButton

前言

最近我竟花了幾天的時間去深入研究button哈误,研究的過程當中隐轩,被imageEdgeInsetstitleEdgeInsets兩個屬性困惑甚久的烁,我為此徹夜不眠,網(wǎng)上也查閱各種資料辩尊,可以說只锻,對于這兩個屬性的解釋,網(wǎng)上的答案滿天飛钳垮,但是惑淳,沒有一個人真正說出了它們的原理。

重要關(guān)聯(lián)屬性contentHorizontalAlignment和contentVerticalAlignment

這是兩個枚舉饺窿,即整個內(nèi)容的水平對齊方式和垂直對齊方式

typedef NS_ENUM(NSInteger, UIControlContentHorizontalAlignment) {
    UIControlContentHorizontalAlignmentCenter = 0,
    UIControlContentHorizontalAlignmentLeft   = 1,
    UIControlContentHorizontalAlignmentRight  = 2,
    UIControlContentHorizontalAlignmentFill   = 3,
    UIControlContentHorizontalAlignmentLeading  API_AVAILABLE(ios(11.0), tvos(11.0)) = 4,
    UIControlContentHorizontalAlignmentTrailing API_AVAILABLE(ios(11.0), tvos(11.0)) = 5,
};

typedef NS_ENUM(NSInteger, UIControlContentVerticalAlignment) {
    UIControlContentVerticalAlignmentCenter  = 0,
    UIControlContentVerticalAlignmentTop     = 1,
    UIControlContentVerticalAlignmentBottom  = 2,
    UIControlContentVerticalAlignmentFill    = 3,
};
// 默認:
 button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
 button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;

其中UIControlContentHorizontalAlignmentLeading和UIControlContentHorizontalAlignmentTrailing為iOS11新增歧焦,在我們大中華地區(qū),Leading就是Left肚医,Trailing就是Right 绢馍,對于部分國家,他們的語言是從右往左寫肠套,這時Leading就是Right舰涌,Trailing就Left

正文

創(chuàng)建一個按鈕,設(shè)置文字和圖片你稚,按鈕的內(nèi)容默認排布如圖:為了便于理解瓷耙,我給的titleLabel和imageView是等寬的


9EC15FFC4CC9871442AD43C376C72DF8.jpg

截圖中:

  1. 黑色邊框為按鈕矩形區(qū)域,其bounds為:(0刁赖,0搁痛,200,100)宇弛,為了便于研究鸡典,contentEdgeInset默認UIEdgeInsetsZero,即按鈕的內(nèi)容區(qū)域就是按鈕的bounds枪芒;
  2. imageView的frame為(50彻况,25谁尸,50,50)纽甘;
  3. titleLabel的frame為(100良蛮,37.5,50悍赢,50)背镇;

現(xiàn)在,我設(shè)置

button.imageEdgeInsets = UIEdgeInsetsMake(0泽裳,50, 0破婆,0);

經(jīng)過上面的設(shè)置后涮总,請大家猜想一下,圖片的位置會在什么地方祷舀?
思考 1s瀑梗、2s、3s裳扯、.......
大家心中差不多有想法了抛丽,圖片的原x值為50,現(xiàn)在設(shè)置UIEdgeInsetsMake(0饰豺,50亿鲜, 0,0)冤吨,相當于整個圖片向右平移50蒿柳,那么現(xiàn)在圖片的x值應該為100,大家想象的結(jié)果是不是這樣的漩蟆,如圖:

2F4D2190781A35ADAC9188C5FC48F8CD.jpg

我要告訴大家垒探,上面的結(jié)果是錯的,正確的結(jié)果如圖:
179A2A60E6C0637971FEB28BF5E1F50D.jpg

實際上怠李,圖片只向右平移了50的一半圾叼,即25,這是為什么捺癞?

網(wǎng)上錯誤結(jié)論:

對于imageView:其imageEdgeInsets的top夷蚊,left,bottom是相對button的contentRect而言翘簇,right是相對titleLabel而言撬码;
對于titleLabel:其titleEdgeInsets的top,right版保,bottom是相對button的contentRect而言呜笑,left是相對imageView而言夫否。

正確結(jié)論

imageEdgeInsetstitleEdgeInsets的top,left叫胁,right凰慈, bottom都是相對button的contentRect而言,當contentEdgeInsets為UIEdgeInsetsZero時驼鹅,button微谓、imageView、titleLabel的安全區(qū)域均為button的bounds输钩。

根據(jù)這個正確結(jié)論瞧挤,當設(shè)置了button.imageEdgeInsets = UIEdgeInsetsMake(0沙热,50, 0,0)時衡奥,那么imageView的安全區(qū)域就是如下圖中的紅色區(qū)域

669CA397468CDFA3318832E6E46F654D.jpg

圖片的區(qū)域我們知道了你弦,根據(jù)水平排列方式默認為UIControlContentHorizontalAlignmentCenter待牵,圖片應當在紅色區(qū)域的中間位置吃型,然而,我們要深刻明白:

重要的話說3遍

  • UIControlContentHorizontalAlignmentCenter的指的是內(nèi)容(圖片+文字)整體居中
  • UIControlContentHorizontalAlignmentCenter的指的是內(nèi)容(圖片+文字)整體居中
  • UIControlContentHorizontalAlignmentCenter的指的是內(nèi)容(圖片+文字)整體居中
    其余枚舉值同理

因此功戚,盡管titleLabel沒有設(shè)置titleEdgeInsets娶眷,但是我們在對imageView進行某種對齊時,不應該只考慮imageView啸臀,應該將imageView+titleLabel這個整體作為考慮對象; 如圖
F2C3BD086D79D226158CE915C5349A99.jpg

核心解釋

上圖中届宠,imageView和藍色的titleLabel作為一個整體,在紅色區(qū)域內(nèi)居中了壳咕,綠色的titleLabel只參與計算席揽,由于我們沒有設(shè)置titleLabel的titleEdgeInsets,所以最終titleLabel的位置依然保持不變谓厘。藍色的titleLabel實際上是虛擬的幌羞,我只是告訴大家,系統(tǒng)進行對齊方式計算時竟稳,永遠是把imageView+titleLabel這個整體作為計算對象属桦,我們來計算一下,圖片向右偏移25是怎么來的:
①紅色區(qū)域的寬度為:200 - 50 = 150他爸;
②圖片+藍色label的總寬度:50 + 50 = 100聂宾;
③圖片的x值:(① - ②) / 2.0 =(150 - 100)/ 2.0 = 25;(除以2是因為居中對齊诊笤,如果是其余對齊就不用除以2)

我不知道我上面的表達夠不夠清楚系谐,如果不清楚,那么我們來一次強化訓練

強化訓練

我們不再按照水平中心對齊,我們來一次左對齊

button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;

設(shè)置后如圖


280C608ED3BFFE70D6865E80E99AAB6D.jpg

再設(shè)置

 button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 50);

大家想想經(jīng)過上面那行代碼之后纪他,結(jié)果是什么呢鄙煤?圖片會向左偏移50的距離嗎?如果按照網(wǎng)上的結(jié)論茶袒,圖片的right是相對titleLabel而言梯刚,那么設(shè)置right為50圖片必會向左偏移50。我要告訴大家薪寓,上面那行代碼設(shè)置之后亡资,不會產(chǎn)生任何變化,為什么向叉?

原因很簡單:上面那行代碼的意思是锥腻,圖片的安全區(qū)域為:在contentRect的基礎(chǔ)上,原區(qū)域右邊往左內(nèi)縮50距離母谎,即下圖中的紅色區(qū)域:
F3B155F5C3B22687C4C5AB809E993FC5.jpg

在這個紅色區(qū)域當中旷太,將imageView+(虛擬)titleLabel這個整體進行左對齊,大家明顯能看到销睁,現(xiàn)在就是左對齊的,所以設(shè)置right為50是不會有任何變化的存崖,那么如果我們修改一下冻记,設(shè)置
 button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 175);

上面那行代碼的意思是,圖片的安全區(qū)域為:在contentRect的基礎(chǔ)上来惧,原區(qū)域右邊往左內(nèi)縮175距離冗栗,即下圖中的紅色區(qū)域:
F6C4759589AC0ECDC7D607881F3B5A6E.jpg

在這個紅色區(qū)域內(nèi),要把imageView+(虛擬)titleLabel這個整體進行左對齊供搀,但是我們發(fā)現(xiàn)隅居,紅色區(qū)域的寬度容不下imageView+titleLabel這個整體,這個時候葛虐,系統(tǒng)先會把titleLabel的寬度壓縮胎源,如果壓縮為0之后,發(fā)現(xiàn)連imageView都容不下屿脐,那么繼續(xù)壓縮imageView涕蚤,直到寬度降為紅色區(qū)域?qū)挒橹梗瑃itleLabel保持不動的诵, 最終顯示結(jié)果如圖
F3B3E996C3F16FB079910B6F7E635DFB.jpg

再次訓練

保持默認設(shè)置

button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;

再設(shè)置

button.imageEdgeInsets = UIEdgeInsetsMake(50, 0, 0, 0);

*上面那行代碼的意思是万栅,圖片的安全區(qū)域為:在contentRect的基礎(chǔ)上,原區(qū)域頂部向下內(nèi)縮50距離西疤,即下圖中的紅色區(qū)域:
F0893D4F8F8B780241E97EF8C9C8F541.jpg

在這個紅色區(qū)域當中烦粒,要依然保證imageView+(虛擬)titleLabel這個整體進行垂直居中, 因此最終結(jié)果如圖:
2102CCCB35A62B6562F0E927EB97BAA7.jpg

從這里我們可以萌生一個思想

imageEdgeInsetstitleEdgeInsets不要去理解為將imageView和titleLabel進行平移代赁,應該理解為將imageView和titleLabel的安全區(qū)域的各邊進行偏移扰她,偏移完成后兽掰,再聯(lián)合contentHorizontalAlignmentcontentVerticalAlignment屬性進行整體對齊

我所知道的秘密

我想大家在實現(xiàn)按鈕圖片位置在上、下义黎、左禾进、右的需求時,有不少人是通過重寫按鈕的imageRectForContentRect:titleRectForContentRect:的廉涕,我個人也很推薦這種做法泻云,重寫layoutSubviews也可以,但我并不推薦狐蜕,可以說重寫layoutSubviews可以實現(xiàn)你的需求宠纯,但是嚴重破壞了系統(tǒng)按鈕,因為层释,系統(tǒng)按鈕在layoutSubviews里面婆瓜,當存在文字或者圖片時,會先調(diào)用imageRectForContentRect:titleRectForContentRect:這2個方法計算出imageRect和titleRect贡羔,然后將計算結(jié)果應用在imageView和titleLabel上廉白,所以,如果你重寫layoutSubviews乖寒,先super , 然后進行一系列自己的布局猴蹂,這就會導致你使用button時,通過imageRectForContentRect:titleRectForContentRect:這2個方法獲取到的rect并非你在layoutSubviews里計算的結(jié)果楣嘁,仍然是系統(tǒng)計算的結(jié)果磅轻,這就是破壞了原始按鈕的方法

  • imageRectForContentRect:titleRectForContentRect:的調(diào)用時機:
  1. 在第一次調(diào)用titleLabel和imageView的getter方法(懶加載)時,alloc init之前會調(diào)用一次(無論有無圖片文字都會直接調(diào)),因此逐虚,在重寫這2個方法時聋溜,在方法里面不要使用self.imageView和self.titleLabel,因為這2個控件是懶加載叭爱,如果在重寫的這2個方法里是第一調(diào)用imageView和titleLabel的getter方法, 則會造成死循環(huán)
  2. 在layoutsSubviews中如果文字或圖片不為空時會調(diào)用, 測試方式:在重寫的這兩個方法里調(diào)用setNeedsLayout(layutSubviews)撮躁,發(fā)現(xiàn)會造成死循環(huán)
  3. 按鈕的frame發(fā)生改變,設(shè)置文字圖片买雾、改動文字和圖片馒胆、設(shè)置對齊方式,設(shè)置內(nèi)容區(qū)域等時會調(diào)用凝果,其實這些祝迂,系統(tǒng)是調(diào)用了layoutSubviews從而間接的去調(diào)用imageRectForContentRect:titleRectForContentRect:
    ......

建議

大家在實現(xiàn)按鈕的圖片在上、左器净、下型雳、右的時候,最好要注意不要去破壞系統(tǒng)按鈕,什么叫破壞呢纠俭?比如你實現(xiàn)完之后沿量,要保證按鈕的所有自帶屬性和方法依然生效,再比如:UIButton中的titleLabel和imageView是懶加載的冤荆,我們不要在實現(xiàn)自己需求的過程中去提前加載朴则,這不符合按鈕的規(guī)則

demo地址: SPButton

demo效果圖

F728B222E090608891172DB207F7EF45.jpg

測試gif圖

演示圖.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市钓简,隨后出現(xiàn)的幾起案子乌妒,更是在濱河造成了極大的恐慌,老刑警劉巖外邓,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撤蚊,死亡現(xiàn)場離奇詭異,居然都是意外死亡损话,警方通過查閱死者的電腦和手機侦啸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丧枪,“玉大人光涂,你說我怎么就攤上這事∨》常” “怎么了顶捷?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長屎篱。 經(jīng)常有香客問我,道長葵蒂,這世上最難降的妖魔是什么交播? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮践付,結(jié)果婚禮上秦士,老公的妹妹穿的比我還像新娘。我一直安慰自己永高,他們只是感情好隧土,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著命爬,像睡著了一般曹傀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饲宛,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天皆愉,我揣著相機與錄音,去河邊找鬼。 笑死幕庐,一個胖子當著我的面吹牛久锥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播异剥,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼瑟由,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了冤寿?” 一聲冷哼從身側(cè)響起歹苦,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疚沐,沒想到半個月后暂氯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡亮蛔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年痴施,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片究流。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡辣吃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芬探,到底是詐尸還是另有隱情神得,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布偷仿,位于F島的核電站哩簿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏酝静。R本人自食惡果不足惜节榜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望别智。 院中可真熱鬧宗苍,春花似錦、人聲如沸薄榛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敞恋。三九已至丽啡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間硬猫,已是汗流浹背碌上。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工倚评, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人馏予。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓天梧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親霞丧。 傳聞我的和親對象是個殘疾皇子呢岗,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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