14

I have a List containing a TextEditor

struct ContentView: View {
    @State var text: String = "test"

    var body: some View {
        List((1...10), id: \.self) { _ in
            TextEditor(text: $text)
        }
    }
}

But it's items are not growing on height change of the TextEditor. I have tried .fixedSize() modifier with no luck. What am I missing here?

pawello2222
  • 46,897
  • 22
  • 145
  • 209
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278

2 Answers2

32

You can use an invisible Text in a ZStack to make it dynamic.

struct ContentView: View {
    @State var text: String = "test"

    var body: some View {
        List((1...10), id: \.self) { _ in
            ZStack {
                TextEditor(text: $text)
                Text(text).opacity(0).padding(.all, 8) // <- This will solve the issue if it is in the same ZStack
            }
        }
    }
}

Note that you should consider changing font size and other properties to match the TextEditor

Preview

Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
  • 2
    I had to put the Text label before the TextEditor in the ZStack. Also to ensure the scrollbar doesn't come up I had to put the ZStack inside a HStack. – ngb Jan 08 '21 at 08:29
  • Also it doesn't work in editMode. I had to fake it by creating the + and - circles – ngb Jan 08 '21 at 08:32
  • 1
    While implementing this approach In iOS 14.4, the `TextEditor` has a scrollbar initially and only one line of text is shown. If I edit the text, the list row expands to fit the content and the `TextEditor`'s scrollbar disappears. It seems pretty buggy. – Clifton Labrum Mar 05 '21 at 22:00
10

As far as I can see from view hierarchy TextEditor is just simple wrapper around UITextView and does not have more to add, so you can huck into that layer and find UIKit solution for what you need, or ...

here is a demo of possible approach to handle it at SwiftUI level (the idea is to use Text view as a reference for wrapping behaviour and adjust TextEditor exactly to it)

Tested with Xcode 12b / iOS 14 (red border is added for better visibility)

demo

Modified your view:

struct ContentView: View {
    @State var text: String = "test"

    @State private var height: CGFloat = .zero
    var body: some View {
        List {
            ForEach((1...10), id: \.self) { _ in
                ZStack(alignment: .leading) {
                    Text(text).foregroundColor(.clear).padding(6)
                        .background(GeometryReader {
                            Color.clear.preference(key: ViewHeightKey.self, value: $0.frame(in: .local).size.height)
                        })
                    TextEditor(text: $text)
                        .frame(minHeight: height)
                        //.border(Color.red)        // << for testing
                }
                .onPreferenceChange(ViewHeightKey.self) { height = $0 }
            }
        }
    }
}

Note: ViewHeightKey is a preference key, used in my other solutions, so can be get from there

ForEach and GeometryReader: variable height for children?

How to make a SwiftUI List scroll automatically?

Automatically adjustable view height based on text height in SwiftUI

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • This approach works great in a `List`, but doesn't seem to work in a `ScrollView` with a `VStack`. Have you ever tried it that way by chance? – Clifton Labrum Mar 05 '21 at 21:09
  • 2
    You can use .fixedSize(horizontal: false, vertical: true) if you aren't using List. – Tarek Hallak Sep 12 '21 at 20:26
  • @TarekHallak Great tip on this! We needed this to make it work. For anybody needing this too, we put the .fixedSize on the Text, not the TextEditor. Why does this work, though? – James Toomey Nov 17 '22 at 16:56
  • Unfortunaltely, the height of the TextEditor is not the same as Text. One need to ad like 10 to disable internal scroll of TextEditor. This may not be seen in a few lines, though! – kelalaka May 04 '23 at 17:50