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

问题描述

我使用以下教程创建了自己的 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: 我想我发现了:

当视图第一次出现时:

-一切正常

第一次重置后...

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

解决方法

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

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

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