UIBarButtonItem changing title not working - ios

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”.

Related

How to pass self to another class in iOS

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];
}
}

Does changing a button's title prevent further action?

I have a bar button, let's call it button 1, which when clicked, programatically segues to a different page.
However, after clicking another button (button 2), button 1's title is changed.
From then on, button 1 no longer responds regardless of whether it's title has been changed back by clicking button 2 again.
I hope you understand, if not - I can clarify
code: (button 2 is edit) - (1 is info/add)
-(void)editButton:(id)sender
{
[self setEditing:(![self isEditing])];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Done"
style: UIBarButtonItemStylePlain
target:self
action:#selector(edit:)];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Add"
style: UIBarButtonItemStylePlain
target:self
action:#selector(infoToAdd:)];
}
-(void) edit:(UIBarButtonItem *) barBtnItem
{
// if not editing
if (![self isEditing]) {
[self setEditing:YES];
[self.navigationItem.rightBarButtonItem setTitle:#"Done"];
[self.navigationItem.leftBarButtonItem setTitle:#"Add"];
} else {
[self setEditing:NO];
[self.navigationItem.rightBarButtonItem setTitle:#"Edit"];
[self.navigationItem.leftBarButtonItem setTitle:#"Info"];
}
}
-(void) infoToAdd:(UIBarButtonItem *) barBtnItem
{
// if not editing
if (![self isEditing]) {
[self.navigationItem.leftBarButtonItem setTitle:#"Info"];
} else {
[self.navigationItem.leftBarButtonItem setTitle:#"Add"];
}
}
-(void)infoAddButton:(id)sender
{
if (self.tableView.isEditing) {
[self performSegueWithIdentifier:#"segueToAdd" sender:self];
} else {
[self performSegueWithIdentifier:#"segueToInfo" sender:self];
}
}
Edit:
It seems the function 'infoAddButton' is not even getting called (I used a breakpoint) when I press the button, after changing it's title.
i think this is help you. here explain only left button same logic you can implement for right button
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Menu" style:UIBarButtonItemStyleBordered target:self action:#selector(dosomeTask:)];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"show" style:UIBarButtonItemStyleBordered target:self action:#selector(dosomeTask1:)];
isValid is a Bool type. so initially in your viewDidLoad set NO
-(void)dosomeTask:(id)sender{
if (isValid) {
self.navigationItem.leftBarButtonItem.title=#"Menu";
isValid=NO;
[self infomationShow];
}else{
self.navigationItem.leftBarButtonItem.title=#"show";
isValid=YES;
[self menuController];
}
}
-(void)menuController{
NSLog(#"menu");
}
-(void)infomationShow{
NSLog(#"infomationShow");
}

iOS 7 change backbarbuttonitem text [duplicate]

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.

How to show/hide search bar with same toolbar button

I have a toolbar button
UIBarButtonItem *systemItem2 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:#selector(pressButton2:)];
systemItem2.style = UIBarButtonItemStyleBordered;
and a press action
- (void) pressButton2:(id)sender{
mapSearch.hidden = NO;
}
in viewWillAppear
- (void)viewWillAppear:(BOOL)animated
{
mapSearch.hidden = YES;
}
How can I show and hide searchbar with same button (second press)?
I understand you would like to toggle the mapSearch.hidden. Here is a solution
mapSearch.hidden = !mapSearch.hidden;
or
mapSearch.hidden = (mapSearch.hidden) ? NO : YES;

How to Show UIPickerView when selecting UITextField

Here is a screenshot of what I did till now:
So what I am trying to do is when you select "pick a name" Textfield I need a Picker to show up, with the input #"Jack".
Since iOS 3.2, UITextField supports the inputView property to assign a custom view to be used as a keyboard, which provides a way to display a UIPickerView:
You could use the inputView property of the UITextField, probably combined with the inputAccessoryView property. You assign your pickerView to the inputView property, and, to dismiss the picker, a done button to the inputAccessoryView property.
UIPickerView *myPickerView = [[UIPickerView alloc] init];
//myPickerView configuration here...
myTextField.inputView = myPickerView;
Like that. This will not give you a direct way to dismiss the view since your UIPickerView has no return button, which is why I recommend to use the inputAccessoryView property to display a toolbar with a done button (the bar is just for aesthetics, you might as well just use a UIButton object):
UIToolbar *myToolbar = [[UIToolbar alloc] initWithFrame:
CGRectMake(0,0, 320, 44)]; //should code with variables to support view resizing
UIBarButtonItem *doneButton =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self action:#selector(inputAccessoryViewDidFinish)];
//using default text field delegate method here, here you could call
//myTextField.resignFirstResponder to dismiss the views
[myToolbar setItems:[NSArray arrayWithObject: doneButton] animated:NO];
myTextField.inputAccessoryView = myToolbar;
I use this and find this a lot cleaner than adding a subview and animating the UIPicker
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
responder = textField;
if ([textField isEqual:self.txtBirthday]) {
UIDatePicker *datepicker = [[UIDatePicker alloc] initWithFrame:CGRectZero];
[datepicker setDatePickerMode:UIDatePickerModeDate];
textField.inputView = datepicker;
}
return YES;
}
it will work for you .. i have edited it .and for that you have to set delegate for textfield. and create a UIPIckrView in NIb file.
- (BOOL) textFieldShouldBeginEditing:(UITextView *)textView
{
pickrView.frame = CGRectMake(0, 500, pickrView.frame.size.width, pickrView.frame.size.height);
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:.50];
[UIView setAnimationDelegate:self];
pickrView.frame = CGRectMake(0, 200, pickrView.frame.size.width, pickrView.frame.size.height);
[self.view addSubview:pickrView];
[UIView commitAnimations];
return NO;
}
Well, you could rely on the UITextFieldDelegate to handle this kind of functionality.
Inside the
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
is where you would set the text of your current UITextField as well as initializing and showing the UIPickerView.
Important notice:
You might also want to conform to the UIPickerViewDelegate.
HTH
Swift:
internal var textFieldHandlerToolBar: UIToolbar = {
let tb = UIToolbar.init(frame: CGRect.init(origin: .zero, size: CGSize.init(width: UIScreen.main.bounds.width, height: 44.0)))
let doneBarButton = UIBarButtonItem.init(title: "Done", style: UIBarButtonItemStyle.done, target: self, action: #selector(actionDonePickerSelection))
tb.setItems([doneBarButton], animated: false)
return tb
}()
internal var pickerView: UIPickerView = {
let pv = UIPickerView.init()
return pv
}()
#objc internal func actionDonePickerSelection() {
textField.resignFirstResponder()
}
override func viewDidLoad() {
super.viewDidLoad()
self.pickerView.delegate = self
self.pickerView.datasource = self
}
Use it like this:
textField.inputAccessoryView = self.textFieldHandlerToolBar
textField.inputView = self.pickerView
What you can do is, create a UIButton with custom type on UITextField. Both having equal sizes. On the touch of button you can show UIPickerView.
http://tmblr.co/ZjkSZteCOUBS
I have the code and everything laid out in my blog to do this exactly. But below, I have the basic concept laid out.
Basically the solution involves an opensource project called ActionSheetPicker on github, and implementing the function textFieldShouldBeginEditing on the UITextFieldDelegate. You can dismiss the keyboard there and provide a UIPickerView instead. The basic code is listed here:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
// We are now showing the UIPickerViewer instead
// Close the keypad if it is showing
[self.superview endEditing:YES];
// Function to show the picker view
[self showPickerViewer :array :pickerTitle];
// Return no so that no cursor is shown in the text box
return NO;
}
ViewController.h
#interface ChangeCurrencyVC : UIViewController <UIPickerViewDataSource, UIPickerViewDelegate>
{
NSArray *availableCurreniesArray;
}
#property (weak, nonatomic) IBOutlet UITextField *chooseCurrencyTxtFldRef;
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
availableCurreniesArray = #[#"Indian Rupee", #"US Dollar", #"European Union Euro", #"Canadian Dollar", #"Australian Dollar", #"Singapore Dollar", #"British Pound", #"Japanese Yen"];
// Do any additional setup after loading the view.
[self pickerview:self];
}
#pragma mark - picker view Custom Method
-(void)pickerview:(id)sender{
UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
pickerView.showsSelectionIndicator = YES;
pickerView.dataSource = self;
pickerView.delegate = self;
// set change the inputView (default is keyboard) to UIPickerView
self.chooseCurrencyTxtFldRef.inputView = pickerView;
// add a toolbar with Cancel & Done button
UIToolbar *toolBar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
toolBar.barStyle = UIBarStyleBlackOpaque;
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:#selector(doneTouched:)];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:#selector(cancelTouched:)];
// the middle button is to make the Done button align to right
[toolBar setItems:[NSArray arrayWithObjects:cancelButton, [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], doneButton, nil]];
self.chooseCurrencyTxtFldRef.inputAccessoryView = toolBar;
}
#pragma mark - doneTouched
- (void)cancelTouched:(UIBarButtonItem *)sender{
// hide the picker view
[self.chooseCurrencyTxtFldRef resignFirstResponder];
}
#pragma mark - doneTouched
- (void)doneTouched:(UIBarButtonItem *)sender{
// hide the picker view
[self.chooseCurrencyTxtFldRef resignFirstResponder];
// perform some action
}
#pragma mark - The Picker Challenge
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return [availableCurreniesArray count];
}
- (nullable NSString *)pickerView:(UIPickerView *)pickerView titleForRow: (NSInteger)row forComponent:(NSInteger)component{
return availableCurreniesArray[row];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
self.chooseCurrencyTxtFldRef.text = availableCurreniesArray[row];
}

Resources