UINavigationBar and new iOS 5+ appearance API - how to supply two background images? - objective-c

I want to exploit the new iOS 5 appearance API to supply custom background images to all UINavigationBar instances in my app. To do this, it's as simple as this:
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"whatever.png"] forBarMetrics:UIBarMetricsDefault];
However, for each instance, I want to provide a different image depending on the value of the translucent property, e.g.
// For UINavigationBar instances where translucent returns YES:
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"whatever-translucent.png"] forBarMetrics:UIBarMetricsDefault];
// Otherwise:
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:#"whatever.png"] forBarMetrics:UIBarMetricsDefault];
Given that the appearance APIs seem to be configured using class methods, is something like this possible?

At the moment, there's no way to do what you're describing - the appearance proxy doesn't know anything about any particular instance at the time you're calling for it.
In practical terms, what you'll probably need to do is figure out how many translucent bars you'd have v. how many non-translucent ones you had. Choose whichever you have more of and use the appearance proxy for that one - for the others, when you go to make it translucent (or ask for full-screen layout), you'll have to set the background image then.
In the meantime, could you file an enhancement request at http://bugreport.apple.com/ for what you're asking? It's not an unreasonable request. Thanks!

You can either set it globally with the class appearance proxy or set it on an instance of a navBar.
I'm currently setting background on an instance of the nav bar and it seems to be working. I have two different navBars with different backgrounds. If you set it on an instance, you should be able to condition the code.
UINavigationController *myNavController = [[UINavigationController alloc] initWithRootViewController:myView];
[viewControllers addObject:myNavController];
// not supported on iOS4
UINavigationBar *navBar = [myNavController navigationBar];
if ([navBar respondsToSelector:#selector(setBackgroundImage:forBarMetrics:)])
{
// right here, you could condition bg image based on properties of this instance
// of the navBar or any other condition.
[navBar setBackgroundImage:[UIImage imageNamed:#"bg.jpg"] forBarMetrics:UIBarMetricsDefault];
}
If you want to set using the class method, you can set for all:
[[UINavigationBar appearance] setBackground ...
I will admit though that it's pretty new and I'm just figuring it out like most folks.

This answer probably won't be of much help to you, but it may be to others. IF you make a subclass, you can specify the appearance for each subclass separately. For instance, I have UITableviewCells and a custom class that is derived from UITableViewCells. I actually do this for a reason, but I discovered that i need to call [[UITableViewCells appearance] setFont:[...]] for both classes specifically.
Since you seem to want to do so based upon a variable that you will not know until runtime, you are probably out of luck!

You can do it like this if you know which classes contain the translucent bars:
[[UIBarButtonItem appearanceWhenContainedIn:[MyClassWithTranslucentBar class], [MyOtherClassWithTranslucentBar class], nil]
setTintColor:desiredColor];

I cant leave a comment so will have to be an answer. Rob Whitlow wrote a great article on this. Check it out: http://ios-blog.co.uk/tutorials/ios-custom-ui-series-tabbar-navbar/

Try this:
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
// Load resources for iOS 6.1 or earlier
navigationController1 = [self customizedNavigationController];
[navigationController1 setViewControllers:[NSArray arrayWithObject: self.homeViewController]];
[self setNavigationController:navigationController1];
[self.window setRootViewController:navigationController];
} else {
// Load resources for iOS 7 or later
navigationController1 = [[UINavigationController alloc] initWithRootViewController:self.homeViewController];
[self.window setRootViewController:navigationController1];
}
- (UINavigationController *)customizedNavigationController {
UINavigationController *navController = [[UINavigationController alloc] initWithNibName:nil bundle:nil];
// Ensure the UINavigationBar is created so that it can be archived. If we do not access the
// navigation bar then it will not be allocated, and thus, it will not be archived by the
// NSKeyedArchvier.
[navController navigationBar];
// Archive the navigation controller.
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:navController forKey:#"root"];
[archiver finishEncoding];
// Unarchive the navigation controller and ensure that our UINavigationBar subclass is used.
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
[unarchiver setClass:[LNTNavigationBar class] forClassName:#"UINavigationBar"];
UINavigationController *customizedNavController = [unarchiver decodeObjectForKey:#"root"];
[unarchiver finishDecoding];
// Modify the navigation bar to have a background image.
LNTNavigationBar *navBar = (LNTNavigationBar *)[customizedNavController navigationBar];
[navBar setTintColor:[UIColor colorWithRed:0.39 green:0.72 blue:0.62 alpha:1.0]];
[navBar setBackgroundImage:[UIImage imageNamed:#"nav_bar_1024_46.png"] forBarMetrics:UIBarMetricsDefault];
[navBar setBackgroundImage:[UIImage imageNamed:#"nav_bar_1024_46.png"] forBarMetrics:UIBarMetricsLandscapePhone];
return customizedNavController;
}

Related

How do you customise UISearchBar colors in iOS7?

I have searched quite a bit but cannot find a good answer to this.
I want to change the backgroundColor of the inner rounded view.
Like in Tweetbot on the search tap where it changes from gray to blue.
I understand that I probably need to iterate over the subviews but I don't know how to get the right object. (for the backgroundColor it's not the _searchLabel)
The default contrast of this element is so bad it's not even funny :(
Ok, this works. But note that you can't set a UIBarStyle beforehand or it will override everything.
https://stackoverflow.com/a/19836215/1252720
If you're still looking for a better answer, I just stumbled across this thread and found a great solution: UISearchBar text color change in iOS 7
If you look at the answer given by Sandeep-Systematix (not the accepted answer, but the answer right below), he mentions a really clean way to modify subviews in any class with this method:
[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setTextColor:[UIColor blueColor]];
You can read more about this in Apple's documentation: https://developer.apple.com/library/ios/documentation/uikit/reference/UIAppearance_Protocol/Reference/Reference.html
That said, here's what you'll need to change the white, rounded background of the UITextField inside the UISearchBar:
[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setBackgroundColor:[UIColor redColor]];
Now if you needed to create different UISearchBars with different styles, you would simply create a subclass of UISearchBar and you'd end up with something like this:
[[UITextField appearanceWhenContainedIn:[MyCustomSearchBar class], nil] setBackgroundColor:[UIColor redColor]];
Use this code.
_searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.contentView.bounds.size.width, 44.0f)];
_searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_searchBar.delegate = self;
[self.view addSubView:_searchBar];
UITextField *txfSearchField = [_searchBar valueForKey:#"_searchField"];
txfSearchField.backgroundColor = [UIColor redColor];

how to run code in tabbarcontroller

Im trying to change the icon tint of my unselected tab bar icon images. I have used the patch code below, however, the post that i found this patch code in says to run this in the tab bar controller but i did not know how to do that so i ran it in the -(void)viewDidLoad method in the viewcontroller .m file. It came up with an error saying "Property 'tabBar' not found on object of type 'ViewController *'" How do i fix this?
// set color of selected icons and text to red
self.tabBar.tintColor = [UIColor redColor];
[[UITabBarItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys: [UIColor redColor], NSForegroundColorAttributeName, nil] forState:UIControlStateSelected];
// set color of unselected text to green
[[UITabBarItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIColor greenColor], NSForegroundColorAttributeName, nil]
forState:UIControlStateNormal];
// set selected and unselected icons
UITabBarItem *item0 = [self.tabBar.items objectAtIndex:0];
// this way, the icon gets rendered as it is (thus, it needs to be green in this example)
item0.image = [[UIImage imageNamed:#"unselected-icon.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
// this icon is used for selected tab and it will get tinted as defined in self.tabBar.tintColor
item0.selectedImage = [UIImage imageNamed:#"selected-icon.png"];
Instead of using that patch of code, try to do the below in your VC:
[self.tabBarItem setFinishedSelectedImage:[UIImage imageNamed:#"item_seleted.png"] withFinishedUnselectedImage:[UIImage imageNamed:#"item_unselected.png"]];
Edit:
The code above is used when subclassing UITabBarController, so if you insis you need to subclass the 'UITabBarController', change the class of your tabBarController in the storyboard and put that block of code in its viewDidLoad.
You can delete your UIViewController from storyboard and add there UITabBarController and set it as Initial View Controller.
Then you can access this UITabBarController in AppDelegate.m like this:
UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
This is the fast way.
Also you can subclass UITabBarController, set this class in the storyboard and put all your code inside this subclass. (as null has said 15 minutes before :)

Can't remove shadow from UINavigationBar

I can't remove the shadow from my UINavigationBar for some reason on iOS6. Why isn't this working? I've tried the following:
if ([[UINavigationBar appearance]respondsToSelector:#selector(setShadowImage:)]){
[[UINavigationBar appearance]setShadowImage:[[UIImage alloc] init]];
}
if ([[UINavigationBar class]respondsToSelector:#selector(setShadowImage:)]){
[[UINavigationBar appearance]setShadowImage:[[UIImage alloc] init]];
}
You have to do the work on a NavigationBar instance...
if ([navigationBarInstance respondsToSelector:#selector(setShadowImage:)]){
[navigationBarInstance setShadowImage:[[UIImage alloc] init]];
}
Edit:
If you for some reason really need to perform the check on the class. This will work:
if ([UINavigationBar instancesRespondToSelector:#selector(setShadowImage:)]) {
}
This had me stumped for a while until I read the docs!
NOTE:
For a custom shadow image to be shown, a custom background image must also be set with the setBackgroundImage:forBarMetrics: method. If the default background image is used, then the default shadow image will be used regardless of the value of this property.
Mike Pollard has it right.
To remove the shadow underneath the UINavigationBar on iOS 6, you need to set a custom background image in addition to setting the shadow image to a blank UIImage.
CustomViewController.m
- (void)viewDidLoad
{
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"Background"] forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setShadowImage:[[UIImage alloc] init]];
}
In the above example, "Background" would be a PNG image in your project.

Adding two right bar button items to the navigation bar

I have a navigation bar to which I have added right BarButton successfully. Both the NavigationBar and BarButton are created programmatically. Now according to my requirement I got to add two right BarButtons to my navigation Bar. Can anyone tell me how to do this? My app is targeting ios4.
This code will do the trick for you,
NSArray *barButtonItems= [[NSArray alloc] initWithObjects:self.addButton,self.sortbyButton,nil];
self.navigationItem.rightBarButtonItems=barButtonItems;
where addButton and sortbyButton are 2 separate BarButton Items
I know it is too late but I faced it recently. So here is what I did
Create a UIView in code and add the buttons as subviews into this view.
Create a ToolbarButton using [[UIBarButtonItem alloc] initWithCustomView:buttons]
Assign this toolbar button as the Left or right barbuttonItem as U wish.
If you application is targeting iOS 4 and above then you should take UISegmentControl and have two segments on it. Catch value changed action event and check which segment is selected and do you operation accordingly.
You can also set images to segments to make look and feel better.
As the documentation to UINavigationItem1 describes, it has a property rightBarButtonItems (as well as leftBarButtonItems) where you can give an array of UIBarButtons. They are display from right (index 0) to left (index n-1).
#Matthias, As stated in documentation, rightBarButtonItems property is available from iOS 5 and above and this function needs to be supported also on iOS 4.
So, UISegmentControl is best way to achieve this.
NSArray *segmentTextContent = [NSArray arrayWithObjects:
NSLocalizedString(#\"Group By\", #\"\"),
NSLocalizedString(#\"Filter By\", #\"\"),
nil];
UISegmentedcontrol *segmentedControl = [[UISegmentedControl alloc] initWithItems:segmentTextContent];
segmentedControl.selectedSegmentIndex = 0;
segmentedControl.autoresizingMask = UIViewAutoresizingFlexibleWidth;
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.frame = CGRectMake(0, 0, 125, 30);
[segmentedControl addTarget:self action:#selector(toggleUnit) forControlEvents:UIControlEventValueChanged];
segmentedControl.tintColor = [UIColor lightGrayColor];
defaultTintColor = [segmentedControl.tintColor retain];
self.navigationItem.rightBarButtonItem = segmentedControl;
[segmentedControl release];

How to create backBarButtomItem with custom view for a UINavigationController

I have a UINavigationController into which I push several views. Inside viewDidLoad for one of these views I want to set the self.navigationItem.backBarButtonItem to a custom view (based on a custom image). I don't know why, but it doesn't seem to work. Instead, I get the standard "back" button.
UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 63, 30)];
[backButton setImage:[UIImage imageNamed:#"back_OFF.png"] forState:UIControlStateNormal];
[backButton setImage:[UIImage imageNamed:#"back_ON.png"] forState:UIControlStateSelected];
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.backBarButtonItem = backButtonItem;
[backButtonItem release];
[backButton release];
I tested with a standard title and it worked. What is wrong with the above code ?
self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Prout" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];
Thanks for any help on this.
As of iOS5 we have an excellent new way of customizing the appearance of almost any control using the UIAppearance protocol, i.e. [UIBarButtonItem appearance]. The appearance proxy allows you to create application wide changes to the look of controls. Below is an example of a custom back button created with the appearance proxy.
Use the example code below to create a back button with custom images for normal and highlighted states. Call the following method from you appDelegate's application:didFinishLaunchingWithOptions:
- (void) customizeAppearance {
UIImage *i1 = [[UIImage imageNamed:#"custom_backButton_30px"]
resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 6)];
UIImage *i2 = [[UIImage imageNamed:#"custom_backButton_24px"]
resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 6)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i1
forState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i2
forState:UIControlStateNormal
barMetrics:UIBarMetricsLandscapePhone];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i1
forState:UIControlStateHighlighted
barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:i2
forState:UIControlStateHighlighted
barMetrics:UIBarMetricsLandscapePhone];
}
This is just a quick example. Normally you would want to have separate images for normal and highlighted (pressed) state.
If you are interested in customizing the appearance of other controls, some good examples can be found here: http://ios.biomsoft.com/2011/10/13/user-interface-customization-in-ios-5/
I'm fairly certain that the backBarButtonItem is a read-only property. Instead of modifying the backBarButtonItem, try setting a custom leftBarButtonItem and hide the backBarButtonItem:
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Prout" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];
self.navigationItem.hidesBackButton = YES;
You will also need to make sure you hook up the custom button to call the back action on the UINavigationBar.
I never been able to create a proper UIBarButtonItem with custom view and setBackBarButtonItem.
Here's the solution i found : let net UINavigationControllerDelegate handles everything! The trick here is to call the popViewControllerAnimated: method of the viewController.navigationController so you don't have to create any custom method.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
if([navigationController.viewControllers count ] > 1) {
UIView *backButtonView = [[UIView alloc] initWithFrame:CGRectMake(0,0,70,35)];
UIButton *myBackButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
[myBackButton setFrame:CGRectMake(0,0,70,35)];
[myBackButton setImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[myBackButton setEnabled:YES];
[myBackButton addTarget:viewController.navigationController action:#selector(popViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside];
[backButtonView addSubview:myBackButton];
[myBackButton release];
UIBarButtonItem* backButton = [[UIBarButtonItem alloc] initWithCustomView:backButtonView];
viewController.navigationItem.leftBarButtonItem = backButton;
[backButtonView release];
[backButton release];
}
}
If your end goal is to simply replace the image used for the back button, you can use a new method on UIBarButtonItem available in iOS 5.0:
setBackButtonBackgroundImage:forState:barMetrics:
Apple Docs:
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIBarButtonItem_Class/Reference/Reference.html
Here's a simple example that sets a custom background image for all back buttons in your app:
UIImage *toolbarBackButtonBackgroundPortrait = [[UIImage imageNamed:#"toolbarBackButtonPortrait"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 17, 0, 6)];
UIImage *toolbarBackButtonBackgroundLandscape = [[UIImage imageNamed:#"toolbarBackButtonLandscape"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 17, 0, 6)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:toolbarBackButtonBackgroundPortrait forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:toolbarBackButtonBackgroundLandscape forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
I would be willing to bet that this is a bug on Apple's part as I am running into the exact same problem. The reason being is that I can get a custom UIBarButtonItem to appear, but only when I don't try to use the initWithCustomView: method. Per the API, the navigation controller checks the following things when a new view controller is pushed:
If the new top-level view controller has a custom left bar button item, that item is displayed. To specify a custom left bar button item, set the leftBarButtonItem property of the view controller’s navigation item.
If the top-level view controller does not have a custom left bar button item, but the navigation item of the previous view controller has a valid item in its backBarButtonItem property, the navigation bar displays that item.
If a custom bar button item is not specified by either of the view controllers, a default back button is used and its title is set to the value of the title property of the previous view controller—that is, the view controller one level down on the stack. (If there is only one view controller on the navigation stack, no back button is displayed.)
My case (as well as yours) is 2. I specify code exactly the same as yours (i.e., creating a UIButton, setting its image properties for various states, creating a UIBarButtonItem, initializing it with the UIButton, then setting my current view controller's backBarButtonItem property to the UIBarButtonItem); however, when I later push my view controller, nothing at all is displayed on the left-hand side of my navigation controller. Strangely, I can click where the "Back" button should be, and it pops the view controller.
Interestingly, if I create a UIBarButtonItem using the initWithTitle:style:target:action: method instead of the initWithCustomView: method, it does show a custom button with a custom title. Also, as Travis mentioned, using the leftBarButtonItem property instead works just fine. I'd rather adhere to the sanctioned logic, however, by specifying the "Back" button for the current view controller -- to be displayed later when a new view controller is pushed -- instead of creating a left button for the next view controller, which, arguably, should have no concern for anything pertaining to the view controller that came before it. :-\
Set the backBarButtonItem before pushing the viewController with the navigationController. Setting the backBarButtonItem in viewDidLoad doesn't work.
Say I have MyTableViewController. When the user taps a particular row I want to push AnotherViewController using the navigationController. The code looks something like this:
// in MyTableViewController's tableView:didSelectRowAtIndexPath method...
UIBarButtonItem *backButton = [[UIBarButtonItem alloc]
initWithImage:[UIImage imageNamed:#"yourImage.png"]
style:UIBarButtonItemStyleBordered
target:nil
action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
[self.navigationController pushViewController:anotherViewController];
When anotherViewController is displayed the back button in the navigation bar will have #"yourImage.png" and the default back button style (rectangular arrow). Also note it's fine to pass nil as target. The button behaves like the back button.
Even though is already answered this worked for me:
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"backArrow.png"] style:UIBarButtonItemStyleBordered target:nil action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
BTW: even in iOS4 initializing my back button with initWithCustomView: didn't work for me. ;(
I too have been having problems with customView on a navigationItem.backBarButtonItem. I suspect it's probably just b0rked in the SDK.
While the workarounds outlined here do work, I've come up with a solution which is a little more elegant: it still uses navigationItem.leftBarButtonItem, but takes care of it for you automagically (no more need for 'child' view controllers to need to know anything about their 'parent' and/or to manually set navigationItem.leftBarButtonItem).
First up, have some class be a UINavigationControllerDelegate for the UINavigationController whose back button you're interested in. Then, in this class, set up something like the following willShowViewController delegate method:
-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
// reference to view controller stack
NSArray *viewControllers = [ navigationController viewControllers ];
if( [ viewControllers count ] > 1 ){
// the view controller we'll be linking to
UIViewController *backViewController = [ viewControllers objectAtIndex: [ viewControllers count ] - 2 ];
// create custom UIBarButtonItem
UIBarButtonItem *leftButton = [[ UIBarButtonItem alloc ] initWithCustomView: someCustomView ];
// set it as the leftBarButtonItem on the incoming viewcontroller
viewController.navigationItem.leftBarButtonItem = leftButton;
// tidy up
[ leftButton release ];
}
}
I had some further problems with this; it seems that UIBarButtonItem.action and UIBarButtonItem.target don't work when it's a navigationItem.leftBarButtonItem. So, you're left with a custom back button that doesn't actually go back. I'll leave responding to touches in your custom view as an exercise for the reader (I used a UIButton), but you'll need add this method to your delegate class:
-(void)onDummyBackButtonTapped{
[ someNavigationController popViewControllerAnimated: YES ];
}
and hook it up to fire when your custom view is tapped.
backBarButtonItem is not a read-only
property. I'm not sure why it behaves
so strangely, and the above is a valid
(if less-than-ideal) workaround.
It behaves strangely because setting a vc's backBarButtonItem doesn't change anything about the appearance of the vc's navigation item - instead, it changes the button that points BACK to the vc. See updating the navigation bar from Apple FMI.
That said I haven't had a whole lot of luck getting it to work myself. If you look around this site, you'll find some threads that suggest placing code very similar to what you already have immediately before the call to push a new view on the stack. I've had some luck there, but unfortunately not when it comes to using a custom image.
The navigationController's backBarButtonItem is set on the item whose title you're trying to affect.
i.e. in Page 1's view controller, say, viewdidLoad:
self.title = #"Page 1 of 4";
self.navigationItem.backBarButtonItem =
[[[UIBarButtonItem alloc] initWithTitle:#"Page 1"
style:UIBarButtonItemStyleBordered
target:nil
action:nil] autorelease];
You would not override this in Page 2.
Documentation for UINavigationItem : backBarButtonItem makes this clear:
When this item is the back item of the
navigation bar—when it is the next
item below the top item—it may be
represented as a back button on the
navigation bar. Use this property to
specify the back button. The target
and action of the back bar button item
you set should be nil. The default
value is a bar button item displaying
the navigation item’s title.
This is how I create a custom square back button with an arrow instead of the usual text.
I simply setup a delegate for my UINavigationController. I use the app delegate for that because the window root view controller is the UINavigationController i want to control.
So AppDelegate.m (ARC):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
((UINavigationController*)_window.rootViewController).delegate = self;
return YES;
}
#pragma mark - UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
if(navigationController.viewControllers.count > 1) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:[UIImage imageNamed:#"back-arrow.png"] forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor grayColor]];
button.frame = CGRectMake(0, 0, 44, 44);
viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
[button addEventHandler:^(id sender) {
[navigationController popViewControllerAnimated:YES];
} forControlEvents:UIControlEventTouchUpInside];
}
}
I'm using BlocksKit to catch the button tap event. It's very convenient for stuff like this but you can also use the regular addTarget:action:forControlEvents: method
I think I found the solution for this.
Simply set the button on the navigation item on the previous controller (The one that you want to go back to)
So if I have for example a root controller
and I push a second controller and want to customize the back button then I should do the following:
self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc]
initWithImage:[UIImage imageNamed:#"ico.png"] style:UIBarButtonItemStyleBordered
target:nil action:nil] autorelease];
Where self is the root view controller and not the second one.
My Logic:
Create a custom button (aka a custom view subclass)
Initialize a barbutton item with the custom button/view
Add an action that allows us to "go back" to our previous view controller
Set the left bar button item to this custom bar button item you created
Hide the back bar button item of the view controller you're pushing to
Step 3 was important. I figured the cleanest way to simulate the "go back" was to just utilize UINavigationController's method (popViewControllerAnimated:). So, I just add that action to the navigationController of the viewController I'm pushing (viewControllerToPush) like so:
[navItemButton addTarget:viewControllerToPush.navigationController action:#selector(popViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside];
Example:
UIViewController *viewControllerToPush = [[UIViewController alloc] init];
UIImage *navImage = [UIImage imageNamed:#"your_back_button_image"];
UIButton *navItemButton = [UIButton buttonWithType:UIButtonTypeCustom];
[navItemButton setImage:navImage forState:UIControlStateNormal];
[navItemButton setImage:navImage forState:UIControlStateHighlighted];
[navItemButton setFrame:CGRectMake(0, 0, navImage.size.width, navImage.size.height)];
[navItemButton addTarget:viewControllerToPush.navigationController action:#selector(popViewControllerAnimated:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:navItemButton];
viewControllerToPush.navigationItem.leftBarButtonItem = barButtonItem;
viewControllerToPush.navigationItem.hidesBackButton = YES;
[self.navigationController pushViewController:viewControllerToPush animated:YES];
You can use leftBarButtonItem instead of back button item. And to remove the default back button item set it to nil like follows;
navigationController?.navigationBar.backIndicatorImage = nil
navigationController?.navigationBar.backIndicatorTransitionMaskImage = nil
let button = UIButton.init(type: .custom)
button.imageView?.contentMode = UIViewContentMode.scaleAspectFit
button.setImage(UIImage.init(named: "top_back"), for: UIControlState.normal)
button.frame = CGRect.init(x: 0, y: 0, width: 75, height: 50)
button.addTarget(self, action: #selector(handleBackButton), for: .touchUpInside)
let barButton = UIBarButtonItem.init(customView: button)
self.navigationItem.leftBarButtonItem = barButton