How can I customize the navigation back button in iOS 7 and above without title? (i.e. with the arrow only)
self.navigationItem.leftBarButtonItem = self.editButtonItem;
I'm just wondering if they have any self.backButtonItem;
OR
something like this?
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemBACK
target:self action:#selector(back)];
It's actually pretty easy, here is what I do:
Objective C
// Set this in every view controller so that the back button displays back instead of the root view controller name
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStylePlain target:nil action:nil];
Swift 2
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
Swift 3
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
Put this line in the view controller that is pushing on to the stack (the previous view controller). The newly pushed view controller back button will now show whatever you put for initWithTitle, which in this case is an empty string.
I found an easy way to make my back button with iOS single arrow.
Let's supouse that you have a navigation controller going to ViewA from ViewB. In IB, select ViewA's navigation bar, you should see these options: Title, Prompt and Back Button.
ViewA navigate bar options
The trick is choose your destiny view controller back button title (ViewB) in the options of previous view controller (View A). If you don't fill the option "Back Button", iOS will put the title "Back" automatically, with previous view controller's title. So, you need to fill this option with a single space.
Fill space in "Back Button" option
The Result:
Just use an image!
OBJ-C:
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"Icon-Back"]
style:UIBarButtonItemStylePlain
target:self.navigationController
action:#selector(popViewControllerAnimated:)];
self.navigationItem.leftBarButtonItem = backButton;
}
SWIFT 4:
let backBTN = UIBarButtonItem(image: UIImage(named: "Back"),
style: .plain,
target: navigationController,
action: #selector(UINavigationController.popViewController(animated:)))
navigationItem.leftBarButtonItem = backBTN
navigationController?.interactivePopGestureRecognizer?.delegate = self
Icon-Back.png
Icon-Back#2x.png
Icon-Back#3x.png
iOS7 has new interface rules, so It's better to keep at least the back arrow when you push a UIView.
It's very easy to change the "back" text programmatically. Just add this code before push the view (Or prepareForSegue if you are using StoryBoards):
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
self.navigationItem.backBarButtonItem=[[UIBarButtonItem alloc] initWithTitle:#"NEW TITLE" style:UIBarButtonItemStylePlain target:nil action:nil];
}
This will change the default "Back" text, but will keep the iOS7 styled back arrow.
You can also change the tint color for the back arrow before push the view:
- (void)viewDidLoad{
//NavBar background color:
self.navigationController.navigationBar.barTintColor=[UIColor redColor];
//NavBar tint color for elements:
self.navigationController.navigationBar.tintColor=[UIColor whiteColor];
}
Hope this helps you!
Nothing much you need to do. You can achieve the same through storyboard itself.
Just go the root Navigation controller and give a space. Remember not to the controller you wanted the back button without title, but to the root navigation controller.
While Kyle Begeman's answer totally does the trick, it is quite annoying to have this code in every view controller possible. I ended up with a simple UINavigationItem category. Beware, here be dragons! Sorry, I mean, swizzling:
#import <objc/runtime.h>
#implementation UINavigationItem (ArrowBackButton)
static char kArrowBackButtonKey;
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method m1 = class_getInstanceMethod(self, #selector(backBarButtonItem));
Method m2 = class_getInstanceMethod(self, #selector(arrowBackButton_backBarButtonItem));
method_exchangeImplementations(m1, m2);
});
}
- (UIBarButtonItem *)arrowBackButton_backBarButtonItem {
UIBarButtonItem *item = [self arrowBackButton_backBarButtonItem];
if (item) {
return item;
}
item = objc_getAssociatedObject(self, &kArrowBackButtonKey);
if (!item) {
item = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStyleBordered target:nil action:NULL];
objc_setAssociatedObject(self, &kArrowBackButtonKey, item, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return item;
}
#end
This works, but it will remove the title of the previous item, even if you pop back to it:
self.navigationController.navigationBar.topItem.title = #"";
Just set this property on viewDidLoad of the pushed View Controller.
EDIT: 2014-04-09: As I gained reputations, I feel sorry because I don't use this trick any more. I recommend Kyle's answer. Also notice that the self of self.navigationItem.backBarButtonItem isn't the view controller the back button is displayed, but the previous view controller to be went back.
If you don't need to have title text for the previous view controller, just fill the title with a blank string;
self.navigationItem.title = #"";
[self.navigationController pushViewController:viewController animated:YES];
This will prevent showing "back" with chevron on the pushed view controller.
EDIT: Even you use non-blank title text, setting the title of the previous view controller in viewWillAppear: works except the title can flicker in a blink when view controller popped. I think "The twitter app" seems to do more subtle hack to avoid the flicker.
This is how I do it and the simplest, works and most clear way to do it.
This works if embed on Navigation Controller
Swift 3
In viewDidLoad I add this to the View Controller you want the back button to be just arrow.
if let topItem = self.navigationController?.navigationBar.topItem {
topItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
The difference of this to #Kyle Begeman's answer is that you call this on the view controller that you want the back button to be just arrow, not on the pushing stack view controller.
You don't have access to the navigation backButtonItem with the way you want, you need to create your own back button like below:
- (void)loadView
{
[super loadView];
UIButton *backButton = [[UIButton alloc] initWithFrame: CGRectMake(0, 0, 44.0f, 30.0f)];
[backButton setImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(popVC) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
}
And off course:
- (void) popVC{
[self.navigationController popViewControllerAnimated:YES];
}
Target:
customizing all back button on UINavigationBar to an white icon
Steps:
1. in "didFinishLaunchingWithOptions" method of AppDelete:
UIImage *backBtnIcon = [UIImage imageNamed:#"navBackBtn"];
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
[UINavigationBar appearance].tintColor = [UIColor whiteColor];
[UINavigationBar appearance].backIndicatorImage = backBtnIcon;
[UINavigationBar appearance].backIndicatorTransitionMaskImage = backBtnIcon;
}else{
UIImage *backButtonImage = [backBtnIcon resizableImageWithCapInsets:UIEdgeInsetsMake(0, backBtnIcon.size.width - 1, 0, 0)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -backButtonImage.size.height*2) forBarMetrics:UIBarMetricsDefault];
}
2.in the viewDidLoad method of the common super ViewController class:
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0")) {
UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:#""
style:UIBarButtonItemStylePlain
target:nil
action:nil];
[self.navigationItem setBackBarButtonItem:backItem];
}else{
//do nothing
}
SWIFT 4
For those looking to create custom back buttons as well as have their title removed please use the following piece of code within the view controller that's pushing the new one:
self.navigationController?.navigationBar.backIndicatorImage = UIImage(named: "close")
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage(named: "close")
self.navigationItem?.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
For a more universal use, do the following:
Create a universal function as follows:
func addCustomizedBackBtn(navigationController: UINavigationController?, navigationItem: UINavigationItem?) {
navigationController?.navigationBar.backIndicatorImage = UIImage(named: "close")
navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage(named: "close")
navigationItem?.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
}
Then use it in the view controllers as follows:
addCustomizedBackBtn(navigationController: self.navigationController, navigationItem: self.navigationItem)
Simple hack from iOS6 works on iOS7 too:
[UIBarButtonItem.appearance setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault];
Edit:
Don't use this hack. See comment for details.
you can use this. This works perfectly for me by just adding a UIButton as a custumview for the UIBarButtonItem.
Try the Below Code
self.navigationItem.leftBarButtonItem=[self backButton];
- (UIBarButtonItem *)backButton
{
UIImage *image = [UIImage imageNamed:#"back-btn.png"];
CGRect buttonFrame = CGRectMake(0, 0, image.size.width, image.size.height);
UIButton *button = [[UIButton alloc] initWithFrame:buttonFrame];
[button addTarget:self action:#selector(backButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[button setImage:image forState:UIControlStateNormal];
UIBarButtonItem *item= [[UIBarButtonItem alloc] initWithCustomView:button];
return item;
}
Create a UILabel with the title you want for your root view controller and assign it to the view controller's navigationItem.titleView.
Now set the title to an empty string and the next view controller you push will have a back button without text.
self.navigationItem.titleView = titleLabel; //Assuming you've created titleLabel above
self.title = #"";
// add left bar button item
try this code:
- (void)viewDidLoad
{
[super viewDidLoad];
UIImage* image_back = [UIImage imageNamed:#"your_leftarrowImage.png"];
CGRect backframe = CGRectMake(250, 9, 15,21);
UIButton *backbutton = [[UIButton alloc] initWithFrame:backframe];
[backbutton setBackgroundImage:image_back forState:UIControlStateNormal];
[backbutton addTarget:self action:#selector(Btn_back:)
forControlEvents:UIControlEventTouchUpInside];
[backbutton setShowsTouchWhenHighlighted:YES];
UIBarButtonItem *backbarbutton =[[UIBarButtonItem alloc] initWithCustomView:backbutton];
self.navigationItem.leftBarButtonItem=backbarbutton;
[backbutton release];
}
-(IBAction)Btn_back:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
All the answers do not solve the issue. It is not acceptable to set back button title in every view controller and adding offset to the title still makes next View Controller title shift to the right.
Here is the method using method swizzling, just create new extension to UINavigationItem
import UIKit
extension UINavigationItem {
public override class func initialize() {
struct Static {
static var token: dispatch_once_t = 0
}
// make sure this isn't a subclass
if self !== UINavigationItem.self {
return
}
dispatch_once(&Static.token) {
let originalSelector = Selector("backBarButtonItem")
let swizzledSelector = #selector(UINavigationItem.noTitleBackBarButtonItem)
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
// MARK: - Method Swizzling
struct AssociatedKeys {
static var ArrowBackButtonKey = "noTitleArrowBackButtonKey"
}
func noTitleBackBarButtonItem() -> UIBarButtonItem? {
if let item = self.noTitleBackBarButtonItem() {
return item
}
if let item = objc_getAssociatedObject(self, &AssociatedKeys.ArrowBackButtonKey) as? UIBarButtonItem {
return item
} else {
let newItem = UIBarButtonItem(title: " ", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
objc_setAssociatedObject(self, &AssociatedKeys.ArrowBackButtonKey, newItem as UIBarButtonItem?, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
return newItem
}
}
}
I applied the following code in viewDidLoad and it works:
// this will set the back button title
self.navigationController.navigationBar.topItem.title = #"Test";
// this line set the back button and default icon color
//[[self.navigationController.navigationBar.subviews lastObject] setTintColor:[UIColor blackColor]];
this line change the back default icon to your custom icon
[[self.navigationController.navigationBar.subviews lastObject] setTintColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"menuicon"]]];
Just to update I use Vector Icon
You can subclass UINavigationController, set itself as the delegate, and set the backBarButtonItem in the delegate method navigationController:willShowViewController:animated:
#interface Custom_NavigationController : UINavigationController <UINavigationControllerDelegate>
#end
#implementation Custom_NavigationController
- (void)viewDidLoad
{
[super viewDidLoad];
self.delegate = self;
}
#pragma mark - UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
viewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStylePlain target:nil action:nil];
}
#end
Set back title empty
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStyleDone target:self action:#selector(handleBack:)];
[backButton setTintColor:Color_WHITE];
[self.navigationItem setBackBarButtonItem:backButton];
Change back image
UIImage *backImg = [[UIImage imageNamed:#"ic_back_white"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[UINavigationBar appearance].backIndicatorImage = backImg;
[UINavigationBar appearance].backIndicatorTransitionMaskImage = backImg;
Check this answer
How to change the UINavigationController back button name?
set title text to string with one blank space as below
title = " "
Don't have enough reputation to add comments :)
I have been using this solution since iOS 5 or so without any problems. I made a utility function that I call in my view controllers. You need to do it either in viewDidLoad or any point after that.
void updateBackButtonTextForViewController(UIViewController *viewController, NSString *text)
{
if(! viewController.navigationItem.backBarButtonItem)
{
viewController.navigationItem.backBarButtonItem =
[[UIBarButtonItem alloc] initWithTitle:text
style:UIBarButtonItemStylePlain
target:nil action:nil];
}
else
{
viewController.navigationItem.backBarButtonItem.title = text;
}
}
In some cases the navigation item may already exist, in other cases it needs to be created. This accounts for both of those cases without messing with the navigation item title. It allows you to remove the title by simply passing in #"".
The only way that worked for me was:
navigationController?.navigationBar.backItem?.title = ""
UPDATE:
When I changed the segue animation flag to true (It was false before), the only way that worked for me was:
navigationController?.navigationBar.topItem?.title = ""
If you have two ViewController(FirstVC, SecondVC) Embed in Navigation Controller, and you want there is only back arrow in SecondVC.
You can try this
In FirstVC's ViewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
}
Then when you push into SecondVC, you'll see the there is only back arrow
If you set the tintColor Of NavigationBar,add a custom back button image without title that tint color will reflect the image color. Please follow this apple documentaion link.
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/UIKitUICatalog/index.html#//apple_ref/doc/uid/TP40012857-UIView-SW7
UINavigationItem *navItem = [[UINavigationItem alloc] init];
navBar.tintColor = self.tintColor;
UIImage *myImage = [UIImage imageNamed:#"left_arrow.png"];
myImage = [myImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIBarButtonItem *leftButton = [[UIBarButtonItem alloc] initWithImage:myImage style:UIBarButtonItemStylePlain target:self action:#selector(cancelButtonFunction:)];
navItem.leftBarButtonItem = leftButton;
navBar.items = #[ navItem ];
I'm written an extension to make this easier:
extension UIViewController {
/// Convenience for setting the back button, which will be used on any view controller that this one pushes onto the stack
#objc var backButtonTitle: String? {
get {
return navigationItem.backBarButtonItem?.title
}
set {
if let existingBackBarButtonItem = navigationItem.backBarButtonItem {
existingBackBarButtonItem.title = newValue
}
else {
let newNavigationItem = UIBarButtonItem(title: newValue, style:.plain, target: nil, action: nil)
navigationItem.backBarButtonItem = newNavigationItem
}
}
}
}
You can change the title to #"" of the current ViewController on viewWillDisappear, and when it's about to show again re-set the title to whatever it was before.
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.title = #"Previous Title";
}
-(void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.title = #"";
}
Change backItem.title = "" to using topItem.title = ""
Setting navigationItem.hidesBackButton = true & navigationItem.leftBarButtonItem will lose the back gesture
Remember we have to create 2 instances of the back image
My solution will change the image & keep the back gesture:
navigationController?.navigationBar.backIndicatorImage = UIImage(named: "back")
navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage(named: "back")
navigationController?.navigationBar.topItem?.title = ""
In the prepareForSegue: method of your first ViewController you set that views title to #"", so when the next view is pushed it will display the previous ViewController title which will be #"".
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
self.navigationItem.title = #" ";
}
The only problem with this is that when you hit the back button your previous view won't have a title, so you may add it again on viewWillAppear:
-(void)viewWillAppear:(BOOL)animated{
self.navigationItem.title = #"First View Title";
}
I don't like very much this solution but it works and i didn't find other way to do it.
To add to Thomas C's answer above, sometimes putting a single space doesn't work and you have to keep adding spaces.
You'll know you succeeded when you see "Bar Button Item - " under the "Navigation Item". That's in the Document Outline (Editor->Show Document Outline). Once you see the above picture, you can delete a few spaces and see if it still works.
Related
I'm using a custom back button in my app. This custom back button is set globally like this:
UINavigationBar.appearance().backIndicatorImage = UIImage(named: "Back").withRenderingMode(.alwaysOriginal)
UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(asset: .back).withRenderingMode(.alwaysOriginal)
Before iOS 11 this code did the trick, but now in iOS 11 the button is not centered vertically anymore as you can see here:
I could change the height of the back button image to 44, but that would break it in iOS < 11. I could also use two different images, but I was looking for something cleaner, like a way to vertically center the image in the back button container view.
EDIT:
Turns out that, as said by banxii1988, the problem is caused by setBackButtonTitlePositionAdjustment when the values deliberately move the title outside the visible screen. That was an hack to avoid removing the back button title in every view controller. I decided to remove this hack and I did the right thing which is:
set the back button item in the storyboard to " ".
in each view controller without an associated storyboard, I set
the backBarButtonItem programmatically
navigationItem.backBarButtonItem = UIBarButtonItem(title: "",
style: .plain, target: nil, action: nil)
Note that the back button title that you see in a view controller is set in the previous one in the navigation stack.
1) remove PositionAdjustment if have any. such as
bap.setBackButtonTitlePositionAdjustment(UIOffset(horizontal: 0, vertical: -64), for: .default)
2) check if the previous ViewController in nav stack has a title
For anyone who couldn't resolve it:
1) Search in your project (cmd+shift+f) for "setBackButtonTitle", you will find a code like below:
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:#[[xController class]]] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault];
2) Change code above with this one:
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:#[[xController class]]] setBackButtonTitlePositionAdjustment:UIOffsetMake(-200, 0) forBarMetrics:UIBarMetricsDefault];
Since iOS 11, above code we used to hide back button titles, moves back button along with the title itself. However if you just move the back button title horizontally, everything works fine and there's no backdraws.
I think this method is ok! It's useful for me.
if(#available(iOS 11, *)) {
[[UIBarButtonItem appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor clearColor]} forState:UIControlStateNormal];
[[UIBarButtonItem appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor clearColor]} forState:UIControlStateHighlighted];
} else {
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(-60, -60) forBarMetrics:UIBarMetricsDefault];
}
This solution works for me for iOS 9, 10 and 11
var backButtonImage: UIImage = UIImage(named: "backButton")!
UINavigationBar.appearance().backIndicatorImage = backButtonImage
UINavigationBar.appearance().backIndicatorTransitionMaskImage = backButtonImage
if #available(iOS 11, *) {
UIBarButtonItem.appearance().setBackButtonTitlePositionAdjustment(UIOffsetMake(-300, 0), for:UIBarMetrics.default)
} else {
UIBarButtonItem.appearance().setBackButtonTitlePositionAdjustment(UIOffsetMake(0, -200), for:UIBarMetrics.default)
}
After several tries and fails, this worked for us for iOS 11:
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(-200, -5) forBarMetrics:UIBarMetricsDefault];
The trick was to move the "Back" text on the x axis quite a lot back, and a bit on the y axis, since setBackButtonTitlePositionAdjustment turned out to be affecting both the image and the text.
However, we don't know how long this solution will work, it might be "fixed" by Apple at any time
i would recommend method swizzling to resolve it, i've used this solution in some of my projects and it works fine.
1 - create a category of UIViewController.
2- import #import <objc/runtime.h>.
3- paste the following method
#import "UIViewController+Extras.h"
#import <objc/runtime.h>
#implementation UIViewController (Extras)
+ (void)load {
static dispatch_once_t once_token;
dispatch_once(&once_token, ^{
SEL viewDidLoadSelector = #selector(viewDidLoad);
SEL viewDidLoadModifyBackButtonSelector = #selector(modifyBackButton_viewDidLoad);
Method originalMethod = class_getInstanceMethod(self, viewDidLoadSelector);
Method extendedMethod = class_getInstanceMethod(self, viewDidLoadModifyBackButtonSelector);
method_exchangeImplementations(originalMethod, extendedMethod);
});
}
- (void)modifyBackButton_viewDidLoad {
[self modifyBackButton_viewDidLoad];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"" style:self.navigationItem.backBarButtonItem.style target:nil action:nil];
}
#end
if(#available(iOS 11, *)) {
[[UIBarButtonItem appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor clearColor]} forState:UIControlStateNormal];
[[UIBarButtonItem appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor clearColor]} forState:UIControlStateHighlighted];
} else {
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(-60, -60) forBarMetrics:UIBarMetricsDefault];
}
The above solution by #Tonin works, but the only concern is nav bar left button text (like "Cancel") is transparent (clear color) when share to other app (email, message, etc), flip color if need share feature:
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:#[title, URL] applicationActivities:nil];
[activityViewController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
// set clear color when back from share
[[UIBarButtonItem appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor clearColor]} forState:UIControlStateNormal];
}];
[self presentViewController:activityViewController animated:YES completion:^{
// set white color when share to other app
[[UIBarButtonItem appearance] setTitleTextAttributes:#{NSForegroundColorAttributeName: [UIColor whiteColor]} forState:UIControlStateNormal];
}];
FOR SWIFT 3+
if #available(iOS 11, *) {
UIBarButtonItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: .normal)
UIBarButtonItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.clear], for: .highlighted)
} else {
UIBarButtonItem.appearance().setBackButtonTitlePositionAdjustment(UIOffsetMake(0,
-60), for:UIBarMetrics.default)
}
1.
extension UINavigationController {
func pushViewC(_ viewController: UIViewController, animated: Bool) {
viewController.navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "return").withRenderingMode(.alwaysOriginal), style: .plain, target: navigationController, action: #selector(popViewController(animated:)))
pushViewController(viewController, animated: animated)
}
}
2.use pushViewC instead of pushViewController
navigationController?.pushViewC(otherVC, animated: true)
I have to create a button multiple times on different ViewControllers and add the event to it. What i have done i created a class to handle them. I pass my UINavigationController to it and add a button to it. but nothing happens. i mean the button doesn't display. i call my Util class like
MyUtil *util = [[MyUtil alloc];
[util initWithNavAndAddBackButton:self.NavigationController];
// now it should display my custom back button which i have created but it is not displayed.
// i have passed nav by value and add a button to it but don't know why it is not showing it.
MyUtil.h
-(void)initWithNavAndAddBackButton:(UINavigationController *)nav;
MyUtil.m
-(void)initWithNavAndAddBackButton:(UINavigationController *)nav
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setImage:[UIImage imageNamed:#"icon_back.png"] forState:UIControlStateNormal];
// [btn setTarget:self action:#selector(popView:) forControlEventTouchUPInside];
// how can i pass self(Context in Android) that for which activity it is calling for
[button setFrame:CGRectMake(0,0,36,20);
UIBarButtonItem *barbtn = [[UIBarButton alloc] initWithCustomView:btn];
nav.navigationController.navigationItem.leftBarButtonItem = barbtn;
}
-(void) popView:(UINavigationController *)nav
{
[nav popViewControllerAnimated:YES];
}
Problems i have
First i have pass UINaviagation and add the UIBarbutton to it but it is not displaying .When I write the same code in each ViewController class . it is running accurately. I simply want to do this in a seperate class like a handler.
And i also want to push and pop in that activity. How can i pass self that or i mean how does this class knows the target that for which class this action belongs to. How can i pass the event for the ViewContoller.
Try returning your navigation controller instance to ViewController from MyUtil.
MyUtil.h
-(UIBarButtonItem *)initWithNavAndAddBackButton();
MyUtil.m
-(UIBarButtonItem *)initWithNavAndAddBackButton()
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setImage:[UIImage imageNamed:#"icon_back.png"] forState:UIControlStateNormal];
[button setFrame:CGRectMake(0,0,36,20);
UIBarButtonItem *barbtn = [[UIBarButton alloc] initWithCustomView:btn];
return barbtn;
}
ViewController.m
MyUtil *util = [[MyUtil alloc];
self.navigationItem.rightBarButtonItem = [util initWithNavAndAddBackButton:self.NavigationController];
Because when you pass an object, it is not passed by reference.
MyUtil.m
+ (void)setNavigationItem:(UINavigationItem *)navItem forViewController:(UIViewController *)controller
{
UIImage *image = [UIImage imageNamed:#"icon_back.png"];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithImage:image
style:UIBarButtonItemStylePlain
target:controller
action:#selector(popView)];
navItem.leftBarButtonItem = item;
}
ViewController.m
[MyUtil setNavigationItem:self.navigationItem forViewController:self];
- (void)popView
{
[self.navigationController popViewControllerAnimated:YES];
}
MyUtil *util = [[MyUtil alloc];
[util initWithNavAndAddBackButton:self.NavigationController];
Should be
MyUtil *util = [[MyUtil alloc] init];
[util initWithNavAndAddBackButton:self.NavigationController];
You are used with Android. In iOS is diffrent.
self.navigationController takes care of pushing and pop viewcontrollers
If you want a custom button to be used everywhere you can use the delegate method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setMyButtonHere:button];
}
}
i have a problem with navigation bar title. i have 2 screen, when i click back button on screen 2 it will back to screen 1 but the title of screen 1 is disappear. Here is my snippet code
screen1 .m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = #"title 1";
....
screen 2 .m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = self.stringTitle;
// change the back button and add an event handler
self.navigationItem.hidesBackButton = YES;
//set custom image to button if needed
UIImage *backButtonImage = [UIImage imageNamed:#"btn_back.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:backButtonImage forState:UIControlStateNormal];
button.frame = CGRectMake(-12, 2, backButtonImage.size.width, backButtonImage.size.height);
[button addTarget:self action:#selector(backAction) forControlEvents:UIControlEventTouchUpInside];
UIView *backButtonView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, backButtonImage.size.width-15, backButtonImage.size.height)];
[backButtonView addSubview:button];
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:backButtonView];
self.navigationItem.leftBarButtonItem = customBarItem;
self.navigationController.navigationBar.titleTextAttributes = #{NSForegroundColorAttributeName: [UIColor whiteColor]};
....
}
- (void) backAction
{
[self.navigationController popViewControllerAnimated:YES];
}
i've tried several tricks to make title of screen 1 appears with the following tricks:
set title on viewwillappear method
- (void)viewWillAppear:(BOOL)animated{self.title = #"tes";}
set title on viewdidappear method
- (void)viewDidAppear:(BOOL)animated{
self.navigationController.navigationBar.topItem.title = #"YourTitle";
self.title = #"tes";
}
but the title is still disappears. please help
here is the way how i moving to next screen
- (IBAction)btGenerateTokenAction:(id)sender {
SofttokenResultController *controller = [[SofttokenResultController alloc] initWithNibName:#"SofttokenResultController" bundle:nil];
controller.navigationController.navigationBar.backItem.title = #"Kembali";
[ViewLoadingUtil animateToNextView:controller from:self :#""];
}
+ (void) animateToNextView:(UIViewController *)controller from:(UIViewController *)fromController :(NSString *)navTitle{
NSLog(#"animateToNextView called");
[fromController.navigationController pushViewController:controller animated:YES];
}
This solved my problem:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated: animated)
self.navigationItem.title="Title text"
}
In my case, it was the backItem?.title that was the problem. In both my view controller and within a custom subclass of UINavigationController I was setting it to an empty string:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
...
navigationController.navigationBar.backItem?.title = ""
}
My guess would be that when using popViewController(animated:) the title is taken from backItem?.title and set into the main title attribute.
In viewWillAppear implement this:
self.navigationItem.title="Title text";
it usually happen when you set backItem?.title = "" because the title of the back controller is taken from this property
I face this issue from months and solved it just by commenting this line
Working for me solution. You can try this one:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.topItem?.title = ""
self.navigationItem.title = "Title"
}
Objective c solution:
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear: animated];
self.navigationItem.title = #"Title text";
}
I come from the Swift world. However I tried many solutions. These were caused by the animation. If you change the Bool to false, it will solve your problem. Finally you can put your code in the viewWillAppear function.
override func viewWillAppear(animated: Bool) {
if animated {
//...
}
}
adding a button to navigation bar on first nib viewController and wanted to view this button to only on main screen not on any other screen
i have worked out on it
i made the button and added to subview of navigation bar
on every button action i have put this
- (IBAction)forth:(id)sender {
forthView *forthview = [[forthView alloc]init];
[self.navigationController pushViewController:forthview animated:YES];
btn.hidden = YES;
}
after this button hides but didnot show up when i got back to main screen
my code for viewdidload is here
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.title = #"My First View";
self.navigationController.navigationBar.barStyle = UIBarStyleBlackTranslucent;
btn = [[UIButton alloc] init];
btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = CGRectMake(10, 10, 65, 30);
[btn setTitle:#"Show" forState:UIControlStateNormal];
[self.navigationController.navigationBar addSubview:btn];
btn.hidden = false;
}
Once you hide the button so you need to unhide it again. here is the simple code to unhide the button:
btn.hidden = No
viewDidLoadis only called the first time your view is loaded. Add an NSLog()statement if you want to test when it gets called. Every time your view appers on screen viewWillAppearis called.
So you need to show the button in viewWillAppear
It seems likely that what you really want to be doing is adding a UIBarButtonItem to your main view controller's navigationItem.leftBarButtonItem property. That way the button will have the correct appearance and show and hide as the view controller is displayed.
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Show"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(onShowButtonPressed:)];
How can I change the title of a UIBarButtonItem? I have the following code which is called when an edit button is pressed on my UINavigationBar.
-(void)editButtonSelected:(id)sender {
NSLog(#"edit button selected!");
if(editing) {
NSLog(#"notediting");
[super setEditing:NO animated:NO];
[tableView setEditing:NO animated:NO];
[tableView reloadData];
[rightButtonItem setTitle:#"Edit"];
[rightButtonItem setStyle:UIBarButtonItemStylePlain];
editing = false;
}
else {
NSLog(#"editing");
[super setEditing:YES animated:YES];
[tableView setEditing:YES animated:YES];
[tableView reloadData];
[rightButtonItem setTitle:#"Done"];
[rightButtonItem setStyle:UIBarButtonItemStyleDone];
editing = true;
}
}
The edit button is changing color (so the line which sets the style is working), however the line which sets the title of the button is not working.
I've done the following to dynamically change the title of a UIBarButtonItem. In this situation I am not using a UIViewTableController and cannot use the standard editButton. I have a view with a tableView as well as other subviews and wanted to emulate the behavior of the limited UIViewTableController.
- (void)InitializeNavigationItem
{
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:2];
UIBarButtonItem* barButton;
barButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self
action:#selector(addNewItem:)];
barButton.style = UIBarButtonItemStyleBordered;
[array addObject:barButton];
// --------------------------------------------------------------------------------------
barButton = [[UIBarButtonItem alloc] initWithTitle:#"Edit" style:UIBarButtonItemStyleBordered
target:self
action:#selector(editMode:)];
barButton.style = UIBarButtonItemStyleBordered;
barButton.possibleTitles = [NSSet setWithObjects:#"Edit", #"Done", nil];
[array addObject:barButton];
self.navigationItem.rightBarButtonItems = array;
}
- (IBAction)editMode:(UIBarButtonItem *)sender
{
if (self.orderTable.editing)
{
sender.title = #"Edit";
[self.orderTable setEditing:NO animated:YES];
}
else
{
sender.title = #"Done";
[self.orderTable setEditing:YES animated:YES];
}
}
Note that I didn't use the the UIBarButtonSystemItemEdit barButton, you cannot manually change the name of that button, which makes sense.
You also might want to take advantage of the possibleTitles property so that the button doesn't resize when you change the title.
If you are using a Storyboard/XIB to create/set these buttons, ensure that the Bar Button Item Identifier is set to Custom for the button which you'd want to control the title for.
I had this problem and resolved it by setting the UIBarButtonItem style to the custom type when it's initialised. Then the titles would set when changing their title values.
You may also want to set the possibleTitle value in the viewDidLoad method to ensure the button is sized correctly for all the possible titles it can have.
In my case what prevented the title being displayed was that in the xib I'd selected the Bar button item 'identifier' property as 'Cancel'.
I tried setting the title property even before assigning the button to the navigation bar, but the title was not being updated.
I made it like this:
And it started working just as I wanted.
If you look at the documentation of the title property, it is explicitly mentioned that you should set it before assigning it to the navigation bar. Instead of doing what you're doing right now, you can use two bar button items – one for done and one for edit, and set them alternatively.
Actually if all you want if switching between "Edit" and "Done", just use
self.navigationItem.rightBarButtonItem = self.editButtonItem;
It will handle this transition for you
Just switch out the buttons each time the user presses them.
- (void)setNavigationButtonAsEdit
{
UIBarButtonItem* editButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"Edit", #"")
style:UIBarButtonItemStylePlain
target:self
action:#selector(editButtonPressed:)];
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObject:editButton];
}
- (void)setNavigationButtonAsDone
{
UIBarButtonItem* doneButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"Done", #"")
style:UIBarButtonItemStylePlain
target:self
action:#selector(editButtonPressed:)];
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObject:doneButton];
}
And then create an IBAction to handle the button press:
- (IBAction)editButtonPressed:(id)sender
{
NSString* buttonText = [sender title];
if ([buttonText isEqualToString:NSLocalizedString(#"Edit",#"")])
{
[self setNavigationButtonAsDone];
}
else
{
[self setNavigationButtonAsEdit];
}
}
Swift 3.0, 3.2, or 4.0
If your custom ViewController containing a tableView object follows the UITableViewController Delegate and Datasource protocols, you can append a system editBarButtonItem to your navigationItem.leftBarButtonItem(s) or navigationItem.rightBarButtonItem(s) with the following code:
func setupTableView() {
tableView.delegate = self
tableView.dataSource = self
navigationItem.rightBarButtonItem = editButtonItem
editButtonItem.action = #selector(CustomViewController.editTableView(_:))
}
#objc func editTableView(_ sender: UIBarButtonItem) {
tableView.isEditing = !tableView.isEditing
if tableView.isEditing {
sender.title = "Done"
} else {
sender.title = "Edit"
}
}
Try:
UIButton *rightButton;
rightButton = (UIButton*)self.navigationItem.rightBarButtonItem.customView;
[rightButton setTitle:#"Done" forState:UIControlStateNormal];
Because rightBarButtonItem.customView == “the button your added”.