I have a simple project based off the "Navigation Based application" project type. The RootViewController has a list of rows with some names for contacts, and a button which should take them to that contact's details page with ABPersonViewController.
I have all the hooks working and I show the correct ABPersonViewController by pushing it on the navigation stack... but it doesn't have a Back button! The app gets stuck because there's no way to get back to my app. How do I enable the back button? Would appreciate some help.
Here's the relevant code:
- (void) nameButtonClicked: (id) sender
{
UIButton *button = (UIButton *) sender;
NSLog(#"name button clicked... record_id = %i", button.tag);
ABRecordRef contact_data = ABAddressBookGetPersonWithRecordID(address_book, button.tag);
ABPersonViewController *ab = [[ABPersonViewController alloc] init];
[ab setPersonViewDelegate:self];
[ab setDisplayedPerson:contact_data];
ab.allowsEditing = YES;
[self.navigationController pushViewController:ab animated:YES];
[ab release];
}
if you cant find the system back button you can always create one yourself.
- (void) nameButtonClicked: (id) sender
{
UIButton *button = (UIButton *) sender;
NSLog(#"name button clicked... record_id = %i", button.tag);
ABRecordRef contact_data = ABAddressBookGetPersonWithRecordID(address_book, button.tag);
ABPersonViewController *ab = [[ABPersonViewController alloc] init];
ab.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"Back",nil) style:UIBarButtonItemStyleDone target:self action:#selector(ReturnFromPersonView)];
[ab setPersonViewDelegate:self];
[ab setDisplayedPerson:contact_data];
ab.allowsEditing = YES;
[self.navigationController pushViewController:ab animated:YES];
[ab release];
}
- (void)ReturnFromPersonView{
[self.navigationController popViewControllerAnimated:YES];
}
good luck =]
btw- if you dont like the "done" button style, you can always use an custom button style for the back button arrow like look.
Related
I am using UIImagePickerController with a custom overlay to record a video in my app. For the implementation of the UIImagePickerController, I have used the code from a great Ray Wenderlich tutorial.
I have hidden the controls for the camera, and created a simple custom overlay view. This has worked and loads fine.
I have then created a toolbar and buttons for the view, to record the video:
- (BOOL) startCameraControllerFromViewController: (UIViewController*) controller
usingDelegate: (id <UIImagePickerControllerDelegate,
UINavigationControllerDelegate>) delegate {
if (([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeCamera] == NO)
|| (delegate == nil)
|| (controller == nil))
return NO;
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
// Displays a control that allows the user to choose movie capture
cameraUI.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeMovie, nil];
// Hides the controls for moving & scaling pictures, or for
// trimming movies. To instead show the controls, use YES.
cameraUI.allowsEditing = NO;
cameraUI.delegate = delegate;
//Overlay view and toolbar setup
// creating overlayView
UIView* overlayView = [[UIView alloc] initWithFrame:cameraUI.view.frame];
// letting png transparency be
float width = 320;
float AR = (4.0/3.0);
float toolbar_height = 480 - (AR*width);
UIToolbar *toolBar=[[UIToolbar alloc]initWithFrame:CGRectMake(0, (AR*width), 320, toolbar_height)];
//toolBar.tintColor = [UIColor colorWithRed:(252/255.) green:(0/255.) blue:(48/255.) alpha:1];
toolBar.tintColor = [UIColor colorWithRed:(49/255.) green:(52/255.) blue:(49/255.) alpha:1];
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *RecordBarButtonItem = [[UIBarButtonItem alloc ] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:#selector(recordPressed:)];
UIBarButtonItem *CancelBarButtonItem = [[UIBarButtonItem alloc ] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:#selector(imagePickerControllerDidCancel:)];
NSArray *buttons = [NSArray arrayWithObjects: CancelBarButtonItem, flexibleSpace, RecordBarButtonItem, flexibleSpace, nil];
[toolBar setItems: buttons animated:NO];
[overlayView addSubview:toolBar];
[overlayView.layer setOpaque:NO];
overlayView.opaque = NO;
cameraUI.showsCameraControls = NO;
cameraUI.cameraOverlayView = overlayView;
[controller presentViewController: cameraUI animated: YES completion:nil];
return YES;
}
My button recordBarButtonItem calls recordPressed which is given by:
- (void) recordPressed: (UIImagePickerController *) picker {
NSLog(#"lets record");
[picker startVideoCapture];
}
So the 'lets record' appears on the log, but I receive an NSInvalidArgumentException error for the startVideoCapture. I know theres something obviously wrong with the way im trying to start the video capture through the button press, but I cant figure it out. Still quite a novice at iOS so forgive me if the solution is simple!
Cheers,
Mike
The problem is that the action you attach to the initWithBarButtonSystemItem call doesn't pass the UIImagePickerController instance along.
What I would do is set the UIImagePickerController as a property of your class and access that property from your action, like this:
In your .h:
#property (nonatomic, strong) UIImagePickerController *cameraUI;
In your .m:
- (BOOL) startCameraControllerFromViewController: (UIViewController*) controller
usingDelegate: (id <UIImagePickerControllerDelegate,
UINavigationControllerDelegate>) delegate {
...
self.cameraUI = [[UIImagePickerController alloc] init];
...
UIBarButtonItem *RecordBarButtonItem = [[UIBarButtonItem alloc ] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:#selector(recordPressed)]; // Removed the ':'
...
}
- (void) recordPressed {
NSLog(#"lets record");
[self.cameraUI startVideoCapture];
}
Actually I've just quickly tested it in some code I've gotten open, the sender for your action on the button press is UIBarButtonItem *. So there's a couple of things you can do, you can either go down the root of
UIBarButtonItem *senderButton = (UIBarButtonItem *)sender;
if(senderButton.image == UIBarButtonSystemItemCamera)
{
//Handle behaviour
}
Or set the tag variable for each button and skip the image check and look at the tags instead, which might make the logic a bit easier.
I have a modalviewcontroller and have two buttons on it: Cancel and Save. There is a UITextField which is editable.
Whenever I click on save button I do save it but the text doesnot get save because when I click on the button to open the modalviewcontroller, the text disappears. Dont know whats wrong with my code.
Here is my code :
- (void)viewWillAppear:(BOOL)animated {
self.cancel = self.navigationItem.leftBarButtonItem;
self.save = self.navigationItem.rightBarButtonItem;
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle:#"Cancel" style:UIBarButtonItemStylePlain target:self action:#selector(cancelAction)];
self.navigationItem.leftBarButtonItem = cancelButton;
[cancelButton release];
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithTitle:#"Save" style:UIBarButtonItemStylePlain target:self action:#selector(saveAction)];
self.navigationItem.rightBarButtonItem = saveButton;
[saveButton release];
[super viewWillAppear:animated];
}
-(IBAction) cancelAction{
[[self parentViewController] dismissModalViewControllerAnimated:YES];
}
-(IBAction) saveAction{
NSString *text = [textFieldBeingEdited text];
[textFieldBeingEdited setText:text];
[self setDescription:text];
[[self parentViewController] dismissModalViewControllerAnimated:YES];
}
I am not sure if I have to use the following code to save the text in textfield :
- (void)textFieldDidEndEditing:(UITextField *)textField
{
[self dismissModalViewControllerAnimated:YES];
}
I don't see in your code anywhere that you are setting the text in the UITextField when the view appears. You can add some code to your viewWillAppear method to set the text when the view appears, such as:
[textField setText:[self description]];
(It looks like you are storing the NSString in the description variable.)
Also, if this is some kind of string that could be considered a User Default, you can store the string there. I've used this before and it populates my UITextField nicely.
Code Example:
-(void)viewDidLoad
{
stringValue = [[NSUserDefaults standardUserDefaults] stringForKey:#"string"];
[textField setText:stringValue];
}
-(IBAction)saveAction
{
stringValue = [textField text];
[[NSUserDefaults standardUserDefaults] setObject:stringValue forKey:#"string"];
}
I have a UITabBarController and want a button seen only on one of the tabs, not the rest. So in my method to handle when tab bar is pressed, I add the button like this:
#pragma mark - UITabBarController delegate
- (void) tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
// check for what type of class you are based on the tab pressed
...
NSMutableArray *barItems = [[self.MainToolbar items] mutableCopy];
UIBarButtonItem *sortBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Sort" style:UIBarButtonItemStyleBordered target:self action:#selector(SortButtonPressed:)];
[barItems insertObject:sortBarButtonItem atIndex:0];
[self.MainToolbar setItems:barItems];
Now how do I remove it when in the other views when the tabBarController is pressed without disturbing their buttons in the UIToolBar they already have that were added in IB.
This is simply achieved the same way you added the button, simply make it an ivar, which gets instanciated only once when you detect the particular view controller the first time.
// in .h
UIBarButtonItem *extraButton;
// in .m
-(void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController {
// if it is the view controller you want {
NSMutableArray *barItems = [[self.MainToolbar items] mutableCopy];
if(!extraButton) {
extraButton = [[UIBarButtonItem alloc]
initWithTitle:#"Sort"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(SortButtonPressed:)];
}
[barItems insertObject:sortBarButtonItem atIndex:0];
[self.MainToolbar setItems:barItems];
// else
// just remove the button
NSMutableArray *barItems = [[self.MainToolbar items] mutableCopy];
[barItems removeObjectAtIndex:0];
[self.MainToolbar setItems:barItems];
}
Don't forget to release the button later when you don't need it anymore, and it should ok.
In my app I have a basic Navigation Controller. For all of my views, except one, the controller works as it should.
However, for one view in particular, I would like the 'back' button to not go back to the previous view, but to go to one I set. In particular it is going to go back 2 views and skip over one.
After doing some research I found that I can intercept the view when it disappears, so I tried to put in code to have it navigate to the page I would like:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
//i set a flag to know that the back button was pressed
if (viewPushed) {
viewPushed = NO;
} else {
// Here, you know that back button was pressed
mainMenu *mainViewController = [[mainMenu alloc] initWithNibName:#"mainMenu" bundle:nil];
[self.navigationController pushViewController:mainViewController animated:YES];
[mainViewController release];
}
}
That didn't work, so does anyone have any ideas?
Thanks!!
In your code, you seem to be trying to push another view controller onto the stack, rather than pop an extra item off it.
Try this as your code that does the going back two levels:
NSArray *vcs = [self.navigationController viewControllers];
[self.navigationController popToViewController:[vcs objectAtIndex:[vcs count]-3];
Alternatively you could totally replace the back button with a button of your own? In your viewController:
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleBordered target:self action:#selector(doSomething:)];
self.navigationItem.hidesBackButton = YES;
self.navigationItem.leftBarButtonItem = item;
[item release];
Then you can write the doSomething: method to pop the two items off the stack, perhaps using the code I posted above.
Simple solution:
- (void)viewWillDisappear:(BOOL)animated {
//if true, back was pressed
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
//your logic
}
}
You can try implementing the UINavigationBarDelegate delegate. When the method -navigationBar:didPopItem: is called, you can pop an additional item from the UINavigationController, and thus pop two items at once.
UIButton *home = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *homeImage = [UIImage imageNamed:#"back.png"];
[home setBackgroundImage:homeImage forState:UIControlStateNormal];
[home addTarget:self action:#selector(LogOut)
forControlEvents:UIControlEventTouchUpInside];
home.frame = CGRectMake(0, 0, 69, 26);
UIBarButtonItem *button2 = [[UIBarButtonItem alloc] initWithCustomView:home];
[[self navigationItem] setLeftBarButtonItem:button2];
[button2 release];
button2 = nil;
How to set action to the backButtonItem on the navigation bar? I have a navigation bar, when I'm pressing the back button, I need to alert some message to the user, and only after user's reaction - return to the previous view. How can I do it? Thanx!
- (void)viewDidLoad
{
[super viewDidLoad];
//no one field don't changed yet
isDirty = FALSE;
//edited user
//set default values
newData = [data copy];
//setting navigation controller rigth button
UIBarButtonItem *rightButton = [[UIBarButtonItem alloc] initWithTitle:#"Save"
style:UIBarButtonSystemItemDone
target: self
action: #selector(saveBtnUserClick)];
self.navigationItem.rightBarButtonItem = rightButton;
[rightButton release];
UIBarButtonItem *leftButton = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonSystemItemDone
target: self
action: #selector(backBtnUserClick)];
self.navigationItem.backBarButtonItem = leftButton;
[leftButton release];
}
//and my method for reaction
-(IBAction) backBtnUserClick
{
NSLog(#"\n Back pressed");
//back to previous view
[self.navigationController popViewControllerAnimated: TRUE];
}
Add the < UINavigationControllerDelegate > in the header file and use this in the .m
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
//insert your back button handling logic here
// let the pop happen
return YES;
}
This sounds like a job for UIAlertView. Instead of calling popViewControllerAnimated: in your IBAction methods, alloc/init a UIAlertView and present it. Then, when the user taps a button on the UIAlertView, dismiss the UIAlertView and call popViewControllerAnimated:.
- (IBAction)backBtnUserClicked:(id)object {
UIAlertView *av = [[[UIAlertView alloc] initWithMessage:#"Wait!"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil] autorelease];
[av show];
}
In your UIAlertViewDelegate methods call popViewControllerAnimated:.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
[[self navigationController] popViewControllerAnimated:YES];
}
To set the action on the back button:
[[[self navigationController] leftBarButtonItem] setTarget:self];
[[[self navigationController] leftBarButtonItem] setAction:#selector(backBtnUserClicked:)];