Ivan Panshin
Ivan Panshin

Reputation: 65

How to access a button from another view controller?

Let's say I have a firstViewController and a secondViewController. The first one contains a firstButton and the second one - a secondButton. Here's what I want to do: when user clicks the secondButton, some firstButton's property changes.

Unfortunately, when I create an instance of a firstViewController in a secondViewController and then trying to access a firstButton, I get an error:

fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)

So, technically, I'm trying to do this as follows:

var ins = firstViewController() 

@IBAction func secondButtonisPressed(){
    ins.firstButton.alpha = 0
}

What is the proper way to implement that?

Thanks in advance.

Upvotes: 5

Views: 6153

Answers (2)

Hugo Alonso
Hugo Alonso

Reputation: 6824

Your problem here is that the IBOutlets of your firstViewController are only available (!= nil) after the viewDidLoad() firstViewController's method has being called.

In other words, you have to present the view, before you can make any changes to a UIViewController IBOutlet.

How you can solve this?

Add a variable into FirstViewController that works as a flag for you.

for example: var hideFirstButton = false

in the viewDidLoad or viewWillAppear method of FirstViewController check for hideFirstButton's value and hide or show your firstButton.

Then, before you present your FirstViewController change the value of hideFirstButton to the needed for your application to run fine.

UPDATE:

Other workaround, using Storyboard is (This approach has the inconvenient that the completion handler is called after viewWillAppear() so the button is visible for a second):

    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let firstViewController = storyboard.instantiateViewControllerWithIdentifier("FirstViewController") as! FirstViewController
    self.presentViewController(firstViewController, animated: true, completion: {
        //This lines will be called after the view is loaded so the code will run
        firstViewController.firstButton.alpha = 0
    })

EXAMPLE: an example at GitHub

Upvotes: 2

crispy_bacon
crispy_bacon

Reputation: 31

You could try to do this using delegation, similar to the way Apple does it in their existing frameworks. For an example, look at the way that you use UITableViewDelegate when working with a UITableView object.

If you wanted to use delegation to tell secondViewController that firstButton was pressed using delegation, you could do it as follows:

Step 1: Create a protocol containing a method for the button press event.

protocol buttonPressDelegate {
    func buttonPressed() -> Void
}

Step 2: In firstViewController, declare that you have an instance of an object of type buttonPressProtocol.

var buttonPressDelegateObj: buttonPressDelegate?

Step 3:

In firstViewController, initialize your buttonPressDelegateObj to contain a reference to your instance of secondViewController. If you want you can create a method to set the reference contained in buttonPressDelegateObj, or do it viewDidLoad in firstViewController, etc.

buttonPressDelegateObj = secondViewControllerObj

Step 4:

In secondViewController, declare that you adopt the buttonPressDelegate protocol.

class secondViewController: UIViewController, buttonPressDelegate {

Step 5:

In secondViewController, implement the protocol method buttonPressed() by adding the function with your desired implementation. Here's an example:

func buttonPressed() {
   secondButton.alpha = 0
}

Step 6:

Create an @IBAction on the button in firstViewController, so that when the button is pressed it calls buttonPressDelegateObj.buttonPressed() and you can respond to the event

@IBAction func firstButtonPressed() {
   if (buttonPressDelegateObj != nil) {
      buttonPressDelegateObj.buttonPressed()
   }
   else {
      print("You forgot to set your reference in buttonPressDelegateObj to contain an instance of secondViewController!")
   }
}

Note: This is just one way that you could do this. To tell firstViewController that secondButton was pressed (go the other way), have firstViewController implement the protocol buttonPressDelegate, have secondViewController contain a reference to firstViewController as an instance of type buttonPressDelegate?, and create an @IBAction in secondViewController that fires when secondButton is pressed that calls your the buttonPressDelegate method.

Note: There is a similar pattern employed in the Android world to get a Fragment to communicate to an Activity, that you can read more about here

Upvotes: 2

Related Questions