缩放文本以适合 iPhone

发布于 2024-10-02 10:34:06 字数 2637 浏览 4 评论 0原文

我在确定在应用程序中呈现文本的“最佳”方法时遇到了一些麻烦。

我的主视图由文本视图组成,应用程序的设计规定了一些事情:

  • 文本的(字体)大小应该是动态的
  • 文本框架应该在视图中垂直居中 连
  • 字符应该是自动的,并且仅在需要时(如果可能的话,避免)

目前,我正在使用 UILabel 和以下代码来尝试猜测用于文本量的最佳字体大小:

txt  = @"this is just some sample text";

mylabel.font = [self getFontForString:txt];
mylabel.adjustsFontSizeToFitWidth = YES;
mylabel.numberOfLines = 0;
[mylabel setText:txt];

并且:

- (UIFont *) getFontForString:(NSString *)txt {
     CGFloat                textLength = txt.length;
     CGFloat                maxFontSize = 71;
     CGFloat                minFontSize = 27;
     CGFloat                newFontSize = 0;

     NSArray                *chunks = [txt componentsSeparatedByString:@" "];
     NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"length" ascending:NO] autorelease];
     NSArray                *sortedChunks = [chunks sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];

     CGSize                labelSize = theThingLabel.bounds.size;
     CGSize                projectedSize = [[sortedChunks objectAtIndex:0] sizeWithFont:[UIFont boldSystemFontOfSize:maxFontSize]];

     if (projectedSize.width > labelSize.width) {
          CGFloat percentageDifference = ((projectedSize.width - labelSize.width)/labelSize.width)*100;
          if (percentageDifference > 50) {
               newFontSize = ((minFontSize/percentageDifference)*100) - 10;
               if (newFontSize < minFontSize) newFontSize = minFontSize;
          } else {
               newFontSize = ((percentageDifference/maxFontSize)*100) - 10;
               if(newFontSize < (maxFontSize/2)) newFontSize = maxFontSize - abs(newFontSize);
          }
     } else {
          if ( textLength > 11 && textLength < 255) {
               newFontSize = (maxFontSize - ((maxFontSize - minFontSize) * ((textLength- 11) / 100)));
          } else if (textLength <= 11) {
               newFontSize = maxFontSize;
          } else if (textLength >= 255) {
               newFontSize = minFontSize;
          }
     }

     return [UIFont boldSystemFontOfSize:newFontSize];
}

这在一定程度上有效,但当文本是长一点,这两个示例显示它渲染以下字符串:

  • “短文本量”
  • “我仍然想很好地渲染的相当长的文本量”。

短文本 longer text

正如您在第二个示例(文本更长)中看到的,存在许多问题

  • 掉落的 y
  • 失踪的“很好”。

因此,考虑到所有这些,我的选择是什么,如果这是正确的解决方案,我愿意转向使用 coretext,但不知道从哪里开始,也有可能我犯了一个我只能做的错误在我的“字体大小猜测”代码中看不到。

I'm having a bit of trouble working out the "best" way to render text in my application.

My main view consists of a text view, and the design of the application dictates a few things:

  • The (font) size of the text should be dynamic
  • The text frame should be centred vertically in the view
  • Hyphenation should be automatic and only when needed (avoided if possible)

At the moment i'm using a UILabel and the following code to try and guess the best font size to use for the amount of text:

txt  = @"this is just some sample text";

mylabel.font = [self getFontForString:txt];
mylabel.adjustsFontSizeToFitWidth = YES;
mylabel.numberOfLines = 0;
[mylabel setText:txt];

And:

- (UIFont *) getFontForString:(NSString *)txt {
     CGFloat                textLength = txt.length;
     CGFloat                maxFontSize = 71;
     CGFloat                minFontSize = 27;
     CGFloat                newFontSize = 0;

     NSArray                *chunks = [txt componentsSeparatedByString:@" "];
     NSSortDescriptor *sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"length" ascending:NO] autorelease];
     NSArray                *sortedChunks = [chunks sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];

     CGSize                labelSize = theThingLabel.bounds.size;
     CGSize                projectedSize = [[sortedChunks objectAtIndex:0] sizeWithFont:[UIFont boldSystemFontOfSize:maxFontSize]];

     if (projectedSize.width > labelSize.width) {
          CGFloat percentageDifference = ((projectedSize.width - labelSize.width)/labelSize.width)*100;
          if (percentageDifference > 50) {
               newFontSize = ((minFontSize/percentageDifference)*100) - 10;
               if (newFontSize < minFontSize) newFontSize = minFontSize;
          } else {
               newFontSize = ((percentageDifference/maxFontSize)*100) - 10;
               if(newFontSize < (maxFontSize/2)) newFontSize = maxFontSize - abs(newFontSize);
          }
     } else {
          if ( textLength > 11 && textLength < 255) {
               newFontSize = (maxFontSize - ((maxFontSize - minFontSize) * ((textLength- 11) / 100)));
          } else if (textLength <= 11) {
               newFontSize = maxFontSize;
          } else if (textLength >= 255) {
               newFontSize = minFontSize;
          }
     }

     return [UIFont boldSystemFontOfSize:newFontSize];
}

This works, to an extent, but often falls over when the text is a bit on the long side, these two example show it rendering the following strings:

  • "short amount of text"
  • "a substantially longer amount of text which i still want to render nicely."

short text
longer text

As you can see in the second example (with far longer text) there are a number of issues:

  • The initial widow
  • The dropped y
  • The missing "nicely."

So with all this in mind, what are my options, I'm open to moving to using coretext if this is the right solution, but have no idea where to start, it's also possible I've made a mistake which I just can't see in my "font size guessing" code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

孤君无依 2024-10-09 10:34:06

以下方法将使用 full 来获取特定矩形(区域)的特定字符串的字体大小。

-(float) maxFontSizeThatFitsForString:(NSString*)_string inRect:(CGRect)rect withFont:(NSString *)fontName onDevice:(int)device {   
// this is the maximum size font that will fit on the device
float _fontSize = maxFontSize;
float widthTweak;

// how much to change the font each iteration. smaller
// numbers will come closer to an exact match at the 
// expense of increasing the number of iterations.
float fontDelta = 2.0;

// sometimes sizeWithFont will break up a word 
// if the tweak is not applied. also note that 
// this should probably take into account the
// font being used -- some fonts work better
// than others using sizeWithFont.
if(device == IPAD)
    widthTweak = 0.2;
else
    widthTweak = 0.2;

CGSize tallerSize = CGSizeMake(rect.size.width-(rect.size.width*widthTweak), 100000);
CGSize stringSize = CGSizeZero;

    if([[UIDevice currentDevice].systemVersion floatValue]>=7.0){
        NSDictionary *stringAttributes = [NSDictionary dictionaryWithObject:[UIFont boldSystemFontOfSize:17] forKey: NSFontAttributeName];
        stringSize = [_string boundingRectWithSize: tallerSize options:NSStringDrawingUsesLineFragmentOrigin attributes:stringAttributes context:nil].size;
    }
    else{
        stringSize = [_string sizeWithFont:[UIFont fontWithName:fontName size:_fontSize] constrainedToSize:tallerSize];
    }

while (stringSize.height >= rect.size.height)
{       
    _fontSize -= fontDelta;
    stringSize = [_string sizeWithFont:[UIFont fontWithName:fontName size:_fontSize] constrainedToSize:tallerSize];
}

return _fontSize;
}

使用它并获取字体大小并分配给标签。

The following method will be use full to get the font size for specific string for specific rectangle (area).

-(float) maxFontSizeThatFitsForString:(NSString*)_string inRect:(CGRect)rect withFont:(NSString *)fontName onDevice:(int)device {   
// this is the maximum size font that will fit on the device
float _fontSize = maxFontSize;
float widthTweak;

// how much to change the font each iteration. smaller
// numbers will come closer to an exact match at the 
// expense of increasing the number of iterations.
float fontDelta = 2.0;

// sometimes sizeWithFont will break up a word 
// if the tweak is not applied. also note that 
// this should probably take into account the
// font being used -- some fonts work better
// than others using sizeWithFont.
if(device == IPAD)
    widthTweak = 0.2;
else
    widthTweak = 0.2;

CGSize tallerSize = CGSizeMake(rect.size.width-(rect.size.width*widthTweak), 100000);
CGSize stringSize = CGSizeZero;

    if([[UIDevice currentDevice].systemVersion floatValue]>=7.0){
        NSDictionary *stringAttributes = [NSDictionary dictionaryWithObject:[UIFont boldSystemFontOfSize:17] forKey: NSFontAttributeName];
        stringSize = [_string boundingRectWithSize: tallerSize options:NSStringDrawingUsesLineFragmentOrigin attributes:stringAttributes context:nil].size;
    }
    else{
        stringSize = [_string sizeWithFont:[UIFont fontWithName:fontName size:_fontSize] constrainedToSize:tallerSize];
    }

while (stringSize.height >= rect.size.height)
{       
    _fontSize -= fontDelta;
    stringSize = [_string sizeWithFont:[UIFont fontWithName:fontName size:_fontSize] constrainedToSize:tallerSize];
}

return _fontSize;
}

Use this and get the font size and assign to the label.

疯狂的代价 2024-10-09 10:34:06

我发现有用的一件事是一个小函数,它接受一个 NSString、一个 UIFont 和一个 CGSize,返回一个 CGFloat 表示该字符串的最大字体大小,该字符串适合传递的 CGSize - 它使用 sizeWithFont连续较小的点大小,直到返回的大小适合 CGSize 参数。如果您不关心一维,您可以将 CGFLOAT_MAX 作为 x 或 y 传递,例如当您检查一条线的宽度并稍后检查整个字符串的高度时。当然,您可以定义最大和最小字体大小。

我首先使用 componentsSeparatedByString 将字符串分隔成单词数组。当所有单词都以相同的字体大小呈现时,您可能想要定义一个可以连字符连接的单词,该单词是下一个最大单词的某个倍数,因此您可以为任意字体大小创建该数组,并且有一个匹配的数组相对宽度(或者可能是字典,其中单词是键,宽度是值)。在继续之前,您需要扫描任何此类单词并将其分成两个单词(一个包含连字符)。接下来,您需要使用上面提到的函数找到所有单词都适合您的大小限制的字体大小,在您显示的示例中是宽度。

当候选字体大小已知时,您会检查其他约束,例如寡妇 - 可能您根据它们可能不会出现的位置(一个的开始)以及形成寡妇的线宽比例来定义这些约束。如果您发现问题,您可以组合数组中的单词以删除寡妇并重新计算新的候选字体大小 - 这样做可能会导致您最终在第一行上使用连字符或整体上使用较小的字体,但如果您的文本是“不成比例”长开始一组小词”那么你可能别无选择。

“很好地”消失的问题并不难,您只需要仔细检查渲染文本的高度 - 一旦您完成了上述过程,您可能可以使用 sizeWithFont:constrainedToSize最后检查。如果失败,请减小最大候选字体大小并重新开始。

所以:

candidateFontSize = CGFLOAT_MAX;
while(stillRendering)

  break string into array of words
  make array of word widths
  check for and divide hyphenation candidates

  for each word
    if max font size returned for rendered word in size constraint < canddiateFontSize
      candidateFontSize = max font size returned

  for each word
    measure each word and record rendered size in candidateFontSize

  stillRendering = NO;

  for each word 
    check each word for problems such as widows and solve
    if problem found
      stillRendering = YES;

  check entire string using sizeWithFont:constrainedToSize using width, CGFLOAT_MAX

  if height is too large
    stillRendering = YES;
    candidateFontSize--;

这只是一个开始,但从那里开始应该是可行的。

One thing I have found useful is a little function that takes a NSString, a UIFont and a CGSize, returning a CGFloat representing the largest font size for that string that will fit in the CGSize passed - it uses sizeWithFont on successively smaller point sizes until the size returned fits within the CGSize argument. You can pass CGFLOAT_MAX as either x or y if you don't care about one dimension, like when you are checking width of a line and will check height later on the whole string. You define max and min font sizes of course.

I would begin by separating the string into an array of words using componentsSeparatedByString. Possibly you want to define a word that may be hyphenated as a word that is some multiple of the next largest word when all words are rendered at the same font size, so you create that array for some arbitrary font size and you have a matching array of relative widths (or maybe a dictionary where the word is the key and the width the value). You need to scan for and separate any such word(s) into two words (one containing the hyphen) before proceeding. Next you need to find the font size at which all the words fit into your size constraint, in the example you have shown that is width, using the function mentioned above.

When the candidate font size is known you check for your other constraints such as widows - probably you define these in terms of the positions they may not occur (the start for one) and the proportion of line width that makes a widow. If you find a problem you combine words in the array to remove the widow and recalculate a new candidate font size - doing this might cause you to end up with hyphenation on the first line or a smaller font overall but if your text is "a disproportionally long start to a group of small words" then you may have no choice.

The problem of "nicely" disappearing isn't so hard, you just need to double-check the height of the rendered text - probably you could use sizeWithFont:constrainedToSize once you have completed the procedures above as your final check. If it fails, reduce maximum candidate font size and start over.

So:

candidateFontSize = CGFLOAT_MAX;
while(stillRendering)

  break string into array of words
  make array of word widths
  check for and divide hyphenation candidates

  for each word
    if max font size returned for rendered word in size constraint < canddiateFontSize
      candidateFontSize = max font size returned

  for each word
    measure each word and record rendered size in candidateFontSize

  stillRendering = NO;

  for each word 
    check each word for problems such as widows and solve
    if problem found
      stillRendering = YES;

  check entire string using sizeWithFont:constrainedToSize using width, CGFLOAT_MAX

  if height is too large
    stillRendering = YES;
    candidateFontSize--;

It's only a start but it should be workable from there.

椵侞 2024-10-09 10:34:06
CGSize expectedLabelSize = [yourString sizeWithFont:yourLable.font];

        //adjust the label the the new height.
    CGRect newFrame = yourLable.frame;
    newFrame.size.width = expectedLabelSize.width;
    newFrame.origin.x = x;
    yourLable.frame = newFrame;
CGSize expectedLabelSize = [yourString sizeWithFont:yourLable.font];

        //adjust the label the the new height.
    CGRect newFrame = yourLable.frame;
    newFrame.size.width = expectedLabelSize.width;
    newFrame.origin.x = x;
    yourLable.frame = newFrame;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文