Как потерять поля / отступы в UITextView? - PullRequest
442 голосов
/ 14 апреля 2009

У меня есть UITextView в моем приложении iOS, которое отображает большой объем текста.

Затем я выполняю постраничный просмотр этого текста с помощью параметра поля смещения UITextView.

Моя проблема в том, что заполнение UITextView сбивает с толку мои расчеты, так как оно кажется различным в зависимости от размера шрифта и используемого шрифта.

Поэтому я задаю вопрос: возможно ли удалить отступы, окружающие содержимое UITextView?

С нетерпением ждем ваших ответов!

Ответы [ 20 ]

758 голосов
/ 24 сентября 2013

Для iOS 7.0 я обнаружил, что трюк contentInset больше не работает. Это код, который я использовал, чтобы избавиться от полей / отступов в iOS 7.

Приводит левый край текста к левому краю контейнера:

textView.textContainer.lineFragmentPadding = 0

Это приводит к тому, что верх текста выравнивается по верху контейнера

textView.textContainerInset = .zero

Обе строки необходимы для полного удаления полей / отступов.

279 голосов
/ 20 февраля 2017

актуально для 2019

Это одна из самых глупых ошибок в iOS.

Класс ниже, UITextViewFixed обычно является разумным решением.

Вот класс:

@IBDesignable class UITextViewFixed: UITextView {
    override func layoutSubviews() {
        super.layoutSubviews()
        setup()
    }
    func setup() {
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0
    }
}

Не забудьте отключить scrollEnabled в Инспекторе!

  1. Решение правильно работает в раскадровке

  2. Решение работает правильно во время выполнения

Вот и все, все готово.

Как правило, это все, что вам нужно в большинстве случаев .

Даже если вы меняете высоту текстового представления на лету, это обычно делает все, что вам нужно.

(Типичный пример изменения высоты на лету, изменение ее по мере ввода пользователем.)

Вот сломанный UITextView от Apple ...

screenshot of IB with UITextView

Вот UITextViewFixed:

screenshot of IB with UITextViewFixed

Обратите внимание, что, конечно, вы должны

отключить scrollEnabled в Инспекторе!

Не забудьте отключить scrollEnabled! :)


Некоторые дальнейшие проблемы

(1) В некоторых необычных случаях - например, в некоторых случаях гибких представлений таблицы высоты ячеек с динамически изменяющейся высотой - Apple делает странную вещь: Они добавляют дополнительное пространство внизу . Нет, правда! Это должно быть одной из самых ярких вещей в iOS.

Вот "быстрое исправление", которое нужно добавить, что обычно помогает с этим безумием.

...
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0

        // this is not ideal, but you can sometimes use this
        // to fix the "extra space at the bottom" insanity
        var b = bounds
        let h = sizeThatFits(CGSize(
           width: bounds.size.width,
           height: CGFloat.greatestFiniteMagnitude)
       ).height
       b.size.height = h
       bounds = b
 ...

(2) Иногда, чтобы исправить еще одну тонкую ошибку Apple, вы должны добавить это:

override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
    super.setContentOffset(contentOffset, animated: false)
}

(3) Возможно, мы должны добавить:

contentInset = UIEdgeInsets.zero

сразу после .lineFragmentPadding = 0 в UITextViewFixed.

Однако ... верьте или нет ... это просто не работает в текущей iOS! (Проверено в 2019.) Возможно, потребуется добавить эту строку в будущем.

Тот факт, что UITextView не работает в iOS, является одной из самых странных вещей во всех мобильных вычислениях. Десятилетие этого вопроса, и он до сих пор не решен!

Наконец, вот несколько похожий совет для текста Поле : https://stackoverflow.com/a/43099816/294884

251 голосов
/ 26 июля 2009

Этот обходной путь был написан в 2009 году, когда была выпущена IOS 3.0. Больше не применяется.

Я столкнулся с точно такой же проблемой, в конце концов мне пришлось закончить, используя

nameField.contentInset = UIEdgeInsetsMake(-4,-8,0,0);

, где nameField является UITextView. Я использовал шрифт Helvetica 16 пунктов. Это единственное нестандартное решение для конкретного размера поля, которое я рисовал. Это делает левое смещение заподлицо с левой стороной, а верхнее смещение там, где я хочу, для поля, в котором оно нарисовано.

Кроме того, похоже, что это применимо только к UITextViews, где вы используете стандартное значение, т. Е.

nameField.textAlignment = NSTextAlignmentLeft;

Например, выровняйтесь вправо, и UIEdgeInsetsMake, кажется, вообще не влияет на правый край.

По крайней мере, использование свойства .contentInset позволяет размещать поля с «правильными» позициями и учитывать отклонения, не компенсируя UITextViews.

70 голосов
/ 04 февраля 2015

Опираясь на некоторые из хороших ответов, которые уже даны, вот чисто решение на основе Interface Builder, которое использует пользовательские атрибуты времени выполнения и работает в iOS 7.0 +:

enter image description here

45 голосов
/ 08 мая 2012

На iOS 5 UIEdgeInsetsMake(-8,-8,-8,-8); вроде бы отлично работает.

39 голосов
/ 24 апреля 2016

Я бы определенно избегал любых ответов, связанных с жестко заданными значениями, поскольку фактические поля могут изменяться в зависимости от настроек размера шрифта пользователя и т. Д.

Вот ответ @ user1687195, написанный без изменения textContainer.lineFragmentPadding (поскольку состояние docs не предназначено для этого).

Это прекрасно работает для iOS 7 и более поздних версий.

self.textView.textContainerInset = UIEdgeInsetsMake(
                                      0, 
                                      -self.textView.textContainer.lineFragmentPadding, 
                                      0, 
                                      -self.textView.textContainer.lineFragmentPadding);

Это фактически тот же результат, только немного чище, поскольку он не использует свойство lineFragmentPadding.

18 голосов
/ 05 сентября 2013

Решение Storyboard или Interface Builder с использованием пользовательских атрибутов времени выполнения. Обновление. Добавлены скриншоты iOS7.1 и iOS6.1 с contentInset = {{-10, -5}, {0, 0}} enter image description here enter image description here

15 голосов
/ 26 июня 2015

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

Размер текстового содержимого

Быстрый способ вычислить размер текста внутри UITextView - использовать NSLayoutManager:

UITextView *textView;
CGSize textSize = [textView usedRectForTextContainer:textView.textContainer].size;

Это дает общий прокручиваемый контент, который может быть больше, чем кадр UITextView. Я обнаружил, что это намного точнее, чем textView.contentSize, поскольку он фактически вычисляет, сколько места занимает текст. Например, дано пустое UITextView:

textView.frame.size = (width=246, height=50)
textSize = (width=10, height=16.701999999999998)
textView.contentSize = (width=246, height=33)
textView.textContainerInset = (top=8, left=0, bottom=8, right=0)

Высота линии

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

UITextView *textView;
CGFloat lineHeight = textView.font.lineHeight;

Расчет видимого размера текста

Определение объема фактически видимого текста важно для обработки эффекта «подкачки». UITextView имеет свойство, называемое textContainerInset, которое фактически является границей между фактическим UITextView.frame и самим текстом. Для расчета реальной высоты видимой рамки вы можете выполнить следующие расчеты:

UITextView *textView;
CGFloat textViewHeight = textView.frame.size.height;
UIEdgeInsets textInsets = textView.textContainerInset;
CGFloat textHeight = textViewHeight - textInsets.top - textInsets.bottom;

Определение размера подкачки

Наконец, теперь, когда у вас есть видимый размер текста и содержание, вы можете быстро определить, какими должны быть ваши смещения, вычитая textHeight из textSize:

// where n is the page number you want
CGFloat pageOffsetY = textSize - textHeight * (n - 1);
textView.contentOffset = CGPointMake(textView.contentOffset.x, pageOffsetY);

// examples
CGFloat page1Offset = 0;
CGFloat page2Offset = textSize - textHeight
CGFloat page3Offset = textSize - textHeight * 2

Используя все эти методы, я не трогал свои вкладыши и мог перейти к каретке или куда угодно в тексте, который я хочу.

11 голосов
/ 05 мая 2015

вы можете использовать textContainerInset свойство UITextView:

textView.textContainerInset = UIEdgeInsetsMake (10, 10, 10, 10);

(сверху, слева, снизу, справа)

9 голосов
/ 12 октября 2016

Для iOS 10 следующая строка работает для удаления верхнего и нижнего отступов.

captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
...