SwiftUIで高さ可変のTextEditorを使用する

# はじめに
自分が調べた限りだと、SwiftUI単体では実装できそうにないので、UIKitを併用する方法で実装しました。

# 完成品
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253564/cf209c8c-bf1f-85fd-20c3-0dbe117623e9.png)![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253564/67ddc9ba-526e-acc8-2595-065c5d33856d.png)



# 実装
まず、UIKitをSwiftUIから呼び出すためにUIViewRepresentableに準拠したViewを作成します。
説明はコード内のコメントを参照してください。

```Swift
struct DynamicHeightTextview: UIViewRepresentable {
    
    //入力値を反映するプロパティ
    @Binding var text: String
    
    //入力値を考慮したTextViewの高さを保持するプロパティ
    @Binding var height: CGFloat
    
    let textView = UITextView()
    
    //実装必須
    func makeUIView(context: Context) -> UITextView {
        textView.backgroundColor = .clear
        textView.font = .systemFont(ofSize: 16)
        textView.delegate = context.coordinator
        
        return textView
    }
    
    //実装必須
    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.text = text
    }
    
    //Delegateメソッドを定義したクラスを返す
    func makeCoordinator() -> Coordinator {
        return Coordinator(dynamicHeightTextView: self)
    }
    
    //UITextViewのDelegateメソッドを実装する
    class Coordinator: NSObject, UITextViewDelegate {
        
        let dynamicHeightTextView: DynamicHeightTextview
        let textView: UITextView
        
        init(dynamicHeightTextView: DynamicHeightTextview) {
            self.dynamicHeightTextView = dynamicHeightTextView
            self.textView = dynamicHeightTextView.textView
        }
        
        func textViewDidChange(_ textView: UITextView) {
            dynamicHeightTextView.text = textView.text
            let textViewSize = textView.sizeThatFits(textView.bounds.size)
            dynamicHeightTextView.height = textViewSize.height
        }
    }
}

```

続いて、DynamicHeightTextviewをもう1度ラップしたViewを作成し、ここで高さの反映とPlaceholderを追加します。

```Swift
struct DynamicHeightTextEditor: View {
    
    @Binding var text: String
    @State var textHeight: CGFloat = 0
    
    var placeholder: String
    var minHeight: CGFloat
    var maxHeight: CGFloat
    
    //TextEditorの高さを保持するプロパティ
    var textEditorHeight: CGFloat {
        if textHeight < minHeight { 
            return minHeight
        }
        
        if textHeight > maxHeight {
            return maxHeight
        }
        
        return textHeight
    }
    
    var body: some View {
        ZStack {
            //TextEditorの背景色
            Color(red: 0.933, green: 0.917, blue: 0.956)
            
            //Placeholder
            if text.isEmpty {
                HStack {
                    Text(placeholder)
                        .foregroundColor(Color(red: 0.62, green: 0.62, blue: 0.62))
                        .padding(.leading, 2)
                    Spacer()
                }
            }
            
            DynamicHeightTextview(text: $text, height: $textHeight)
        }
        .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
        .frame(height: textEditorHeight) //← ここで高さを反映
    }
}
```
# 使用方法

``` Swift
struct ContentView: View {
    @State var text = ""
    var body: some View {
        DynamicHeightTextEditor(text: $text, placeholder: "テキストを入力", minHeight: 35, maxHeight: 150)
            .padding(.leading, 10)
            .padding(.trailing, 10)
    }
}
```

以上です。