1

I am trying to take a screenshot of my UIView and Crop it, save it to my Photo library. As i am trying to do this there are 3 conflicts.

(1) - I want to take Screenshot with Blur in it, As blur filter never gets saved in the screenshot.

(2) - The image quality is very low.

(3) - I am not able to crop the image.

This is my code -

@IBAction func Screenshot(_ sender: UIButton) {

    // Declare the snapshot boundaries
    let top: CGFloat = 70
    let bottom: CGFloat = 400

    // The size of the cropped image
    let size = CGSize(width: view.frame.size.width, height: view.frame.size.height - top - bottom)

    // Start the context
    UIGraphicsBeginImageContext(size)

    // we are going to use context in a couple of places
    let context = UIGraphicsGetCurrentContext()!

    // Transform the context so that anything drawn into it is displaced "top" pixels up
    // Something drawn at coordinate (0, 0) will now be drawn at (0, -top)
    // This will result in the "top" pixels being cut off
    // The bottom pixels are cut off because the size of the of the context
    context.translateBy(x: 0, y: -top)

    // Draw the view into the context (this is the snapshot)
    view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
    let snapshot = UIGraphicsGetImageFromCurrentImageContext()

    // End the context (this is required to not leak resources)
    UIGraphicsEndImageContext()

    // Save to photos
    UIImageWriteToSavedPhotosAlbum(snapshot!, nil, nil, nil)

}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
AshishVerma
  • 43
  • 1
  • 5
  • I am not able to add (afterScreenUpdates: true) "As it is not working" on this code - UIGraphicsBeginImageContextWithOptions(size, view.isOpaque, 0) self.view.snapshotView(afterScreenUpdates: true). - The screenshot is not capturing blur effect. But the image quality did improve. – AshishVerma Oct 07 '16 at 16:46
  • I Used this code to implement Blur effect slider - @IBAction func sliderValueChanged(_ sender: UISlider) { animator?.fractionComplete = CGFloat(sender.value) } – AshishVerma Oct 07 '16 at 17:42
  • That's how - @IBOutlet weak var effectView: UIVisualEffectView! var animator: UIViewPropertyAnimator? override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. imageview.image = UIImage(named:"0002-gallery-iceland-waterfall-1.jpg") imagePicker.delegate = se animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { self.effectView.effect = nil } } – AshishVerma Oct 07 '16 at 17:47

2 Answers2

0

You said:

  1. I want to take Screenshot with Blur in it, As blur filter never gets saved in the screenshot.

I wonder if the view being snapshotted might not be the one with the UIVisualEffectView as a subview. Because when I use the code at the end of the answer, the blur effect (and the impact of changing the fractionCompleted) is captured.

  1. The image quality is very low.

If you use UIGraphicsBeginImageContextWithOptions with a scale of zero, it should capture the image at the resolution of the device:

UIGraphicsBeginImageContextWithOptions(size, isOpaque, 0)
  1. I am not able to crop the image.

I personally capture the whole view, and then crop as needed. See UIView extension below.

In Swift 3:

class ViewController: UIViewController {

        var animator: UIViewPropertyAnimator?
        @IBOutlet weak var imageView: UIImageView!

        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)

            let blur = UIBlurEffect(style: .light)
            let effectView = UIVisualEffectView(effect: blur)
            view.addSubview(effectView)
            effectView.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                effectView.leadingAnchor.constraint(equalTo: imageView.leadingAnchor),
                effectView.trailingAnchor.constraint(equalTo: imageView.trailingAnchor),
                effectView.topAnchor.constraint(equalTo: imageView.topAnchor),
                effectView.bottomAnchor.constraint(equalTo: imageView.bottomAnchor)
            ])

            animator = UIViewPropertyAnimator(duration: 0, curve: .linear) { effectView.effect = nil }
        }

        @IBAction func didChangeValueForSlider(_ sender: UISlider) {
            animator?.fractionComplete = CGFloat(sender.value)
        }

        @IBAction func didTapSnapshotButton(_ sender: AnyObject) {
            if let snapshot = view.snapshot(of: imageView.frame) {
                UIImageWriteToSavedPhotosAlbum(snapshot, nil, nil, nil)
            }
        }

    }

    extension UIView {

        /// Create snapshot
        ///
        /// - parameter rect: The `CGRect` of the portion of the view to return. If `nil` (or omitted),
        ///                 return snapshot of the whole view.
        ///
        /// - returns: Returns `UIImage` of the specified portion of the view.

        func snapshot(of rect: CGRect? = nil) -> UIImage? {
            // snapshot entire view

            UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
            drawHierarchy(in: bounds, afterScreenUpdates: true)
            let wholeImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()

            // if no `rect` provided, return image of whole view

            guard let image = wholeImage, let rect = rect else { return wholeImage }

            // otherwise, grab specified `rect` of image

            let scale = image.scale
            let scaledRect = CGRect(x: rect.origin.x * scale, y: rect.origin.y * scale, width: rect.size.width * scale, height: rect.size.height * scale)
            guard let cgImage = image.cgImage?.cropping(to: scaledRect) else { return nil }
            return UIImage(cgImage: cgImage, scale: scale, orientation: .up)
        }

    }
}

Or in Swift 2:

class ViewController: UIViewController {

    var animator: UIViewPropertyAnimator?
    @IBOutlet weak var imageView: UIImageView!

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        let blur = UIBlurEffect(style: .Light)
        let effectView = UIVisualEffectView(effect: blur)
        view.addSubview(effectView)
        effectView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activateConstraints([
            effectView.leadingAnchor.constraintEqualToAnchor(imageView.leadingAnchor),
            effectView.trailingAnchor.constraintEqualToAnchor(imageView.trailingAnchor),
            effectView.topAnchor.constraintEqualToAnchor(imageView.topAnchor),
            effectView.bottomAnchor.constraintEqualToAnchor(imageView.bottomAnchor)
        ])

        animator = UIViewPropertyAnimator(duration: 0, curve: .Linear) { effectView.effect = nil }
    }

    @IBAction func didChangeValueForSlider(sender: UISlider) {
        animator?.fractionComplete = CGFloat(sender.value)
    }

    @IBAction func didTapSnapshotButton(sender: AnyObject) {
        if let snapshot = view.snapshot(of: imageView.frame) {
            UIImageWriteToSavedPhotosAlbum(snapshot, nil, nil, nil)
        }
    }

}

extension UIView {

    /// Create snapshot
    ///
    /// - parameter rect: The `CGRect` of the portion of the view to return. If `nil` (or omitted),
    ///                 return snapshot of the whole view.
    ///
    /// - returns: Returns `UIImage` of the specified portion of the view.

    func snapshot(of rect: CGRect? = nil) -> UIImage? {
        // snapshot entire view

        UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, 0)
        drawViewHierarchyInRect(bounds, afterScreenUpdates: true)
        let wholeImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        // if no `rect` provided, return image of whole view

        guard let rect = rect, let image = wholeImage else { return wholeImage }

        // otherwise, grab specified `rect` of image

        let scale = image.scale
        let scaledRect = CGRect(x: rect.origin.x * scale, y: rect.origin.y * scale, width: rect.size.width * scale, height: rect.size.height * scale)
        guard let cgImage = CGImageCreateWithImageInRect(image.CGImage!, scaledRect) else { return nil }
        return UIImage(CGImage: cgImage, scale: scale, orientation: .Up)
    }

}

So, when I capture four images at four different slider positions, that yields:

enter image description here

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

I am not able to crop the image in the right way, As there is navigation bar and status bar showing with blank (White) background. (Rest of the image crops well).

here is the code -

let top: CGFloat = 70 let bottom: CGFloat = 280

    // The size of the cropped image
    let size = CGSize(width: view.frame.size.width, height: view.frame.size.height - top - bottom)

    // Start the context
    UIGraphicsBeginImageContext(size)

    // we are going to use context in a couple of places
    let context = UIGraphicsGetCurrentContext()!

    // Transform the context so that anything drawn into it is displaced "top" pixels up
    // Something drawn at coordinate (0, 0) will now be drawn at (0, -top)
    // This will result in the "top" pixels being cut off
    // The bottom pixels are cut off because the size of the of the context
    context.translateBy(x: 0, y: 0)

    // Draw the view into the context (this is the snapshot)
    UIGraphicsBeginImageContextWithOptions(size,view.isOpaque, 0)
    self.view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
    let snapshot = UIGraphicsGetImageFromCurrentImageContext()
AshishVerma
  • 43
  • 1
  • 5