web-dev-qa-db-ja.com

UIViewの一部のスクリーンショットを撮る方法は?

Swiftでプログラムでボタンを押した後、ユーザーが自分のアプリにアクセスしてアプリのスクリーンショットを撮ってほしい。 UIGraphicsGetImageFromCurrentImageContext()がスクリーンショットを撮ることは知っていますが、画面全体の写真は必要ありません。長方形をポップアップさせたいのですが(切り抜きツールのようなものです)、ユーザーは長方形をドラッグしてサイズを変更し、画面の特定の部分のみのスクリーンショットを撮ることができます。長方形がWKWebViewを越えて、Webビューの写真を切り抜いてほしい。

11
Chris

標準のスナップショット手法は drawHierarchy(in:afterScreenUpdates:) であり、それを画像コンテキストに描画します。 iOS 10以降では、 UIGraphicsImageRenderer :を使用できます。

_extension UIView {

    /// Create image snapshot of view.
    ///
    /// - Parameters:
    ///   - rect: The coordinates (in the view's own coordinate space) to be captured. If omitted, the entire `bounds` will be captured.
    ///   - afterScreenUpdates: A Boolean value that indicates whether the snapshot should be rendered after recent changes have been incorporated. Specify the value false if you want to render a snapshot in the view hierarchy’s current state, which might not include recent changes. Defaults to `true`.
    ///
    /// - Returns: The `UIImage` snapshot.

    func snapshot(of rect: CGRect? = nil, afterScreenUpdates: Bool = true) -> UIImage {
        return UIGraphicsImageRenderer(bounds: rect ?? bounds).image { _ in
            drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
        }
    }
}
_

そして、あなたはそのようにそれを使うでしょう:

_let image = webView.snapshot(of: rect)
_

IOS 10より前は、画像の一部を取得するため、CGImageメソッド cropping(to:) を使用できます。例えば。:

_extension UIView {

    /// Create snapshot
    ///
    /// - Parameters:
    ///   - rect: The coordinates (in the view's own coordinate space) to be captured. If omitted, the entire `bounds` will be captured.
    ///   - afterScreenUpdates: A Boolean value that indicates whether the snapshot should be rendered after recent changes have been incorporated. Specify the value false if you want to render a snapshot in the view hierarchy’s current state, which might not include recent changes. Defaults to `true`.
    ///
    /// - Returns: Returns `UIImage` of the specified portion of the view.

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

        UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0)
        drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
        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

        guard let cgImage = image.cgImage?.cropping(to: rect * image.scale) else { return nil }
        return UIImage(cgImage: cgImage, scale: image.scale, orientation: .up)
    }

}
_

この少し便利な演算子を使用します:

_extension CGRect {
    static func * (lhs: CGRect, rhs: CGFloat) -> CGRect {
        return CGRect(x: lhs.minX * rhs, y: lhs.minY * rhs, width: lhs.width * rhs, height: lhs.height * rhs)
    }
}
_

そしてそれを使用するには、次のことができます。

_if let image = webView.snapshot(of: rect) {
    // do something with `image` here
}
_

Swift 2レンディションについては、 この回答の以前のリビジョン を参照してください。

31
Rob

これは以前に尋ねられた質問です 網膜ディスプレイの品質を損なうことなくUIViewをUIImageにキャプチャする方法 しかしSwift(2.3):

_extension UIView {

    class func image(view: UIView) -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0)
        guard let ctx = UIGraphicsGetCurrentContext() else {
            return nil
        }
        view.layer.renderInContext(ctx)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return img
    }

    func image() -> UIImage? {
        return UIView.image(self)
    }
}
_

したがって、UIView.image(theView)を使用してビューから画像を取得するか、ビュー自体にlet viewImage = self.view.image()を要求することができます。

これは大まかなことであり、おそらくスレッドセーフなどをさらに検討する必要があることを覚えておいてください。

3
Adrian Sluyters