zoukankan      html  css  js  c++  java
  • 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
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    读书笔记--Linux Shell脚本攻略
    深入理解javascript原型链
    javascript基本类型及类型转换
    ECMAScript6 Promise
    git初体验
    走进git
    Dom编程优化
    go 递归实现快排
    go语言排序
    实现一个迭代器类
  • 原文地址:https://www.cnblogs.com/itlover2013/p/13575007.html
Copyright © 2011-2022 走看看