I have a UIViewController I'm presenting using a custom transition (for blurring). When I press the close button, I want the UIViewController to fade out. I reduce the opacity of the view and it fades out fine. And in the completion block, I dismiss the UIViewController. It gets dismissed, but there's a flicker soon after the dismiss is done and over with.
Here's what I'm doing:
- (IBAction)closePressed:(id)sender {
[CATransaction setCompletionBlock:^{
[self dismissViewControllerAnimated:NO completion:nil];
}];
CABasicAnimation *fadeOut = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeOut.fromValue = [NSNumber numberWithFloat:1.0];
fadeOut.toValue = [NSNumber numberWithFloat:0.0];
fadeOut.duration = 0.4f;
[self.view.layer addAnimation:fadeOut forKey:#"fadeOut"];
}
Can't paste a screenshot or anything as it happens quickly. There's a flicker before the parent UIViewController is shown.
When you animate via CAAnimation you are actually animating the presentation layer of the view, not the actual view. So when animation ends, you'll end up with the view as it was before the animation started.
To avoid that add this two lines before addAnimation:forKey:
fadeOut.removedOnCompletion = NO;
fadeOut.fillMode = kCAFillModeForwards;
Have you tried as like below?
[UIView animateWithDuration:0.4 animations:^
{
self.view.alpha = 0.0;
} completion:^(BOOL finished)
{
[self dismissViewControllerAnimated:NO completion:nil];
}];
Instead of above method, You can use self.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
Try switching your opaque setting for that viewcontroller to YES / NO
Default value is YES, so you probably want to try out NO
Related
I have a readonly UITextView, and I'm updating the text.
When the new text appears, I want it to animate as follows: New text slides in from the right, as the old text slides offscreen toward the left side.
Is it possible to do this with transitionWithView? If not, what's the best way to do it? I can make it do a CrossDissolve, but that's not the animation I'm looking for:
[UIView transitionWithView:self.storyTextView
duration:0.5
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{
[self.storyTextView setText:withStory.storyText];
}
completion:nil];
The reason for insisting on the right to left animation is because eventually I want the user to be able to trigger it by swiping toward the left on the UITextView.
CATransition will allow you to do this, with a transition type of 'push' and a subtype of 'from right'. It's very straightforward to use:
CATransition *transition = [CATransition new];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromRight;
// Make any view changes
self.textView.text = newText;
// Add the transition
[self.textView.layer addAnimation:transition forKey:#"transition"];
Note that this, while it's what you've asked for, won't fit nicely with a gesture without some extra tinkering - to tie it to a gesture you'd need to do something like set the layer speed to 0 and the manually update the progress of the animation to match your gesture.
What about just using UIView animateWithDuration and slide the one textView to the right while sliding a new textView from the left. Here is an example just have to work out the positions. Let me know if this is what you were looking to do.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.textView2.frame = CGRectMake(1000.0,self.textView1.frame.origin.y,self.textView2.frame.size.width,self.textView2.frame.size.height);
}
- (IBAction)animate:(id)sender {
[UIView animateWithDuration:0.5 animations:^{
self.textView1.frame = CGRectMake(-200.0,self.textView1.frame.origin.y,self.textView1.frame.size.width,self.textView1.frame.size.height);
[UIView animateWithDuration:0.5 animations:^{
self.textView2.center = (CGPointMake(200, 200));
}];
}];
}
You can also try something like this.
You can create a containerView for your text view and then change the coordinates using UIView animations to give it a slide from right to left.
Please see code below.
First declare a container view and the make the required text view its subview.
In .h
#property (strong, nonatomic)IBOutlet UIView *containerView;
#property (strong, nonatomic)IBOutlet UITextView *childTextView; //I have set it as subview in xib
In .m make sure to set the following in viewDidLoad or in Xib
self.containerView.clipsToBounds = YES;
Gestures
UISwipeGestureRecognizer *recog = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(changeText)];
recog.direction = UISwipeGestureRecognizerDirectionLeft;
[self.containerView addGestureRecognizer:recog];
Beautification (for this you would need QuartzCore/QuartzCore.h)
self.containerView.layer.borderWidth = 1.0f;
self.containerView.layer.borderColor = [UIColor lightGrayColor].CGColor;
self.containerView.layer.cornerRadius = 5.0f;
To the Point
- (void)changeText
{
[UIView animateWithDuration:0.3f animations:^(void){
CGRect endPos = self.childTextView.frame;
endPos.origin.x -= endPos.size.width; //Move it out of the view's frame to the left
self.childTextView.frame = endPos;
} completion:^(BOOL done){
CGRect startPos = self.childTextView.frame;
startPos.origin.x += (2*startPos.size.width);// this will take it to the right.
self.childTextView.frame = startPos;
self.childTextView.text = #"Changed text";
[UIView animateWithDuration:0.2f animations:^(void){
CGRect displayPos = self.childTextView.frame;
displayPos.origin.x -= displayPos.size.width; // To compensate for the double +ve on top
self.childTextView.frame = displayPos;
}];
}];
}
This question already has answers here:
How to animate the change of image in an UIImageView?
(15 answers)
Closed 8 years ago.
i'm trying to animate a UIImageView to change UIImage during the animation, but the duration in the method doesn't work, the animation is done immediately, why?
this is the code:
- (IBAction)do_action:(id)sender {
[UIView animateWithDuration:3.0 animations:^{
[self.my_image setImage:[UIImage imageNamed:#"image1"]];
} completion:^(BOOL finished) {
[self.my_image setImage:[UIImage imageNamed:#"image2"]];
}];
}
i have inserted 3 second for the animation duration, but doesn't work, do it immediately , how i can do?
Try this:
self.my_image.image = [UIImage imageNamed:#"image1"];
CATransition *transition = [CATransition animation];
transition.duration = 1.0f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionFade;
[self.my_image.layer addAnimation:transition forKey:nil];
To set the second image, you need to implement a delegate to get notified about the event when the animation completes.
CATransition has a delegate method animationDidStop:finished: which you can use.
Set the delegate property and implement the method:
transition.delegate = self;
...
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
[self.my_image setImage:[UIImage imageNamed:#"image2"]];
}
SetImage is not an animatable Behavior.
Easier way to accomplish is to use setAnimationImages property of UIImageView
//set Your Image names as array
[self.imageView setAnimationImages:[NSArray arrayWithObjects:#"IMG_1_Name",#"IMG_2_Name",nil]];
[self.imageView setAnimationRepeatCount:1];
[self.imageView setAnimationDuration:0.5]; //Your total cycle time. here 0.25 sec for each image
[self.imageView startAnimating];
I am new to iOS. I am trying to change the transition animation in navigation controller to load a new UIView controller from bottom to top, using Segue. I believe it will not be too hard to implement but may be I am not able to understand it.
I could not find any solution in other posts.
Here's the code I've been trying:-
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"alarmSegue"]) {
tab2ViewController *destViewController = segue.destinationViewController;
UIView *destView = destViewController.view;
destViewController.selectionName = #"alarms";
[sender setEnabled:NO];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView animateWithDuration:0.0
delay:0.0
options: UIViewAnimationOptionCurveLinear
animations:^{
[destView setTransform:CGAffineTransformMakeTranslation(0, -1000)];
//[destView setFrame:CGRectMake(0, 440, 400, 45)];
//destView.frame = CGRectMake(0, 0, 320, 460);
}
completion:^(BOOL finished){
[sender setEnabled:YES];
}];
[UIView commitAnimations];
}
}
I just want to implement a SIMPLE transition from bottom to top, using Segue. I want to set some properties of destination controller as well.
Replace the code between [UIView beginAnimations:nil context:nil]; and [UIView commitAnimations]; (including those two lines as well) with the following:
CGAffineTransform baseTransform = destView.transform; //1
destView.transform = CGAffineTransformTranslate(baseT,0,destView.bounds.size.height); //2
[UIView animateWithDuration: 0.5 animations:^{
destView.transform = baseTransform; //3
}];
Make a reference of the original transform.
Push our view down without an animation so we can animate it back up
Set the original transform back to the view. This time, it'll animate
You can further add the delay, options and completion parameters to that UIView Animation method. Xcode will probably help you with the autocompletes on those.
Well you are nearly there.
You need to change two things mainly:
1 Change the initial position of the view. You want the view to appear from bottom to top, so initially your view should be at the bottom. This piece of code should be written before the animation.
[destView setTransform:CGAffineTransformMakeTranslation(0, -1000)];
2 Then you need to slide from bottom to the original position , hence inside the animation block:
[destView setTransform:CGAffineTransformMakeTranslation(0, 0)];
One more suggestion, I guess you are using a newer version of iOS(above iOS4) instead of using animation blocks use UIView's animation method
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
For further info, refer description.
EDIT :
According to your edit on question, you are looking to change the transition animation for NavigationController in the app using storyboard. For that you could use Custom Segue. You will have to create a custom segue, a class that subclasses UIStorySegue and override its perform method like this:
-(void)perform {
//UIViewAnimationOptions options;
UIViewController *sourceViewController = (UIViewController*)[self sourceViewController];
UIViewController *destinationController = (UIViewController*)[self destinationViewController];
CATransition* transition = [CATransition animation];
transition.duration = 0.9f;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush; //kCATransitionMoveIn; //, kCATransitionPush, kCATransitionReveal, kCATransitionFade
transition.subtype = kCATransitionFromTop; //kCATransitionFromLeft, kCATransitionFromRight, kCATransitionFromTop, kCATransitionFromBottom
[destinationController.view.layer addAnimation:transition forKey:kCATransition];
[sourceViewController presentViewController:destinationController animated:NO completion:nil];
}
You have to change the segue class to this custom segue in storyboard as well, to make it function.
Try using transition effects
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"alarmSegue"]) {
CATransition transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.type = kCATransitionPush;
transition.subtype = direction;
[self.view.layer addAnimation:transition forKey:kCATransition];
tab2ViewController *destViewController = segue.destinationViewController;
UIView *destView = destViewController.view;
destViewController.selectionName = #"alarms";
[sender setEnabled:NO];
}
}
Hi I have a custom transition between two view controllers and I want to remove the black gap between them when it makes the transition. What can I do to do this? Thank you! Here is how the transition is performed currently with a CATransition and a picture of the gap.
- (void)bottomButtonScreen4:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"Swipe Up Worked");
settingsViewController = [[SettingsViewController alloc] init];
CATransition* transition = [CATransition animation];
transition.duration = 0.5;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromTop;
[self.navigationController.view.layer addAnimation:transition forKey:nil];
settingsViewController = [self.storyboard instantiateViewControllerWithIdentifier: #"settingView"];
[self.navigationController pushViewController:settingsViewController animated:NO];
}
Using this transition, there's only so much you can do. You'll notice (if you make it slow enough) that as the new view moves in, it's also fading in and the outgoing view is fading out. You can get rid of the black bar by setting the window's background color to the same color as your incoming view's background color, but you'll also see the outgoing controller's view fade to that color as it goes out. Check that out, and see if that's a look you can live with.
self.view.window.backgroundColor = settingsViewController.view.backgroundColor;
P.S. You should get rid of the line where you alloc init settingsViewController. It isn't doing anything now, and it was the wrong way to get that instance anyway.
After Edit:
Another way to do this that gets rid of the black bar problem, is to use animateWithDuration like this:
-(IBAction) pushViewControllerFromTop {
CGFloat height = self.view.bounds.size.height;
UIViewController *settingsViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Blue"];
settingsViewController.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y - height, self.view.frame.size.width, self.view.frame.size.height);
[self.view.superview addSubview:settingsViewController.view];
[UIView animateWithDuration:.5 animations:^{
self.view.center = CGPointMake(self.view.center.x, self.view.center.y + height);
settingsViewController.view.center = CGPointMake(settingsViewController.view.center.x, settingsViewController.view.center.y + height);
} completion:^(BOOL finished) {
[self.navigationController pushViewController:settingsViewController animated:NO];
}];
}
I'm using ECSliding and I have this problem!
In my project there are this files:
InitViewController (ECSlidingController)
FirstViewController (UIViewController)
SecondViewController (UIViewController)
LeftMenuViewController (UIViewController)
I use InitView to instatiate my FirstView as the topview.
In my FirstView there is a button, when pressed sets SecondView as topview.
Is it possible to animate the changing topview like I'm opening a new view?
or how can I open a new view that uses ECSliding like the first one?
I'm using this code to change topview:
self.slidingViewController.topViewController = second;
[self.slidingViewController resetTopView];
The animation that I want could just be the default one, like:
[self presentViewController:(*UIViewController) animated:YES completion:nil];
Subclass ECSlidingViewController. I call my subclass DeckController:
In DeckController.h:
- (void)setTopViewController:(UIViewController *)topViewController withTransition:(CATransition*)transition;
In DeckController.m:
- (void)setTopViewController:(UIViewController *)topViewController withTransition:(CATransition*)transition {
self.topViewController = topViewController;
[UIView animateWithDuration:0.25f animations:^() {
[[self.view layer] addAnimation:transition forKey:#"topViewTransition"];
}];
}
Then in the view controller that wants to switch to the new top view, do something like this:
CATransition *animation = [CATransition animation];
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromRight];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[animation setFillMode:kCAFillModeBoth];
[animation setDuration:.25];
DeckController *deck = (DeckController*)self.slidingViewController;
[deck setTopViewController:newTopViewController withTransition:animation];
Probably your best option is to use the animation methods of the sliding view controller. Animate the top view off screen, in the completion block change the top view controller (to second) then start a new animation to animate the top view back onto screen.
[self.slidingViewController anchorTopViewOffScreenTo:...
animations:...
onComplete:^{
self.slidingViewController.topViewController = second;
[self.slidingViewController resetTopView];
}];
I just use same operation as when Im clicking on a menuitem in my left menu.
[self.anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^{
CGRect frame = self.topViewController.view.frame;
self.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"secondVC"];
self.topViewController.view.frame = frame;
[self resetTopView];
}];
Just generalize this, and you can call it everywhere. It does the animation by itself.
You can do as #Alex, but if you don't have to do some custom animation, this is all you need.
NB. in my example Im using this in the initViewController (for which is the ECSlidingViewController, therefore i'm sending to self), for forcing user to a specific viewController. If you need to change view in any subViewControllers you just sending to self.slidingViewController like the example below:
[self.slidingViewController anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^{
CGRect frame = self.slidingViewController.topViewController.view.frame;
self.slidingViewController.topViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"secondVC"];
self.slidingViewController.topViewController.view.frame = frame;
[self.slidingViewController resetTopView];
}];
You can manually create the animation then set the topViewController at the end so that the sliding view controller is in the state that you expect.
SecondTopViewController *secondTopViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SecondTop"];
secondTopViewController.view.frame = self.slidingViewController.view.bounds;
secondTopViewController.view.alpha = 0;
// temporarily add the second top view so that we can animate it in.
[self.slidingViewController.view addSubview:secondTopViewController.view];
[UIView animateWithDuration:0.5 animations:^{
secondTopViewController.view.alpha = 1;
} completion:^(BOOL finished) {
// remove the second top view.
[secondTopViewController.view removeFromSuperview];
// let sliding view controlller take over from here
self.slidingViewController.topViewController = secondTopViewController;
}];
See my working example here: https://github.com/enriquez/ECSlidingViewController/tree/so-16658539
#Michael-Enriquez's answer converted to swift.
let viewController = (self.storyboard?.instantiateViewControllerWithIdentifier("ViewController"))! as? ViewController
let navController = UINavigationController(rootViewController: viewController!)
navController.view.frame = self.slidingViewController().view.bounds
navController.view.alpha = 0
self.slidingViewController().view.addSubview(navController.view)
UIView.animateWithDuration(0.25, animations: {
navController.view.alpha = 1
}, completion: {(finished:Bool) -> Void in
navController.view.removeFromSuperview()
self.slidingViewController().topViewController = navController
})