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

Интерполирование UITextFields с помощью UITextView с использованием текстового набора?

Я работаю над текстовым видом который заменяет заполнители UITextFields. Я передаю ему объект (структуру или словарь) с текстом, содержащим несколько экземпляров токена-заполнителя. Словарь также содержит массив полей, из которых мы хотим собирать данные. Моя цель - разместить UITextFields (или другие представления) во всем тексте и скрыть маркеры.

Используя методы NSLayoutManager для вычисления местоположения токенов-заполнителей в текстовых контейнерах, я конвертирую эти точки в CGR-объекты, а затем пути исключения, выкладываю текст по текстовым полям. Вот что это выглядит:

func createAndAssignExclusionPathsForInputTextFields () {

    var index = 0
    let textFieldCount = self.textFields.count

    var exclusionPaths : [UIBezierPath] = []

    while index < textFieldCount {

        let textField : AgreementTextField = self.textFields[index]

        let location = self.calculatePositionOfPlaceholderAtIndex(index)
        let size = textField.intrinsicContentSize()
        textField.frame = CGRectMake(location.x, location.y, size.width, size.height)

        exclusionPaths.append(textField.exclusionPath())

        index = index + 1
    }

    self.textContainer.exclusionPaths = exclusionPaths
}

// ...

func calculatePositionOfPlaceholderAtIndex(textIndex : NSInteger) -> CGPoint {

    let layoutManager : NSLayoutManager = self.textContainer.layoutManager!

    let delimiterRange = self.indices[textIndex]
    let characterIndex = delimiterRange.location
    let glyphRange = self.layoutManager.glyphRangeForCharacterRange(delimiterRange, actualCharacterRange:nil)
    let glyphIndex = glyphRange.location
    let rect = layoutManager.lineFragmentRectForGlyphAtIndex(glyphIndex, effectiveRange: nil, withoutAdditionalLayout: true)

    let remainingRect : UnsafeMutablePointer<CGRect> = nil

    let textContainerRect = self.textContainer.lineFragmentRectForProposedRect(rect, atIndex: characterIndex, writingDirection: .LeftToRight, remainingRect: remainingRect)

    let position = CGPointMake(textContainerRect.origin.x, textContainerRect.origin.y)

    return position
}

На этом этапе у меня есть три вопроса:

  • Как только я назначу путь исключения для textContainer, расчетные позиции глифов для других заполнителей теперь все не так.
  • Метод calculatePositionOfPlaceholderAtIndex дает мне довольно хорошие значения y, но значения x равны 0.
  • Мне не удалось скрыть тег-заполнители.

Итак, чтобы решить первую проблему в моем списке, я попытался добавить путь исключения до вычисления следующего, изменив createAndAssignExclusionPathsForInputTextFields:

func createAndAssignExclusionPathsForInputTextFields () {

    var index = 0
    let textFieldCount = self.textFields.count

    while index < textFieldCount {

        let textField : AgreementTextField = self.textFields[index]

        let location = self.calculatePositionOfPlaceholderAtIndex(index)
        let size = textField.intrinsicContentSize()
        textField.frame = CGRectMake(location.x, location.y, size.width, size.height)

        self.textContainer.exclusionPaths.append(textField.exclusionPath())

        index = index + 1
    }
}

Теперь мои расчетные позиции возвращаются 0, 0. Не то, что мы хотим. Добавление пути исключения, по-видимому, делает расчетные местоположения недействительными, но получение 0, 0 назад для каждого прямого метода не помогает.

Как я могу попросить менеджера макета пересчитать позицию для глифов на экране после добавления пути исключения или скрытия глифа?

РЕДАКТИРОВАТЬ: Ответ на Alain T, я пробовал следующее без везения:

    func createAndAssignExclusionPathsForInputTextFields () {

    var index = 0
    let textFieldCount = self.textFields.count

    var exclusionPaths : [UIBezierPath] = []

    while index < textFieldCount {

        let textField : AgreementTextField = self.textFields[index]

        let location = self.calculatePositionOfPlaceholderAtIndex(index)
        let size = textField.intrinsicContentSize()
        textField.frame = CGRectMake(location.x, location.y, size.width, size.height)

        exclusionPaths.append(textField.exclusionPath())
        self.textContainer.exclusionPaths = exclusionPaths

        self.layoutManager.ensureLayoutForTextContainer(self.textContainer)
        index = index + 1
    }

    self.textContainer.exclusionPaths = exclusionPaths
}
4b9b3361

Ответ 1

Я могу только делать предложения, поскольку я новичок в TextKit сам, но что-то впилось в меня в ваш код, и я подумал, что упомянул об этом, если это может помочь.

В вашей функции createAndAssignExclusionPathsForInputTextFields вы обрабатываете свои поля в порядке вашего массива self.textFields.

Когда вы устанавливаете значение self.textContainer.exclusionPaths в конце функции, диспетчер компоновки (потенциально) повторно передает текст вокруг всех ваших исключений, тем самым аннулируя некоторые из ваших вычислений, которые были выполнены без учета влияние других исключений.

Способы сортировки массива self.textFields так, чтобы они соответствовали текстовому потоку (они уже могут быть в указанном порядке, я не могу сказать). Затем вы должны очистить все исключения из self.textContainer.exclusionPaths и добавить их обратно один за другим, чтобы перед вычислением следующего исключения менеджер компоновки переплавил текст вокруг ранее добавленного исключения. (Я считаю, что он делает это каждый раз, когда вы устанавливаете исключения, но если это не так, вам может потребоваться вызвать функцию layoutManager для обеспечения функцииLayoutForManager)

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

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

Ответ 2

Возможно, вам следует использовать makeGlyphsForCharacterRange вместо этого, а также makeLayoutForManager. Мне нужно было бы построить более полную тестовую программу, чтобы точно понять это (сейчас у меня недостаточно свободного времени).

Однако я смотрел альтернативный способ иметь редактируемые поля и редактируемый текст в UITextView и придумывал только текстовый подход (грубый и неполный, но это отправная точка).

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

// function to determine if attributes of a range of text allow editing
// (you decide which attributes correspond to editable text)
func editableText(attributes:[String:AnyObject]) -> Bool
{
   if let textColor = attributes[NSForegroundColorAttributeName] as? UIColor
           where textColor == UIColor.redColor() // example: red text is editable
   { return true }
   return false
}

// UITextViewDelegate function to allow editing a specific text area (range)
func textView(textView: UITextView, var shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool
{
   // 0 length selection is an insertion point
   // will get the attibutes of the text preceding it as typing attributes
   if range.length == 0 
   && text != ""
   && editableText(textView.typingAttributes) 
   { return true }

   // in case we're on an insertion point at the very begining of an editable field
   // lets look at the next character
   range.length = max(1, range.length)

   // for non-zero ranges, all subranges must have the editable attributes to allow editing
   // (also setting typingAttributes to cover the insertion point at begining of field case)
   var canEdit = true
   textView.attributedText.enumerateAttributesInRange(range, options:[])
   { 
     if self.editableText($0.0) 
     { textView.typingAttributes = $0.0 }
     else
     { canEdit = false }          
   }       
   return canEdit      
}

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

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