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

发布于 2024-12-08 19:21:36 字数 5000 浏览 0 评论 0原文

我正在构建某种滚动标签,它基本上是一个 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。 请注意,当文本实际适合父级框架时,不会发生这种情况。

我几乎不知道为什么会发生这种情况;我显然可以猜测在设置文本后有些东西没有正确设置,但我无法弄清楚那是什么。 以更通用的形式询问,但当然没有结论性的答案。

Im building some sort of a scroll label, which basically is a UIView containing a label that i animate, causing the child Label to go back and forth if the text is larger than the 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

Except when the animation is running (that is when the text takes up more space than the container view is wide), if you change the text to something else that also takes up more space than the container view is wide, somehow the thing is regressing into some state of flux in which all of the animations end with finish = NO (even though the label and parent view has correct size), which of course in turn causes layoutSubviews to be called, which in turn causes the animation to start again and end again with finish = NO.
Note that this doesn't happen when the text actually fits in the frame of the parent.

Im pretty much clueless to why it happens; i can obviously guess that something isn't properly set after setting the text, but i can't figure out what that something is. Asked in a more general form, but of course no conclusive answers.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

时光瘦了 2024-12-15 19:21:36

我解决了问题;显然,当文本设置后,动画停止并正确调用 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

I solved the problem; apparently when the text got set, the animation stopped and layoutSubViews called properly, while the stopping animation would go finished = NO and thus call layoutSubViews again restarting the animation. Solution is to check if an animation is going, and only call layoutSubviews when no animation is currently running, else rely on the animation to cancel and that to call 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文