I have a custom UIView. It initializes two rectangular UIBezierPaths, and in drawRect it simply fills these two paths.
This view is placed on a button. When the button is clicked, a rotation is applied to it inside an animateWithDuration block:
icon.transform = CGAffineTransformMakeRotation(M_PI*-0.25);
This works great.
When the button is clicked again, I am trying to make the view rotate back to it's original orientation (reverse the rotation). I have tried:
icon.transform = CGAffineTransformInvert(icon.transform);
icon.transform = CGAffineTransformMakeRotation(M_PI*0.25);
icon.transform = CGAffineTransformIdentity;
... and a number of other variations, but none of them seem to work. Instead, the view is distorted until it's no longer visible -- sorta stretched to nothing. Sometimes, reapplying the first transform brings it back but with other reversals it doesn't.
What am I doing wrong?
In ios the transform is always relative, so once transformation is done, the next transformation should be relative to the previous transformation. You can simply change the frame of the view and revert it back to normal on second button click. On even clicks you will have to revert back the frame. Implement the following code. You can use the block level code if you feel comfortable with it as it is the new norm.
int noOfClicks = 0;
-(IBAction)yourBtnAction:(id)sender
{
noOfClicks++;
if(noOfClicks %2 != 0)
{
[UIView beginAnimations:#"Rotate" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelegate:self];
CGRect frame=yourView.frame;
frame.origin.y+=20;
yourview.frame=frame;
[UIView commitAnimations];
}
else
{
[UIView beginAnimations:#"Rotate" context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelegate:self];
CGRect frame=yourView.frame;
frame.origin.y-=20;
yourview.frame=frame;
[UIView commitAnimations];
}
}
If you don't want to make so many change you can use following to revert back but employing same logic:
[UIView beginAnimations:#"Rotate back" context:nil];
[UIView setAnimationDuration:1.0];
yourView.transform = CGAffineTransformIdentity;
[UIView commitAnimations];
Related
I want to animate a view to a specified origin when ever someone touched it and then comeback to its original position whenever user touched it again. I am using AutoLayout in my storyboard to align my views.
It first go upward with perfect position but does not come back to its original position perfectly.
I store the staring origin value in a CGFloat variable in my viewDidLoad method and then re assign that object to bring back the view.
Here is my code
Animation for upward (This is working perfect)
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
CGRect frame = _pickLocationView.frame;
frame.origin.y = _cityLabelView.frame.origin.y;
_pickLocationView.frame = frame;
[UIView commitAnimations];
Animation for Downward. (This method has some issue)
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.2];
CGRect frame = _pickLocationView.frame;
frame.origin.y = kDefaultOriginOfPickLocationView;
_pickLocationView.frame = frame;
[UIView commitAnimations];
A suggestion:
Use the method - [self setNeedsLayout] before the animation starts.
The frame is not guaranteed to have its final dimensions and position in viewDidLoad. Use viewDidLayoutSubviews to set your original values instead. Note that viewDidLayoutSubviews can be called multiple times if the bounds of your view changes.
In your case you could also just save the original value just before you start the first animation.
I had an IPhone application in which i want to transform my image view to its double size without changing the position of the image view.(i.e. the centre need not be changed)I am doing like this
CGAffineTransform firstTransform = bg.transform;
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.5];
bg.transform = CGAffineTransformScale(bg.transform,2,2);
[UIView setAnimationDelegate:self];
[UIView commitAnimations];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 0.5];
bg.transform = firstTransform;
[UIView setAnimationDelegate:self];
[UIView commitAnimations];
But when transforming its position gets changed.so
Now i am doing like this to do not change the position of the image view.i want the image view to be transformed to the double size on click with an animation like this
animation on the red button when clicking next
can anybody guide me on this?
Make sure the anchor point is set to the centre, bearing in mind when setting the anchor point the value has to be between 0 and 1: 0 being the left/top side and 1 being the right/bottom side. So to set the anchor point to the middle, set it to 0.5 on both the X and Y axis:
[itemName.layer setAnchorPoint:CGPointMake(0.5, 0.5)];
Then when you scale the object, it should scale up from around the centre point.
I also suggest using animateWithDuration:completion. The code will become much simpler:
[UIView animateWithDuration:1.0f animations:^{
[bg setTransform:CGAffineTransformScale(bg.transform, 2.0, 2.0)];
} completion ^{
[UIView animateWithDuration:1.0f animations:^{
[bg setTransform:CGAffineTransformScale(bg.transform, 0.5, 0.5)];
];
}];
Hope this helps you achieve the desired effect!
Make sure that you are not using auto layout. To disable that open your xib, click on first tab in the right panel, uncheck the Use Auto Layout under Interface Builder Document. Screen shot is attached for help.
I have an UIView animation to move an object on the screen and I want the animation to be stopped (and the object to keep the position when the animation is stopped) when the object goes off the screen.
I want my object to always be visible, and to stop moving when it meets an edge of the screen. The target point may be anywhere.
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
self.MyObject.center = targetPoint;
[UIView commitAnimations];
I tried to fire a scheduled timer which check if the object is off screen but it seems that the position my object does not change.
Thanks for your help.
Below is the code sample if you are looking to animate the object from left to right edge of the screen.
CGRect objectFrame = self.MyObject.frame;
objectFrame.origin.x = self.view.frame.size.width-objectFrame.size.width;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[self.MyObject setFrame:objectFrame];
[UIView commitAnimations];
Hope it helps.
Cheers.
I have a tabbed application for iPad. On the first tab I have a separate menu with a pointer that highlights the currently active menu button. The pointer (a UIImage) is animated into position after tapping the button.
The problem is when you leave the highlighted image on any button that's not the original/default button and move to another tab in the application, then back again, the image has moved back to the default location. However, when you tap another button the image moves in the wrong direction. I think it's because the image moves back but the old coordinates are retained, or the other way around? It's a bit hit and miss and behaves differently testing in the simulator compared to directly on the iPad.
I think I need to start with absolute coordinates and stick with them throughout the process. But I can't get my head around how to achieve it with the CGAffine functions.
Here is my current code
- (IBAction)diplayForm1:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 0);
[UIView commitAnimations];
}
- (IBAction)diplayForm2:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 80);
[UIView commitAnimations];
}
- (IBAction)diplayForm3:(id)sender {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
buttonHighlightImage.transform = CGAffineTransformMakeTranslation(0, 160);
[UIView commitAnimations];
}
EDIT - - -
#Mundi I tried the code you suggested. I'm assuming by newFrame you mean define a new UIImageView and property which I did in the .h (I called it 'sfh'), then #synthesize in the .m.
This is my code in the .m
-(void)viewWillAppear:(BOOL)animated {
// The "pointer" and the "old" frame are the same so I thought it pointless to do
// selectedFormHighlight.frame = selectedFormHighlight.frame;
// I tried anyway and also tried this and many other combinations
selectedFormHighlight.frame = sfh.frame;
}
-(void)viewDidAppear:(BOOL)animated {
[UIView beginAnimations:nil context:NULL];
[UIView animateWithDuration:0.3 animations:^{
selectedFormHighlight.frame = CGRectMake(0,0,0,0);
}];
[UIView commitAnimations];
}
When I run this the pointer animates off the screen (top-left) and doesn't come back. I have tried every possible combination I can think of in viewWillAppear and viewDidAppear but it makes no difference. I tried removing the other lines in viewDidAppear and it still does the same thing.
I think you're suggesting to animate the pointer from the original (default) position when the user returns from another screen. I don't want that, it should simply remain in the same spot where they left it without animation. Or at least put it back there without them realizing it. The only animation is when they choose to tap a different button within that view.
The reason you are experiencing this is that the actual view object and its presentation layer get mixed up when the view is hidden and redisplayed. The solution is to change the view properties rather than animate the layers.
Thus, you do not need to use affine transforms. Just set the new position of the pointer view directly by changing its frame or center.
The more up-to-date format to do this is with block based animations:
[UIView animateWithDuration:0.5 animations:^{
pointer.frame = newFrame;
}];
Also, it seems very inefficient to have three methods just differing by one variable. You could just multiply the desired y position by 80.
Edit:
Another reason your button resets is because that is where it was put when the view is instantiated. Just set the position of your view in viewWillAppear of your view controller and then animate it to the new location in viewDidAppear.
I appreciate the help from #Mundi but couldn't get it to work that way. I eventually arrived at the following code.
// This initially sets the frame with a variable value that
// corresponds to the current child view
-(void)viewDidAppear:(BOOL)animated {
selectedFormHighlight.frame = CGRectMake(0, self.getY, 250, 4100);
}
// Button actions (converted to a single action instead of one for each button)
// This was done by referencing a tag added to each button (the button tags
// match the children's tags)
-(IBAction)goToForm:(id)sender {
UIButton *btn = (UIButton *)sender;
self.currentChild = self.formViewControllers[btn.tag];
[UIView beginAnimations.nil context:NULL];
[UIView animateWithDuration:0.3 animations:^{
selectedFormHighlight.frame = CGRectMake(0, self.getY, 250, 4100);
}];
[UIView commitAnimations];
sfh.frame = selectedFormHighlight.frame;
}
// This returns the 'y' (vertical center position) of the pointer
// frame for each of the child views
-(int)getY {
switch(_currentChild.view.tag) {
case 1:
return -2005;
break;
case 2:
return -1925;
break;
default:
return -1845;
break;
}
}
I have been trying to build an entry effect for a logo to come from the top of the screen to the bottom and remain there when a new view loads in my application. I have seen all of the tutorials that use NSTimer to bounce an image but once my logo hits the bottom it needs to exit. I'm going to read up on animation block codes to see if my solution resides there.
Apologies I'm a new be and am very grateful for the assistance.
Set logo frame to top and then:
[UIView beginAnimations: #"moveLogo" context: nil];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:1.0];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
logoToMove.frame = CGRectMake( final frame at the bottom );
[UIView commitAnimations];
logoToMove is your logo, give it an outlet and hook it in xib.
So you will set the initial frame and in the animation - the final frame. The animation will do the rest of the job.
Change UIViewAnimationCurveLinear to a desired one if you don't like that. Also the duration to speed up or slow down the movement.
To remove the view at the end of your animation, the easiest way would be to use blocks :
logoToMove.frame = topRect;
[UIView animateWithDuration:duration
animations:^{
logoToMove.frame = bottomFrame;
}
completion:^(BOOL finished) {
[logoToMove removeFromSuperview];
}
];
Doing it like that gives you control over the animation and on what to do once it's finished in a single method