如何使NSStringDrawingContext缩小文本?

时间:2023-02-09 21:14:27

I'm trying to use the attributed string API of iOS 6 to calculate the size of text and shrink the font size if necessary. However, I can't get it to work as the documentation says.

我正在尝试使用iOS 6的属性字符串API来计算文本的大小,并在必要时缩小字体大小。但是,正如文档所说,我无法让它工作。

NSString *string = @"This is a long text that doesn't shrink as it should";

NSStringDrawingContext *context = [NSStringDrawingContext new];
context.minimumScaleFactor = 0.5;

UIFont *font = [UIFont fontWithName:@"SourceSansPro-Bold" size:32.f];

NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.lineBreakMode = NSLineBreakByClipping;

NSDictionary *attributes = @{ NSFontAttributeName: font,
                              NSParagraphStyleAttributeName: paragraphStyle };

NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:self.title attributes:attributes];

CGRect rect = [attributedString boundingRectWithSize:CGSizeMake(512.f, 512.f) options:NSStringDrawingUsesLineFragmentOrigin context:context];

NSLog(@"rect: %@, context: %@", NSStringFromCGRect(rect), context.debugDescription);

But the text doesn't shrink and is truncated. actualScaleFactor is always 1. The log results are:

但是文本没有缩小并被截断。 actualScaleFactor始终为1.日志结果为:

rect:{{0, 0}, {431.64801, 80.447998}}, context:<NSStringDrawingContext: 0x14e85770> minimumScaleFactor:0.500000 minimumTrackingAdjustment:0.000000 actualScaleFactor:1.000000 actualTrackingAdjustment:0.000000 totalBounds:{{0, 0}, {431.64801, 80.447998}}

The result is the same if I use the actual drawing method and not the measuring method. If I remove the paragraph style, it makes the text wrap and doesn't shrink it. If I remove the paragraph style AND I choose a size that only allows one line of text, the text is truncated too instead of being shrunk. What is wrong? There is very little documentation or online resources dealing with NSStringDrawingContext. And I'm trying to avoid the use of sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode: which is deprecated in iOS 7.

如果我使用实际绘图方法而不是测量方法,结果是相同的。如果我删除段落样式,它会使文本换行并且不会缩小它。如果我删除段落样式并且我选择仅允许一行文本的大小,则文本也会被截断而不是缩小。怎么了?处理NSStringDrawingContext的文档或在线资源非常少。我试图避免使用sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:在iOS 7中不推荐使用。

3 个解决方案

#1


8  

NSStringDrawingContext's minimumScaleFactor appears to broken in iOS 7.

在iOS 7中,NSStringDrawingContext的minimumScaleFactor似乎已损坏。

As far as I can make out, even in iOS 6, it didn't work for drawing; it worked for measuring, so you could work out what would happen in a context where it does work for drawing, like a UILabel. That way, you know the correct minimum height for the label.

据我所知,即使在iOS 6中,它也不适用于绘图;它适用于测量,所以你可以弄清楚在它可以用于绘图的上下文中会发生什么,比如UILabel。这样,您就知道标签的正确最小高度。

Or, you could use the resulting scale factor to shrink the text yourself, in the knowledge that now it will fit.

或者,您可以使用生成的比例因子自行缩小文本,以了解现在适合的文本。

Example:

- (void)drawRect:(CGRect)rect
{
    // rect is 0,0,210,31

    NSMutableAttributedString* s = 
        [[NSMutableAttributedString alloc] initWithString: @"This is the army Mister Jones."];
    [s addAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"GillSans" size:20]} 
        range:NSMakeRange(0,s.length)];

    NSMutableParagraphStyle* para = [[NSMutableParagraphStyle alloc] init];
    para.lineBreakMode = NSLineBreakByTruncatingTail;
    [s addAttributes:@{NSParagraphStyleAttributeName:para} 
        range:NSMakeRange(0,s.length)];

    NSStringDrawingContext* con = [[NSStringDrawingContext alloc] init];
    con.minimumScaleFactor = 0.5;

    CGRect result = 
        [s boundingRectWithSize:rect.size 
            options:NSStringDrawingUsesLineFragmentOrigin context:con];
    CGFloat scale = con.actualScaleFactor;
    // ...(could have a check here to see if result fits in target rect)...

    // fix font to use scale factor, and draw
    [s addAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"GillSans" size:20*scale]} 
        range:NSMakeRange(0,s.length)];
    [s drawWithRect:rect options:NSStringDrawingUsesLineFragmentOrigin context:nil];

}

In iOS 6, scale is about 0.85 and you can use it as shown to shrink the text. But in iOS 7, scale remains at 1, suggesting that no shrinkage is happening and this feature of NSStringDrawingContext is now useless. I can't tell whether that's a bug or whether the feature has been deliberately abandoned.

在iOS 6中,缩放比例约为0.85,您可以使用它来缩小文本。但是在iOS 7中,比例仍为1,表明没有收缩,NSStringDrawingContext的这个功能现在没用了。我不知道这是一个错误还是故意放弃了这个功能。

#2


4  

After googling for a long time I did not find a solution working under iOS7. Right now I use the following workaround, knowing that it is very ugly. I render a UILabel in memory, take a screenshot and draw that. UILabel is able to shrink the text correctly.

谷歌搜索了很长一段时间后,我找不到在iOS7下工作的解决方案。现在我使用以下解决方法,知道它非常难看。我在内存中渲染一个UILabel,截取屏幕截图并绘制它。 UILabel能够正确缩小文本。

But perhaps someone finds it useful.

但也许有人发现它很有用。

    UILabel *myLabel = [[UILabel alloc] initWithFrame:myLabelFrame];
    myLabel.font = [UIFont fontWithName:@"HelveticaNeue-BoldItalic" size:16];
    myLabel.text = @"Some text that is too long";
    myLabel.minimumScaleFactor = 0.5;
    myLabel.adjustsFontSizeToFitWidth = YES;
    myLabel.backgroundColor = [UIColor clearColor];

    UIGraphicsBeginImageContextWithOptions(myLabelFrame.size, NO, 0.0f);
    [[myLabel layer] renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    [screenshot drawInRect:myLabel.frame];

#3


0  

Just wanted to post this solution as I have been battling with this for a couple hours. I could never get the text area to scale down and would always have the last lines of text cut off.

只是想发布这个解决方案,因为我一直在与此争斗几个小时。我永远无法让文本区域缩小,并且总是会删除最后一行文本。

The solution for me was to not add a context and just set it to nil. I got this when looking at the example on this site. https://littlebitesofcocoa.com/144-drawing-multiline-strings

对我来说,解决方案是不添加上下文并将其设置为nil。在查看本网站上的示例时,我得到了这个。 https://littlebitesofcocoa.com/144-drawing-multiline-strings

Note after getting the size the box would draw at using

在获得大小后,请注意使用时框

pageStringSize = [myString boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrsDictionary context:nil];

I still needed to loop through scaling down the font manually:

我仍然需要手动循环缩小字体:

while (pageStringSize.size.height > 140 && scale > 0.5) {
            scale = scale - 0.1;
            font = [UIFont fontWithName:@"Chalkboard SE" size:24.0 *scale];
            attrsDictionary = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName,[NSNumber numberWithFloat:1.0], NSBaselineOffsetAttributeName, nil];

            pageStringSize = [myString boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrsDictionary context:nil];

        }

#1


8  

NSStringDrawingContext's minimumScaleFactor appears to broken in iOS 7.

在iOS 7中,NSStringDrawingContext的minimumScaleFactor似乎已损坏。

As far as I can make out, even in iOS 6, it didn't work for drawing; it worked for measuring, so you could work out what would happen in a context where it does work for drawing, like a UILabel. That way, you know the correct minimum height for the label.

据我所知,即使在iOS 6中,它也不适用于绘图;它适用于测量,所以你可以弄清楚在它可以用于绘图的上下文中会发生什么,比如UILabel。这样,您就知道标签的正确最小高度。

Or, you could use the resulting scale factor to shrink the text yourself, in the knowledge that now it will fit.

或者,您可以使用生成的比例因子自行缩小文本,以了解现在适合的文本。

Example:

- (void)drawRect:(CGRect)rect
{
    // rect is 0,0,210,31

    NSMutableAttributedString* s = 
        [[NSMutableAttributedString alloc] initWithString: @"This is the army Mister Jones."];
    [s addAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"GillSans" size:20]} 
        range:NSMakeRange(0,s.length)];

    NSMutableParagraphStyle* para = [[NSMutableParagraphStyle alloc] init];
    para.lineBreakMode = NSLineBreakByTruncatingTail;
    [s addAttributes:@{NSParagraphStyleAttributeName:para} 
        range:NSMakeRange(0,s.length)];

    NSStringDrawingContext* con = [[NSStringDrawingContext alloc] init];
    con.minimumScaleFactor = 0.5;

    CGRect result = 
        [s boundingRectWithSize:rect.size 
            options:NSStringDrawingUsesLineFragmentOrigin context:con];
    CGFloat scale = con.actualScaleFactor;
    // ...(could have a check here to see if result fits in target rect)...

    // fix font to use scale factor, and draw
    [s addAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"GillSans" size:20*scale]} 
        range:NSMakeRange(0,s.length)];
    [s drawWithRect:rect options:NSStringDrawingUsesLineFragmentOrigin context:nil];

}

In iOS 6, scale is about 0.85 and you can use it as shown to shrink the text. But in iOS 7, scale remains at 1, suggesting that no shrinkage is happening and this feature of NSStringDrawingContext is now useless. I can't tell whether that's a bug or whether the feature has been deliberately abandoned.

在iOS 6中,缩放比例约为0.85,您可以使用它来缩小文本。但是在iOS 7中,比例仍为1,表明没有收缩,NSStringDrawingContext的这个功能现在没用了。我不知道这是一个错误还是故意放弃了这个功能。

#2


4  

After googling for a long time I did not find a solution working under iOS7. Right now I use the following workaround, knowing that it is very ugly. I render a UILabel in memory, take a screenshot and draw that. UILabel is able to shrink the text correctly.

谷歌搜索了很长一段时间后,我找不到在iOS7下工作的解决方案。现在我使用以下解决方法,知道它非常难看。我在内存中渲染一个UILabel,截取屏幕截图并绘制它。 UILabel能够正确缩小文本。

But perhaps someone finds it useful.

但也许有人发现它很有用。

    UILabel *myLabel = [[UILabel alloc] initWithFrame:myLabelFrame];
    myLabel.font = [UIFont fontWithName:@"HelveticaNeue-BoldItalic" size:16];
    myLabel.text = @"Some text that is too long";
    myLabel.minimumScaleFactor = 0.5;
    myLabel.adjustsFontSizeToFitWidth = YES;
    myLabel.backgroundColor = [UIColor clearColor];

    UIGraphicsBeginImageContextWithOptions(myLabelFrame.size, NO, 0.0f);
    [[myLabel layer] renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *screenshot = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    [screenshot drawInRect:myLabel.frame];

#3


0  

Just wanted to post this solution as I have been battling with this for a couple hours. I could never get the text area to scale down and would always have the last lines of text cut off.

只是想发布这个解决方案,因为我一直在与此争斗几个小时。我永远无法让文本区域缩小,并且总是会删除最后一行文本。

The solution for me was to not add a context and just set it to nil. I got this when looking at the example on this site. https://littlebitesofcocoa.com/144-drawing-multiline-strings

对我来说,解决方案是不添加上下文并将其设置为nil。在查看本网站上的示例时,我得到了这个。 https://littlebitesofcocoa.com/144-drawing-multiline-strings

Note after getting the size the box would draw at using

在获得大小后,请注意使用时框

pageStringSize = [myString boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrsDictionary context:nil];

I still needed to loop through scaling down the font manually:

我仍然需要手动循环缩小字体:

while (pageStringSize.size.height > 140 && scale > 0.5) {
            scale = scale - 0.1;
            font = [UIFont fontWithName:@"Chalkboard SE" size:24.0 *scale];
            attrsDictionary = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName,[NSNumber numberWithFloat:1.0], NSBaselineOffsetAttributeName, nil];

            pageStringSize = [myString boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attrsDictionary context:nil];

        }