2

This is my first question, so please excuse me if I'm unclear in any way.

I would like to be able to create a new UIImage2 from the contents of a freehand drawn shape over an existing UIImage1 to display over the existing UIImage1.

A visual example: UIImage1 is a photo of a city skyline. The user should be able to draw an outline around a skyscraper and create a new UIImage2 which will be displayed stacked over UIImage1, with the ability to edit (size, color, etc) of UIImage 2.

Drawing lines over a UIImage isn't too difficult, but I've yet to figure out how to capture the content from the UIImage within that freehand drawn shape creating a separate UIImage.

I'd greatly appreciate any help.

Ian
  • 23
  • 2
  • Hey Rob, thanks for asking for clarification. The captured image should contain the portion of the original image that is within the drawn shape. – Ian Dec 16 '18 at 22:34
  • and thanks for the tip. I'll look into those funcs in depth. Appreciate it! – Ian Dec 16 '18 at 22:36
  • You're a saint. Thanks for pointing me in the right direction. – Ian Dec 16 '18 at 22:48

1 Answers1

2

If you want to create an image from a masked path, you can:

  • Create UIBezierPath from user gesture;
  • Use that bezier path as the CAShapeLayer on the image view;
  • When the user gesture is done, remove that shape layer, but create new shape layer and use it as a mask; and
  • Create image from that masked image view using UIGraphicsImageRenderer and drawHierarchy to render whatever should be captured in the image.

For example:

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    
    private var path: UIBezierPath?
    private var strokeLayer: CAShapeLayer?
    
    @IBAction func didHandlePan(_ gesture: UIPanGestureRecognizer) {
        let location = gesture.location(in: imageView)
        
        switch gesture.state {
        case .began:
            path = UIBezierPath()
            path?.move(to: location)
            strokeLayer = CAShapeLayer()
            imageView.layer.addSublayer(strokeLayer!)
            strokeLayer?.strokeColor = #colorLiteral(red: 1, green: 0.1491314173, blue: 0, alpha: 1).cgColor
            strokeLayer?.fillColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor
            strokeLayer?.lineWidth = 5
            strokeLayer?.path = path?.cgPath
            
        case .changed:
            path?.addLine(to: location)
            strokeLayer?.path = path?.cgPath
            
        case .cancelled, .ended:
            // remove stroke from image view
            
            strokeLayer?.removeFromSuperlayer()
            strokeLayer = nil

            // mask the image view
            
            let mask = CAShapeLayer()
            mask.fillColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1).cgColor
            mask.strokeColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0).cgColor
            mask.lineWidth = 0
            mask.path = path?.cgPath
            imageView.layer.mask = mask

            // get cropped image

            let image = imageView?.snapshot
            imageView.layer.mask = nil

            // perhaps use that image?

            imageView.image = image

        default: break
        }
    }        
}

By the way, to create UIImage from a view (masked or otherwise), you can use:

extension UIView {
    var snapshot: UIImage {
        UIGraphicsImageRenderer(bounds: bounds).image { _ in
            drawHierarchy(in: bounds, afterScreenUpdates: true)
        }
    }
}

That yields something like:

enter image description here

Rob
  • 415,655
  • 72
  • 787
  • 1,044