热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

对于NSAttributedString,AutoLayout行高度计算错误

如何解决《对于NSAttributedString,AutoLayout行高度计算错误》经验,为你挑选了1个好方法。

我的应用程序从API中提取HTML,将其转换为NSAttributedString(为了允许可点击的链接)并将其写入AutoLayout表中的一行.麻烦的是,每当我调用这种类型的单元格时,高度都会被错误计算并且内容会被切断.我尝试过不同的行高计算实现,但都没有正常工作.

如何准确,动态地计算其中一行的高度,同时仍保持点击HTML链接的能力?

不良行为的示例

我的代码如下.

- (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;
}

wcd.. 7

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

    解析html字符串以获取DOM对象.我使用带有光包装的libxml,hpple.这种组合非常快速且易于使用.强力推荐.

    递归遍历DOM对象以构造NSAttributedString对象,使用自定义属性标记链接,用于NSTextAttachment标记图像.我称之为富文本.

    创建或重用主Text Kit对象.即NSLayoutManager,NSTextStorage,NSTextContainer.分配后将它们连接起来.

    布局过程

      将步骤2中构造的富文本传递给步骤3中的NSTextStorage对象[NSTextStorage setAttributedString:]

      使用方法[NSLayoutManager ensureLayoutForTextContainer:]强制布局发生

    计算用方法绘制富文本所需的帧[NSLayoutManager usedRectForTextContainer:].如果需要,添加填充或边距.

    渲染过程

      返回步骤5中计算的高度 [tableView: heightForRowAtIndexPath:]

      用第2步绘制富文本[NSLayoutManager drawGlyphsForGlyphRange:atPoint:].我在这里使用离屏绘图技术,因此结果是一个UIImage对象.

      使用a UIImageView来渲染最终结果图像.或者,结果图像对象传递给contents财产layer的财产contentView的财产UITableViewCell在对象[tableView:cellForRowAtIndexPath:].

    事件处理

      捕捉触摸事件.我使用附有表视图的轻敲手势识别器.

      获取触摸事件的位置.在这个位置可检查,如果用户点击一个链接或图片[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

[NSAttributedString initWithData:options:documentAttributes:error:]渲染相同的html字符串相比,此方法更快,更可定制.即使没有剖析我也可以说Text Kit方法更快.即使我必须自己解析html并构造属性字符串,它也非常快速且令人满意.这种NSDocumentTypeDocumentAttribute方法太慢,因此是不可接受的.有了Text Kit,我还可以创建复杂的布局,如带有可变缩进,边框,任意深度嵌套文本块等的文本块.但它确实需要编写更多代码来构造NSAttributedString和控制布局过程.我不知道如何计算用它创建的属性字符串的边界矩形NSDocumentTypeDocumentAttribute.我相信创建的属性字符串NSDocumentTypeDocumentAttribute是由Web Kit而不是Text Kit.因此,不适用于可变高度表视图单元.

编辑: 如果你必须使用NSDocumentTypeDocumentAttribute,我认为你必须弄清楚布局过程是如何发生的.也许您可以设置一些断点来查看哪个对象负责布局过程.然后,也许您可​​以查询该对象或使用另一种方法来模拟布局过程以获取布局信息.有些人使用ad-hoc单元格或UITextView对象来计算高度,我认为这不是一个好的解决方案.因为这样,应用程序必须至少两次布局相同的文本块.无论你是否知道,在你的应用程序的某个地方,某些对象必须布置文本,这样你才能获得布局信息,如边界矩形.既然您提到了NSAttributedString类,那么最好的解决方案是Text Kit在iOS 7之后.或者Core Text如果您的应用程序是针对早期的iOS版本.

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



1> wcd..:

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

    解析html字符串以获取DOM对象.我使用带有光包装的libxml,hpple.这种组合非常快速且易于使用.强力推荐.

    递归遍历DOM对象以构造NSAttributedString对象,使用自定义属性标记链接,用于NSTextAttachment标记图像.我称之为富文本.

    创建或重用主Text Kit对象.即NSLayoutManager,NSTextStorage,NSTextContainer.分配后将它们连接起来.

    布局过程

      将步骤2中构造的富文本传递给步骤3中的NSTextStorage对象[NSTextStorage setAttributedString:]

      使用方法[NSLayoutManager ensureLayoutForTextContainer:]强制布局发生

    计算用方法绘制富文本所需的帧[NSLayoutManager usedRectForTextContainer:].如果需要,添加填充或边距.

    渲染过程

      返回步骤5中计算的高度 [tableView: heightForRowAtIndexPath:]

      用第2步绘制富文本[NSLayoutManager drawGlyphsForGlyphRange:atPoint:].我在这里使用离屏绘图技术,因此结果是一个UIImage对象.

      使用a UIImageView来渲染最终结果图像.或者,结果图像对象传递给contents财产layer的财产contentView的财产UITableViewCell在对象[tableView:cellForRowAtIndexPath:].

    事件处理

      捕捉触摸事件.我使用附有表视图的轻敲手势识别器.

      获取触摸事件的位置.在这个位置可检查,如果用户点击一个链接或图片[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

[NSAttributedString initWithData:options:documentAttributes:error:]渲染相同的html字符串相比,此方法更快,更可定制.即使没有剖析我也可以说Text Kit方法更快.即使我必须自己解析html并构造属性字符串,它也非常快速且令人满意.这种NSDocumentTypeDocumentAttribute方法太慢,因此是不可接受的.有了Text Kit,我还可以创建复杂的布局,如带有可变缩进,边框,任意深度嵌套文本块等的文本块.但它确实需要编写更多代码来构造NSAttributedString和控制布局过程.我不知道如何计算用它创建的属性字符串的边界矩形NSDocumentTypeDocumentAttribute.我相信创建的属性字符串NSDocumentTypeDocumentAttribute是由Web Kit而不是Text Kit.因此,不适用于可变高度表视图单元.

编辑: 如果你必须使用NSDocumentTypeDocumentAttribute,我认为你必须弄清楚布局过程是如何发生的.也许您可以设置一些断点来查看哪个对象负责布局过程.然后,也许您可​​以查询该对象或使用另一种方法来模拟布局过程以获取布局信息.有些人使用ad-hoc单元格或UITextView对象来计算高度,我认为这不是一个好的解决方案.因为这样,应用程序必须至少两次布局相同的文本块.无论你是否知道,在你的应用程序的某个地方,某些对象必须布置文本,这样你才能获得布局信息,如边界矩形.既然您提到了NSAttributedString类,那么最好的解决方案是Text Kit在iOS 7之后.或者Core Text如果您的应用程序是针对早期的iOS版本.

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


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
author-avatar
bl乄ue光耀
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有