I am parsing SVG image file and convert it paths into CAShapeLayer, after that I am adding this CAShapeLayer to UIImage View's layer. It's work fine, but the problem is that it does not fit to the UIImage View. That's output. Image 1 . That's code of adding layers to UIImageView
if let svgURL = Bundle.main.url(forResource: "image", withExtension: "svg") {
let paths = SVGBezierPath.pathsFromSVG(at: svgURL)
for path in paths {
items.append(path)
let layer = createLayer(path: path)
layer.frame = self.backgroundIV.bounds
self.backgroundIV.layer.addSublayer(layer)
}
} // Create Layer from Paths Method
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
}
I Searched and added these two lines of code, i got this result
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)
}
That's is output. Image 2
I don't know how to calculate scaleX
or scaleY
(using in CGAffineTransform
method) value according to uiImage View bounds
Edit Note: I am apply touch support to CAShapeLayer, That's code.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
guard let point = touches.first?.location(in: self.backgroundIV),
let layers = self.backgroundIV.layer.sublayers
else { return }
var hitLayers: [CAShapeLayer] = []
selectedLayer?.lineWidth = CGFloat(0)
for subLayer in layers {
if let thisLayer = subLayer as? CAShapeLayer,
let pth = thisLayer.path {
let layerPoint: CGPoint = thisLayer.convert(point, from: self.backgroundIV.layer)
if pth.contains(layerPoint) {
// undoModel.append()
hitLayers.append(thisLayer)
}
}
}
selectedLayer = hitLayers.last
selectedLayer?.strokeColor = UIColor.red.cgColor
selectedLayer?.lineWidth = CGFloat(3)
undo.append(.init(stokeColor: nil, selectedLayer: selectedLayer, points: []))
if ((selectedLayer?.frame.contains(point)) != nil) {
isDragged = true
}
}
According to your updated question, calculating scale using view bounds
var paths = [SVGBezierPath]()
override func viewDidLoad() {
super.viewDidLoad()
if let svgURL = Bundle.main.url(forResource: "Freesample", withExtension: "svg") {
paths = SVGBezierPath.pathsFromSVG(at: svgURL)
backgroundIV.layer.borderColor = UIColor.black.withAlphaComponent(0.3).cgColor
backgroundIV.layer.borderWidth = 1
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let svgBounds = SVGBoundingRectForPaths(paths)
var scale = CGFloat.zero
if backgroundIV.bounds.width < backgroundIV.bounds.height {
scale = backgroundIV.bounds.width / svgBounds.width
} else {
scale = backgroundIV.bounds.height / svgBounds.height
}
self.backgroundIV.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
for path in paths {
path.apply(.init(scaleX: scale, y: scale))
let layer = createLayer(path: path)
layer.frame = self.backgroundIV.bounds
self.backgroundIV.layer.addSublayer(layer)
}
}
This is basically the same answer I gave you to your previous question about detecting touches on multiple layers.
The difference is, instead of using your code to create/add the layers, this example uses SVGImageView
which is part of PocketSVG
:
class BoxesViewController: UIViewController {
var backgroundIV: SVGImageView!
var detectMode: DetectMode = .All
var detectType: DetectType = .ShapePath
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemYellow
guard let svgURL = Bundle.main.url(forResource: "roundedboxes", withExtension: "svg") else {
fatalError("SVG file not found!!!")
}
backgroundIV = SVGImageView.init(contentsOf: svgURL)
backgroundIV.frame = view.bounds
backgroundIV.contentMode = .scaleAspectFit
backgroundIV.autoresizingMask = [.flexibleWidth, .flexibleHeight]
let modeControl = UISegmentedControl(items: ["All", "Top Most", "Bottom Most"])
let typeControl = UISegmentedControl(items: ["Shape Path", "Shape Bounding Box"])
modeControl.translatesAutoresizingMaskIntoConstraints = false
typeControl.translatesAutoresizingMaskIntoConstraints = false
backgroundIV.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(modeControl)
view.addSubview(typeControl)
view.addSubview(backgroundIV)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
modeControl.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
modeControl.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
modeControl.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
typeControl.topAnchor.constraint(equalTo: modeControl.bottomAnchor, constant: 40.0),
typeControl.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
typeControl.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
backgroundIV.topAnchor.constraint(equalTo: typeControl.bottomAnchor, constant: 40.0),
backgroundIV.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
backgroundIV.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
backgroundIV.heightAnchor.constraint(equalTo: backgroundIV.widthAnchor),
])
modeControl.addTarget(self, action: #selector(modeChanged(_:)), for: .valueChanged)
typeControl.addTarget(self, action: #selector(typeChanged(_:)), for: .valueChanged)
modeControl.selectedSegmentIndex = 0
typeControl.selectedSegmentIndex = 0
// so we can see the frame of the image view
backgroundIV.backgroundColor = .white
}
@objc func modeChanged(_ sender: UISegmentedControl) -> Void {
switch sender.selectedSegmentIndex {
case 0:
detectMode = .All
case 1:
detectMode = .TopMost
case 2:
detectMode = .BottomMost
default:
()
}
}
@objc func typeChanged(_ sender: UISegmentedControl) -> Void {
switch sender.selectedSegmentIndex {
case 0:
detectType = .ShapePath
case 1:
detectType = .ShapeBounds
default:
()
}
}
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
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let point = touches.first?.location(in: self.backgroundIV),
// make sure backgroundIV has sublayers
let layers = self.backgroundIV.layer.sublayers
else { return }
var hitLayers: [CAShapeLayer] = []
// loop through all sublayers
for subLayer in layers {
// make sure
// it is a CAShapeLayer
// it has a path
if let thisLayer = subLayer as? CAShapeLayer,
let pth = thisLayer.path {
// clear the lineWidth... we'll reset it after getting the hit layers
thisLayer.lineWidth = 0
// convert touch point from backgroundIV.layer to thisLayer
let layerPoint: CGPoint = thisLayer.convert(point, from: self.backgroundIV.layer)
if detectType == .ShapePath {
// does the path contain the point?
if pth.contains(layerPoint) {
hitLayers.append(thisLayer)
}
} else if detectType == .ShapeBounds {
if pth.boundingBox.contains(layerPoint) {
hitLayers.append(thisLayer)
}
}
}
}
if detectMode == .All {
hitLayers.forEach { layer in
layer.strokeColor = UIColor.cyan.cgColor
layer.lineWidth = 3
}
} else if detectMode == .TopMost {
if let layer = hitLayers.last {
layer.strokeColor = UIColor.cyan.cgColor
layer.lineWidth = 3
}
} else if detectMode == .BottomMost {
if let layer = hitLayers.first {
layer.strokeColor = UIColor.cyan.cgColor
layer.lineWidth = 3
}
}
}
}
Now the SVG file is scaled to fit the view:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.