iPad - iOS 5.1 - Xcode 4.3.2
When the keyboard pop ups I animate the y origin of my UIImageView up by "x"
When the keyboard goes down, i animate it back down by the same "x"
So the keyboard pops up, the image goes up by X, the keyboard comes down, the image goes down by x, but it doesn't end up in the same place!. You go up 60, down 60, and you're not in the same place! it's further down, as if the coordinate system changed between the appearance of the keyboard and the disappearance of it. It absolutely drives me crazy. I can't understand why this happens.
//add gesture recognizer when keyboard appears
-(void)keyboardWillShow:(NSNotification *) note {
[self.view addGestureRecognizer:tapRecognizer];
// Setup the animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3f];
[UIView setAnimationCurve:UIViewAnimationOptionCurveEaseInOut];
[UIView setAnimationBeginsFromCurrentState:YES];
// The transform matrix: Logo Image
CGAffineTransform moveLogo = CGAffineTransformMakeTranslation(0, -60);
self.logoImageView.transform = moveLogo;
// Commit the changes
[UIView commitAnimations];
}
//add gesture recognizer when keyboard appears
-(void)keyboardWillHide:(NSNotification *) note {
[self.view removeGestureRecognizer:tapRecognizer];
// Setup the animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3f];
[UIView setAnimationCurve:UIViewAnimationOptionCurveEaseInOut];
[UIView setAnimationBeginsFromCurrentState:YES];
// The transform matrix: Logo Image
CGAffineTransform moveLogo = CGAffineTransformMakeTranslation(0, 60);
self.logoImageView.transform = moveLogo;
// Commit the changes
[UIView commitAnimations];
}
That's because you are not translating it, when you use CGAffineTransformMakeTranslation you are saying Give me these coordinates not give me this offset. In other words, make translation gives you the translation from the identity matrix. The function you want is CGAffineTransformTranslate. This will apply the translation to the current transform (passed as the first argument).
the transform property is not cumulative. You are not doing what you think you're doing. You are calculating the new transform from your identity, not from the current transform.
Instead of:
CGAffineTransform moveLogo = CGAffineTransformMakeTranslation(0, 60);
self.logoImageView.transform = moveLogo;
Try:
self.logoImageView.transform = CGAffineTransformIdentity;
Instead of messing around with Core Graphics you might want to animate simple view properties, like frame or size. For example:
[self.logoImageView.center = CGPointMake(self.logoImageView.center.x,
self.logoImageView.center.y + 60);
Do this between beginAnimations and 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 a method to perform a small shake animation. This animation works somewhat, but it rotates every time I call it from landscape position. When the animation is called while the simulator is in landscape, the entire view rotates itself to portrait, and then performs the animation. The rotation itself is not animated, it just changes suddenly and with no delay. Everything in the view shifts as well, all buttons, text fields, image views, etc.
Animation Code:
- (void)shakeView
{
CGFloat t = 8.0;
CGAffineTransform translateRight = CGAffineTransformTranslate(CGAffineTransformIdentity, 0.0, t);
CGAffineTransform translateLeft = CGAffineTransformTranslate(CGAffineTransformIdentity, 0.0, -t);
self.view.transform = translateLeft;
[UIView animateWithDuration:0.07 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{[UIView setAnimationRepeatCount:3.0];
self.view.transform = translateRight;
}
completion:^(BOOL finished){
if (finished)
{
[UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{self.view.transform = CGAffineTransformIdentity;
}
completion:NULL];
}
}];
}
Honestly, I don't do much with animations, so I have no idea what I am doing wrong here, much less what else I should try or where I should look for an answer.
I am looking to keep the animation while also keeping the orientation.
The issue is that the view has a non identity transform on it already. In order for a view to display properly in landscape, the system applies a transform on it that rotates the view. The transform of the view is CGAffineTransformIdentity ONLY when the device is in UIDeviceOrientationPortrait. This orientation rotation ONLY applies to the root child of the application, the first subview of the window. When you are initializing your translation animation with:
CGAffineTransform translateRight = CGAffineTransformTranslate(CGAffineTransformIdentity, 0.0, t);
You are telling the rotated view to ignore its rotation and snap back to its orientation in portrait mode. You can solve this issue by doing one of two things:
Make the animated view a subview of the root view. This has the advantage that the system will still handle rotations for free, and there isn't any chance that your animation will screw up the the orientation changes. Furthermore, the code will be simpler and easier to maintain in the end.
Store the initial transformation of the view, and apply all transformations to it.
I'll try to do #2 here.
- (void)shakeView
{
CGFloat t = 8.0;
//store the initial transform and use it to create the animated transforms
CGAffineTransform initialTransform = self.view.transform;
CGAffineTransform translateRight = CGAffineTransformTranslate(initialTransform, 0.0, t);
CGAffineTransform translateLeft = CGAffineTransformTranslate(initialTransform, 0.0, -t);
self.view.transform = translateLeft;
[UIView animateWithDuration:0.07
delay:0.0
options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat
animations:^{
[UIView setAnimationRepeatCount:3.0];
self.view.transform = translateRight;
}
completion:^(BOOL finished){
if (finished)
{
[UIView animateWithDuration:0.05
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
//restore the initial transform
self.view.transform = initialTransform;
}
completion:NULL];
}
}];
}
Note - As of iOS 8, the root view of the UIWindow is no longer transformed on rotation. Instead the bounds of the UIWindow itself changes. For another question about this, see here: UIWindow frame in ios 8 different from ios 7 in Landscape
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 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];