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;
}
}];
Related
I have a few ViewControllers that all have buttons which should segue to some others. There will never be a back button, but instead everything is connected through a bunch of loops so that there is never a dead end. So I'd like to fully transition from one View Controller to another, and have the old View Controller be completely deleted. There is no hierarchy and no parent/child relationship between the View Controllers. How should I handle this situation?
Instantiate the view controller you want to go to, then set it as the window's root view controller.
NextViewController *next = [self.storyboard instantiateViewControllerWithIdentifier:#"Next"]; // or other instantiation method depending on how you create your controller
self.view.window.rootViewController = next;
You could do this with custom segues if you want to show the flow from controller to controller in your storyboard (you wouldn't need any code at all then). The custom segue's perform method would look like this,
#implementation RootVCReplaceSegue
-(void)perform {
UIViewController *source = (UIViewController *)self.sourceViewController;
source.view.window.rootViewController = self.destinationViewController;
}
If you want a fade animation, you can add a snapshot of the source view controller as a subview of the destination view controller's view, then fade it out,
-(void)perform {
UIViewController *source = (UIViewController *)self.sourceViewController;
UIView *sourceView = [source.view snapshotViewAfterScreenUpdates:YES];
[[self.destinationViewController view] addSubview:sourceView];
source.view.window.rootViewController = self.destinationViewController;
[UIView animateWithDuration:.5 animations:^{
sourceView.alpha = 0;
} completion:^(BOOL finished) {
[sourceView removeFromSuperview];
}];
}
I can't find this anywhere. So, if you possess the info about it, please give me a link.
I have one view controller, I made a menu for a simple game. There are some buttons on it.
I have another view controller and there some buttons too.
The question is:
"How I can do an animation of this buttons (hiding off the screen) after I choose one button that triggers a custom segue (without any animation) to another View Controller, which will run it's button animation(coming to the screen from a border of the screen)?"
I made this like this:
1) Theory: I make a IBAction for a menu button, then in this IBAction I call an animation method, which call a performSegueMethod:. After this in new VC in viewWillAppear method call a animation method (that almost equal method from source VC). All this works, but this don't look smooth. The problem with this animation occurs when destination VC replace source VC. There is some split second, when all looks static, and only after this animation starts.
I don't know how to remove this destination VC lag. May be I must load a destination view before a segue? I tried to do this, but may be a made something wrong, or it's just don't help me.
2) Practice:
firstViewController.m:
- (IBAction)newGameSegueButton {
[self menuSlideInDirection:#"CenterToLeft" performingSegueWithIdentifier:#"mode"];
}
-(void)menuSlideInDirection:(NSString *)direction performingSegueWithIdentifier:(NSString *)segueIdentifier
{
[UIView animateWithDuration:0.4 animations:^{
CGPoint newGameButtonCenter;
newGameButtonCenter.x = directionIndex * 160;
newGameButtonCenter.y = self.gameButtonSlide.center.y;
self.gameButtonSlide.center = newGameButtonCenter;
[UIView animateWithDuration:0.3 delay:0.1 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
//some animation too
} completion:nil];
[UIView animateWithDuration:0.2 delay:0.2 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
//animation
} completion:nil];
[UIView animateWithDuration:0.1 delay:0.3 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
//some animation too
} completion:^(BOOL finished){
if(segueIdentifier){
[self performSegueWithIdentifier:segueIdentifier sender:self];
}
}];
}];
}
Okay, then custom segue code is pretty simple:
-(void)perform
{
UIViewController *src = self.sourceViewController;
UIViewController *dst = self.destinationViewController;
[src.navigationController pushViewController:dst animated:NO];
}
And my secondViewController.m:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self menuSlideInDirection:#"RightToCenter" performingSegueWithIdentifier:nil];
}
menuSlideInDirection:#"RightToCenter" performingSegueWithIdentifier:nil in secondViewController identical to method with same name in firstView.
I'm looking for smooth animation of particular objects of destination view controller right after a segue.
May be a doing all wrong and there a another way to do this? (I only think of adding all view and all controls of destination VC to source VC and remove "destination VC" at all).
Hope, somebody can help me with this.
Try doing all first view controller's animations in perform method.
As for the second view controller's animations, I don't think there is any other 'good' way than doing it in viewWillAppear (although I would prefer viewDidAppear) since the outlets of the destination view controller are not set while performing the segue(they will be nil). In other words, you do not have a way to access your buttons, let alone animate them.
A hackish way would be to call [segue.destinationViewController view] before perform so that the destination view controller's view hierarchy is loaded and the outlets are set. Then perhaps, you may animate buttons in the destination view controller in perform before pushing it onto navigation stack.
For my question, I choose the way of putting two views under the same VC. Animation is smooth and it's look much better than using perform / viewWillAppear method.
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 */
}];
}
}
I have a view controller with several textfields/text views and would like to allow the user to populate these fields with things they find on the web. To allow this I have a different web view presented modally. My problem is that I would like to use a partial page curl to display the first view controller (with any populated information). However, you can't present a live view controller a second time, and the new instance is not pre populated with whatever information was already placed in the textfields.
It seems like I could just pass that information back and forth (from the first instance, to the second view controller, then copy the info into the second instance, then copy back when I dismiss the second instance, etc) but it seems like there should be a better way.
I could also just add the web view controller as a subview, but I miss the functionality of the partial curl, which I really like.
Thanks!
However, you can't present a live view controller a second time, and
the new instance is not pre populated with whatever information was
already placed in the textfields.
You dont need to create a new view controller each time. Just store that view controller object as an instance variable and dont release it until your user is done.
if (myViewController == nil) {
myViewController = [UIViewController alloc] init];
}
[self presentViewController:myViewController animated:YES];
So I've kind of combined the comments and the posted answer.
First: I store an instance of the vc with the web view and add its view to the first view controller and then hide the view:
self.webVC = [self.storyboard instantiateViewControllerWithIdentifier:#"webView"];
self.webVC.delegate = self;
self.webVC.view.frame = self.view.bounds;
[self.view addSubview:self.webVC.view];
[self.webVC.view didMoveToSuperview];
self.findPoemVC.view.hidden = YES;
Then I used a category on UIView to uncurl and curl the view. I found the code here:
To show the view I perform a CurlDown animation like this:
self.findPoemVC.view.hidden = NO;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown
forView:self.webVC.view
cache:YES];
self.webVC.view.hidden = NO;
[UIView commitAnimations];
On the webVC I implemented a protocol and implement the delegate methods in the first view controller:
In the webVC:
#protocol PoemFinderDelegate <NSObject>
-(void)shouldUncurl;
-(void)doneWasPressed;
#end
The delegate was set earlier (see above) and the implementation of the delegate methods to partially uncurl and totally uncurl the webview were done like this inside the first view controller:
-(void)shouldUncurl{
[self.webVC.view animationPartialCurlUp];
}
-(void)doneWasPressed{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.50];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp
forView:self.webVC.view
cache:YES];
self.webVC.view.hidden = YES;
[UIView commitAnimations];
}
I built a app with storyboard, with an initial view controller, connected to many subsequent view controllers, connected sequentially, using cross dissolve segue. These work one swipes. All works fine. However, instead of being forced to use the basic segues, I want to have a custom segue that will slide the view controller content on and off the screen, much like the push for navigationControllers, but, being enabled to go left and right depending if one is going forward or backwards in the app.
I have set the segue to custom, and have created and saved a MySegue.h file. HOWEVER, I don't know how to code a custom segue that will slide one viewcontroller off the screen as the other slides on and back and forth as I move between view controllers.
Can anyone please provide me with come coding (it should be easy!) for a custom segue to move from one view controller to the next and back by sliding the screen on and off so I don't have to use the basic cross dissolve or standard flips offered in Xcode 4.2? I would be most grateful.
I was running into the same issue here, but I was using swipe gestures to go from one view controller to the next. Using the storyboard itself to set up segues was working fine, but when I needed a swipe to "go back" to the previous view controller, the animation went from right-to-left, instead of left-to-right. To fix that issue I did the following:
Embedded a Navigation Controller in the root view controller.
Used segues to push new view controllers.
When I wanted to "go back" to the previous view controller, I did not add a segue to the storyboard to push the previous view controller. Instead, I called the Navigation Controller's popViewControllerAnimated method whenever a back swipe occurred.
To create a custom segue that slides view controllers first create a class that extends UIStoryboardSegue, then override the perform selector. I just made something like this and it should work for you too. Here's my code:
#define kAnimationDuration 0.5
#import "CustomSlideSegue.h"
#implementation CustomSlideSegue
- (void)perform
{
UIViewController *sourceViewController = (UIViewController *) self.sourceViewController;
UIViewController *destinationViewController = (UIViewController *) self.destinationViewController;
[sourceViewController.view addSubview:destinationViewController.view];
[destinationViewController.view setFrame:sourceViewController.view.window.frame];
[destinationViewController.view setBounds:sourceViewController.view.bounds];
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
if ( !self.slideLeft ) {
[UIView animateWithDuration:kAnimationDuration
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[destinationViewController.view setCenter:CGPointMake(screenSize.height + screenSize.height/2, screenSize.height/2 - 138)];
[destinationViewController.view setCenter:CGPointMake(screenSize.width/2 + 127, screenSize.height/2 - 138)];
}
completion:^(BOOL finished){
[destinationViewController.view removeFromSuperview];
[sourceViewController presentViewController:destinationViewController animated:NO completion:nil];
}];
} else {
[UIView animateWithDuration:kAnimationDuration
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[destinationViewController.view setCenter:CGPointMake(-1*screenSize.height/2, screenSize.height/2 - 138)];
[destinationViewController.view setCenter:CGPointMake(screenSize.width/2 + 127, screenSize.height/2 - 138)];
}
completion:^(BOOL finished){
[destinationViewController.view removeFromSuperview];
[sourceViewController presentViewController:destinationViewController animated:NO completion:nil];
}];
}
}
Then when you want to perform the segue use this code:
UIViewController *destination = [self.storyboard instantiateViewControllerWithIdentifier:#"some identifier"];
CustomZoomSegue *segue = [[CustomZoomSegue alloc] initWithIdentifier:#"segue vc identifier" source:self destination:destination];
[self prepareForSegue:segue sender:self];
[segue perform];
The CGPointMake calls are custom to my code (I used landscape orientation) but you should be able to change it to fit your needs. If you need further clarification or have any question let me know and I will try to answer.
NOTE: I use storyboards with no segue lines between the view controllers.
Why not just use a UINavigationController? This seems to be exactly what they're designed for.