Stacking a UIImagePickerController inside a navigation controller? - ios

My app basically has two view controllers, starting in a navigation controller:
1) MainViewController, which asks user to pick Camera or Album and then presents UIImagePicker
2) PhotoViewController, which receives photo.
However, in the second VC, when the user hits "Back", I would prefer to immediately return to either the Camera or Album they selected, rather than MainVC and then reloading the picker.
Should I separate an entirely new ViewController dedicated to the ImagePicker rather than presenting it within MainViewController? Using pushViewController: doesn't work as 'Pushing a navigation controller is not supported' perhaps meaning that imagePickers are nav controllers too.
MainViewController currently displays it like this:
[self.navigationController presentViewController:self.imagePicker animated:YES completion:nil];
Any suggestions appreciated, thanks.

A simple solution could be inside your MainVC have 2 bool values calledCamera, calledLibrary
when you create your new VC you set the one you choose your pic from to YES. So when you come back...
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (calledCamera){
//call cameraPicker to load before de MainVc shows
}
if (calledLibrary){
//call the LibraryPicker before...
}
}

Related

Presenting ViewController undesirably visible between transitions

I have a ViewController, HomeVC, who manages the transition between three other view controllers, PhotopickerVC, CroppingVC and EditingVC.
As HomeVC dismisses one VC and presents another VC, I get an undesired effect in which HomeVC is visible for a fraction of a second between these transitions.
Here is a little more details about my code.
The normal flow of the app is HomeVC → PhotopickerVC → CroppingVC → EditingVC (both PhotopickerVC and CroppingVC are only assisting in getting to the final EditingVC, they are no longer required after they are dismissed).
I implement this flow using segues from the HomeVC in the following form:
Starting from HomeVC visible, Photo button is pressed, a (subclassed) UIImagePickerController is created and presented.
When photo was chosen, (subclassed) UIImagePickerController calls [picker dismissViewControllerAnimated:NO completion:nil]; This takes us back to HomeVC
In HomeVC method viewWillAppear, I call [self performSegueWithIdentifier:#"CroppingVC" sender:self];
In CroppingVC, when user presses Accept button, CroppingVC calls [self dismissViewControllerAnimated:NO completion:nil]; This takes us back to HomeVC
In HomeVC viewWillAppear, I call [self performSegueWithIdentifier:#"EditingVC" sender:self];
In EditingVC, when the user presses Home, EditingVC calls [self dismissViewControllerAnimated:YES completion:nil]; This takes us back to HomeVC.
From what I see, you do not really need the HomeVC to manage transitions. What you should do, is to make the VC hierarchy exactly as you wrote:
HomeVC → PhotopickerVC → CroppingVC → EditingVC
The arrows should be eg. push segues, and then in the prepareForSegue method you can just pass all the necessary parameters.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
YourViewController *destinationController = (YourViewController *)segue.destinationViewController;
// do sth
}
This way you can achieve nice and smooth transitions. The way your application is now seems to have a structural flaw, because as you have said, when a ViewController gets used once, it is not needed anymore. After that you can just return to the HomeVC.

How do i handle navigation Back button view hierarchy?

Here is the problem
1) Rootview controller - MYAssetVC-> Embedded with NavigationController here pushing for button to another Addfilevc.
2) Addfilevc Has dropdownTextfield It will push to another vc have tableview selected row will display in the textfield.
3)if i select another value from the dropdown textfield it will push to the vc again there i will select.
Navigation bar back button will navigate to all my view hierarchy i want to handle this one. if i go to same view it should navigate back only once that to the recent visit how to do this.
As i am new to iOS. give any suggestion.
Navigation from 1->2->3
navigation backbtn 3->2->1
if i navigate like this 1->2->3-> backbutton 3->2 again 2->3 backbutton 3->2 again 2->3
IF i navigate now using back it is displaying all my route path it should navigate like 1->2->3> and 3->2->1 if any number of times i perform actions in 2 & 3.
1,2,3 are view controllers.
Create an IBAction for the back button and use popViewController.
[self.navigationController popViewControllerAnimated:YES];
This will help you to go back one page. You have to write this in all the pages where there is a back button and you want to go back one page.
If you want to go back directly to rootViewController, try this:
[self.navigationController popToRootViewControllerAnimated:YES];
And if you want to pop to any specific viewController in the stack, you run a for loop to find the viewController you want to navigate to and then simply popToViewController, like this:
for (UIViewController *viewController in self.navigationController.viewControllers) {
if ([viewController isKindOfClass:[Addfilevc class]]) {
[self.navigationController popToViewController:viewController animated:YES];
}
}
Hope this helps to clear your concept.
EDIT
In swift:
The [self.navigationController popViewControllerAnimated:YES]; will become self.navigationController?.popViewControllerAnimated(true)
The [self.navigationController popToRootViewControllerAnimated:YES]; will become navigationController?.popToRootViewControllerAnimated(true)
And the for loop will be as below:
You can use this if you are using storyboard
let switchViewController = self.storyboard?.instantiateViewControllerWithIdentifier("view2") as ComposeViewController
self.navigationController?.pushViewController(switchViewController, animated: true)
And the for-in loop
if let viewControllers = self.navigationController?.viewControllers {
for viewController in viewControllers {
self.navigationController!.popToViewController(viewController, animated: true);
}
}
Thanks.

segue from second view controller to first view controller ios8

I have 4 ViewControllers. Three of those view controllers are connected to the fourth one, which I call Photo_Detail. The segues there work fine, but now I have to make the back button go to the VC that someone came from. How can I do that? A research said to create a unwind segue in the Photo_detail, what i did but it didn't work out. That's the code I tried to use:
- (IBAction)done:(UIStoryboardSegue *)segue {
NSLog(#"Popping back to this view controller!");
// reset UI elements etc here
}
Thanks for any help in advance!
- (IBAction)done:(UIStoryboardSegue *)segue {
NSLog(#"Popping back to this view controller!");
// reset UI elements etc here
[self dismissViewControllerAnimated:NO completion:nil];
}
For Modal type segues, this will help you to move back.
And also select it as normal action from ViewController rather then exit. That will fix it.
Hope this helps.
If the second VC is presented modally, you simply have to dismiss the controller
[self dismissViewControllerAnimated:NO completion:nil]:

Switching two view controller, back function not working

im have two view controller UserListView and UserProfileView!
in UserListView view controller i'm have a button for swtich to UserProfileView and here is code.
UserListView.m - Click Action
- (IBAction)SettingClick:(id)sender
{
UserList *UserProfile = [self.storyboard instantiateViewControllerWithIdentifier:#"UserProfileView"];
[self presentViewController:UserProfile animated:YES completion:nil];
}
And code working fine, when user switch to profile (UserProfileView) have a close button back to UserListView and here is code.
UserProfileView.m - Close click action
- (IBAction)CloseClick:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
UserProfile *UseList = [self.storyboard instantiateViewControllerWithIdentifier:#"UserListView"];
[self presentViewController:UseList animated:YES completion:nil];
}
in this code i will using [self dismissViewControllerAnimated:YES completion:nil]; to close UserProfileView view controller for low ram usage and it work.
But affter i close UserProfileView i want to open this view controller again and it do not work, UserProfileView not showing again??
i using xcode 5 and building an App for ios 7, please help.
Thanks for your time.
If I understand correctly, when you call SettingClick: your app is displaying a UserList. So, when you dismiss a view controller presented on top of it, you should go back to UserList without the need for presenting it again. So you can try with:
- (IBAction)CloseClick:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
This will work unless you had originally presented UserList as well. In this case, UserList will be dismissed with the top controller. In this case, you can delay presenting a second time UserList after dismissing UserProfile, and it should work.
In the latter case, I would suggest you to use a navigation controller instead of simply presenting your controllers like you are doing. As you see, it is not really straightforward and you will get into catches of any kind. Presenting a controller works ok when you present just one controller at a time. On the other hand, if you instantiate a UINavigationController, this will handle the controllers' hierarchy for you.
use this -
- (IBAction)CloseClick:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
This will return to your previous view when u click that button that has been linked with this action

How to dismiss view controllers at any time (even during transitions) or when it is safe to dismiss a view controller ?

I have an iOS app that has a connection to a server. If we get disconnected, I want to be able to dismiss the top view controllers to get back to a "connecting to server" view controller. The problem is that a disconnection can occur at any time, including during a transition between view controllers.
The view controller hierarchy is like so:
ConnectingToServerViewController
SignInViewController
MainAppViewController
Other view controllers
When a disconnection is detected I want the view hierarchy to collapse back to:
ConnectingToServerViewController
So when a disconnection is detected, this method is called on the ConnectingToServerViewController to dismiss anything that it has presented and go back to attempting to connect to server:
- (void)restartSession
{
if (self.presentedViewController) {
[self dismissViewControllerAnimated:NO completion:nil];
}
}
However, if I try to dismiss while a view transition is occurring, I get errors such as
*** Assertion failure in -[UIWindowController transition:fromViewController:toViewController:target:didEndSelector:], /SourceCache/UIKit/UIKit-2380.17/UIWindowController.m:211
attempt to dismiss modal view controller whose view does not currently appear. self = <YYYYYViewController: 0x2089c8a0> modalViewController = <XXXXXViewController: 0x208e6610>
attempt to dismiss modal view controller whose view does not currently appear. self = <WWWWWWViewController: 0x1fd9e990> modalViewController = <YYYYYViewController: 0x2089c8a0>
The first of which will crash the app, the second will just not dismiss anything and continue to show the current presented view controller.
Thoughts:
delays won't work since we don't know when to start the delay
is there a way to track when view transitions complete?
should all view controllers override willAppear, didAppear and alert the app when it is safe to dismiss?
perhaps instead of dismiss, I should just set a new root view controller?
I've made sure that all overridden view(will|did)(dis)?appear methods call the appropriate super method.
Any solution that requires all view controllers to override view(did|will)appear methods to track state sounds like it could cause issues if we forget to set the base class for a new view controller.
Do something like this. Try this out once,
UIViewController *controller = self.presentingViewController; //THIS LINE IS IMP
[self dismissViewControllerAnimated:YES
completion:^{
[controller presentViewController:adminViewController animated:YES completion:nil];
adminViewController.view.superview.frame = CGRectMake(1024/2 - 400, 768/2 - 280, 800 , 560);//it's important to do this after
[adminViewController release];
}];
One way that has worked for me is to assign a new view controller to the root view controller. That way, views in the old hierarchy can animate and transition to their hearts content while we have new controllers.
eg
- (void)restartSession
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
ConnectingToServerViewController *vc = [storyboard instantiateViewControllerWithIdentifier:#"ConnectingToServerViewController"];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[UIApplication sharedApplication].delegate.window.rootViewController = vc;
}
I'm not sure if I'm aware of all the downsides to this though. Perhaps the old view controllers will never get freed because of a dangling strong reference? We're no longer reusing ConnectingToServerViewController, we have to recreate that each time.
I based the code on what I saw in this answer for Managing and dismissing Multiple View Controllers in iOS.
It seems like you are trying to dismiss the view controller when it is not currently on screen. To check if it is on screen you could use:
if (self.presentedViewController.view.window)
{
[self dismissViewControllerAnimated:NO completion:nil];
}
else
{
self.presentedViewController = nil;
}
I will answer in order.
is there a way to track when view transitions complete?
You could try with the UINavigationControllerDelegate (if you are using one of those). Other approach could be using a custom animator.
should all view controllers override willAppear, didAppear and alert the app when it is safe to dismiss?
That's an option. You are free to do it if you want. Another option is not to do that. I think that container view controllers such as navigation controller has better approaches.
I should just set a new root view controller?
I would suggest to do the opposite. I would set the SignInViewController / MainAppViewController as the root flow, and present modally ConnectingToServerViewController on demand. In my opinion that's a healthier approach.
Hope it helps.

Resources