How to animate uislider thumb animation in iphone sdk?
Please check my code below.
Create Slider
slider = [[UISlider alloc] initWithFrame:CGRectMake(0.0, 300.0, 320.0, 3.0)];
[[UISlider appearance] setThumbImage:[UIImage imageNamed:#"chnavigate-1.png"] forState:UIControlStateNormal];
UIImage *clearImage = [[UIImage imageNamed:#"png1.png"] stretchableImageWithLeftCapWidth:14.0 topCapHeight:0.0];
[slider setMinimumTrackImage:clearImage forState:UIControlStateNormal];
[slider setMaximumTrackImage:clearImage forState:UIControlStateNormal];
[slider addTarget:self
action:#selector(sliderAction:)
forControlEvents:UIControlEventValueChanged];
slider.minimumValue=0.0;
slider.maximumValue=3.6;
[slider setValue:0.0];
//slider.value = 0.1f;
[slider setContinuous:YES];
//slider.thumbTintColor=[UIColor redColor];
[slider addTarget:self action:#selector(sliderAction:) forControlEvents:UIControlEventValueChanged];
Animation Code below:
[UIView animateWithDuration:0.3 delay:1.3 usingSpringWithDamping:0.1 initialSpringVelocity:1.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
//Animations
slider.frame = CGRectMake(slider.frame.origin.x,slider.frame.origin.y - 20,slider.frame.size.width,slider.frame.size.height);
}
completion:^(BOOL finished) {
//Completion Block
}];
This animation gives animates full silder. My requirement is animate onlu slider thumb image only.
Hemp me.
Thanks...
I did some poking around and wrote some test code, and my conclusion is that you can't animate the thumb image of UISlider, except for the one following situation: when the thumb is in the exact middle of the slider and not moving.
In that one situation you can make a UIImageView (since you can't animate UIImage) with your thumb image of choice (you have to make one, there's no way to get a pointer to the default thumb image as of iOS 7+), place it as a subview on your UISlider over the default thumb image, and animate that.
HOWEVER, as soon as the thumb is in a non-centered position, you butt your head against the fact that there is no way to get a UISlider's track size, and the track size is smaller than the UISlider's frame.size.width by some amount/factor/magic number that's hidden in Apple's implementation.
The two approaches I thought of are as follows:
Respond to UISlider UIControlEventValueChanged notifications and recalculate the position of the UIImageView overlaying UISlider's thumb image. However, if you base this calculation on the UISlider's frame.size.width, as soon as you move off the the centered position, there will start to be an offset between the default thumb and your UIImageView. The further you move off-center the greater this offset becomes, returning to 0 (perfectly lined up) when you move back to center.
Let the UIImageView respond independently to touch events. However, as before, since there is no way of getting UISlider's track size, while you could lock the UIImageView to an initial y value and let it move along the x, there is no way to know what the maximum or minimum x values would be.
In either case, you could trail and error your way to a value for the difference between the UISlider's frame width and the track's width that might work for a given situation in a given version of iOS, but there's no guarantee another situation or an update won't break that.
My solution, if this was essential, would be to create my own version of UISlider as a subclass of UIControl that allowed such things.
Related
I have a UIButton (size 100,100) with text and a colored background. When the button is pressed, I want the text to disappear immediately and the button to change size, animated, to 1000,1000. Here is the code I'm using:
[myButton setTitleColor:[UIColor colorWithRed:195/255.0f green:255/255.0f blue:180/255.0f alpha:0.0f] forState:UIControlStateNormal];
[UIView animateWithDuration:2.0f
delay:1.0f
options:UIViewAnimationCurveEaseInOut
animations:^{
[myButton setBounds:CGRectMake(0, 0, 1000, 1000)];
}
completion:^(BOOL finished){
SetupView * sv = [[SetupView alloc] initWithNibName:nil bundle:nil];
//[self presentViewController:sv animated:NO completion:nil];
}
];
Now, if I take out setTitleColor, the button expands just fine, but the text is still there (obviously). However, keeping setTitleColor in makes the button size 0,0 and then animates to 100,100. This also happens when I replace setTitleColor with setTitle:#"". I've also tried changing the text or color in the animation with the same result.
I feel like it's something obvious I've missed, but I can't seem to see it.
edit: If I don't run the animation and just do setTitleColor or setTitle:#"" by themselves, the text disappears as expected and the button stays the same size.
If you want to preserve the way you used UIKit to style your button, just modify the layer in the animation block, not the view.
button.layer.transform = CATransform3DMakeScale(10,10,1)
Maybe it is even sufficient to use the view's transform property.
button.transform = CGAffineTransformMakeScale(10,10)
NB 10 is just an arbitrary large value.
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.
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.
I have one UIbutton. I want it to move from point A to point B. While going, that button have to do one full 360' rotation.
After some event, I want that UIButton to come back to its position, this time rotating in the reverse direction.
I wrote this code, but it didnt work. I'm seeing the button rotate in unexpected ways. What am I missing here?
To move:
[UIView animateWithDuration:0.3 animations:^{
CGAffineTransform rotate360AntiClockWise = CGAffineTransformMakeRotation(M_PI_2);
CGRect initialFrame = button.frame;
[button setTransform:rotate360AntiClockWise];
[button setFrame:CGRectMake(initialFrame.origin.x - 140.0, initialFrame.origin.y, initialFrame.size.width, initialFrame.size.height)];
}];
To comeback to the same position
[UIView animateWithDuration:0.4 animations:^{
CGAffineTransform rotate360ClockWise = CGAffineTransformMakeRotation(-M_PI_2);
[button setFrame:originalFrame]; //for movement
[button setTransform:rotate360ClockWise];
}];
}
I can't try it at the moment, but if I remember correctly using the frame causes problems when applying transformations. Have you tried setting the center of the button?
[button setCenter:newCenter]
Where newCenter is a corresponding CGPoint.
The simplest way to do a rotation and a transform without messing up the Transformation matrix of the view is:
place the button inside of a view (moveView) with the same frame-size with origin 0|0
when you want to do a rotation, rotate the button
when you want to move the button, translate the moveView
Please do this in 3 steps
CGAffineTransform matOne = CGAffineTransformMakeTranslation(x_offset, y_offset);
CGAffineTransform matTwo = CGAffineTransformMakeRotation(M_PI_2);
CGAffineTransform matResult = CGAffineTransformConcat(matOne, matTwo);
// Finally
[button setTransform:matResult];
I hope this helps you. Please make sure Concat has matOne as the first parameter.
Also this is a possible duplicate for another question:
translation after rotation view using CGAffine
I try to set the background image in animation completion and basically it prevents the animation to happen.
This is the code for animation:
[UIView animateWithDuration: 0.5f animations: ^
{
//[buttonPanel setBackgroundImage: [UIImage imageNamed: imageName] forState: UIControlStateNormal];
CGFloat yOrigin = buttonFrame.origin.y - (self.bounds.size.height - buttonFrame.size.height);
yOrigin -= buttonFrame.size.height;
buttonPanel.frame = CGRectMake(buttonFrame.origin.x, yOrigin, buttonFrame.size.width, buttonFrame.size.height);
}
completion: ^(BOOL finished)
{
[UIView animateWithDuration: 0.5f animations:^
{
[buttonPanel setBackgroundImage: [UIImage imageNamed: imageName] forState: UIControlStateNormal];
[self setAlpha: 1.0f];
}];
}];
If I remove the setBackgroundImage: message it just animating to new frame position correctly. Why?
Thank you.
OK, new answer! Your comment gave me the clue I needed. You said:
Makes the button to return to initial frame for a reason. basically any operation, like calling a modal UIViewController from this UIViewController sets the button to initial Fram position
Right. Because showing a modal controller and then dismissing it causes your view to appear, and causes layout to happen. You must be using Auto Layout! Auto Layout would cause this to happen. So the problem here is that you don't understand how to change the position/size of a view (in animation or any other time) when using Auto Layout.
The rule is simple. He who lives by the constraint dies by the constraint. If you are using Auto Layout, you must not change the frame of anything. You must make all positional / size adjustments by changing a constraint.
The wonderful thing is that this is usually much easier and simpler than changing the frame! You don't have to fetch the frame, change it, and assign the frame; instead, you can usually just change a constraint's constant directly.
In this example, I animate the shrinkage of a UITextView so that the whole UITextView will be visible when the keyboard rises from the bottom of the screen (iPhone):
[UIView animateWithDuration:duration.floatValue
animations:
^{
self.bottomConstraint.constant = -r.size.height;
[self.view layoutIfNeeded];
} completion:nil];
That is how to animate a constraint: as you change the constant, you must also call layoutIfNeeded to get new layout at each instant of the animation.
So, I'm not saying that you are wrong not to use Auto Layout. It is "opt-in"; you can start using when you want to, you can use it in some nibs and not others, etc etc. But it is much more powerful than the old way of doing layout, and in many ways it leads to simpler code - once you know how to use it.
backgroundImage is not an animatable property so why are you setting it inside an animation block? Try setting it before all the animations. Just a guess, but worth trying.
EDIT: I just tried this:
[UIView animateWithDuration:0.4 animations:^{
CGRect r = self.otherButton.frame;
r.origin.y += 100;
self.otherButton.frame = r;
[self.otherButton setBackgroundImage:[UIImage imageNamed:#"car.jpg"]
forState:UIControlStateNormal];
}];
It worked fine. So I'm not able to reproduce your issue. Is there something else going on in your code that you aren't telling us about? In other words, try reducing it to a simpler case that works, and then build it back up and that way you'll discover the point at which things go wrong.
OK,
I have found this crazy issue, I had a UIViewController, Use Autolayout check and it was messing up all my animations.
Even if you have a UIViewController checked Use Autolayout it messes up all the controllers, maybe because I don't use UINavigationController, and
the segues are modal.
This was.