Showing UIImagePickerController in UIPopoverController with existing UINavigationController (adding back button) - objective-c

So I have a UIPopoverController what houses my UINavigationController where I have my UITableViewController however one of my options on the UITableView is to go and select an image with the UIImagePickerController... Now on the iPhone I can simply use presentModalViewController:animated: however calling that from within a UIPopoverController causes a crash so thats not possible...
I also know the UIImagePickerController needs its own UINavigationController so I can't just push pushViewController:animated: either...
So I figured out that if I keep a link to the UIPopoverController I created, I can then use setContentViewController:animated: to switch to the UIImagePickerController's viewController...
However, I am now stuck at giving the user a way to go back to the previous UINavigationController as I need to be able to add a cancel button to the UIImagePickerController but when I try to do this the cancel button won't get added...
Heres my code that i'm using
-(void)doPhotoalbums {
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
[imagePicker setDelegate:self];
[imagePicker setSourceType:UIImagePickerControllerSourceTypePhotoLibrary];
[imagePicker setContentSizeForViewInPopover:CGSizeMake(320, 480)];
UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:nil];
[imagePicker.navigationItem setLeftBarButtonItem:cancel];
//[self presentModalViewController:imagePicker animated:YES];
[[self parentPopoverController] setContentViewController:imagePicker animated:YES];
} else {
[UIAlertView showMessage:#"This device does not have any photo albums."];
}
}
So my question is.. Does anybody know how I can get around this? either by adding a cancel/back button what I can hook up to make the navigationControllers switch back or another way to present this (i'd like to avoid switching between two UIPopoverControllers but I don't know what else I can do..
Thanks
Liam

Ahh.. after a little break I found this: https://discussions.apple.com/thread/1710435?start=0&tstart=0
using the UINavigationControllerDelegate you can use the navigationController:willShowViewController:animated: method to access the navigationBar.. then with some code (below) you can add a button.
if ([navigationController isKindOfClass:[UIImagePickerController class]]) {
UINavigationBar *bar = navigationController.navigationBar;
UINavigationItem *top = bar.topItem;
UIBarButtonItem *cancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:#selector(imagePickerControllerDidCancel:)];
[top setLeftBarButtonItem:cancel];
} else {
//do non imagePickerController things
}

Related

Add UINavigationItem to UINavigationController that wraps Google Drive OAuth

Goal: For connection to iOS Google Drive, wrap the iOS Google OAuth view controller in a programmatically created navigation controller, and add a Cancel button to enable the user to cancel the Google OAuth process, should they choose to do so.
Problem: While I can successfully wrap the OAuth view controller in a navigation controller, I cannot seem to add a navigation item, such as the desired Cancel button.
I add a navigation controller that wraps the Google Drive OAuth view controller, as follows...
GTMOAuth2ViewControllerTouch *authViewController = nil;
if (!self.isAuthorized) {
SEL selectorFinish = #selector(viewController:finishedWithAuth:error:);
SEL selectorButtonCancel = #selector(buttonCancelTapped:);
authViewController = [[GTMOAuth2ViewControllerTouch alloc] initWithScope:kGTLAuthScopeDrive
clientID:kClientID
clientSecret:kClientSecret
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:selectorFinish];
UINavigationController *navController = [[UINavigationController alloc] init];
[navController addChildViewController:authViewController];
[self.parentTVC presentViewController:navController animated:YES completion:nil];
}
For clarity, the variable parentTVC is a public property,
#property (nonatomic, strong) UITableViewController *parentTVC;
and is set using a custom init method, as follows...
- (id)initWithParentTVC:(UITableViewController *)tvc {
self = [super init];
[self setParentTVC:tvc];
return self;
}
I have attempted to add UINavigationItems to the UINavigationController instance navController, however this does not work, and instead I seem to be stuck with the UIView with the two small buttons (< and >) in the nib file GTMOAuth2ViewTouch.xib, image included below...
I have read through the GTL file GTMOAuth2ViewControllerTouch.m to attempt to see whether there is a method I could possible use or whether I can override by subclassing, but I am not confident in my attempts to do this.
My best guess is that any navigation controller wrapping the OAuth view controller set by this code from GTMOAuth2ViewControllerTouch.m...
- (void)setUpNavigation {
rightBarButtonItem_.customView = navButtonsView_;
self.navigationItem.rightBarButtonItem = rightBarButtonItem_;
}
Assistance please?
This is my re-interpretation of Imran Khan's excellent answer provided in his response to this stack overflow question: Google Drive iOS SDK: Display Cancel Login Button
The googleAuthCheck method should be called in either the viewDidLoad or viewWillAppear method of the parent view controller. (I assume here a reasonable understanding of iOS Google Drive SDK, so let me know if I need to add further clarification.)
Also, albeit a small issue, using initWithBarButtonSystemItem:UIBarButtonSystemItemCancel requires that only the title text of the view controller then needs to be localised (if you are implementing localisation).
- (void)googleAuthCheck {
if (!self.isAuthorized) {
SEL selectorFinish = #selector(viewController:finishedWithAuth:error:);
SEL selectorButtonCancel = #selector(buttonCancelTapped:);
UINavigationController *navController = [[UINavigationController alloc] init];
UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:<<localised string for title>>];
UIBarButtonItem *barButtonItemCancel = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:selectorButtonCancel];
UINavigationBar *navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, 320, 63)];
[navigationItem setRightBarButtonItem:barButtonItemCancel];
[navigationBar setTranslucent:NO];
[navigationBar setItems:[NSArray arrayWithObjects: navigationItem,nil]];
[navController.view addSubview:navigationBar];
GTMOAuth2ViewControllerTouch *authViewController = nil;
authViewController = [[GTMOAuth2ViewControllerTouch alloc] initWithScope:kGTLAuthScopeDrive
clientID:kClientID
clientSecret:kClientSecret
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:selectorFinish];
[navController addChildViewController:authViewController];
[self.parentTVC presentViewController:navController animated:YES completion:nil];
}
}
For clarity, the buttonCancelTapped: method is as follows...
- (IBAction)buttonCancelTapped:(UIBarButtonItem *)sender {
[self.parentTVC dismissViewControllerAnimated:YES completion:^(void){}];
}
For clarity, the variable parentTVC is a public property,
#property (nonatomic, strong) UITableViewController *parentTVC;
and is set using a custom init method, as follows...
- (id)initWithParentTVC:(UITableViewController *)tvc {
self = [super init];
[self setParentTVC:tvc];
return self;
}
This custom init method is called from the parent view controller.

Pop controller after back bar button is pressed

I have a UINavigationController ans a chain of 3 simple controllers. Each one has a button. When press a button a next controller is Pushed. ViewController1 -> ViewController2 -> ViewController3. When I push a back button on the 3rd view i want to move to the first view. Using of backBarButtonItem is obligatory. Here is the code for second controller:
#import "ViewController2.h"
static BOOL isBackButtonPressed;
#implementation ViewController2
- (void)viewDidLoad {
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:#"back from 3" style:UIBarButtonItemStyleBordered target:nil action:nil];
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
if (isBackButtonPressed) {
[self.navigationController popViewControllerAnimated:YES];
} else {
isBackButtonPressed = YES;
}
[super viewWillAppear:animated];
}
#end
But when I press back button on the third view I return to the second view instead of the first view. Could you help me to return to the first view pressing back button on the third view.
I tried suggestions from answers but they don't help.
Adding a selector to backBarButtonItem doesn't help because it is never called.
Adding a [self.navigationController popToRootViewControllerAnimated:YES] in viewWillDisappear methos also doesn't work. I don't know why. I think that the actual problem is how backBarButtonItem works.
Any other suggestions?
The behaviour I try to achieve exists in the calendar on iPhone. When you rotate iPhone to landscape you get to the weeek view. Then go to the event details, and rotate to the portrait. When you press back button you will get to a day view not to a week view, so a controller with weekview is skipped.
After countless number of tries my solution was simply not use backBarButtonItem! As whatever i do it always goes to previous viewController instead of calling its selector
Instead I use only leftBarButtonItem for navigation, as it guarantees calling my action.
Here an example
UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 27, 22)];
[backButton setImage:[UIImage imageNamed:#"backbutton"] forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
This certainly calls backButtonPressed action.. This works both for IOS 6 and 7
No need to register a new selector for the back button, just do:
-(void)viewWillDisappear{
if ( [self.navigationController.viewControllers containsObject:self] )
//It means that the view controller was popped (back button pressed or whatever)
//so we'll just pop one more view controller
[self.navigationController popViewControllerAnimated:NO];
}
in your ViewController3 viewWillDisappear method
Try using this in your third view controller, this way you check if you have pressed the back button and directly pop to the root view controller which as you stated is your first view controller.
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
[self.navigationController popToRootViewControllerAnimated:YES];
}
[super viewWillDisappear:animated];
}
I had the same problem as you beofre and fixed it like this:
You can capture the back button on the ViewController3 and before poping the view, remove ViewController2 from the navigation stack like this:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:#"back from 3" style:UIBarButtonItemStyleBordered target:self action:#selector(customBackPressed:)];
}
-(void)customBackPressed:(id)sender {
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray: navigationController.viewControllers];
for (UIViewController *vc in viewControllers)
{
// If vc is ViewController2 type, remove it
}
navigationController.viewControllers = allViewControllers;
[self.navigationController popViewControllerAnimated:YES];
}
Because ViewController2 is not in the stack anymore, it will jump to ViewController1.
Also, if ViewController1 is the root view controller, you can just do:
[self.navigationController popToRootViewControllerAnimated:YES];

Replacing UITabBar with UIToolbar programmatically

I'm trying to write a tabbed application where each tab is a Navigation Controller. This tab bar appears at every view in the navigation controller as its being inferred on each view.
I would like to replace this tab bar on a detail view with a tool bar with a couple of buttons on it.
I've tried the following code in that detail view's viewDidLoad: method
self.navigationController.toolbarHidden = NO;
self.navigationController.toolbar.barStyle = UIBarStyleBlackTranslucent;
UIBarButtonItem *accept = [[UIBarButtonItem alloc] initWithTitle:#"Accept"
style:UIBarButtonItemStyleBordered
target:self
action:nil];
UIBarButtonItem *decline = [[UIBarButtonItem alloc] initWithTitle:#"Decline"
style:UIBarButtonItemStyleBordered
target:self
action:nil];
NSArray *items = [NSArray arrayWithObjects:accept, decline, nil];
[self.navigationController.toolbar setItems:items animated:YES];
// code suggested
[self.view addSubview:self.navigationController.toolbar];
It still doesn't show up. Though hides the tab bar now for adding the following line in the view that's presenting the detail view:-
theDetailTableViewController.hidesBottomBarWhenPushed = YES;
Have I missed something?
I usually put toolbarHidden = YES or NO, as applicable, in the viewWillAppear or viewDidAppear methods. I am not sure if that is why it is not working for you, but you need to address when you return to the presenting view anyway.
If you don't address it, the toolbar will still be visible when you go back.
Wherever you are pushing your detailViewController from, do this to hide the Tab Bar in the detail view:
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
detailViewController.hidesBottomBarWhenPushed = YES;
[self.navigationController detailViewController animated:YES];
and in your detail view, just add the ToolBar as a subview to the detailView.

UIView Modal Popup with Embedded UITableView with UIToolbar, Toolbar moves with Table

This is driving me insane! (ios 5+, ARC)
Pretty simple concept here:
I have a UIView with an embedded UITableView, when clicking on a specific cell, I have iOS launch a modalview with a segue.
This modalview, is a UIView with an Embedded UITableView in it, filled with names from a data source. You can choose multiple items in this view (using cellaccessory: checkmark)
The Goal
Get some sort of "Done" button to show up
Ok, so after much running around, I am to understand that Modal windows do not, in fact, allow navigationController items. No toolbars, no Navigationbars by default.
Ok, so I'll create my own.
- (void)viewDidLoad
{
[super viewDidLoad];
NSInteger tbHeight = 50;
UIToolbar *tb = [[UIToolbar alloc] initWithFrame:CGRectMake(0, (self.view.frame.size.height - tbHeight), self.view.frame.size.width, tbHeight)];
tb.translucent = YES;
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStyleBordered target:self action:#selector(doneAction:)];
NSArray *barButton = [[NSArray alloc] initWithObjects:flexibleSpace,doneButton,nil];
[tb setItems:barButton];
[self.view addSubview:tb];
barButton = nil;
//....
}
Simple enough right?
Well, the toolbar does show up, for sure. BUT, it doesnt stick to the bottom like its supposed to. In fact, when you scroll the embedded tableview up and down, the UIToolbar goes with it, almost like its stuck on the tablevie somehow.
Ive been looking for hours for solutions, I've come across nothing. Wondering if anybody here has any ideas?
If you need more information, please by all means, enquire :)
It's strange that your toolbar is scrolling with the table if it's a UIViewController subclass, unless you've assigned a UITableView to self.view or something... But since it is, this is what I do when adding a fixed item to a table view:
-(void) scrollViewDidScroll:(UIScrollView *)scrollView {
tb.frame = CGRectMake(0, self.view.bounds.size.height-tb.bounds.size.height+scrollView.contentOffset.y, self.view.bounds.size.width, tb.bounds.size.height);
}

Change autoresizingMask on programmatically created navigation bar

So I have a view-based app in which I need one page with a tableview and I wanted a navigation bar above it to go back. I added this programmatically with:
-(IBAction)switchToTableView; {
tableView *table = [[tableView alloc] initWithNibName:#"tableView" bundle:nil];
UINavigationController *navControl = [[UINavigationController alloc] initWithRootViewController: table];
table.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[table release];
[self presentModalViewController: navControl animated:YES];
[navControl release];
}
Then in the viewDidLoad of the tableview class I implemented:
- (void)viewDidLoad
{
self.tableView.rowHeight = 70.f;
UIBarButtonItem *back = [[UIBarButtonItem alloc]
initWithTitle:#"Back"
style:UIBarButtonItemStyleDone
target:self
action:#selector(switchToHome)];
self.navigationItem.leftBarButtonItem = back;
UIBarButtonItem *map = [[UIBarButtonItem alloc]
initWithTitle:#"Map"
style:UIBarButtonItemStyleDone
target:self
action:#selector(switchBacktoFindArt)];
self.navigationItem.rightBarButtonItem = map;
[map release];
[super viewDidLoad];
}
to add the needed buttons. Now, all of this worked great, but now that I'm going through my app and allowing for orientation changes, I'm having trouble with this one since I can't change the autoresizingMask in IB. The whole tableview page resizes nicely except the navigation bar becomes very skinny top-to-bottom wise. I'd like it to stay the same height. How do I go about setting the appropriate Masks or even accessing the navigation bar outside of IB?
I suppose you can play with things like this to disable height changes:
navControl.navigationBar.autoresizingMask ^= UIViewAutoresizingFlexibleHeight
But a UINavigationController might really get wonky if you play around with its internals like that. I'm surprised that the nav's bar isn't already resizing to an appropriately pleasing height, like it's supposed to do by default. (It feels like something else might be wrong elsewhere in your code, but I'd have to see more to find out).