iOS总结 | 修改button响应区域的各种姿势
https://www.jianshu.com/p/4a606d6780ae
一、巧用透明button
在背景view上放一个比背景view更小的透明button,这样给人的感觉就是响应区域变小了;
在透明button上放一个比透明button更小的view,这样给人的感觉就是响应区域变大了。

二、设置imageEdgeInsets
按钮的背景颜色为clear的时候这招很好用。其实就是在不改变图片大小的情况下将按钮调大。
以下两种情况图片的size都是20x20,但是响应区域却不一样:
button.setImage(UIImage.init(named: "ic_middle_cicle_notice"), for: .normal)
button.frame = CGRect.init(x: 0, y: 0, width: 40, height: 40)
button.imageEdgeInsets = UIEdgeInsets.init(top: 10, left: 10, bottom: 10, right: 10)
button.frame = CGRect.init(x: 0, y: 0, width: 20, height: 20)
同方案一,也是间接修改按钮响应区域。
三、重写pointInside
上面两种方案遇到圆形按钮就比较无力了,如图:

你看这按钮它又大又圆,需要特别注意的是按钮图片的四周留白非常大。
你要是按照UI图来做弄一个204x204的button然后赋值图片,看起来肯定是没有任何问题的,问题是整个204x204的区域都会响应点击事件,由于图片留白很大,就会出现明明就没有点击按钮(绿色区域),按钮却响应了这种体验不太好的情况。
最好的体验是只有点击绿色区域才响应。
用方案一,imageView作为背景view,然后放一个跟绿色区域一样大一样圆的透明圆button。听起来貌似完美,然而如果只是设置button的cornerRadius,它看起来是圆了,但是它的响应区域依旧是方的。

方案二同样无解。
开始方案三:

我们想要的效果是以绿色圆圆心为中心方圆70pt的区域能够响应用户交互。因此可以计算用户touch的点距离圆心的距离,如果这个距离低于或等于70,才允许响应:
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
// 按钮中心
let centerX = self.bounds.size.width / 2
let centerY = self.bounds.size.height / 2
// 点击位置
let pointX = point.x
let pointY = point.y
// delta
let dltX = pointX - centerX
let dltY = pointY - centerY
// 点击点距离按钮中心的距离
let length = sqrt(pow(dltX, 2) + pow(dltY, 2))
return length <= 70
}
或者定义一个圈,判断touch点是否在这个圈里面:
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let originX: CGFloat = (204 - 140) / 2
// 画一个圈,看touch点是否在圈里面
let circle = UIBezierPath.init(ovalIn: CGRect.init(x: originX, y: originX, width: 140, height: 140))
return circle.contains(point)
}
注:按钮左上角的点是(0, 0)
这样,UI展现与设计图是一模一样的,用户交互也是我们所期望的。
补充:子view超出父view的情况
如图:

还是那个又大又圆的按钮,背景色给它设置成了黄色,然后这个按钮是被add到红色view上的,但是只有一半在上面,也就是说点击按钮上半部分不会有反应。
问:如何才能让点击按钮上半部分也会有反应?
老司机估计沉不住气了:
这是傻逼吧?为什么非得放在红色view上?放在controller的view上会怀孕?

不懂变通确实略显呆滞,但我们今天的游戏规则就是按钮必须add到红色view上。

根据响应者链可知,当用户点击按钮上半部分的时候,事件传递从application到window再到controller的view,然后,由于没有处于点击范围内的subview了,所以最终响应的是controller的view。
如何让事件传递从controller的view到红色view?
一种做法是扩大红色view的响应区域,比如说认定所有点击都在红色view的范围内:
// 方法一,重写redView的`pointInside`
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
// 直接返回true
// 响应区域全开
return true
}
当然这种做法比较极端。
另一种做法是重写hitTest
方法,根据touch点来设置响应的view:
// 方法二
// 重新设定响应view
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
// 坐标转换
let tempPoint = self.scanButton.convert(point, from: self)
// 判断点击点是否在button上
if self.scanButton.point(inside: tempPoint, with: event) {
return self.scanButton
}
return super.hitTest(point, with: event)
}
两种操作都是围绕事件传递的两个方法展开的,如果对响应者链不熟悉,也许会轻度懵逼。
demo
https://github.com/CaiWanFeng/iOS_Storage

结束了
多种姿势,你学会了吗?
如果还有其它姿势,欢迎共享。
作者:无夜之星辰
链接:https://www.jianshu.com/p/4a606d6780ae
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2019-08-28 iOS Bezier曲线
2019-08-28 iOS CALayer之CAEmitterLayer粒子发射器的神奇效果
2019-08-28 iOS学习笔记-084.粒子效果——路径移动
2015-08-28 详解 CALayer 和 UIView 的区别和联系
2015-08-28 8行代码教你搞定导航控制器全屏滑动返回效果