Best way to animate a CGRect in Xcode? - ios

My app will use a Pan Gesture to allow you to pan gesture a container view within a view controller to the right to display a menu hidden behind the container view. (similar to how the YouTube app lets the user pan his/her finger to the right to bring up the main menu OR how the default lock screen lets you pan to the right to reveal the passcode keypad)
- (IBAction)testPan:(UIPanGestureRecognizer *)sender
{
CGPoint translation = [sender translationInView:self.view];
CGPoint location = [sender locationInView:self.view];
CGPoint velocity = [sender velocityInView:self.view];
_theView.frame = CGRectMake( translation.x, 50, 300, 300);
if (sender.state == UIGestureRecognizerStateEnded) {
NSLog(#"All fingers are lifted");
_theView.frame = CGRectMake( 10, 50, 300, 300);
}
}
So what I'd like to do right now is make sure that this line:
_theView.frame = CGRectMake( 10, 50, 300, 300);
After the if statement happens gradually rather than instantly as it does right now.
My plan initially was to use a loop that would take the value "translation.x" and gradually through milliseconds make it equal to 10 to give the pan gesture an animated looking effect.
But apparently Objective-C doesn't allow you to sleep() for milliseconds, I'm guessing they don't allow this for efficiency purposes.
So how could I make a float value go from x to 10 within 2 seconds rather than instantly?
Does CGRect already include something to do this for you automatically to transform a shape over time?

The exact mechanism to do what you want is built into the view system and very easy to use:
[UIView animateWithDuration:2.0 animations:^{
_theView.frame = CGRectMake(10, 50, 300, 300);
}];
You basically give UIView a duration (2 seconds) and a block inside of which you make direct changes to any animatable view properties (like frame), which will then happen over the course of the duration. (So rather than managing a timer yourself and updating your rect and then the frame, you can just tell the system to animate the frame directly.)

Related

UIView not changing position in animateWithDuration

I have two UIViews (My bad it is a UIView and a UIButton) which I am animating at the same time. I originally had a view and a containerView which would animate just fine and worked like a charm.
Now only one of my UIViews will move/animate in animateWithDuration even though through debugging the frame of the other view says that it is in a position it is not.
CGRect rect = self.toggle.frame;
CGRect tabRect = self.tabButton.frame;
rect.origin.x = rect.origin.x - rect.size.width;
NSLog(#"%f Before",tabRect.origin.x);
tabRect.origin.x = tabRect.origin.x - rect.size.width;
NSLog(#"%f After", tabRect.origin.x);
[UIView animateWithDuration:0.3 animations:^{ // animate the following:
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
}];
NSLog(#"%f AfterAnimation", tabButton.frame.origin.x);
The toggle view moves fine, but the tabButton view does not animate or move. The strange thing is that both the "After" and "AfterAnimation" debugging code returns the same value, which suggests the frame has indeed moved. Is there a specific reason that this will not work when toggle is a UIView when it would work as a UIContainerView?
Note that if I remove the line
self.toggle.frame = rect;
tabButton will animate correctly, but if I move toggle, tabButton will not move regardless of whether it is first in the animation block or second.
Edit: I have tried moving them into separate blocks and to change the center point rather than the frame, to no avail. It seems that if the toggle view moves, the tabButton will not move.
Edit 2: The pictorial evidence.{
In the following screenshots tabButton bg is green and toggle bg is red.
Above: Initial position (toggle is off-screen) correct position
Above: The problem in question toggle is correct tabButton is not
Above: When self.toggle.frame = rect is commented out (tabButton correct, toggle not)
}
Edit 3: It's even worse than I feared.{
I have done a few more tests and even if I take the toggle change out of the animation block to make it an instant thing, the tabButton will still not animate. This makes me think the tabButton may just fundamentally dislike the toggle view and/or myself so will not move just to spite me.
}
Edit 4:{
If I change the tabButton animation to tabButton.frame = CGRectMake(10,10,100,100) the View snaps instantly to that location and animates back to its original position in the same time as the animation duration.
}
I better add more bookkeeping/TLDR information in case things aren't clear.
toggle is an instance of ToggleDraw which is a subview of UIView which I created.
tabButton is a UIButton which is part of my IB viewController and a property of the class
Both toggle and tabButton are subviews of self.view
The animations will work individually with no modifications to the logic of the rects but will not work if they are animated at the same time
toggle animation seems to take precedence over tabButton animation regardless of the order
I had a problem with the animation of an UIView created in IB (the animation didn't start from the current position of the view, and ended in the initial position).
All worked fine after sending layoutIfNeeded() to the underlaying view before the animation block:
self.view.layoutIfNeeded()
UIView.animateWithDuration(0.5) { () -> Void in
...
I think it is not a problem about a UIView Animation. Maybe your toggle posiztion is related to your tabButton. For a try, your can set toggle frame to a rect lick (10, 10, 100,100), then check the result.
I've created an example of what you describe and everything seems to work fine. This is what I used:
UIView *toggle = [[UIView alloc] initWithFrame:CGRectMake(320, 64, 100, 100)];
[toggle setBackgroundColor:[UIColor redColor]];
[self.view addSubview:toggle];
UIButton *tabButton = [[UIButton alloc] initWithFrame:CGRectMake(220, 64, 100, 100)];
[tabButton setBackgroundColor:[UIColor greenColor]];
[self.view addSubview:tabButton];
CGRect rect = toggle.frame;
CGRect tabRect = tabButton.frame;
rect.origin.x = rect.origin.x - rect.size.width;
NSLog(#"%f Before",tabRect.origin.x);
tabRect.origin.x = tabRect.origin.x - rect.size.width;
NSLog(#"%f After", tabRect.origin.x);
[UIView animateWithDuration:1 animations:^{ // animate the following:
toggle.frame = rect; // move to new location
tabButton.frame = tabRect;
}];
What I can suggest is to make sure that the code is being ran on mainthread:
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:0.3 animations:^{
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
}];
});
Also take into account that the log you have after the animation code is incorrect as it won't run after the animation, but rather right next to asking for the animation.
If you want code to run after the animation you should use:
[UIView animateWithDuration:0.3 animations:^{
self.toggle.frame = rect; // move to new location
self.tabButton.frame = tabRect;
} completion:^(BOOL finished){
NSLog(#"Finished animating!");
}];
I have found a solution to the problem. If I initialise the UIButton (tabButton) programmatically rather than through the interface builder, both views will animate simultaneously.
This however seems very hacky to me, kind of like putting a bandaid over a missing foot and hoping it will sort itself out.
I could not work out the root cause of the problem but at least I could avoid the symptoms.
If anyone knows why the views would not animate when the button was made in the interface builder post an answer here, I am interested in knowing the reason behind this.
Thanks for your help everyone.

could not figure out uiview transitions used in yahoo weather app

I was trying to mimic the yahoo weather app screen transition between cities, I couldn't figure out what transition it is. Any clue?
I really appreciate you time.
Thanks
Edit:
I have slideView ctrler which has a subview. The sliderview has an image and the subview has text. When I make a swipe, the text view with text must be moving and dragging way the view ctrler with it at a slower rate and this intern should start dragging in the next view ctrler which is an instance of slider Ctrler.
There is no built-in transition that does this for you (I assume you're talking about the images that are transitioning their frame/center at a different rate than the view itself). You'd probably have to write it yourself. Some basic familiarity with gesture recognizers and view animation is needed.
The basic effect is by simultaneously adjusting the center property for two image views as you change the frame of those views (or their super views). (Or, you can achieve this by having image views whose contentMode is UIViewContentModeCenter and just changing the frame.) I'd suggest you start with some simple tests of the effect and build from there.
For example, I created a scene that has two image views, whose autolayout constraints were defined as follows:
H:|[leftImageView][rightImageView]|
V:|[leftImageView]|
V:|[rightImageView]|
I then defined a width constraint for the leftImageView, and hooked it up to an IBOutlet for that constraint, e.g. leftImageWidthConstraint. I then have a UIPanGestureRecognizer that could handle the gesture, simply changing this leftImageWidthConstraint accordingly (and with auto layout, the rest of the frame is calculated automatically for me from that):
- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
CGPoint translate = [gesture translationInView:gesture.view];
static CGFloat width;
if (gesture.state == UIGestureRecognizerStateBegan)
{
width = self.leftImageWidthConstraint.constant;
}
CGFloat newWidth = width + translate.x;
if (newWidth < 0)
newWidth = 0;
else if (newWidth > self.view.bounds.size.width)
newWidth = self.view.bounds.size.width;
self.leftImageWidthConstraint.constant = newWidth;
// if you let go, animate the views to their final position
if (gesture.state == UIGestureRecognizerStateEnded)
{
// if more than half way, set left view's target width to take up full width,
// otherwise set left view's target width to zero
if (newWidth > (self.view.bounds.size.width / 2.0))
newWidth = self.view.bounds.size.width;
else
newWidth = 0;
// animate the changing of the constraint (and thus the `frame`) accordingly
self.leftImageWidthConstraint.constant = newWidth;
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
}
Thus, as I pan across, the two images are centered within their clipped frames:
This is a very basic implementation of the idea. There are, though, a ton of implementation details (custom container vs subviews, autolayout vs not, etc.), so until you answer some of those questions, it's going to be hard to be more specific.
It is not default but i achieved similar to it in my old app
Register gesture on your view and on detection set isFromLeftSide accordingly
Call following. do fine tune this as per your requirements
[self.view addSubview:mySlidingView];
mySlidingView.frame = // set offscreen frame, in the direction you want it to appear from depending on flag isFromLeftSide
[UIView animateWithDuration:8.0
animations:^{
mySlidingView.frame = // desired end location
}];

Simulate UIScrollView Deceleration

I have an UIPanGestureRecognize which I use to change the frame of a view. Is there a way to simulate the deceleration of the UIScrollView or UITableView when the gesture's state is UIGestureRecognizerStateEnded? Here is my current code:
if (panGesture.state == UIGestureRecognizerStateEnded)
{
[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.view.frame = CGRectMake(182, 0, self.view.frame.size.width, self.view.frame.size.height);
}
completion:^(BOOL finished) {
if (finished) {
//Do something
}
}];
}
but this is not a smooth scroll. I would like something that decelerates until it stops to the point I've set. Thanks
Session 223 at WWDC 2012, "Enhancing User Experience With Scroll Views", covered a method to use a scrollview's behavior and "feel" to animate the position of a different view (without the scrollview ever actually being visible to the user).
The benefit of the method shown in the session is that your deceleration would always match UIScrollView's, now and forever.
https://developer.apple.com/videos/wwdc/2012/?id=223
You would have to come up with an algorithm of some sort to calculate where you want the view to stop at depending on the velocity of the gesture, which can be obtained like this:
CGPoint velocity = [panGesture velocityInView:panGesture.view];
From there it should just be a matter of animating your view into its calculated resting place and adding an animation to get it there. I believe UIViewAnimationOptionCurveEaseOut would be appropriate here.

IOS core animation for zoom in and zoom out

I am working a pictureView which needs to be zoom in and zoom out on tap,
I have searched for the proper zoom in and zoom out, but not found (I have tried to achieve same task with UIView but its not good), so i came to ask if any body can help ?
I have 4 images assembled in a tableview, 2 in a row.
What i want is if user taps an image, it just zoom in and if he taps again it should zoom out.
Following is the code sample i have tried so far
[UIView animateWithDuration:1.0f animations:^{
img.frame = CGRectMake(0, 0, 300, 300);
img.alpha = 1.0;
}];
but when ever i try to use that, instead of zooming in, it translates from one of the 4 diagonals :( I hope I have cleared this question as possible as I can.
Regards
You need to add tap gesture recognizer for each UIImageView you are adding to the cells.
You need to have hidden UIImageView to show when user taps the image.
Here is what you do on tap:
2.1 Assign image to your hidden UIImageView, set its alpha to 0.0 and make it not hidden
2.2 Set proper frame to the hidden image view.
2.3 animate the reveal of the image like
[UIView animateWithDuration:1.0f animations:^{
img.frame = CGRectMake(0, 0, 300, 300);
img.alpha = 1.0;
}];
2.4 add tap gesture recognizer to your hidden image view in order to dismiss it when user taps.
2.5 make reverse animation when user taps e.g. set alpha of displayed image view to 0.0 in uiview animation block.
I didn't provide the code for the whole thing assuming that you will learn and remember such things.
Good Luck!
EDIT: don't forget to add hidden uiimageview to your view.

Slow down UIView animation within Animation

i have simple UIView animation block which animates the origin of 2 views. I have a Button on a special position placed on a mapview view. so when i'd like to animate the center of the map and move the pin with the map, the map moves faster than the button. Is there a way, to speed up the animation of the button or to slow down the animation of the map? At the moment it looks like the map moves and the button jumps to his end position.
CGPoint newCenter = mapView.center;
newCenter.x -= 1;
newCenter.y -= (button.frame.size.height/2)
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationCurveEaseIn animations:^{
CGPoint screenPoint = fakePin.frame.origin;
screenPoint.x -= 5;
screenPoint.y += button.frame.size.height-4;
mapView.mapCoord = [mapView.map convertPoint:screenPoint toCoordinateFromView:self.view];
self.mapView.map.centerCoordinate = mapView.mapCoord;
[button setCenter:newCenter];
}];
Any Ideas?
Why not show the button as an annotation? Then it would move along the map.
Still, try to animate the property frame on the button instead of center. You'll need to do the calculations on your own but I think that may be the problem.
i just figured out, that the Problem was that my setCenter: was called without animating, somehow it worked with the Google Maps in ios5 (animating itself but not in ios6=
No it works!!

Resources