簡體   English   中英

使用Text Kit使用UITextView插入UITextFields?

[英]Interpolating UITextFields with UITextView using Text Kit?

我正在開發一個文本視圖 ,用UITextFields替換占位符。 我傳遞了一個對象(結構或字典),其中包含多個占位符令牌實例的文本。 該字典還包含我們想要從中收集數據的字段數組。 我的目標是在我的文本中放置UITextFields(或其他視圖),並隱藏令牌。

使用NSLayoutManager方法計算文本容器中占位符標記的位置,我將這些點轉換為CGRects然后排除路徑到文本字段周圍。 這是看起來像:

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
}

此時,我有三個問題:

  1. 一旦我將一個排除路徑分配給textContainer,其他占位符的計算字形位置現在都是錯誤的。
  2. calculatePositionOfPlaceholderAtIndex方法給出了非常好的y值,但x值都是0。
  3. 我無法成功隱藏占位符令牌。

因此,要解決我列表中的第一個問題,我嘗試在計算下一個問題之前添加排除路徑,方法是更改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。不是我們想要的。 可以理解地添加排除路徑會使計算的位置無效,但是每個rect方法返回0,0都沒有用。

在添加排除路徑或隱藏字形后,如何讓布局管理器重新計算屏幕上字形的位置?

編輯: Per 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
}

我只能提出建議,因為我自己是TextKit的新手,但在你的代碼中有些瞪着我,我想我會提到它,以防它可以提供幫助。

在createAndAssignExclusionPathsForInputTextFields函數中,您將按self.textFields數組的順序處理字段。

當您在函數末尾設置self.textContainer.exclusionPaths的值時,布局管理器將(可能)重新圍繞所有排除項重新生成文本,從而使您執行的某些計算無效,而不考慮其他計算的影響排除。

解決這個問題的方法是對self.textFields數組進行排序,使它們對應於文本流(它們可能已經按順序排列,我無法分辨)。 然后,您必須清除self.textContainer.exclusionPaths中的所有排除項並逐個添加它們,以便在計算下一個排除項之前,布局管理器已對先前添加的排除項周圍的文本進行了重排。 (我相信它每次設置排除項時都會這樣做但如果沒有,你可能需要調用layoutManager的ensureLayoutForManager函數)

您必須以文本流順序執行此操作,以便在文本區域上添加每個排除項,這些區域不會使先前的任何排除無效。

優化這一點以避免完全清除/重建排除也是可能的,但我建議在嘗試之前進入工作基線。

也許您應該使用ensureGlyphsForCharacterRange或者使用ensureLayoutForManager。 我需要建立一個更全面的測試程序來確定(我現在沒有足夠的空閑時間)。

然而,我確實在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's 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      
}

這留下了一些需要管理的東西,比如字段之間的標簽,初始光標位置和格式化文本輸入的一些光標行為,但它們看起來都很簡單。

如果你沒有走太遠的排除路徑並且你的要求不是要約束,那么這可能有助於清除障礙。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM