-->

Custom shape touchable area

2019-04-15 02:55发布

问题:

I have a shape like below and I want set it as background to my UIButton but touchable area is a rectangle, any suggestion to change touchable area to shape boarders?

回答1:

The idea is to have a subclass of UIButton that overrides the following methods:

func hitTest(_ point: CGPoint,
   withEvent event: UIEvent?) -> UIView? // that's for handling the case of multiple custom subviews on your view rather than for evaluating if it's your view to handle the touch

and

func pointInside(_ point: CGPoint,
       withEvent event: UIEvent?) -> Bool // way more preferable than hit test in your case!!!

There is a tutorial in Objective-C that utilizes hit test (just to catch the idea). In your case, the hardest problem is to detect if the received touch location is within the bounds of your custom shape (the tutorial above relies on pixel transparency, which is not the case for you). I assume, you draw the shape using a Bezier path. If that's what you do, you can implement the point inside evaluation with the func containsPoint(_ point: CGPoint) -> Bool of UIBezierPath. Good luck. P.S.There's also one tricky thing about UIBezierPath:

A point is not considered to be enclosed by the path if it is inside an open subpath, regardless of whether that area would be painted during a fill operation. Therefore, to determine mouse hits on open paths, you must create a copy of the path object and explicitly close any subpaths (using the closePath method) before calling this method.



回答2:

Actually I found it on this like and working perfectly:

How to know that if the only visible area of a .png is touched in XCode (swift or objective C)

But must change code like below:

   func alphaFromPoint(point: CGPoint) -> CGFloat {
   var pixel: [UInt8] = [0, 0, 0, 0]
   let colorSpace = CGColorSpaceCreateDeviceRGB();
   let alphaInfo : CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue)
   let context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, colorSpace, alphaInfo.rawValue) //need add .rawValue to alphaInfo

   CGContextTranslateCTM(context, -point.x, -point.y);

   self.layer.renderInContext(context!)

   let floatAlpha = CGFloat(pixel[3])
   return floatAlpha

}



回答3:

Swift 4.2

It's better to write an extension for UIButton. Override the touchesBegan function and check if the touch area is transparent or not.

extension UIButton{

    open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let touch = event!.touches(for: self)?.first {
            let location = touch.location(in: self)
            if alphaFromPoint(point: location) == 0 {
                self.cancelTracking(with: nil)
                print("cancelled!")
            } else{
                super.touchesBegan(touches, with: event)
            }
        }
    }

    func alphaFromPoint(point: CGPoint) -> CGFloat {
        var pixel: [UInt8] = [0, 0, 0, 0]
        let colorSpace = CGColorSpaceCreateDeviceRGB();
        let alphaInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
        let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: alphaInfo.rawValue)

        context!.translateBy(x: -point.x, y: -point.y)
        self.layer.render(in: context!)

        let floatAlpha = CGFloat(pixel[3])
        return floatAlpha
    }
}