limitingRectWithSize для повернення NSAttributedString неправильного розміру


151

Я намагаюсь отримати прямоточку для атрибутированной строки, але виклик limitingRectWithSize не поважає розмір, який я передаю, і повертає пряму точку з висотою однієї лінії на відміну від великої висоти (це довга струна). Я експериментував, передаючи дуже велике значення для висоти, а також 0, як у наведеному нижче коді, але пряма повертається завжди однакова.

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

Це зламано, чи мені потрібно зробити щось інше, щоб він повернув пряму для обгорнутого тексту?


5
Чи трапляється у вас стиль абзацу з обрізанням / відсіканням lineBreakMode?
danyowdee

1
якщо ви читаєте це через те, що вимірювання / обгортання UILabel до неправильної ширини, подивіться на stackoverflow.com/questions/46200027/… . зокрема, встановивши NSAllowsDefaultLineBreakStrategy на false під час запуску програми.
eric

Відповіді:


312

Схоже, ви не надали правильних варіантів. Для обгортання етикеток вкажіть щонайменше:

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

Примітка: якщо ширина оригінального тексту менше 300.f не буде обгортання рядків, тому переконайтесь, що розмір зв'язаного зображення є правильним, інакше ви все одно отримаєте неправильні результати.


25
Кожна людина повинна подивитися на цю відповідь, бо вона працює. Можливо, для цього методу потрібна більш чітка документація та загальнопризначені рядки; не очевидно, куди потрібно шукати.
macserv

31
Увага! Це не завжди працює! Іноді повернута ширина більша за ширину параметра розміру. Документація методу Even Apples зазначає: "Обмеження, які ви вказуєте в параметрі розміру, є керівництвом для рендерінгу щодо розміру рядка. Однак фактичний обмежувальний прямокутник, повернутий цим методом, може бути більшим, ніж обмеження, якщо потрібно додаткове місце для вивести весь рядок. "
Клаас

75
API для NSAttributedString і limitingRectWithSize абсолютно шокує.
мальхал

27
Інша проблема полягає в тому, що висота (і, мабуть, ширина), що повертається в абзаціRect, майже завжди є дробовим значенням. Таким чином, може виявитися висота 141,3. Вам потрібно використовувати результат, ceilf(paragraphRect.size.height)щоб він округлювався. Я весь час це забуваю і дивуюсь, чому мої етикетки все ще обрізають.
jamone

39
Я завжди обгортаю свої boundingRectWithSize:обчислення в CGRectIntegral (), що CGRectIntegral rounds the rectangle’s origin downward and its size upward to the nearest whole integersв цьому випадку округлятиме висоту та ширину, щоб забезпечити відсутність відсікання, якщо значення дробу або ширини є дробовим.
runmad

47

З якоїсь причини, limitingRectWithSize завжди повертає неправильний розмір. Я придумав рішення. Існує метод для UItextView -sizeThatFits, який повертає належний розмір для набору тексту. Таким чином, замість використання limitingRectWithSize, створіть UITextView з випадковим кадром та викличте його sizeThatFits із відповідною шириною та висотою CGFLOAT_MAX. Він повертає розмір, який матиме належну висоту.

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

Якщо ви обчислюєте розмір у циклі часу, не забудьте додати, що в пулі автовипуску, оскільки буде створено n кількість створених UITextView, пам’ять часу роботи програми збільшиться, якщо ми не будемо використовувати авторелізери.


1
Це працює. Інші способи, які я спробував, ніколи не змогли дістатися до роботи. Я думаю, що моя проблема випливає зі складності атрибутованого рядка, який я призначив. Він надходив з RTF-файлу і використовував різноманітні шрифти, міжрядкові інтервали, навіть тіні, і незважаючи на використання UsesLineFragmentOrigin та UsesFontLeading, я ніколи не міг отримати результат, який був достатньо високим, і проблема була найгіршою, чим довше отримана відповідна рядок. Я здогадуюсь, що для відносно простих рядків може працювати метод limitingRectWithSize . Їм справді потрібен кращий метод роботи з цим, imo.
Джон Бушнелл

ти не вважаєш, що створити UITextViewстільки часу, скільки heightForRowAtIndexPathвикликаєш метод, зайве ? потрібен час
Янос

Я згоден з тобою @ János, але це було єдине рішення, яке працювало для мене.
шоан

btw Я попросив належного рішення: stackoverflow.com/questions/32495744/…
János

це єдине рішення, яке працює. Ця проблема має 9 років, і Apple, очевидно, не хоче вирішувати це, або їх інженери слабкі, щоб знайти рішення для цього. Ми говоримо тут про iOS 11, який все ще несе ту саму помилку.
Качка

31

Ед Макманус, безумовно, надав ключ до того, щоб це спрацювало. Я знайшов випадок, який не працює

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

прямої висоти прямокутник не матиме. Зауважте, що ініціалізація AnotherString (яка додається до рядка ) була ініціалізована без словника атрибутів. Це законний ініціалізатор для AnotherString, але limitingRectWithSize : не дає точного розміру в цьому випадку.


33
Дякую! Виявляється, КОЖНА частина NSAttributedString повинна мати словниковий набір з принаймні NSFontAttributeName та NSForegroundColorAttributeName, якщо ви хочете, щоб факти обмежувалиRectWithSize насправді працювали! Я ніде цього не бачу документально.
Бен Уілер

3
Зверніть увагу, що також видається, що різні NSAttributedStrings, які поєднуються для створення єдиного, повинні використовувати всі словник SAME (і, таким чином, той самий шрифт) для того, щоб borderingRectWithSize працював правильно!
Бен Уілер

1
Це рішення дуже схоже на рішення, яке я використовую для моєї категорії UILabel для "зменшення до розміру". Ключовим моментом, щоб я працював над цим, було створення обмежувального прямокутника з висотою FLT_MAX. Без цього я, здавалося б, отримав прямокутник лише для одного рядка.
dre1138

+ 1 сек. Це мене справді отримало, тому спасибі за те, що відпрацювали хлопці.
Векс

У мене була ця проблема. Я використовував пробіли та нові рядки NSMutableAttributedStringбез атрибутів, і я отримував неправильний розмір.
вбеженар

28

Моє остаточне рішення після тривалого дослідження:
- boundingRectWithSizeфункція повертає правильний розмір лише для безперебійної послідовності символів! Якщо рядок містить пробіли чи щось інше (Apple називається "Деякі з гліфів") - неможливо отримати фактичний розмір прямої частини, необхідний для відображення тексту!
Я замінив пробіли в своїх рядках літерами і одразу отримав правильний результат.

Apple каже тут: https://developer.apple.com/documentation/foundation/nsstring/1524729-boundingrectwithsize

"Цей метод повертає фактичні межі гліфів у рядку. Деякі з гліфів (наприклад, пробіли) дозволяють перекривати обмеження макета, визначені розміром, що передається, тому в деяких випадках значення ширини компонента розміру повернене CGRectможе перевищувати значення ширини параметра розміру. "

Тому необхідно знайти інший спосіб розрахунку фактичної прямої ...


Після тривалого розслідування рішення нарешті знайдено !!! Я не впевнений, що це буде добре для всіх випадків, пов’язаних із цим UITextView, але головне та важливе було виявлено!

boundingRectWithSizeФункція також, як і CTFramesetterSuggestFrameSizeWithConstraints(та багато інших методів), буде обчислювати розмір та частину тексту правильними, коли використовується правильний прямокутник. Наприклад - UITextViewмає textView.bounds.size.width- і це значення не є фактичним прямокутником, який використовує система при малюванні тексту UITextView.

Я знайшов дуже цікавий параметр і провів простий розрахунок у коді:

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

І магія працює - всі мої тексти зараз обчислені правильно! Насолоджуйтесь!


1
дріждж, я помітив, що теж не тримає обмежень на ширину, він завжди виходить більшим. Це насправді не круто, вони застаріли методом роботи, і тепер ми маємо справу з цією лайнею.
Борис Гафуров

1
Ви, пане, мій новий герой! Це зводило мене з розуму. Я встановлюю вставки textContainers, тому довелося використовувати textView.textContainer.size.width замість textView.bounds.size.witdh.
GCBenson

Я не міг проголосувати це досить. Так дивно, що проміжки не враховуються в обмежуючих обчисленнях.
Чейз Голландія

1
Замінивши пробіли на якийсь манекенний символ на зразок "3" повертає точну висоту
Абузар Амін

1
це врятувало мою кар’єру та мої стосунки із командою з QA. Я завдячую тобі людиною пива !!
lucaslt89

14

Швидка чотири версія

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 працює найкраще, оскільки він забезпечує цілі розміри та добре обробляє емоджи та інші символи unicode.


10

Мені не пощастило з жодною з цих пропозицій. Мій рядок містив точки кулі Unicode, і я підозрюю, що вони викликали горе в обчисленні. Я помітив, що UITextView чудово поводиться з кресленням, тому я придивився до цього, щоб використати його обчислення. Я зробив наступне, що, мабуть, не настільки оптимально, як методи малювання NSString, але принаймні це точно. Це також трохи оптимальніше, ніж ініціалізація UITextView просто для виклику -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, можливо, це були пробіли в кінці рядка. Дивіться відповідь вище: stackoverflow.com/a/25941139/337934 .
СамБ

9

Якщо ви хочете отримати обмежувальний ящик обрізанням хвоста, це питання може вам допомогти.

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

Виявляється, КОЖНА частина NSAttributedString повинна мати набір словника з принаймні NSFontAttributeName та NSForegroundColorAttributeName, якщо ви хочете, щоб факти обмежувалиRectWithSize насправді працювали!

Я ніде цього не бачу документально.


Дякую, це рішення було для мене, за винятком того, що я виявив, що потрібен лише NSFontAttributeName, а не NSForegroundColorAttributeName
Marmoy

7

@warrenm Вибачте, що метод framesetter не працював для мене.

Я отримав це. Ця функція може допомогти нам визначити розмір кадру, необхідний для діапазону рядків NSAttributedString в iphone / Ipad SDK для заданої ширини:

Його можна використовувати для динамічної висоти комірок UITableView

- (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 >>> http://haddadissa.blogspot.in/2010/09/compute-needed-heigh-for-fixed-width-of.html


Не працює для мене на жодному пристрої (iPhone 4 і 5). Обидва на пристрої з iOS7.1.2 і 4s Simulator з iOS8.3 :(
sanjana

Цьому потрібен імпорт?
jose920405

@KaranAlangat так#import <CoreText/CoreText.h>
jose920405

7

Я виявив, що бажане рішення не обробляє розриви ліній.

Я виявив, що такий підхід працює у всіх випадках:

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;

У моєму випадку я повинен робити розрахунок на фоновій нитці, а використання елементів інтерфейсу заборонено
Lubbo

4

У мене була така ж проблема з тим, щоб не отримати точного розміру за допомогою цих методів, і я змінив свій підхід, щоб він працював.

У мене є довгий атрибутивний рядок, який я намагався вписати у вигляд прокрутки, щоб він відображався належним чином без врізання. Що я зробив для того, щоб текст працював надійно, це не встановлювати висоту взагалі як обмеження, а натомість дозволяв внутрішньому розміру брати верх. Тепер текст відображається правильно, не обрізаючи, і не потрібно обчислювати висоту.

Я вважаю, що якщо мені потрібно було надійно досягти висоти, я створив би прихований вид і ці обмеження і отримав би висоту кадру після застосування обмежень.


2
Чи можете ви додати зразок коду, щоб продемонструвати це? Дякую.
Натан Будгія

3

Я трохи запізнююсь на гру - але я намагався знайти спосіб, який працює, щоб знайти обмежувальне поле, яке буде розміщуватися навколо атрибутованої рядка, щоб зробити кільце фокусування, як редагування файлу в Finder. все, що я спробував, не вдалося, коли в кінці рядка є пробіли або кілька пробілів всередині рядка. boundingRectWithSizeне вдається і для цього CTFramesetterCreateWithAttributedString.

Використання NSLayoutManagerнаступного коду, здається, виконує трюк у всіх знайдених нами випадках і повертає пряму, яка ідеально поєднує рядок. Бонус: якщо ви вибираєте текст, краї виділення йдуть прямо до меж повернутої прямої. У наведеному нижче коді використовується layoutManager від 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));

Працює на всіх шрифтах ідеально!


1

У мене була така ж проблема, але я визнав, що обмежена висота встановлена ​​правильно. Тому я зробив наступне:

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

Я спробував усе на цій сторінці, і все ще був один випадок для UILabel, який не форматувався правильно. Власне встановлення attributedText на етикетці остаточно вирішило проблему.


1

Одне, що я помітив, - це те, що пряма частина, яка повернулася (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)contextб, матиме більшу ширину, ніж те, про що я перейшов. Коли це сталося, моя струна буде усічена. Я вирішив це так:

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;

Ще дещо контекст; У мене був багаторядковий текст, і я намагався знайти потрібну висоту для його відображення. урізав би. Під час тестування, коли limitingRectWithSize використовував неправильну ширину, величина, на яку вона зробить коротку висоту, склала 1 рядок. Тож я би перевірив, чи більша ширина, і якщо так, додайте рядок шрифтуHeight, щоб забезпечити достатньо місця, щоб уникнути усікання.


Як впливає висота лінії на ширину?
Рой Мулія

Висота лінії @RoiMulia не впливає на ширину. Я оновив свою відповідь, щоб надати більше контексту щодо того, як це виправило мою помилку.
одіт

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;

якщо рамка мітки не додати 10, цей спосіб ніколи не буде працювати! сподіваюся, що це може вам допомогти! goog удача.


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

У моєму випадку я повинен робити розрахунок на фоновій нитці, а використання елементів інтерфейсу заборонено
Lubbo

0

Я хотів би додати свої думки, оскільки у мене була точно така ж проблема.

Я використовував, UITextViewоскільки в ній було приємніше вирівнювання тексту (виправдати, яке на той момент було недоступне в UILabel), але для того, щоб "імітувати" неінтерактивне-не прокручуване UILabel, я б відключив повністю прокручування, підстрибування та взаємодію з користувачем .

Звичайно, проблема полягала в тому, що текст був динамічним, і хоча фіксується ширина, висоту слід перераховувати кожного разу, коли я встановлював би нове значення тексту.

boundingRectWithSizeМені зовсім не вдалося, з того, що я міг бачити, UITextViewдодав верхню частину поля, яка boundingRectWithSizeне піддавалася б підрахунку, отже, отримана висота boundingRectWithSizeбула меншою, ніж повинна бути.

Оскільки текст не повинен був швидко оновлюватися, він використовується лише для деякої інформації, яка може оновлюватись кожні 2-3 секунди найбільше, я вирішив наступний підхід:

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

* Наведений вище код не скопіюється безпосередньо з мого джерела, я мусив його відкоригувати / очистити від купки інших речей, не потрібних для цієї статті. Не приймайте його за код "копіювати-вставляй-і-працюй".

Очевидним недоліком є ​​те, що він має розподіл та випуск для кожного дзвінка.

Але, перевага полягає в тому, що ви уникнете в залежності від сумісності між тим, як boundingRectWithSize малює текст і обчислив його розмір і реалізація тексту креслення в UITextView(або UILabelякі також можна використовувати просто замінити UITextViewз UILabel). Таким чином можна уникнути будь-яких «помилок» у Apple.

PS Здавалося б, вам не потрібна ця "темп", UITextViewа ви можете просто запитати sizeThatFitsбезпосередньо у цілі, однак це не спрацювало для мене. Хоча логіка скаже, що вона повинна працювати, а розподіл / звільнення тимчасових UITextViewне потрібне, це не було. Але це рішення спрацювало бездоганно для будь-якого тексту, який я б задав.


У моєму випадку я повинен робити розрахунок на фоновій нитці, а використання елементів інтерфейсу заборонено
Lubbo

0

Гаразд, я витратив багато часу на налагодження цього. Я з'ясував, що максимальна висота тексту, визначена boundingRectWithSizeдозволеним відображенням тексту моїм, UITextViewбула меншою за розмір кадру.

У моєму випадку кадр становить максимум 140pt, але тексти UITextView терплять не більше 131pt.

Мені довелося зрозуміти це вручну і жорстко кодувати "справжню" максимальну висоту.

Ось моє рішення:

- (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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.