A better way to animate resizing of UIView and subviews? - ios

Example screen capture:
What is a better way to animate the resizing of a UIView that includes subviews?
My current method:
In the screen capture, the parent UIView (orange) has a subview UILabel (yellow + autoresizing). animateWithDuration is animating the resizing of the parent UIView's frame. The problem with this method is it does not animate the resizing of the subview. It abruptly changes, where it should scale animatedly.
Is explicitly animating the subview best, or is there a more efficient method? (e.g. CGAffineTransform?)
Sample code:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.parentView = [[UIView alloc] initWithFrame:CGRectMake(10, 100, 200, 200)];
self.parentView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:self.parentView];
self.childLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 180, 180)];
self.childLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
self.childLabel.backgroundColor = [UIColor yellowColor];
[self.parentView addSubview:self.childLabel];
[NSTimer scheduledTimerWithTimeInterval:2
target:self
selector:#selector(randomizeParentViewSize)
userInfo:nil
repeats:YES];
}
- (void)randomizeParentViewSize {
CGFloat width = arc4random_uniform(100) + 50;
CGFloat height = arc4random_uniform(100) + 50;
[UIView animateWithDuration:1.5f
delay:0.0f
options:0
animations:^{
self.parentView.frame = CGRectMake(10, 50 + height, width, height);
}
completion:^(BOOL finished) {
}];
}

I Guess Your Yellow SubView is UILabel ?
When UILabel change frame size in UIViewAnimation will scale Immediately
unless you use the CGAffineTransformScale
some example
when
[self.view addSubview:view_parent];
[view_parent addSubview:view_component];
if both view_parent and view_component is UIView
[UIView animateWithDuration:2.0 animations:^{
[view_parent setFrame:CGRectMake(0, 20, 160, 160)];
[view_component setFrame:CGRectMake(20, 20, 80, 80)];
}];
is work fine
but when view_parent is UIView and view_component is UILabel
you need do something like ...
[UIView animateWithDuration:2.0 animations:^{
[view_parent setFrame:CGRectMake(0, 20, 160, 160)];
view_component.transform = CGAffineTransformScale(view_component.transform, 0.5, 0.5);
[view_component setFrame:CGRectMake(20, 20, 80, 80)];
}];
Wish help ~

To animate the view and also the subview you could use this code:
[UIView animateWithDuration:0.5f animations:^{
self.testView.transform = CGAffineTransformMakeScale(0.5, 0.5);
}];
More Info
You can't use frame, bounds, or center if you are also animating things using CGAffine, So if you also need to move the view use something like this:
CGAffineTransform transform = CGAffineTransformMakeTranslation(150,400);
transform = CGAffineTransformScale(transform, 0.5, 0.5);

Use UIViewAnimationOptionLayoutSubviews:
Add proper leading, trailing, top and bottom constraints on the subview.
[UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{ //resize the superview
} completion:nil];

Related

Replace subview with another, animating size change

I have a container view that has one subview. I want to replace this subview with another view which has a different size than original, so the container should be resized as well. Here's what I'm trying to do
UIView *content = NEW_CONTENT;
content.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentContainer layoutIfNeeded]; // the container view
// make the old content disappear first
[UIView animateWithDuration:0.3
animations:^{
UIView *oldContent = [self.contentContainer.subviews firstObject];
oldContent.alpha = 0.0f;
}
completion:^(BOOL finished) {
// then replace it with a new content
[UIView transitionWithView:self.contentContainer
duration:0.1
options:0
animations:^{
[[self.contentContainer.subviews firstObject] removeFromSuperview];
[self.contentContainer addSubview:content];
[content autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsZero];
[self.contentContainer layoutIfNeeded];
}
completion:^(BOOL finished) { // completion stuff}];
}];
Old content opacity animates properly, but the layout is changed without any animation. How could I fix it?
//3 views(container,first,second) allocate in viewdidload
UIView *container=[[UIView alloc] initWithFrame:CGRectMake(20, 20, 250, 300)];
container.backgroundColor=[UIColor redColor];
[self.view addSubview: container];
UIView *first=[[UIView alloc] initWithFrame:CGRectMake(100, 50, 100, 100)];
first.backgroundColor=[UIColor blueColor];
[self.view addSubview: first];
UIView *second=[[UIView alloc] initWithFrame:CGRectMake(50, 50, 200, 150)];
second.backgroundColor=[UIColor purpleColor];
second.alpha=0;
[self.view addSubview: second];
//Animation Function with first view removed adn changing the frame of container and adding the second view
[UIView transitionWithView:first duration:1.0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
first.alpha=0.0;
} completion:^(BOOL finished) {
[UIView transitionWithView:second duration:1.0 options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
container.frame=CGRectMake(50, 10, 100, 200);
second.alpha=1.0;
} completion:^(BOOL finished) {
}];
}];
I think so u r trying this same scenario.it works perfectly in my case

Unable to add subviews to a UIView when using the View's layer as a mask

I am trying to animate a UIView with a popover kind of effect, and I want the background of the UIView to have that ios7 style blur as the background.
Making this slightly more complicated, I'd also like to be able to apply a transform to the popover and its contents by dragging the corner, much like a chat in the Facebook app.
Now, my approach is basically this: take a blurred snapshot of the window, add a UIImageView as a subview of the controller's view, mask that imageView with the UIView popover. However, I am then finding that I can not interact with that UIView at all - subviews don't show up, gesture recognizers don't fire, etc.
Here is my code:
UIWindow *mainWindow = [[UIApplication sharedApplication] keyWindow];
CGRect frame = [self.tableView.tableHeaderView convertRect:self.displayView.frame toView:self.view];
UIView *view = [UIView new];
view.frame = frame;
view.backgroundColor = [UIColor blackColor];
view.layer.cornerRadius = 10.f;
view.tag = 100;
//Take the snapshot
UIGraphicsBeginImageContextWithOptions(mainWindow.rootViewController.view.bounds.size, YES, [[UIScreen mainScreen] scale]);
[mainWindow drawViewHierarchyInRect:mainWindow.bounds afterScreenUpdates:NO];
UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImage *blur = [screenImage applyDarkEffect];
UIImageView *imageView = [[UIImageView alloc] initWithImage:blur];
imageView.frame = self.view.bounds;
[self.view addSubview:imageView];
[self.view addSubview:view];
imageView.layer.mask = view.layer;
//Animate the view
[UIView animateKeyframesWithDuration:1.4f delay:0.0f options:UIViewKeyframeAnimationOptionCalculationModeCubic
animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.75 animations:^{
view.frame = CGRectInset(mainWindow.frame, 30, 40);
}];
[UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:0.05 animations:^{
view.frame = CGRectInset(mainWindow.frame, 20, 30);
}];
[UIView addKeyframeWithRelativeStartTime:0.8 relativeDuration:0.20 animations:^{
view.frame = CGRectInset(mainWindow.frame, 25, 35);
}];
} completion:^(BOOL finished) {
//This does nothing.
UIView *test = [UIView new];
test.frame = CGRectMake(100, 100, 100, 100);
test.backgroundColor = [UIColor redColor];
[view addSubview:test];
}];
Any insights in to why this isn't working? Or alternative approaches?
One alternate approach I thought of would be to roll my own keyframe animation: Add the imageView as a subview of the 'view', use an update timer, and calculate the frame of the view for each frame, offsetting the image view appropriately. But I was hoping there would be an easier way around this issue.

Show banner from top of the screen

I want to create a turn-based match. When player received a notification player:receivedTurnEventForMatch:didBecomeActive:
I want to show a banner that slides from the top of the screen. "Your turn. Play" similar to the banner that Game Center shows when player is authenticated.
How can I do it? Should it be a UIViewController or UIAlert, or what?
here is a good example for notification for what you want, i hope that help you:
https://github.com/ekurutepe/MPNotificationView
or
https://github.com/edgurgel/CMNavBarNotificationView
or
https://github.com/terryworona/TWMessageBarManager
There are MANY ways to do what you mentioned. This is just one of them. Feel free to change the code around and read up on UIView animations to you can try out different things yourself.
-(void)myMethod {
// create UILabel and set its properties
UILabel *myLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, 320, 50)];
myLabel.backgroundColor = [UIColor grayColor];
NSString *myLabelText = #"Welcome back, Mike Lyman"; // showing you to use NSString for label text
myLabel.textAlignment = NSTextAlignmentCenter;
myLabel.text = myLabelText;
[self.view addSubview:myLabel]; // add UILabel to view
myLabel.frame = CGRectMake(0, -50, 320, 50); // set text label frame offscreen
// start the animation to slide UILabel into visible view
[UIView animateWithDuration:0.5 delay:0
options:UIViewAnimationOptionCurveLinear
animations:^ {
myLabel.frame = CGRectMake(0, 20, 320, 50);
}
completion:^(BOOL finished) {
// after 1 second delay, start sliding UILabel out of visible view
[UIView animateWithDuration:0.5 delay:1
options:UIViewAnimationOptionCurveLinear
animations:^ {
myLabel.frame = CGRectMake(0, -50, 320, 50);
}
completion:^(BOOL finished) {
NSLog(#"Done");
[self.view removeFromSuperview]; // remove UILabel from view completely
}];
}];
}

Creating a custom view with an animation

Im trying to make a custom view where one of it's elements(a UILabel) has animation that when a button of the custom view is tapped the label should expand and then contract. The first part of the animation happens correctly but not the second. Im using this method.
[UIView animateWithDuration:1.3 animations:^{
CGRect frame = CGRectMake(50, 15, 140, 20);
[labelFav setFrame:frame];
} completion:^(BOOL finished) {
//[labelFav setHidden:YES];
[UIView animateWithDuration:1.3 animations:^{
CGRect frame = CGRectMake(50, 15, 0, 20);
[labelFav setFrame:frame];
}];
}];
I believe it's a problem with how the methods Refresh and LayoutSubviews are implemented.
I did it following this tutorial, ray wenderlinch
The problem comes when after the first animation is called, LayoutSubviews is called, so all the items are set to the initial position, and when the first animation is finished the second one starts but the items are set to the original state, so I believe the animation doesnt happen because there's been a change between animations.
I would like to know how to implement this correctly, can you lend me a hand?
Thanks.
PS: This is a second question about the same problem but with a different angle.my previous question
[UPDATE]
This is my code for LayoutSubviews:
- (void)layoutSubviews {
[super layoutSubviews];
if(_idEvent == 0) return;
_buttonFav = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 40, 40)];
[_buttonFav setImage:[UIImage imageNamed:#"fav.png"] forState:UIControlStateNormal];
[_buttonFav addTarget:self action:#selector(pressedFavButton:) forControlEvents:UIControlEventTouchDown];
_labelButton = [[UILabel alloc]initWithFrame:CGRectMake(0, 45, 0, 20)];
_labelButton.text = #"LABEL";
[self addSubview:_labelButton];
[self addSubview:_buttonFav];
}
Which is called right before the second animation. My refresh method is:
- (void)refresh {
if(selected){
[_buttonFav setImage:[UIImage imageNamed:#"fav_sel.png"] forState:UIControlStateNormal];
}
else{
[_buttonFav setImage:[UIImage imageNamed:#"fav.png"] forState:UIControlStateNormal];
}
}
Where I just set the image for the button depending on an attribute that lets me know if the button was tapped.
I do, how to make an animation with Auto-layout then? With the new
XCode you dont have an a option to disable it.
Add a width constraint to a label. Then create IBOutlet to this constraint in your implementation file (see the section 3. here, if you don't know how to do that), let's say something like:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *buttonWidthConstraint;
Then you can animate it this way:
[UIView animateWithDuration:1.3 animations:^{
self.buttonWidthConstraint.constant = 140;
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
[UIView animateWithDuration:1.3 animations:^{
self.buttonWidthConstraint.constant = 0;
[self.view layoutIfNeeded];
}];
}];
use SetTransform and CGAffineTransform
try this out
[UIView animateWithDuration:1.3 animations:^{
[labelFav setTransform:CGAffineTransformMakeScale(0.5,0.5)];
} completion:^(BOOL finished) {
[UIView animateWithDuration:1.3 animations:^{
[labelFav setTransform:CGAffineTransformMakeScale(1.0,1.0)];
}];
}];
Try to encapsulate the UILabel into a UIView and animate that view.
UIView *auxView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 100.0)];
[auxView addSubview:labelFav];
[UIView animateWithDuration:1.3 animations:^{
CGRect frame = CGRectMake(50, 15, 140, 20);
[auxView setFrame:frame];
} completion:^(BOOL finished) {
//[labelFav setHidden:YES];
[UIView animateWithDuration:1.3 animations:^{
CGRect frame = CGRectMake(50, 15, 10, 20);
[auxView setFrame:frame];
}];
}];

Display the y value of a uiImageView during an animation

I have the following code:
NSString *yValue;
yValue=[[NSString alloc] initWithFormat:#"%1.2f" , imageView.center.y];
CGRect labelFrame = CGRectMake( 10, 100, 200, 50 );
UILabel* label5 = [[UILabel alloc] initWithFrame: labelFrame];
[label5 setBackgroundColor:[UIColor blackColor]];
[label5 setText: yValue];
[label5 setTextColor: [UIColor orangeColor]];
[self.view addSubview: label5];
This correctly shows the y value of the UIimageView but the problem is that it only shows the ending y value.
When a person flicks the UIImageView I animate it using:
[UIView animateWithDuration:2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{ recognizer.view.center = finalPoint; }
What I want is the label5 to display the y value of the imageView during the animation not just after. Is it possible to do this?

Resources