2

I have a page with a player that has a height of 1/3 of the screen height. Under the channels, there are multiple channels inside a scroll view. I am trying to set the height of the scroll view section exactly from bottom of the player to bottom of the page. I tried to set it to (screen height - (playerheight) - (navigation bar height) - (tabbed bar height)). But the problem is that the tabbed bar height changes from device to device. I tried the following code:

.frame(height: (UIScreen.main.bounds.height) - ((UIScreen.main.bounds.height) * 2/6) - (geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom ) )

But it looks like geometry.safeAreaInsets.bottom does not contain the tabbed bar height. So is there any way to calculate it in SwiftUI or is there any other way to achieve a similar appearance?

I just want to place scrollview that contains the channels exactly between player and tab bar for all screen sizes.

sample design

C.Aglar
  • 1,290
  • 2
  • 14
  • 30
  • Does this answer your question? [Programmatically detect Tab Bar or TabView height in SwiftUI](https://stackoverflow.com/questions/59969911/programmatically-detect-tab-bar-or-tabview-height-in-swiftui) – Asperi Jan 31 '20 at 21:52
  • The question has already been answered, though I think the following solution is much simpler. In the initializer of your app (or any View you want) you can create a UITabBarController object and then access its size with UITabBarController().controller.tabBar.frame.size – Simon Henn Nov 20 '22 at 13:10

1 Answers1

3

I'm just starting to look at SwiftUI, but... I'm under the impression GeometryReader should handle this for you.

Give this a try:

struct Tab1View: View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Text("Top == 1/3 height")
                    .frame(width: geometry.size.width, height: geometry.size.height / 3.0, alignment: .center)
                    .background(Color.init(red: 0.1, green: 0.1, blue: 0.5))
                    .foregroundColor(Color.white)
                    .border(Color.yellow)
                Text("Bottom == 2/3 height")
                    .frame(width: geometry.size.width, height: geometry.size.height * 2.0 / 3.0, alignment: .center)
                    .background(Color.init(red: 0.5, green: 0.1, blue: 0.1))
                    .foregroundColor(Color.white)
                    .border(Color.yellow)
            }
            .frame(width: geometry.size.width,
                   height: geometry.size.height,
                   alignment: .topLeading)
        }
    }

}
struct Tab2View: View {
    var body: some View {
        Color.blue
    }
}

struct MyTabView: View {
    var body: some View {

        TabView {
            //Text("The content of the first view")
            Tab1View()
                .tabItem {
                    Image(systemName: "phone.fill")
                    Text("First Tab")
            }
            //Text("The content of the second view")
            Tab2View()
                .tabItem {
                    Image(systemName: "tv.fill")
                    Text("Second Tab")
            }
        }
    }
}

struct MyTabView_Previews: PreviewProvider {
    static var previews: some View {
        MyTabView()
    }
}

Result:

enter image description here

and, auto-adjusted to TabBar height when rotated:

enter image description here


Edit

After some more investigation, it looks like ScrollView can size itself to fill available space, so we only need to set the height on the top view.

Here's a modified example:

struct MyItemView: View {
    var itemDesc = "Testing"
    var geoWidth: CGFloat = 100
    var body: some View {
            Text("\(self.itemDesc)")
                .foregroundColor(Color.white)
                .padding(16.0)
                .frame(width: self.geoWidth, height: nil, alignment: .leading)
    }
}

struct Tab1View: View {
    var body: some View {
        GeometryReader { geometry in
            VStack(spacing: 0.0) {
                Text("Top == 1/3 height")
                    .frame(width: geometry.size.width, height: geometry.size.height * 1.0 / 3.0, alignment: .center)
                    .background(Color.init(red: 0.1, green: 0.1, blue: 0.5))
                    .foregroundColor(Color.white)
                    .border(Color.yellow)

                ScrollView(.vertical) {
                    VStack(alignment: .leading, spacing: 0) {
                        ForEach((1...20), id: \.self) {
                            MyItemView(itemDesc: "This is item \($0)", geoWidth: geometry.size.width)
                                .background(Color.init(red: 0.5, green: 0.1, blue: 0.1))
                                .border(Color.yellow)
                        }
                    }
                }
                .frame(width: geometry.size.width, height: nil, alignment: .leading)
            }
        }
    }
}
struct Tab2View: View {
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Text("Top == 1/3 height")
                    .frame(width: geometry.size.width, height: geometry.size.height * 1.0 / 3.0, alignment: .center)
                    .background(Color.init(red: 0.1, green: 0.1, blue: 0.7))
                    .foregroundColor(Color.white)
                    .border(Color.yellow)
                Text("Bottom == 2/3 height")
                    .frame(width: geometry.size.width, height: geometry.size.height * 2.0 / 3.0, alignment: .center)
                    .background(Color.init(red: 0.2, green: 0.6, blue: 0.1))
                    .foregroundColor(Color.white)
                    .border(Color.yellow)
            }
        }
    }
}
struct MyTabView: View {
    var body: some View {
        TabView {
            Tab1View()
                .tabItem {
                    Image(systemName: "phone.fill")
                    Text("First Tab")
            }
            Tab2View()
                .tabItem {
                    Image(systemName: "tv.fill")
                    Text("Second Tab")
            }
        }
    }
}

struct MyTabView_Previews: PreviewProvider {
    static var previews: some View {
        MyTabView()
    }
}

and, new result:

enter image description here

enter image description here

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • setting the frame of VStack embedded in GeometryReader is redundant, isn't it? – user3441734 Feb 01 '20 at 10:57
  • Very possibly/probably. I started with getting the VStack to fill the height, thinking there’d be a “distribution: fill” setting like UIStackView. So, yeah. – DonMag Feb 01 '20 at 12:18
  • 1
    @user3441734 - yep... quick test shows setting `.frame` on the `VStack` is not needed. – DonMag Feb 01 '20 at 13:05
  • 1
    @user3441734 - as I mentioned, I have ***just started*** looking at SwiftUI, so forgive any obvious mistakes. I've edited my answer with implementation of a scroll view for the lower 2/3 space. – DonMag Feb 01 '20 at 15:16