0

The following is a simplified version of a screen that is in my app:

enter image description here

From the following code:

import SwiftUI

struct HomeView: View
{
    @State private var textFieldUserInput: String = ""

    var body: some View
    {
        VStack
        {
            List
            {
                Section
                {
                    TextField("Enter Text", text: self.$textFieldUserInput)
                }
                
                Section
                {
                    Text("Example Text 1")
                    Text("Example Text 2")
                    Text("Example Text 3")
                }
            }
        }
    }
}

I was wanting to have the second Section being able to scroll, while the TextField in the first Section is unable to scroll.

When set I .scrollDisabled to true on the List, and put the second Section in a ScrollView, it works, but it completely changes the formatting and style of the second Section.

Similarly, when I set .scrollDisabled to false and move the Section with the TextField outside of the List, it also makes it look completely different. If I try wrapping the TextField in another List while it's outside of the original List, it maintains the same look, but then each List takes up half of the screen (the second Section doesn't appear directly under the TextField anymore).

I'm just wondering if it's possible to maintain the exact look and style as I have in the above image while enabling the Section below the TextField to be scrollable while the TextField itself is unable to scroll.

2
  • Why does the first section have to be inside the list? Commented Jun 8 at 7:07
  • @JoakimDanielson When I move it outside of the list, it loses the list style formatting that I love and instead just looks like a long rectangle affixed to the top of the screen. Commented Jun 8 at 7:10

1 Answer 1

3

It would be quite easy to copy the styling that you get when the TextField is inside a List and just apply it to a field outside the list. But if you want to keep the field inside a List, your idea of wrapping the field in a separate List will work. You just need to set a maximum height on the List. You might also want to set the .contentMargins, to reduce the amount of space above and below the lists.


With iOS 18, you can actually measure the height of the List content using .onScrollGeometryChange and then apply this as max height to the List:

@State private var maxTopListHeight: CGFloat?
VStack(spacing: 0) {
    List {
        TextField("Enter Text", text: $textFieldUserInput)
    }
    .frame(maxHeight: maxTopListHeight, alignment: .top)
    .contentMargins(.top, 20)
    .onScrollGeometryChange(for: CGFloat.self, of: \.contentSize.height) { _, height in
        maxTopListHeight = height
    }
    List {
        Section {
            ForEach(1..<100) { i in
                Text("Example Text \(i)")
            }
        }
    }
    .contentMargins(.top, 0)
}

For older iOS versions, you could define the maximum height as a ScaledMetric instead, so that it adapts to dynamic font sizes.

The value doesn't have to be too exact because any excess space below the text field is filled by the List with the group background color. So the configured height just needs to exceed the height of the field itself + top content margin.

@ScaledMetric(relativeTo: .title) private var maxTopListHeight = 80.0
VStack(spacing: 0) {
    List {
        TextField("Enter Text", text: $textFieldUserInput)
    }
    .frame(maxHeight: maxTopListHeight, alignment: .top)
    .contentMargins(.top, 20)
    List {
        Section {
            ForEach(1..<100) { i in
                Text("Example Text \(i)")
            }
        }
    }
    .contentMargins(.top, 0)
}

Animation

Sign up to request clarification or add additional context in comments.

5 Comments

Is 80.0 just a magic number?
Yes. It doesn't have to be too exact because the list adds more padding below the field anyway. So it just needs to exceed the height of the field itself + top content margin. Using a ScaledMetric is a safe way to do this.
Updated with a better solution for iOS 18, maybe this is what you were hinting at before ;)
I was not hinting at anything... I couldn't think of a way to do it without magic numbers either. But yeah onScrollGeometryChange is great.
Very nice solution thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.