I'm trying recreate a view in SwiftUI that has the same behaviour as the Twitter profile page.
The expected behaviour is a stretchy header, with tabbed views underneath.
The content size of each tab will differ, however when changing tab the view should maintain the scroll position. It should not jump and resize.
I have embedded a TabView
within a ScrollView
and have something close to what I am expected, however my TabView
does not size itself to its content and I must give it a fixed value.
This has the side effect of cutting off content in the longer tab.
Is it possible to achieve this effect?
struct ContentView: View {
@State private var selection: Int = 0
var body: some View {
GeometryReader { proxy in
ScrollView {
VStack(spacing: 0) {
ProfileHeaderView()
.frame(height: 184)
TabBarView(selection: $selection)
.frame(height: 44)
.background(Color.purple)
}
TabView(selection: $selection) {
LazyVStack {
Text("Tab #1 - Row #0")
}
.tag(0)
LazyVStack {
ForEach(0..<50) { index in
Text("Tab #2 - Row #\(index)")
}
}
.tag(1)
}
.frame(minHeight: proxy.frame(in: .global).height - 228)
}
}
}
}
struct ProfileHeaderView: View {
var body: some View {
GeometryReader { proxy in
let minY = proxy.frame(in: .global).minY
let height = proxy.frame(in: .global).height
Color.red
.offset(y: minY > 0 ? -minY : 0)
.frame(height: minY > 0 ? height + minY : height)
}
}
}
struct TabBarView: View {
@Binding var selection: Int
@State var index: Int = 0 {
didSet { selection = index }
}
var body: some View {
HStack {
Spacer()
Button(action: { index = 0 }, label: { Text("Tab 1") })
.foregroundColor(Color(uiColor: index == 0 ? .label : .secondaryLabel))
Spacer()
Button(action: { index = 1 }, label: { Text("Tab 2") })
.foregroundColor(Color(uiColor: index == 1 ? .label : .secondaryLabel))
Spacer()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}