【问题标题】:How can you move the cursor to the end in a SwiftUI TextField?如何在 SwiftUI TextField 中将光标移动到末尾?
【发布时间】:2020-01-27 21:49:57
【问题描述】:

我正在使用带有绑定字符串的 SwiftUI TextField 将用户的输入更改为电话格式。键入时,格式正在发生,但光标没有移动到文本字段的末尾,它保持在输入时的位置。例如,如果我输入1,texfield 的值(格式化后)将为(1,但光标停留在第一个字符之后,而不是在行尾。

有没有办法将文本字段的光标移动到行尾?

这里是示例代码:

import SwiftUI
import AnyFormatKit

struct ContentView: View {
    @State var phoneNumber = ""
    let phoneFormatter = DefaultTextFormatter(textPattern: "(###) ###-####")

    var body: some View {

    let phoneNumberProxy = Binding<String>(
        get: {
            return (self.phoneFormatter.format(self.phoneNumber) ?? "")
        },
        set: {
            self.phoneNumber = self.phoneFormatter.unformat($0) ?? ""
        }
    )

        return TextField("Phone Number", text: phoneNumberProxy)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

【问题讨论】:

  • 请用您遇到此问题的代码更新您的帖子,以便更容易找到您的问题的解决方案!
  • @njdeveloper 你找到解决方案了吗?
  • 不,我还没有找到解决方案。
  • 天哪,我希望在这里找到答案...但是...
  • 有什么想法吗?

标签: swiftui


【解决方案1】:

您可能必须使用UITextField 而不是TextFieldUITextField 允许设置自定义光标位置。要将光标定位在文本的末尾,您可以在文本内容更新时使用textField.endOfDocument 设置UITextField.selectedTextRange

@objc func textFieldDidChange(_ textField: UITextField) {        
    let newPosition = textField.endOfDocument
    textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
}

以下 SwiftUI 代码 sn-p 显示了一个示例实现。

import SwiftUI
import UIKit
//import AnyFormatKit

struct ContentView: View {
    @State var phoneNumber = ""

    let phoneFormatter = DefaultTextFormatter(textPattern: "(###) ###-####")

    var body: some View {

    let phoneNumberProxy = Binding<String>(
        get: {
            return (self.phoneFormatter.format(self.phoneNumber) ?? "")
        },
        set: {
            self.phoneNumber = self.phoneFormatter.unformat($0) ?? ""
        }
    )

        return TextFieldContainer("Phone Number", text: phoneNumberProxy)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

/************************************************/

struct TextFieldContainer: UIViewRepresentable {
    private var placeholder : String
    private var text : Binding<String>

    init(_ placeholder:String, text:Binding<String>) {
        self.placeholder = placeholder
        self.text = text
    }

    func makeCoordinator() -> TextFieldContainer.Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: UIViewRepresentableContext<TextFieldContainer>) -> UITextField {

        let innertTextField = UITextField(frame: .zero)
        innertTextField.placeholder = placeholder
        innertTextField.text = text.wrappedValue
        innertTextField.delegate = context.coordinator

        context.coordinator.setup(innertTextField)

        return innertTextField
    }

    func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<TextFieldContainer>) {
        uiView.text = self.text.wrappedValue
    }

    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: TextFieldContainer

        init(_ textFieldContainer: TextFieldContainer) {
            self.parent = textFieldContainer
        }

        func setup(_ textField:UITextField) {
            textField.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
        }

        @objc func textFieldDidChange(_ textField: UITextField) {
            self.parent.text.wrappedValue = textField.text ?? ""

            let newPosition = textField.endOfDocument
            textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
        }
    }
}

【讨论】:

  • 当解决更复杂问题的答案得到较少关注时,我总是很难过。这是一项伟大的工作,它立即为我工作!我可以确认,这是目前最简单的光标移动解决方案。
  • 这种解决方案是否也适用于 TextEditor?好奇是否值得一试。
【解决方案2】:

不幸的是,我无法评论 ddelver 的出色答案,但我只是想为我补充一下,当我更改绑定字符串时,这不起作用。

我的用例是我有一个自定义文本字段组件,用于编辑列表中的选定项目,因此当您更改选定项目时,绑定的字符串会更改。这意味着只要绑定发生变化,TextFieldContainerinit 方法就会被调用,但 Coordinator 内部的 parent 仍然引用初始父级。

我是 Swift 新手,因此可能有更好的解决方法,但我通过向 Coordinator 添加一个方法来解决它:

func updateParent(_ parent : TextFieldContainer) {
    self.parent = parent
}

然后从func updateUIView 调用它,例如:

func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<TextFieldContainer>) {
    uiView.text = self.text.wrappedValue
    context.coordinator.updateParent(self)
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-17
    • 2021-11-21
    • 1970-01-01
    • 2019-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多