preface

  • In front-end page development, buttons are often used. The picture of the button is relatively small, but the click range of the button needs to be expanded. At this point, we usually think about doing things on the response chain

  • hitTest

/// Returns the farthest descendant of the receiver containing the specified point in the view hierarchy (including itself).
func hitTest(_ point: CGPoint.with event: UIEvent?). -> UIView?
Copy the code
  • This method iterates through the view hierarchy by calling the point(Inside: With 🙂 method of each subview to determine which subview should receive touch events. If point(inside:with:) returns true, the hierarchy of subviews will be similarly traversed until the foremost view containing the specified point is found. If a view does not contain this point, its view hierarchy branch is ignored. You rarely need to call this method yourself, but you can override it to hide touch events in subviews.
  • This method ignores view objects that are hidden, disabled for user interaction, or whose alpha level is less than 0.01. This method does not consider the contents of the view when determining a hit. Therefore, the view can still be returned even if the specified point is in a transparent part of the view’s content.
  • Points outside the receiver boundary are never reported as hits, even if they are actually in one of the receiver’s subviews. This can happen if the current view’s clipsToBounds property is set to false and the affected subview extends beyond the view’s boundaries.

Implementation scheme

  • Instead of adding separate properties to the button, we use the expanded click areaUIEdgeInsetsIt is convenient and intuitive, and not easy to set up errors
  • Here is the implementation code
open class EnlargeEdgeButton: UIButton {
    
    open var enlargeEdge: UIEdgeInsets = .zero
    
    open override func hitTest(_ point: CGPoint.with event: UIEvent?). -> UIView? {
        if alpha = = 0 || isHidden = = true || enlargeEdge = = .zero {
            return super.hitTest(point, with: event)
        }
        let rect = bounds.enlargeRect(edgeInsets: enlargeEdge)
        return rect.contains(point) ? self : nil}}extension CGRect {
    public func enlargeRect(edgeInsets: UIEdgeInsets) -> CGRect {
        return CGRect(x: minX - edgeInsets.left, y: minY - edgeInsets.top, width: width + (edgeInsets.left + edgeInsets.right), height: height + (edgeInsets.top + edgeInsets.bottom))
    }
}
Copy the code
  • It is particularly important to note that several special cases of buttons need to be handled

1.alpha == Transparency to 0, 0, quite is button is invisible, so at this moment, our expectations when the state of the button is hidden, if here to add the code to enlarge the range of button clicks, leads to while button is hidden, click on the button of the magnified area, however, respond to events, or it will appear a very strange bugs, and it’s difficult to find a bug, I have made this mistake in my work

2. IsHidden == true; similarly, hiding states should not extend the click range

4. EnlargeEdge ==. Zero; enlargeEdge ==. Zero

Classification VS subclassing

  • Personally, I think the classification is more intrusive, if givenUIButtonAdd category AttributesenlargeEdgeCauses any inheritance fromUIButtonAll classes have this functionality. Chances are they don’t even want it, but you already have it
  • Subclassing, if you want this functionality, you can use this class, or you can inherit from this class, and you can have precise control