【问题标题】:String length with given font to fit UITextView给定字体的字符串长度以适合 UITextView
【发布时间】:2009-05-12 09:01:59
【问题描述】:

我需要将用户输入到大的多行 UITextView 的文本移动到较小的(但仍然是多行的)UITextView*。如果用户输入的文本比在较小视图中显示的要多,我想截断文本,使其适合所有可见的(截断的)文本。 (大的UITextView 和较小的都不能滚动。)

最好的方法是什么?

我可以使用循环,每次将字符串缩短一个字符,然后使用NSStringsizeWithFont: constrainedToSize: lineBreakMode: 找出这个较短字符串所需的高度,然后将其与我可用的高度进行比较在我较小的UITextView 中,当字符串适合时结束循环 - 但这似乎很慢而且很尴尬。一定有更好的办法。

我只想告诉目的地UITextView 在屏幕上显示它时截断它的 displayText 成员,但我一直无法找到一种方法来做到这一点。

*关于此的更多上下文,来自我在下面的评论:

我有一个横向应用程序。我根据用户选择的照片更改视图的布局。如果是风景照片,标题会更小 - 只是照片底部的一条线。如果她选择一张肖像照片,那么我可以在照片的侧面有足够的空间来放置标题,因此标题会更大。

如果用户将她的照片方向从纵向更改为横向,那么我想截断文本,然后允许她对其进行编辑以使其有意义。我可以直接删除它,但我更愿意保留它以尽量减少她的打字。

【问题讨论】:

    标签: iphone ios cocoa-touch


    【解决方案1】:

    我编写了以下递归方法和公共 API 来正确执行此操作。丑陋的软糖因素是this question的主题。

    #define kFudgeFactor 15.0
    #define kMaxFieldHeight 9999.0
    
    // recursive method called by the main API
    -(NSString*) sizeStringToFit:(NSString*)aString min:(int)aMin max:(int)aMax
    {
    if ((aMax-aMin) <= 1)
        {
        NSString* subString = [aString substringToIndex:aMin];
        return subString;
        }
    
    int mean = (aMin + aMax)/2; 
    NSString* subString = [aString substringToIndex:mean];
    
    CGSize tallerSize = CGSizeMake(self.frame.size.width-kFudgeFactor,kMaxFieldHeight);
    CGSize stringSize = [subString sizeWithFont:self.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];
    
    if (stringSize.height <= self.frame.size.height)
        return [self sizeStringToFit:aString min:mean max:aMax]; // too small
    else    
            return [self sizeStringToFit:aString min:aMin max:mean];// too big
    }
    
    -(NSString*)sizeStringToFit:(NSString*)aString
    {
    
    CGSize tallerSize = CGSizeMake(self.frame.size.width-kFudgeFactor,kMaxFieldHeight);
    CGSize stringSize = [aString sizeWithFont:self.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap];
    
    // if it fits, just return
    if (stringSize.height < self.frame.size.height)
        return aString; 
    
    // too big - call the recursive method to size it       
    NSString* smallerString = [self sizeStringToFit:aString min:0 max:[aString length]];
    return smallerString;   
    }
    

    【讨论】:

    • 这段代码太棒了,谢谢,但我发现了两种失败的情况:1)如果字符串上有很多空格 2)“如果”行中的“,则返回缺少最后一个字符的字符串
    【解决方案2】:

    这实际上不是一个修复,但它确实为计算提供了一个良好的起点。

    如果您使用 NSString 的 sizeWithFont: constrainedToSize: lineBreakMode:,您将获得文本的垂直高度。如果将它除以字体的前导高度,则得到整个字符串中的行数。 [NSString count] 除以该数字可以得到每行字符数的近似值。这假设字符串是同质的,并且如果有人键入(例如)'iiiiiiiiiiii..." 与 "MMMMMMMMM..." 相对,则该字符串将不准确。

    您还可以将边界框除以相关字体的leading 高度,以获得适合边界框的行数。

    将每行的字符乘以行数为您提供了寻找合适文本的起点。

    您可以通过对“iiiiii...”和“MMMMMM...”字符串进行相同的计算来计算该图中的误差范围。

    【讨论】:

    • 同意,这是一个很好的方法,使用上述方法对字符串长度进行二进制切割也是如此。我仍然希望有更好的方法,但我现在感觉没有。
    • @Tristan,仅当您在换行时忽略空格。不过,这将是一个更简单的问题。
    【解决方案3】:

    我建议采用稍微不同的方法,看看是否可以使用 UILabel 代替较小的 UITextView。

    UILabels 可以通过它们的 numberOfLines 属性设置为像 UITextView 一样的多行。

    UILabels 也有一个 lineBreakMode 属性,我相信该属性的默认值会产生您正在寻找的确切截断效果。

    【讨论】:

    • 但我不认为 UILabel 可以编辑,是吗?我希望用户能够在这两个字段中输入文本。 (尤其是在我截断它之后——文本可能没有意义。)
    • 你说得对 UILabel 不可编辑。我想你现在难倒我了。一般来说,我想知道编辑较小的 UITextView 时的用户体验是什么。用户可以编辑所有文本还是只编辑没有被截断的部分?如果用户能够编辑所有文本,如果编辑会话结束后碰巧被截断,用户如何判断他们的编辑已生效?由于这些原因,我可能会考虑将较小的视图保留为文本的只读摘要。
    • 需要更多信息。我有一个风景应用程序。发生的情况是我根据用户选择的照片更改视图的布局。如果是风景照片,标题会更小 - 只是照片底部的一条线。如果他们选择肖像照片,那么我可以在照片侧面有足够的空间用于标题,因此标题更大。如果用户将他的照片方向从纵向更改为横向,那么我想截断文本,然后让他更改它以使其有意义。
    • 我所拥有的只是另一种可能的选择供您考虑。我记得当照片实际上是你打印在纸上并握在手中的东西时。在那个年代,人们是如何添加字幕的?他们在照片的背面写了字。因此,也许您可​​以使用标准翻转动画之一“翻转到照片的背面”。现在你的 UITextView 可以更大,它的大小可以独立于照片方向。您也将有更多空间来显示照片本身。如果有必要,您也可以随时在照片前面显示带有标签的标题。
    • 感谢乔纳森的所有帮助 - 我真的很感激。问题是我确实有一张卡片背面,但它用于其他用途。文字必须在前面。我很惊讶这是多么困难 - 我原以为其他人会这样做。
    【解决方案4】:

    我认为 Jonathan 对 UILabel 有所了解...

    因此,用户完成了 UITextView 的编辑,您将获取文本字符串并将其传递给 UILabel。您将 UITextView 的 alpha 更改为 0 和/或将其从超级视图中删除。可能将未截断的全文存储在 ivar 中。

    UILabel 不是“可编辑的”,但是您可以使用 UILabel(或者它的超级视图)检测触摸。 当您检测到UILabel上的触摸时,您只需恢复隐藏的UITextView并恢复您保存的字符串。

    SDK 有时很痛苦,但它几乎总能赢得战斗。很多时候,最好将您的设计调整为 UIKit 约定

    【讨论】:

      猜你喜欢
      • 2010-11-08
      • 1970-01-01
      • 2015-03-20
      • 2021-03-03
      • 1970-01-01
      • 2019-01-01
      • 2014-05-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多