SwiftUI:创建自己的 TextField 并以编程方式更改内容

Posted

技术标签:

【中文标题】SwiftUI:创建自己的 TextField 并以编程方式更改内容【英文标题】:SwiftUI: Create own TextField and change the content programmatically 【发布时间】:2021-01-30 16:01:47 【问题描述】:

我使用以下教程创建了自己的 TextField:https://www.youtube.com/watch?v=9kfbJVqqTKc&t=17s。一切正常,但我添加了一个功能来刷新 TextField 的内容。问题是当我以编程方式设置内容时,协调器中没有调用函数 textViewDidChange() 。这会导致整个 TextField 崩溃,这意味着当我键入内容时占位符不再消失并且它不会自动变大。当您到达应该创建新行的位置时,无论出于何种原因,文本都会被简单地删除。

有没有办法解决这个问题?

代码:

import SwiftUI

struct CustomTextField: View 
var title: String
@Binding var text: String
var isFocused: Binding<Bool>?

@State var height: CGFloat = 20

var onCommit: (() -> ())?

init(_ title: String, text: Binding <String>, isFocused: Binding<Bool>? = nil, onCommit: (() -> ())? = nil)
    self.title = title
    self._text = text
    self.isFocused = isFocused
    self.onCommit = onCommit

var body: some View 
    ZStack(alignment: .topLeading)
        Text(title)
            .foregroundColor(Color.secondary.opacity(text.isEmpty ? 0.5 : 0))
            .animation(nil)
        CustomTextFieldRep(text: $text, isFocused: isFocused, height: $height, onCommit: onCommit)
            .frame(height: height)
    



struct CustomTextFieldRep: UIViewRepresentable

@Binding var text: String
var isFocused: Binding<Bool>?
@Binding var height: CGFloat
var onCommit: (() -> ())?

func makeUIView(context: Context) -> UITextView 
    let uiView = UITextView()
    
    uiView.font = UIFont.preferredFont(forTextStyle: .body)
    uiView.backgroundColor = .clear
    uiView.text = text
    uiView.delegate = context.coordinator
    uiView.textContainerInset = .zero
    uiView.textContainer.lineFragmentPadding = 0
    
    return uiView


func updateUIView(_ uiView: UITextView, context: Context) 
    if uiView.text != text
        uiView.text = text
        
        let size = uiView.sizeThatFits(CGSize(width: uiView.frame.width, height: CGFloat.greatestFiniteMagnitude))
        height = size.height
    
    
    if let isFocused = isFocused?.wrappedValue
        if isFocused && !uiView.isFirstResponder
            uiView.becomeFirstResponder()
        else if !isFocused && uiView.isFirstResponder
            uiView.resignFirstResponder()
        
    


func makeCoordinator() -> Coordinator 
    Coordinator(rep: self)


class Coordinator: NSObject, UITextViewDelegate
    
    var rep: CustomTextFieldRep
    
    internal init(rep: CustomTextFieldRep)
        self.rep = rep
    
    
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool 
        if let onCommit = rep.onCommit, text == "\n"
            onCommit()
            return false
        
        
        return true
    
    
    func textViewDidChange(_ textView: UITextView) 
        rep.text = textView.text
        
        let size = textView.sizeThatFits(CGSize(width: textView.frame.width, height: CGFloat.greatestFiniteMagnitude))
        rep.height = size.height
    
    
    func textViewDidBeginEditing(_ textView: UITextView) 
        rep.isFocused?.wrappedValue = true
    
    
    func textViewDidEndEditing(_ textView: UITextView) 
        rep.isFocused?.wrappedValue = false
    


我认为如果我在协调器本身中调用 textViewDidChange() 函数,问题就可以解决。但是,我不知道该怎么做。

提前致谢

编辑: 我是如何实现自定义 TextField 的:

struct View: View
@ObservedObject private var addUebungVM = AddÜbungViewModel()
enum Focus
    case name, beschreibung


@State var focus: Focus? = .name

var nameFocusBinding: Binding<Bool>
    $focus.isEqual(to: .name)


var beschreibungFocusBinding: Binding<Bool>
    $focus.isEqual(to: .beschreibung)


var body: some View
    CustomTextField(placeholderName, text: $addUebungVM.übungName, isFocused: nameFocusBinding, onCommit: saveNewÜbung)
      .alignmentNewExercise()
      .styleTextField()


private func saveNewÜbung()
    if !addUebungVM.übungName.isEmpty
        DispatchQueue.main.async
            addUebungVM.übungName = " "
            addUebungVM.übungBeschreibung = " "
            placeholderName = NSLocalizedString("nächsteÜbungString", comment: "")
            focus = .name
        
    


编辑 2: 我想我发现了:

当视图第一次出现时:

-一切都很好

第一次重置后...

    当我通过键盘按钮“返回”调用第二个重置时,它在文本不超过正常行时起作用,但是当我调用 通过“正常”按钮重置它根本不起作用 占位符不再显示 当我输入的文本比正常行长并且文本框应该变大时,文本会丢失

【问题讨论】:

这是否回答了您的问题***.com/a/58639072/12299030? @Asperi 不幸的是没有。当我使用您的变体时,同样的问题仍然存在 【参考方案1】:

我正在使用您的代码并通过如下状态变量刷新内容:

struct ContentView: View 
    @State var text = ""
    @State var isFocused = true
    var body: some View 
        CustomTextField("Initial text", text: $text, isFocused: $isFocused) 
            print("done")
            let t = DispatchTime.now() + 2.0
            DispatchQueue.main.asyncAfter(deadline: t) 
                text = "Changed text"
            
        
    

这是解决您问题的方法吗?

【讨论】:

硬编码延迟不是很好的做法 @Subha_26 它以某种方式工作。我现在可以按下重置按钮并再次输入一些内容。但是,如果我已经删除了一次内容,然后再次输入内容并退出文本字段,则内容将自动删​​除。你知道为什么吗? @aheze 这样做只是为了对 TextField 的内容进行编程更改。实际上,这会在某些操作或 API 响应时触发。 还有一件事:当我以编程方式更改文本时,占位符不再可见。 @Subha_26 啊我明白了

以上是关于SwiftUI:创建自己的 TextField 并以编程方式更改内容的主要内容,如果未能解决你的问题,请参考以下文章

使用 SwiftUI 启动 TextField 时出现 VStack 错误

如何创建只接受数字和单个点的 SwiftUI TextField?

UIPickerView 作为 SwiftUI 中 TextField() 的输入

检测用户何时停止/暂停在 TextField 中写入 - SwiftUI

SwiftUI:如何从核心数据中的 TextField 中保存值

3个关于SwiftUI中TextField不得不看的知识点