0

I wanted to be able to call this function from two places: When I finish editing a text field, I want to add a new webView when there are none in a stackView, and I also want to be able to use a barButtonItem to do so.

I'm having two problems. when the bar button calls this function, the parameter 'url', becomes an object, type UIBarButtonItem. when it's called from textFieldShouldReturn, it properly comes in as an NSURL. if the user doesn't type anything in the address field, and hits enter, a blank NSURL comes in, and the default value is not used. (i'd like it to be)

what should the call look like from the textfieldShouldReturn function, so that a blank will trigger the default?

how do i handle the fact that either my function or the button will call the function, and why does my named parameter 'url' become what i guess would be 'sender?'

    override func viewDidLoad() {
    super.viewDidLoad()

    setDefaultTitle()

    let add = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: #selector(ViewController.addWebView))
    let delete = UIBarButtonItem(barButtonSystemItem: .Trash, target: self, action: #selector(ViewController.deleteWebView))
    navigationItem.rightBarButtonItems = [delete, add]
}

   func addWebView(url: NSURL = NSURL(string: "https://www.google.com")!) {
    let webView = UIWebView()
    webView.delegate = self
    stackView .addArrangedSubview(webView)
    webView.loadRequest(NSURLRequest(URL: url))

    webView.layer.borderColor = UIColor.blueColor().CGColor
    selectWebView(webView)

    let recognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.webViewTapped))
    recognizer.delegate = self
    webView.addGestureRecognizer(recognizer)
}

   func textFieldShouldReturn(textField: UITextField) -> Bool {
    if let webView = activeWebView, address = addressBar.text {
        if let url = NSURL(string: address) {
            webView.loadRequest(NSURLRequest(URL: url))
        }

    } else if stackView.arrangedSubviews.count == 0 {
         let address = NSURL(string: addressBar.text!)!
        addWebView(address)
    }
    textField.resignFirstResponder()
    return true
}
Dave Kliman
  • 441
  • 4
  • 17

1 Answers1

1

That's right that you are getting sender object which is actually UIBarButtonItem. Have you heard about Target-Action Cocoa pattern? If no, you can read more here: https://developer.apple.com/library/ios/documentation/General/Conceptual/Devpedia-CocoaApp/TargetAction.html

Especially relevant section to you is "An Action Method Must Have a Certain Form".

Consider to introduce addWebView overload:

func addWebView(sender: NSObject) {

    addWebView(url: NSURL(string: "https://www.google.com")!)
}

private func addWebView(url: NSURL) {
    //left as is ...
}

Here is update per Dave's comments. Have to use different name for actual implementation method. Otherwise Swift compiler is failed to resolve the assigned selector name.

Useful code, which demonstrates the problem is attached below:

class Notifier: NSObject {

    private var _target: NSObject!
    private var _action: Selector!

    func addObserver(target: NSObject, action: Selector) {
        _target = target
        _action = action
    }

    func invokeMethod() {

        guard let t = _target else {
            print("target must be set")
            return
        }

        guard let a = _action else {
            print("action must be set")
            return
        }
        if t.respondsToSelector(a) {
            t.performSelector(a, withObject: self)
        }
    }

}

class Observer: NSObject {

    func subscribe(notifier: Notifier) {
        notifier.addObserver(self, action: #selector(Observer.callback))
    }

    func callback(sender: NSObject) {
        callbackImpl(NSURL(string: "https://www.google.com")!)
    }

    private func callbackImpl(url: NSURL) {
        print("url\(url)")
    }
}

//client's code
let n = Notifier()
let o = Observer()
o.subscribe(n)
n.invokeMethod()
Igor B.
  • 2,219
  • 13
  • 17
  • oh. I liked the sound of what you are saying, but Xcode does not. It doesn't work. – Dave Kliman Apr 14 '16 at 08:02
  • Can you be more specific, what exactly does not work? BTW That's important to mark the actual implementation method (which accepts an url) with private keyword. – Igor B. Apr 14 '16 at 08:06
  • ` let add = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: #selector(ViewController.addWebView)) ` stops working. – Dave Kliman Apr 14 '16 at 08:21
  • it works if i just use a different name for the function that the button calls and the one that gets called from the textfield delegate method... then i just do as you did above and call the other one... thing is the textfield will send a NSURL object with a "" string, which doesn't trigger the default, so i could just have an if statement to get a default but i was trying to be more swifty. – Dave Kliman Apr 14 '16 at 08:41
  • Yes, you are right. I just played with that more in order to get deeper into your problem. Using different name for implementation method defiantly help – Igor B. Apr 14 '16 at 08:56
  • I have logged a bug/improvement request to Swift's community. If you are interested in, check out it here: https://bugs.swift.org/browse/SR-1228 – Igor B. Apr 14 '16 at 09:59