LiangWang
LiangWang

Reputation: 8816

Vertical Paging in SwiftUI

I'm creating a vertical paging view via TabView following this

Everything is perfect except the strange right margin as highlighted in pic below.

Here is the code I use. Appreciate it if anyone could point out the root cause.

enter image description here

import SwiftUI

fileprivate struct VCompatibleTabView<Content: View>: View {
    let proxy: GeometryProxy
    let content: Content
    
    init(proxy: GeometryProxy, @ViewBuilder content: () -> Content) {
        self.proxy = proxy
        self.content = content()
    }
    
    var body: some View {
        if #available(iOS 15.0, *) {
            // Credit to Gary Tokmen for this bit of Geometry Reader code: https://blog.prototypr.io/how-to-vertical-paging-in-swiftui-f0e4afa739ba
            TabView {
                content
                .rotationEffect(.degrees(-90)) // Rotate content
                .frame(
                    width: proxy.size.width,
                    height: proxy.size.height
                )
            }
            .frame(
                width: proxy.size.height, // Height & width swap
                height: proxy.size.width
            )
            .rotationEffect(.degrees(90), anchor: .topLeading) // Rotate TabView
            .offset(x: proxy.size.width) // Offset back into screens bounds
            .tabViewStyle(
                PageTabViewStyle(indexDisplayMode: .never)
            )
        } else {
            ScrollView(.vertical, showsIndicators: false) {
                LazyVStack(spacing: 0) {
                    content
                }
            }
            .frame(
                width: proxy.size.width,
                height: proxy.size.height)        }
    }
}

struct BBYouView: View {

    var body: some View {
        ZStack {
            GeometryReader { proxy in
                VCompatibleTabView(proxy: proxy) {
                    ForEach(0..<3, id: \.self) { item in
                        Rectangle().fill(Color.pink)
                        .frame(
                            width: proxy.size.width,
                            height: proxy.size.height
                        )
                    }
                }
            }
        }
        .background(Color.yellow)
    }


}

Upvotes: 6

Views: 2918

Answers (3)

David Shi
David Shi

Reputation: 206

Try adding the scaledToFill() modifier on the content view within the TabView. That modifier scales the view to fill its parent. I've found that it works when the content view is an AsyncImage or Image view and it should work with any single views. However, with a horizontal TabView and it had the same issue with the strange margin on the right edge of the screen.

Example code below:

  TabView {
                    content.scaledToFill()
                    .rotationEffect(.degrees(-90)) // Rotate content
                    .frame(
                        width: proxy.size.width,
                        height: proxy.size.height
                    )
                }
                .frame(
                    width: proxy.size.height, // Height & width swap
                    height: proxy.size.width
                )
                .rotationEffect(.degrees(90), anchor: .topLeading) // Rotate TabView
                .offset(x: proxy.size.width) // Offset back into screens bounds
                .tabViewStyle(
                    PageTabViewStyle(indexDisplayMode: .never)
                )
}

Upvotes: 0

Oleksandr
Oleksandr

Reputation: 189

There is iOS17 modifier .scrollTargetBehavior(.paging), for older versions this hack should work:

import SwiftUIIntrospect

GeometryReader { proxy in
    ScrollView {
        LazyVStack(spacing: 0) {
            ForEach([Color.red, Color.green, Color.blue], id: \.self) { color in
                color.frame(proxy.size)
            }
        }
    }
    .introspect(.scrollView, on: .iOS(.v15, .v16, .v17)) { sv in
        sv.isPagingEnabled = true
    }
}

Upvotes: 3

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 119108

Horizontal/Vertical paging in iOS 17

From iOS 17, you don't need to do the hack of rotating the tab view and you can enable the paging behavior by applying the following modifier on the ScrollView:

.scrollTargetBehavior(.paging)

So anything inside the ScrollView like a simple ForEach inside a stack would be paging as desired. This works for both horizontal and vertical scrollviews.

Upvotes: 4

Related Questions