In IB, Cannot Connect IBOutlet Delegate to Another View Controller? - ios

I have a view controller which as one of its views a container view (in IB storyboard) which embeds a table view controller, which in turn has a container view that embeds yet another view controller. In this last view controller I set up a delegate protocol with a weak synthesized delegate property as an IBOutlet. The very first view controller is what I want to receive the delegate methods from the last and I added the protocol <...> to it.
The problem is that I have not figured out a way while in storyboard (or otherwise) to link the IBOutlet delegate of the last view controller to the first view controller which follows the protocol so the last can send the first messages. I thought I could just drag and drop (with the control key) but all I find is segue options on the destination. It seems even though the delegate appears in the outlet connections window, it will not connect to ANY view controller in my project.
Can't ANY view controller be a delegate of another's protocol? And can be linked in IB? If I cannot do it with IB, I don't know how to make another VC a delegate upstream.
Any advice would be appreciated. Thanks.

You cannot connect IBOutlets between view controllers. You need to do it in code. You'll have to go through the chain of childViewController to get from the first controller to the last -- if I understand your structure properly, from the first view controller:
LastController *last = ((UIViewController *)self.childViewControllers[0]).childViewControllers[0];
last.delegate = self;

I asked a similar question Interface Builder won't allow connections to custom UIView class? I ultimately opened a bug with Apple. After a few follow up queries from Apple, I haven't heard any kind of resolution.

Related

Navigate to UIViewController from a UIView

My app header is currently a full width, 65pt height UIView which I then use as a generic header for all pages.
class AppHeader: UIView {
...
}
Then, in my Main.storyboard I have a UIViewController with a View from the object library which has its class specified as AppHeader.
My AppHeader (UIView) has multiple buttons which should, if clicked, take you to from the page/controller you're currently on to another.
From the AppHeader class I do not have access to use the present method to show another controller as its not within scope.
Here is my AppHeader.xib:
How can I resolve this?
This is very bad behaviour, you should not use a UIView as a header. You should add a Navigation View Controller as the first view controller of your app. Navigation view controller has the navigation bar where you can put the buttons you want there. From that ones, you will be able to push or present other view controllers.
Check the official documentation: https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/NavigationControllers.html
Make your AppHeader view a custom subclass of UIView if it isn't already. Wire the actions on the button to IBAction methods in the view.
Create a protocol AppHeaderDelegateProtocol. Give your AppHeader class a weak delegate property. Define methods in that protocol that let the AppHeader notify it's owning view controller about button presses.
Implement your AppHeaderDelegateProtocol in view controllers that will contain instances of AppHeader.
Connect the delegate property to each instance of AppHeader's owning view controller.
That should do it.
You can create a protocol - call it AppHeaderDelegate or something - and set up your other viewControllers to adopt that protocol. You could define functions to let your delegate know that a certain button was pressed, and your delegate viewController can react to that by presenting the correct view controller.
Apple's docs on protocols here.
Alternatively, you can use NotificationCenter to broadcast notifications to let subscribers know that a certain button was pressed, and have your viewControllers listening for these notifications and reacting to them accordingly. You have to manage when classes start/stop listening, though, as you may have several objects trying to react to a single notification.

Using storyboarding in xCode how can I change the value of a label from a different view?

I'm writing an app that uses storyboarding and I want to update the labels in one view by clicking a button in a previous view.
_label.text = variable1;
is the line I would use to change the value of label, which is in the next view, when I click the button. Using this method I can easily change labels in the same view as the button but it does nothing when I go to the next view and see empty labels.
I've tried looking everywhere and found similar issues but couldn't find anything that worked for me so any solution would be very appreciated!
Unfortunately, it is not possible to connect IBOutlets between different scenes in storyboard.
It is difficult to suggest some precise solution because you have to provide more details about the setup which you have. Still, it is possible to outline some possible solutions:
Using prepareForSegue
If the view controller which you want to modify appears after the segue is performed you can customise its appearance in prepareForSegue function.
Using delegation
You can assign the view controller which wants to modify another view controller as its delegate. For example, if ViewController1 wants to modify ViewController2:
#interface ViewController1: UIViewController {}
#property (nonatomic,weak) ViewController2 *controllerThatIWantToModify;
with such setup you can call:
self.controllerThatIWantToModify.label.text = variable1;
You use storyboards, so there must be a segue from your first viewController (with the button) to your second (with labels in it).
If it is the case, you can set up the labels of the second view controller from the prepareForSegue method of your first view controller.
This method is called with a segue object which has a destinationViewController property which is your second view controller.
If you have several segue from this viewController, you should check if it is the right segue and then set it up.
To do that you need to set up outlets that gives you access to the labels from the viewController.
Then you can either write a setUpLabelsWith:(NSString)text1 ... method in your view controller, or directly access the outlets from the first view controller (supposing their are not private).
Yes this supposes your second view controller has a custom class.

Present UIViewController from another UIViewController

I have a UIViewController named ViewControllerHome and when the user touches an image on the screen I would like to display a second view which is a Membership Card. I am doing the following from the touch of the image:
membershipCardViewController = [[MembershipCardViewController alloc] initWithNibName:#"MembershipCard" bundle:nil];
[self presentViewController:membershipCardViewController animated:YES completion:nil];
When the code executes an exception is thrown on the presentViewController line.
I have an .xib with a ViewController that contains a view and a UIImageView of the Membership Card. I set the class of the ViewController to my MembershipViewController.
Once that shows up I will dismiss it on a touch.
Can anyone tell me what I am missing? I thought I had all the steps correct to present the view controller.
Thanks for the help.
In the MembershipCardViewController's nib file, its view (what ever view it is controlling) is connected to the view controllers view outlet.
To do this control drag from files owner to the view you want to connect it to (the grey view in this case)
And you should get this:
Files owner should point to your MembershipCardViewController. Every view controller has a pointer to a view. I'm going to guess that you added some custom view after deleting the stock one. Control drag from files owner to that view to make the outlet. (If this outlet returns nil, an exception will be thrown).
To be safe, make sure file's owner (in the nib) is pointing to MembershipCardViewController
(This probably isn't the problem but it sounds like you may have started with an empty nib).
To do this, click on files owner, and select the identity inspector on the right. Make sure the class says MembershipCardViewController
I answered another question before about this Am I right in saying initWithNibName:bundle is used to manually load nib files and that initWithCoder would be used as an alternative?
This explains what is actually going on.
The purpose of your XIB is to archive the view of the controller. Having the controller class set is only part of the required information, you also need to connect any IBOutlet relationships between the controller and the views.
As standard any subclass of UIViewController provides an outlet called view. You need to ensure that it's connected. Otherwise when you load the XIB the view doesn't get set and you get an exception.
There are a number of ways to make the connection. Check this.
See also loaded-nib-but-the-view-outlet-was-not-set-new-to-interfacebuilder.

Unable to link UIViewController IBOutlets with StoryBoard

I have two UIViewControllers (A and B) in storyboard. The A view controller has a property
#property (nonatomic, retain) IBOutlet UIViewController *viewController;
that I want to link via storyboard to B.
The outlet shows under the the IBOutlets section in storyboard menu but I'm unable to link.
It might seem strange why I'm trying to do that but I need it. Someone knows how to do that?
IBOutlets are connections within a single view controller. Create references to objects inside the view controller so you can use those objects in code.
You cannot create IBOutlets from one view controller to another one. A property is the correct way to go, but you have to assign the property in code. Normally when one view controller creates another one, it might set a reference to itself.
OtherViewController *otherViewController = [OtherViewController alloc] init];
otherViewController.masterViewController = self;
// at this point "otherViewController" has a reference to the current view controller
Now I understand what I need to do. I need to create a custom segue to achieve the same result as when you link a UINavigationController to other ViewController and mark as RootViewController. It is done by segue and not by IBOutlet.
I am a bit late to the party however I put together a segue class to help with this job. View the git repository to see the class in action: https://github.com/datinc/DATOutletSegue.
Basically it uses the identifier of the segue to connect to the parent controller

Table View Controller code not getting invoked

I've got an app with a Web View and a Table View. The Web View works fine, but the Table View does not seem to be invoking the TableViewController.h code when displaying. I think I missed a linking step somewhere, but I can't seem to figure it out.
To clarify, I originally set up the project as a single view application but later added a Navigation controller and the Table View manually.
After I manually added the Table View to the storyboard, I created TableViewController.h and TableViewController.m, but I can't figure out how to link the code to the Table View in the story board.
Can someone explain what I'm missing?
Set the class of the controller you're using in the storyboard to be TableViewController
Two scenarios:
First, if you added a tableview to a standard UIViewController (i.e. you dragged a table view to your blank, standard view controller in Interface Builder and the .h for your view controller specifies that it is a subclass of UIViewController), then you have to specify the "data source" and "delegate" properties of your table view manually. Thus, you should make sure to configure the "data source" and "delegate" for your tableview to reference your view controller. You can do this in either Interface Builder (by selecting the tableview, going to the "Connections Inspector" in the far right panel, and then make sure you've specified the outlets for data source and delegate):
Or set dataSource and delegate properties in your UIViewController subclass viewDidLoad code:
self.tableView.dataSource = self;
self.tableView.delegate = self;
In this scenario, you also want to ensure that you created a IBOutlet for your tableview, too.
Alternatively, if you used a UITableViewController (i.e. you removed the blank view controller and added your own table view controller into your storyboard in Interface Builder and have made sure that your view controller is a subclass of UITableViewController, not UIViewController), see Moxy's answer.

Resources