Capturing WKWebview is difficult along with your view’s layer, as WebKit lives in a standalone course of. Fortunately, there’s a devoted API that captures screenshot from WKWebview
takeSnapshotWithConfiguration:completionHandler:
In OC, you need to use it like this:
- (void)captureWebViewSnapshot:(WKWebView *)webView {
if (!webView) { return; }
// Optionally available: guard in opposition to capturing whereas loading
if (webView.isLoading) {
// Present an alert/toast as wanted and return
return;
}
WKSnapshotConfiguration *config = [[WKSnapshotConfiguration alloc] init];
config.rect = webView.bounds; // viewport coordinates
[webView takeSnapshotWithConfiguration:config
completionHandler:^(UIImage * _Nullable image, NSError * _Nullable error) {
if (error) {
// Handle error (e.g., show alert with error.localizedDescription)
return;
}
if (!image) {
// Handle empty image
return;
}
// TODO: SAVE IMAGE HERE.
}];
}
Right here is the Full working code in Swift
import SwiftUI
import WebKit
import Pictures
struct ContentView: View {
@State personal var showingAlert = false
@State personal var alertMessage = ""
var physique: some View {
ZStack {
// WebView
WebView()
.ignoresSafeArea()
// Seize Button
VStack {
HStack {
Spacer()
Button(motion: captureWebView) {
Picture(systemName: "digicam.fill")
.font(.title2)
.foregroundColor(.white)
.body(width: 50, top: 50)
.background(Shade.blue)
.clipShape(Circle())
.shadow(radius: 5)
}
.padding(.trailing, 20)
}
Spacer()
}
.padding(.prime, 50)
}
.alert("Seize Consequence", isPresented: $showingAlert) {
Button("OK") { }
} message: {
Textual content(alertMessage)
}
}
personal func captureWebView() {
guard let webView = webView else {
alertMessage = "WebView not prepared"
showingAlert = true
return
}
// Await the web page to load if it is nonetheless loading
if webView.isLoading {
alertMessage = "Please anticipate the web page to complete loading"
showingAlert = true
return
}
let configuration = WKSnapshotConfiguration()
configuration.rect = webView.bounds
webView.takeSnapshot(with: configuration) { picture, error in
if let error = error {
alertMessage = "Did not seize: (error.localizedDescription)"
showingAlert = true
return
}
guard let picture = picture else {
alertMessage = "Did not generate picture"
showingAlert = true
return
}
saveImageToPhotoLibrary(picture)
}
}
/// Saves images to picture library
/// - Parameter picture: picture
/// - NOTE: It is advisable to add `NSPhotoLibraryUsageDescription` and `NSPhotoLibraryAddUsageDescription` in information.plist
personal func saveImageToPhotoLibrary(_ picture: UIImage) {
PHPhotoLibrary.requestAuthorization { standing in
DispatchQueue.fundamental.async {
swap standing {
case .approved, .restricted:
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAsset(from: picture)
}) { success, error in
DispatchQueue.fundamental.async {
if success {
alertMessage = "Picture saved to picture library efficiently!"
} else if let error = error {
alertMessage = "Failed to save lots of picture: (error.localizedDescription)"
} else {
alertMessage = "Failed to save lots of picture"
}
showingAlert = true
}
}
case .denied, .restricted:
alertMessage = "Photograph library entry denied"
showingAlert = true
case .notDetermined:
alertMessage = "Photograph library entry not decided"
showingAlert = true
@unknown default:
alertMessage = "Unknown picture library standing"
showingAlert = true
}
}
}
}
}
var webView: WKWebView?
struct WebView: UIViewRepresentable {
func makeUIView(context: Context) -> WKWebView {
let configuration = WKWebViewConfiguration()
configuration.allowsInlineMediaPlayback = true
webView = WKWebView(body: .zero, configuration: configuration)
// Load a default webpage
if let url = URL(string: "https://www.apple.com") {
let request = URLRequest(url: url)
webView!.load(request)
}
return webView!
}
func updateUIView(_ uiView: WKWebView, context: Context) {
// Replace UI if wanted
}
}
#Preview {
ContentView()
}