LoginSignup
5
3

More than 1 year has passed since last update.

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

Posted at

はじめに

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

完成品

1.png2.png

実装

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

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を追加します。

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) //← ここで高さを反映
    }
}

使用方法

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

以上です。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3