滚动UILabel动画不会在特定内容更改后启动

时间:2011-10-10 13:59:45

标签: iphone ios animation uilabel

我正在构建某种滚动标签,它基本上是一个包含我动画的标签的UIView,如果文本大于UIView,会导致子标签来回走动;

@implementation ScrollLabel

static float DEFAULT_SPEED_IN_PIXELS_PER_SECOND = 30.0;
static float DEFAULT_ANIMATION_DELAY = 2.0;

#pragma mark - Properties

#pragma mark - Initialization and Memory Management

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    label1 = [[UILabel label] retain];
    [label1 setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
    [label1 setBackgroundColor:[UIColor clearColor]];
    [label1 setNumberOfLines:1];

    [self addSubview:label1];

    speedInPixelsPerSecond = DEFAULT_SPEED_IN_PIXELS_PER_SECOND;
    scrollAnimationDelay = DEFAULT_ANIMATION_DELAY;

    scrollType = theScrollType;
    //[self setBackgroundColor:[UIColor greenColor]];
        [self setClipsToBounds:YES];

    return self;
}

- (void)dealloc
{
    [label1 release];

    [super dealloc];
}

#pragma mark - Public Static Methods

#pragma mark - Public Instance Methods

// Implement this method if you need more precise control over the layout of your subviews than the autoresizing behaviors provide.
- (void)layoutSubviews
{   
    [super layoutSubviews];

    [self setLabelSize];

    [self checkToStartOrStopAnimating];
}

- (void)setText:(NSString *)text
{
    if( text == [label1 text] ) return;

    [self stopAnimating];   

    [label1 setText:text];

    [self setNeedsLayout];

    scrollAnimationDelay = DEFAULT_ANIMATION_DELAY;
}

- (void)checkToStartOrStopAnimating
{   
    if ([self shouldAnimate])
    {
        [self startAnimating];
    }
    else
    {
        [self stopAnimating];
    }
}

- (void)setLabelSize
{
    [label1 sizeToFit];
    [label1 setFrame:CGRectMake(0, 0, [label1 frame].size.width, [label1 frame].size.height)];

}

- (void)startAnimating
{
    if (!animating)
    {
        animating = YES;

        [self animateForwards];
    } 
}

- (void)stopAnimating
{   
    if(animating)
    {
        animating = NO;

        [[label1 layer] removeAllAnimations];
    }
}

- (BOOL)isAnimating
{
    return animating;
}

#pragma mark - Private Methods

- (void)animateForwards
{
    float distanceToTravel = [label1 frame].size.width - [self frame].size.width;

    if(distanceToTravel > 0 && animating )
    {
        CGRect rect = [label1 frame];

        [UIView animateWithDuration:distanceToTravel / speedInPixelsPerSecond 
                delay:scrollAnimationDelay 
                options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionCurveLinear
                animations:^(void) 
                {

                    [label1 setFrame:CGRectMake(rect.size.width - self.size.width, 0, rect.size.width, rect.size.height)];
                } 
                completion:^(BOOL finished) 
                {
                    scrollAnimationDelay = DEFAULT_ANIMATION_DELAY;

                    if(finished)
                    {
                        [self animateBackwards];                    
                    } 
                    else
                    {
                        [self stopAnimating];
                        [self setNeedsLayout];
                    }               
                }];
    }
    else
    {
        [self stopAnimating];
        [self setNeedsLayout];
    }
}

- (void)animateBackwards
{
    float distanceToTravel = [label1 frame].size.width - [self frame].size.width;

    if (distanceToTravel > 0 && animating) 
    {
        CGRect rect = [label1 frame];
        [UIView animateWithDuration:distanceToTravel / speedInPixelsPerSecond 
                delay:scrollAnimationDelay 
                options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionCurveLinear
                animations:^(void) 
                {
                    [label1 setFrame:CGRectMake(0, 0, rect.size.width, rect.size.height)];
                } 
                completion:^(BOOL finished) 
                {
                    if (finished)
                    {
                        [self animateForwards];
                    }
                    else
                    {
                        [self stopAnimating];
                        [self setNeedsLayout];
                    }
                }];
    }
    else 
    {
        [self stopAnimating];
        [self setNeedsLayout];
    }
}

- (BOOL) shouldAnimate
{ 
    return [label1 frame].size.width > [self frame].size.width && [self frame] > 0.0;
}

@end

除非动画正在运行时(即文本占用的空间超出容器视图的宽度),如果将文本更改为其他占用空间的容量超过容器视图的宽度,则不知何故事物正在回归到某种状态,其中所有动画以finish = NO结束(即使标签和父视图具有正确的大小),这当然反过来导致layoutSubviews被调用,这反过来导致动画再次开始并以finish = NO结束。 请注意,当文本实际适合父级的框架时,不会发生这种情况。

我对它为什么会发生几乎一无所知;我可以明显地猜到在设置文本后没有正确设置某些东西,但我无法弄清楚那是什么东西。 Asked in a more general form,但当然没有确凿的答案。

1 个答案:

答案 0 :(得分:0)

我解决了这个问题;显然,当文本设置完毕后,动画停止并且layoutSubViews正确调用,而停止动画将finished = NO,因此再次调用layoutSubViews重新启动动画。解决方案是检查动画是否正在进行,并且仅在当前没有动画运行时调用layoutSubviews,否则依赖动画取消并调用layoutSubviews

@implementation ScrollLabel

static float DEFAULT_SPEED_IN_PIXELS_PER_SECOND = 30.0;
static float DEFAULT_ANIMATION_DELAY = 2.0;

#pragma mark - Properties

#pragma mark - Initialization and Memory Management

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    label1 = [[UILabel label] retain];
    [label1 setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
    [label1 setBackgroundColor:[UIColor clearColor]];
    [label1 setNumberOfLines:1];

    [self addSubview:label1];

    speedInPixelsPerSecond = DEFAULT_SPEED_IN_PIXELS_PER_SECOND;
    scrollAnimationDelay = DEFAULT_ANIMATION_DELAY;

    scrollType = theScrollType;
    //[self setBackgroundColor:[UIColor greenColor]];
        [self setClipsToBounds:YES];

    return self;
}

- (void)dealloc
{
    [label1 release];

    [super dealloc];
}

#pragma mark - Public Static Methods

#pragma mark - Public Instance Methods

// Implement this method if you need more precise control over the layout of your subviews than the autoresizing behaviors provide.
- (void)layoutSubviews
{   
    [super layoutSubviews];

    [self setLabelSize];

    [self checkToStartOrStopAnimating];
}

- (void)setText:(NSString *)text
{
    if( text == [label1 text] ) return;

    [label1 setText:text];

    if(animating) 
{
    [self stopAnimating];
} 
else 
{
    [self setNeedsLayout];
}

    scrollAnimationDelay = DEFAULT_ANIMATION_DELAY;
}

- (void)checkToStartOrStopAnimating
{   
    if ([self shouldAnimate])
    {
        [self startAnimating];
    }
    else
    {
        [self stopAnimating];
    }
}

- (void)setLabelSize
{
    [label1 sizeToFit];
    [label1 setFrame:CGRectMake(0, 0, [label1 frame].size.width, [label1 frame].size.height)];

}

- (void)startAnimating
{
    if (!animating)
    {
        animating = YES;

        [self animateForwards];
    } 
}

- (void)stopAnimating
{   
    if(animating)
    {
        animating = NO;

        [[label1 layer] removeAllAnimations];
    }
}

- (BOOL)isAnimating
{
    return animating;
}

#pragma mark - Private Methods

- (void)animateForwards
{
    float distanceToTravel = [label1 frame].size.width - [self frame].size.width;

    if(distanceToTravel > 0 && animating )
    {
        CGRect rect = [label1 frame];

        [UIView animateWithDuration:distanceToTravel / speedInPixelsPerSecond 
                delay:scrollAnimationDelay 
                options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionCurveLinear
                animations:^(void) 
                {

                    [label1 setFrame:CGRectMake(rect.size.width - self.size.width, 0, rect.size.width, rect.size.height)];
                } 
                completion:^(BOOL finished) 
                {
                    scrollAnimationDelay = DEFAULT_ANIMATION_DELAY;

                    if(finished)
                    {
                        [self animateBackwards];                    
                    } 
                    else
                    {
                        [self stopAnimating];
                        [self setNeedsLayout];
                    }               
                }];
    }
    else
    {
        [self stopAnimating];
    }
}

- (void)animateBackwards
{
    float distanceToTravel = [label1 frame].size.width - [self frame].size.width;

    if (distanceToTravel > 0 && animating) 
    {
        CGRect rect = [label1 frame];
        [UIView animateWithDuration:distanceToTravel / speedInPixelsPerSecond 
                delay:scrollAnimationDelay 
                options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionCurveLinear
                animations:^(void) 
                {
                    [label1 setFrame:CGRectMake(0, 0, rect.size.width, rect.size.height)];
                } 
                completion:^(BOOL finished) 
                {
                    if (finished)
                    {
                        [self animateForwards];
                    }
                    else
                    {
                        [self stopAnimating];
                        [self setNeedsLayout];
                    }
                }];
    }
    else 
    {
        [self stopAnimating];
    }
}

- (BOOL) shouldAnimate
{ 
    return [label1 frame].size.width > [self frame].size.width && [self frame] > 0.0;
}

@end