Penggantian untuk ukuran yang sudah usangWithFont: di iOS 7?

320

Di iOS 7, sizeWithFont:sekarang sudah usang. Bagaimana saya sekarang meneruskan objek UIFont ke metode penggantian sizeWithAttributes:?

James Kuang
sumber

Jawaban:

521

Gunakan sizeWithAttributes:sebagai gantinya, yang sekarang membutuhkan NSDictionary. Masukkan pasangan dengan kunci UITextAttributeFontdan objek font Anda seperti ini:

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));
James Kuang
sumber
60
ketika bekerja dengan NSStringdan UILabel(tidak SELALU kasusnya, tetapi sering kali demikian), untuk mencegah duplikasi kode / dll, Anda juga dapat mengganti [UIFont systemFontOfSize:17.0f]dengan label.font- membantu pemeliharaan kode dengan merujuk data yang ada vs. Anda mengetiknya beberapa kali atau merujuk konstanta di seluruh tempat, dll
toblerpwn
8
@toblerpwn mungkin saja labelnya tidak ada dan Anda mencoba menghitung label teoretis.
botbot
5
Bagaimana saya menggunakan contoh ini untuk mendapatkan size.height dengan label yang mendapat lebar tetap misalnya 250? ATAU jika itu label dengan penyihir autolatyout mengambil persentase dari lebar dan saya pergi ke landscapemode.
Pedroinpeace
12
@Pedroinpeace Anda akan menggunakannya boundingRectWithSize:options:attributes:context:sebagai gantinya, dengan menyerahkan CGSizeMake(250.0f, CGFLOAT_MAX)sebagian besar kasus.
James Kuang
9
Sesuatu yang lain untuk dicatat sizeWithAttributes: tidak 100% setara. sizeWithFont digunakan untuk mengumpulkan nilai-nilai untuk integer (pixel). Sarankan gunakan ceilf pada tinggi / lebar yang dihasilkan atau Anda mungkin mendapatkan artefak buram (khususnya HW non retina) jika Anda menggunakan ini untuk perhitungan posisi.
Nick H247
172

Saya percaya fungsi tersebut sudah usang karena serangkaian NSString+UIKitfungsi ( sizewithFont:..., dll) didasarkan pada UIStringDrawingperpustakaan, yang bukan thread aman. Jika Anda mencoba menjalankannya bukan di utas utama (seperti UIKitfungsi lainnya ), Anda akan mendapatkan perilaku yang tidak terduga. Khususnya, jika Anda menjalankan fungsi pada banyak utas secara bersamaan, mungkin aplikasi Anda akan macet. Inilah sebabnya mengapa di iOS 6, mereka memperkenalkan boundingRectWithSize:...metode untuk NSAttributedString. Ini dibangun di atas NSStringDrawingperpustakaan dan aman utasnya.

Jika Anda melihat NSString boundingRectWithSize:...fungsi baru , ia meminta array atribut dengan cara yang sama dengan a NSAttributeString. Jika saya harus menebak, NSStringfungsi baru ini di iOS 7 hanyalah pembungkus untuk NSAttributeStringfungsi dari iOS 6.

Pada catatan itu, jika Anda hanya mendukung iOS 6 dan iOS 7, maka saya pasti akan mengubah semua Anda NSString sizeWithFont:...menjadi NSAttributeString boundingRectWithSize. Ini akan menghemat banyak sakit kepala jika Anda memiliki kasing sudut multi-threading yang aneh! Inilah cara saya mengonversi NSString sizeWithFont:constrainedToSize::

Apa yang dulu:

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

Dapat diganti dengan:

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;

Harap perhatikan dokumentasi yang menyebutkan:

Di iOS 7 dan yang lebih baru, metode ini mengembalikan ukuran fraksional (dalam komponen ukuran yang dikembalikan CGRect); untuk menggunakan ukuran yang dikembalikan ke ukuran tampilan, Anda harus menggunakan menaikkan nilainya ke bilangan bulat terdekat terdekat menggunakan fungsi ceil.

Jadi untuk mengeluarkan tinggi atau lebar yang dihitung yang akan digunakan untuk mengukur ukuran, saya akan menggunakan:

CGFloat height = ceilf(size.height);
CGFloat width  = ceilf(size.width);
Tuan T
sumber
boundingRectWithSize tidak digunakan lagi di iOS 6.0.
Nirav
2
@ Nirav Saya tidak melihat penyebutan. Bisakah Anda mengarahkan saya ke tempat yang dikatakan sudah usang? Terima kasih! ( developer.apple.com/library/ios/documentation/uikit/reference/… )
Tn.
2
Mungkin @Nirav berarti tidak tersedia untuk NSString di iOS 6 (yang disebutkan secara tidak langsung dalam jawabannya)?
newenglander
1
@ VagueExplanation Saya baru saja mencobanya dan boundingRectForSize masih belum ditinggalkan untuk NSAttributedString. Itu juga tidak mengatakan usang dalam dokumentasi.
Tn.
5
Fantastis untuk disebutkan menggunakan ceilf (). Tidak ada tempat lain yang menyebutkan itu dan ukuran saya selalu sedikit terlalu kecil. Terima kasih!
Jonathan Brown
29

Seperti yang Anda lihat sizeWithFontdi situs Pengembang Apple itu sudah usang jadi kami perlu menggunakannya 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]}];
}
Ayush
sumber
17
Dalam hal ini lebih baik menggunakan [NSObject respondsToSelector:]metode seperti di sini: stackoverflow.com/a/3863039/1226304
derpoliuk
16

Saya membuat kategori untuk menangani masalah ini, ini dia:

#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]);
}

Dengan cara ini Anda hanya perlu mencari / mengganti sizeWithFont:dengan sizeWithMyFont:dan Anda siap melakukannya.

ROM.
sumber
1
meskipun ini akan menghasilkan peringatan kompiler pada ios7 + untuk referensi sizeWithFont, atau kompilasi peringatan / kesalahan pada <ios7 untuk referensi sizeWithAttributes! Mungkin lebih baik menggunakan makro daripada memeriksa respondsToSelector - jika perlu. Tetapi karena Anda tidak dapat mengirim ke apel dengan ioS6 SDK lagi ... mungkin tidak!
Nick H247
Tambahkan ini ke supress warning #pragma GCC diagnostik diabaikan "-Wdeprecated-declarations"
Ryan Heitner
10

Di iOS7 saya membutuhkan logika untuk mengembalikan ketinggian yang benar untuk tampilan tabel: metode heightForRowAtIndexPath, tetapi sizeWithAttributes selalu mengembalikan ketinggian yang sama terlepas dari panjang string karena tidak tahu bahwa itu akan dimasukkan ke dalam sel tabel lebar tetap . Saya menemukan ini bekerja bagus untuk saya dan menghitung ketinggian yang benar dengan mempertimbangkan lebar untuk sel tabel! Ini berdasarkan jawaban Mr. T di atas.

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
pengguna3055587
sumber
7

Label multi-garis menggunakan ketinggian dinamis mungkin memerlukan informasi tambahan untuk mengatur ukuran dengan benar. Anda dapat menggunakan sizeWithAttributes dengan UIFont dan NSParagraphStyle untuk menentukan font dan mode line-break.

Anda akan menentukan Gaya Paragraf dan menggunakan NSDictionary seperti ini:

// 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];

Anda dapat menggunakan properti CGSize 'customizedSize' atau CGRect sebagai rect.size.height jika Anda mencari ketinggian.

Info lebih lanjut tentang NSParagraphStyle di sini: https://developer.apple.com/library/mac/documentation/cocoa/reference/applicationkit/classes/NSParagraphStyle_Class/Reference/Reference.html

bitsand
sumber
1
Sepertinya ada masalah sintaksis segera setelah CustomizedSize.
Chris Prince
Terima kasih telah menangkap masalah sintaksis, 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));
Kirit Vaghela
sumber
1
Ini menyelamatkan saya dari sakit kepala berjam-jam, terima kasih Kirit, mendefinisikan atribut dan kamus sebelum blok CGRect adalah apa yang melakukannya untuk saya ... juga lebih mudah dibaca.
Azin Mehrnoosh
3

Buat fungsi yang mengambil instance UILabel. dan mengembalikan 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;
Muhammad Idris
sumber
Saya kasus saya untuk ketinggian Row dinamis Saya benar-benar menghapus metode HeightForRow dan menggunakan UITableViewAutomaticDimension. tableView.estimatedRowHeight = 68.0 tableView.rowHeight = UITableViewAutomaticDimension
Eugene Braginets
3

Solusi alternatif-

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];
}
Pemberani
sumber
1
Ini akan menghasilkan peringatan kompiler di iOS6 & 7. Dan akan memiliki perilaku yang sangat berbeda untuk masing-masing!
Nick H247
3

Membangun di @bitsand, ini adalah metode baru yang saya tambahkan ke kategori NSString + Extras saya:

- (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;
}

Saya hanya menggunakan ukuran frame yang dihasilkan.

Chris Prince
sumber
2

Anda masih bisa menggunakan sizeWithFont. tetapi, di iOS> = 7.0 metode menyebabkan crash jika string berisi spasi awal dan akhir atau garis akhir \n.

Memotong teks sebelum menggunakannya

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

Itu juga mungkin berlaku untuk sizeWithAttributesdan [label sizeToFit].

juga, setiap kali Anda memiliki nsstringdrawingtextstorage message sent to deallocated instanceperangkat iOS 7.0 itu berurusan dengan ini.

hasan
sumber
2

Lebih baik gunakan dimensi otomatis (Swift):

  tableView.estimatedRowHeight = 68.0
  tableView.rowHeight = UITableViewAutomaticDimension

NB: 1. Prototipe UITableViewCell harus dirancang dengan baik (untuk contoh jangan lupa setel UILabel.numberOfLines = 0 dll) 2. Hapus metode HeightForRowAtIndexPath

masukkan deskripsi gambar di sini

VIDEO: https://youtu.be/Sz3XfCsSb6k

Eugene Braginets
sumber
Butuh ketinggian sel statis yang ditentukan dalam sel prototipe storyboard
Kirit Vaghela
Menambahkan dua baris itu dan memastikan UILabel saya disetel ke 0 jumlah baris. Tidak berfungsi seperti yang diiklankan. Apa lagi yang Anda lakukan untuk membuatnya bekerja hanya dengan dua baris kode itu?
Will
1
hm, Anda juga harus menyematkan batasan label ke bagian atas dan bawah sel
Eugene Braginets
1
Tidak, ini sudah cukup. Apakah Anda memiliki batasan autolayout untuk label yang disematkan ke superview?
Eugene Braginets
1
boundingRectWithSize:options:attributes:context:
Holden
sumber
1

Jawaban yang diterima di Xamarin adalah (gunakan sizeWithAttributes dan UITextAttributeFont):

        UIStringAttributes attributes = new UIStringAttributes
        { 
            Font = UIFont.SystemFontOfSize(17) 
        }; 
        var size = text.GetSizeUsingAttributes(attributes);
Alex Sorokoletov
sumber
1

Sebagai jawaban @Ayush:

Seperti yang Anda lihat sizeWithFontdi situs Pengembang Apple itu sudah usang jadi kami perlu menggunakannya sizeWithAttributes.

Nah, seandainya pada 2019+ Anda mungkin menggunakan Swift dan Stringbukannya Objective-c dan NSString, inilah cara yang benar untuk mendapatkan ukuran huruf Stringdengan font yang telah ditentukan:

let stringSize = NSString(string: label.text!).size(withAttributes: [.font : UIFont(name: "OpenSans-Regular", size: 15)!])
Romulo BM
sumber
Bagaimana Anda bisa menggunakan ini untuk menentukan ukuran font?
Joseph Astrahan
@JosephAstrahan ini bukan untuk menentukan ukuran font, itu untuk mendapatkan ukuran (CGSize) dari string dengan font yang ditentukan. Jika Anda ingin mendapatkan ukuran font label, Anda dapat dengan mudah menggunakan label.font
Romulo BM
menggunakan ide Anda, saya membuat cara untuk mendapatkan ukuran font lebih atau kurang ( stackoverflow.com/questions/61651614/... ), label.font tidak akan berfungsi dalam kasus saya karena beberapa masalah rumit dengan label yang hanya dapat diklik jika font yang tepat diketahui Anda dapat membacanya di posting saya.
Joseph Astrahan
0
- (CGSize) sizeWithMyFont:(UIFont *)fontToUse
{
    if ([self respondsToSelector:@selector(sizeWithAttributes:)])
    {
        NSDictionary* attribs = @{NSFontAttributeName:fontToUse};
        return ([self sizeWithAttributes:attribs]);
    }
    return ([self sizeWithFont:fontToUse]);
}
Prosenjit Goswami
sumber
0

Berikut ini adalah setara monotouch jika ada yang membutuhkannya:

/// <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;
}

yang bisa digunakan seperti ini:

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);
}
Roosevelt
sumber
0
CGSize maximumLabelSize = CGSizeMake(label.frame.size.width, FLT_MAX);
CGSize expectedLabelSize = [label sizeThatFits:maximumLabelSize];
float heightUse = expectedLabelSize.height;
pengguna1802778
sumber
0

Coba sintaks ini:

NSAttributedString *attributedText =
    [[NSAttributedString alloc] initWithString:text 
                                    attributes:@{NSFontAttributeName: font}];
Muruganandam Sathasivam
sumber
-1

Tak satu pun dari ini bekerja untuk saya di ios 7. Inilah yang akhirnya saya lakukan. Saya menempatkan ini di kelas sel khusus saya dan memanggil metode dalam metode heightForCellAtIndexPath saya.

Sel saya terlihat mirip dengan sel deskripsi saat melihat aplikasi di app store.

Pertama di storyboard, atur label Anda ke 'attributedText', atur jumlah baris menjadi 0 (yang akan mengubah ukuran label secara otomatis (hanya 6 + lebih)) dan atur ke bungkus kata.

Lalu saya hanya menambahkan semua ketinggian isi sel di Kelas Sel kustom saya. Dalam kasus saya, saya memiliki Label di bagian atas yang selalu bertuliskan "Keterangan" (_descriptionHeadingLabel), label yang lebih kecil yang ukuran variabel yang berisi deskripsi aktual (_descriptionLabel) kendala dari bagian atas sel ke pos (_descriptionHeadingLabelTopConstraint) . Saya juga menambahkan 3 ke spasi sedikit di bagian bawah (tentang jumlah yang sama tempat apel pada sel jenis subtitle.)

- (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;
}

Dan dalam delegasi Table View saya:

- (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;
}

Anda bisa mengubah pernyataan if menjadi sedikit 'lebih pintar' dan benar-benar mendapatkan pengenal sel dari semacam sumber data. Dalam kasus saya sel-sel akan dikodekan dengan keras karena akan ada jumlah yang tetap dari mereka dalam urutan tertentu.

Kubee
sumber
boundingRectWithSizedi ios 9.2 masalah sekarang, hasilnya berbeda dengan ios <9.2. Anda menemukan atau mengetahui cara terbaik lain untuk membuat ini.
jose920405