I’m trying to add a container view above a list view that stretches edge-to-edge across the device.
What’s the recommended approach to achieve this layout with Swift UI?
Example
I’m trying to add a container view above a list view that stretches edge-to-edge across the device.
What’s the recommended approach to achieve this layout with Swift UI?
Example
Hello @danilopeixoto,
One way is to place both views in a VStack, while making sure the top content uses .ignoresSafeArea().
You can see this demonstrated in our sample code Landmarks
If you still need help feel free to provide more information in this thread!
Travis Trotto - DTS Engineer
Do you mean something like this?
VStack {
ContainerView()
.ignoresSafeArea()
List {}
}
However, with this approach only the List is scrollable. The behavior I’m trying to achieve is similar to the image in the post above. Perhaps something like this?
ScrollView {
ContainerView()
.ignoresSafeArea()
List { }
}
However, this doesn’t work because List is scrollable on its own. However, Im still interested in the look and feel of a List below the container.
The Apple sample code Landmarks project uses a LazyVStack inside a ScrollView.
For a List, which you want, you will need to:
import SwiftUI
struct ListHeaderScene: View {
@State var headerRect: CGRect?
var headerHeight: CGFloat {
headerRect?.maxY ?? 0
}
var body: some View {
List {
ContainerView()
.readFrame { headerRect = $0 }
.listRowSeparator(.hidden)
.listRowBackground(EmptyView())
Section("Section Title") {
Text("Row 1")
Text("Row 2")
}
}
.listStyle(.plain)
.background(alignment: .top) {
// You can swap this for an image or whatever.
Color.purple
.frame(height: headerHeight)
.ignoresSafeArea()
}
}
}
struct ContainerView: View {
var body: some View {
VStack {
Image(systemName: "fork.knife.circle.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 66)
.foregroundStyle(Color.purple)
.padding(8)
.background(.white)
.clipShape(RoundedRectangle(cornerRadius: 24))
Text("Nutrition Healthcare & Fitness")
.font(.title)
.bold()
.multilineTextAlignment(.center)
.colorScheme(.dark)
HStack {
Button("Open") {}
Button("Feedback") {}
}
.buttonStyle(.borderedProminent)
.accentColor(Color.black.opacity(0.2))
}
.padding()
.frame(maxWidth: .infinity, alignment: .center)
}
}
#Preview {
NavigationStack {
ListHeaderScene()
}
}
// Extracted from BFWViews: https://bitbucket.org/barefeetware/bfwviews/
import SwiftUI
public extension View {
func readFrame(
in coordinateSpace: CoordinateSpace = .global,
writer: @escaping (CGRect) -> Void
) -> some View {
background(
GeometryReader { geometry in
Color.clear.preference(
key: FramePreferenceKey.self,
value: geometry.frame(in: coordinateSpace)
)
.onPreferenceChange(FramePreferenceKey.self) {
writer($0)
}
}
)
}
}
private struct FramePreferenceKey: PreferenceKey {
static let defaultValue: CGRect = .zero
static func reduce(value: inout CGRect, nextValue: () -> CGRect) {
value = nextValue()
}
}