Animating the frame of a UISegmentedControl - cocoa-touch

I have a UISegmentedControl that I'm trying to animate the frame of using the following (simplified, but functional) code:
UISegmentedControl *seg = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"item1", #"item2", nil]];
[seg setFrame:CGRectMake(0, 300, 100, 50)];
[self addSubview:seg];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^ {
[UIView animateWithDuration:5.0 animations: ^ {
[seg setFrame:CGRectMake(100, 0, 400, 50)];
}];
});
What happens is the size changes immediately, but the position animates properly. Why is it doing this, and are there any workarounds?

Tell the segmented control to layout if needed in the animation block.
[UIView animateWithDuration:.25f animations:^{
self.segmentedControl.frame = frame;
[self.segmentedControl layoutIfNeeded];
}];

It seems that you can work around this by manually resizing all the subviews of the UISegmentedControl inside the animation block.

Related

Using cgaffinetransformmakescale and cgaffinetransformmakerotation deforms my view

I have a simple UIView:
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
view.backgroundColor = [UIColor greenColor];
[self.view addSubview:view];
self.someView = view;
Then I do this
[UIView animateWithDuration:10
delay:1
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.someView.center = CGPointMake(CGRectGetWidth(self.view.bounds) - CGRectGetWidth(self.someView.frame) / 2, 150);
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI);
self.someView.transform = CGAffineTransformScale(transform, 2, 0.5);
}
completion:^(BOOL finished) {
}];
While is executed this simple animation, someView is deformed. As far as I know it should rotate and change it's size. I can't understand why is it happening so, how can i fix it?
Thanks
The problem is animating a combined scaling and rotation. The end result is the same as animating them separately, but the frames in between are not easily defined. So the animation framework makes a guess, and it is not what you are expecting.
If you want the animation to show a rotation and scaling separately, you need to apply them separately to the view hierarchy. For example:
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
view.backgroundColor = [UIColor greenColor];
[self.view addSubview:view];
self.someView = view;
UIView *innerView = [[UIView alloc] initWithFrame:view.bounds];
innerView.backgroundColor = [UIColor blueColor];
[view addSubview:innerView];
self.someInnerView = innerView;
The animation splits the transform:
[UIView animateWithDuration:10
delay:1
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.someView.center = CGPointMake(CGRectGetWidth(self.view.bounds) - CGRectGetWidth(self.someView.frame) / 2, 150);
self.someView.transform = CGAffineTransformMakeRotation(M_PI);
self.someInnerView.transform = CGAffineTransformMakeScale(2, 0.5);
}
completion:^(BOOL finished) {
}];

How can I move a UIView to an end location but make it slow down as it approaches it?

I'm trying to move a box from point A = (0, 0) to B = (0, 1000) but I want to slow down as it approaches point B. I tried UIViewAnimationOptionCurveEaseOut but it seems to be moving at a constant velocity.
UIView *box = [[UIView alloc] init];
box.frame = CGRectMake(0, 0, 500, 500);
box.backgroundColor = [UIColor redColor];
[self.view addSubview:box];
// Animation
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
box.frame = CGRectMake(0, 1000, box.frame.size.width, box.frame.size.height);
} completion:nil];
Any ideas?
The easiest solution is to do the animation in multiple stages, with different speed, e.g. like this:
// Animation stage 1
[UIView animateWithDuration:0.1 //First half is fast
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
box.frame = CGRectMake(0, 500, box.frame.size.width, box.frame.size.height);
} completion:^(BOOL finished) {
// Animation stage 2
[UIView animateWithDuration:0.2 //Second half slower
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
box.frame = CGRectMake(0, 1000, box.frame.size.width, box.frame.size.height);
} completion:nil];
}];
It should be possible to get a smooth animation by playing around with it a bit.
If this is iOS 7+, use spring animations. Add this code to viewDidAppear: to test it out and see the difference between using a spring vs. a simple ease out curve.
Also note that Apple really encourages the use of spring animations going forward, since almost all (or all?) of their animations in iOS 7+ are done with springs.
UIView *fooView = [[UIView alloc] initWithFrame:CGRectMake(50, 100, 50, 50)];
fooView.backgroundColor = [UIColor redColor];
[self.view addSubview:fooView];
[UIView animateWithDuration:1
delay:1
usingSpringWithDamping:1
initialSpringVelocity:1
options:0
animations:^{
fooView.frame = CGRectMake(50, 400, 50, 50);
}
completion:nil];
UIView *barView = [[UIView alloc] initWithFrame:CGRectMake(200, 100, 50, 50)];
barView.backgroundColor = [UIColor redColor];
[self.view addSubview:barView];
[UIView animateWithDuration:1
delay:1
options:UIViewAnimationOptionCurveEaseOut
animations:^{
barView.frame = CGRectMake(200, 400, 50, 50);
}
completion:nil];
If you're not satisfied with built-in easing functions you can create your own. You want something related to CAMediaTimerFunction or CAKeyframeAnimation. You can read about it here and here. There's also some good answers here.

Rotate between 2 views

I have two views. How do I transition between them by rotation? I want to rotate the first view to get the second view... Thanks.
I tried using [UIView transitionFromView but it does not have a rotation parameter.
It might be what you are expecting. But, not quite as you expected since it is not using transition animation simply adding or removing the subviews.
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(60, 140, 200, 200)];
view1.backgroundColor = [UIColor greenColor];
[self.view addSubview:view1];
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(60, 140, 200, 200)];
view2.backgroundColor = [UIColor redColor];
[self.view addSubview:view2];
view2.alpha = 0;
[UIView transitionWithView:view1 duration:2.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
view1.transform = CGAffineTransformMakeRotation(M_PI/2);
} completion:^(BOOL com){
view2.alpha = 1;
[view1 removeFromSuperview];
}];

Setting UIView's frame property in overlapping animation blocks causes jump

(iOS 5.1, XCode 4.4)
In a project I am working on, I have two controllers that both set the frame property of the same UIView (to set size and position, respectively), in separate animation blocks that temporally overlap (the second animation block is started before the first one finishes). This seems to cause an odd jump (unanimated change) in the position of the UIView.
Minimal example to reproduce (place in viewcontroller of a new single view project (iphone), and hook up some button to the action method).
#interface ViewController ()
#property (nonatomic, strong) UIView *viewA;
#end
#implementation ViewController
- (IBAction)buttonPressed
{
// Animate
[UIView animateWithDuration:5 animations:^{
self.viewA.frame = CGRectMake(0, self.viewA.frame.origin.y, 320, 200);
}];
[UIView animateWithDuration:5 animations:^{
self.viewA.frame = CGRectMake(0, 200, 320, self.viewA.frame.size.height);
}];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.viewA = [[UIView alloc] init];
self.viewA.backgroundColor = [UIColor orangeColor];
self.viewA.frame = CGRectMake(0, 100, 320, 100);
[self.view addSubview:self.viewA];
}
#end
On pressing the button, you'll see the view jump about 50 pixels (half the distance it moves).
If anyone know why this is happening and/or how to address this issue, I would love to hear from you. Thank you in advance,
Patrick
You can use the UIViewAnimationOptionBeginFromCurrentState option to avoid the jump:
[UIView animateWithDuration:5.0 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.viewA.frame = CGRectMake(0, 200, 320, self.viewA.frame.size.height);
} completion:nil];
That's how blocks work, you are doing both animations at the same time, try this.
[UIView animateWithDuration:5 animations:^{
self.viewA.frame = CGRectMake(0, self.viewA.frame.origin.y, 320, 200);
}
completion:^(BOOL finished) {
self.viewA.frame = CGRectMake(0, 200, 320, self.viewA.frame.size.height);
}
];

Animated background in iOS

I want to animate my background after a view got loaded in my iOS application. This works with solid colors, but I'm not able to let it work using background images. This is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
[UIView animateWithDuration:5.0 animations:^{
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background.png"]];
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background_dark.png"]];
}];
[UIView commitAnimations];
}
Does anyone know the correct way to do this?
Thanks
You animate things like this:
view.backgroundColor = INITIAL COLOR
[UIView animateWithDuration:5.0 animations:^{
view.backgroundColor = FINAL COLOR
}];
The animate method animates the change from the current state to whatever you set up in the animation block. The animation block is not a list of steps.
Therefore, I think what you want is this:
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background.png"]];
[UIView animateWithDuration:5.0 animations:^{
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background_dark.png"]];
}];
Also, do NOT call commitAnimations afterward, that's only if you're using the old-style beginAnimations method. Read the documentation, and don't just call methods to see if they work.
Not all properties can be animated like this. It is very likely that a background image is one of them. What you can do is have a second view on top with the new image and animate its alpha from 0 to 1.
Ty this:
[UIView animateWithDuration:5.0 animations:^{
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background.png"]];
} completion:^(BOOL finished)
{
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"background_dark.png"]];
}];