34

Using a TabView as a pageviewer by using .tabViewStyle(PageTabViewStyle()) works fine, but trying to let it run from edge to edge by applying edgesIgnoringSafeArea does not seem to work.

What am I missing here?

struct ContentView: View {
    let colors: [Color] = [.red, .green, .blue]
    var body: some View {
        TabView {
            ForEach(0...2, id: \.self) { index in
                Rectangle()
                    .fill(colors[index])
            }
        }
        .tabViewStyle(PageTabViewStyle())
        .edgesIgnoringSafeArea(.all)
    }
}

TabView + PageTabViewStyle + edgesIgnoringSafeArea

Adding another .edgesIgnoringSafeArea(.all) to the Rectangle or ForEach also doen't work.

Note that all these questions are different because they do not use use PageTabViewStyle():

Their solution (adding edgesIgnoringSafeArea(.all)) doesn't work in this case.

malhal
  • 26,330
  • 7
  • 115
  • 133
Tieme
  • 62,602
  • 20
  • 102
  • 156

7 Answers7

36
  1. Remove .edgesIgnoringSafeArea(.all) from the TabView
  2. Add frame to the TabView with screen width and height
  3. Wrap a TabView with ScrollView
  4. Add .edgesIgnoringSafeArea(.all) to the ScrollView
struct ContentView: View {
    let colors: [Color] = [.red, .green, .blue]

    var body: some View {
        ScrollView {
            TabView {
                ForEach(0...2, id: \.self) { index in
                    Rectangle()
                        .fill(colors[index])
                }
            }
            .frame(
                width: UIScreen.main.bounds.width ,
                height: UIScreen.main.bounds.height
            )
            .tabViewStyle(PageTabViewStyle())
            
        }
        .edgesIgnoringSafeArea(.all)
    }
}
kimigori
  • 651
  • 6
  • 7
  • 2
    This works in iOS 14.3! I wasn't a fan of using UIScreen.main.bounds.width/height, so after I moved out the .edgesIgnoringSafeArea(.all) to the enclosing GeometryReader, I was able to get the full screen Tab View ignoring the safe area while setting the TabView's frame using geometry.size.width/height. Thanks!! – andrewcar Dec 21 '20 at 15:53
  • @andrewcar can you share how you did it with GeometryReader? – bze12 Jan 03 '21 at 21:28
  • 4
    @bze12 The principle is the same as above. Just wrap the `ScrollView` in a `GeometryReader` and replace the frame width/height with the ones from the geometry reader. This definitely feels like a bug in `TabView` with the `PageTabViewStyle` but at least the above is a good workaround. Was able to get a fairly complex view with top half of the view as the paged view and bottom half with other stuff working this way. – Mark Thormann Feb 03 '21 at 18:22
6

Update in SwiftUI 3.0:

 TabView {
            ForEach(0...2, id: \.self) { index in
                Rectangle()
                    .fill(colors[index])
            }
            .ignoresSafeArea()
        }
        .tabViewStyle(PageTabViewStyle())
        .edgesIgnoringSafeArea(.all)
ouflak
  • 2,458
  • 10
  • 44
  • 49
sjk.27
  • 83
  • 1
  • 5
5

Here is a maximum what I've got... anyway I assume that originally it is a bug and worth submitting feedback to Apple.

Tested with Xcode 12b

demo

struct TestPagingStyle: View {
    let colors: [Color] = [.red, .green, .blue]
    var body: some View {
        ZStack {
            Color.black.overlay(
                GeometryReader { gp in
                    TabView {
                        ForEach(0..<3, id: \.self) { index in
                            Text("Hello, World \(index)")
                                .frame(width: gp.size.width, height: gp.size.height)
                                .background(colors[index])
                        }
                    }
                    .tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
                }
            )
        }.edgesIgnoringSafeArea(.all)
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
4

Wrapping TabView in a ScrollView as suggested by @kimigori here works but causes the page dots to become scrollable. To fix them in place use a horizontal ScrollView.

This is a re-usable PageView that works with ignoresSafeArea(_:edges:):

struct PageView<Content: View>: View {
    let content: () -> Content

    var body: some View {
        GeometryReader { geo in
            ScrollView(.horizontal) {
                TabView {
                    content()
                }
                .frame(width: geo.size.width, height: geo.size.height)
                .tabViewStyle(PageTabViewStyle())
            }
        }
    }

    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }
}

Example:

TestPageView gif

struct TestPageView: View {
    var body: some View {
        PageView {
            ForEach(0...2, id: \.self) { index in
                ZStack {
                    Rectangle()
                        .fill([.red, .green, .blue][index])
                    Text("Page \(index + 1)")
                }
            }
        }
        .ignoresSafeArea()
    }
}
tim
  • 57
  • 7
3

I worked around this annoying issue by "extending" the TabView's height and "shifting" it towards the top by the same amount:

struct ContentView: View {
    var body: some View {
        let yExtension: CGFloat = 50
        GeometryReader { g in
            TabView {
                Color.red.overlay(Text("1"))
                Color.green.overlay(Text("2"))
                Color.blue.overlay(Text("3"))
                Color.orange.overlay(Text("4"))
            }
            .frame(width: g.size.width, height: g.size.height + yExtension)
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .automatic))
            .font(Font.title.bold())
        }
        .offset(y: -yExtension)
        .edgesIgnoringSafeArea(.all)
    }
}

Tested on Xcode 12.4, iOS 14.4:

demo animation

markiv
  • 1,578
  • 16
  • 12
  • Your solution is better than what Apple delivers with its bug-prone `PageTabViewStyle`. However, your solution does not allow for rotating into Landscape. When you do so, then the page-index jumps to the beginning (and that is most likely not what the user wants). The [ScrollView-approach that kimigory showed above](https://stackoverflow.com/a/65127817/3826232) is a bit better in that respect. – iKK Mar 31 '21 at 11:58
1

Just use:

.ignoresSafeArea(edges:[.top,.bottom])

I suppose that TabView can't ignore leading and trailing, which is included in .all. However, if you manually set .top and .bottom it works fine.

Timmy
  • 4,098
  • 2
  • 14
  • 34
Sergey Udalov
  • 75
  • 1
  • 7
1

Found this by accident, so hope it works for others. Works for me on iOS15 and 16, YMMV.

Add a background color to the TabView:

TabView(selection: $index) {
    YourTabViewContent
    .ignoresSafeArea()
}
.background(Color.background)
.tabViewStyle(.page(indexDisplayMode: .never))
.edgesIgnoringSafeArea(.all)
Rob Phillips
  • 295
  • 3
  • 9