Thay thế cho kích thước không dùng nữaWithFont: trong iOS 7?


320

Trong iOS 7, sizeWithFont:hiện không dùng nữa. Làm thế nào để bây giờ tôi chuyển đối tượng UIFont vào phương thức thay thế sizeWithAttributes:?

Câu trả lời:


521

Sử dụng sizeWithAttributes:thay thế, mà bây giờ mất một NSDictionary. Chuyển qua cặp với khóa UITextAttributeFontvà đối tượng phông chữ của bạn như thế này:

CGSize size = [string sizeWithAttributes:
    @{NSFontAttributeName: [UIFont systemFontOfSize:17.0f]}];

// Values are fractional -- you should take the ceilf to get equivalent values
CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));

60
khi làm việc với một NSStringvà một UILabel(không phải LUÔN LUÔN, nhưng thường là như vậy), để ngăn chặn mã trùng lặp / etc, bạn cũng có thể thay thế [UIFont systemFontOfSize:17.0f]bằng label.font- giúp bảo trì mã bằng cách tham chiếu dữ liệu hiện có so với việc bạn nhập nhiều lần hoặc tham chiếu các hằng số trên tất cả địa điểm, v.v.
toblerpwn

8
@toblerpwn có thể nhãn không tồn tại và bạn đang cố gắng tính nhãn lý thuyết.
botbot

5
Làm cách nào để sử dụng ví dụ này để lấy size.height với nhãn có chiều rộng cố định ví dụ 250? HOẶC nếu nhãn của nó có phù thủy tự động chiếm một phần lớn về chiều rộng và tôi đi đến phong cảnh.
Pedroinpeace 18/12/13

12
@Pedroinpeace Bạn sẽ sử dụng boundingRectWithSize:options:attributes:context:thay thế, CGSizeMake(250.0f, CGFLOAT_MAX)trong hầu hết các trường hợp.
James Kuang

9
Một cái gì đó khác cần lưu ý sizeWithAttribut: không tương đương 100%. sizeWithFont được sử dụng để làm tròn các kích thước thành giá trị số nguyên (pixel). Đề nghị sử dụng ceilf trên chiều cao / chiều rộng kết quả hoặc bạn có thể nhận được các vật phẩm mờ (đặc biệt là không retina HW) nếu bạn sử dụng điều này cho các tính toán vị trí.
Nick H247

172

Tôi tin rằng chức năng này không được chấp nhận vì một loạt các NSString+UIKitchức năng ( sizewithFont:..., v.v.) được dựa trên UIStringDrawingthư viện, không an toàn cho chuỗi. Nếu bạn đã cố chạy chúng không phải trên luồng chính (như mọi UIKitchức năng khác ), bạn sẽ có những hành vi không thể đoán trước. Cụ thể, nếu bạn chạy chức năng trên nhiều luồng cùng một lúc, nó có thể sẽ làm hỏng ứng dụng của bạn. Đây là lý do tại sao trong iOS 6, họ đã giới thiệu một boundingRectWithSize:...phương pháp cho NSAttributedString. Điều này đã được xây dựng trên đầu các NSStringDrawingthư viện và là chủ đề an toàn.

Nếu bạn nhìn vào NSString boundingRectWithSize:...hàm mới , nó sẽ yêu cầu một mảng thuộc tính theo cách tương tự như a NSAttributeString. Nếu tôi phải đoán, NSStringchức năng mới này trong iOS 7 chỉ đơn thuần là một trình bao bọc cho NSAttributeStringchức năng từ iOS 6.

Trên lưu ý đó, nếu bạn chỉ hỗ trợ iOS 6 và iOS 7, thì tôi chắc chắn sẽ thay đổi tất cả của bạn NSString sizeWithFont:...thành NSAttributeString boundingRectWithSize. Nó sẽ giúp bạn đỡ đau đầu hơn nếu bạn có một trường hợp góc đa luồng kỳ lạ! Đây là cách tôi chuyển đổi NSString sizeWithFont:constrainedToSize::

Những gì đã từng là:

NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
CGSize size = [text sizeWithFont:font 
               constrainedToSize:(CGSize){width, CGFLOAT_MAX}];

Có thể được thay thế bằng:

NSString *text = ...;
CGFloat width = ...;
UIFont *font = ...;
NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text 
                                    attributes:@{NSFontAttributeName: font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
CGSize size = rect.size;

Xin lưu ý các tài liệu đề cập:

Trong iOS 7 trở lên, phương thức này trả về kích thước phân đoạn (trong thành phần kích thước được trả về CGRect); để sử dụng kích thước được trả về cho chế độ xem kích thước, bạn phải sử dụng nâng giá trị của nó lên số nguyên cao hơn gần nhất bằng hàm trần.

Vì vậy, để kéo ra chiều cao hoặc chiều rộng được tính toán được sử dụng để định cỡ khung nhìn, tôi sẽ sử dụng:

CGFloat height = ceilf(size.height);
CGFloat width  = ceilf(size.width);

ràng buộcRectWithSize không được dùng trong iOS 6.0.
Nirav

2
@Nirav Tôi không thấy bất kỳ đề cập nào về sự phản đối. Bạn có thể chỉ cho tôi nơi nó nói không được tán thành? Cảm ơn! ( developer.apple.com/l
Ông T

2
Có lẽ @Nirav có nghĩa là nó không khả dụng cho NSString trong iOS 6 (được đề cập gián tiếp trong câu trả lời)?
newenglander

1
@VagueExplanation Tôi vừa thử điều đó và ràng buộcRectForSize vẫn không được dùng cho NSAttributionString. Nó cũng không nói không dùng nữa trong tài liệu.
Ông T

5
Tuyệt vời để đề cập đến bằng cách sử dụng ceilf (). Không nơi nào khác đề cập đến điều đó và kích thước của tôi luôn luôn quá nhỏ. Cảm ơn!
Jonathan Brown

29

Như bạn có thể thấy sizeWithFonttại trang web Apple Developer, nó không được dùng nữa nên chúng tôi cần sử dụng sizeWithAttributes.

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

NSString *text = @"Hello iOS 7.0";
if (SYSTEM_VERSION_LESS_THAN(@"7.0")) {
    // code here for iOS 5.0,6.0 and so on
    CGSize fontSize = [text sizeWithFont:[UIFont fontWithName:@"Helvetica" 
                                                         size:12]];
} else {
    // code here for iOS 7.0
   CGSize fontSize = [text sizeWithAttributes: 
                            @{NSFontAttributeName: 
                              [UIFont fontWithName:@"Helvetica" size:12]}];
}

17
Trong trường hợp này tốt hơn để sử dụng [NSObject respondsToSelector:]phương pháp như ở đây: stackoverflow.com/a/3863039/1226304
derpoliuk

16

Tôi đã tạo một danh mục để xử lý vấn đề này, đây là:

#import "NSString+StringSizeWithFont.h"

@implementation NSString (StringSizeWithFont)

- (CGSize) sizeWithMyFont:(UIFont *)fontToUse
{
    if ([self respondsToSelector:@selector(sizeWithAttributes:)])
    {
        NSDictionary* attribs = @{NSFontAttributeName:fontToUse};
        return ([self sizeWithAttributes:attribs]);
    }
    return ([self sizeWithFont:fontToUse]);
}

Bằng cách này, bạn chỉ cần tìm / thay thế sizeWithFont:với sizeWithMyFont:và bạn tốt để đi.


1
mặc dù điều này sẽ tạo cảnh báo trình biên dịch trên ios7 + để tham chiếu sizeWithFont hoặc cảnh báo / lỗi biên dịch trên <ios7 để tham chiếu sizeWithAttribution! Có lẽ tốt nhất để sử dụng macro thay vì kiểm tra responssToSelector - nếu cần. Nhưng vì bạn không thể cung cấp cho Apple với SDK ioS6 nữa ... có lẽ là không!
Nick H247

Thêm phần này vào cảnh báo thay thế #pragma Chẩn đoán GCC bị bỏ qua "-Wdeprecated-tuyên bố"
Ryan Heitner

10

Trong iOS7, tôi cần logic để trả về chiều cao chính xác cho chế độ xem bảng: phương thức heightForRowAtIndexPath, nhưng sizeWithAttribution luôn trả về cùng một chiều cao bất kể độ dài chuỗi vì nó không biết rằng nó sẽ được đặt trong một ô bảng có chiều rộng cố định . Tôi thấy điều này rất tốt cho tôi và tính toán chiều cao chính xác khi xem xét chiều rộng cho ô của bảng! Điều này dựa trên câu trả lời của ông T ở trên.

NSString *text = @"The text that I want to wrap in a table cell."

CGFloat width = tableView.frame.size.width - 15 - 30 - 15;  //tableView width - left border width - accessory indicator - right border width
UIFont *font = [UIFont systemFontOfSize:17];
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: font}];
CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
CGSize size = rect.size;
size.height = ceilf(size.height);
size.width  = ceilf(size.width);
return size.height + 15;  //Add a little more padding for big thumbs and the detailText label

7

Nhãn nhiều dòng sử dụng chiều cao động có thể yêu cầu thông tin bổ sung để đặt kích thước chính xác. Bạn có thể sử dụng sizeWithAttribution với UIFont và NSPar ĐoạnStyle để chỉ định cả phông chữ và chế độ ngắt dòng.

Bạn sẽ xác định Kiểu đoạn văn và sử dụng một NSDipedia như thế này:

// set paragraph style
NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[style setLineBreakMode:NSLineBreakByWordWrapping];
// make dictionary of attributes with paragraph style
NSDictionary *sizeAttributes        = @{NSFontAttributeName:myLabel.font, NSParagraphStyleAttributeName: style};
// get the CGSize
CGSize adjustedSize = CGSizeMake(label.frame.size.width, CGFLOAT_MAX);

// alternatively you can also get a CGRect to determine height
CGRect rect = [myLabel.text boundingRectWithSize:adjustedSize
                                                         options:NSStringDrawingUsesLineFragmentOrigin
                                                      attributes:sizeAttributes
                                                         context:nil];

Bạn có thể sử dụng thuộc tính CGSize 'điều chỉnh kích thước' hoặc CGRect làm thuộc tính orth.size.height nếu bạn đang tìm kiếm chiều cao.

Thông tin thêm về NSPar ĐoạnStyle tại đây: https://developer.apple.com/l Library / mac / document / cocoa / refference / applicationkit / classes / NSPar ĐoạnStyle_Class / Reference / Reference.html


1
Có vẻ như có một vấn đề cú pháp ngay sau khi điều chỉnh kích thước.
Hoàng tử Chris

Cảm ơn bạn đã nắm bắt được vấn đề cú pháp đó, Chris
bitsand

6
// max size constraint
CGSize maximumLabelSize = CGSizeMake(184, FLT_MAX)

// font
UIFont *font = [UIFont fontWithName:TRADE_GOTHIC_REGULAR size:20.0f];

// set paragraph style
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;

// dictionary of attributes
NSDictionary *attributes = @{NSFontAttributeName:font,
                             NSParagraphStyleAttributeName: paragraphStyle.copy};

CGRect textRect = [string boundingRectWithSize: maximumLabelSize
                                     options:NSStringDrawingUsesLineFragmentOrigin
                                  attributes:attributes
                                     context:nil];

CGSize expectedLabelSize = CGSizeMake(ceil(textRect.size.width), ceil(textRect.size.height));

1
Điều này đã giúp tôi tiết kiệm hàng giờ đau đầu, cảm ơn Kirit, xác định các thuộc tính và từ điển trước khối CGRect là điều đã làm cho tôi ... cũng dễ đọc hơn nhiều.
Azin Mehrnoosh

3

Tạo một hàm lấy một ví dụ UILabel. và trả về CGSize

CGSize constraint = CGSizeMake(label.frame.size.width , 2000.0);
// Adjust according to requirement

CGSize size;
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0){

    NSRange range = NSMakeRange(0, [label.attributedText length]);

    NSDictionary *attributes = [label.attributedText attributesAtIndex:0 effectiveRange:&range];
    CGSize boundingBox = [label.text boundingRectWithSize:constraint options: NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;

    size = CGSizeMake(ceil(boundingBox.width), ceil(boundingBox.height));
}
else{
    size = [label.text sizeWithFont:label.font constrainedToSize:constraint lineBreakMode:label.lineBreakMode];
}

return size;

Tôi là trường hợp của tôi đối với chiều cao Hàng động Tôi đã loại bỏ hoàn toàn phương pháp heightForRow và sử dụng UITableViewAutomaticDimension. tableView.estimatedRowHeight = 68.0 tableView.rowHeight = UITableViewAutomaticDimension
Eugene Braginets

3

Giải pháp thay thế-

CGSize expectedLabelSize;
if ([subTitle respondsToSelector:@selector(sizeWithAttributes:)])
{
    expectedLabelSize = [subTitle sizeWithAttributes:@{NSFontAttributeName:subTitleLabel.font}];
}else{
    expectedLabelSize = [subTitle sizeWithFont:subTitleLabel.font constrainedToSize:subTitleLabel.frame.size lineBreakMode:NSLineBreakByWordWrapping];
}

1
Điều này sẽ tạo ra một cảnh báo trình biên dịch trên cả iOS6 & 7. Và sẽ có hành vi khá khác nhau cho mỗi!
Nick H247

3

Dựa trên @bitsand, đây là một phương pháp mới tôi vừa thêm vào danh mục NSString + Extras của mình:

- (CGRect) boundingRectWithFont:(UIFont *) font constrainedToSize:(CGSize) constraintSize lineBreakMode:(NSLineBreakMode) lineBreakMode;
{
    // set paragraph style
    NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    [style setLineBreakMode:lineBreakMode];

    // make dictionary of attributes with paragraph style
    NSDictionary *sizeAttributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName: style};

    CGRect frame = [self boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:sizeAttributes context:nil];

    /*
    // OLD
    CGSize stringSize = [self sizeWithFont:font
                              constrainedToSize:constraintSize
                                  lineBreakMode:lineBreakMode];
    // OLD
    */

    return frame;
}

Tôi chỉ sử dụng kích thước của khung kết quả.


2

Bạn vẫn có thể sử dụng sizeWithFont. nhưng, trong phương thức iOS> = 7.0 gây ra sự cố nếu chuỗi chứa khoảng trắng ở đầu và cuối hoặc cuối dòng \n.

Cắt bớt văn bản trước khi sử dụng nó

label.text = [label.text stringByTrimmingCharactersInSet:
             [NSCharacterSet whitespaceAndNewlineCharacterSet]];

Điều đó cũng có thể áp dụng cho sizeWithAttributes[label sizeToFit].

Ngoài ra, bất cứ khi nào bạn có nsstringdrawingtextstorage message sent to deallocated instancetrong thiết bị iOS 7.0, nó sẽ xử lý vấn đề này.


2

Sử dụng tốt hơn kích thước tự động (Swift):

  tableView.estimatedRowHeight = 68.0
  tableView.rowHeight = UITableViewAutomaticDimension

Lưu ý

nhập mô tả hình ảnh ở đây

VIDEO: https://youtu.be/Sz3XfCsSb6k


Nó lấy chiều cao của ô tĩnh được xác định trong ô nguyên mẫu của bảng phân cảnh
Kirit Vaghela

Đã thêm hai dòng đó và đảm bảo UILabel của tôi được đặt thành 0 số dòng. Không làm việc như quảng cáo. Bạn đã làm gì khác để làm cho nó hoạt động chỉ với hai dòng mã đó?
Sẽ

1
hm, bạn cũng phải ghim các ràng buộc nhãn của bạn vào đầu và cuối của ô
Eugene Braginets

1
Không, điều này là đủ. Bạn có các ràng buộc tự động thanh toán cho nhãn được ghim vào giám sát không?
Eugene Braginets

1
boundingRectWithSize:options:attributes:context:


1

Như câu trả lời @Ayush:

Như bạn có thể thấy sizeWithFonttại trang web Apple Developer, nó không được dùng nữa nên chúng tôi cần sử dụng sizeWithAttributes.

Chà, giả sử rằng vào năm 2019+, có lẽ bạn đang sử dụng SwiftStringthay vì Objective-c và NSString, đây là cách chính xác để có được kích thước của một Stringphông chữ được xác định trước:

let stringSize = NSString(string: label.text!).size(withAttributes: [.font : UIFont(name: "OpenSans-Regular", size: 15)!])

Làm thế nào bạn có thể sử dụng điều này để xác định kích thước phông chữ?
Joseph Astrahan

@JosephAstrahan đây không phải là để xác định kích thước phông chữ, nó là để lấy kích thước (CGSize) của một chuỗi với phông chữ xác định. Nếu bạn muốn lấy kích thước phông chữ của nhãn, bạn có thể dễ dàng sử dụng nhãn.font
Romulo BM

sử dụng ý tưởng của bạn, tôi đã tạo ra một cách để có được kích thước phông chữ nhiều hơn hoặc ít hơn ( stackoverflow.com/questions/61651614/ ,), nhãn.font sẽ không hoạt động trong trường hợp của tôi do một số vấn đề phức tạp với nhãn chỉ có thể nhấp vào nếu phông chữ chính xác được biết bạn có thể đọc về nó trên bài viết của tôi.
Joseph Astrahan

0
- (CGSize) sizeWithMyFont:(UIFont *)fontToUse
{
    if ([self respondsToSelector:@selector(sizeWithAttributes:)])
    {
        NSDictionary* attribs = @{NSFontAttributeName:fontToUse};
        return ([self sizeWithAttributes:attribs]);
    }
    return ([self sizeWithFont:fontToUse]);
}

0

Đây là tương đương monotouch nếu bất cứ ai cần nó:

/// <summary>
/// Measures the height of the string for the given width.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="font">The font.</param>
/// <param name="width">The width.</param>
/// <param name="padding">The padding.</param>
/// <returns></returns>
public static float MeasureStringHeightForWidth(this string text, UIFont font, float width, float padding = 20)
{
    NSAttributedString attributedString = new NSAttributedString(text, new UIStringAttributes() { Font = font });
    RectangleF rect = attributedString.GetBoundingRect(new SizeF(width, float.MaxValue), NSStringDrawingOptions.UsesLineFragmentOrigin, null);
    return rect.Height + padding;
}

có thể được sử dụng như thế này:

public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
{
    //Elements is a string array
    return Elements[indexPath.Row].MeasureStringHeightForWidth(UIFont.SystemFontOfSize(UIFont.LabelFontSize), tableView.Frame.Size.Width - 15 - 30 - 15);
}

0
CGSize maximumLabelSize = CGSizeMake(label.frame.size.width, FLT_MAX);
CGSize expectedLabelSize = [label sizeThatFits:maximumLabelSize];
float heightUse = expectedLabelSize.height;

0

Hãy thử cú pháp này:

NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text 
                                    attributes:@{NSFontAttributeName: font}];

-1

Không ai trong số này làm việc cho tôi trong ios 7. Đây là những gì tôi đã làm. Tôi đặt cái này trong lớp ô tùy chỉnh của mình và gọi phương thức trong phương thức heightForCellAtIndexPath của tôi.

Ô của tôi trông giống với ô mô tả khi xem một ứng dụng trong cửa hàng ứng dụng.

Đầu tiên trong bảng phân cảnh, hãy đặt nhãn của bạn thành 'AttTextText', đặt số lượng dòng thành 0 (sẽ tự động thay đổi kích thước nhãn (chỉ ios 6+)) và đặt nó thành bao bọc từ.

Sau đó, tôi chỉ cần cộng tất cả các độ cao của nội dung của ô trong Lớp ô tùy chỉnh của mình. Trong trường hợp của tôi, tôi có một Nhãn ở đầu luôn ghi "Mô tả" (_descripHeadLabel), một nhãn nhỏ hơn có kích thước thay đổi có chứa mô tả thực tế (_descripLabel) một ràng buộc từ đầu ô đến tiêu đề (_descripHeadLabelTopConstraint) . Tôi cũng đã thêm 3 vào khoảng trống phía dưới một chút (khoảng cùng một số vị trí táo trên ô loại phụ đề.)

- (CGFloat)calculateHeight
{
    CGFloat width = _descriptionLabel.frame.size.width;
    NSAttributedString *attributedText = _descriptionLabel.attributedText;
    CGRect rect = [attributedText boundingRectWithSize:(CGSize){width, CGFLOAT_MAX} options: NSStringDrawingUsesLineFragmentOrigin context:nil];

    return rect.size.height + _descriptionHeadingLabel.frame.size.height + _descriptionHeadingLabelTopConstraint.constant + 3;
}

Và trong đại biểu Bảng xem của tôi:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
    if (indexPath.row == 0) {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"descriptionCell"];
        DescriptionCell *descriptionCell = (DescriptionCell *)cell;
        NSString *text = [_event objectForKey:@"description"];
        descriptionCell.descriptionLabel.text = text;

        return [descriptionCell calculateHeight];
    }

    return 44.0f;
}

Bạn có thể thay đổi câu lệnh if trở nên 'thông minh hơn' một chút và thực sự lấy mã định danh ô từ một số nguồn dữ liệu. Trong trường hợp của tôi, các ô sẽ được mã hóa cứng vì sẽ có số lượng cố định của chúng theo một thứ tự cụ thể.


boundingRectWithSizetrong ios 9.2 có vấn đề, là kết quả khác với ios <9.2. Bạn tìm thấy hoặc biết bất kỳ cách tốt nhất khác để thực hiện điều này.
jose920405
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.