I'm using a segmented control to navigate between tabs. My issue is, when I scroll inside one tab, all the other tabs are also strolling. Which is something I don't want.
I want that the scrolling works only inside the selected tab and when I click on the other tabs, it shows the top of the content of the selected tab.
Check attached video : https://i.sstatic.net/uBZAK.jpg
and here is my code
Thanks!
import SwiftUI
struct ContentView: View {
@State var selectedState = 0
@State var showTopTabBar: Bool = false
var body: some View {
ZStack (alignment: . top){
ScrollView (showsIndicators: false){
Text("Welcome")
.frame(height: 300)
Picker("Options", selection: $selectedState) {
Text("First").tag(0)
Text("Second").tag(1)
Text("Third").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
.background(Color.white)
.opacity(showTopTabBar ? 0 : 1)
.readPos { rect in
if showTopTabBar != (rect.minY <= 100) {
showTopTabBar.toggle()
}
}
if (selectedState==0) {
FirstView()
}
else if (selectedState==1){
Secondview()
}
else {
ThirdView()
}
Spacer()
}
.padding()
Picker("Options", selection: $selectedState) {
Text("First").tag(0)
Text("Second").tag(1)
Text("Third").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
.padding()
.background(Color.white)
.opacity(showTopTabBar ? 1 : 0)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
extension View {
func readPos(onChange: @escaping (CGRect) -> Void) -> some View {
background(
GeometryReader { geometryProxy in
Color.clear
.preference(key: CoordinatesPreferenceKey.self, value: geometryProxy.frame(in: .global))
}
)
.onPreferenceChange(CoordinatesPreferenceKey.self, perform: onChange)
}
}
private struct CoordinatesPreferenceKey: PreferenceKey {
static var defaultValue: CGRect = .zero
static func reduce(value: inout CGRect, nextValue: () -> CGRect) {}
}
You can use ScrollViewReader
to scroll up to start on change of Tabs:
struct ContentView: View {
@State var selectedState = 0
@State var showTopTabBar: Bool = false
var body: some View {
ZStack (alignment: . top) {
ScrollView (showsIndicators: false) {
// added ScrollViewReader
ScrollViewReader { scrollProxy in
Text("Welcome")
.frame(height: 300)
.id("welcome") // added id to scroll to
tabPicker
.background(Color.white)
.opacity(showTopTabBar ? 0 : 1)
.readPos { rect in
if showTopTabBar != (rect.minY <= 100) {
showTopTabBar.toggle()
}
}
if (selectedState==0) {
ListContentView(tab: "First View", color: .blue)
}
else if (selectedState==1){
ListContentView(tab: "Second View", color: .green)
}
else {
ListContentView(tab: "Third View", color: .orange)
}
Spacer()
// on Change of selected Tab scroll to top
.onChange(of: selectedState) { newValue in
scrollProxy.scrollTo("welcome", anchor: .center)
}
}
}
.padding()
tabPicker
.padding()
.background(Color.white)
.opacity(showTopTabBar ? 1 : 0)
}
}
var tabPicker: some View {
Picker("Options", selection: $selectedState) {
Text("First").tag(0)
Text("Second").tag(1)
Text("Third").tag(2)
}
.pickerStyle(SegmentedPickerStyle())
}
}
// dummy content view
struct ListContentView: View {
let tab: String
let color: Color
var body: some View {
ForEach(0..<30) { item in
Text("\(tab) - \(item)")
.padding()
.frame(maxWidth: .infinity)
.background(color)
}
}
}
This now always scrolls up on change of Tab. If you want to preserve the positions in each tab you have to save them in @State vars based on .id
s of your displayed content.