WKWebView截图

时间:2014-07-13 22:32:34

标签: swift

我正在尝试捕获webview正在向用户显示的图像,因此我可以对网页进行一些颜色分析。当我试图从它的父母那里获取图像时,我基本上会得到一个白色的盒子,即使页面已呈现:

func makeImageSnapshot()-> (NSImage)
{


    let imgSize = self.view.bounds.size
    let bir = self.viewbitmapImageRepForCachingDisplayInRect(self.webView!.view.bounds)

    bir.size = imgSize
    self.webView.cacheDisplayInRect(self.view.bounds, toBitmapImageRep:bir)

    let image = NSImage(size:imgSize)
    image.addRepresentation(bir)
    self.image = image

    return image
}

func saveSnapshot()
{
    let imgRep = self.image!.representations[0]
    let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: nil)
    data.writeToFile("/tmp/file.png", atomically: false)

}

在我看来,我无法访问webView内部实际视图(在本例中为边界)的属性。当我尝试访问它时,编译器barfs:

/Users/josh/Canary/MacOsCanary/canary/canary/Modules/Overview/Overview.swift:55:37:'(NSView!,stringForToolTip:NSToolTipTag,point:NSPoint,userData:UnsafePointer<() >) - > !字符串'没有名为' bounds'

的成员

我的猜测是,由于OS X和iOS使用的扩展方法,这种情况正在发生。任何想法,还是我应该回到使用旧版WebView?

4 个答案:

答案 0 :(得分:2)

Swift 3

extension WKWebView {
    func screenshot() -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, 0);
        self.drawHierarchy(in: self.bounds, afterScreenUpdates: true);
        let snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return snapshotImage;
    }
}

注意:此解决方案仅适用于iOS。

答案 1 :(得分:1)

我意识到这个问题适用于Mac OS X,但我在搜索iOS解决方案时找到了这个页面。我的答案在Mac OS X上不起作用,因为drawViewHierarchyInRect()API调用目前仅限iOS,但我把它放在这里供其他iOS搜索者参考。

这个Stackoverflow answer在iOS 8上使用WKWebView为我解决了这个问题。该答案的示例代码在Objective-C中,但Swift等效于UIView子类或扩展中的代码将沿着下面的代码行。代码忽略了drawViewHierarchyInRect()的返回值,但您可能需要注意它。

func imageSnapshot() -> UIImage
{
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, 0);
    self.drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true);
    let snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return snapshotImage;
}

答案 2 :(得分:0)

今天发现自己在同一条船上,但找到了一个解决方案(通过使用私有API)。

如果您没有定位到App Store并且通常不怕使用私有API,那么可以在OS X上捕获WKWebView的屏幕截图:

https://github.com/lemonmojo/WKWebView-Screenshot

答案 3 :(得分:0)

您将需要访问目标可写位置-snapshotURL,即,例如桌面,因此我们提供了一个处理程序:

func registerSnaphotsURL(_ sender: NSMenuItem, handler: @escaping (URL) -> Void) {
    var targetURL : URL

    //  1st around authenticate and cache sandbox data if needed
    if isSandboxed, desktopData == nil {
        targetURL =
            UserSettings.SnapshotsURL.value.count == 0
                ? getDesktopDirectory()
                : URL.init(fileURLWithPath: UserSettings.SnapshotsURL.value, isDirectory: true)
        
        let openPanel = NSOpenPanel()
        openPanel.message = "Authorize access to "
        openPanel.prompt = "Authorize"
        openPanel.canChooseFiles = false
        openPanel.canChooseDirectories = true
        openPanel.canCreateDirectories = true
        openPanel.directoryURL = targetURL
        openPanel.begin() { (result) -> Void in
            if (result == .OK) {
                targetURL = openPanel.url!
                
                //  Since we do not have data, clear any bookmark
                
                if self.storeBookmark(url: targetURL, options: self.rwOptions) {
                    self.desktopData = self.bookmarks[targetURL]
                    UserSettings.SnapshotsURL.value = targetURL.absoluteString
                    if !self.saveBookmarks() {
                        print("Yoink, unable to save snapshot bookmark")
                    }

                    self.desktopData = self.bookmarks[targetURL]
                    handler(targetURL)
                }
            }
            else
            {
                return
            }
        }
    }
    else
    {
        targetURL =
            UserSettings.SnapshotsURL.value.count == 0
                ? getDesktopDirectory()
                : URL.init(fileURLWithPath: UserSettings.SnapshotsURL.value, isDirectory: true)
        handler(targetURL)
    }
}

我们希望允许单个(视图控制器)和所有当前视图(应用程序委托),因此它们各自文件中的两个动作均使用寄存器处理程序。

应用代理

@objc @IBAction func snapshotAllPress(_ sender: NSMenuItem) {
    registerSnaphotsURL(sender) { (snapshotURL) in
        //  If we have a return object just call them, else notify all
        if let wvc : WebViewController = sender.representedObject as? WebViewController {
            sender.representedObject = snapshotURL
            wvc.snapshot(sender)
        }
        else
        {
            sender.representedObject = snapshotURL
            let notif = Notification(name: Notification.Name(rawValue: "SnapshotAll"), object: sender)
            NotificationCenter.default.post(notif)
        }
    }
}

视图控制器

func viewDidLoad() {
    NotificationCenter.default.addObserver(
        self,
        selector: #selector(WebViewController.snapshotAll(_:)),
        name: NSNotification.Name(rawValue: "SnapshotAll"),
        object: nil)
}

@objc func snapshotAll(_ note: Notification) {
    snapshot(note.object as! NSMenuItem)
}

查看单例动作

@objc @IBAction func snapshotPress(_ sender: NSMenuItem) {
    guard let url = webView.url, url != webView.homeURL else { return }
    guard let snapshotURL = sender.representedObject as? URL else {
        //  Dispatch to app delegate to handle a singleton
        sender.representedObject = self
        appDelegate.snapshotAllPress(sender)
        return
    }
    
    sender.representedObject = snapshotURL
    snapshot(sender)
}

webView交互以捕获图像

@objc func snapshot(_ sender: NSMenuItem) {
    guard let url = webView.url, url != webView.homeURL else { return }
    guard var snapshotURL = sender.representedObject as? URL else { return }
    
    //  URL has only destination, so add name and extension
    let filename = String(format: "%@ Shapshot at %@",
                          (url.lastPathComponent as NSString).deletingPathExtension,
                          String.prettyStamp())
    snapshotURL.appendPathComponent(filename)
    snapshotURL = snapshotURL.appendingPathExtension("png")
    
    webView.takeSnapshot(with: nil) { image, error in
        if let image = image {
            self.webImageView.image = image
            DispatchQueue.main.async {
                self.processSnapshotImage(image, to: snapshotURL)
            }
        }
        else
        {
            self.userAlertMessage("Failed taking snapshot", info: error?.localizedDescription)
            self.webImageView.image = nil
        }
    }
}

并捕获到目标区域

func processSnapshotImage(_ image: NSImage, to snapshotURL: URL) {
    guard let tiffData = image.tiffRepresentation else { NSSound(named: "Sosumi")?.play(); return }
    let bitmapImageRep = NSBitmapImageRep(data: tiffData)

    do
    {
        try bitmapImageRep?.representation(using: .png, properties: [:])?.write(to: snapshotURL)
        // https://developer.apple.com/library/archive/qa/qa1913/_index.html
        if let asset = NSDataAsset(name:"Grab") {

            do {
                // Use NSDataAsset's data property to access the audio file stored in Sound.
                let player = try AVAudioPlayer(data:asset.data, fileTypeHint:"caf")
                // Play the above sound file.
                player.play()
            } catch {
                print("no sound for you")
            }
        }
        if snapshotURL.hideFileExtensionInPath(), let name = snapshotURL.lastPathComponent.removingPercentEncoding {
            print("snapshot => \(name)")
        }
    } catch let error {
        appDelegate.userAlertMessage("Snapshot failed", info: error.localizedDescription)
    }
}