I am really confused regarding few things in UIViewController - ios

I am really confused regarding few things in UIViewController, I have already read the View Controller Programming Guide and searched lot on the Internet but still confused.
When I want to jump or switch from firstVC to secondVC how many types of methods are available? I am listing which I know:
UINavigationController
UITabBarController
presentModalViewController:
Add secondVC to root view
If secondVC is added to root view then how firstVC object will be released?
Is it a good practice to add every view I want to jump/switch to root view?
transitionFromView:
I dont understand this portion from Apple doc:
This method modifies the views in their view hierarchy only. It does
not modify your application’s view controllers in any way. For
example, if you use this method to change the root view displayed by a
view controller, it is your responsibility to update the view
controller appropriately to handle the change.
If I do this:
secondViewController *sVc = [[secondViewController alloc]init];
[transitionFromView:self.view toView:sVc.view...
Still viewDidLoad:, viewWillAppear:, viewDidAppear: are working fine: I don't need to call them. So why did Apple say this:
it is your responsibility to update the view controller appropriately to handle the change.
Are there any other methods available?

Actually the standard methods used are :
1) Using NavigationController
//push the another VC to the stack
[self.navigationController pushViewController:anotherVC animated:YES];
//remove it from the stack
[self.navigationController popViewControllerAnimated:NO];
//or presenting another VC from current navigationController
[self.navigationController presentViewController:anotherVC animated:YES completion:nil];
//dismiss it
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
2) Presenting the VC
//presenting another VC from current VC
[self presentViewController:anotherVC animated:YES completion:nil
//dismiss it
[self dismissViewControllerAnimated:YES completion:nil];
Never use the method you described in points 4. It's not a good practice to change root view controller's dynamically. window's root VC is usually defined on applicationdidfinishlaunchingwithoptions after that it shouldn't be changed , if you are to follow apple standards.
Example for transitionFromView:toView
-(IBAction) anAction:(id) sender {
// assume view1 and view2 are some subviews of self.view
// view1 will be replaced with view2 in the view hierarchy
[UIView transitionFromView:view1
toView:view2
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:^(BOOL finished){
/* do something on animation completion */
}];
}
}

Related

Dismiss and Present same view controller

i have a view controller and i need to dismiss it and present it back in same time.
i had tried dismiss it and call back the view controller but not working.
[self dismissViewControllerAnimated:YES completion:nil];
UIStoryboard *storyboard=[UIStoryboard storyboardWithName:#"Main" bundle:nil];
ExpandViewController *expandView=
[storyboard instantiateViewControllerWithIdentifier:#"ExpandViewController"];
expandView.delegate=self;
[expandView setEventDict:dict];
[self presentViewController:expandView animated:YES completion:NULL];
I am not exactly sure what outcome/functionality you are looking for in your question, but #matt is correct. However, you may be looking to have this happen seamlessly. Therefore you could use child view controllers instead of presenting the view controller using the [self presentViewController:VC animated:animate completion:nil] method.
Adding child vc:
[self addChildViewController:myVC];
[self.view addSubview:myVC.view];
[myVC didMoveToParentViewController:self];
Removing child vc:
[myVC willMoveToParentViewController:nil];
... remove subview.
You can set up a delegate between the two controllers to tell the parent when to dismiss the view to make things easy. You can also add the subviews at different indexes using [self.view insertSubview:myVC atIndex:index] or the other possible functions such as the insert above subview etc, to have one subview be added before dismissing the other to give a more seamless transition.
Hope this helps!
You can't present a view controller until the currently presented view controller has finished being dismissed. You won't know this has happened until the completion handler from your dismissal is called. Your mistake is that the completion handler is nil. Instead, provide a completion handler (in your first line), consisting of the remaining lines of your code. Thus, they will execute after the dismissal finishes.
[self dismissViewControllerAnimated:YES completion:^{
// ... the rest of your code goes in here ...
}];

ViewController WILL NOT dismiss

WLINewPostViewController *newPostViewController = [[WLINewPostViewController alloc] initWithNibName:#"WLINewPostViewController" bundle:nil];
UINavigationController *newPostNavigationController = [[UINavigationController alloc] initWithRootViewController:newPostViewController];
newPostNavigationController.navigationBar.translucent = NO;
[tabBarController presentViewController:newPostNavigationController animated:YES completion:nil];
So I just simply push a new UIViewController.
Then after it posts the server callback calls a method with this code from the WLINewPostViewController.m:
[self dismissViewControllerAnimated:YES completion:^{
NSLog(#"Completed");
}];
[[self navigationController] popViewControllerAnimated:YES];
if (self == self.navigationController.visibleViewController){
NSLog(#"self = visibile");
}
if (self == self.presentingViewController.presentingViewController){
NSLog(#"self = presenting");
}
}
I tried a bunch of different things and none work.
I am relatively new to Xcode but after trying
[self dismissViewControllerAnimated:YES completion]
[self.navigationController popViewControllerAnimated:YES]
[self.navigationController.visibleViewController.presentedViewController dismissViewControllerAnimated:YES completion:nil];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
and every other possibility, I am officially stumped. The WLINewPostViewController still won't dismiss.
It Logs out "self = visible"
Let me illustrate what you are trying to do
You have a navigation controller with Controller A.
Here you are trying to present another Controller B from Controller A.
Now when you get a callback from the server, you should call dismissViewControllerAnimated from Controller B to dismiss itself.
So after dismissViewControllerAnimated:completion: method call, the Controller B will be dismissed and Controller A will be shown automatically. Now you do not need to call popViewControllerAnimated: in completion block again as there is no other Controller in navigation controller to load.
If you have different use case, let me know I can provide solution.
You are presenting a view over navigationbar instead of pushing it over navigationbar.
When push you pop. When you present you dismiss. So instead of popViewControllerAnimated you need to use dismissViewControllerAnimated:completion
dismiss behaves differently depending on the receiver. From the docs:
The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, it automatically forwards the message to the presenting view controller.
If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.
In short, if the vc on top calls it on itself, it dismisses itself. Anywhere else on the stack dismisses to that point, animating only the topmost vc.
What's extra confusing (for you and many others) is that the navigation vc has a stack too, and your problem is complicated further by presenting an navigation vc atop a tab-bar vc.
So what to do? The question is unclear about which vc is the receiver in the posted code (who is self in that snippet?). The text implies that self is a vc on the stack of the presented navigation vc, like...
TabBarVC --- presents ---> NavVC
| |
| --- viewControllers stack = rootVC, vc1
|
---> viewControllers for each tab
... and it's root or vc1 that wants to dismiss. If I'm right about that, then, given the docs, the solution is clear:
[self.navigationController dismissViewControllerAnimated:YES completion:^{}];
will put us back on the tabbar vc on whatever tab was visible when we did the present.

Remove viewcontroller & move to next view controller

Hi i have been working on an iOS app.What i am doing is navigating among diffrent view controllers. But the problem is i want finish the current view controller from emoery and then move to the next view controller.
I am using `[self.view removeFromSuperview]; for finishing the cureent view & using
self.loginView = [self.storyboard instantiateViewControllerWithIdentifier:#"LOGIN"];
[self presentViewController:self.loginView animated:NO completion:nil];
for moving to next view controller but the thing is i am not able to remove it from memory.
Please tell me how can i do it?
Thanks in advance.
`
It's better to create a container view controller which manages your view controllers. For example, in viewDidLoad: of container controller you add current controller:
[self addChildViewController:self.currentViewController];
[self.currentViewController didMoveToParentViewController:self];
[self.view addSubView:self.currentViewController.view];
//here set up currentViewController view's frame or constraints if needed
When you need to open login controller, do this:
[self addChildViewController:loginViewController];
[self.loginViewController didMoveToParentViewController:self];
[self.view addSubView:loginViewController.view];
//here set up loginViewController view's frame or constraints if needed
//then remove current view controller
[self.currentViewController willMoveToParentViewController:nil];
[self.currentViewController removeFromParentViewController];
[self.currentViewController.view removeFromSuperview];
Remove from superview will remove it from the current view, but OS won't remove it until he needs to (this is topic for more explanation, let's say it won't remove it asap).
If you want something deleted just call it nil:
self.view = nil;
This will make the pointer to nil, so view won't be there any more. (the view really will be somewhere but you won't have access to it)
I am revising your code
self.loginView = [self.storyboard instantiateViewControllerWithIdentifier:#"LOGIN"];
[self presentViewController:self.loginView animated:NO completion:nil];
What you are doing here is presenting your login viewcontroller.
self: This is the instance of the viewcontroller you are currently working on. So how could you remove self from memory. (Not Possible)
You can approach alternate ways.
For example: 1. Changing root view controller
Pop to root view controller and then Push Login View controller.
If you try to remove not only UIView but the whole UIViewController from a navigation controller stack use this snippet
NSMutableArray *stack = [[NSMutableArray alloc] initWithArray: self.navigationController.viewControllers];
[stack removeObject:yourController];
self.navigationController.viewControllers = stack;
Be aware of using this only when you've already pass to the next controller view.
UPD:
Oh, now I see what you are trying to do. And I can't figure out, why you're trying to step your controllers this way (modally). I think you should use UINavigationController with navigation segues defined directly from your storyboard. Look at this article where apple purely explains what navigation is. That article is about modal views

Dismissing a ViewController lower in the stack does not behave as expected

I'm building a complex app that has kind of a branch in the middle.
At some point in the app, a particular UIViewController is presented, we'll call it mainViewController (shortened mainVC).
The mainVC presents another view controller, by code, using the following code (I strip out parts of it for privacy reasons):
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"SecondaryStoryboard" bundle:secondaryBundle];
SecondViewController *secondVC = [storyboard instantiateInitialViewController];
[self presentViewController:secondVC animated:YES completion:nil];
So the secondVC will later present another view controller, called thirdVC. This is done using a custom segue, set in the storyboard used in the code above, which code looks like this:
#implementation VCCustomPushSegue
- (void)perform {
UIView *sourceView = ((UIViewController *)self.sourceViewController).view;
UIView *destinationView = ((UIViewController *)self.destinationViewController).view;
UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
destinationView.center = CGPointMake(sourceView.center.x + sourceView.frame.size.width, destinationView.center.y);
[window insertSubview:destinationView aboveSubview:sourceView];
[UIView animateWithDuration:0.4
animations:^{
destinationView.center = CGPointMake(sourceView.center.x, destinationView.center.y);
sourceView.center = CGPointMake(0 - sourceView.center.x, destinationView.center.y);
}
completion:^(BOOL finished){
[self.sourceViewController presentViewController:self.destinationViewController animated:NO completion:nil];
}];
}
#end
As you can see this segue presents the destination view controller modally (by the use of presentViewController:) with a custom animation (a slide from right to left).
So basically up to here everything is fine. I present the secondVC with a classic modal animation (slide up from bottom) and present the thirdVC with my custom transition.
But when I want to dismiss the thirdVC, what I want is to go back directly to the mainVC. So I call the following from the thirdVC :
self.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:_animate completion:nil];
That way, I'm calling dismissViewControllerAnimated: directly on mainVC (referenced by self.presentingViewController.presentingViewController), and I'm expecting the thirdVC to be dismissed with an animation, and the secondVC to just disappear without animation.
As Apple says in the UIViewController Class Documentation:
The presenting view controller is responsible for dismissing the view
controller it presented. If you call this method on the presented view
controller itself, it automatically forwards the message to the
presenting view controller.
If you present several view controllers in succession, thus building a
stack of presented view controllers, calling this method on a view
controller lower in the stack dismisses its immediate child view
controller and all view controllers above that child on the stack.
When this happens, only the top-most view is dismissed in an animated
fashion; any intermediate view controllers are simply removed from the
stack. The top-most view is dismissed using its modal transition
style, which may differ from the styles used by other view controllers
lower in the stack.
The issue is that it's not what happens. In my scenario, the thirdVC disappears, and shows the secondVC being dismissed with the classic modal slide to bottom animation.
What am I doing wrong ?
Edit :
So #codeFi's answer is probably working in a classic project, but the problem here is that I'm working on a framework. So mainVC would be in a client app, and the secondVC and thirdVC are in my framework, in a separate storyboard. I don't have access to mainVC in any other way than a reference to it in my code, so unwind segues are unfortunately not an option here.
I've been having this exact same issue, and I've managed to visually work around it by adding a snapshot of the screen as a subview to secondVC.view, like so:
if (self.presentedViewController.presentedViewController) {
[self.presentedViewController.view addSubview:[[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO]];
}
[self dismissViewControllerAnimated:YES completion:nil];
Not pretty, but it seems to be working.
NOTE: if your secondVC has a navigation bar, you will need to hide the navigation bar in between snapshotting the screen and adding the snapshot as a subview to secondVC, as otherwise the snapshot will appear below the navigation bar, thus seemingly displaying a double navigation bar during the dismissal animation. Code:
if (self.presentedViewController.presentedViewController) {
UIView *snapshot = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
[self.presentedViewController.navigationController setNavigationBarHidden:YES animated:NO];
[self.presentedViewController.view addSubview:snapshot];
}
[self dismissViewControllerAnimated:YES completion:nil];
I had the same issue and I've fixed it by using UnwindSegues.
Basically, all you have to do is add an IBAction Unwind Segue method in the ViewController that you want to segue to and then connect in IB the Exit action to your Unwind Segue method.
Example:
Let's say you have three ViewControllers (VC1, VC2, VC3) and you want to go from VC3 to VC1.
Step 1
Add a method to VC1 like the following:
- (IBAction)unwindToVC1:(UIStoryboardSegue*)sender
{
}
Step 2
Go in Interface Builder to VC3 and select it. Then CTRL-drag from your VC icon to Exit icon and select the method you've just added in VC1.
Step 3
While still in IB and with VC3 selected, select your Unwind Segue and in the Attributes Inspector add a Segue Identifier.
Step 4
Go to VC3 where you need to perform your segue (or dismiss the VC) and add the following:
[self performSegueWithIdentifier:#"VC1Segue" sender:self];

Need assistance regarding transitionFromView:toView:duration:options:completion:

Regarding transitionFromView:toView:duration:options:completion: Apple doc says this in last few lines:
This method modifies the views in their view hierarchy only. It does
not modify your application’s view controllers in any way. For
example, if you use this method to change the root view displayed by a
view controller, it is your responsibility to update the view
controller appropriately to handle the change.
If a ViewController has 2 full screen size views display one at a time then no issues:
[transitionFromView:self.view toView:self.view2...
but what this means it is your responsibility to update the view controller appropriately to handle the change?
if I do this:
secondViewController *sVc = [[secondViewController alloc]init];
[transitionFromView:self.view toView:sVc.view...
how its my responsibility to update the view controller appropriately to handle the change? or how to update ViewController?
UPDATE
I created a single view projec, add secondVC then in firstVC on button tap i did this:
self.svc = [[secondVC alloc]init];
[UIView transitionFromView:self.view toView:self.svc.view duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {}];
... secondVC viewDidLoad is working its nslog is working.
Then how to handle updating of viewcontroller?
The statement "it is your responsibility to update the view controller appropriately to handle the change." it meant that you have to appropriately call view hierarchy delegate methods such as:
- (void)viewDidLoad;
- (void)viewDidUnload;
- (void)viewWillAppear;
- (void)viewDidDisappear;
And other methods that are responsible for proper view management.
Here are some examples.
When we use transitionFromView:toView:duration:options:completion: we are only
bringing toView up in view hierarchy. but Apple says we should handle updating
of ViewControllers which are parent of these views.
Maintaining viewcontroller in navigationcontroller stack...
For .ex: if You have TabController in your application,
somewhere at tabIndex two you required to show view of viewcontroller at tabindex 1,
then you should update your tabIndex when you will use transitionfromview method
[UIView transitionFromView:fromView
toView:toView
duration:0.5
options:(controllerIndex > tabBarController.selectedIndex ? UIViewAnimationOptionTransitionCurlUp : UIViewAnimationOptionTransitionCurlDown)
completion:^(BOOL finished) {
if (finished) {
tabBarController.selectedIndex = controllerIndex;
}
}];

Resources