Configuring the backBarButtonItem of a View Controller's Navigation Item in a Storyboard - interface-builder

It's easy enough to drag and drop bar button items onto a view controller's navigation bar in a storyboard in Interface Builder. In this way, you can set the leftBarButtonItem and rightBarButtonItem outlets of the view controller's navigation item. But there's also a backBarButtonItem outlet, and it's not obvious at all how to set it. How can I set a custom back bar button item using Interface Builder?

Select the view controller whose navigation items you want to change. The black bar displaying the identity of the view controller changes to an iconified tray of its referenced objects.
Drag and drop a bar button item from the object library onto the tray.
Right-click on the view controller's navigation item in the main object tray on the left-hand side. Wire up the newly added button as the navigation item's backBarButtonItem outlet.
Select the bar button and configure it in any way you choose with the Attributes Inspector.

As #wcochran noted above, when working with viewControllers pushed onto a navigationController's stack, the backBarButtonItem outlet is already wired and can't be changed. Furthermore, selecting the child VC's navigationItem and changing the Back Button text in IB doesn't do what you would expect.
Now you might think that replacing the child VC's backBarButtonItem would solve the problem, but it doesn't. Confusingly, if you want to set the title of the back button of a child VC, you have to set the back button title of its parent (!), like so:
- (void)viewWillAppear:(BOOL)animated // in the parent VC!
{
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backButton;
}
This won't do anything on the parent VC. In fact, if the parent is the navigationController's RootViewController, there won't be a back button at all. But the child will inherit (or pick up) the back button you've created.
This only applies to the immediate child VC, so if you want to maintain the label down through the navigationController's stack you need to set it on each parent.
Thanks to #wiliz in #iphonedev for explaining this to me.

As #AdamBlock noted above, you have to set things right in the parent VC.
He shows how to do this programmatically. It is also possible to do this in interface builder.
Select the parent VC
Select the navigation Item
Open the Attributes inspector
Set the title for the Back Button.

In Interface Builder, you can change the Navigation Item back button's title.
Programmatically, you can set a custom back button in your view controller's viewDidLoad method. In this example we set the button's image to an image named "customImage.png":
- (void)viewDidLoad
{
[super viewDidLoad];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Custom" style:UIBarButtonItemStyleBordered target:self action:nil];
// Set custom image here
backButton.image = [UIImage imageNamed:#"customImage.png"];
self.navigationItem.backBarButtonItem = backButton;
}

Related

How to Configure Multiple Gesture Recognizers for Side Menu

I have a UIViewController that adds other UIViewControllers as children so that I can use their views to create a menu hierarchy. The following images represent my current interface.
The interface before displaying the menu:
The interface after displaying the menu:
The following explains my hierarchy:
In AppDelegate I create an instance of MainContainerViewController and set it as the window's rootViewController. The view controller acts as a container allowing me to add Chile view controllers.
In MainContainerViewController I create an instance of CenterViewController and add it as the rootViewController of an UINavigationController. I add the navigation controller as a child view controller, adding the navigation controller's view as a subview of MainContainerViewController. I also create an instance of LeftMenuViewController, adding it as a child view controller and inserting its view below the navigation controller's view. This allows me to tap the UIBarButtonItem of CenterViewController and show the menu by sliding the navigation controller's view to the right.
I have added an UIScreenEdgePanGestureRecognizer to the navigation controller's view which allows me to show the menu by sliding CenterViewController's view off to the right.
I have added an UITapGestureRecognizer to the navigation controller's view which allows me to close the menu by tapping the navigation controller's view.
I want to be able to use an UIPanGestureRecognizer to allow the user to interactively close the menu similar to how Slack allows, if you're familiar with their mobile UI.
I added the pan gesture recognizer to the navigation controller's view, just as I did with the two aforementioned gesture recognizers. However, I can't get the UIScreenEdgePanGestureRecognizer and UIPanGestureRecognizer to play well together. I have tried implementing combinations of UIGestureRecognizerDelegate methods without success. I feel a little lost with gesture recognizers, and I don't know where to start with trying to get these gesture recognizers to play well.
I have followed Apple's example, adapted to my requirements. If the menu is not displayed, then the UIPanGestureRecognizer should allow the UIScreenEdgePanGestureRecognizer to open the menu.
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if leftMenuState == .hidden {
if gestureRecognizer == panGestureRecognizer && otherGestureRecognizer.view!.isDescendant(of: gestureRecognizer.view!) {
return true
}
}
return false
}
What I expect:
Ability to display menu by using UIScreenEdgePanGestureRecognizer
Ability to tap CenterViewController and have menu close
Ability to pan CenterViewController and close menu
What happens:
Menu is not opened by panning with UIScreenEdgePanGestureRecognizer because UIPanGestureRecognizer receives events
Menu is opened by UIBarButtonItem
Menu is closed by UIBarButtonItem or UITapGestureRecognizer
What do I need to do to enable the intended functionality?

Navigation Bar Layout Issue on Tab Bar Controller + Navigation Controller

I've followed a how-to to create a simple Tab bar controller with a navigation controller in the first tab. Until here all is working correctly, expect a strange issue on the layout.
When the app starts the first time, the Navigation Bar on the top of the first loaded nib is a little outside of the view. I cannot figure out why this happen. In the first view there is a button "Add new System" that opens a modal view. If I press this button and the modal view appears and then I dismiss the modal going back to the initial view, then the Navigation bar at the top is placed/refreshed correctly. The same happens if I press the second TAB (it's a simple nib without Navigation controller for now) and then back to the first TAB, the Navigation bar is placed in the correct position.
Here a screenshot on the first startup:
And here when I press the modal view or the second TAB and then back to the first view:
The code is quit simple following one of the numerous tutorials on the net. I'm NOT using storyboard. Only customization was adding the buttons on the top of the Navigation Bar:
UIImage *editbuttonImage = [UIImage imageNamed:#"edit_pressed.png"];
UIButton *editButton = [UIButton buttonWithType:UIButtonTypeCustom];
[editButton setBackgroundImage:editbuttonImage forState:UIControlStateNormal];
editButton.frame = CGRectMake(0, 0, editbuttonImage.size.width, editbuttonImage.size.height);
[editButton addTarget:self action:#selector(leaveEditMode)
forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]
initWithCustomView:editButton];
[editButton release];
[editbuttonImage release];
No other modifications were made. The nib was used before in a single view. Then I've tried to insert it into a TAB Controller + Navigation Controller.
I could post the whole code in case it's needed. Under Select System there is a Table View, in these pictures empty, also not shown.
Thank's for the help!
Simon
I've solved the issue myself. On startup I've setup to hide the status bar and shown it again in the app delegate. The directive used :
[[UIApplication sharedApplication] setStatusBarHidden:NO];
was after adding the navController as subview. Also the Navigation controller BAR was not out of the view, simply under the status bar.
Hope this helps someone :)
Cheers, Simon

Hiding and showing different scrollviews

So I have two scroll views that I want my XIB to show.
My first scroll view is loaded with the XIB and I want the second to be loaded when a button is pressed.
Does any body know how I can do this?
UIScrollView inherits from UIView. All you have to do is set it's hidden property to hidden. It is a simple set method. By default it will display as long as you add the view.
On your click action or method triggered simply set the property for the UIScrollView like:
[myScrollView setHidden:NO];
And if you want it to disappear again, on a different action:
[myScrollView setHidden:YES];

Finding File Owner in Xcode 4.2

Please glance at the image below and help me find a File Owner for the class.
Generally i would connect my UILabel to it, but, alas, i can't find it.
Question: What should i connect my Label to?
Storyboard:
Meanwhile class is set up as
As storyboards don't have an owner, you can use the View Controller instead.
Ctrl click (or right click) the label, drag the blue line to connect up with the orange View Controller.
Right click the Label and connect to the View controller scene
You have put your finger on a key difference between storyboards and nibs: when a nib is loaded, an owner instance is specified, but a storyboard is not loaded with an owner, so there is no file's owner in a storyboard. Your ViewController instance is created by the storyboard and is proxied in the scene (listed as View Controller), so you can draw a connection between that and an interface item. But if you want to form a connection with an already-existing instance not represented in the storyboard, you'll have to identify that instance in some other way (perhaps by a tag) and find it and runtime and form the connection in code after the storyboard loads.
For example, in this code, I manually load a storyboard (to use its initial scene in a popover) and then form connections from some bar button items within it:
UINavigationController* nav =
(UINavigationController*)[[UIStoryboard storyboardWithName:#"Storyboard"
bundle:nil]
instantiateInitialViewController];
// there is no file's owner...
// so we can't just draw the connection from button items to ourself,
// because we are not proxied in the storyboard
// so, locate the button items in some other way and do it in code
UIViewController* root = [nav.viewControllers objectAtIndex: 0];
[root.navigationItem.leftBarButtonItem setTarget:self];
[root.navigationItem.leftBarButtonItem setAction:#selector(save:)];
[root.navigationItem.rightBarButtonItem setTarget:self];
[root.navigationItem.rightBarButtonItem setAction:#selector(cancel:)];
In some cases, there's a trick you can use to inject an arbitrary existing instance into a scene so that a connection to it will work: make that instance the first responder. There is a first responder proxy in every scene, so this can give you something to connect to by drawing within the storyboard. So, this code could work instead of the above:
[self becomeFirstResponder];
UINavigationController* nav =
(UINavigationController*)[[UIStoryboard storyboardWithName:#"Storyboard"
bundle:nil]
instantiateInitialViewController];
(And the button action connections have been drawn in the scene from each button to the first responder proxy object.)
Menu: Navigate - Reveal in Project Navigator
In the Project Navigator, Click on the "Main Storyboard"
Menu: View - Show Assistant Editor
You should have the Storyboard on the left with your label, and the view controler.h text on the right.
Click on your label, hold down the control button, and drag a blue line to the View Controler.h source code on the right. Type in a reference name (for example myLabel), and click connect.
Automagically you will see something like this generated:
#property (weak,nonatomic) IBOutlet UILabel *myLabel;
Inside the View Controler.m, you will see something like this generated:
#synthesize *myLabel;
Inside your IBAction events, you can set the label:
myLabel.text =

How to add a bar button item in interface builder?

I am really new to iPhone development and I need help setting up my views.
I have a view that is named FirstViewController.xib and a controller class for this view.
In my MainWindox.xib I have setup a root controller with a moveToNextView function that is connected to the options bar button item.
So when I click on this item the current view switches to the first view and I am able to swticht back. That works fine so far.
The navigation bar at the top of the screen from the MainWindow.xib is displayed in the first view, too. But when I open FirstViewController.xib there isn't any navigation bar defined (but on build&run it is displayed).
This is a problem for me because I want to add a save bar item to the first view. How do I solve that?
Assuming you have a UIViewController (or UIViewController subclass) that is a child of a UINavigationController. Note, I'm using storyboards so your results may vary when using xibs.
If you do not see a UINavigationBar on the interface, try manually changing the simulated metrics.
Drag a Navigation Item onto the view (anywhere). You should now have a place to enter the title in the interface builder.
Now you can drag Bar Button Items onto the nav bar.
You have to do it from code. Add to your FirstViewController class viewDidLoad method:
UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithTitle:#"Save" style:UIBarButtonItemStyleDone target:self action:#selector(doSave:)];
self.navigationItem.rightBarButtonItem = anotherButton;
[anotherButton release];
Just drag a bar button item onto your nav bar in Interface Builder. Xcode automatically wires it up then you can use it like anything else...

Resources