Как потерять margin/padding в UITextView?

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

Затем я UITextView страницы этот текст, используя параметр отступа поля UITextView.

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

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

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

+442
14 апр. '09 в 7:42
источник поделиться
20 ответов

Обновленный на 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, является одной из самых странных вещей во всех мобильных вычислениях. Десять лет назад этот вопрос еще не решен!

Наконец, вот несколько похожий совет для текстового поля: qaru.site/questions/19323/...

+278
19 февр. '17 в 22:45
источник

Связанные вопросы


Похожие вопросы

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

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

textView.textContainer.lineFragmentPadding = 0

Это приведет к выравниванию верхней части текста с верхней частью контейнера

textView.textContainerInset = .zero

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

+758
24 сент. '13 в 17:05
источник

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

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

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

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

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

nameField.textAlignment = NSTextAlignmentLeft;

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

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

+251
26 июл. '09 в 18:59
источник

Исходя из уже полученных хороших ответов, вот чисто основанное на интерфейс Builder решение, которое использует атрибуты User Defined Runtime и работает в iOS 7.0 +:

enter image description here

+70
04 февр. '15 в 0:58
источник

В iOS 5 UIEdgeInsetsMake(-8,-8,-8,-8);, похоже, отлично работает.

+45
08 мая '12 в 12:36
источник

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

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

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

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

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

+39
24 апр. '16 в 18:36
источник

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

+18
05 сент. '13 в 9:02
источник

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

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

Быстрый способ рассчитать размер текста внутри 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

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

+15
26 июн. '15 в 15:31
источник

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

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

(верхний, левый, нижний, правый)

+11
05 мая '15 в 5:24
источник

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

captionTextView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
+9
12 окт. '16 в 6:38
источник

Вот обновленная версия Fattie очень полезный ответ. Он добавляет 2 очень важные строки, которые помогли мне получить идеальный макет, работающий на iOS 10 и 11 (и, вероятно, на более низких).
д

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

Важными строками являются два оператора translatesAutoresizingMaskIntoConstraints = <true/false>!

Это удивительно удаляет все поля во всех моих обстоятельствах!

Пока textView не является первым ответчиком, может случиться, что есть какой-то странный нижний край, который не может быть решен с помощью метода sizeThatFits, упомянутого в принятом ответе.
При внезапном нажатии на TextView странное нижнее поле исчезло, и все выглядело так, как должно, но только после того, как textView получил firstResponder.

Итак, я читаю здесь, на SO, что включение и отключение translatesAutoresizingMaskIntoConstraints помогает при установке фрейма/границ вручную между вызовами.

К счастью, это не только работает с настройкой фрейма, но и с двумя линиями setup(), зажатыми между двумя вызовами translatesAutoresizingMaskIntoConstraints!

Это, например, очень полезно при вычислении фрейма представления с помощью systemLayoutSizeFitting на UIView. Возвращает правильный размер (который раньше он не делал)!

Как и в первоначальном ответе:
Не забудьте выключить scrollEnabled в инспекторе!
Это решение работает правильно в раскадровке, а также во время выполнения.

Что это, теперь вы действительно сделали!

+6
14 февр. '18 в 14:37
источник

Последний Свифт:

self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0
+5
24 мая '18 в 7:54
источник

Для быстрого 4, Xcode 9

Используйте следующую функцию, чтобы изменить поле/дополнение текста в UITextView

public func UIEdgeInsetsMake (_ top: CGFloat, _ left: CGFloat, _ bottom: CGFloat, _ right: CGFloat) → UIEdgeInsets

поэтому в этом случае

 self.textView?.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
+3
26 окт. '17 в 16:54
источник

Здесь небольшое небольшое расширение, которое удалит маркер по умолчанию Apple из каждого текстового вида в вашем приложении.

Примечание. Интерфейс Builder по-прежнему будет показывать старую маржу, но ваше приложение будет работать так, как ожидалось.

extension UITextView {

   open override func awakeFromNib() {
      super.awakeFromNib();
      removeMargins();
   }

   /** Removes the Apple textview margins. */
   public func removeMargins() {
      self.contentInset = UIEdgeInsetsMake(
         0, -textContainer.lineFragmentPadding,
         0, -textContainer.lineFragmentPadding);
   }
}
+2
04 мар. '18 в 0:19
источник

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

+2
31 мар. '11 в 23:49
источник

Для меня (iOS 11 и Xcode 9.4.1) волшебным образом работало создание свойства UIFont.preferred(forTextStyle:UIFontTextStyle) в UIFont.preferred(forTextStyle:UIFontTextStyle) а также первого ответа, упомянутого @Fattie. Но ответ @Fattie не сработал, пока я не установил свойство textView.font, иначе UITextView продолжает работать беспорядочно.

0
17 июн. '18 в 11:20
источник

Если кто-то ищет последнюю версию Swift, то приведенный ниже код работает нормально с Xcode 10.2 и Swift 4.2

yourTextView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
0
16 мая '19 в 10:44
источник

Прокрутка textView также влияет на положение текста и делает его похожим на вертикальное центрирование. Мне удалось центрировать текст в представлении, отключив прокрутку и установив верхнюю вставку на 0:

    textView.scrollEnabled = NO;
    textView.textContainerInset = UIEdgeInsetsMake(0, textView.textContainerInset.left, textView.textContainerInset.bottom, textView.textContainerInset.right);

По какой-то причине я еще не понял, курсор по-прежнему не центрирован до начала ввода, но текст сразу начинается с начала ввода.

-1
25 дек. '16 в 19:37
источник

Я нашел еще один подход, получив представление с текстом из подпроектов UITextView и установив его в методе layoutSubview для подкласса:

- (void)layoutSubviews {
    [super layoutSubviews];

    const int textViewIndex = 1;
    UIView *textView = [self.subviews objectAtIndex:textViewIndex];
    textView.frame = CGRectMake(
                                 kStatusViewContentOffset,
                                 0.0f,
                                 self.bounds.size.width - (2.0f * kStatusViewContentOffset),
                                 self.bounds.size.height - kStatusViewContentOffset);
}
-1
28 февр. '13 в 10:17
источник
[firstNameTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];
-5
11 авг. '11 в 21:48
источник

Посмотрите другие вопросы по меткам или Задайте вопрос