Reputation: 566
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?
Upvotes: 1
Views: 1625
Reputation: 188
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
}
}
Upvotes: 2
Reputation: 566
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
}
Upvotes: 1
Reputation: 2107
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.
Upvotes: 2