The following is a simplified version of a screen that is in my app:
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.
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)
}