I'm having trouble understanding what is going wrong if you can help me it would be great!
I have a UIlabel being animated by UIView AnimationWithDuration like a marquee... (I need to make this code work, I can't fork something elsewhere please don't provide marquee reps.)
During this animation I set a new text, unfortunately the former one is not replace but drawn over. I end up with two text drawn over each other. And if I try to set the text again it's possible that it get cleaned or it's possible that it get a third line drawn over.
I've tried the following code thinking it's probably a thread issue but it doesn't do anything
#synchronized (self.songLabel.text) {
self.songLabel.text = nil;
self.songLabel.text = [trackItems objectForKey:#"TrackName"];
}
I've set my songLabel getter and setters as atomic, it does not help either.
I don't know what to try... thank you for your help!
EDIT: Here is the code that take care of the animation
- (void)animateSongLabel
{
if (!self.player.rate) {
[UIView animateWithDuration:.5 animations:^{
self.songLabel.left = 25.f;
}];
return;
}
if (!self.canAnimateLabel) {
return;
}
self.animateLabel = NO;
[UIView animateWithDuration:.25 delay:0. options:UIViewAnimationOptionCurveLinear animations:^{
self.songLabel.left -= 15.f;
} completion:^(BOOL finished) {
self.animateLabel = YES;
if (self.songLabel.right < -10.f) {
self.songLabel.left = 320.f;
}
[self animateSongLabel];
}];
}
Related
I know there's lots of 3rd party solutions out there, but I've decided to build one myself to be in full control of the animations and to learn something new.
It seems like most of the time it is made through subclassing UIControl and tracking touches, but I've used a different approach. I create system buttons with images. This way I get a nice highlight animation on a press for free. Anyway, it works quite nice until you press on it really fast. Here's a gif of what's happening.
Sometimes, if you do it really fast a full star gets stuck. I'll try to explain what's happening behind this animation. Since full and empty stars have different color, I change tint color of the button each time i becomes full or empty. The animation you see is a separate UIImageView that is added on top of the button right before the beginning of the animation block and is removed in the completion block. I believe sometimes on emptying the star animation is not fired and image is not removed in the completion block. But I can't catch this bug in code.
I added some debugging code to see if the block that makes a star empty is even fired. Seems like it is, and even completion block is called.
2015-04-23 13:00:00.416 RERatingControl[24011:787202]
Touch number: 5
2015-04-23 13:00:00.416 RERatingControl[24011:787202]
removing a star at index: 4
2015-04-23 13:00:00.693 RERatingControl[24011:787202] star removed
Star indexes are zero-based, so the 5th star is supposed to be removed, but it's not.
Full project is on github. It's pretty small. I would appreciate if someone would help me fix this. Oh, and feel free to use this code if you want to :-)
UPDATE. I was suggested to post some logic code here. I didn't do it right away because I felt it would be too cumbersome.
Button action method:
- (void)buttonPressed:(id)sender {
static int touchNumber = 0;
NSLog(#"\nTouch number: %d", touchNumber);
touchNumber++;
if (self.isUpdating) {
return;
}
self.updating = YES;
NSInteger index = [self.stars indexOfObject:sender];
__block NSTimeInterval delay = 0;
[self.stars enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
REStarButton *btn = (REStarButton*)obj;
if (idx <= index) {
if (!btn.isFull) {
NSLog(#"\nadding a star at index: %ld", (long)idx);
[btn makeFullWithDelay:delay];
delay += animationDelay;
}
} else {
if (btn.isFull) {
NSLog(#"\nremoving a star at index: %ld", (long)idx);
[btn makeEmptyWithDelay:0];
}
}
}];
if ([self.delegate respondsToSelector:#selector(ratingDidChangeTo:)]) {
[self.delegate ratingDidChangeTo:index + 1];
}
self.updating = NO;
}
Empty a full star method:
- (void)makeEmptyWithDelay:(NSTimeInterval)delay {
// if (!self.isFull) {
// return;
// }
self.full = NO;
UIImageView *fullStar = [[UIImageView alloc] initWithImage:self.starImageFull];
[self addSubview:fullStar];
CGFloat xOffset = CGRectGetMidX(self.bounds) - CGRectGetMidX(fullStar.bounds);
CGFloat yOffset = CGRectGetMidY(self.bounds) - CGRectGetMidY(fullStar.bounds);
fullStar.frame = CGRectOffset(fullStar.frame, xOffset, yOffset);
[self setImage:self.starImageEmpty forState:UIControlStateNormal];
[self setTintColor:self.superview.tintColor];
[UIView animateWithDuration:animationDuration delay:delay options:UIViewAnimationOptionCurveEaseOut animations:^{
//fullStar.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
fullStar.transform = CGAffineTransformMakeTranslation(0, 10);
fullStar.alpha = 0;
} completion:^(BOOL finished) {
[fullStar removeFromSuperview];
NSLog(#"star removed");
}];
Make an empty star full method:
}
- (void)makeFullWithDelay:(NSTimeInterval)delay {
// if (self.isFull) {
// return;
// }
self.full = YES;
UIImageView *fullStar = [[UIImageView alloc] initWithImage:self.starImageFull];
CGFloat xOffset = CGRectGetMidX(self.bounds) - CGRectGetMidX(fullStar.bounds);
CGFloat yOffset = CGRectGetMidY(self.bounds) - CGRectGetMidY(fullStar.bounds);
fullStar.frame = CGRectOffset(fullStar.frame, xOffset, yOffset);
fullStar.transform = CGAffineTransformMakeScale(0.01f, 0.01f);
[self addSubview:fullStar];
[UIView animateKeyframesWithDuration:animationDuration delay:delay options:0 animations:^{
CGFloat ratio = 0.35;
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:ratio animations:^{
fullStar.transform = CGAffineTransformMakeScale(animationScaleRatio, animationScaleRatio);
}];
[UIView addKeyframeWithRelativeStartTime:ratio relativeDuration:1 - ratio animations:^{
fullStar.transform = CGAffineTransformIdentity;
}];
} completion:^(BOOL finished) {
[self setImage:self.starImageFull forState:UIControlStateNormal];
[self setTintColor:self.fullTintColor];
[fullStar removeFromSuperview];
}];
}
Ah, I've managed to find a reason behind my bug and it works perfect now!
I used View Debugging to see if it's an UIImageView from animation is stuck on the button or the image of button itself is not set properly. I was surprised to see it's the button image that causing the issue. And then I looked through code again and realised I was changing the button image in the completion block after delay with no regard to if it's state has already changed. A simple fix to makeFullWithDelay: solved my issue.
} completion:^(BOOL finished) {
if (self.isFull) {
[self setImage:self.starImageFull forState:UIControlStateNormal];
[self setTintColor:self.fullTintColor];
}
[fullStar removeFromSuperview];
}];
I used whole evening trying to find the answer yesterday, and after I posted the question answer came by itself. Telling people about your problem really helps to go through your logic with more attention I guess. Thanks to everyone :-)
Should I delete the post or it might help someone in the future?
I have an object that falls from the top of the screen however i want it to come back up after the bottom of the object reaches the bottom of the screen. This is my code at the moment. I tried adding another image view to trigger the up motion by collision. The timer is in ViewDidLoad
hammer.center = CGPointMake(hammer.center.x, hammer.center.y + 12);
if (CGRectIntersectsRect(hammer.frame, floor.frame)) {
hammer.center = CGPointMake(hammer.center.x , hammer.center.y -50);
}
you wont see it animate, maybe try animating the view instead of moving it instantly.
Place this in viewDidAppear to see it after the view has come on the screen.
[UIView animateWithDuration:0.3f
animations:^{
hammer.center = CGPointMake(hammer.center.x, hammer.center.y + 12);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3f
animations:^{
if (CGRectIntersectsRect(hammer.frame, floor.frame)) {
hammer.center = CGPointMake(hammer.center.x , hammer.center.y -50);
}
}];
}];
Whilst playing around with UIView animation, I came across a situation where I think some refactoring is needed:
The following views whose opacity are initially set to 0.0f.
Ex:
[UIView animateWithDuration:1.0f
animations:^
{
firstView.layer.opacity = 1.0f;
}
completion:^(BOOL finished)
{
[UIView animateWithDuration:1.0f
animations:^
{
secondView.layer.opacity = 1.0f;
firstView.layer.opacity = 0.0f;
}
completion:^(BOOL finished)
{
[UIView animateWithDuration:1.0f
animations:^
{
thirdView.layer.opacity = 1.0f;
secondView.layer.opacity = 0.0f;
}
completion:^(BOOL finished)
{
thirdView.layer.opacity = 0.0f;
}];
}];
}];
All 3 views are just subclass of UIView's, which are added as subviews of the main view.
This simply animates the opacity of the first view to 1.0f and then that of the second view, and then that of the third view.
Simple. Nothing special here.
My Question is:
What if I had more views, let say 100, that I wanted to perform the same action (same sequence of animation), this block of code would expand and expand.
So for the sake of refactoring and being adhered to good practice of writing code, I thought may be this could be done with less code via the use of a method and perhaps a loop.
Could you enlighten me on this in regards to refactoring; in addition, would dispatch_apply be useful here along with the refactoring process if a loop is needed?
If you wanted to animate 100 images, you would probably want to use 2 views and load alternating images into each one. I recently created a sample app on github that does exactly that:
Animating UIImages with cross-fade opacity changing
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to use CGAffineTransformMakeRotation?
I have an imageview that acts like a button, I am looking for a way to show the user that he clicked on it, I came up with this code that will rotate it a bit :
float angle = 60.0;
[UIView animateWithDuration:0.5f animations:^{
customimageView.transform = CGAffineTransformMakeRotation(angle);
}];
but its not doing the behavior I am looking fpr.
Anyone know how can I make the imagekinda zoom in ( or enlarge/ blowup ) to maybe 1.5 its size on its click? what kind of animation is relevant here?
feel free to share behaviors you guys thought was cool in this situation.
Thanks.
As a side note, how can I make it just rotate full circle? I tried passing in 360 but its not doing it??
You can always CGAffineTransformConcat your rotation with a scaling transformation, made with CGAffineTransformMakeScale.
If you're interested in another button pressed effect, I needed something cool, too, and settled on a "throb" animation as follows:
BOOL keepThrobbing = NO;
-(void)buttonThrob:(CGFloat)inset {
UIViewAnimationOptions opts = UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionAllowUserInteraction;
[UIView animateWithDuration:0.2 delay:0.0 options:opts animations:^{
self.button.frame = CGRectInset(self.button.frame, inset, inset);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.2 delay:0.0 options:opts animations:^{
self.button.frame = CGRectInset(self.button.frame, -inset, -inset);
} completion:^(BOOL finished) {
if (finished && keepThrobbing) {
[self buttonThrob:inset];
}
}];
}];
}
Call it like this:
keepThrobbing = YES;
[self buttonThrob:10.0]; // expand/shrink by 10px in x and y
Use CGAffineTransformMakeScale to change the scale.
CustomimageView.transform = CGAffineTransformMakeScale(1.5,1.5);
You can combine two affine transforms with CGAffineTransformConcat:
[customimageView setTransform:CGAffineTransformConcat(CGAffineTransformMakeScale(1.5, 1.5), CGAffineTransformMakeRotation(angle))];
I have a requirement to animate images . I have a large number of images and this needs to be played as an video. In between playing sometimes i need to change some images as they will be updated at server. so playing should automatically update this new images .
I have tried using UIImageView. There we cannot control the animation.
I then tried CAKeyframeAnimation supplying image array to values property. I could play and pause the animation. But here also i cannot dynamically change the image while playing.
Can anyone help me in solving this problem.
Thanks
mia
The animation system on UIImageView is very limited. I would suggest that you make your own implementation with say 2 image view.
You would than change the image in one imageview and the fade in and out using UIView animateWithDuration
I have written a method for you. Please note: I have not tested it.
It assumes you have your frames in a array called 'frames' and have two UIIMageView placed on top of each other called 'imgv1' and 'imgv2'
-(void)performAnimationOfFrameAtIndex:(NSNumber*)indexNum
{
int index = [indexNum intValue];
if(index >= frames.count)
{
return;
}
UIImage *img = [frames objectAtIndex:index];
UIImageView *imgIn;
UIImageView *imgOut;
if(index % 2 == 0)
{
imgIn = imgv1;
imgOut = imgv2;
}
else {
imgIn = imgv2;
imgOut = imgv1;
}
imgIn.image = img;
[self.view sendSubviewToBack:imgIn];
[UIView animateWithDuration:0.1 animations:^{
imgIn.alpha = 1;
imgOut.alpha = 0;
} completion:^(BOOL finished) {
[self performSelectorOnMainThread:#selector(performAnimationOfFrameAtIndex:) withObject:[NSNumber numberWithInt:index+1] waitUntilDone:NO];
}];
}
Use two UIImageViews and swap them.
If you have a weak reference on the "animation" UIView, be sure to check if animation has finished in the completion block. Otherwise you may experience performance issues when you navigate to another view, recursive calls to animation method will continue!
- (void)animateImagesAtIndex:(NSNumber *)imageIdx {
// Do something here
[UIView animateWithDuration:1 animations:^{
// Do swap
}
completion:^(BOOL finished) {
if (finished) {
[self animateImagesAtIndex:imageIdx];
}
}];
}
UIImageView supports animations for a series of images. You only have to set the properties animationImages with an array of the images, and call the methods startAnimating and stopAnimating.