【问题标题】:How do I take Cropped screenshot with Retina image quality in my snapshot implementation in swift如何在我的快照实现中快速拍摄具有 Retina 图像质量的裁剪屏幕截图
【发布时间】:2016-10-08 12:42:47
【问题描述】:

我正在尝试截取我的 UIView 并裁剪它,将其保存到我的照片库中。当我尝试这样做时,有 3 个冲突。

(1) - 我想拍摄带有模糊的屏幕截图,因为模糊滤镜永远不会保存在屏幕截图中。

(2) - 图像质量非常低。

(3) - 我无法裁剪图像。

这是我的代码 -

@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)

}

【问题讨论】:

  • 我无法在此代码上添加 (afterScreenUpdates: true) “因为它不工作” - UIGraphicsBeginImageContextWithOptions(size, view.isOpaque, 0) self.view.snapshotView(afterScreenUpdates: true) . - 截图没有捕捉到模糊效果。但图像质量确实有所提高。
  • 我用这段代码实现了模糊效果滑块 - @IBAction func sliderValueChanged(_ sender: UISlider) { animator?.fractionComplete = CGFloat(sender.value) }
  • 就是这样 - @IBOutlet weak var effectView: UIVisualEffectView! var 动画师:UIViewPropertyAnimator? override func viewDidLoad() { super.viewDidLoad() // 在加载视图后做任何额外的设置,通常是从一个笔尖。 imageview.image = UIImage(named:"0002-gallery-iceland-waterfall-1.jpg") imagePicker.delegate = se animator = UIViewPropertyAnimator(duration: 1, curve: .linear) { self.effectView.effect = nil } }

标签: screenshot swift3 crop image-quality


【解决方案1】:

你说:

  1. 我想拍摄带有模糊的屏幕截图,因为模糊滤镜永远不会保存在屏幕截图中。

我想知道被快照的视图是否可能不是以UIVisualEffectView 作为子视图的视图。因为当我使用答案末尾的代码时,会捕捉到模糊效果(以及更改fractionCompleted 的影响)。

  1. 图像质量非常低。

如果您使用比例为零的UIGraphicsBeginImageContextWithOptions,它应该以设备的分辨率捕获图像:

UIGraphicsBeginImageContextWithOptions(size, isOpaque, 0)
  1. 我无法裁剪图像。

我亲自捕捉整个视图,然后根据需要进行裁剪。请参阅下面的UIView 扩展名。

在 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)
        }

    }
}

或者在 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)
    }

}

因此,当我在四个不同的滑块位置捕获四张图像时,会产生:

【讨论】:

  • 我还有一个关于裁剪图像的问题,将问题发布在下面的答案中。
【解决方案2】:

我无法以正确的方式裁剪图像,因为导航栏和状态栏显示为空白(白色)背景。 (图像的其余部分裁剪良好)。

这里是代码 -

让顶部:CGFloat = 70 让底部: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()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-03
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多