1

I have two ViewControllers, FirstViewController and SecondViewController. Both have an own Swift file, FirstViewController.swift and SecondViewController.swift.

FirstViewController.swift contains:

class FirstViewController: UIViewController {
    @IBAction func callFunctionInOtherClass(sender: AnyObject) {
//        Call "func showAlert" in SecondViewController when clicking the UIButton in FirstViewController
    }
}

SecondViewController.swift contains:

class SecondViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    func showAlert(sender: AnyObject) {
        let alert = UIAlertView(title: "Working!", message: "This function was called from FirstViewController!\nTextField says: \(textField.text!)", delegate: nil, cancelButtonTitle: "Okay")
        alert.show()
    }
}

I want to be able to call the func showAlert() in SecondViewController when taping on the UIButton in FirstViewController.

I've already spent many nights to find a solution but none worked. Does anybody know what to do to reach this goal?

I uploaded a sample Xcode project here: CallFuntionInOtherClassX | filedropper.com

P.S.: Of course, I could post some code and explain what error I get, but I think it's not reasonable because I really don't know how to do that.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • Why do you need this? Is this the exact use case (i.e. showing the alert)? If not, what is the actual use case? – Losiowaty Sep 05 '16 at 10:17
  • Without allocating secondviewcontroller how can you call showAlert() method from firstviewcontroller? – Poles Sep 05 '16 at 10:19
  • use deleage or notification – Shobhakar Tiwari Sep 05 '16 at 10:46
  • @Losiowaty Not, it's not the exact use case. The real case is: I have a ViewController with two ContainerViews and I want to be able to e.g. submit a form filled in a ContainerView by taping a button in the Main/Parent-ViewController, because it has the navigation buttons, title… –  Sep 05 '16 at 10:49
  • @Poles I'm not very experienced in programming. Maybe you can help me, please? –  Sep 05 '16 at 10:50
  • @Shobhakar Tiwari What does it exactly mean? Do you have an example or so? –  Sep 05 '16 at 10:51
  • http://stackoverflow.com/a/25792213/3400991 here is the link try this way – Shobhakar Tiwari Sep 05 '16 at 10:53

4 Answers4

7

You may use NSNotificationCentre to accomplish this task.

In viewDidLoad method of your SecondViewController class register self as observer to receive notification broadcasts:-

override func viewDidLoad() {
    NotificationCenter.default.addObserver(self, selector: #selector(showAlert), name: NSNotification.Name(rawValue: "callForAlert"), object: nil)
}

and in FirstViewController's button action method you should fire the notification by writing :-

@IBAction func callFunctionInOtherClass(sender: AnyObject) {
    //Call "func showAlert" in SecondViewController when clicking the UIButton in FirstViewController
    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "callForAlert"), object: nil)
}

Don't forget to call removeObserver in SecondViewController's viewDidUnload method.

Pankaj Yadav
  • 601
  • 9
  • 23
  • Thank you very much, that works and saved me hours. But still two questions: `1.` What do you mean with your last sentence? What does this do? `2.` Why are you the only one who suggested this solution? Is it an uncommon way? Because, it seems very easy. I'm wondering why not any other had this idea. –  Sep 05 '16 at 13:33
  • Shobhakar Tiwari also suggested the standard ways of implementing target action mechanism . But your specified case can't be handled by delegates leaving only one option i.e. NotificationCentre. and as per apple documentation : Be sure to invoke this method (or removeObserver:) before the observer object or any object specified in addObserver:selector:name:object: is deallocated. Because it might crash your app when notification is called on nil observer. – Pankaj Yadav Sep 05 '16 at 13:41
  • It Also have memory leak implications. – Pankaj Yadav Sep 05 '16 at 13:48
  • Sorry, but NSNotificationCenter for ordinary communication between objects is the wrong decision for 99 % of the time. – Eiko Sep 05 '16 at 15:58
  • Yes Eiko, but his use case doesn't qualify for delegate or Block implementations. And can you please explain why shouldn't we use in the scenario mentioned above and according to you what should be the correct way to achieve the solution of above question ? – Pankaj Yadav Sep 06 '16 at 07:24
  • 1
    @Eiko I don't think this answer deserves a Downvote, You may have other options but rather down voting you should suggest a better answer, I can see u are a huge contributor to the SO community, as the Owner of this question has already accepted the answer. – vishwa.deepak Sep 07 '16 at 07:33
  • 1
    Similar question get asked here over and over again - it should have been closed as a duplicate right away. I see no reason why delegates or blocks wouldn't work here, or why storing a reference to the other controller as a simple property and calling a method on that would fail. The notification approach is like ordering in a fast food restaurant and waiting for someone to shout "Food is ready for pickup" - yes, it could work, and without a name, it might even reach recipient at first try... – Eiko Sep 07 '16 at 07:44
  • I agree, storing a reference to the other controller as a simple property and calling a method on that stored property is good option. – Pankaj Yadav Sep 07 '16 at 07:58
2

EDIT: These functions have been revised in swift 3 as follows:

Code in FirstViewController

 override function viewDidLoad(){
NotificationCenter.default.addObserver(self, selector: #selector(showAlert), name: NSNotification.Name(rawValue: "showAlert"), object: nil)
}

Code in SecondViewController:

@IBAction func callFunctionInOtherClass(sender: AnyObject) {
 NotificationCenter.default.post(name: NSNotification.Name(rawValue: "showAlert"), object: nil)
}
Ketan Dubey
  • 430
  • 8
  • 19
0

Try this:

SecondViewController().showAlert(self)

In your second view controller

 if let text = textField?.text {
     dispatch_async(dispatch_get_main_queue(),{

      let alert = UIAlertView(title: "Working!", message: "This function was called from FirstViewController!\nTextField says: \(text)", delegate: nil, cancelButtonTitle: "Okay")
      alert.show()

    })
}
Khalil Khalaf
  • 9,259
  • 11
  • 62
  • 104
Sofeda
  • 1,361
  • 1
  • 13
  • 23
  • I'm getting `fatal error: unexpectedly found nil while unwrapping an Optional value` and it crashes. But when removing the alert code and just doing a `print("It works!")` there's no crash and I get the print in the console. –  Sep 05 '16 at 10:46
  • `\(textField.text!)` seems to be the problem. When removing it, it works, with and without `dispatch_async`. Any ideas what do to? –  Sep 05 '16 at 10:57
  • First check does it really hold text then use else not – Sofeda Sep 05 '16 at 10:58
  • I don't think it'll help because if textField empty it also shows an empty string, not an error message. Or, am I wrong? –  Sep 05 '16 at 11:00
  • :) read the crash message dear. found nil while unwrapping an Optional value – Sofeda Sep 05 '16 at 11:01
  • What you say sounds reasonable. I'll try it. –  Sep 05 '16 at 11:03
  • Is that the right way? Code:`if (textField.text == "") {` `let myString = textField` `}` `else {` `let myString : String = "empty"` `}` And `\(myString.text)` in the alert. Because I get: `Use of unresolved identifier 'myString'` –  Sep 05 '16 at 11:08
  • It may work but lengthy. Try the edit if it fits you – Sofeda Sep 05 '16 at 11:11
  • You can use if let text = textField.text inside the main queue their to fit for empty string – Sofeda Sep 05 '16 at 11:13
  • Unfortunately still the same: Crash and `fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)` –  Sep 05 '16 at 11:16
  • give me some time plz – Sofeda Sep 05 '16 at 11:17
  • Does your outlet looks like this? @IBOutlet var textField: UITextField! – Sofeda Sep 05 '16 at 11:45
  • `@IBOutlet weak var textField: UITextField!` –  Sep 05 '16 at 11:48
  • Still trying something. I'll reply soon. –  Sep 05 '16 at 11:49
0

If you want to show alert view to second viewcontroller from firstview controller when go to it you can do something like,

  self.performSegueWithIdentifier("your segue identifier", sender: self)  // or whatever way if you are not using storyboard or segue

    let alert = UIAlertView(title: "Working!", message: "This function was called from FirstViewController!", delegate: nil, cancelButtonTitle: "Okay")
    alert.show()

If you want to set any variables of secondviewcontroller then you need to implement prepareForSegue method. (if you are using segue).

Second thing you can show alertview in viewDidload of secondViewController also.

Ketan Parmar
  • 27,092
  • 9
  • 50
  • 75
  • Unfortunately won't work. I need this function executed it `SecondViewController` because I'll need `@IBOutlet's` for it's `ViewController`. –  Sep 05 '16 at 11:19