我正在尝试捕获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?
答案 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的屏幕截图:
答案 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)
}
}