Swift for OsX WebFrameLoadDelegate doesn't works - macos

I am new in OSX developing and i am new in Swift. I want to write simple app with WebView.
I can load url, but I need to catch event when, WebView end loading content.
it's my app delegate.swift file:
import WebKit
class AppDelegate: NSObject, NSApplicationDelegate{
#IBOutlet weak var window: NSWindow!
#IBOutlet var webview: WebView!
func applicationDidFinishLaunching(aNotification: NSNotification?) {
let url = NSURL(string: "google.com")
let request = NSURLRequest(URL: url);
webview.mainFrame.loadRequest(request);
}
func applicationWillTerminate(aNotification: NSNotification?) {
}
}
and i try to make WebFrameLoad delegate class:
import WebKit
class WebViewControllerDelegate: WebFrameLoadDelegate{
#IBOutlet var webview: WebView!
override func didFinishLoadForFrame()
{
println("ok:");
}
}
It doesn't work. And also i don't know how to set this Delegate to my WebView.
TY for answers.

Generally speaking the WebViewControllerDelegate would be the class to have the webView as a property.
Move this code:
#IBOutlet var webview: WebView!
let url = NSURL(string: "google.com")
let request = NSURLRequest(URL: url)
self.webview.mainFrame.loadRequest(request)
to your WebViewControllerDelegate
You then also need to set the delegate for the WebView in your WebViewControllerDelegate
self.webView.delegate = self
If you don't set this, then the system will not call the delegate methods for you.
Finally you need to ensure that your class conforms to the WebFrameLoadDelegate protocol (which you have already done).
class WebViewControllerDelegate: WebFrameLoadDelegate{
The above means that you have a viewController which has a webView property, is conforming to the correct delegate protocol, and is itself the delegate for the webView.
Delegate methods defined inside the WebViewControllerDelegate will then be called for you.

Related

Replace NSViewController under Swift2 Storyboard MAC OSX

I am new to Mac OSX and with Apple promoting the fact that the bodies of code are becoming similar decided to tell the folk I am writing code for we should be able to do a Mac OSX version. iPhone and iPad versions are all good and about to release second version so no issues there.
So I am subclassing NSWindowController to get access to the Toolbar and worked out how to remove and add items on the toolbar, but for the life of me I can not get one NSViewController (firstViewController) to dismiss and bring up the second NSViewController (secondViewController) in the same NSWindowController.
So the 2 issues are that
1. I want to be able to performSegueWithIdentifier from the first NSViewController in code and
2. bring up the second NSViewController by replacing the first NSViewController in the same NSWindowController.
If I add a button to the firstViewController and put a segue to the secondViewController then when I select the button the secondViewController comes up just fine but in a seperate window not the same NSWindowController that I want it to and the firstViewController does not get replaced but stays in the NSWindowController.
So I know the segue idea will work but its not working in code and when I do insert the segue from a button it works but into a seperate NSViewController that is not part of the NSWindowController.
I am trying to find some programming guide from Apple on the issue but no luck so far.
Here is an overview from my Storyboard:
Here is my NSWindowController subclassed and the func loginToMe2Team is trigger from the NSToolBar and its working just find as the print statements show up on the console.
import Cocoa
class me2teamWindowsController: NSWindowController {
#IBOutlet var mySignUp : NSToolbarItem!
#IBOutlet var myToolbar : NSToolbar!
let controller = ViewController()
override func windowDidLoad() {
super.windowDidLoad()
print("window loaded")
}
override func windowWillLoad() {
print("window will load")
}
#IBAction func logInToMe2Team(sender: AnyObject){
controller.LogIn() //THIS IS THE FUNC I AM TESTING WITH
}
#IBAction func signUpToMe2Team(sender: AnyObject){
controller.signUp()
}
Here is my NSViewController subclassed with the func LogIn. Its getting selected just fine but the performSegueWithIdentifier is not. And I did cut and past the Identifier to make absolutely sure it was the same.
import Cocoa
import WebKit
class ViewController: NSViewController {
#IBOutlet weak var theWebPage: WebView!
#IBOutlet weak var progressIndicator: NSProgressIndicator!
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "https://thewebpage.com.au"
self.theWebPage.mainFrame.loadRequest(NSURLRequest(URL: NSURL(string: urlString)!))
}
override func viewDidAppear() {
}
func LogIn() {
print("I logged in")
self.performSegueWithIdentifier("goToTeamPage", sender: self)
//THIS IS THE BIT THATS NOT WORKING
}
func signUp() {
print("I have to sign up now")
}
override var representedObject: AnyObject? {
didSet {
}
}
func webView(sender: WebView!, didStartProvisionalLoadForFrame frame: WebFrame!)
{
self.progressIndicator.startAnimation(self)
}
func webView(sender: WebView!, didFinishLoadForFrame frame: WebFrame!)
{
self.progressIndicator.stopAnimation(self)
}
}
You need to use a custom segue class (or possibly NSTabViewController if it’s enough for your needs). Set the segue’s type to Custom, with your class name specified:
…and implement it. With no animation, it’s simple:
class ReplaceSegue: NSStoryboardSegue {
override func perform() {
if let src = self.sourceController as? NSViewController,
let dest = self.destinationController as? NSViewController,
let window = src.view.window {
// this updates the content and adjusts window size
window.contentViewController = dest
}
}
}
In my case, I was using a sheet and wanted to transition to a different sheet with a different size, so I needed to do more:
class ReplaceSheetSegue: NSStoryboardSegue {
override func perform() {
if let src = self.sourceController as? NSViewController,
let dest = self.destinationController as? NSViewController,
let window = src.view.window {
// calculate new frame:
var rect = window.frameRectForContentRect(dest.view.frame)
rect.origin.x += (src.view.frame.width - dest.view.frame.width) / 2
rect.origin.y += src.view.frame.height - dest.view.frame.height
// don’t shrink visible content, prevent minsize from intervening:
window.contentViewController = nil
// animate resizing (TODO: crossover blending):
window.setFrame(window.convertRectToScreen(rect), display: true, animate: true)
// set new controller
window.contentViewController = dest
}
}
}

Detection of WebView load finish

I am creating a simple app with a WebView basing on this tutorial.
I would like to display a progress indicator while loading the page, but the methods didStartProvisionalLoadForFrame and didFinishLoadForFrame are never called. What am I doing wrong?
ViewController.swift
import Cocoa
import WebKit
class ViewController: NSViewController {
#IBOutlet weak var webView: WebView!
#IBOutlet weak var webViewProgressIndicator: NSProgressIndicator!
let messengerUrl = "https://www.messenger.com/"
override func viewDidLoad() {
super.viewDidLoad()
self.webView.mainFrame.loadRequest(NSURLRequest(URL: NSURL(string: messengerUrl)!))
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
func webView(sender: WebView!, didStartProvisionalLoadForFrame frame: WebFrame!)
{
self.webViewProgressIndicator.startAnimation(self)
}
func webView(sender: WebView!, didFinishLoadForFrame frame: WebFrame!)
{
self.webViewProgressIndicator.stopAnimation(self)
}
}
I am using Xcode 7 Beta and OS X 10.11 Beta.
Rich's answer works on Xcode 7.0.1 ElCaptain Swift2
BUT I had to connect it in the connections inspector as Swift2 did not accept
self.view.frameLoadDelegate = self
as it gave the following error
Cannot assign a value of type 'ViewController' to a value of type
'WebFrameLoadDelegate!'
So as long as you connect it up as follows, all works great.
The tutorial missed out something, Need to add:
self.webView.frameLoadDelegate = self
(can be done in connections inspector)
then call the load..
self.webView.mainFrame.loadRequest(NSURLRequest(URL: NSURL(string: messengerUrl)!))
Also add the WebFrameLoadDelegate interface to your ViewController.. (thanks tjv)
class ViewController: NSViewController, WebFrameLoadDelegate{ .....
You need to assign the delegate of your UIWebview ie: webview.delegate = self

Cannot replace views in swift for OS X

probably I don't understand how views works in swift. I'm trying to replace view in my LoginViewController but nothing is happening.
#IBAction func loginBtn(sender: AnyObject) {
let authViewController = AuthViewController(nibName: "AuthView", bundle: nil)!
let appDelegate = NSApplication.sharedApplication().delegate as AppDelegate
appDelegate.window.contentView.replaceSubview(self.view, with: authViewController.authView)
}
and my AppDelegate.swift
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
#IBOutlet weak var customView: NSView!
#IBOutlet weak var statusMenu: NSMenu!
func applicationDidFinishLaunching(aNotification: NSNotification) {
let loginViewController = LoginViewController(nibName: "LoginView", bundle: nil)!
window.contentView.addSubview(loginViewController.view)
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
}
I'm not sure but will it help if you tell frame after your replace subview?
let authViewController = AuthViewController(nibName: "AuthView", bundle: nil)!
let appDelegate = NSApplication.sharedApplication().delegate as AppDelegate
appDelegate.window.contentView.replaceSubview(self.view, with: authViewController.authView)
authViewController.view.frame = (appDelegate.window.contentView as! NSView).frame
loginViewController will be deallocated right away as nobody is holding it. Same is true for authViewController
=> VIEWS don't hold their VIEW CONTROLLERS so the views will go away right away
make a member variable to hold on to them

Cannot invoke <method> with and argument list of type '(String)'

Im trying to write an OSX app. A functionality of this app is that it displays the machine IP address.
The address is fetched when the program is opened (AppDelegate.swift):
#NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate {
var ipadd:String = ""
var path:String = ""
var status:String = ""
func applicationDidFinishLaunching(aNotification: NSNotification) {
ipadd = getIFAddress() //<-- ip stored in here as String
println(ipadd) //successfully prints out the ip
ViewController.setIpDisp(ipadd) //error on this line
}
...
}
And in ViewController.swift:
class ViewController: NSViewController {
#IBOutlet weak var ip: NSTextField!
...
func setIpDisp(ipin: String){
ip.stringValue = ipin
}
To be exact, the error is "Cannot invoke 'setIpDisp' with an argument list of type '(String)'
Thanks
The AppDelegate is trying to call a ViewController method that is updating an #IBOutlet in the view controller's view. It needs a valid ViewController instance to do that.
But this is backwards: The app delegate should not be trying to call view controller methods. The view controller can call methods/properties of the app delegate, but the app delegate really shouldn't be calling view controller methods.
If you need to update the IP number field in the view controller, then the view controller should be initiating this (e.g. in viewDidLoad):
class ViewController: NSViewController {
#IBOutlet weak var ip: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
updateIpDisp()
}
func updateIpDisp() {
let appDelegate = NSApplication.sharedApplication().delegate as! AppDelegate
ip.stringValue = appDelegate.getIFAddress()
}
}
Or, if you wanted, the AppDelegate set some ipadd string property in its init method (not applicationDidFinishLaunching), and then the updateIpDisp() method could retrieve the property's value from the app delegate, too. (Given that IP numbers are dynamic and can change, that doesn't seem right to me, but do it however you want.) Anyway, that might look like:
class AppDelegate: NSObject, NSApplicationDelegate {
var ipadd: String!
override init() {
super.init()
ipadd = getIFAddress()
}
}
and
class ViewController: NSViewController {
#IBOutlet weak var ip: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
updateIpDisp()
}
func updateIpDisp() {
let appDelegate = NSApplication.sharedApplication().delegate as! AppDelegate
ip.stringValue = appDelegate.ipadd
}
}
But the view controller should be requesting the IP number from the app delegate and updating its own view. But the app delegate has no business calling methods in the view controller(s).
Your function isn't static, so make sure to initialise an instance of it, like so
#NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate {
let viewController = ViewController()
var ipadd:String = ""
var path:String = ""
var status:String = ""
func applicationDidFinishLaunching(aNotification: NSNotification) {
ipadd = getIFAddress() //<-- ip stored in here as String
println(ipadd) //successfully prints out the ip
viewController.setIpDisp(ipadd) //error on this line
}
...
}

How to pass data from NSWindowController to its NSViewController?

I have a IBOutlet of a NSToolBar button in my NSWindowController class, which is my main window class:
class MainWindowController: NSWindowController {
#IBOutlet weak var myButton: NSButton!
// ...
}
I have a class MainViewController that is that content NSViewController of the main window.
How can I access this button in my content NSViewController? Is there a better way to organize the IBOutlets and the controllers to facilitate this access?
To access NSViewController from NSWindowController:
let viewController:MainViewController = self.window!.contentViewController as! MainViewController
To access NSWindowController from NSViewController:
let windowController:MainWindowController = self.view.window?.windowController as! MainWindowController
How about like this using delegate? This example will change your button's title.
#objc protocol SomeDelegate {
func changeTitle(title: String)
}
class ViewController: NSViewController {
weak var delegate: SomeDelegate?
#IBAction func myAction(sender: AnyObject) {
delegate?.changeTitle("NewTitle")
}
}
class MainWindowController: NSWindowController, SomeDelegate {
#IBOutlet weak var myButton: NSButton!
override func windowDidLoad() {
super.windowDidLoad()
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
let myVc = window!.contentViewController as! ViewController
myVc.delegate = self
}
func changeTitle(title: String) {
myButton.title = title
}
}

Resources