前言:
寫這篇文章來(lái)給大家分享一下我了解的關(guān)于UIButton的圖片顯示知識(shí)點(diǎn)和我遇到的坑及解決方案,幫助遇到同樣問(wèn)題和相關(guān)知識(shí)點(diǎn)不清晰的同學(xué),如有錯(cuò)誤,歡迎指正,共同進(jìn)步??。
本文在解析的時(shí)候可能較為啰嗦,是為了將思路完整呈現(xiàn),對(duì)于初學(xué)者作用可能較大,老手可忽略,直接看結(jié)論。
需求:
相信大家都遇到過(guò)這種日常需求:一張圖片上面有點(diǎn)擊事件,同時(shí)這個(gè)控件的長(zhǎng)寬比例固定或者干脆是個(gè)正方形,然而顯示的圖片則是長(zhǎng)寬比例不固定的長(zhǎng)方形,并且根據(jù)產(chǎn)品的需求,這張圖片必須要覆蓋整個(gè)控件,那么這圖片勢(shì)必要進(jìn)行縮放;
還有圖片必須不能變形(都知道,產(chǎn)品和UI的日常需求,變形了確實(shí)太丑),也就是說(shuō)顯示出來(lái)的時(shí)候原始圖片長(zhǎng)寬比例不能變。
分析:
涉及到圖片縮放,那就不得不提UIView的contentMode屬性,既然要縮放并且圖片長(zhǎng)寬比例不能變,解決方案應(yīng)該就是放大或縮小圖片,但長(zhǎng)寬比例不變,然后將圖片居中顯示,再裁減掉多余的部分,只顯示控件大小的圖片,當(dāng)然是在clipToBounds為YES的情況下。
很顯然,只有UIViewContentModeScaleAspectFill符合條件,這個(gè)枚舉的作用大概是以下幾種情況:
這里為了方便,假設(shè)橫向長(zhǎng)度為x,縱向高度為y。
當(dāng)目標(biāo)控件和原始圖片都為正方形時(shí),只需要
xy同比例縮放即可;-
當(dāng)原始圖片
xy比例不為1:1時(shí),根據(jù)目標(biāo)控件的xy比來(lái)適當(dāng)?shù)目s放:1)當(dāng)
原始圖片的x:y>目標(biāo)控件的x:y,縮放原始圖片的y值等于目標(biāo)控件的y值,然后根據(jù)原始圖片的xy比縮放x,得到的圖片肯定會(huì)在x方向大于目標(biāo)控件,接著橫向居中放置縮放后的原始圖片,再裁掉多余部分。2)當(dāng)
原始圖片的x:y<目標(biāo)控件的x:y,縮放原始圖片的x值等于目標(biāo)控件的x值,然后根據(jù)原始圖片的xy比縮放y,得到的圖片肯定會(huì)在y方向大于目標(biāo)控件,接著縱向居中放置縮放后的原始圖片,再裁掉多余部分。
好了,這個(gè)枚舉的作用介紹到這里,很清晰了(其他枚舉的用法相信大家都知道,不知道的自行查資料,相信很容易找到)。
實(shí)驗(yàn):
知道了這些知識(shí)點(diǎn),要搞定這個(gè)需求就很容易了。
我先在這里準(zhǔn)備了兩張具有代表性的圖片,模擬容易出錯(cuò)的情況。
-
第一張,這張圖非常小,是模擬加載比控件小的圖時(shí)的情況:55x53.jpeg
-
第二張,這張圖是長(zhǎng)方形,正中間有一輪明月,主要模擬長(zhǎng)寬比例不一樣時(shí)的情況,明月用來(lái)便于判斷圖片是否變形:525x350.jpeg
一、UIButton
我們寫一個(gè)button,為了方便,長(zhǎng)寬一樣,設(shè)定為100,距離底部100,左右居中,然后clipToBounds屬性為YES,為了便于分析,給個(gè)背景顏色,就用亮油油的火紅火紅的綠色,如圖:

開(kāi)始設(shè)置圖片
1) 先放一張長(zhǎng)方形的圖,來(lái)測(cè)試長(zhǎng)寬比例不一樣的情況,我這里用的是setImage:forState:(以下省去forState):


UIButton.contentMode就像個(gè)擺設(shè),而要設(shè)置button圖片的contentMode只有用button.imageView.contentMode,我們來(lái)試試

好了,這樣就對(duì)了,應(yīng)該是滿足我們剛才說(shuō)的
原始圖片x:y > 目標(biāo)控件x:y,于是y縮放到目標(biāo)控件的y值大小,x等比例縮放,再居中顯示,x方向兩邊多余的部分就被裁切了,好了,這種情況已經(jīng)達(dá)到產(chǎn)品的需求。
2)剛才的情況測(cè)試了長(zhǎng)方形且不比目標(biāo)控件小的情況,另一種情況就比較煩了,也是這次我寫這篇文章遇到的坑點(diǎn),比目標(biāo)控件小的圖片,我們放上去試試,代碼不變,contentMode還是scaleAspectFill:

??此時(shí)我的心情跟效果3中那個(gè)小人的心情一模一樣,還記得我們剛才設(shè)置的按鈕綠色背景,現(xiàn)在起作用了,不然就只能看到很小的一個(gè)小人在中間。
為什么圖片沒(méi)有縮放呢,不是設(shè)置好了contentMode嗎?這么坑的嗎?呵呵,就是這么坑,這就是button的神奇之處,我想可能是因?yàn)槭窃O(shè)置的是button的圖標(biāo)的原因吧(就是setImage這個(gè)方法)。
好,那我們?cè)囋?code>setBackgoundImage,呵呵,算了,這樣影響我排版了,先把setImage說(shuō)完,因?yàn)槲抑?code>setBackgroundImage也沒(méi)用,等會(huì)兒我們?cè)偻暾卦囋嚒?/p>
emmmm......怎么解決呢?經(jīng)過(guò)查閱資料??,這次我找到了解決方案,原來(lái)UIControl有contentHorizontalAlignment和contentVerticalAlignment兩個(gè)屬性


fill了,試下效果:



懵逼中......這跟說(shuō)好的不一樣啊,然后我點(diǎn)擊了一下
button,
繼續(xù)懵逼......不過(guò)效果總算符合預(yù)期了,這里究竟是怎么回事?
經(jīng)過(guò)查閱資料,我猜應(yīng)該是我在storyboard里面沒(méi)有做設(shè)置,默認(rèn)的是center,而我寫成fill是在代碼里寫的,點(diǎn)擊的時(shí)候才重新刷新了布局,如果直接在storyboard里面調(diào)整應(yīng)該不會(huì)出現(xiàn)這種情況,不過(guò)到這里,應(yīng)該知道要怎么設(shè)置了



果然,小圖也被放大了,并且沒(méi)有變形,那就是裁去了多余的部分,總算搞定!至此,
button設(shè)置圖片的所有情況應(yīng)該都涵蓋了,正方形的情況是自然沒(méi)問(wèn)題的。
setBackgrounImage
好了,剛才我們說(shuō)到的setBackgroundImage方法,試一下

這里因?yàn)槲业男D準(zhǔn)備的是一張近似正方形的圖片,所以我把
button改成100x200的長(zhǎng)方形(之前一直是200x200),效果會(huì)更明顯,如圖

很明顯,
setBackgroundImage是將圖片直接強(qiáng)行縮放到跟目標(biāo)控件的大小一樣,且任由圖片變形,不會(huì)保持其長(zhǎng)寬比例縮放后裁剪,同時(shí)各種設(shè)置完全沒(méi)有作用,也就是沒(méi)得商量??,完全不適合本文所提需求。
二、UIImageView
接下來(lái)嘗試imageView,同樣的,創(chuàng)建一個(gè)imageView,距離頂部100,長(zhǎng)寬200,左右居中,clipToBounds為YES



完美滿足需求,此時(shí)
imageView只需要再添加一個(gè)點(diǎn)擊手勢(shì)即可。
結(jié)論:
-
UIButton的setBackgroundImage方法不適合用此類目標(biāo)控件與原始圖片寬高比例不一致同時(shí)又要求顯示出的圖片不變形的需求,因?yàn)檫@個(gè)方法會(huì)無(wú)腦將原始圖片生拉硬拽成目標(biāo)控件的大小,即使原始圖片各種變形。 -
UIButton的contentMode沒(méi)有任何作用,設(shè)置了也沒(méi)有效果,只有設(shè)置UIButton的imageView的contentMode才有用,并且只有是調(diào)用的setImage時(shí)才有用。 -
由于
UIButton繼承自UIControl,UIControl有兩個(gè)屬性,contentHorizontalAlignment和contentVerticalAlignment,這兩個(gè)屬性類似contentMode,是單獨(dú)分別針對(duì)橫向和豎向的,且默認(rèn)都為center,猜測(cè)優(yōu)先級(jí)上應(yīng)該是這兩個(gè)屬性大于button的imageView的contentMode。所以如果button的imageView的contentMode和這兩個(gè)屬性矛盾,優(yōu)先遵循UIControl的兩個(gè)屬性,即本文button用小圖時(shí)所遇到的情況。 -
實(shí)現(xiàn)此需求的兩個(gè)最佳解決方案:
-
用
UIButton的setImage:forState:方法,設(shè)置UIButton.imageView.contentMode為UIViewContentModeScaleAspectFill,同時(shí)設(shè)置contentHorizontalAlignment和contentVerticalAlignment均為fill。 -
用
UIImageView,設(shè)置contentMode為UIViewContentModeScaleAspectFill,同時(shí)添加點(diǎn)擊事件。
-

