ABPeoplePickerNavigationController - remove "Cancel" button without using private methods/properties? - iphone

I'm using the ABPeoplePickerNavigationController, a subclass of UINavigationController, and in the context I'm using it the default nav bar button for the right side, "Cancel", makes no sense. I can't find a way to disable or hide it, and whatever method used needs to be public and store-approvable.
Getting rid of the nav bar entirely (picker.navigationBarHidden = YES;) might be an option except that after popping back to the list of contacts the nav bar reappears.
Subclassing ABPeoplePickerNavigationController and intercepting viewWillAppear to try and nil the cancel button did not work.
[picker setAllowsCancel:NO]; DOES work but is undocumented so I expect would never pass approval.

this one
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
UIView *custom = [[UIView alloc] initWithFrame:CGRectMake(0.0f,0.0f,0.0f,0.0f)];
UIBarButtonItem *btn = [[UIBarButtonItem alloc] initWithCustomView:custom];
//UIBarButtonItem *btn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addAction)];
[viewController.navigationItem setRightBarButtonItem:btn animated:NO];
[btn release];
[custom release];
}
works perfect!

The examples herein using the delegate method navigationController:willShowViewController:animated: do work, but it may be the case that you want to add your own navigation item in your own controllers and the given options will remove anything that you might set in your own controllers. Here's code that I've successfully used to get this option to work well:
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated {
// Here we want to remove the 'Cancel' button, but only if we're showing
// either of the ABPeoplePickerNavigationController's top two controllers
if ([navigationController.viewControllers indexOfObject:viewController] <= 1) {
viewController.navigationItem.rightBarButtonItem = nil;
}
}
Note that there are two view controllers in the navigation controller's stack, the one for contact groups and one for the contact list. This is why we cannot just check fi the viewController is the navigation controller's top view controller.

There is no answer to this - write a new person picker if you can't live with the cancel.

You can achieve that result browsing through the picker subviews. Just a little boring...

I haven't tried it yet, but I think Uby is saying to iterate through the subviews of the picker until you find one that is isKindOfClass:[UIBarButtonItem class] and then you can change it's title property. It might also be in the navigationBar's 'Item' array.

Set delegate to PeoplePickerController controller.
In the delegate class, have this delegate method.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
UIView *pCustomView = [[UIView alloc] initWithFrame:CGRectMake(0,0,0,0)];
UIBarButtonItem *pBtn = [[UIBarButtonItem alloc] initWithCustomView:pCustomView];
[viewController.navigationItem setRightBarButtonItem:pBtn animated:NO];
[pBtn release];
[pCustomView release];
}

Be sure to set the delegate for picker object (not the peoplePickerDelegate, just the delegate) to the class that implement the following method:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
UIView *custom = [[UIView alloc] initWithFrame:CGRectMake(0,0,0,0)];
UIBarButtonItem *btn = [[UIBarButtonItem alloc] initWithCustomView:custom];
[viewController.navigationItem setRightBarButtonItem:btn animated:NO];
[btn release];
[custom release];
}

It works fine but in iOS 4 there is one more thing. When I switch back to my app using Fast App Switching feature, the cancel button appears again.
The method
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
doesn't get called. So I made this:
- (void)applicationDidEnterBackground:(UIApplication *)application {
id topView = pickerControllerDelegate.peoplePicker.topViewController;
topView.navigationItem.rightBarButtonItem = nil;
}
It works pretty well.

according to russel b you could just overwrite your viewdidapper
this worked for me:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UINavigationItem *item = (UINavigationItem *)[self.navigationBar.items lastObject];
item.rightBarButtonItems = [[NSArray alloc] init];
item.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:#selector(addPerson)];
}

EDIT: See comments below. This is now an illustration of what not to do.
I tried to get the desired behavior with the public API by subclassing ABPeoplePickerNavigationController and intercepting all the events that change the current navigation view controller view. Then one can just navigate the view hierarchy and purge all the unwanted buttons.
You can navigate the view hierarchy from a delegate, but you aren't privy to the events that change the view state... which makes it hard to kill the Cancel button and make it stick.
This code kind of worked for me (NOTE: it brute-force kills all the right-hand buttons):
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self killCancelButton];
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
[super pushViewController:viewController animated:animated];
[self killCancelButton];
}
- (UIViewController*)popViewControllerAnimated:(BOOL)animated {
UIViewController *result = [super popViewControllerAnimated:animated];
[self killCancelButton];
return result;
}
- (void)killCancelButton {
for (NSUInteger itemIdx = 0; itemIdx < self.navigationBar.items.count; itemIdx++) {
UINavigationItem *item = [self.navigationBar.items objectAtIndex:itemIdx];
item.rightBarButtonItems = [[NSArray alloc] init];
}
}

Related

iPhone : How to remove right button from navigation bar in a view controller?

I have made a custom navigation bar, same for all the views by implementing the method below -:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
// viewController.navigationItem.rightBarButtonItem = cancelButton;
// -- Adding INFO button to Navigation bar --
UIBarButtonItem *infoButton = [[UIBarButtonItem alloc]
initWithTitle:#"i"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(showInfo)];
infoButton.tag = 10;
self.navCntrl.topViewController.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:infoButton, nil];
self.navCntrl.navigationBar.tintColor = [UIColor colorWithRed:45/255.0 green:77/255.0 blue:67/255.0 alpha:1];
// NSLog(#"Inside implemented method");
}
of UINavigationControllerDelegate.
In the above method I have added a right button to the navigation Item. Now I want to hide this right button in a particular view. How can I achieve this ?
Thanks.
Try to use this one
self.navigationItem.rightBarButtonItem = nil;
self.navigationItem.rightBarButtonItem.enabled = NO;
In viewDidLoad, try
self.navigationItem.rightBarButtonItem = nil;
And in viewWillDisappear, don't forget to put it back.
use this
self.navigationItem.rightBarButtonItem = nil;
A nice way to do it would be to have your view controllers implement a protocol. You pick the name, but it can be something like CustomNavigationCustomization, and have a single method:
#protocol CustomNavigationCustomization
- (BOOL)shouldShowRightButton;
#end
Then, you could change your method to something like this:
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
BOOL shouldShowRightButton = YES;
if ([viewController conformsToProtocol:#protocol(CustomNavigationCustomization)) {
UIViewController <CustomNavigationCustomization> *customizableViewController =
(UIViewController <CustomNavigationCustomization>)viewController;
shouldShowRightButton = [customizableViewController shouldShowRightButton];
}
if (shouldShowRightButton) {
// viewController.navigationItem.rightBarButtonItem = cancelButton;
// -- Adding INFO button to Navigation bar --
UIBarButtonItem *infoButton = [[UIBarButtonItem alloc] initWithTitle:#"i"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(showInfo)];
infoButton.tag = 10;
self.navCntrl.topViewController.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:infoButton, nil];
self.navCntrl.navigationBar.tintColor = [UIColor colorWithRed:45/255.0 green:77/255.0 blue:67/255.0 alpha:1];
// NSLog(#"Inside implemented method");
}
}
Note that the method in the navigation controller delegate is very defensive: it checks if your view controller conforms to the protocol, and only then it invokes the method. This way, you don't need to conform to the protocol in most of your view controllers, only in those you wish to customise.
Just check if the viewController pushed of tf the type where you do not want the righ bar button:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
// Replace the YourViewController with the type of the viewcontroller
// you want not the have the right bar button.
if ([viewController isKindOfClass:[YourViewController class]]) {
return;
}
// viewController.navigationItem.rightBarButtonItem = cancelButton;
// -- Adding INFO button to Navigation bar --
UIBarButtonItem *infoButton = [[UIBarButtonItem alloc]
initWithTitle:#"i"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(showInfo)];
infoButton.tag = 10;
self.navCntrl.topViewController.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:infoButton, nil];
self.navCntrl.navigationBar.tintColor = [UIColor colorWithRed:45/255.0 green:77/255.0 blue:67/255.0 alpha:1];
// NSLog(#"Inside implemented method");
}
For those that are still looking for an answer, this code worked for me in AppDelegate.m
- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
// Get rid of the edit button in UITabBarController's moreNavigationController
tabBarController.customizableViewControllers = nil;
...
}

Toolbar in Navigation Controller

I am creating a toolbar in a Navigation Controller using the following code:
[self.navigationController setToolbarHidden:NO];
//Create a button
NSArray *toolbarItems = [NSArray arrayWithObjects:
[[UIBarButtonItem alloc] initWithTitle:#"Help" style:UIBarButtonItemStyleBordered target:self action:#selector(helpButton:)]
,nil];
The only problem is that the toolbar is visible whenever there is a navigation controller(multiple other views). Is there a way to only restrict the toolbar to a single view?
Thanks
To quote the UINavigationController Class Reference:
The navigation toolbar is hidden by default but you can show it for your navigation interface by calling the setToolbarHidden:animated: method of your navigation controller object. If not all of your view controllers support toolbar items, your delegate object can call this method to toggle the visibility of the toolbar during subsequent push and pop operations.
So, set a delegate for your navigation controller. In your delegate's navigationController:willShowViewController:animated:, do something like this:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
BOOL shouldShowToolbar = (viewController == theViewControllerThatNeedsAToolbar);
[navigationController setToolbarHidden:shouldShowToolbar animated:animated];
}
If you slightly modify the above example you can easily make it so the toolbar will automatically show when toolbar items are set in the current view controller's viewDidLoad method:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
BOOL shouldHide = [viewController.toolbarItems count] == 0;
[navigationController setToolbarHidden:shouldHide animated:animated];
}

UINavigationController: Hiding Back Button on One View Hides it For All Views

I have a UINavigationController that contains 3 UIViewControllers on the stack.
View A - is the root
View B - is pushed by View A and has `self.navigationItem.hidesBackButton = YES;`
View C - is pushed by View B and has `self.navigationItem.hidesBackButton = NO;`
View C does not show the back button, even though I have hidesBackButton set to NO. How can I resolve this?
Update
A possible bug in 4.2 as it works till 4.1 sdks
I have tried this and mine is working perfectly. I am just posting the implementation of B view controller (BVC) and C view controller (CVC). My initial guess is that you are not setting the title of BVC in viewDidLoad.
#implementation BVC
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"I am B";
}
- (void) viewWillAppear:(BOOL)animated{
self.navigationItem.hidesBackButton = YES;
}
- (IBAction)pushB:(UIButton *)sender{
CVC *cvc = [[CVC alloc] initWithNibName:#"CVC" bundle:nil];
[self.navigationController pushViewController:cvc animated:YES];
[cvc release];
}
#end
#implementation CVC
- (void) viewWillAppear:(BOOL)animated{
self.navigationItem.hidesBackButton = NO;
}
#end
I think you have to set that property before you push or pop a view controller to affect the next view controller, setting it for the current viewcontroller in viewWillAppear is too late.
Edit: this looks like a bug in 4.2! The back button remains hidden both in the 4.2 simulator and on the device with 4.2, but it works in the 3.2, 4.1, and 4.0 simulators!
Here's the code where when pushing a VC with a hidden back button:
- (IBAction) goto2nd
{
SecondVC *vc = [[[SecondVC alloc] initWithNibName:#"SecondVC" bundle:nil] autorelease];
vc.navigationItem.hidesBackButton = YES;
[self.navigationController pushViewController:vc animated:YES];
}
That is all that should be needed, each VC has its own navigationItem, it's not a global setting, so you don't need to bother undoing it to restore the back button (at least when popping back to a VC where it is set to "NO").
Here's a workaround that I'm using successfully on 4.3.
Instead of hiding the back button, set the left bar button view to an empty view:
UIView *tmpView = [[UIView alloc] initWithFrame:CGRectZero];
UIBarButtonItem *tmpButtonItem = [[UIBarButtonItem alloc] initWithCustomView:tmpView];
[tmpView release];
self.navigationItem.leftBarButtonItem = tmpButtonItem;
[tmpButtonItem release];
To restore the back button, just set the left bar button item to nil:
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
Update: It appears as if the bug is gone in 4.3.
Note: Even though the bug seems to be fixed, I prefer the "empty view" technique because it allows the disappearance and reappearance of the back button to be animated.
The solution for this problem is somewhat tricky..just try it it will surely work since even I faced the same problem.
First set Navigation title in viewWillAppear.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationItem.title = #"SET YOUR TITLE";
}
When you are navigating to other page just set your navigation title to null.This will not show you any button on top.Since you can get rid of writing
self.navigationItem.hidesBackButton = YES; everytime.
- (IBAction)pushB:(UIButton *)sender
{
SecondVC *vc = [[[SecondVC alloc] initWithNibName:#"SecondVC" bundle:nil] autorelease];
self.navigationItem.title = #"";
[self.navigationController pushViewController:vc animated:YES];
[vc release];
}
I'm running the same issue and it's only happening on the iOS 4.2 simulator, so probably it's a bug on that version.
Reedit:
Try with this, it worked for me:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.navigationItem.hidesBackButton = NO;
}
Use the UINavigationControllerDelegate method -navigationController:willShowViewController:animated:. You will implement this in view controller A and view controller B. In A you will set -hidesBackButton: to YES and alternatively to NO in view controller B.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
viewController.hidesBackButton = YES;
}
You can also use following sample code;
- (void) viewWillAppear:(BOOL)animated{
self.navigationItem.hidesBackButton = YES;
}
- (void) viewWillDisappear:(BOOL)animated{
self.navigationItem.hidesBackButton = NO;
}
If your view heirarchy is really such that View B should not show a back button but View C should, then the simplest way to get around this is to refactor your heirarchy. I'm thinking of the following alternative:
View A calls presentModalViewController:animated: on View B*, a UINavigationController whose view property is View B. View B* pushes View C onto its navigation stack in response to an event (or otherwise) from View B. If you need to jump back to
View A quickly then call dismissModalViewControllerAnimated: on View A. If you want to keep the state of View B* and C in memory then you could also keep another pointer to View B* somewhere so it doesn't go away when dismissed.

EKEventViewDelegate didCompleteWithAction not getting called

I don't get a call to my eventViewController:didCompleteWithAction: when the EKEventViewController finishes edting an event.
Here's how I set it up:
- (void)showCalendar:(id)sender {
EKEventViewController *eventViewController = [[EKEventViewController alloc] init];
eventViewController.delegate = self;
eventViewController.event = self.event;
// Allow event editing.
eventViewController.allowsEditing = YES;
[self.navigationController pushViewController:eventViewController animated:YES];
[eventViewController release];
}
I do have the protocol on my class and the method was implements by copy and pasting the definition from the docs. It just doesn't get called.
If I use the EKEventEditViewController and its corresponding delegate, then that does get called when the event is saved.
I was able to reproduce the problem in the SimpleEKDemo code same as well. Does anyone know what might be wrong?
I could just drop the view functionality and go straight to the EKEventEditViewController, but I'd rather not.
Might be a bit late to be helpful, but I had this problem as well.
To get around it I subclassed EKEventViewController, then in the subclass' viewDidLoad I replaced the standard edit button with one of my own:
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *editItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self.delegate action:#selector(editCalEvent)];
self.navigationItem.rightBarButtonItem = editItem;
}
That way when you want to edit an event, you can set up your own EKEventEditViewController and specify its delegate in order to respond to changes:
- (void)editCalEvent {
EKEventEditViewController *editController = [[EKEventEditViewController alloc] init];
editController.event = editingEvent;
editController.eventStore = self.eventStore;
editController.editViewDelegate = self;
[self presentModalViewController:editController animated:YES];
[editController release];
}
Hope that helps.
I had the similar problem when I use "pushViewController", the result is that it will go to
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{}
But after I changed to presentModalViewController, it will go to eventViewController:didCompleteWithAction: when Done/Cancel/Delete are pressed.
in this .m file you need to import the EventKit/EventKit.h and EventKitUI/EventKitUI.h
and in the .h file you need to implement the 'EKEventViewDelegate' delegates.
hope it helps you
This does seem to be a fairly obvious omission in the library. My workaround: I'm presenting the EKEventViewController in a UINavigationController. I detect completion in the viewWillAppear method of the controller than pushed the EKEventViewController onto the view stack. Use a boolean variable in this view controller to track and differentiate between initial appearance and re-appearance due to the EKEventViewController being popped. There is a risk that your code will get called at other times, but if you are just refreshing tableviews, etc, then this should be sufficient.

backButton of NavigationController don't appear

I have already post this question but this time I post code. So I have a uiviewController, and in the viewDidLoad of this viewController I hide the backButton of the navigationController. After that, I push a new uiviewcontroller, and I set the backbutton to visible in the viewDidLoad, but the backbutton is still hidden...
Implementation of the first uiviewcontroller
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"page2page2page2page2page2";
self.navigationItem.hidesBackButton = TRUE;
}
-(IBAction)click
{
page3 *controller = [[page3 alloc] init];
[self.navigationController pushViewController:controller animated:YES];
[page3 release];
}
Implementation of the page 3
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"page3";
self.navigationItem.hidesBackButton = FALSE;
}
and the page3 has no backbutton, but the space is created for the button, because the tile "page 3" is on the right and not in the center... all this happen with the ios 4.2
thx
Neither of the above workarounds seemed to work for me. However when the third view was being displayed, i could see the button blink for a moment. So I suspected the problem (bug) has to do with the animation
When change animated to NO on the pushViewController the problem went away
- (IBAction)btnNext:(id)sender {
[[self navigationController] pushViewController:thirdViewController animated:NO];
}
My trick is setting setNavigationBarHidden to YES and immediately NO.
[self.navigationItem setHidesBackButton:NO animated:YES];
[self.navigationController setNavigationBarHidden:YES];
[self.navigationController setNavigationBarHidden:NO];
So as this the backButton not be animated but it's really work and my manager have not notice it ;P
I get the same behaviour and I must say I find it quite strange. I can't say why it doesn't work but as a workaround you can do:
In page2:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.navigationItem setHidesBackButton:YES animated:YES];
}
And in page3:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.navigationItem setHidesBackButton:NO animated:YES];
}
And remove the calls to self.navigationItem.hidesBackButton = ... in both controllers.
Well, I had the same problem running iOS 4.2. The back button would refuse to appear. Upon autoroating to landscape, it then appears. My solution was to do the following - This fixed the problem...or should we say its a workaround ;)
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.hidesBackButton = YES;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.navigationItem.hidesBackButton = NO;
}