SwiftUI pagination tab bar not changing page on click of button

147 views Asked by At

I have developed scrollable tab view in a SwiftUI application. When I click on button from the bottom scrollable view at bottom, tab view with pagination is not changing page. I am getting correct index in log in Pageview, still page is not changed in tabview. How to resolve this issue?

import SwiftUI

//page view
struct PageView: View {
    @Binding var selection: Int
    let dataModel: [String]
    
    init(selection: Binding<Int>, dataModel: [String]) {
        self._selection = selection
        self.dataModel = dataModel
        print("init page",selection)
    }
    var body: some View {
        TabView(selection:$selection) {
            ForEach(0..<dataModel.count) { i in
                VStack {
                    HStack {
                    Text(dataModel[i])
                        .foregroundColor(Color.primary)
                        .padding()
                     Spacer()
                    }
                    Spacer()
                }.tag(i)
            }
        }
        .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height - 170)
        //give padding nav height + scrollable Tab
        .tabViewStyle(PageTabViewStyle.init(indexDisplayMode: .never))

    }
}
//Tab bar
extension HorizontalAlignment {
private enum UnderlineLeading: AlignmentID {
    static func defaultValue(in d: ViewDimensions) -> CGFloat {
        return d[.leading]
    }
}

static let underlineLeading = HorizontalAlignment(UnderlineLeading.self)
}

struct WidthPreferenceKey: PreferenceKey {
static var defaultValue = CGFloat(0)

static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
    value = nextValue()
}

typealias Value = CGFloat
}


struct ScrollableTabView : View {

@Binding var activeIdx: Int
@State private var w: [CGFloat]
private let dataSet: [String]
init(activeIdx: Binding<Int>, dataSet: [String]) {
    self._activeIdx = activeIdx
    self.dataSet = dataSet
    _w = State.init(initialValue: [CGFloat](repeating: 0, count: dataSet.count))
        
}

var body: some View {
    VStack(alignment: .underlineLeading) {
        HStack {
            ForEach(0..<dataSet.count) { i in
                Text(dataSet[i])
                    .font(Font.title2.bold())
                    .modifier(ScrollableTabViewModifier(activeIdx: $activeIdx, idx: i))
                    .background(TextGeometry())
                    .onPreferenceChange(WidthPreferenceKey.self, perform: { self.w[i] = $0 })
                    .id(i)
                Spacer().frame(width: 20)
            }
        }
        .padding(.horizontal, 5)
        Rectangle()
            .alignmentGuide(.underlineLeading) { d in d[.leading]  }
            .frame(width: w[activeIdx],  height: 4)
            .animation(.linear)
    }
}
}

struct TextGeometry: View {
var body: some View {
    GeometryReader { geometry in
        return Rectangle().fill(Color.clear).preference(key: WidthPreferenceKey.self, value: geometry.size.width)
    }
}
}

struct ScrollableTabViewModifier: ViewModifier {
@Binding var activeIdx: Int
let idx: Int

func body(content: Content) -> some View {
    Group {
        if activeIdx == idx {
            content.alignmentGuide(.underlineLeading) { d in
                return d[.leading]
            }.onTapGesture {
                withAnimation{
                    self.activeIdx = self.idx
                }
            }

        } else {
            content.onTapGesture {
                withAnimation{
                    self.activeIdx = self.idx
                }
            }
        }
    }
}
}


struct ContentView: View {
    @State private var selection = 1 //selected page
    let dataModel = ["Forums", "Learn", "Careers", "Store", "About"]
    var body: some View {
        NavigationView {
            VStack {
                //ScrollableTabView
                //Spacer()
                 LazyHStack {
                     PageView(selection: $selection, dataModel: dataModel)
                 }

                ScrollView(.horizontal, showsIndicators: false, content: {
                    ScrollViewReader { scrollReader in
                        ScrollableTabView(activeIdx: $selection,dataSet: dataModel)
                            .padding(.top).onChange(of: selection, perform: { value in
                                withAnimation{
                                    print("selection button",selection)
                                    scrollReader.scrollTo(value, anchor: .center)
                                }
                            })
                    }
                })
                .background(Color(UIColor.secondarySystemFill))
            }
            .navigationBarTitle("Demo", displayMode: .inline)
        }.onChange(of: selection, perform: { value in
            //update tab
        })
    }
}

#Preview {
    ContentView()
}

Another issue I am facing is when I make default selection = 1 and click on first button(zero index button from the button scrollable panel) then page in tab view does not update. this happens only first time. On swipe it works but not on button click.

0

There are 0 answers