yanlış boyut döndüren NSAttributedString için boundingRectWithSize


151

Ilişkili bir dize için rect almaya çalışıyorum, ama boundingRectWithSize çağrı ben geçmek boyutu saygı değil ve büyük bir yüksekliğe (uzun bir dize) aksine tek bir satır yüksekliği ile bir rect döndürüyor. Yükseklik için çok büyük bir değer ve aynı zamanda aşağıdaki kodda olduğu gibi 0 geçerek denedim, ancak döndürülen rekt her zaman aynıdır.

CGRect paragraphRect = [attributedText boundingRectWithSize:CGSizeMake(300,0.0)
  options:NSStringDrawingUsesDeviceMetrics
  context:nil];

Bu bozuk mu, yoksa sarılmış metin için bir rekt döndürmesi için başka bir şey yapmam gerekiyor mu?


5
Kırpma / kırpma ile bir paragraf stiliniz var lineBreakModemı?
danyowdee

1
UILabel yanlış genişlikte ölçülmesi / kaydırılması nedeniyle bunu okuyorsanız stackoverflow.com/questions/46200027/… adresine bakın . özellikle, uygulama başlatıldığında NSAllowsDefaultLineBreakStrategy değerini false olarak ayarlamak.
eric

Yanıtlar:


312

Görünüşe göre doğru seçenekleri sunmuyorsunuz. Etiketleri sarmak için en azından şunları sağlayın:

CGRect paragraphRect =
  [attributedText boundingRectWithSize:CGSizeMake(300.f, CGFLOAT_MAX)
  options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
  context:nil];

Not: orijinal metin genişliği 300.f'nin altındaysa, satır kaydırma olmaz, bu nedenle ciltli boyutun doğru olduğundan emin olun, aksi takdirde yine de yanlış sonuçlar alırsınız.


25
Herkes bu cevaba bakmalı, çünkü işe yarıyor. Belki de bu yöntem ve genel olarak ilişkilendirilmiş dizeler için daha açık belgeler gereklidir; nereye bakmanız gerektiği belli değil.
macserv

31
Dikkat! Her zaman işe yaramaz! Bazen döndürülen genişlik, size parametresinin genişliğinden daha büyüktür. Hatta Apples yöntemi dokümantasyonu şunları ifade eder: "size parametresinde belirttiğiniz kısıtlamalar, dizenin boyutlandırılması için oluşturucu için bir kılavuzdur. Ancak, bu yöntemle döndürülen gerçek sınırlayıcı dikdörtgen, tüm dizeyi oluştur. "
Klaas

75
NSAttributedString ve boundingRectWithSize için API kesinlikle şok edici.
malhal

27
Başka bir sorun, paragrafRect'te döndürülen yüksekliğin (ve muhtemelen genişliğin) neredeyse her zaman kesirli bir değer olmasıdır. Bu yüzden yükseklik olarak 141.3 diyerek ortaya çıkabilir. Sonucunun ceilf(paragraphRect.size.height)yuvarlanması için kullanmanız gerekir . Bunu her zaman unutuyorum ve etiketlerimin neden hala kırpıldığını merak ediyorum.
jamone

39
boundingRectWithSize:Hesaplamalarımı her zaman CGRectIntegral () 'de sararım CGRectIntegral rounds the rectangle’s origin downward and its size upward to the nearest whole integers, bu durumda yükseklik veya genişlik kesirli bir değer olursa kırpma oluşmamasını sağlamak için yüksekliği ve genişliği yuvarlar.
runmad

47

Nedense, boundingRectWithSize her zaman yanlış boyut döndürür. Bir çözüm buldum. UItextView -sizeThatFits için metin kümesi için uygun boyutu döndüren bir yöntem vardır. Dolayısıyla, boundingRectWithSize yerine rastgele bir çerçeveye sahip bir UITextView oluşturun ve size ilgili genişlik ve CGFLOAT_MAX yüksekliğiyle sizeThatFits öğelerini çağırın. Uygun yüksekliğe sahip olan boyutu döndürür.

   UITextView *view=[[UITextView alloc] initWithFrame:CGRectMake(0, 0, width, 10)];   
   view.text=text;
   CGSize size=[view sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)];
   height=size.height; 

Bir while döngüsünde boyutu hesaplıyorsanız, bir otomatik yayın havuzuna eklemeyi unutmayın, çünkü oluşturulan sayıda UITextView olacak, otomatik yayın havuzunu kullanmazsak uygulamanın çalışma süresi belleği artacaktır.


1
Bu çalışıyor. Denediğim diğer yollar asla iş bulamamıştım. Benim sorunum atadığım atfedilen dize karmaşıklığından kaynaklanıyor sanırım. Bir RTF dosyasından geliyordu ve çeşitli yazı tipleri, satır aralığı, hatta gölgeler kullanıyordu ve UsesLineFragmentOrigin ve UsesFontLeading'i kullanmamıza rağmen, yeterince uzun bir sonuç alamadım ve sorun, ilişkilendirilen dize ne kadar uzunsa o kadar kötüydü. Benim tahminim nispeten basit dizeleri boundingRectWithSize yöntemi olmasıdır olabilir çalışırlar. Bununla çalışmak için gerçekten daha iyi bir yönteme ihtiyaçları var, imo.
John Bushnell

yöntem çağrıldığı UITextViewkadar çok zaman oluşturmanın aşırıya kaçtığını düşünmüyor heightForRowAtIndexPathmusunuz? zaman alır
János

Sana katılıyorum @ János, ama bu benim için çalışan tek çözümdü.
shoan

btw Doğru çözümü sordum: stackoverflow.com/questions/32495744/…
János

çalışan tek çözüm budur. Bu sorunun 9 yılı var ve Apple bunu çözmek istemiyor ya da mühendisleri bunun için bir çözüm bulmak için zayıflar. Burada hala aynı hatayı taşıyan iOS 11'den bahsediyoruz.
Ördek

31

Ed McManus, bunun işe yaraması için kesinlikle bir anahtar sağlamıştır. Çalışmayan bir dava buldum

UIFont *font = ...
UIColor *color = ...
NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                     font, NSFontAttributeName,
                                     color, NSForegroundColorAttributeName,
                                     nil];

NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString: someString attributes:attributesDictionary];

[string appendAttributedString: [[NSAttributedString alloc] initWithString: anotherString];

CGRect rect = [string boundingRectWithSize:constraint options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];

rect doğru yüksekliğe sahip olmayacaktır. Bir öznitelik sözlüğü olmadan anotherString ( dizeye eklenir ) başlatıldığına dikkat edin . Bu anotherString için meşru bir başlatıcıdır ancak boundingRectWithSize: bu durumda doğru bir boyut vermez.


33
Teşekkür ederim! Herhangi bir NSAttributedString parçasının en azından NSFontAttributeName ve NSForegroundColorAttributeName kümesiyle bir sözlük kümesinin olması gerektiği ortaya çıkıyor, eğer boundingRectWithSize gerçekten çalışması için! Hiçbir yerde belgelendiğini görmüyorum.
Ben Wheeler

3
Ayrıca, bir tane yapmak için birleştirilen farklı NSAttributedStrings öğelerinin, boundingRectWithSize öğesinin düzgün çalışması için SAME sözlüğünü (ve böylece aynı yazı tipini) kullanması gerektiğini unutmayın.
Ben Wheeler

1
Bu çözüm, "sığdırmak için küçült" için UILabel kategorim için kullandığım çözümle çok benzer. Bunu işe almamın anahtarı FLT_MAX yüksekliğinde sınırlayıcı dikdörtgen oluşturmaktı. O olmadan, sadece bir satır için bir dikdörtgen alıyorum.
dre1138

+ 1'ler her yerde. Bu beni gerçekten etkiledi, bu yüzden çalıştığınız için teşekkürler çocuklar.
Wex

Bu sorunu yaşadım. NSMutableAttributedStringNitelikler olmadan boşluklar ve yeni satırlar kullandım ve yanlış boyut alıyordum.
vbezhenar

28

Uzun araştırmadan sonraki son kararım:
- boundingRectWithSizeişlev, sadece kesintisiz karakter dizisi için doğru boyutu döndürür! Dize boşluk veya başka bir şey içeriyorsa (Apple tarafından "Bazı glifler" olarak adlandırılır) - metni görüntülemek için gereken gerçek boyutta rektifi elde etmek imkansızdır!
Dizelerimdeki boşlukları harflerle değiştirdim ve hemen doğru sonucu aldım.

Apple burada diyor: https://developer.apple.com/documentation/foundation/nsstring/1524729-boundingrectwithsize

"Bu yöntem, dizedeki gliflerin gerçek sınırlarını döndürür. Gliflerin bazılarının (örneğin boşluklar), iletilen boyutla belirtilen düzen sınırlamalarının üstüne gelmesine izin verilir, bu nedenle bazı durumlarda boyut bileşeninin genişlik değeri geri döndü CGRect , size parametresinin genişlik değerini aşabilir. "

Bu yüzden gerçek rect'i hesaplamak için başka bir yol bulmak gerekir ...


Uzun soruşturma sürecinden sonra nihayet çözüm bulundu !!! İlgili tüm vakalar için iyi çalışacağından emin değilim UITextView, ancak ana ve önemli şey tespit edildi!

boundingRectWithSizeişlevinin yanı sıra CTFramesetterSuggestFrameSizeWithConstraints(ve diğer birçok yöntem), doğru dikdörtgen kullanıldığında boyutu ve metin bölümünü doğru hesaplar. Örneğin - UITextViewhas textView.bounds.size.width- ve bu değer metin çizerken sistem tarafından kullanılan gerçek dikdörtgen değil UITextView.

Çok ilginç bir parametre buldum ve kodda basit hesaplama yaptım:

CGFloat padding = textView.textContainer.lineFragmentPadding;  
CGFloat  actualPageWidth = textView.bounds.size.width - padding * 2;

Ve sihir çalışıyor - tüm metinlerim doğru hesaplandı! Zevk almak!


1
Ben de fark ettim ki, genişlik için kısıtlama tutmuyor, her zaman daha büyük çıkıyor. Bu gerçekten hoş değil, çalışma yöntemini onayladılar ve şimdi bu saçmalıkla uğraşmak zorundayız.
Boris Gafurov

1
Siz efendim, benim yeni kahramanımsınız! Bu beni delirtiyordu. TextContainers iç metinleri ayarlıyorum, bu yüzden textView.bounds.size.witdh yerine textView.textContainer.size.width kullanmak zorunda kaldım.
GCBenson

Buna yeterince oy veremedim. Sınırlayıcı hesaplamalarda boşlukların dikkate alınmaması çok garip.
Chase Holland

1
Boşlukları "3" gibi bir kukla karakterle değiştirmek tam yüksekliği döndürür
Abuzar Amin

1
bu benim kariyerimi ve KG ekibiyle olan ilişkimi kurtardı. Sana bir bira borcum var !!
lucaslt89

14

Hızlı dört versiyon

let string = "A great test string."
let font = UIFont.systemFont(ofSize: 14)
let attributes: [NSAttributedStringKey: Any] = [.font: font]
let attributedString = NSAttributedString(string: string, attributes: attributes)
let largestSize = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)

//Option one (best option)
let framesetter = CTFramesetterCreateWithAttributedString(attributedString)
let textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRange(), nil, largestSize, nil)

//Option two
let textSize = (string as NSString).boundingRect(with: largestSize, options: [.usesLineFragmentOrigin , .usesFontLeading], attributes: attributes, context: nil).size

//Option three
let textSize = attributedString.boundingRect(with: largestSize, options: [.usesLineFragmentOrigin , .usesFontLeading], context: nil).size

CTFramesetter ile metnin ölçülmesi, tamsayı boyutları sağladığı ve emoji ve diğer unicode karakterleri iyi işlediği için en iyi sonucu verir.


10

Bu önerilerin hiçbirinde şansım olmadı. Benim dize unicode mermi noktaları içeriyor ve hesaplamada keder neden şüpheli. UITextView çiziminin ince işlediğini fark ettim, bu yüzden hesaplamadan yararlanmak için ona baktım. Aşağıdakileri yaptım, muhtemelen NSString çizim yöntemleri kadar optimal değil, ama en azından doğru. Sadece aramak için bir UITextView başlatmaktan biraz daha uygundur -sizeThatFits:.

NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(width, CGFLOAT_MAX)];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[layoutManager addTextContainer:textContainer];

NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:formattedString];
[textStorage addLayoutManager:layoutManager];

const CGFloat formattedStringHeight = ceilf([layoutManager usedRectForTextContainer:textContainer].size.height);

Unicode noktaları olmayabilir, dizenizin sonunda boşluklar olabilir. Yukarıdaki cevaba bakınız: stackoverflow.com/a/25941139/337934 .
SamB

9

Kuyruğu keserek sınırlayıcı kutu almak istiyorsanız, bu soru size yardımcı olabilir.

CGFloat maxTitleWidth = 200;

NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.lineBreakMode = NSLineBreakByTruncatingTail;

NSDictionary *attributes = @{NSFontAttributeName : self.textLabel.font,
                             NSParagraphStyleAttributeName: paragraph};

CGRect box = [self.textLabel.text
              boundingRectWithSize:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)
              options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
              attributes:attributes context:nil];

9

Herhangi bir NSAttributedString parçasının en azından NSFontAttributeName ve NSForegroundColorAttributeName ayarlı bir sözlük kümesine sahip olması gerektiği ortaya çıkıyor, eğer boundingRectWithSize gerçekten çalışması için!

Hiçbir yerde belgelendiğini görmüyorum.


Teşekkürler, bu benim için bir çözümdü, sadece NSFontAttributeName'in gerekli olduğunu gördüm, NSForegroundColorAttributeName
Marmoy

7

@warrenm framesetter yönteminin benim için çalışmadığını söylediğim için üzgünüm.

Bu işlev, belirli bir Genişlik için iphone / Ipad SDK'da bir NSAttributedString dizesi aralığı için gereken çerçeve boyutunu belirlememize yardımcı olabilir:

UITableView Hücrelerinin dinamik yüksekliği için kullanılabilir

- (CGSize)frameSizeForAttributedString:(NSAttributedString *)attributedString
{
    CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString((CFAttributedStringRef)attributedString);
    CGFloat width = YOUR_FIXED_WIDTH;

    CFIndex offset = 0, length;
    CGFloat y = 0;
    do {
        length = CTTypesetterSuggestLineBreak(typesetter, offset, width);
        CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(offset, length));

        CGFloat ascent, descent, leading;
        CTLineGetTypographicBounds(line, &ascent, &descent, &leading);

        CFRelease(line);

        offset += length;
        y += ascent + descent + leading;
    } while (offset < [attributedString length]);

    CFRelease(typesetter);

    return CGSizeMake(width, ceil(y));
}

HADDAD ISSA sayesinde >>> http://haddadissa.blogspot.in/2010/09/compute-needed-heigh-for-fixed-width-of.html


Herhangi bir cihazda (iPhone 4 ve 5) benim için çalışmıyor. Hem iOS7.1.2 yüklü cihazlarda hem de iOS8.3 yüklü 4s Simulator :(
sanjana

Bu herhangi bir ithalat gerekir?
jose920405

@KaranAlangat yes#import <CoreText/CoreText.h>
jose920405

7

Tercih edilen çözümün satır sonlarını işlemediğini buldum.

Bu yaklaşımın her durumda işe yaradığını gördüm:

UILabel* dummyLabel = [UILabel new];
[dummyLabel setFrame:CGRectMake(0, 0, desiredWidth, CGFLOAT_MAX)];
dummyLabel.numberOfLines = 0;
[dummyLabel setLineBreakMode:NSLineBreakByWordWrapping];
dummyLabel.attributedText = myString;
[dummyLabel sizeToFit];
CGSize requiredSize = dummyLabel.frame.size;

Benim durumumda arka plan iş parçacığı üzerinde hesaplama yapmalıyım ve UI öğelerinin kullanımına izin verilmiyor
Lubbo

4

Bu teknikleri kullanarak doğru bir boyut elde edememekle aynı problemi yaşadım ve çalışmasını sağlamak için yaklaşımımı değiştirdim.

Ben kesilmiş olmadan düzgün gösterir böylece kaydırma görünümü sığdırmaya çalışıyorum uzun bir öznitelik dize var. Metnin güvenilir bir şekilde çalışmasını sağlamak için yaptığım şey, yüksekliği hiç bir kısıtlama olarak ayarlamak değil, bunun yerine iç boyutun devralmasına izin vermekti. Şimdi metin kesilmeden doğru görüntüleniyor ve yüksekliği hesaplamak zorunda değilim.

Yüksekliği güvenilir bir şekilde almam gerekiyorsa, gizli bir görünüm ve bu kısıtlamalar yaratacağım ve kısıtlamalar uygulandıktan sonra çerçevenin yüksekliğini alacağım.


2
Bunu göstermek için bir kod örneği ekleyebilir misiniz? Teşekkürler.
Nathan Buggia

3

Oyuna biraz geç kaldım - ama Finder'da bir dosyayı düzenlemek gibi bir odak halkası yapmak için ilişkilendirilmiş bir dizenin etrafına sığacak sınırlayıcı kutuyu bulmak için çalışan bir yol bulmaya çalışıyorum. dizenin sonunda boşluklar veya dizenin içinde birden çok boşluk olduğunda denediğim her şey başarısız oldu. boundingRectWithSizebunun için de sefil bir şekilde başarısız olur CTFramesetterCreateWithAttributedString.

NSLayoutManagerAşağıdaki kodu kullanarak şimdiye kadar bulduğum tüm durumlarda hile yapmak gibi görünüyor ve mükemmel dize sınırlayan bir rekt döndürür. Bonus: Metni seçerseniz, seçimin kenarları döndürülen rektörün sınırlarına kadar gider. Aşağıdaki kod, a NSTextView.

NSLayoutManager* layout = [self layoutManager];
NSTextContainer* container = [self textContainer];

CGRect focusRingFrame = [layout boundingRectForGlyphRange:NSMakeRange(0, [[self textStorage] length]) inTextContainer:container];

2
textView.textContainerInset = UIEdgeInsetsZero;
NSString *string = @"Some string";
NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:12.0f], NSForegroundColorAttributeName:[UIColor blackColor]};
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:attributes];
[textView setAttributedText:attributedString];
CGRect textViewFrame = [textView.attributedText boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.view.frame)-8.0f, 9999.0f) options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) context:nil];
NSLog(@"%f", ceilf(textViewFrame.size.height));

Tüm yazı tiplerinde mükemmel çalışır!


1

Aynı sorunu yaşadım, ancak yükseklik kısıtlamasının doğru ayarlandığını fark ettim. Bu yüzden aşağıdakileri yaptım:

-(CGSize)MaxHeighForTextInRow:(NSString *)RowText width:(float)UITextviewWidth {

    CGSize constrainedSize = CGSizeMake(UITextviewWidth, CGFLOAT_MAX);

    NSDictionary *attributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                                          [UIFont fontWithName:@"HelveticaNeue" size:11.0], NSFontAttributeName,
                                          nil];

    NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:RowText attributes:attributesDictionary];

    CGRect requiredHeight = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];

    if (requiredHeight.size.width > UITextviewWidth) {
        requiredHeight = CGRectMake(0, 0, UITextviewWidth, requiredHeight.size.height);
    }

    return requiredHeight.size;
}

1
    NSDictionary *stringAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [UIFont systemFontOfSize:18], NSFontAttributeName,
                                      [UIColor blackColor], NSForegroundColorAttributeName,
                                      nil];

    NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:myLabel.text attributes:stringAttributes];
    myLabel.attributedText = attributedString; //this is the key!

    CGSize maximumLabelSize = CGSizeMake (screenRect.size.width - 40, CGFLOAT_MAX);

    CGRect newRect = [myLabel.text boundingRectWithSize:maximumLabelSize
                                                       options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                                    attributes:stringAttributes context:nil];

    self.myLabelHeightConstraint.constant = ceilf(newRect.size.height);

Bu sayfadaki her şeyi denedim ve hala doğru biçimlendirilmemiş bir UILabel için bir durum vardı. Aslında attributedText öğesini etikete ayarlamak sonunda sorunu çözdü.


1

Fark ettiğim bir şey, geri gelecek olan rektifin (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)contextgeçirdiğimden daha geniş bir genişliğe sahip olacağıydı. Bu olduğunda benim telim kesilecekti. Ben böyle çözdüm:

NSString *aLongString = ...
NSInteger width = //some width;            
UIFont *font = //your font;
CGRect rect = [aLongString boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX)
                                        options:(NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin)
                                     attributes:@{ NSFontAttributeName : font,
                                                   NSForegroundColorAttributeName : [UIColor whiteColor]}
                                        context:nil];

if(rect.size.width > width)
{
    return rect.size.height + font.lineHeight;
}
return rect.size.height;

Biraz daha bağlam için; Çok satırlı metnim vardı ve içinde görüntülemek için doğru yüksekliği bulmaya çalışıyordum. BoundRectWithSize bazen belirttiğimden daha geniş bir genişlik döndürüyordu, bu nedenle geçmişimi genişliğinde ve metnimi görüntülemek için hesaplanan yüksekliği kullandığımda, kesilir. RectWithSize yanlış genişliği kullandığında testten, yüksekliği kısaltacağı miktar 1 satırdı. Bu yüzden genişliğin daha büyük olup olmadığını kontrol ederdim ve eğer öyleyse, kesmeyi önlemek için yeterli alan sağlamak için font'un lineHeight'ını ekleyin.


Çizgi yüksekliği Genişlik ile nasıl etki eder?
Roi Mulia

@RoiMulia satır yüksekliği genişliği etkilemez. Yanıtımı, bunun hatamı nasıl düzelttiği konusunda daha fazla bağlam sağlamak için güncelledim.
odyth

0
    NSAttributedString *attributedText =[[[NSAttributedString alloc]
                                          initWithString:joyMeComment.content
                                          attributes:@{ NSFontAttributeName: [UIFont systemFontOfSize:TextFont]}] autorelease];

    CGRect paragraphRect =
    [attributedText boundingRectWithSize:CGSizeMake(kWith, CGFLOAT_MAX)
                                 options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                 context:nil];
    contentSize = paragraphRect.size;

    contentSize.size.height+=10;
    label.frame=contentSize;

etiketin çerçevesi 10 eklemezse bu yöntem asla işe yaramaz! Umarım bu size yardımcı olabilir! iyi şanslar.


0
Add Following methods in ur code for getting correct size of attribute string 
1.
    - (CGFloat)findHeightForText:(NSAttributedString *)text havingWidth:(CGFloat)widthValue andFont:(UIFont *)font
 {
    UITextView *textView = [[UITextView alloc] init];
    [textView setAttributedText:text];
    [textView setFont:font];
    CGSize size = [textView sizeThatFits:CGSizeMake(widthValue, FLT_MAX)];
    return size.height;

}

2. Call on heightForRowAtIndexPath method
     int h = [self findHeightForText:attrString havingWidth:yourScreenWidth andFont:urFont];

Benim durumumda arka plan iş parçacığı üzerinde hesaplama yapmalıyım ve UI öğelerinin kullanımına izin verilmiyor
Lubbo

0

Aynı sorunu yaşadığım için düşüncelerimi eklemek istiyorum.

Kullanıyordum UITextView daha hoş metin hizalama (o zaman mevcut değildi haklı) vardı beriUILabel , ama etkileşimli-kaydırılamaz "simüle" amacıyla UILabel, tamamen kaydırma, sıçrayan ve kullanıcı etkileşimi kapatmak istiyorum .

Tabii ki, sorun metnin dinamik olmasıydı ve genişlik sabitlenirken, her yeni metin değeri ayarladığımda yükseklik yeniden hesaplanmalıdır.

boundingRectWithSizebenim için hiç iyi çalışmadı, görebildiğim kadarıyla UITextViewüstüne biraz marj ekliyordu.boundingRectWithSize bir sayıya girmeyecek bir , dolayısıyla, alınan yükseklik boundingRectWithSizeolması gerekenden daha küçüktü.

Metin hızlı bir şekilde güncellenmeyeceğinden, en çok 2-3 saniyede bir güncellenebilecek bazı bilgiler için kullanılır, aşağıdaki yaklaşıma karar verdim:

/* This f is nested in a custom UIView-inherited class that is built using xib file */
-(void) setTextAndAutoSize:(NSString*)text inTextView:(UITextView*)tv
{
    CGFloat msgWidth = tv.frame.size.width; // get target's width

    // Make "test" UITextView to calculate correct size
    UITextView *temp = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, msgWidth, 300)]; // we set some height, really doesn't matter, just put some value like this one.
    // Set all font and text related parameters to be exact as the ones in targeted text view
    [temp setFont:tv.font];
    [temp setTextAlignment:tv.textAlignment];
    [temp setTextColor:tv.textColor];
    [temp setText:text];

    // Ask for size that fits :P
    CGSize tv_size = [temp sizeThatFits:CGSizeMake(msgWidth, 300)];

    // kill this "test" UITextView, it's purpose is over
    [temp release];
    temp = nil;

    // apply calculated size. if calcualted width differs, I choose to ignore it anyway and use only height because I want to have width absolutely fixed to designed value
    tv.frame = CGRectMake(tv.frame.origin.x, tv.frame.origin.y, msgWidth, tv_size.height );
}

* Yukarıdaki kod doğrudan kaynağımdan kopyalanmadı, ayarlamak için / bu makale için gerekli olmayan diğer şeylerden temizlemek zorunda kaldı. Kopyala-yapıştır-ve-işe yarayacak-kod için almayın.

Açık olan dezavantajı, her çağrı için ayırma ve serbest bırakma olmasıdır.

Ama avantajı boundingRectWithSize metin ve hesaplamanın çizer nasıl arasındaki uyumluluk bağlı kaçındıklarını olduğunu o boyutu ve metin çizim uygulaması UITextView(ya UILabelda sadece değiştirmek kullanabilirsiniz UITextViewile UILabel). Apple'ın sahip olabileceği herhangi bir "hata" bu şekilde önlenir.

PS Görünüşe göre bu "temp" gerekmez UITextViewve sadece sizeThatFitsdoğrudan hedefe sorabilirsiniz , ancak bu benim için işe yaramadı. Mantık, çalışması gerektiğini söylese ve geçici UITextViewolarak tahsis / serbest bırakmaya gerek yoktur, ancak gerekmedi. Ancak bu çözüm, girebileceğim herhangi bir metin için kusursuz çalıştı.


Benim durumumda arka plan iş parçacığı üzerinde hesaplama yapmalıyım ve UI öğelerinin kullanımına izin verilmiyor
Lubbo

0

Tamam, bu hata ayıklama için çok zaman harcadım. Tarafından tanımlanan maksimum metin yüksekliğininboundingRectWithSize metin görüntülenmesine izin verilenUITextView çerçeve boyutundan daha düşük olduğunu .

Benim durumumda kare en fazla 140pt, ancak UITextView metinleri en fazla 131pt tolere ediyor.

Ben bunu anlamak zorunda ve "gerçek" maksimum yüksekliği sabit kod.

İşte benim çözümüm:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    NSString *proposedText = [textView.text stringByReplacingCharactersInRange:range withString:text];
    NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:proposedText];
    CGRect boundingRect;
    CGFloat maxFontSize = 100;
    CGFloat minFontSize = 30;
    CGFloat fontSize = maxFontSize + 1;
    BOOL fit;
    NSLog(@"Trying text: \"%@\"", proposedText);
    do {
        fontSize -= 1;
        //XXX Seems like trailing whitespaces count for 0. find a workaround
        [attributedText addAttribute:NSFontAttributeName value:[textView.font fontWithSize:fontSize] range:NSMakeRange(0, attributedText.length)];
        CGFloat padding = textView.textContainer.lineFragmentPadding;
        CGSize boundingSize = CGSizeMake(textView.frame.size.width - padding * 2, CGFLOAT_MAX);
        boundingRect = [attributedText boundingRectWithSize:boundingSize options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading context:nil];
        NSLog(@"bounding rect for font %f is %@; (max is %f %f). Padding: %f", fontSize, NSStringFromCGRect(boundingRect), textView.frame.size.width, 148.0, padding);
        fit =  boundingRect.size.height <= 131;
    } while (!fit && fontSize > minFontSize);
    if (fit) {
        self.textView.font = [self.textView.font fontWithSize:fontSize];
        NSLog(@"Fit!");
    } else {
        NSLog(@"No fit");
    }
    return fit;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.