swiftextension-methodsboundscgrectuigraphicscontext

extension function screenshot not capturing the same area in iphone and ipad


I am using a extension function to take a screenshot of a uiview. The problem is that the results look very different. I want the photo to look the same regardless of weather its a ipad or iphone. I am manually entering the constraints so I expect the images to be the same. I want to using the function to go to the orgin or center of the viewcontroller and be 200 height and 200 width. So the image should be the same regardless of the device because it starts in the center and is the same size.

     let vex = self.view.screenshot(for: CGRect(x: 0, y:UIScreen.main.bounds.size.height*0.65,, width: 100, height: 100), with: UIImage(named: "a.png"))

    extension UIView {
/// Takes a screenshot of a UIView, with an option to clip to view bounds and place a waterwark image
/// - Parameter rect: offset and size of the screenshot to take
/// - Parameter clipToBounds: Bool to check where self.bounds and rect intersect and adjust size so there is no empty space
/// - Parameter watermark: UIImage of the watermark to place on top
func screenshot(for rect: CGRect, clipToBounds: Bool = true, with watermark: UIImage? = nil) -> UIImage {
    var imageRect = rect
    if clipToBounds {
        imageRect = bounds.intersection(rect)
    }
    return UIGraphicsImageRenderer(bounds: imageRect).image { _ in
    drawHierarchy(in: CGRect(origin: .zero, size: bounds.size), afterScreenUpdates: true)
    watermark?.draw(in: CGRect(x: 0, y: 0, width: 32, height: 32))
    }
}}

IPHONE enter image description here

IPAD enter image description here


Solution

  • Take a look at the below screenshot. 


    View hierarchy   
     -self.view
       -blueView (subview of self.view) 
       -redView   (subview of self.view)
    

    enter image description here fullsize screenshot

    Notice how the screenshot target (based on X = 0 and Y = screen.y multiplier) differs based on device size?


 With the supplied extension you can fix the above issue in several different ways depending on view hierarchy. In case your hierarchy remains as listed above you can do something like this:


    Option 1 If you want to take a screenshot of exactly what’s inside of the blueView then you can do this:
(for this blueView is either an IBOutlet or a property that is set programmatically)

    let image = self.view.screenshot(for: blueView.frame, clipToBounds: true, with: UIImage(named: "star”))

    The above takes a screenshot of self.view at the given rect for all of subviews of self.view. (output below) enter image description here

    Option 2 If your view hierarchy changes to something like this: -self.view - blueView (subview of self.view) - redView (subview of blueView)


    And you want to take a snapshot of just blueView and subviews, then you can simplify the above by doing:

    let image = blueView.screenshot(for: blueView.bounds, with: UIImage(named: "star”)) This works, because redView is subview of blueView (output same as the screenshot above)

    Option 3 Now lets say you want to take a screenshot of self.view in the center with a screenshot size of 300 then you can do something like this

    let size = CGSize(width: 300, height: 300) // notice this is bigger than blueView size
    let rect = CGRect(origin: CGPoint(x: self.view.center.x - size.width / 2, y: self.view.center.y - size.height / 2), size: size)
    let image = self.view.screenshot(for: rect, clipToBounds: false, with: UIImage(named: "star"))
    

    Can all of these be optimized? YES, but I’m trying to spell things out for you step by step. (below is the output of this) enter image description here

    By the way, the star in those screenshots is the watermark

    For those wondering, the OP's extension is from this answer and it has been updated a little from the time of this question.