Format UITextField text without having cursor move to the end

后端 未结 6 1536
借酒劲吻你
借酒劲吻你 2020-12-23 17:21

I am trying to apply NSAttributedString styles to a UITextField after processing a new text entry, keystroke by keystroke. The problem is t

6条回答
  •  我在风中等你
    2020-12-23 17:49

    To piggy back off an answer to a different question here: https://stackoverflow.com/a/51814368/431271, you shouldn't be modifying the text in shouldChangeCharactersInRange since that delegate method is intended only to let the field know whether or not to allow a change and isn't supposed to mutate.

    Instead, I like to handle the text change by subscribing to the value change, like this:

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

    where the implementation of sanitizeText looks like this:

    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)
            }
        }
    }
    

提交回复
热议问题