Ios 设置UITextField文本的格式,而无需将光标移到末尾

Ios 设置UITextField文本的格式,而无需将光标移到末尾,ios,swift,uitextfield,uitextfielddelegate,Ios,Swift,Uitextfield,Uitextfielddelegate,在处理了一个新的文本条目之后,我试图将NSAttributedString样式应用到UITextField。问题是,每当我替换文本时,每次更改光标都会跳到最末端。这是我目前的方法 立即接收并显示文本更改(当我返回false并手动执行文本替换时,同样的问题也适用) 我已订阅UITextFieldTextDidChangeNotification通知。这将触发样式设置。当文本被更改时,我使用一些format()函数将其替换为格式化版本(NSAttributedString) func textFie

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

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

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

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

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

实现这一功能的方法是获取光标的位置,更新字段内容,然后将光标替换到其原始位置。我不确定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;
}

这是一个老问题,但我有一个类似的问题,并通过以下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

它在我的应用程序环境下工作,希望对某人有用

这是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”应该始终位于代码的末尾。

感谢Objective-C中的代码@Stonz2。它就像一个符咒!我在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 
}

为了补充此线程中的其他正确答案,这里有一些Swift 4.2以及UITextFieldUITextView的代码片段

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

要在此处重复回答另一个问题:,您不应该修改
shouldChangeCharactersInRange
中的文本,因为该委托方法仅用于让字段知道是否允许更改,而不应进行变异

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

textField.addTarget(self,action:#选择器(handleTextChanged),for:.editingChanged)
//在文件的其他地方
func handleTextChanged(textField:UITextField){
textField.sanitizeText(映射:formatText)
}
其中,
sanitizeText
的实现如下所示:

扩展UITextField{ //使用此选项可以过滤或更改文本值,而无需 //丢失当前选择 func sanitizeText(映射:((字符串)->字符串)){ guard let text=self.text, let selection=selectedextrange else{ 返回 } 设newText=map(text) //仅当文本不同时才执行以下操作 guard newText!=text else{return} //确定新光标位置的起始位置 //因此光标不会被发送到末尾 设diff=text.count-newText.count 让cursorPosition=偏移量(从:BeginingOfDocument到:selection.start)-差异 self.text=newText //通知更改的值(以确保触发委托方法) sendActions(针对:.valueChanged) //之后更新所选内容 如果让newPosition=位置(从:开始到文档,偏移:光标位置){ selectedTextRange=textRange(从:newPosition到:newPosition) } } }
Is
UITextPosition*cursorLocation=[textField positionFromPosition:start offset:(range.location+string.length)]不再需要了?这非常有效,非常感谢,因为上面标记的解决方案在某些情况下对我不起作用。你能帮我回答这个问题吗:stackoverflow.com/q/53440489/2714877在我的情况下,我需要textView中的这段代码:ShouldChangeTextInRange,我只替换了“textField”->“textView”和“string”->“text”它是开箱即用的。谢谢你能帮我回答这个问题吗:stackoverflow.com/q/53440489/2714877对我不起作用(iOS 13.3.1)。仅当我将[textField setSelectedTextRange:[textField textRangeFromPosition:cursorLocation toPosition:cursorLocation:cursorLocation]]放入主线程时才起作用,例如:dispatch_async(dispatch_get_main_queue(),^{[textField textRangeFromPosition:cursorLocation toPosition:cursorLocation];})@据我所知,这些委托方法应该已经在主线程上启动了。您是否在前面的方法中执行了某些操作以将操作发送到其他线程?我不理解formatText项是什么。我想传递一个字符串,但我不知道如何使用(string)->string,例如
let-formatText:(string)->string={$0.replacingOccurrences(of:“.”,with:”)}
@radicalappdev--请参阅