缩放文本以适合iPhone

时间:2010-11-16 22:28:23

标签: iphone layout text uilabel core-text

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

我的主视图包含一个文本视图,应用程序的设计决定了一些事项:

  • 文本的(字体)大小应该是动态的
  • 文本框架应在视图中垂直居中
  • 连字应该是自动的,只在需要时(尽可能避免)

目前我正在使用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];
}

这在某种程度上起作用,但是当文本有点偏长时经常会失败,这两个例子显示它呈现以下字符串:

  • “短文”
  • “我想要很好地呈现的文本数量要长得多。”

short text longer text

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

  • 最初的寡妇
  • 丢弃的y
  • 失踪的“很好。”

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

3 个答案:

答案 0 :(得分:3)

以下方法将使用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;
}

使用此选项并获取字体大小并指定给标签。

答案 1 :(得分:2)

我发现有用的一件事是一个小函数,它接受一个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--;

这只是一个开始,但它应该可行。

答案 2 :(得分:0)

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;