2011-10-10 247 views
0

林建设某种滚动标签,这基本上是一个包含我的动画标记一个UIView,造成孩子标签去来回如果文本比UIView的大;滚动的UILabel动画无法启动

@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,但当然没有确凿的答案。

回答

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