2

次のチュートリアルで独自の 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番目のリセットを呼び出すと、テキストが通常の行より長くない場合に機能しますが、「通常」ボタンでリセットを呼び出すとまったく機能しません
  2. プレースホルダーが表示されなくなりました
  3. 入力したテキストが通常の行よりも長くなり、テキスト ボックスを大きくする必要がある場合、テキストが失われます
4

1 に答える 1