Chcę przekonwertować UIView na obraz i zapisać go w mojej aplikacji. Czy ktoś może mi powiedzieć, jak zrobić zrzut ekranu widoku lub przekonwertować go na obraz i jaki jest najlepszy sposób, aby zapisać go w aplikacji (nie w rolce z aparatu)? Oto kod widoku:
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)
ios
swift
uiview
screenshot
Sameer Hussain
źródło
źródło
Odpowiedzi:
Rozszerzenie na
UIView
powinno załatwić sprawę.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 odradza
UIGraphicsBeginImageContext
uruchamianie iOS 10 wraz z wprowadzeniem gamy kolorów P3.UIGraphicsBeginImageContext
jest sRGB i tylko w wersji 32-bitowej. Wprowadzili nowyUIGraphicsImageRenderer
interfejs API, który jest w pełni zarządzany kolorami, oparty na blokach, ma podklasy dla plików PDF i obrazów oraz automatycznie zarządza okresem życia kontekstu. Sprawdź sesję WWDC16 205, aby uzyskać więcej informacji (renderowanie obrazu zaczyna się około godziny 11:50)Aby mieć pewność, że działa na każdym urządzeniu, użyj
#available
z powrotem do wcześniejszych wersji 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!) } } }
źródło
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)); view.backgroundColor = .black; view.layer.cornerRadius = 9; view.asImage();
możesz użyć rozszerzenia
extension UIImage { convenience init(view: UIView) { UIGraphicsBeginImageContext(view.frame.size) view.layer.render(in:UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() self.init(cgImage: image!.cgImage!) } }
Tutaj szybka wersja 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!) } }
źródło
let image = UIImage(view: myView)
UIGraphicsBeginImageContextWithOptions(view.frame.size, false, UIScreen.main.scale)
Konwertuj swój UIView na obraz przez drawViewHierarchyInRect: afterScreenUpdates: który jest wielokrotnie szybszy niż renderInContext
Ważna uwaga: nie wywołuj tej funkcji z viewDidLoad lub viewWillAppear , upewnij się, że przechwytujesz widok po jego wyświetleniu / załadowaniu w pełni
Obj C
UIGraphicsBeginImageContextWithOptions(myView.bounds.size, myView.opaque, 0.0f); [myView drawViewHierarchyInRect:myView.bounds afterScreenUpdates:NO]; UIImage *snapshotImageFromMyView = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); myImageView.image = snapshotImageFromMyView;
Zapisz edytowany obraz Album fotograficzny
UIImageWriteToSavedPhotosAlbum(snapshotImageFromMyView, nil,nil, nil);
Swift 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
Super łatwa generalizacja z rozszerzeniem, 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)
źródło
W systemie 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)!) } }
źródło
Najlepsza praktyka od iOS 10 i Swift 3
podczas gdy nadal obsługuje iOS 9 i wcześniejsze wersje nadal działa od 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() } } }
Nie jestem pewien, co oznacza to pytanie:
źródło
UIGraphicsBeginImageContext(self.view.bounds.size); self.view.layer.renderInContext(UIGraphicsGetCurrentContext()) var screenShot = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
źródło
Na przykład, jeśli mam widok rozmiaru: 50 50 na 100,100. Mogę zrobić zrzut ekranu:
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();
źródło
Moim zdaniem podejście z inicjatorem nie jest takie świetne, ponieważ tworzy dwa obrazy.
Wolę to:
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 } }
źródło
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) } } }
Próba
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)
źródło
Działa to dla mnie dla Xcode 9 / Swift 3.2 / Swift 4 i 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! }
Oto jak go używać wewnątrz funkcji:
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! } }
Oto jak zrobić coś z obrazem zwróconym przez funkcję:
@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! }
Otrzymałem odpowiedź Xcode 9 / Swift 3.2 / Swift 4 od Paula Hudsona przekonwertować uiview na uiimage
Mam Xcode 8 / Swift 3 skądś na SO dawno temu i zapomniałem gdzie :(
źródło
var snapshot = overView.snapshotViewAfterScreenUpdates(false)
lub w celu-c
UIView* snapshot = [overView snapshotViewAfterScreenUpdates:NO];
źródło
Możesz go łatwo używać, używając takiego rozszerzenia
// 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)
Jeśli chcesz użyć tych metod / zmiennych rozszerzających, zaimplementuj to
Rozszerzenie UIImage
extension UIImage { convenience init(view: UIView) { if let cgImage = view.snapshot?.cgImage { self.init(cgImage: cgImage) } else { self.init() } } }
Rozszerzenie 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 } }
Rozszerzenie UIApplication
extension UIApplication { var snapshot: UIImage? { return keyWindow?.rootViewController?.view.snapshot } }
źródło
lub iOS 10+ możesz użyć nowego UIGraphicsImageRenderer + zalecanej drawHierarchy, która w niektórych sytuacjach może być znacznie szybsza niż 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) } } }
źródło
Dzięki @ Bao Tuan Diep! Chcę dodać suplement.
Kiedy używasz kodu:
yourView.layer.render(in:UIGraphicsGetCurrentContext()!)
Musisz zauważyć, że:
- 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.
Następnie musisz użyć :
zaktualizować
yourView
wcześniejyourView.layer.render(in:UIGraphicsGetCurrentContext()!)
.W przeciwnym razie możesz otrzymać obiekt obrazu, który nie zawiera żadnych elementów
źródło
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)!) } }
źródło
Wdrożenie w Swift 3 :
Dodaj poniższy kod poza zakresem klasy.
extension UIImage { convenience init(_ view: UIView) { UIGraphicsBeginImageContext(view.frame.size) view.layer.render(in: UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() self.init(cgImage: (image?.cgImage)!) } }
Stosowanie :
let image = UIImage( Your_View_Outlet )
źródło
I wdrożone @Naveed J. ten sposób i zadziałała jak urok.
Oto jego zakres:
Oto jak to wdrożyłem.
//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()
źródło
Inicjator z nowym UIGraphicsImageRenderer dostępnym od iOS 10:
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)!) } }
źródło
współpracuj ze mną dobrze!
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! } }
źródło
return snapshotImageFromMyView!
Wątek 1: Błąd krytyczny: nieoczekiwanie znaleziono zero podczas rozpakowywania wartości opcjonalnejW przypadku widoku zawiera rozmyty widok podrzędny (np. Instancja UIVisualEffectView ), tylko drawViewHierarchyInRect: afterScreenUpdates .
Odpowiedź @ViJay Avhad jest prawidłowa w tym przypadku.
źródło
Korzystanie z UIGraphicsImageRenderer nie działa, gdy widok zawiera podglądy zestawu scen, metalowe lub z zestawu Sprite. W takich przypadkach użyj
let renderer = UIGraphicsImageRenderer(size: view.bounds.size) let image = renderer.image { ctx in view.drawHierarchy(in: view.bounds, afterScreenUpdates: true) }
źródło
please try below code. -(UIImage *)getMainImageFromContext { UIGraphicsBeginImageContextWithOptions(viewBG.bounds.size, viewBG.opaque, 0.0); [viewBG.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage * img = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return img; }
źródło