I have a UIView
with a transparent maskLayer
of certain radius at certain point. Besides that I have a UIImageView
with UIPanGesture
& UIPinchGesture
.
Now I can drag or zoom UIImageView
so that it can fit in the part of UIView
mask. Once it is done I need to get UIImage
from UIImageView
with respect to transparent part.
I don't know how to achieve it.
Below is the code which creates overlay on a UIView
with a specified mask CGRect
& radius.
func createOverlay(frame: CGRect,
xOffset: CGFloat,
yOffset: CGFloat,
radius: CGFloat) -> UIView {
// Step 1
let overlayView = UIView(frame: frame)
overlayView.backgroundColor = UIColor.groupTableViewBackground.withAlphaComponent(0.8)
// Step 2
let path = CGMutablePath()
beizerPath = UIBezierPath(arcCenter: CGPoint(x: xOffset, y: yOffset + radius), radius: radius, startAngle: 0.0, endAngle: 2.0 * .pi, clockwise: false)
path.addArc(center: CGPoint(x: xOffset, y: yOffset),
radius: radius,
startAngle: 0.0,
endAngle: 2.0 * .pi,
clockwise: false)
path.addRect(CGRect(origin: .zero, size: overlayView.frame.size))
// Step 3
let maskLayer = CAShapeLayer()
maskLayer.backgroundColor = UIColor.black.cgColor
maskLayer.path = path
maskLayer.fillRule = .evenOdd
// Step 4
overlayView.layer.mask = maskLayer
overlayView.clipsToBounds = true
return overlayView
}
Here I a storing UIBezierPath
in case of any need!
Then, I tried clipping UIBezierPath
on an UIImage
but then it's an issue with the rect. Because, UIImageView
can be dragged and zoom so it's CGRect
changes. Below is the code I was using to create clipping.
extension UIImage {
func imageByApplyingClippingBezierPath(_ path: UIBezierPath) -> UIImage {
// Mask image using path
let maskedImage = imageByApplyingMaskingBezierPath(path)
// Crop image to frame of path
let croppedImage = UIImage(cgImage: maskedImage.cgImage!.cropping(to: path.bounds)!)
return croppedImage
}
func imageByApplyingMaskingBezierPath(_ path: UIBezierPath) -> UIImage {
// Define graphic context (canvas) to paint on
UIGraphicsBeginImageContext(size)
let context = UIGraphicsGetCurrentContext()!
context.saveGState()
// Set the clipping mask
path.addClip()
draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let maskedImage = UIGraphicsGetImageFromCurrentImageContext()!
// Restore previous drawing context
context.restoreGState()
UIGraphicsEndImageContext()
return maskedImage
}
}
Other Solution, I think of taking snapshot of complete view and then, cut down the CGRect
from it. But I don't think so that's the proper way to do so!
If possible it is easiest to create a round view and create a snapshot of that view. Check the following solution:
func cutImageCircle(_ image: UIImage?, inFrame imageFrame: CGRect, contentMode: UIView.ContentMode = .scaleAspectFill, circle: (center: CGPoint, radius: CGFloat)) -> UIImage? {
guard let image = image else { return nil }
func generateSnapshotImage(ofView view: UIView, scale: CGFloat = 0.0) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, scale)
defer { UIGraphicsEndImageContext() }
view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
return UIGraphicsGetImageFromCurrentImageContext()
}
let imageViewPanel = UIView(frame: CGRect(x: 0.0, y: 0.0, width: circle.radius*2.0, height: circle.radius*2.0))
imageViewPanel.clipsToBounds = true
imageViewPanel.layer.cornerRadius = circle.radius
let imageView = UIImageView(frame: {
var frame = imageFrame
frame.origin.x -= circle.center.x-circle.radius
frame.origin.y -= circle.center.y-circle.radius
return frame
}())
imageView.contentMode = contentMode
imageView.image = image
imageViewPanel.addSubview(imageView)
let cutImage = generateSnapshotImage(ofView: imageViewPanel, scale: 1.0)
return cutImage
}
You only need to compute where circle center is and what size it has. I added the option to adjust frame in there so you could control the size of output image. This way you can increase the quality of image taken.