Reputation: 139
I'm quite new to iOS Development (so please forgive my ineptitude - I've looked everywhere!), and was looking to find a way to detect a tap on a CAShapeLayer
. So far, I've come across hitTest
. Is hitTest
the best method, and if so how is it used in Swift especially with CAShapeLayer
s? Also, if I had numerous CAShapeLayers
, how would I use the hitTest
method to refer to them individually?
This is how I created the CAShapeLayer
:
let newBounds = CGRect(x: 0, y: 0, width: 200, height: 200)
let newShape = CAShapeLayer()
newShape.bounds = newBounds
newShape.position = view.center
newShape.cornerRadius = newBounds.width / 2
newShape.path = UIBezierPath(ovalInRect: newShape.bounds).CGPath
newShape.lineWidth = 42
newShape.strokeColor = UIColor(red: 222/255.0, green: 171/255.0, blue: 66/255.0, alpha: 1.0).CGColor
newShape.fillColor = UIColor.clearColor().CGColor
newShape.strokeStart = 0.2
newShape.strokeEnd = 0.4
view.layer.addSublayer(newShape)
Upvotes: 9
Views: 12134
Reputation: 16820
Swift 4 & 5
My UIImageView
has multiple embedded CAShapeLayer
objects. Here is how I was able to detect taps on them. Referenced from this tutorial.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
guard let point = touch?.location(in: imageView) else { return }
guard let sublayers = imageView.layer.sublayers as? [CAShapeLayer] else { return }
for layer in sublayers {
if let path = layer.path, path.contains(point) {
print(layer)
}
}
}
Upvotes: 11
Reputation: 6983
I used Arbitur's code and i had some errors. Here is a code i had with no errors. For swift 3.2 / 4.0
override func viewDidLoad() {
super.viewDidLoad()
let layer = CAShapeLayer()
layer.anchorPoint = CGPoint.zero
layer.path = UIBezierPath.init(ovalIn: CGRect(x: 0, y: 0, width: 100, height: 200)).cgPath
layer.bounds = (layer.path?.boundingBox)! // IMPORTANT, without this hitTest wont work
layer.fillColor = UIColor.red.cgColor
self.view.layer.addSublayer(layer)
}
// Check for touches
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let point = touches.first?.location(in: self.view) // Where you pressed
if let layer = self.view.layer.hitTest(point!) as? CAShapeLayer { // If you hit a layer and if its a Shapelayer
if (layer.path?.contains(point!))! { // Optional, if you are inside its content path
print("Hit shapeLayer") // Do something
}
}
}
Upvotes: 3
Reputation: 39081
Heres imo the best way to do what you want to achieve:
// First add the shapelayer
let layer = CAShapeLayer()
layer.anchorPoint = CGPointZero
layer.path = UIBezierPath(ovalInRect: CGRect(x: 0, y: 0, width: 100, height: 200)).CGPath
layer.bounds = CGPathGetBoundingBox(layer.path) // IMPORTANT, without this hitTest wont work
layer.fillColor = UIColor.redColor().CGColor
self.view.layer.addSublayer(layer)
// Check for touches
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let point = touches.anyObject()!.locationInView(self.view) // Where you pressed
if let layer = self.view.layer.hitTest(point) as? CAShapeLayer { // If you hit a layer and if its a Shapelayer
if CGPathContainsPoint(layer.path, nil, point, false) { // Optional, if you are inside its content path
println("Hit shapeLayer") // Do something
}
}
}
Upvotes: 13
Reputation: 16864
Use following code for get touch of CAShapeLayer
.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInView:self.view];
for (id sublayer in self.view.layer.sublayers) {
BOOL touchInLayer = NO;
if ([sublayer isKindOfClass:[CAShapeLayer class]]) {
CAShapeLayer *shapeLayer = sublayer;
if (CGPathContainsPoint(shapeLayer.path, 0, touchLocation, YES)) {
// Here your code for do any opration.
touchInLayer = YES;
}
} else {
CALayer *layer = sublayer;
if (CGRectContainsPoint(layer.frame, touchLocation)) {
// Touch is in this rectangular layer
touchInLayer = YES;
}
}
}
}
}
Upvotes: 0