UIView 框架未按时更新大小以在 UILabel 中创建滚动文本动画
我正在控制器的 cellForRowAtIndexPath 中执行通常的“将您自己的标签/图像添加到 cell.contentview”。我试图让其中一个标签具有动画滚动文本:也就是说,如果标签中的文本溢出 viewCell 宽度,则单元格中的标签应该滚动文本(动画 - 非手动)。此外,整个表格视图 + 滚动文本应该适用于 iPhone 和 iPad(因此所有内容都需要相应地自动调整大小)。
切入正题,问题是:
如何获取 cell.contentView 的子视图来及时更新其帧大小,以测试该子视图内的文本是否溢出。
或者,这是否是正确的解决方法?
这是我的意思的图像(第二标签等解释如下):
现在您可以继续阅读详细信息
:我尝试的方法是让 cellForRowAtIndexPath 将自定义 UISCrollLabelView 实例添加到单元格的 contentView 中。这个自定义类实际上是一个在内部管理 UILabel 的 UIView。
UISCrollLabelView 应该自动调整大小,使其不会溢出表格单元格(应该适用于 iPhone 和 iPad)。然后,根据 cellForRowAtIndexPath 传入的文本长度,它应该自动调整其内部标签的大小以适合所有文本,并且如果标签最终溢出视图(自身),则为 UILabel 的滚动设置动画。
我对此有几个问题,但主要的一个是:UIScrollableView 根据其内部标签的frame.size.width 与self.frame.size.width 的比较来触发(或不触发)动画。但显然,self 的框架宽度从 0 更新到插入 cell.contenView 后最终调整到的大小需要一些时间。这意味着当我的自定义 UIScrollLabelView 测试 (label.frame.size.width > self.frame.size.width)
时,这始终是 true,并且不溢出的文本仍然会滚动。大约一秒钟后,self.frame 确实更新为正确的大小。
这是 cellForRowAtIndexPath (secondLabel
是此处的滚动视图):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIImageSmartView *cachedImage;
UILabel *mainLabel;
UIScrollLabelView *secondLabel;
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
mainLabel = [[[UILabel alloc] initWithFrame:CGRectMake(70, 0, 0, 20)] autorelease];
mainLabel.font = [UIFont boldSystemFontOfSize:18];
mainLabel.textAlignment = UITextAlignmentLeft;
mainLabel.textColor = [UIColor blackColor];
mainLabel.backgroundColor = [UIColor clearColor];
mainLabel.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin);
mainLabel.tag = MAINLABEL_TAG;
secondLabel = [[[UIScrollLabelView alloc] initWithFrame:CGRectMake(70, 40, 0, 20)] autorelease];
secondLabel.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin);
//secondLabel.backgroundColor = [UIColor clearColor];
secondLabel.tag = SECONDLABEL_TAG;
cachedImage = [[UIImageSmartView alloc] initWithFrame:CGRectMake(5, 5, 57, 80)];
cachedImage.tag = PHOTO_TAG;
[cell.contentView addSubview:cachedImage];
[cell.contentView addSubview:mainLabel];
[cell.contentView addSubview:secondLabel];
}
else
{
cachedImage = (UIImageSmartView*)[cell.contentView viewWithTag:PHOTO_TAG];
mainLabel = (UILabel*)[cell.contentView viewWithTag:MAINLABEL_TAG];
secondLabel = (UIScrollLabelView*)[cell.contentView viewWithTag:SECONDLABEL_TAG];
}
// Configure the cell...
NSString *ImageName = [[dbData objectAtIndex:indexPath.row] objectAtIndex:2];
NSString *imageURL = [NSString stringWithFormat:@"http://someserver", referencingTable];
[cachedImage loadAndCacheImageFromFile:ImageName fromURL:imageURL inSize:CGSizeMake(57, 80) withBorderWidth:4];
mainLabel.text = [[dbData objectAtIndex:indexPath.row] objectAtIndex:0];
// -> At this point I load the text into the "scrolling label"
// (actually a UIView with a label)
[secondLabel setScrollingText:[[dbData objectAtIndex:indexPath.row] objectAtIndex:1]];
return cell;
}
到目前为止,我的 UIScrollLabelView
实现如下所示:
@implementation UIScrollLabelView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
isScrolling = NO;
self.clipsToBounds = YES;
UILabel *label = [[[UILabel alloc] init] autorelease];
label.font = [UIFont systemFontOfSize:14.0];
label.textAlignment = UITextAlignmentLeft;
label.textColor = [UIColor darkGrayColor];
label.backgroundColor = [UIColor greenColor];
self.backgroundColor = [UIColor redColor];
//label.backgroundColor = [UIColor clearColor];
[self addSubview: label];
}
return self;
}
- (void)setScrollingText:(NSString*)text
{
[self setNeedsLayout];
UILabel *label = [[self subviews ] objectAtIndex:0];
label.text = text;
[label sizeToFit];
if(label.frame.size.width > self.frame.size.width)
[self scrollText];
}
- (void)scrollText
{
if(isScrolling)
return;
isScrolling = YES;
UILabel *label = [[self subviews ] objectAtIndex:0];
[UIView beginAnimations:@"scroll" context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDidStopSelector:@selector(scrollDidComplete)];
[UIView setAnimationDuration: label.frame.size.width/(float)100];
CGRect frame = label.frame;
if(frame.origin.x == 0)
frame.origin.x = frame.origin.x - (frame.size.width - self.frame.size.width);
else
frame.origin.x = 0;
label.frame = frame;
[UIView commitAnimations];
}
- (void)scrollDidComplete
{
isScrolling = NO;
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(scrollText) userInfo:nil repeats:NO];
}
@end
I'm doing the usual "add your own labels/images to cell.contentview" from within a controller's cellForRowAtIndexPath. And I'm trying to have one of the labels have animated scrolling text: that is, if the text in the label is overflows the viewCell width, the label in the cell should scroll the text (animated - not manual). Also, this whole tableview + scrolling text should work for both iPhone and iPad (so everything needs to autoresize accordingly).
Cutting to the chase, the question is:
How do I get a a subview of cell.contentView to update it's frame size on time for testing if the text inside this subview overflows or not.
Or perhaps, is this even the right way to go about it?
Here's an image of what I mean (secondLabel,etc are explained below):
Now you can keep reading for the details:
The way I tried going about it is having cellForRowAtIndexPath add a custom UISCrollLabelView instance to a cell's contentView. Where this custom class is really a UIView that manages internally a UILabel.
UISCrollLabelView should autoresize to it doesn't overflow the table cell (should work for both iPhone and iPad). And then, depending on the length of the text passed in to it by cellForRowAtIndexPath, it should autoresize its internal label to fit all the text and if the label ends up overflowing the view (self), animate the scrolling of the UILabel.
I have a couple problems with this, but the main one being: UIScrollableView triggers (or not) the animation based on comparing its internal label's frame.size.width with self.frame.size.width. But apparently, it takes some time for self's frame width to update from 0 to the size it ends up resizing to after inserted in the cell.contenView. Which means that when my custom UIScrollLabelView tests (label.frame.size.width > self.frame.size.width)
this is always true and text that does not overflow still scrolls. A second or so later, self.frame does update to the correct size.
Here's cellForRowAtIndexPath (secondLabel
is the scrollView here):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIImageSmartView *cachedImage;
UILabel *mainLabel;
UIScrollLabelView *secondLabel;
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
mainLabel = [[[UILabel alloc] initWithFrame:CGRectMake(70, 0, 0, 20)] autorelease];
mainLabel.font = [UIFont boldSystemFontOfSize:18];
mainLabel.textAlignment = UITextAlignmentLeft;
mainLabel.textColor = [UIColor blackColor];
mainLabel.backgroundColor = [UIColor clearColor];
mainLabel.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin);
mainLabel.tag = MAINLABEL_TAG;
secondLabel = [[[UIScrollLabelView alloc] initWithFrame:CGRectMake(70, 40, 0, 20)] autorelease];
secondLabel.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin);
//secondLabel.backgroundColor = [UIColor clearColor];
secondLabel.tag = SECONDLABEL_TAG;
cachedImage = [[UIImageSmartView alloc] initWithFrame:CGRectMake(5, 5, 57, 80)];
cachedImage.tag = PHOTO_TAG;
[cell.contentView addSubview:cachedImage];
[cell.contentView addSubview:mainLabel];
[cell.contentView addSubview:secondLabel];
}
else
{
cachedImage = (UIImageSmartView*)[cell.contentView viewWithTag:PHOTO_TAG];
mainLabel = (UILabel*)[cell.contentView viewWithTag:MAINLABEL_TAG];
secondLabel = (UIScrollLabelView*)[cell.contentView viewWithTag:SECONDLABEL_TAG];
}
// Configure the cell...
NSString *ImageName = [[dbData objectAtIndex:indexPath.row] objectAtIndex:2];
NSString *imageURL = [NSString stringWithFormat:@"http://someserver", referencingTable];
[cachedImage loadAndCacheImageFromFile:ImageName fromURL:imageURL inSize:CGSizeMake(57, 80) withBorderWidth:4];
mainLabel.text = [[dbData objectAtIndex:indexPath.row] objectAtIndex:0];
// -> At this point I load the text into the "scrolling label"
// (actually a UIView with a label)
[secondLabel setScrollingText:[[dbData objectAtIndex:indexPath.row] objectAtIndex:1]];
return cell;
}
And my UIScrollLabelView
implementation looks like this so far:
@implementation UIScrollLabelView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
isScrolling = NO;
self.clipsToBounds = YES;
UILabel *label = [[[UILabel alloc] init] autorelease];
label.font = [UIFont systemFontOfSize:14.0];
label.textAlignment = UITextAlignmentLeft;
label.textColor = [UIColor darkGrayColor];
label.backgroundColor = [UIColor greenColor];
self.backgroundColor = [UIColor redColor];
//label.backgroundColor = [UIColor clearColor];
[self addSubview: label];
}
return self;
}
- (void)setScrollingText:(NSString*)text
{
[self setNeedsLayout];
UILabel *label = [[self subviews ] objectAtIndex:0];
label.text = text;
[label sizeToFit];
if(label.frame.size.width > self.frame.size.width)
[self scrollText];
}
- (void)scrollText
{
if(isScrolling)
return;
isScrolling = YES;
UILabel *label = [[self subviews ] objectAtIndex:0];
[UIView beginAnimations:@"scroll" context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDidStopSelector:@selector(scrollDidComplete)];
[UIView setAnimationDuration: label.frame.size.width/(float)100];
CGRect frame = label.frame;
if(frame.origin.x == 0)
frame.origin.x = frame.origin.x - (frame.size.width - self.frame.size.width);
else
frame.origin.x = 0;
label.frame = frame;
[UIView commitAnimations];
}
- (void)scrollDidComplete
{
isScrolling = NO;
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(scrollText) userInfo:nil repeats:NO];
}
@end
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
当单元格的大小尚未定义时,您正在设置标签大小。然后你就再也不用检查实际尺寸了。
您需要重写 setFrame 和 setBounds 来捕获帧大小的变化。
You are setting the label size when the size of the cell is not defined, yet. And then you never check for the actual size again.
You need to override setFrame and setBounds to catch frame size changes.