Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to convert UIBezierpath to SVG? #194

Open
firattamur opened this issue Oct 9, 2021 · 8 comments
Open

How to convert UIBezierpath to SVG? #194

firattamur opened this issue Oct 9, 2021 · 8 comments
Labels

Comments

@firattamur
Copy link

Hello,

I am trying to create a coloring app. I can read svg image and fill their backgrounds. I want to save my UIBezier paths as svg after coloring process is done. I saw the function SVGStringFromCGPaths path I could not figure out how to set AttributeSet of svg? A short explanation or a small examples would be great.

Thanks,

@fjolnir
Copy link
Member

fjolnir commented Oct 11, 2021

You'll need to explain how the "coloring process" works.

If your paths stay as SVGBezierPath instances you can just use self.SVGRepresentation.

@firattamur
Copy link
Author

Hello,

Thank you for your response first of all. Here is my code for coloring process. I am creating CAShapeLayers from SVGBezierPaths then fill inside of them. I want to save result of coloring process as SVG. Is there any way to convert colored CAShapeLayers to SVG?

import UIKit
import PocketSVG


class ViewController: UIViewController {

private var paths: [UIBezierPath]!
private var shapeLayerIndices = [CAShapeLayer:Int]()
private var touchedshapedLayerIndices = [CAShapeLayer:Int]()
private var shapeLayers = [CAShapeLayer]()
private var affineTransform: CGAffineTransform!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    
    let url = Bundle.main.url(forResource: "simple", withExtension: "svg")!
    
    paths = SVGBezierPath.pathsFromSVG(at: url)
    let tigerLayer = CALayer()
    
    for (index, path) in paths.enumerated() {
    
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path.cgPath
        
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.fillColor = UIColor.white.cgColor

        shapeLayerIndices[shapeLayer] = index
        tigerLayer.addSublayer(shapeLayer)
        
    }
    
    let scale = CGAffineTransform(scaleX: 1, y: 1)
    let transform = CGAffineTransform(translationX: 100, y: 100)
    affineTransform = scale.concatenating(transform)
    tigerLayer.setAffineTransform(affineTransform)
    view.layer.addSublayer(tigerLayer)
                
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
   
    let touch = touches.first
    let point = touch!.location(in: self.view).applying(self.affineTransform.inverted())
        
    guard let sublayers = self.view.layer.sublayers?[0].sublayers else { return }

    self.touchedshapedLayerIndices = [CAShapeLayer:Int]()
    
    for (_, sublayer) in sublayers.enumerated() {
        
        if let sublayer = sublayer as? CAShapeLayer, let path = sublayer.path {
            
            if path.contains(point) {
                touchedshapedLayerIndices[sublayer] = self.shapeLayerIndices[sublayer]
            }
        }
        
    }
    
    let topShapeLayer = touchedshapedLayerIndices.max { a, b in a.value < b.value }
    
    guard topShapeLayer != nil else {
        return
    }
    
    let sublayer = topShapeLayer!.key
    let index    = topShapeLayer!.value
    
    sublayer.removeFromSuperlayer()
    sublayer.fillColor = UIColor.red.cgColor
    self.view.layer.sublayers?[0].insertSublayer(sublayer, at: UInt32(index))
    
}

}

@fjolnir
Copy link
Member

fjolnir commented Oct 12, 2021

You'll have to iterate over the path array(and change its type to [SVGBezierPath]), then for each of the paths, use pathBySettingSVGAttributes: with the svg attributes you want to change. It'll return a new path reflecting the changes.

something like

let attributeSet = SVGMutableAttributeSet()
let paths = shapeLayers.map { layer in 
    let path = paths[shapeLayerIndices[layer]]
    let newPath = path.pathBySettingSVGAttributes(["fill": layer.backgroundColor])
    attributeSet.set(attributes: path.svgAttributes, forPath: newPath.CGPath)
    return newPath.CGPath
}

let svgString = SVGStringFromCGPaths(updatedPaths, attributeSet)

I haven't actually compiled the above, but the general idea should work.

@firattamur
Copy link
Author

Thanks a lot! I will try it and share the results.

@firattamur
Copy link
Author

Hello,

I have tried your code. I got the error in below. I tried to give color parameter as UIColor and CgColor but neither of them worked.

Screen Shot 2021-10-13 at 7 43 07 PM
Screen Shot 2021-10-13 at 7 42 50 PM

Thanks,

@CodeForRabbit
Copy link
Contributor

CodeForRabbit commented Apr 18, 2023

I had the same problem, and I modified the code myself to keep it from crashing

for(NSString *key in pathAttrs) {
      if(![pathAttrs[key] isKindOfClass:[NSString class]]) { // Color
           [svg appendFormat:@" %@=\"%@\"", key, hexTriplet((__bridge CGColorRef)pathAttrs[key]).string()];
              if (CFGetTypeID((__bridge CFTypeRef)(pathAttrs[key])) == CGColorGetTypeID()) {
                    float const alpha = CGColorGetAlpha((__bridge CGColorRef)pathAttrs[key]);
                    if(alpha < 1.0)
                        [svg appendFormat:@" %@-opacity=\"%.2g\"", key, alpha];
                }
      } else
           [svg appendFormat:@" %@=\"%@\"", key, pathAttrs[key]];
}

@fjolnir
Copy link
Member

fjolnir commented Apr 18, 2023

@CodeForRabbit It'd be great if you could make a PR with that

@arielelkin
Copy link
Collaborator

fixed via #214

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants