Подтвердить что ты не робот

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Совсем случайный совет: как добавить "..." в конце

Часто вы используете UITextView "как UILabel". Итак, вы хотите, чтобы он урезал текст с помощью многоточия "..."

Если это так, добавьте третью строку кода в "настройку":

 textContainer.lineBreakMode = .byTruncatingTail

Полезный совет, если вы хотите нулевую высоту, когда нет текста

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

Это здорово, но если текста нет, вы получаете ту же высоту, как если бы была одна строка текста. Текстовое представление никогда не "уходит".

enter image description here

Если вы хотите, чтобы он "ушел", просто добавьте это

override var intrinsicContentSize: CGSize {
    var i = super.intrinsicContentSize
    print("for \(text) size will be \(i)")
    if text == "" { i.height = 1.0 }
    print("   but we changed it to \(i)")
    return i
}

enter image description here

(Я сделал это "1", чтобы было понятно, что происходит, "0" - это хорошо.)

Ответ 2

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

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

textView.textContainer.lineFragmentPadding = 0

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

textView.textContainerInset = .zero

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

Ответ 3

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

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

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

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

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

nameField.textAlignment = NSTextAlignmentLeft;

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

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

Ответ 4

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

enter image description here

Ответ 5

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

Ответ 6

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

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

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

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

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

Ответ 7

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

Ответ 8

Все эти ответы затрагивают вопрос названия, но я хотел предложить некоторые решения для проблем, представленных в теле вопроса 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

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

Ответ 9

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

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

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

Ответ 10

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

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

Ответ 11

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

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

Ответ 12

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

self.textView.textContainerInset = .init(top: -2, left: 0, bottom: 0, right: 0)
self.textView.textContainer.lineFragmentPadding = 0

Ответ 13

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

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

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

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

 self.textView?.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0)

Ответ 14

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

Ответ 15

Здесь небольшое небольшое расширение, которое удалит маркер по умолчанию 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);
   }
}

Ответ 16

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

Ответ 17

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

yourTextView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

Ответ 18

Я нашел еще один подход, получив представление с текстом из подпроектов 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);
}

Ответ 19

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

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

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

Ответ 20

[firstNameTextField setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];