iOS開發之UIButton的圖片顯示解惑

前言:

寫這篇文章來給大家分享一下我了解的關于UIButton的圖片顯示知識點和我遇到的坑及解決方案,幫助遇到同樣問題和相關知識點不清晰的同學,如有錯誤,歡迎指正,共同進步??。
本文在解析的時候可能較為啰嗦,是為了將思路完整呈現,對于初學者作用可能較大,老手可忽略,直接看結論。

需求:

相信大家都遇到過這種日常需求:一張圖片上面有點擊事件,同時這個控件的長寬比例固定或者干脆是個正方形,然而顯示的圖片則是長寬比例不固定的長方形,并且根據產品的需求,這張圖片必須要覆蓋整個控件,那么這圖片勢必要進行縮放;
還有圖片必須不能變形(都知道,產品和UI的日常需求,變形了確實太丑),也就是說顯示出來的時候原始圖片長寬比例不能變。

分析:

涉及到圖片縮放,那就不得不提UIViewcontentMode屬性,既然要縮放并且圖片長寬比例不能變,解決方案應該就是放大或縮小圖片,但長寬比例不變,然后將圖片居中顯示,再裁減掉多余的部分,只顯示控件大小的圖片,當然是在clipToBoundsYES的情況下。

很顯然,只有UIViewContentModeScaleAspectFill符合條件,這個枚舉的作用大概是以下幾種情況:

這里為了方便,假設橫向長度為x,縱向高度為y

  1. 當目標控件和原始圖片都為正方形時,只需要xy同比例縮放即可;

  2. 當原始圖片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,為了便于分析,給個背景顏色,就用亮油油的火紅火紅的綠色,如圖:

綠色的Button

開始設置圖片

1) 先放一張長方形的圖,來測試長寬比例不一樣的情況,我這里用的是setImage:forState:(以下省去forState):

效果1
可以看到月亮已經扁了,明顯變形,哦,知道,是因為contentMode的問題,設置一下
代碼1
但是沒有任何作用,就不上圖了,運行后跟圖效果1一模一樣,怎么回事呢,經過查閱資料,我還是不知道??,總之就是UIButton.contentMode就像個擺設,而要設置button圖片的contentMode只有用button.imageView.contentMode,我們來試試
代碼2
效果2

好了,這樣就對了,應該是滿足我們剛才說的原始圖片x:y > 目標控件x:y,于是y縮放到目標控件的y值大小,x等比例縮放,再居中顯示,x方向兩邊多余的部分就被裁切了,好了,這種情況已經達到產品的需求。

2)剛才的情況測試了長方形且不比目標控件小的情況,另一種情況就比較煩了,也是這次我寫這篇文章遇到的坑點,比目標控件小的圖片,我們放上去試試,代碼不變,contentMode還是scaleAspectFill:

效果3

??此時我的心情跟效果3中那個小人的心情一模一樣,還記得我們剛才設置的按鈕綠色背景,現在起作用了,不然就只能看到很小的一個小人在中間。

為什么圖片沒有縮放呢,不是設置好了contentMode嗎?這么坑的嗎?呵呵,就是這么坑,這就是button的神奇之處,我想可能是因為是設置的是button的圖標的原因吧(就是setImage這個方法)。

好,那我們試試setBackgoundImage,呵呵,算了,這樣影響我排版了,先把setImage說完,因為我知道setBackgroundImage也沒用,等會兒我們再完整地試試。

emmmm......怎么解決呢?經過查閱資料??,這次我找到了解決方案,原來UIControlcontentHorizontalAlignmentcontentVerticalAlignment兩個屬性

系統Api中ContentHorizontalAlignment
系統Api中ContentVerticalAlignment
我們這里顯而易見是用fill了,試下效果:
代碼4(就是沒有代碼3,要一一對應 ??,下同)
效果4
代碼5

效果5(1)

懵逼中......這跟說好的不一樣啊,然后我點擊了一下button,
效果5(2)

繼續懵逼......不過效果總算符合預期了,這里究竟是怎么回事?

經過查閱資料,我猜應該是我在storyboard里面沒有做設置,默認的是center,而我寫成fill是在代碼里寫的,點擊的時候才重新刷新了布局,如果直接在storyboard里面調整應該不會出現這種情況,不過到這里,應該知道要怎么設置了

storyboard中,如果用storyboard的可以直接選右邊紅框框出的選項

代碼6

效果6

果然,小圖也被放大了,并且沒有變形,那就是裁去了多余的部分,總算搞定!至此,button設置圖片的所有情況應該都涵蓋了,正方形的情況是自然沒問題的。

setBackgrounImage

好了,剛才我們說到的setBackgroundImage方法,試一下

代碼7

這里因為我的小圖準備的是一張近似正方形的圖片,所以我把button改成100x200的長方形(之前一直是200x200),效果會更明顯,如圖
效果7(1)
效果7(2)

很明顯,setBackgroundImage是將圖片直接強行縮放到跟目標控件的大小一樣,且任由圖片變形,不會保持其長寬比例縮放后裁剪,同時各種設置完全沒有作用,也就是沒得商量??,完全不適合本文所提需求。

二、UIImageView

接下來嘗試imageView,同樣的,創建一個imageView,距離頂部100,長寬200,左右居中,clipToBoundsYES

代碼
放長方形圖
放小圖

完美滿足需求,此時imageView只需要再添加一個點擊手勢即可。

結論:

  1. UIButtonsetBackgroundImage方法不適合用此類目標控件與原始圖片寬高比例不一致同時又要求顯示出的圖片不變形的需求,因為這個方法會無腦將原始圖片生拉硬拽成目標控件的大小,即使原始圖片各種變形。
  2. UIButtoncontentMode沒有任何作用,設置了也沒有效果,只有設置UIButtonimageViewcontentMode才有用,并且只有是調用的setImage時才有用。
  3. 由于UIButton繼承自UIControl,UIControl有兩個屬性,contentHorizontalAlignmentcontentVerticalAlignment,這兩個屬性類似contentMode,是單獨分別針對橫向和豎向的,且默認都為center,猜測優先級上應該是這兩個屬性大于buttonimageViewcontentMode。所以如果buttonimageViewcontentMode和這兩個屬性矛盾,優先遵循UIControl的兩個屬性,即本文button用小圖時所遇到的情況。
  4. 實現此需求的兩個最佳解決方案:
    • UIButtonsetImage:forState:方法,設置UIButton.imageView.contentModeUIViewContentModeScaleAspectFill,同時設置contentHorizontalAlignmentcontentVerticalAlignment均為fill
    • UIImageView,設置contentModeUIViewContentModeScaleAspectFill,同時添加點擊事件。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容