Home > Blockchain >  SwiftUI TextEditor Divider doesn't change Y position based on text-line count?
SwiftUI TextEditor Divider doesn't change Y position based on text-line count?

Time:03-08

I am trying to make a SwiftUI TextEditor with a Divider that adapts its position to stay under the bottom-most line of text inside of a edit-bio section of the app.

Note: I have a frame on my TextEditor so that it doesn't take up the whole-screen

Right now the Divider is static and stays in one place. Is there a built-in way to make the divider stay under the bottom most line of text?

I would think the Spacer would have given me this behavior?

Thank you!

enter image description here

struct EditBio: View {
    @ObservedObject var editProfileVM: EditProfileViewModel
    
    var body: some View {
        VStack(spacing: 10) {
            TextEditor(text: $editProfileVM.bio)
                .foregroundColor(.white)
                .padding(.top, 70)
                .padding([.leading, .trailing], 50)
                .frame(minWidth: 100, idealWidth: 200, maxWidth: 400, maxHeight: 200, alignment: .center)
            Divider().frame(height: 1).background(.white)
            Spacer()
        }
    }
}

CodePudding user response:

It is doing exactly what you told it to do. But a background color on your TextEditor. You will see that it has a height of 200 a spacing of 10 from the VStack.

I changed your code to make it obvious:

struct EditBio: View {
    @State var editProfileVM = ""
    
    var body: some View {
        VStack(spacing: 10) {
            TextEditor(text: $editProfileVM)
                .foregroundColor(.white)
                .padding(.top, 70)
                .padding([.leading, .trailing], 50)
                .frame(minWidth: 100, idealWidth: 200, maxWidth: 400, maxHeight: 200, alignment: .center)
                .background(Color.gray)
            Divider().frame(height: 1).background(.red)
            Spacer()
        }
    }
}

to produce this:

enter image description here

You can see the TextEditor naturally wants to be taller than 200, but that is limiting it. Therefore, the Spacer() is not going to cause the TextEditor to be any smaller.

The other problem that setting a fixed frame causes will be that your text will end up off screen at some point. I am presuming what you really want is a self sizing TextEditor that is no larger than it's contents.

That can be simply done with the following code:

struct EditBio: View {
    @State var editProfileVM = ""
    var body: some View {
        VStack(spacing: 10) {
            SelfSizingTextEditor(text: $editProfileVM)
             // Frame removed for the image below.
            // .frame(minWidth: 100, idealWidth: 200, maxWidth: 400, maxHeight: 200, alignment: .center)
                .foregroundColor(.white)
                // made the .top padding to be .vertical
                .padding(.vertical, 70)
                .padding([.leading, .trailing], 50)
                .background(Color.gray)
            Divider().frame(height: 5).background(.red)
            Spacer()
        }
    }
}

struct SelfSizingTextEditor: View {
    
    @Binding var text: String
    @State var textEditorSize = CGSize.zero

    var body: some View {
        ZStack {
            Text(text)
                .foregroundColor(.clear)
                .copySize(to: $textEditorSize)
            TextEditor(text: $text)
                .frame(height: textEditorSize.height)
        }
    }
}

extension View {
    func readSize(onChange: @escaping (CGSize) -> Void) -> some View {
        background(
            GeometryReader { geometryProxy in
                Color.clear
                    .preference(key: SizePreferenceKey.self, value: geometryProxy.size)
            }
        )
            .onPreferenceChange(SizePreferenceKey.self, perform: onChange)
    }

    func copySize(to binding: Binding<CGSize>) -> some View {
        self.readSize { size in
            binding.wrappedValue = size
        }
    }
}

producing this view:

enter image description here

  • Related