gpt4 book ai didi

ios - NSLayoutdString的AutoLayout行高计算错误

转载 作者:技术小花猫 更新时间:2023-10-29 10:25:51 29 4
gpt4 key购买 nike

我的应用程序从API中提取HTML,将其转换为NSAttributedString(以便允许可点击的链接),并将其写入AutoLayout表中的一行。麻烦的是,每当我调用这种类型的单元格时,高度的计算都会错误,并且内容会被截断。我尝试了行高计算的不同实现,但都无法正常工作。
我如何准确,动态地计算这些行之一的高度,同时仍保持点击HTML链接的功能?
Example of undesired behavior
我的代码如下。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
switch(indexPath.section) {
...
case kContent:
{
FlexibleTextViewTableViewCell* cell = (FlexibleTextViewTableViewCell*)[TableFactory getCellForIdentifier:@"content" cellClass:FlexibleTextViewTableViewCell.class forTable:tableView withStyle:UITableViewCellStyleDefault];

[self configureContentCellForIndexPath:cell atIndexPath:indexPath];
[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.desc.font = [UIFont fontWithName:[StringFactory defaultFontType] size:14.0f];

return cell;
}
...
default:
return nil;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UIFont *contentFont = [UIFont fontWithName:[StringFactory defaultFontType] size:14.0f];
switch(indexPath.section) {
...
case kContent:
return [self textViewHeightForAttributedText:[self convertHTMLtoAttributedString:myHTMLString] andFont:contentFont andWidth:self.tappableCell.width];
break;
...
default:
return 0.0f;
}
}
-(NSAttributedString*) convertHTMLtoAttributedString: (NSString *) html {
return [[NSAttributedString alloc] initWithData:[html dataUsingEncoding:NSUTF8StringEncoding]
options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
documentAttributes:nil
error:nil];
}
- (CGFloat)textViewHeightForAttributedText:(NSAttributedString*)text andFont:(UIFont *)font andWidth:(CGFloat)width {
NSMutableAttributedString *mutableText = [[NSMutableAttributedString alloc] initWithAttributedString:text];

[mutableText addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, text.length)];

UITextView *calculationView = [[UITextView alloc] init];
[calculationView setAttributedText:mutableText];

CGSize size = [self text:mutableText.string sizeWithFont:font constrainedToSize:CGSizeMake(width,FLT_MAX)];
CGSize sizeThatFits = [calculationView sizeThatFits:CGSizeMake(width, FLT_MAX)];

return sizeThatFits.height;
}

最佳答案

在我正在使用的应用程序中,该应用程序从其他人编写的糟糕的API中提取了可怕的HTML字符串,并将HTML字符串转换为NSAttributedString对象。我别无选择,只能使用这个糟糕的API。很伤心任何必须解析可怕的HTML字符串的人都知道我的痛苦。我使用Text Kit。方法如下:

  • 解析html字符串以获取DOM对象。我将libxml与一个轻包装器hpple一起使用。这种组合 super 快速且易于使用。强烈推荐。
  • 递归遍历DOM对象以构造NSAttributedString对象,使用自定义属性标记链接,使用NSTextAttachment标记图像。我称其为富文本。
  • 创建或重用主要的Text Kit对象。即NSLayoutManagerNSTextStorageNSTextContainer。分配后将它们连接起来。
  • 布局过程
  • 使用NSTextStorage
  • 将步骤2中构造的富文本传递到步骤3中的 [NSTextStorage setAttributedString:]对象。
  • 使用方法[NSLayoutManager ensureLayoutForTextContainer:]强制布局发生
  • 使用[NSLayoutManager usedRectForTextContainer:]方法计算绘制富文本格式所需的框架。如果需要,添加填充或边距。
  • 渲染过程
  • [tableView: heightForRowAtIndexPath:]
  • 中返回在步骤5中计算的高度
  • 在第2步中使用[NSLayoutManager drawGlyphsForGlyphRange:atPoint:]绘制富文本。我在这里使用屏幕外绘画技术,因此结果是一个UIImage对象。
  • 使用UIImageView渲染最终结果图像。或将结果图像对象传递给contentslayer对象的contentView属性的UITableViewCell属性的[tableView:cellForRowAtIndexPath:]属性。
  • 事件处理
  • 捕获触摸事件。我使用表格 View 附带的轻击手势识别器。
  • 获取触摸事件的位置。使用此位置来检查用户是否使用[NSLayoutManager glyphIndexForPoint:inTextContainer:fractionOfDistanceThroughGlyph][NSAttributedString attribute:atIndex:effectiveRange:]轻拍了链接或图像。

  • 事件处理代码段:
    CGPoint location = [tap locationInView:self.tableView];
    // tap is a tap gesture recognizer

    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
    if (!indexPath) {
    return;
    }

    CustomDataModel *post = [self getPostWithIndexPath:indexPath];
    // CustomDataModel is a subclass of NSObject class.

    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
    location = [tap locationInView:cell.contentView];
    // the rich text is drawn into a bitmap context and rendered with
    // cell.contentView.layer.contents

    // The `Text Kit` objects can be accessed with the model object.
    NSUInteger index = [post.layoutManager
    glyphIndexForPoint:location
    inTextContainer:post.textContainer
    fractionOfDistanceThroughGlyph:NULL];

    CustomLinkAttribute *link = [post.content.richText
    attribute:CustomLinkAttributeName
    atIndex:index
    effectiveRange:NULL];
    // CustomLinkAttributeName is a string constant defined in other file
    // CustomLinkAttribute is a subclass of NSObject class. The instance of
    // this class contains information of a link
    if (link) {
    // handle tap on link
    }

    // same technique can be used to handle tap on image

    当呈现相同的html字符串时,此方法比 [NSAttributedString initWithData:options:documentAttributes:error:]更快,更可自定义。即使没有概要分析,我也可以告诉 Text Kit方法更快。即使我必须自己解析html并构造属性字符串,它也非常快速且令人满意。 NSDocumentTypeDocumentAttribute方法太慢,因此是 Not Acceptable 。使用 Text Kit,我还可以创建复杂的布局,例如带有可变缩进的文本块,边框,任意深度的嵌套文本块等。但是它确实需要编写更多代码来构造 NSAttributedString并控制布局过程。我不知道如何计算使用 NSDocumentTypeDocumentAttribute创建的属性字符串的边界rect。我相信用 NSDocumentTypeDocumentAttribute创建的属性字符串是由 Web Kit而不是 Text Kit处理的。因此,这并不意味着可变高度表 View 单元格。

    编辑:
    如果必须使用 NSDocumentTypeDocumentAttribute,我认为您必须弄清楚布局过程是如何发生的。也许您可以设置一些断点,以查看哪个对象负责布局过程。然后,您可以查询该对象或使用另一种方法来模拟布局过程以获取布局信息。有些人使用即席单元格或 UITextView对象来计算高度,我认为这不是一个好的解决方案。因为以这种方式,应用程序必须布局相同的文本块至少两次。不管您是否知道,在应用程序中的某个位置,某些对象都必须对文本进行布局,以便您可以获取布局信息(例如边界矩形)。由于您提到了 NSAttributedString类,所以最好的解决方案是iOS 7之后的 Text Kit。如果您的应用程序针对较早的iOS版本,则为 Core Text

    我强烈建议使用 Text Kit,因为这样,对于从API中提取的每个html字符串,布局过程只会发生一次,并且 NSLayoutManager对象会缓存布局信息(例如边界rect和每个字形的位置)。只要保留 Text Kit对象,您就可以随时重用它们。当使用表格 View 呈现任意长度的文本时,这是非常有效的,因为文本仅布局一次,并且每次需要显示一个单元格时才绘制一次。我还建议使用 Text Kit而不使用 UITextView,如官方的Apple文档建议的那样。因为如果一个人想重用该 UITextView附带的 Text Kit对象,则必须缓存每个 UITextView。像我一样将 Text Kit对象附加到模型对象,并且仅当从API中提取新的html字符串时,才更新 NSTextStorage并强制 NSLayoutManager进行布局。如果表 View 的行数是固定的,则还可以使用固定位置的占位符模型对象列表,以避免重复分配和配置。并且因为 drawRect:导致 Core Animation创建了必须避免的无用的支持位图,所以请勿使用 UIViewdrawRect:。使用 CALayer绘制技术或将文本绘制到位图上下文中。我使用后一种方法,因为可以在后台线程中使用 GCD进行此操作,因此主线程可以自由响应用户的操作。我的应用程序中的结果确实令人满意,快速,排版很好,表格 View 的滚动非常流畅(60 fps),因为所有绘制过程都是在后台线程中使用 GCD完成的。每个应用程序都需要使用表 View 绘制一些文本,应使用 Text Kit

    关于ios - NSLayoutdString的AutoLayout行高计算错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27803453/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com