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

У меня есть UITextView в моем приложении iOS, которое отображает большой объем текста. Затем я просматриваю этот текст с помощью параметра margin margin UITextView. Моя проблема заключается в том, что заполнение UITextView путает мои вычисления, поскольку они кажутся разными в зависимости от размера шрифта и шрифта, который я использую.

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

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

364
задан sniurkst 14 апр. '09 в 10:42
источник поделиться
17 ответов

В течение 2018 года

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

UITextViewFixed - обычно разумное решение:

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

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

Это решение работает правильно в раскадровке, а также во время выполнения.

Чтобы все было сделано,

В общем, , который должен быть всем, что вам нужно в большинстве случаев.

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

Вот разбитый 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 "space at 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 mess-up, вы должны добавить это:

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

(3) Можно, наверное, добавить:

contentInset = UIEdgeInsets.zero

сразу после .lineFragmentPadding = 0. Однако... он не работает в текущем iOS! Может быть необходимо добавить, что в будущих версиях ОС.

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

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

129
ответ дан Fattie 20 февр. '17 в 1:45
источник поделиться

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

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

textView.textContainer.lineFragmentPadding = 0

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

textView.textContainerInset = .zero

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

705
ответ дан user1687195 24 сент. '13 в 20:05
источник поделиться

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

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

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

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

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

nameField.textAlignment = NSTextAlignmentLeft;

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

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

251
ответ дан Michael 26 июля '09 в 21:59
источник поделиться

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

enter image description here

54
ответ дан azdev 04 февр. '15 в 3:58
источник поделиться

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

45
ответ дан Engin Kurutepe 08 мая '12 в 15:36
источник поделиться

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

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

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

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

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

36
ответ дан ldoogy 24 апр. '16 в 21:36
источник поделиться

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

16
ответ дан Uladzimir 05 сент. '13 в 12: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
ответ дан mikeho 26 июня '15 в 18:31
источник поделиться

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

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

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

11
ответ дан Himanshu padia 05 мая '15 в 8:24
источник поделиться

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

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

Обратите внимание, что изменить его на UIEdgeInset.zero с правой стороны не работает

6
ответ дан Anson Yao 12 окт. '16 в 9:38
источник поделиться

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

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

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

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

 self.textView?.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)
2
ответ дан Shan Ye 26 окт. '17 в 19:54
источник поделиться

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

2
ответ дан rp90 01 апр. '11 в 2:49
источник поделиться

Вот обновленная версия 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 в инспекторе!
Это решение работает правильно в раскадровке, а также во время выполнения.

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

0
ответ дан Fab1n 14 февр. '18 в 17:37
источник поделиться

Здесь небольшое небольшое расширение, которое удалит маркер по умолчанию 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);
   }
}
0
ответ дан Cal S 04 марта '18 в 3:19
источник поделиться

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

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

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

0
ответ дан Alex 25 дек. '16 в 22: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
ответ дан Nikita 28 февр. '13 в 13:17
источник поделиться
[firstNameTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];
-4
ответ дан to0nice4eva 12 авг. '11 в 0:48
источник поделиться

Другие вопросы по меткам