前言:
写这篇文章来给大家分享一下我了解的关于UIButton的图片显示知识点和我遇到的坑及解决方案,帮助遇到同样问题和相关知识点不清晰的同学,如有错误,欢迎指正,共同进步🤝,本文在解析的时候可能较为啰嗦,是为了将思路完整呈现,对于初学者作用可能较大,老手可忽略,直接看结论。
需求:
相信大家都遇到过这种日常需求:一张图片上面有点击事件,同时这个控件的长宽比例固定或者干脆是个正方形,然而显示的图片则是长宽比例不固定的长方形,并且根据产品的需求,这张图片必须要覆盖整个控件,那么这图片势必要进行缩放;还有图片必须不能变形(都知道,产品和UI的日常需求,变形了确实太丑),也就是说显示出来的时候原始图片长宽比例不能变。
分析:
涉及到图片缩放,那就不得不提UIView
的contentMode
属性,既然要缩放并且图片长宽比例不能变,解决方案应该就是放大或缩小图片,但长宽比例不变,然后将图片居中显示,再裁减掉多余的部分,只显示控件大小的图片,当然是在clipToBounds
为YES
的情况下。很显然,只有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
,为了便于分析,给个背景颜色,就用亮油油的火红火红的绿色,如图:
开始设置图片
1) 先放一张长方形的图,来测试长宽比例不一样的情况,我这里用的是setImage:forState:
(以下省去forState
):
contentMode
的问题,设置一下
代码1
但是没有任何作用,就不上图了,运行后跟图效果1一模一样,怎么回事呢,经过查阅资料,我还是不知道🤓,总之就是UIButton.contentMode
就像个摆设,而要设置button
图片的contentMode
只有用button.imageView.contentMode
,我们来试试
代码2
效果2
好了,这样就对了,应该是满足我们刚才说的
原始图片x:y
> 目标控件x:y
,于是y
缩放到目标控件的y
值大小,x
等比例缩放,再居中显示,x
方向两边多余的部分就被裁切了,好了,这种情况已经达到产品的需求。
2)刚才的情况测试了长方形且不比目标控件小的情况,另一种情况就比较烦了,也是这次我写这篇文章遇到的坑点,比目标控件小的图片,我们放上去试试,代码不变,contentMode
还是scaleAspectFill
:
😂此时我的心情跟效果3中那个小人的心情一模一样,还记得我们刚才设置的按钮绿色背景,现在起作用了,不然就只能看到很小的一个小人在中间。为什么图片没有缩放呢,不是设置好了contentMode
吗?这么坑的吗?呵呵,就是这么坑,这就是button
的神奇之处,我想可能是因为是设置的是button的图标的原因吧(就是setImage
这个方法)。好,那我们试试setBackgoundImage
,呵呵,算了,这样影响我排版了,先把setImage
说完,因为我知道setBackgroundImage
也没用,等会儿我们再完整地试试。
emmmm......怎么解决呢?经过查阅资料🌝,这次我找到了解决方案,原来UIControl
有contentHorizontalAlignment
和contentVerticalAlignment
两个属性
fill
了,试下效果:
代码4(就是没有代码3,要一一对应 🙂,下同)
效果4
代码5
效果5(1)
懵逼中......这跟说好的不一样啊,然后我点击了一下
button
,
效果5(2)
继续懵逼......不过效果总算符合预期了,这里究竟是怎么回事,经过查阅资料,我猜应该是我在
storyboard
里面没有做设置,默认的是center
,而我写成fill
是在代码里写的,点击的时候才重新刷新了布局,如果直接在storyboard
里面调整应该不会出现这种情况,不过到这里,应该知道要怎么设置了
storyboard中,如果用storyboard的可以直接选右边红框框出的选项
代码6
效果6
果然,小图也被放大了,并且没有变形,那就是裁去了多余的部分,总算搞定!至此,
button
设置图片的所有情况应该都涵盖了,正方形的情况是自然没问题的。
setBackgrounImage
好了,刚才我们说到的setBackgroundImage
方法,试一下
这里因为我的小图准备的是一张近似正方形的图片,所以我把
button
改成100x200
的长方形(之前一直是200x200
),效果会更明显,如图
效果7(1)
效果7(2)
很明显,
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
,所以如果imageView
的contentMode
和这两个属性矛盾,优先遵循这两个属性,即本文button
用小图时所遇到的情况。 -
实现此需求的两个最佳解决方案:
-
用
UIButton
的setImage:forState:
方法,设置UIButton.imageView.contentMode
为UIViewContentModeScaleAspectFill
,同时设置contentHorizontalAlignment
和contentVerticalAlignment
均为fill
。 -
用
UIImageView
,设置contentMode
为UIViewContentModeScaleAspectFill
,同时添加点击事件。
-
网友评论