-2

I am looking to crop a rectangle segment of a screen shot. I am currently getting back no images when i perform the screen shot and subsequent crop. Below is a image of what i would like to crop (the rectangular grid) and my code

The solution presented in : Cropping image with Swift and put it on center position

is not applicable as I am trying to perform a crop of a screenshot, not crop an existing image. Cropping an image as presented in the above solution does not work for rotated UIImageView.

enter image description here

Code:

private func takeScreenShotCrop(cropGridRect: CGRect) -> UIImage?{

    let cropGridOrigin = CGPoint.init(x: cropGridRect.origin.x, y: cropGridRect.origin.y)
    let cropGridSize = CGSize.init(width: cropGridRect.width, height: cropGridRect.height)
    let cropZoneRect = CGRect.init(origin: cropGridOrigin, size: cropGridSize)

    UIGraphicsBeginImageContext(cropGridSize)
    view.drawHierarchy(in: cropZoneRect, afterScreenUpdates: true)
    guard let croppedIm: UIImage = UIGraphicsGetImageFromCurrentImageContext() else{return nil}
    UIGraphicsEndImageContext()
    return croppedIm
}
B K.
  • 534
  • 5
  • 18
babylon
  • 45
  • 1
  • 10
  • @ElTomato the suggested duplicate you suggested is not applicable. As mentioned in the edited question above – babylon Aug 22 '19 at 06:39
  • Your initial question was how to crop an image with a given rect as you end up with no result according to your code. You did not mention anything about rotation. – El Tomato Aug 22 '19 at 07:43

1 Answers1

0

Your question is a bit too general perhaps since you gave no data about your hierarchy. I will expect that there is a view (UIView or it's subclass) which has a certain transform that includes rotation (will call it imageView from now on). I will expect there is a view that shows that grid (will call it panel from now on). I will expect that imageView and panel have some common superview parent (they don't need to exactly be same superview, they just need to be in the same window hierarchy).

You can convert frames in relation from one and another. It is a bit of a pain due to transformations but generally you could write something like the following:

func screenshotView(_ viewToScreenshot: UIView, croppedBy cropView: UIView) -> UIImage? {

    guard let originalScreenshot: UIImage = {
        UIGraphicsBeginImageContext(viewToScreenshot.bounds.size)
        viewToScreenshot.drawHierarchy(in: viewToScreenshot.bounds, afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }() else { return nil }


    let panel = UIView(frame: cropView.bounds)
    let imageView = UIImageView(frame: viewToScreenshot.bounds)
    imageView.image = originalScreenshot

    let viewOriginalTransform = viewToScreenshot.transform
    viewToScreenshot.transform = .identity
    imageView.frame = viewToScreenshot.convert(viewToScreenshot.bounds, to: cropView)
    viewToScreenshot.transform = viewOriginalTransform

    panel.addSubview(imageView)
    imageView.transform = viewOriginalTransform

    UIGraphicsBeginImageContext(panel.bounds.size)
    panel.drawHierarchy(in: panel.bounds, afterScreenUpdates: true)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return image
}

And the usage in your case is then

let image = screenshotView(imageView, croppedBy: panel)

So what we do is first grab the screenshot of your image view. This will not crop it nor will it rotate it. It is just an internal snapshot of your view. This may be skipped and just the image is used IF the imageView is in fact UIImageView and the image is presented as it is (no effects like rounded corners).

The snapshot is then placed into a newly created panel and it's frame adjusted depending on the frame relation between the two views. We create a new snapshot from newly generated panel which is the result we are looking for.

To create "the image view" optimization you can simply add a single line:

guard let originalScreenshot: UIImage = {
    if let image = (viewToScreenshot as? UIImageView)?.image { return image }
    UIGraphicsBeginImageContext(viewToScreenshot.bounds.size)
    viewToScreenshot.drawHierarchy(in: viewToScreenshot.bounds, afterScreenUpdates: true)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}() else { return nil }
Matic Oblak
  • 16,318
  • 3
  • 24
  • 43