滚动 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。 请注意,当文本实际适合父级框架时,不会发生这种情况。
我几乎不知道为什么会发生这种情况;我显然可以猜测在设置文本后有些东西没有正确设置,但我无法弄清楚那是什么。 以更通用的形式询问,但当然没有结论性的答案。
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我解决了问题;显然,当文本设置后,动画停止并正确调用
layoutSubViews
,而停止动画将finished = NO
并因此再次调用layoutSubViews
重新启动动画。解决方案是检查动画是否正在进行,并且仅在当前没有动画运行时调用layoutSubviews
,否则依赖动画取消并调用layoutSubviews
。I solved the problem; apparently when the text got set, the animation stopped and
layoutSubViews
called properly, while the stopping animation would gofinished = NO
and thus calllayoutSubViews
again restarting the animation. Solution is to check if an animation is going, and only calllayoutSubviews
when no animation is currently running, else rely on the animation to cancel and that to calllayoutSubviews
.