前言:
寫這篇文章來給大家分享一下我了解的關于UIButton的圖片顯示知識點和我遇到的坑及解決方案,幫助遇到同樣問題和相關知識點不清晰的同學,如有錯誤,歡迎指正,共同進步??。
本文在解析的時候可能較為啰嗦,是為了將思路完整呈現,對于初學者作用可能較大,老手可忽略,直接看結論。
需求:
相信大家都遇到過這種日常需求:一張圖片上面有點擊事件,同時這個控件的長寬比例固定或者干脆是個正方形,然而顯示的圖片則是長寬比例不固定的長方形,并且根據產品的需求,這張圖片必須要覆蓋整個控件,那么這圖片勢必要進行縮放;
還有圖片必須不能變形(都知道,產品和UI的日常需求,變形了確實太丑),也就是說顯示出來的時候原始圖片長寬比例不能變。
分析:
涉及到圖片縮放,那就不得不提UIView
的contentMode
屬性,既然要縮放并且圖片長寬比例不能變,解決方案應該就是放大或縮小圖片,但長寬比例不變,然后將圖片居中顯示,再裁減掉多余的部分,只顯示控件大小的圖片,當然是在clipToBounds
為YES
的情況下。
很顯然,只有UIViewContentModeScaleAspectFill
符合條件,這個枚舉的作用大概是以下幾種情況:
這里為了方便,假設橫向長度為x
,縱向高度為y
。
當目標控件和原始圖片都為正方形時,只需要
xy
同比例縮放即可;-
當原始圖片
xy
比例不為1:1
時,根據目標控件的xy
比來適當的縮放:1)當
原始圖片的x:y
>目標控件的x:y
,縮放原始圖片的y
值等于目標控件的y
值,然后根據原始圖片的xy
比縮放x
,得到的圖片肯定會在x方向大于目標控件,接著橫向居中放置縮放后的原始圖片,再裁掉多余部分。2)當
原始圖片的x:y
<目標控件的x:y
,縮放原始圖片的x
值等于目標控件的x
值,然后根據原始圖片的xy
比縮放y
,得到的圖片肯定會在y
方向大于目標控件,接著縱向居中放置縮放后的原始圖片,再裁掉多余部分。
好了,這個枚舉的作用介紹到這里,很清晰了(其他枚舉的用法相信大家都知道,不知道的自行查資料,相信很容易找到)。
實驗:
知道了這些知識點,要搞定這個需求就很容易了。
我先在這里準備了兩張具有代表性的圖片,模擬容易出錯的情況。
-
第一張,這張圖非常小,是模擬加載比控件小的圖時的情況:55x53.jpeg
-
第二張,這張圖是長方形,正中間有一輪明月,主要模擬長寬比例不一樣時的情況,明月用來便于判斷圖片是否變形:525x350.jpeg
一、UIButton
我們寫一個button
,為了方便,長寬一樣,設定為100
,距離底部100
,左右居中,然后clipToBounds
屬性為YES
,為了便于分析,給個背景顏色,就用亮油油的火紅火紅的綠色,如圖:
開始設置圖片
1) 先放一張長方形的圖,來測試長寬比例不一樣的情況,我這里用的是setImage:forState:
(以下省去forState
):
contentMode
的問題,設置一下UIButton.contentMode
就像個擺設,而要設置button
圖片的contentMode
只有用button.imageView.contentMode
,我們來試試好了,這樣就對了,應該是滿足我們剛才說的
原始圖片x:y
> 目標控件x:y
,于是y
縮放到目標控件的y
值大小,x
等比例縮放,再居中顯示,x
方向兩邊多余的部分就被裁切了,好了,這種情況已經達到產品的需求。
2)剛才的情況測試了長方形且不比目標控件小的情況,另一種情況就比較煩了,也是這次我寫這篇文章遇到的坑點,比目標控件小的圖片,我們放上去試試,代碼不變,contentMode
還是scaleAspectFill
:
??此時我的心情跟效果3中那個小人的心情一模一樣,還記得我們剛才設置的按鈕綠色背景,現在起作用了,不然就只能看到很小的一個小人在中間。
為什么圖片沒有縮放呢,不是設置好了contentMode
嗎?這么坑的嗎?呵呵,就是這么坑,這就是button
的神奇之處,我想可能是因為是設置的是button的圖標的原因吧(就是setImage
這個方法)。
好,那我們試試setBackgoundImage
,呵呵,算了,這樣影響我排版了,先把setImage
說完,因為我知道setBackgroundImage
也沒用,等會兒我們再完整地試試。
emmmm......怎么解決呢?經過查閱資料??,這次我找到了解決方案,原來UIControl
有contentHorizontalAlignment
和contentVerticalAlignment
兩個屬性
fill
了,試下效果:懵逼中......這跟說好的不一樣啊,然后我點擊了一下
button
,繼續懵逼......不過效果總算符合預期了,這里究竟是怎么回事?
經過查閱資料,我猜應該是我在storyboard
里面沒有做設置,默認的是center
,而我寫成fill
是在代碼里寫的,點擊的時候才重新刷新了布局,如果直接在storyboard
里面調整應該不會出現這種情況,不過到這里,應該知道要怎么設置了
果然,小圖也被放大了,并且沒有變形,那就是裁去了多余的部分,總算搞定!至此,
button
設置圖片的所有情況應該都涵蓋了,正方形的情況是自然沒問題的。
setBackgrounImage
好了,剛才我們說到的setBackgroundImage
方法,試一下
這里因為我的小圖準備的是一張近似正方形的圖片,所以我把
button
改成100x200
的長方形(之前一直是200x200
),效果會更明顯,如圖很明顯,
setBackgroundImage
是將圖片直接強行縮放到跟目標控件的大小一樣,且任由圖片變形,不會保持其長寬比例縮放后裁剪,同時各種設置完全沒有作用,也就是沒得商量??,完全不適合本文所提需求。
二、UIImageView
接下來嘗試imageView
,同樣的,創建一個imageView
,距離頂部100
,長寬200
,左右居中,clipToBounds
為YES
完美滿足需求,此時
imageView
只需要再添加一個點擊手勢即可。
結論:
-
UIButton
的setBackgroundImage
方法不適合用此類目標控件與原始圖片寬高比例不一致同時又要求顯示出的圖片不變形的需求,因為這個方法會無腦將原始圖片生拉硬拽成目標控件的大小,即使原始圖片各種變形。 -
UIButton
的contentMode
沒有任何作用,設置了也沒有效果,只有設置UIButton
的imageView
的contentMode
才有用,并且只有是調用的setImage
時才有用。 -
由于
UIButton
繼承自UIControl
,UIControl
有兩個屬性,contentHorizontalAlignment
和contentVerticalAlignment
,這兩個屬性類似contentMode
,是單獨分別針對橫向和豎向的,且默認都為center
,猜測優先級上應該是這兩個屬性大于button
的imageView
的contentMode
。所以如果button
的imageView
的contentMode
和這兩個屬性矛盾,優先遵循UIControl
的兩個屬性,即本文button
用小圖時所遇到的情況。 -
實現此需求的兩個最佳解決方案:
-
用
UIButton
的setImage:forState:
方法,設置UIButton.imageView.contentMode
為UIViewContentModeScaleAspectFill
,同時設置contentHorizontalAlignment
和contentVerticalAlignment
均為fill
。 -
用
UIImageView
,設置contentMode
為UIViewContentModeScaleAspectFill
,同時添加點擊事件。
-