UIViewを画像に変換する方法


94

UIViewを画像に変換してアプリに保存したい。ビューのスクリーンショットを撮る方法、またはビューに画像を変換する方法と、それをアプリ(カメラロールではない)に保存する最良の方法は何ですか?ビューのコードは次のとおりです。

var overView   = UIView(frame: CGRectMake(0, 0, self.view.frame.width/1.3, self.view.frame.height/1.3))
overView.center = CGPointMake(CGRectGetMidX(self.view.bounds),
CGRectGetMidY(self.view.bounds)-self.view.frame.height/16);
overView.backgroundColor = UIColor.whiteColor()
self.view.addSubview(overView)
self.view.bringSubviewToFront(overView)

回答:


189

上の拡張機能でUIViewうまくいくはずです。

extension UIView {

    // Using a function since `var image` might conflict with an existing variable
    // (like on `UIImageView`)
    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }
}

Apple UIGraphicsBeginImageContextは、P3色域の導入により、iOS 10の使用を推奨していません。UIGraphicsBeginImageContextsRGBおよび32ビットのみです。彼らUIGraphicsImageRendererは、完全にカラー管理され、ブロックベースで、PDFと画像のサブクラスを持ち、コンテキストの有効期間を自動的に管理する新しいAPIを導入しました。詳細については、WWDC16セッション205を確認してください(画像のレンダリングは11:50頃に始まります)

すべてのデバイスで動作することを確認するに#availableは、iOSの以前のバージョンへのフォールバックで使用します。

extension UIView {

    // Using a function since `var image` might conflict with an existing variable
    // (like on `UIImageView`)
    func asImage() -> UIImage {
        if #available(iOS 10.0, *) {
            let renderer = UIGraphicsImageRenderer(bounds: bounds)
            return renderer.image { rendererContext in
                layer.render(in: rendererContext.cgContext)
            }
        } else {
            UIGraphicsBeginImageContext(self.frame.size)
            self.layer.render(in:UIGraphicsGetCurrentContext()!)
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return UIImage(cgImage: image!.cgImage!)
        }
    }
}

9
これは正解としてマークする必要があります。他のすべてのオプションを使用すると、レンダリングされたイメージの鮮明さが失われ、ピクセル化/ぼやけて見えます。
TuplingD 2017

唯一正しい方法!初心者向けの使用法は次のとおりです。let image = customView.asImage()
Fabio

1
透明な背景でUIViewをレンダリングすることは可能ですか?鉱山の角は丸くなっています。
ixany 2018

@ixanyこれですでに処理されているはずです。私は遊び場で次でそれを試してみました、それが働いた: let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)); view.backgroundColor = .black; view.layer.cornerRadius = 9; view.asImage();
Naveed J.

2
ありがとうございました!誰かが知っているなら、私はhackingwithswift.com/example-code/media/…からのコードとどのような違いがあるのか​​知りたいと思います:return renderer.image {ctx in drawHierarchy(in:bounds、afterScreenUpdates:true)}
Kqtr

63

あなたは拡張機能を使うことができます

extension UIImage {
    convenience init(view: UIView) {
        UIGraphicsBeginImageContext(view.frame.size)
        view.layer.render(in:UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: image!.cgImage!)
    }
}

ここで迅速な3/4バージョン:

extension UIImage {
    convenience init(view: UIView) {
        UIGraphicsBeginImageContext(view.frame.size)
        view.layer.render(in:UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: image!.cgImage!)
    }
}

5
これを尋ねるのが恥ずかしいですが、どうやってこれを呼ぶのですか?画像がなく、画像にしたいUIViewがありません。上記のコードは、UIImage拡張機能を提供します...しかし、今は何ですか?
Dave G

8
let image = UIImage(view: myView)
doctorBroctor

UIGraphicsGetCurrentContext()がnilになる可能性があり、コードがクラッシュするため、私にとっては機能しません。
ファンカルロスオスピナゴンザレス2016

@JuanCarlosOspinaGonzalez UIGraphicsGetCurrentContextがnilを返した場合、UIGraphicsBeginImageContextをチェックして、失敗した理由を理解する必要があると思います。私の推測では、あなたのサイズの次元の1つがゼロであるか、それ以外の場合は無効ですが、何が原因で失敗するのか実際にはわかりません。
John Stephen

2
私はこれで初期化子の最初の行を置き換えますので、あなたも、念頭に置いて、画面のスケールを維持する必要があります: UIGraphicsBeginImageContextWithOptions(view.frame.size, false, UIScreen.main.scale)
iVentis

41

drawViewHierarchyInRect:afterScreenUpdates:によってUIViewを画像に変換しますこれはrenderInContextよりも何倍も高速です。

重要な注意:viewDidLoadまたはviewWillAppearからこの関数を呼び出さないでください。完全に表示/ロードされた後、ビューをキャプチャしていることを確認してください

Obj C

     UIGraphicsBeginImageContextWithOptions(myView.bounds.size, myView.opaque, 0.0f);
     [myView drawViewHierarchyInRect:myView.bounds afterScreenUpdates:NO];
     UIImage *snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext();
     UIGraphicsEndImageContext();

     myImageView.image =  snapshotImageFromMyView;

編集した画像を保存フォトアルバム

     UIImageWriteToSavedPhotosAlbum(snapshotImageFromMyView, nil,nil, nil);

スイフト3/4

    UIGraphicsBeginImageContextWithOptions(myView.bounds.size, myView.isOpaque, 0.0)
    myView.drawHierarchy(in: myView.bounds, afterScreenUpdates: false)
    let snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    print(snapshotImageFromMyView)
    myImageView.image = snapshotImageFromMyView

拡張、iOS11、swift3 / 4による超簡単な汎化

extension UIImage{
    convenience init(view: UIView) {

    UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
    view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    self.init(cgImage: (image?.cgImage)!)

  }
}


Use : 
 //myView is completly loaded/visible , calling this code after only after viewDidAppear is call
 imgVV.image = UIImage.init(view: myView)
 // Simple image object
 let img =  UIImage.init(view: myView)

3
うまくいきません。UiImageのブラックスクリーンを取得しました
Badr Filali 2017年

本当に ??ここにコードを貼り付けることはできますか?Uが間違ったUIViewオブジェクトを使用してUIImageとして変換している可能性があります。
ViJay Avhad 2017年

`func convertViewIntoImg()-> Void {imgFakeCard.image = UIImage.init(view:card)}` Swift 3の拡張機能では、この関数はviewDidLoadで呼び出されます
Badr Filali

1
コードを投稿し、viewDidLoadで呼び出したことをお伝えいただきありがとうございます。これがあなたが間違ったことです。メモリに読み込まれる前にビューのスナップショットをキャプチャしています。問題を確実に解決するviewDidAppearでコードを呼び出してみてください。質問があれば返信してください。
ViJay Avhad 2017年

問題なく動作しました。問題は私が拡張機能を呼び出したところから来ました
Badr Filali 2017年

20

iOS 10の場合:

extension UIImage {
    convenience init(view: UIView) {
        UIGraphicsBeginImageContext(view.frame.size)
        view.layer.render(in: UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: (image?.cgImage)!)
    }
}

1
これはクラッシュにつながります。
KingPolygon 2017

18

iOS 10およびSwift 3のベストプラクティス

iOS 9以前をサポートしながら 、iOS 13、Xcode 11.1、Swift 5.1以降でも機能します

extension UIView {

    func asImage() -> UIImage? {
        if #available(iOS 10.0, *) {
            let renderer = UIGraphicsImageRenderer(bounds: bounds)
            return renderer.image { rendererContext in
                layer.render(in: rendererContext.cgContext)
            }
        } else {
            UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0)
            defer { UIGraphicsEndImageContext() }
            guard let currentContext = UIGraphicsGetCurrentContext() else {
                return nil
            }
            self.layer.render(in: currentContext)
            return UIGraphicsGetImageFromCurrentImageContext()
        }
    }
}

質問の意味がわかりません。

アプリ(カメラロールではない)に保存する最良の方法は何ですか?


17
    UIGraphicsBeginImageContext(self.view.bounds.size);        
    self.view.layer.renderInContext(UIGraphicsGetCurrentContext())
    var screenShot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();


1
UIGraphicsGetImageFromCurrentImageContextはUIImageを返します。UIImageにキャストする必要はありません
Leo

これはビュー全体のスクリーンショットを撮りますね?私は、UIView、つまりself.viewのサブビューであるコード内の概要を画像に変換したいだけです。全体ではありません!
Sameer Hussain

@Vakasがimage.itを表示しようとしたとき、表示が小さすぎます。これに対する解決策は
何ですか

6
スクリーンショットの画像の品質が良くありません。どうすればよいですか?
Neela 2017年

13

たとえば、サイズのビューがある場合:50 50 at 100,100。以下を使用してスクリーンショットを撮ることができます:

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), false, 0);
    self.view.drawViewHierarchyInRect(CGRectMake(-50,-5-,view.bounds.size.width,view.bounds.size.height), afterScreenUpdates: true)
    var image:UIImage = UIGraphicsGetImageFromCurrentImageContext();

self.view行でパラメーターに誤りがあります
。--

@sameer質問が1つあります。この画像を印刷すると小さすぎます
Krutarth Patel

7

私の意見では、イニシャライザを使用したアプローチは、2つのイメージを作成するため、それほど優れていません。

私はこれが好きです:

extension UIView {
    var snapshot: UIImage? {
        UIGraphicsBeginImageContext(self.frame.size)
        guard let context = UIGraphicsGetCurrentContext() else {
            return nil
        }
        layer.render(in: context)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}

プロパティは画像以外のものと呼ぶ必要があります。これは、UIImageViewなど、画像と呼ばれるプロパティを持つサブクラスを妨害する可能性があります。
ホッブス

@HobbestheTigeの提案に従い、プロパティの名前を変更しました
Tino

6

Swift 4.2、iOS 10

extension UIView {

    // If Swift version is lower than 4.2, 
    // You should change the name. (ex. var renderedImage: UIImage?)

    var image: UIImage? {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in layer.render(in: rendererContext.cgContext) }
    }
}

サンプル

let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view.backgroundColor = .blue

let view2 = UIView(frame: CGRect(x: 10, y: 10, width: 20, height: 20))
view2.backgroundColor = .red
view.addSubview(view2)

let imageView = UIImageView(image: view.image)

ここに画像の説明を入力してください


プロジェクトでUIImageViewsを使用している場合は、別の名前を付ける必要があることを指摘したいだけです。それ以外の場合、.imageは読み取り専用になります。「イメージ」ではなく「screenCapture」と呼ぶかもしれません。
Chris

@クリスうん、あなたは正しいです。Swiftのバージョンが4.2より前の場合は、名前を変更する必要があります。間違いを指摘していただきありがとうございます。
Den

6

これは、Xcode 9 / Swift 3.2 / Swift 4およびXcode 8 / Swift 3で機能します

 if #available(iOS 10.0, *) {

     // for Xcode 9/Swift 3.2/Swift 4 -Paul Hudson's code
     let renderer = UIGraphicsImageRenderer(size: view!.bounds.size)
     let capturedImage = renderer.image { 
         (ctx) in
         view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: true)
     }
     return capturedImage

 } else {

     // for Xcode 8/Swift 3
     UIGraphicsBeginImageContextWithOptions((view!.bounds.size), view!.isOpaque, 0.0)
     view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: false)
     let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
     UIGraphicsEndImageContext()
     return capturedImage!
 }

関数内での使用方法は次のとおりです。

fileprivate func captureUIImageFromUIView(_ view:UIView?) -> UIImage {

     guard (view != nil) else{

        // if the view is nil (it's happened to me) return an alternative image
        let errorImage = UIImage(named: "Error Image")
        return errorImage
     }

     // if the view is all good then convert the image inside the view to a uiimage
     if #available(iOS 10.0, *) {

         let renderer = UIGraphicsImageRenderer(size: view!.bounds.size)
         let capturedImage = renderer.image { 
             (ctx) in
             view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: true)
         }
         return capturedImage

     } else {

         UIGraphicsBeginImageContextWithOptions((view!.bounds.size), view!.isOpaque, 0.0)
         view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: false)
         let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
         UIGraphicsEndImageContext()
         return capturedImage!
     }
}

関数から返された画像を使用して何かを行う方法は次のとおりです。

@IBOutlet weak fileprivate var myCustomView: UIView!
var myPic: UIImage?

let myImageView = UIImageView()

@IBAction fileprivate func saveImageButtonTapped(_ sender: UIButton) {

   myPic = captureUIImageFromUIView(myCustomView)

   // display the pic inside a UIImageView
   myImageView.image = myPic!
}

Paul HudsonからXcode 9 / Swift 3.2 / Swift 4の回答を得ました。uiviewをuiimageに変換します

Xcode 8 / Swift 3をSOのどこかからかなり前に入手しましたが、どこで忘れたか:(


また、画像はどこに表示および/または保存されますか?
Famic Tech 2018

@FamicTechは、画像が作成された後、それをどのように処理するかを若者が決定します。私のコードの例では、UIViewを「myPic」という名前のUIImageに変換しただけですが、「myPic」では何もしませんでした。次に考えられる手順は、myImageVIew.image = myPicのように、UIImageVIew内に「myPic」を表示することです。collectionViewを使用していて、その中に画像を表示したい場合は、各セルにUIImageVIewを作成し、データソースのindexPath.itemを介してUIImageVIew内に画像(myPic)を表示します。例:myImageView.image = dataSource [indexPath .item] .myPic
ランスサマリア

@FamicTechあなたはわずかな間違いを犯しています。これは、コレクションとは関係ありません。UIImageとUIViewの2つの異なるクラスタイプがあります。どちらも画像の表示に使用できますが、方法は異なります。iOSのほとんどすべてがUIViewのサブクラスですが、UIImageは画像(2つの異なるもの)しか表示できません。この関数は、UIVIewを取得してUIImageに変換します。より簡単な例。4.0(Double)を4(Int)に変換したい場合、それをキャストするとInt(4.0)の結果は4になりますが、これを使用して、UIVIew内の画像をUIImageに変換します。理解する?
ランスサマリア

私がやろうとしていることは、ユーザーに私のコレクションビューコントローラーに来て、現在のビュー(たまたまコレクションビュー10x10)を取得し、10x10グリッド全体の画像を作成するボタンを押すことです(全体がグリッドは完全に表示されません)。上記のコードはそのユースケースで解決しますか?
Famic Tech 2018

ボタンはナビゲーションコントローラにあります。ユーザーがそれをクリックすると、ナビゲーションコントローラーの下に表示されるコレクションビューが表示され、コレクションビューのセル内のすべての情報を含むコレクションビューの画像が作成されます(PDFも同様に機能します)。
Famic Tech 2018

5
var snapshot = overView.snapshotViewAfterScreenUpdates(false)

またはobjective-c

UIView* snapshot = [overView snapshotViewAfterScreenUpdates:NO];

imageViewはimageと同じですか?もしそうなら、どうすればそれを自分のアプリに保存できますか?
Sameer Hussain

4

このような拡張機能を使用することで簡単に使用できます

// Take a snapshot from a view (just one view)
let viewSnapshot = myView.snapshot

// Take a screenshot (with every views in screen)
let screenSnapshot = UIApplication.shared.snapshot

// Take a snapshot from UIImage initialization
UIImage(view: self.view)

これらの拡張メソッド/変数を使用したい場合は、これを実装します

  1. UIImage拡張

    extension UIImage {
        convenience init(view: UIView) {
            if let cgImage = view.snapshot?.cgImage {
                self.init(cgImage: cgImage)
            } else {
                self.init()
            }
        }
    }
  2. UIView拡張

    extension UIView {
    
        var snapshot: UIImage? {
            UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0.0)
            if UIGraphicsGetCurrentContext() != nil {
                drawHierarchy(in: bounds, afterScreenUpdates: true)
                let screenshot = UIGraphicsGetImageFromCurrentImageContext()
                UIGraphicsEndImageContext()
                return screenshot
            }
            return nil
        }
    }
  3. UIApplication拡張

    extension UIApplication {
    
        var snapshot: UIImage? {
            return keyWindow?.rootViewController?.view.snapshot
        }
    }

サブビューのzPositionを操作してビューを表示しましたが、他の回答では正しいレイヤー位置が得られませんでした。ありがとうございます。
Ashkan Ghodrat

3

またはiOS 10以降では、新しいUIGraphicsImageRenderer +推奨されるdrawHierarchyを使用できます。これは、状況によっては、layer.renderInContextよりもはるかに高速になる場合があります。

extension UIView {
    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(size: self.bounds.size)
        return renderer.image { _ in
            self.drawHierarchy(in: CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height), afterScreenUpdates: false)
        }
    }
}

3

@Bao Tuan Diepに感謝します!サプリメントを追加したい。

コードを使用する場合:

yourView.layer.render(in:UIGraphicsGetCurrentContext()!)

次のことに注意する必要があります。

 - If you had used `autoLayout` or `Masonry` in `yourView` (that you want to convert) .
 - If you did not add `yourView` to another view which means that `yourView` was not used as a subview but just an object.

次に、使用する必要があります

[yourView setNeedsLayout];
[yourView layoutIfNeeded];

yourView前に更新するyourView.layer.render(in:UIGraphicsGetCurrentContext()!)

そうしないと、要素を含まない画像オブジェクトを取得する可能性があります


これをありがとう。たくさん助けてくれました!!
Maksim Kniazev

2

Swift 4.2

import Foundation
import UIKit  

extension UIImage {

    convenience init(view: UIView) {

        UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.isOpaque, 0.0)
        view.drawHierarchy(in: view.bounds, afterScreenUpdates: false)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: (image?.cgImage)!)

    } 
}

使用:

let img = UIImage.init(view:self.holderView)


1

Swift 3での実装

以下のコードをクラスのスコープ外に追加します。

extension UIImage {
    convenience init(_ view: UIView) {
        UIGraphicsBeginImageContext(view.frame.size)
        view.layer.render(in: UIGraphicsGetCurrentContext()!)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        self.init(cgImage: (image?.cgImage)!)
    }
}

使用法 :

let image = UIImage( Your_View_Outlet )

エラー「少なくとも一度はレンダリングされていないビュー(0x1040a6970、UIView)を描画する必要があります」
kishu mewara

0

@Naveed Jを実装しました。このようなのメソッド、それは魅力のように機能しました。

ここに彼の拡張がありました:

extension UIView {

    // Using a function since `var image` might conflict with an existing variable
    // (like on `UIImageView`)
    func asImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }
}

ここに私がそれを実装した方法があります。

//create an image from yourView to display
//determine the frame of the view/imageimage
let screen = self.superview!.bounds
let width = screen.width / 4 //make image 1/4 width of screen
let height = width
let frame = CGRect(x: 0, y: 0, width: width, height: height)
let x = (screen.size.width - frame.size.width) * 0.5
let y = (screen.size.height - frame.size.height) * 0.5
let mainFrame = CGRect(x: x, y: y, width: frame.size.width, height: frame.size.height)

let yourView = YourView() //instantiate yourView
yourView.frame = mainFrame //give it the frame
yourView.setNeedsDisplay() //tell it to display (I am not 100% sure this is needed)

let characterViewImage = yourView.asImage()

0

iOS 10以降で利用可能な新しいUIGraphicsImageRendererを使用した初期化子:

extension UIImage{
    convenience init(view: UIView) {

    let renderer = UIGraphicsImageRenderer(size: self.bounds.size)
    let canvas = CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height)
    let image = renderer.image { _ in
        self.drawHierarchy(in: canvas, afterScreenUpdates: false)
    }
    self.init(cgImage: (image?.cgImage)!)
  }
}

0

私と一緒にうまくいきます!

Swift4

 extension UIView {

    func toImage() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0)
        self.drawHierarchy(in: self.bounds, afterScreenUpdates: false)
        let snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return snapshotImageFromMyView!
    }

    }

return snapshotImageFromMyView!スレッド1:致命的なエラー:オプション値のアンラップ中に予期せずnilが見つかりました
Takasur

0

ビューにぼやけたサブビュー(例:UIVisualEffectViewインスタンス)が含まれている場合、drawViewHierarchyInRect:afterScreenUpdatesのみ機能します。

@ViJay Avhadの答えは、この場合に正しいです。


0

ビューにシーンキットのサブビュー、メタルまたはスプライトキットのサブビューが含まれている場合、UIGraphicsImageRendererの使用は機能しません。このような場合は、

let renderer = UIGraphicsImageRenderer(size: view.bounds.size)
let image = renderer.image { ctx in
    view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
}

-1
please try below code.
-(UIImage *)getMainImageFromContext
{
UIGraphicsBeginImageContextWithOptions(viewBG.bounds.size, viewBG.opaque, 0.0);
[viewBG.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.