How to tease adjacent pages in PageTabView?
Asked Answered
C

1

11

I am working with SwiftUI 2 and using a TabView with PageTabViewStyle.

Now, I am searching for a way to "tease" the pages adjacent to the current page like so:

Image source: http://ios-dev.co/swiftui/how-to-create-page-view-in-swiftui/

Is it possible to achieve this effect with TabView and PageTabViewStyle?

I already tried to reduce the width of my TabView to be windowWidth-50. However, this did not lead to the adjacent pages being visible at the sides. Instead, this change introduced a hard vertical edge 50px left of the right window border, where new pages would slide in.

Carranza answered 25/4, 2021 at 12:32 Comment(2)
You would have to create your ownSemaphore
use this library, other than that, probably not possibleElohist
O
9

Here is a simple implementation. You can use the struct with the AnyView array or use the logic directly in your own implementation.

enter image description here

struct ContentView: View {
    
    @State private var selected = 4

    var body: some View {
        
        // the trailing closure takes an Array of AnyView type erased views
        TeasingTabView(selectedTab: $selected, spacing: 20) {
            [
                AnyView(TabContentView(title: "First", color: .yellow)),
                AnyView(TabContentView(title: "Second", color: .orange)),
                AnyView(TabContentView(title: "Fourth", color: .green)),
                AnyView(TabContentView(title: "Fifth", color: .blue)),
                AnyView(
                    Image(systemName: "lizard")
                        .resizable().scaledToFit()
                        .padding()
                        .frame(maxHeight: .infinity)
                        .border(.red)
                )
            ]
        }
    }
}
        
        
struct TeasingTabView: View {
    
    @Binding var selectedTab: Int
    let spacing: CGFloat
    let views: () -> [AnyView]
    
    @State private var offset = CGFloat.zero
    var viewCount: Int { views().count }

    var body: some View {
        
        VStack(spacing: spacing) {
            GeometryReader { geo in
                let width = geo.size.width * 0.7
                
                LazyHStack(spacing: spacing) {
                    Color.clear
                        .frame(width: geo.size.width * 0.15 - spacing)
                    ForEach(0..<viewCount, id: \.self) { idx in
                        views()[idx]
                            .frame(width: width)
                            .padding(.vertical)
                   }
                }
                .offset(x: CGFloat(-selectedTab) * (width + spacing) + offset)
                .animation(.easeOut, value: selectedTab)
                .gesture(
                    DragGesture()
                        .onChanged { value in
                            offset = value.translation.width
                        }
                        .onEnded { value in
                            withAnimation(.easeOut) {
                                offset = value.predictedEndTranslation.width
                                selectedTab -= Int((offset / width).rounded())
                                selectedTab = max(0, min(selectedTab, viewCount-1))
                                offset = 0
                            }
                        }
                )
            }
            //
            HStack {
                ForEach(0..<viewCount, id: \.self) { idx in
                    Circle().frame(width: 8)
                        .foregroundColor(idx == selectedTab ? .primary : .secondary.opacity(0.5))
                        .onTapGesture {
                            selectedTab = idx
                        }
                }
            }
        }
    }
}


struct TabContentView: View {
    
    let title: String
    let color: Color
    
    var body: some View {
        Text(title).font(.title)
            .padding()
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(color.opacity(0.4), ignoresSafeAreaEdges: .all)
            .clipShape(RoundedRectangle(cornerRadius: 20))
    }
}
Onshore answered 5/1, 2023 at 11:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.