how can I expand the hit area of a specific UIButton in Swift?

跟風遠走 提交于 2019-12-25 08:44:22

问题


In my application I have a UIButton that is quite small, so I thought about increasing the hit area of it.

I found an extension for that:

fileprivate let minimumHitArea = CGSize(width: 100, height: 100)

extension UIButton {
    open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // if the button is hidden/disabled/transparent it can't be hit
        if self.isHidden || !self.isUserInteractionEnabled || self.alpha < 0.01 { return nil }

        // increase the hit frame to be at least as big as `minimumHitArea`
        let buttonSize = self.bounds.size
        let widthToAdd = max(minimumHitArea.width - buttonSize.width, 0)
        let heightToAdd = max(minimumHitArea.height - buttonSize.height, 0)
        let largerFrame = self.bounds.insetBy(dx: -widthToAdd / 2, dy: -heightToAdd / 2)

        // perform hit test on larger frame
        return (largerFrame.contains(point)) ? self : nil
    }
}

but when I use it, every button in my app has a bigger hit area. I want to increase it to only one specialButton - how can I do it?


回答1:


You can add a computed property to your extension which you can set in your controller if you want to enable the hit area like this:

fileprivate let minimumHitArea = CGSize(width: 100, height: 100)

extension UIButton {

  var isIncreasedHitAreaEnabled: Bool {
    get {
      // default value is false, so you can easily handle from outside when to enable the increased hit area of your specific button
      return false
    }
    set {

    }
  }

  open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    // if the button is hidden/disabled/transparent it can't be hit
    if self.isHidden || !self.isUserInteractionEnabled || self.alpha < 0.01 { return nil }

    // only if it's true the hit area can be increased
    if isIncreasedHitAreaEnabled {
      // increase the hit frame to be at least as big as `minimumHitArea`
      let buttonSize = self.bounds.size
      let widthToAdd = max(minimumHitArea.width - buttonSize.width, 0)
      let heightToAdd = max(minimumHitArea.height - buttonSize.height, 0)
      let largerFrame = self.bounds.insetBy(dx: -widthToAdd / 2, dy: -heightToAdd / 2)

      // perform hit test on larger frame
      return (largerFrame.contains(point)) ? self : nil
    }
    return self
  }
}

And then in your view controller just set isIncreasedHitAreaEnabled to true if you want to increase it for a specific button (e.g. in your viewDidLoad):

override func viewDidLoad() {
  super.viewDidLoad()
  specialButton.isIncreasedHitAreaEnabled = true
}

Update:

First of all the method which you use doesn't work like expected. And also my approach with the computed property did not work like I thought. So even if the method does not work like expected, I just want to update my approach with the computed property. So to set the property from outside, you can use Associated Objects. Also have look here. With this solution now it would be possible to handle the Bool property isIncreasedHitAreaEnabled from your controller:

fileprivate let minimumHitArea = CGSize(width: 100, height: 100)
private var xoAssociationKey: UInt8 = 0

extension UIButton {

  var isIncreasedHitAreaEnabled: Bool {
    get {
      return (objc_getAssociatedObject(self, &xoAssociationKey) != nil)
    }
    set(newValue) {
      objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }
  }

  open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    // if the button is hidden/disabled/transparent it can't be hit
    if self.isHidden || !self.isUserInteractionEnabled || self.alpha < 0.01 { return nil }

    if isIncreasedHitAreaEnabled {
      // increase the hit frame to be at least as big as `minimumHitArea`
      let buttonSize = self.bounds.size
      let widthToAdd = max(minimumHitArea.width - buttonSize.width, 0)
      let heightToAdd = max(minimumHitArea.height - buttonSize.height, 0)
      let largerFrame = self.bounds.insetBy(dx: -widthToAdd / 2, dy: -heightToAdd / 2)

      // perform hit test on larger frame
      return (largerFrame.contains(point)) ? self : nil
    }
    return self
  }
}



回答2:


Don't expand the hit area; shrink the drawing area. Make the button a subclass of UIButton, and in that subclass, implement rect methods, along these lines:

class MyButton : UIButton {
    override func contentRect(forBounds bounds: CGRect) -> CGRect {
        return bounds.insetBy(dx: 30, dy: 30)
    }
    override func backgroundRect(forBounds bounds: CGRect) -> CGRect {
        return bounds.insetBy(dx: 30, dy: 30)
    }
}

Now the button is tappable 30 points outside its visible background.




回答3:


Create a Custom UIButton Class and override pointInside:(CGPoint)point method like below. Then set these properties value from viewController.

#import <UIKit/UIKit.h>
@interface CustomButton : UIButton
    @property (nonatomic) CGFloat leftArea;
    @property (nonatomic) CGFloat rightArea;
@property (nonatomic) CGFloat topArea;
@property (nonatomic) CGFloat bottomArea;
@end




#import "CustomButton.h
@implementation CustomButton

-(BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    CGRect newArea = CGRectMake(self.bounds.origin.x - _leftArea, self.bounds.origin.y - _topArea, self.bounds.size.width + _leftArea + _rightArea, self.bounds.size.height + _bottomArea + _topArea);

    return CGRectContainsPoint(newArea, point);
}
@end


来源:https://stackoverflow.com/questions/42181550/how-can-i-expand-the-hit-area-of-a-specific-uibutton-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!