我正在尝试将 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通知。这会触发样式。更改文本后,我使用格式化()函数将格式化版本( NSAttributedString )替换为 NSString 。
func textFieldTextDidChangeNotification(userInfo:NSDictionary) {
var attributedString:NSMutableAttributedString = NSMutableAttributedString(string: fullString)
attributedString = format(textField.text) as NSMutableAttributedString
textField.attributedText = attributedString
}
样式工作正常。但是,在每次文本替换后,光标会跳转到字符串的末尾。如何关闭此行为或手动将光标移回到开始编辑的位置? ...或者在编辑时是否有更好的方法来设置文本样式?
答案 0 :(得分:36)
使这项工作的方法是获取光标的位置,更新字段内容,然后将光标替换为其原始位置。我不确定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;
}
答案 1 :(得分:9)
这是一个老问题,但我遇到了类似的问题并使用以下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
它适用于我的应用程序环境,希望它对某人有用!
答案 2 :(得分:9)
通过Objective-C中的代码感谢@ Stonz2。它就像一个魅力!我在Swift项目中使用它。 Swift中的相同代码:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let positionOriginal = textField.beginningOfDocument
let cursorLocation = textField.positionFromPosition(positionOriginal, offset: (range.location + NSString(string: string).length))
/* MAKE YOUR CHANGES TO THE FIELD CONTENTS AS NEEDED HERE */
if(cursorLocation != nil) {
textField.selectedTextRange = textField.textRangeFromPosition(cursorLocation!, toPosition: cursorLocation!)
}
return false
}
答案 3 :(得分:2)
这是一个适用于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
}
请注意,最后"如果让"应始终留在代码的末尾。
答案 4 :(得分:2)
为补充该线程中的其他正确答案,以下是 Swift 4.2 以及 UITextField 和 UITextView
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 :(得分:0)
要在此处回答另一个问题: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)
}
}
}