2

i'm trying to take a screenshot of the whole view when i press a button in swift. The problem is that, when i take the screenshot, some parts are cut off, like the top...

enter image description here

Other part that is cut off is my container view that i have in the bottom of the screen. It contains a switch, a textfield and a button. This is the code that i'm using to take the screenshot ...

func screenShotMethod() {
    let layer = UIApplication.shared.keyWindow!.layer
    let scale = UIScreen.main.scale
    UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);

    layer.render(in: UIGraphicsGetCurrentContext()!)
    let screenshot = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    UIImageWriteToSavedPhotosAlbum(screenshot!, nil, nil, nil)
}

This is the code that i used to create the container view...

lazy var inputContainerView: UIView = {

    let containerView = UIView()
    containerView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: 50)
    containerView.backgroundColor = UIColor.white
    //Other things...

override var inputAccessoryView: UIView? {
    get {
        return inputContainerView
    }
}

override var canBecomeFirstResponder : Bool {
    return true
}

And this is what the finale image looks like... enter image description here

So what can i do to solve it? Thanks!

Edward Pizzurro
  • 575
  • 4
  • 15

2 Answers2

6

If you have some sort of Objective-C knowledge then here is the answer that you want.

Open this Link and follow the last answer which iterates through all the view hierarchy and And have line by line comments to fully understand the code.

The Image is finally converted into both NSData and UIImage. If you are still confuse then i can convert it into swift but first try it by your self.

Here is the Swift code of that Answer.

func screenshot() -> UIImage {
    let imageSize = UIScreen.main.bounds.size as CGSize;
    UIGraphicsBeginImageContextWithOptions(imageSize, false, 0)
    let context = UIGraphicsGetCurrentContext()
    for obj : AnyObject in UIApplication.shared.windows {
        if let window = obj as? UIWindow {
            if window.responds(to: #selector(getter: UIWindow.screen)) || window.screen == UIScreen.main {
                                // so we must first apply the layer's geometry to the graphics context
                                context!.saveGState();
                                // Center the context around the window's anchor point
                                context!.translateBy(x: window.center.x, y: window.center
                                    .y);
                                // Apply the window's transform about the anchor point
                                context!.concatenate(window.transform);
                                // Offset by the portion of the bounds left of and above the anchor point
                context!.translateBy(x: -window.bounds.size.width * window.layer.anchorPoint.x,
                                     y: -window.bounds.size.height * window.layer.anchorPoint.y);

                                // Render the layer hierarchy to the current context
                                window.layer.render(in: context!)

                                // Restore the context
                                context!.restoreGState();
            }
        }
    }
    let image = UIGraphicsGetImageFromCurrentImageContext();
    return image!
}
Community
  • 1
  • 1
Syed Qamar Abbas
  • 3,637
  • 1
  • 28
  • 52
  • I'm trying to understand that code, but i don't know too much about objective-c. There are some things that i don't understand. Can you help me with that code on swift? – Edward Pizzurro Dec 23 '16 at 23:15
  • I'm not getting from this part.. if (![window respondsToSelector:@selector(screen)] || [window screen] == [UIScreen mainScreen]) { tho this part... CGContextRestoreGState(context); – Edward Pizzurro Dec 23 '16 at 23:32
  • Actually, can you help me to implement that code into my code. Because i'm trying to implement that in my actual code, and i can't do it. Thanks! – Edward Pizzurro Dec 24 '16 at 00:55
  • I have update my answer with swift code implementation. – Syed Qamar Abbas Dec 24 '16 at 15:55
  • THANK YOU, it works fine... To implement it and save the screenshot to the album library, just have to put this line... UIImageWriteToSavedPhotosAlbum(screenshot(), nil, nil, nil) – Edward Pizzurro Dec 24 '16 at 16:36
  • still Status bar not included in screen shot – bLacK hoLE Nov 05 '17 at 15:02
0

In Swift 5.0 its possible whit:

yourView.snapshot(saveToHDR: false) { image in
        UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
    }

I use this for an AR Feature where I can take the shot whitout all Buttons on the Screen.

You can combine this whit an alert to do different things, like save or share:

Inside a Button:

        yourView.snapshot(saveToHDR: false) { image in
        if let saveImage = image {
            self.saveAndExport(sender: sender, saveImage: saveImage)
        }
    }

Save and Export alert:

func saveAndExport(sender: UIButton, saveImage: UIImage){
    let image: UIImage = saveImage
    var exportName:String?
    
    var alert = UIAlertController(title: "Title"  , message: "Subtitle", preferredStyle: .actionSheet)
    
    
    // Setup for Ipad preferredStyle: .alert cause it cant show preferredStyle: .actionSheet)
    if UIDevice.current.userInterfaceIdiom == .pad {
    alert = UIAlertController(title: "Title", message: "Subtitle", preferredStyle: .alert)
    }
    
    let shareFunctions = UIAlertAction(title: "Share", style: .default) { (action) in
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyyMMddhhmmss"
        exportName = dateFormatter.string(from: Date()).appending(".jpg")
        //            }
        let documentsPathUSDZ = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let urlUSDZ = documentsPathUSDZ.appendingPathComponent("\(exportName!).jpeg")
        do {
            try image.jpegData(compressionQuality: 1.0)?.write(to: urlUSDZ, options: .atomic)
            let activityController = UIActivityViewController(activityItems: [urlUSDZ], applicationActivities: nil)
            activityController.popoverPresentationController?.sourceView = sender
            self.present(activityController, animated: true, completion: nil)
        } catch let error {
            fatalError(error.localizedDescription)
        }
    }
    
    let backAction = UIAlertAction(title: "Back", style: .default) { (action) in
    }
    
    let saveInPhotos = UIAlertAction(title: "Save", style: .default) { (action) in
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }
    

    let saveImage = UIImage.init(systemName: "square.and.arrow.down")
    saveInPhotos.setValue(saveImage, forKey: "image")
    saveInPhotos.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
    
    let shareImage = UIImage.init(systemName: "square.and.arrow.up")
    shareFunctions.setValue(shareImage, forKey: "image")
    shareFunctions.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
    
    let backImage = UIImage.init(systemName: "chevron.backward")
    backAction.setValue(backImage, forKey: "image")
    backAction.setValue(CATextLayerAlignmentMode.left, forKey: "titleTextAlignment")
    
    
    alert.addAction(saveInPhotos)
    alert.addAction(shareFunctions)
    alert.addAction(backAction)
    present(alert, animated: true, completion: nil)
}

But that doesn't work on iPad and I couldn't find a fix for. Even if I change preferredStyle to: .alert

If anyone know why help pls.

Tonkyboy
  • 91
  • 1
  • 3