Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 3: Create UIImage from UIBezierPath

I have a UIBezierPath, and I ultimately I need a UIImageView from it. Right now I'm trying to first create a UIImage, and then a UIImageView from that.

I'm working in swift, and I have looked at similar questions, and the answers either don't work or produce a shape rather than a curve. My beziers represent sketches as opposed to actual shapes. I don't want the bezier closed, which is what UIImage.shapeImageWithBezierPath() does. This last solution is the only one which has actually encapsulated the full curve. All the other solutions either produce fragments or don't show up at all.

The end goal is to create a CollectionView with these curves as items in the list, meaning that I ultimately have to put UIBeziers into CollectionViewCells.

Thanks in advance.

like image 293
Jean Valjean Avatar asked Mar 16 '17 01:03

Jean Valjean


1 Answers

If you want UIImage (which you can use with UIImageView), you can use UIGraphicsImageRenderer:

private func image(with path: UIBezierPath, size: CGSize) -> UIImage {
    return UIGraphicsImageRenderer(size: size).image { _ in
        UIColor.blue.setStroke()
        path.lineWidth = 2
        path.stroke()
    }
}

That was introduced in iOS 10. If you need to support earlier iOS versions, you can use the UIGraphicsGetImageFromCurrentImageContext:

private func image(with path: UIBezierPath, size: CGSize) -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(size, false, 0)
    UIColor.blue.setStroke()
    path.lineWidth = 2
    path.stroke()
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}

Alternatively, you can bypass the UIImage if you want, and just define a UIView subclass with a CAShapeLayer:

@IBDesignable
class ShapeView: UIView {

    @IBInspectable var lineWidth:   CGFloat = 1   { didSet { shapeLayer.lineWidth   = lineWidth } }
    @IBInspectable var strokeColor: UIColor = #colorLiteral(red: 0, green: 0, blue: 1, alpha: 1)  { didSet { shapeLayer.strokeColor = strokeColor.cgColor } }
    @IBInspectable var fillColor:   UIColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0)  { didSet { shapeLayer.fillColor   = fillColor.cgColor } }

    var path:  UIBezierPath? { didSet { shapeLayer.path = path?.cgPath } }

    private let shapeLayer = CAShapeLayer()

    override init(frame: CGRect) {
        super.init(frame: frame)

        configure()
    }

    convenience init() {
        self.init(frame: .zero)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        configure()
    }

    /// Add and configure the view.
    ///
    /// Add and configure shape.

    private func configure() {
        layer.addSublayer(shapeLayer)

        shapeLayer.strokeColor = self.strokeColor.cgColor
        shapeLayer.fillColor   = self.fillColor.cgColor
        shapeLayer.lineWidth   = self.lineWidth
    }

    // just add some path so if you used this in IB, you'll see something

    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        shapeLayer.path = UIBezierPath(ovalIn: bounds.insetBy(dx: lineWidth / 2, dy: lineWidth / 2)).cgPath
    }

}
like image 197
Rob Avatar answered Nov 19 '22 17:11

Rob