SizeWithFont не дает правильной высоты для UITextView, если в тексте завершена длинная строка

Есть ли способ получить правильный размер NSString, используя:

- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode

который не будет сброшен на 2 или 3 сотни символьных строк. На данный момент, если я попытаюсь использовать этот метод для этих длинных строк, он неправильно вычисляет их, и я получаю много пробелов в нижней части UITextView.

Я пробовал использовать UILineBreakModeWordWrap и UILineBreakModeCharacterWrap.

изменение размера выполняется в

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    CGFloat     result = 44.0f;
    NSString*   text = nil;
    CGFloat     width = 0;
    CGFloat     tableViewWidth;
    CGRect      bounds = [UIScreen mainScreen].bounds;

    tableViewWidth = bounds.size.width;


    width = tableViewWidth - 150;       

    text = stringWithLongWords;

    if (text) {
        CGSize      textSize = { width, 20000.0f };     
        CGSize      size = [text sizeWithFont:[UIFont systemFontOfSize:10.0f] constrainedToSize:textSize lineBreakMode:UILineBreakModeWordWrap];

        size.height += 50.0f;               
        result = MAX(size.height, 44.0f+30.0f); 
    }

    return result;
}

Ответ 1

UITextView не совсем похож на UILabel, завернутый в UIScrollView. Он имеет межстрочный интервал, отличный от размера шрифта и полей, которые sizeWithFont:constrainedToSize:linkBreakMode: не учитывает.

Зная размер шрифта, вы могли бы вычислить количество строк и учесть интервал между строками. Вы можете угадать на полях и попытаться обмануть sizeWithFont:, чтобы дать более полезный ответ.

Популярные решения выглядят так:

  • просто используйте UILabel, если вам не нужна функция UITextView

  • если вам нужно hyperlinks, наложить UIButtons, которые выглядят как гиперссылки над UILabel

  • используйте внеэкранный UITextView и его метод sizeToFit, чтобы получить реальный ответ

Мне не повезло с третьим вариантом, но похоже, что он должен работать, поэтому, возможно, я сделал что-то не так.

Я попытаюсь использовать кнопки UILabel и overlaying для гиперссылок. Посмотрим, как это получится.

Если это терпит неудачу, всегда найдется вариант, взятый Лорен Брихтер (из славы Tweetie): потяните все в UIView самостоятельно, используя CoreGraphics.

Удачи!

Ответ 2

Отметьте это сообщение Как настроить UITextView для его содержимого?

Похоже, что textView.contentSize.height должен работать (с оговоркой, что правильный contentSize доступен только после добавления UITextView в представление с помощью addSubview)

Ответ 3

Вы сказали, что у вас есть UITableView с разной высотой. Вы установили идентификатор повторного использования в одно и то же для всех ячеек? Возможно, что старые клетки с уже установленной высотой повторяются. Если это проблема, вы должны изменить размер ячейки снова при повторном использовании.

Ответ 4

Лучшим решением, которое я нашел до сих пор, является наличие отдельного скрытого UITextView с теми же настройками шрифта и установка его текста. После этого ее размер должен быть точным.

Ответ 5

Ширина, которую вы используете, - это ширина для вашего UITextView... но вас не интересует эта ширина, вы занимаетесь шириной фактической текстовой области, вложенной в текстовое представление.

UITextViews по умолчанию имеют прописку вокруг своих границ, чтобы создать промежуток между напечатанным текстом и краем UITextView на несколько пикселей в ширину (и длинный для вершины)... Чтобы получить правильный размер, вы должны Использовать

textView.frame.size.width

но,

textView.frame.size.width-(textView.contentInset.left+textView.contentInset.right+textView.textContainerInset.left+textView.textContainerInset.right+textView.textContainer.lineFragmentPadding/*left*/+textView.textContainer.lineFragmentPadding/*right*/)

^ Которая берет ширину UITextView и вычитает все отступы, поэтому вы остаетесь с шириной только текстовой области, имеющей тип.

То же самое касается высоты, за исключением того, что lineFragmentPadding не имеет дна, поэтому вы вычитаете его один раз вместо двух.


Последний код выглядит примерно так:

    CGSize textViewContentSize = CGSizeMake(theTextView.frame.size.width-(theTextView.contentInset.left+theTextView.contentInset.right+theTextView.textContainerInset.left+theTextView.textContainerInset.right+theTextView.textContainer.lineFragmentPadding/*left*/+theTextView.textContainer.lineFragmentPadding/*right*/), theTextView.frame.size.height-(theTextView.contentInset.top+theTextView.contentInset.bottom+theTextView.textContainerInset.top+theTextView.textContainerInset.bottom+theTextView.textContainer.lineFragmentPadding/*top*//*+theTextView.textContainer.lineFragmentPadding*//*there is no bottom padding*/));

    CGSize calculatedSize = [theTextView.text sizeWithFont:theTextView.font
                                          constrainedToSize:textViewContentSize
                                              lineBreakMode:NSLineBreakByWordWrapping];


    CGSize adjustedSize = CGSizeMake(ceilf(calculatedSize.width), ceilf(calculatedSize.height));

Ответ 6

Вдохновленный @MrNickBarker ответ, вот мое решение:

CGFloat width = 280.0f;

UITextView *t = [[UITextView alloc] init];
[t setFont:[UIFont systemFontOfSize:17]];
[label setText:@"some short or long text, works both"];

CGRect frame = CGRectMake(0, 0, width, 0);
[t setFrame:frame];
// Here the trick: after applying the 0-frame, the content size is calculated and can be used in a second invocation
frame = CGRectMake(0, 0, width, t.contentSize.height);
[t setFrame:frame];

Единственная проблема, остающаяся для меня, заключается в том, что это не работает с измененными вставками.

По-прежнему не могу поверить, что такие завихрения требуются, но поскольку -[NSString sizeWithFont:forWidth:lineBreakMode:] не учитывает вставки, paddings, поля, расстояния между линиями и т.п., кажется, что это единственное рабочее решение на данный момент (т.е. iOS 6).