I have a Cocoa App which uses WebView
to load some pages and take a screenshot.
Cocoa App Code:
class ViewController: NSViewController, WebFrameLoadDelegate {
@IBOutlet var web: WebView!
override func viewDidLoad() {
super.viewDidLoad()
web.frame = NSRect(x: 0, y: 0, width: 1024, height: 768)
web.preferences.isJavaScriptEnabled = true
web.preferences.isJavaEnabled = true
web.preferences.arePlugInsEnabled = true
web.frameLoadDelegate = self
web.mainFrame.load(URLRequest(url: URL(string: "https://google.com")!))
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func webView(_ sender: WebView!, didFinishLoadFor frame: WebFrame!) {
let queue = DispatchQueue(label: "pic")
let delay: DispatchTimeInterval = .seconds(2)
queue.asyncAfter(deadline: .now() + delay, execute: {
let data = self.web.layer?.data() // data() is a extension on CALayer
let nd = NSData(data: data!)
nd.write(toFile: "/Users/mac/Desktop/screenshot.png", atomically: true)
})
}
}
It works fine and I get a screenshot of the webpage.
Now, I thought of doing it in command-line instead of Cocoa App.
Command-line Code:
class Main1: NSObject, WebFrameLoadDelegate
{
var web: WebView!
override init()
{
super.init()
web = WebView(frame: CGRect(x: 0, y: 0, width: 1024, height: 768))
web.preferences.isJavaScriptEnabled = true
web.preferences.isJavaEnabled = true
web.preferences.arePlugInsEnabled = true
web.frameLoadDelegate = self
web.shouldUpdateWhileOffscreen = true
web.mainFrame.load(URLRequest(url: URL(string: "https://google.com")!))
}
func webView(_ sender: WebView!, didFinishLoadFor frame: WebFrame!)
{
let queue = DispatchQueue(label: "pic")
let delay: DispatchTimeInterval = .seconds(2)
queue.asyncAfter(deadline: .now() + delay, execute: {
let data = self.web.layer?.data()
let nd = NSData(data: data!) // <-- The data is nil error
nd.write(toFile: "/Users/mac/Desktop/screenshot.png", atomically: true)
})
}
}
extension CALayer {
/// Get `Data` representation of the layer.
///
/// - Parameters:
/// - fileType: The format of file. Defaults to PNG.
/// - properties: A dictionary that contains key-value pairs specifying image properties.
///
/// - Returns: `Data` for image.
func data(using fileType: NSBitmapImageFileType = .PNG, properties: [String : Any] = [:]) -> Data {
let width = Int(bounds.width * self.contentsScale)
let height = Int(bounds.height * self.contentsScale)
let imageRepresentation = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: width, pixelsHigh: height, bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSDeviceRGBColorSpace, bytesPerRow: 0, bitsPerPixel: 0)!
imageRepresentation.size = bounds.size
let context = NSGraphicsContext(bitmapImageRep: imageRepresentation)!
render(in: context.cgContext)
return imageRepresentation.representation(using: fileType, properties: properties)!
}
}
let m = Main1()
RunLoop.main.run()
I'm getting fatal error: unexpectedly found nil while unwrapping an Optional value
.
Why can't I use same code for command-line? What might be the reason?
How can it be solved?
Thank you!
P.S. The extension for CALayer
is taken from here