I have got mutliple bezier paths which are incorporated into CAShapeLayers and then add all layers to UIImageView. I have implemented hittest to all layers for selection, But it select the last CAShapeLayer. I want to select others layer as touch, but i don't know how?
here is my code for touch.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
super.touchesBegan(touches, with: event)
if let touch = touches.first, let touchedLayer = self.layerFor(touch)
{
print("hi")
selectedLayer = touchedLayer
touchedLayer.strokeColor = UIColor.red.cgColor
touchedLayer.lineWidth = CGFloat(3)
}
}
private func layerFor(_ touch: UITouch) -> CAShapeLayer?
{
let touchLocation = touch.location(in: self.backgroundIV)
let locationInView = self.backgroundIV!.convert(touchLocation, to: nil)
print("\(locationInView.x) \(locationInView.y)")
let hitPresentationLayer = view!.layer.presentation()?.hitTest(locationInView) as? CAShapeLayer
return hitPresentationLayer?.model()
}
Here is how I create layers from path
fileprivate func createLayer(path: SVGBezierPath) -> CAShapeLayer {
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
if let any = path.svgAttributes["stroke"] {
shapeLayer.strokeColor = (any as! CGColor)
}
if let any = path.svgAttributes["fill"] {
shapeLayer.fillColor = (any as! CGColor)
}
return shapeLayer
}
EDIT: here is the code that add shape layers to parent view
if let svgURL = Bundle.main.url(forResource: "image", withExtension: "svg") {
let paths = SVGBezierPath.pathsFromSVG(at: svgURL)
let scale = CGFloat(0.5)
for path in paths {
path.apply(CGAffineTransform(scaleX: scale, y: scale))
items.append(path)
let layer = createLayer(path: path)
layer.frame = self.backgroundIV.bounds
self.backgroundIV.layer.addSublayer(layer)
}
}
and changes in touchBegan methods
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
let point = touches.first?.location(in: self.backgroundIV)
if let layer = self.backgroundIV.layer.hitTest(point!) as? CAShapeLayer {
selectedLayer = layer
selectedLayer.strokeColor = UIColor.red.cgColor
selectedLayer.lineWidth = CGFloat(3)
print("Touched")
}
}
I'll make a couple assumptions here...
PocketSVG
(or similar)Even if you're only looking for the layer (not only inside the path of the layer), I would recommend looping through the sublayers and using
.contains(point)
rather than trying to uselayer.hitTest(point)
.Here is a quick example:
When you run this, it will look like this (I'm in a navigation controller):
This is the SVG file I used:
roundedboxes.svg
By default, we're going to test for tap inside the shape path on each layer, and we'll highlight all layers that pass the test:
Note that tapping where the shapes / layers overlap will select all layers where the tap is inside its path.
If we want only the Top Most layer, we'll get this:
If we want only the Bottom Most layer, we'll get this:
If we switch to detecting the Shape Bounding Box, we get this:
If that is at least close to what you're trying for, play around with this example code and see what you get.
Edit - minor change...
Using
SVGImageView
instead ofUIImageView
so we can scale the SVG to fit the view:Result: