【问题标题】:Format UITextField text without having cursor move to the end格式化 UITextField 文本而不将光标移动到末尾
【发布时间】:2014-12-04 17:29:50
【问题描述】:

在处理一个新的文本条目后,我正在尝试将 NSAttributedString 样式应用到 UITextField,逐个击键。问题是每当我替换文本时,光标都会在每次更改时跳到最后。这是我目前的方法……

立即接收并显示文本更改(当我返回 false 并手动进行文本替换时,同样的问题也适用)

func textField(textField: UITextField!, shouldChangeCharactersInRange range: NSRange, replacementString string: String!) -> Bool {        
    let range:NSRange = NSRange(location: range.location, length: range.length)
    let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string);
    return true
}

我订阅了 UITextFieldTextDidChangeNotification 通知。这会触发样式。当文本更改时,我使用一些 format() 函数将它 (NSString) 替换为格式化版本 (NSAttributedString)。

func textFieldTextDidChangeNotification(userInfo:NSDictionary) {
    var attributedString:NSMutableAttributedString = NSMutableAttributedString(string: fullString)
    attributedString = format(textField.text) as NSMutableAttributedString
    textField.attributedText = attributedString
}

这样的造型效果很好。但是,在每次替换文本后,光标都会跳到字符串的末尾。如何关闭此行为或手动将光标移回开始编辑的位置? ... 或者有更好的解决方案在编辑时设置文本样式?

【问题讨论】:

    标签: ios swift uitextfield uitextfielddelegate


    【解决方案1】:

    实现这项工作的方法是抓取光标的位置,更新字段内容,然后将光标替换到其原始位置。我不确定 Swift 中的确切等价物,但以下是我在 Obj-C 中的操作方式。

    - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
    {
        UITextPosition *beginning = textField.beginningOfDocument;
        UITextPosition *cursorLocation = [textField positionFromPosition:beginning offset:(range.location + string.length)];
    
        textField.text = [textField.text stringByReplacingCharactersInRange:range withString:string];
    
        /* MAKE YOUR CHANGES TO THE FIELD CONTENTS AS NEEDED HERE */
    
        // cursorLocation will be (null) if you're inputting text at the end of the string
        // if already at the end, no need to change location as it will default to end anyway
        if(cursorLocation)
        {
            // set start/end location to same spot so that nothing is highlighted
            [textField setSelectedTextRange:[textField textRangeFromPosition:cursorLocation toPosition:cursorLocation]];
        }
    
        return NO;
    }
    

    【讨论】:

    • 你能帮我解答这个问题吗:stackoverflow.com/q/53440489/2714877
    • 对我不起作用(iOS 13.3.1)。仅当我将 [textField setSelectedTextRange:[textField textRangeFromPosition:cursorLocation toPosition:cursorLocation]] 放入主线程时才有效,如下所示: dispatch_async(dispatch_get_main_queue(), ^{ [textField textRangeFromPosition:cursorLocation toPosition:cursorLocation]]; });跨度>
    • @LavrinPristazh 据我所知,这些委托方法应该已经在主线程上触发了。您是否在方法的早期执行了一些操作以将操作发送到不同的线程?
    【解决方案2】:

    感谢 @Stonz2,感谢 Objective-C 中的代码。它就像一个魅力!我在我的 Swift 项目中使用了它。 Swift 中的相同代码:

    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    
        let positionOriginal = textField.beginningOfDocument
        let cursorLocation = textField.position(from: positionOriginal, offset: (range.location + NSString(string: string).length))
    
        /* MAKE YOUR CHANGES TO THE FIELD CONTENTS AS NEEDED HERE */
    
        if let cursorLoc = cursorLocation {
            textField.selectedTextRange = textField.textRange(from: cursorLoc, to: cursorLoc)
        }
        return false 
    }
    

    【讨论】:

    • 在我的情况下,我需要 textView:ShouldChangeTextInRange 中的这段代码,我只替换了“textField”->“textView”和“string”->“text”,它开箱即用。谢谢!
    【解决方案3】:

    这是一个老问题,但我遇到了类似的问题,并使用以下 Swift 解决了它:

    // store the current cursor position as a range
    var preAttributedRange: NSRange = textField.selectedRange 
    // apply attributed string
    var attributedString:NSMutableAttributedString = NSMutableAttributedString(string: fullString)
    attributedString = format(textField.text) as NSMutableAttributedString
    textField.attributedText = attributedString
    // reapply the range
    textField.selectedRange = preAttributedRange
    

    它在我的应用程序的上下文中工作,希望它对某人有用!

    【讨论】:

    • 这很有效,非常感谢,因为上面标记的解决方案在某些情况下对我不起作用。
    • 你能帮我解答这个问题吗:stackoverflow.com/q/53440489/2714877
    【解决方案4】:

    为了补充这个线程中的其他正确答案,这里有一些代码 sn-ps 用于 Swift 4.2 以及 UITextFieldUITextView

    UITextField

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        // Update Cursor
        let positionOriginal = textField.beginningOfDocument
        let cursorLocation = textField.position(from: positionOriginal, offset: (range.location + string.count))
        if let cursorLocation = cursorLocation {
            textField.selectedTextRange = textField.textRange(from: cursorLocation, to: cursorLocation)
        }
        return false
    }
    

    UITextView

    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        // Update Cursor
        let positionOriginal = textView.beginningOfDocument
        let cursorLocation = textView.position(from: positionOriginal, offset: (range.location + text.count))
        if let cursorLocation = cursorLocation {
            textView.selectedTextRange = textView.textRange(from: cursorLocation, to: cursorLocation)
        }
        return false
    }
    

    【讨论】:

      【解决方案5】:

      这是一个适用于 swift 2 的代码。尽情享受吧!

       func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
          let beginnig: UITextPosition =  textField.beginningOfDocument
      
          if let txt = textField.text {
              textField.text = NSString(string: txt).stringByReplacingCharactersInRange(range, withString: string)
          }
      
          if let cursorLocation: UITextPosition = textField.positionFromPosition(beginnig, offset: (range.location + string.characters.count) ) {
              textField.selectedTextRange = textField.textRangeFromPosition(cursorLocation, toPosition: cursorLocation)
          }
      
          return false
      }
      

      请注意,最后一个“if let”应始终位于代码末尾。

      【讨论】:

      • 不再需要UITextPosition *cursorLocation = [textField positionFromPosition:beginning offset:(range.location + string.length)]; 了吗?
      【解决方案6】:

      在这里背对另一个问题的答案:https://stackoverflow.com/a/51814368/431271,您不应该修改shouldChangeCharactersInRange 中的文本,因为该委托方法只是为了让该字段知道是否允许更改并且不应该发生变异。

      相反,我喜欢通过订阅值更改来处理文本更改,如下所示:

      textField.addTarget(self, action: #selector(handleTextChanged), for: .editingChanged)
      
      // elsewhere in the file
      func handleTextChanged(_ textField: UITextField) {
          textField.sanitizeText(map: formatText)
      }
      

      sanitizeText 的实现如下所示:

      extension UITextField {
      
          // Use this to filter out or change the text value without 
          // losing the current selection
          func sanitizeText(map: ((String) -> String)) {
              guard let text = self.text,
                  let selection = selectedTextRange else {
                      return
              }
      
              let newText = map(text)
      
              // only execute below if text is different
              guard newText != text else { return }
      
              // determine where new cursor position should start
              // so the cursor doesnt get sent to the end
              let diff = text.count - newText.count
              let cursorPosition = offset(from: beginningOfDocument, to: selection.start) - diff
      
              self.text = newText
      
              // notify the value changed (to ensure delegate methods get triggered)
              sendActions(for: .valueChanged)
      
              // update selection afterwards
              if let newPosition = position(from: beginningOfDocument, offset: cursorPosition) {
                  selectedTextRange = textRange(from: newPosition, to: newPosition)
              }
          }
      }
      

      【讨论】:

      • 我不明白 formatText 项是什么。我有一个要传递的字符串,但我不明白如何使用 (String) -> String)
      • 例如。 let formatText: (String) -> String = { $0.replacingOccurrences(of: ".", with: "") }@radicalappdev -- 见docs.swift.org/swift-book/LanguageGuide/Closures.html
      猜你喜欢
      • 2011-11-08
      • 1970-01-01
      • 1970-01-01
      • 2011-12-17
      • 2019-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-11
      相关资源
      最近更新 更多