I've been asked to add a fully custom transition between two UIViewControllers and came up with this solution. It works, but I don't know if there's a better / more elegant way to do it.
By fully custom, I mean involving modifying the first view controller subviews (moving them, not only fading or whatever), being able to change the duration, etc.
I'd like to know two things :
Can this be broken up in any way ? Some problem I might have forgotten maybe ?
Do you think this is ugly ? If so, is there a better solution ?
As a few lines of code is better than a thousand words, here is a very simple example to get the idea :
// Considering that self.mainView is a direct subview of self.view
// with exact same bounds, and it contains all the subviews
DCSomeViewController *nextViewController = [DCSomeViewController new];
UIView *nextView = nextViewController.view;
// Add the next view in self.view, below self.mainView
[self.view addSubview:newView];
[self.view sendSubviewToBack:newView];
[UIView animateWithDuration:.75f animations:^{
// Do any animations on self.mainView, as nextView is behind, you can fade, send self.mainView to wherever you want, change its scale...
self.mainView.transform = CGAffineTransformMakeScale(0, 0);
} completion:^(BOOL finished) {
// Push the nextViewController, without animation
[self.navigationController pushViewController:vc animated:NO];
// Restore old
self.backgroundImageView.transform = CGAffineTransformIdentity;
}];
I've double checked a few things that I thought might get wrong :
After the push, the view hierarchy isn't broken, everything looks fine, self.view suffer any changes
When poping, nextView isn't in self.view any more.
Thanks for your opinions.
There are differents way to make a transition between ViewControllers. Your code works but you can implement a more official way.
Use methode transitionFromViewController:toViewController:
Check the tutorial : http://www.objc.io/issue-1/containment-view-controller.html#transitions
Or you can use UIViewControllerContextTransitioning Check the tutorials : http://www.objc.io/issue-5/view-controller-transitions.html and http://www.teehanlax.com/blog/custom-uiviewcontroller-transitions/
Related
Does anyone know of a way to load a view controller at a point other than the top. This would be similar to opening a web page part way down ie Go to Target
This is not a tableview or webview so the point is not to go to a specific row of the table or place in the webview. Rather it is a regular UIVIewController and the point is to go to a specific vertical point on the screen where there is some relevant content.
Thanks for any suggestions.
Edit: I ended up using the following, not as elegant as I was hoping, but adequate:
-(void) raiseView:(float) float {
CGRect contextRect = self.view.bounds;
float boundsY = contextRect.origin.y;
contextRect.origin.y=boundsY+float;
self.view.bounds = contextRect;
}
I recommend reading up further on controller based interface and how to best use the common practices to your advantage.
My suggestion would be to rely on the presentation styles available to aUIViewController. Here would be an example of presenting a view controller over the current view:
MyViewController *controller = [[MyViewController alloc] init];
controller.modalPresentationStyle = UIModalPresentationOverCurrentContext;
[currentViewController presentViewController:controller animated:YES completion:^{
// Do whatever you need to here
}];
Then upon completion, you can do anything you need to for presenting your content. Here you can simply set the controllers view to be whatever frame you desire (with, in your terms, any top point you would like). This way you can present a VC to the user and display anything you need on top at the point relevant to the content on the previous view.
Happy coding!
Okay guys/gals, here's what I'm looking to do, I'd love some pointers to a good tutorial or a good explanation on how to do this. As much as I'd love someone to post code here, I need to learn how to do this for future projects and overall feel good points.(I've searched myself but could only find swift tutorials which were similar but not the same)
Imagine a screen with a circular button in the middle, wait you don't have to
Sorry about the size, I want to press that button and perform a segue which expands from that button, eventually filling the screen with the new red screen. So in essence the button grows and fill the screen but really I'm transitioning to a new screen.
Any pointers greatly appreciated. Thanks.
A minor detail: this is the only screen on storyboard at present
I had to do a couple of non-obvious things to make this work. First, get rid of button constraints in the storyboard. Second, add a line to my viewDidLoad to derive constraints.
- (void)viewDidLoad {
[super viewDidLoad];
self.redButton.translatesAutoresizingMaskIntoConstraints = YES;
}
Then, for the actual effect I create an animation when the button is pressed. The animated:NO bit in the completion handler prevents extra visual noise.
- (IBAction)redButtonTapped:(id)sender {
[UIView animateWithDuration:3.0 animations:^{
self.redButton.frame = self.view.frame;
} completion:^(BOOL finished) {
UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"redController"];
[self presentViewController:vc animated:NO completion:nil];
}];
}
I used a simple button object for this. To make it work with a round image, you may have to play with the new frame size a bit.
I am working on an iOS app. The root view controller contains a UINavigationController which wraps up the main contents of the app, and a footerViewController (audio player) that will compress the main content when it animates up into view.
I’m using auto layout to show and hide this footer like so:
_footerVisibleConstraints = [… #“V:|[navControllerView][footerView(==90)]|" …];
_footerHiddenConstraints = [… #“V:|[navControllerView][footerView(==0)]|" …];
Generally this works well. But I’m struggling with one issue. I have a situation where I need to push a new view on the UINavigationController stack and animate my footer into view the same time:
[self.view layoutIfNeeded];
[UIView animateWithDuration:1.5f animations:^{
[[self view] removeConstraints:_footerHiddenConstraints];
[[self view] addConstraints:_footVisibleConstraints];
[[self view] layoutIfNeeded];
}];
[navigationController pushViewController:newViewController animated:YES];
The problem in this situation is that newViewController is animating in snapped to it's final (compressed) state, and not beginning from the full starting height of the view. So there is a gap at the bottom while the footer animates in.
I’ve slowed down the animation and posted a video here to demonstrate what I am describing.
Also, notice how when I pop back to the root view controller the content in the UINavigationController isn’t compressed either.
So, can someone explain to me what’s going on here? Is there a way to accomplish what I am after?
Just add a variable to the .h of your VC to stipulate whether the footer needs to open or not. Then add the footer animation to the didAppear method with a check on the variable. This will result in performing the actions in the order you want them to happen.
If you want both animations to happen at the same time you will need to subclass a segue and add a custom animation.
I want to use Storyboards to design content for a slider, and it seems like an easy way to design offscreen content is to use a childViewController. So I've done this
myViewController = [[UIStoryboard storyboardWithName:#"ipad" bundle:NULL] instantiateViewControllerWithIdentifier:#"keyPadOffScreen"];
[self addChildViewController:myViewController];
[myViewController didMoveToParentViewController:self];
newView = myViewController.view;
[self.view addSubview:newView];
And that adds the entire view controller over top of my root view. The problem is, I only want one of the subviews to show up, not the whole view. I can handle the animation, as long as I know how to add the root view. I tried this to just add the subview (sliderView is the name of the subview I want) instead of the whole view, but that did nothing
newView = myViewController.sliderView;
[self.view addSubview:newView];
Should I be using a different strategy?
EDIT: this DOES work, but it seems silly - setting the views size to just be the size of the subview.
newView.frame = CGRectMake(newView.frame.origin.x, newView.frame.origin.y, newView.frame.size.width, **myViewController.sliderView.frame.size.height**);
It does seem a bit overkill for just a view. Once you start doing a lot of custom view/animation/transition stuff it's often easier to implement in code, or at least it is for me since I've been doing it that way for a long time.
But maybe you want to stick with Storyboards. I respect that. And if you have a few developers working on this then it's important to keep some uniformity to how you set up your UI.
Instead of keeping it in a separate view controller and adding it when you need it to animate on-screen, simply add it to your existing view controller and either set it to hidden, or set it's alpha to 0.0 in IB. Then your animation can undo that and make it visible.
you can use custom segue here, for instance:
#implementation FRPresentEnteringPopupSegue
- (void)perform
{
FirstVC *toVC = self.destinationViewController;
SecondNavigationController *fromVC = self.sourceViewController;
toVC.view.frame = CGRectMake(0.0, 0.0, 300.0, 135.0);
toVC.view.center = CGPointMake(fromVC.view.bounds.size.width/2, fromVC.view.bounds.size.height + toVC.view.bounds.size.height/2);
[fromVC.view addSubview:toVC.view];
[toVC viewWillAppear:YES];
[UIView animateWithDuration:0.5
delay:0.0
usingSpringWithDamping:0.7
initialSpringVelocity:0.5
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
toVC.view.center = CGPointMake(fromVC.view.bounds.size.width/2, fromVC.view.bounds.size.height/2);
}completion:^(BOOL finished) {
[toVC viewDidAppear:YES];
}];
}
#end
make your UIStoryboardSegue subclass
override - (void)perform method with your custom view appearance code
use segue usual way
I'm trying to change from viewcontroller view1 to viewcontroller view2 with a smooth and nice transition, where view1 gets pushed to the left by view2 within a second. I have made a simple illustration of what I want in my iPhone app.
--> --> -->
I am using storyboard and developing for iOS 6 and higher. I'm not using segues. Currently I'm using this code to change from view1 to view2 without any animations in Xcode:
SecondViewController *viewTwo = [self.storyboard instantiateViewControllerWithIdentifier:#"View2"];
[self presentViewController:viewTwo animated:NO completion:nil];
I am looking for code that can replace my existing code with. Code that changes from view1 to view2 with the push transition
NOTE: I am not using NavigationControllers! Just normal ViewControllers. I would prefer to do this with codes.
EDIT: I understand that this would be very easy to do if I used a navigation controller, but the problem is that I'm done building the storyboard and I have already buildt everything using normal view controller. And that creates my second question:
Are there any way I can simply convert my UIViewControllers to work as UINavigationControllers. Without having to delete my UIViewController and build it again only using UINavController?
ANSWER: I solved my problem by implementing a navigation controller to my project. I have totally misunderstood the whole concept of navigations controllers earlier, but I have now figured it out. So my problem is solved!
Set animation for the view2. Use this code.
-(void)ViewNavigationOnLeftTabs
{
self.view.frame=CGRectMake(420, 0,self.view.frame.size.width,self.view.frame.size.height);
[UIView beginAnimations:#"Anim2" context:nil];
[UIView setAnimationDuration:0.4];
[UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
[UIView setAnimationDelegate:self];
self.view.frame=CGRectMake(0, 0,self.view.frame.size.width,self.view.frame.size.height);
[UIView commitAnimations];
}
-(IBAction)nextView
{
SecondViewController *sampleV=[[SecondViewController alloc] init];
[self.view addSubview:sampleV.view];
[self ViewNavigationOnLeftTabs];
}
Here I have added transition effect for the secondViewController exactly like your requirement. You need to handle the background view(view1) during the animation.
I have used this code in my app, and it's working fine for me.
Consider using container view controllers. Here is a tutorial:
http://www.cocoanetics.com/2012/04/containing-viewcontrollers/