mobile - Multiplatform SwiftUI app with a next layout: a top bar, bottom tab bar and a viewport in the middle - Stack Overflow

I'd like to create my pet project on SwiftUI for iPhone, iPad and MacOs (Mac Catalyst).My idea to

I'd like to create my pet project on SwiftUI for iPhone, iPad and MacOs (Mac Catalyst). My idea to have the next layout on all devices: A top bar (with buttons) above, a bottom tab bar at the bottom, and a Viewport view in the middle, taking all remaining area.

I also would like to get the size of the Viewport and save it in a global variable (ObservableObject): But every time I have some issue on different devices (sometimes the Viewport covers all screen or overlapping with a top bar or with a tab bar.

class GlobalUI: ObservableObject {
  @Published var viewportSize: CGSize = .zero // Store available viewport size
}


struct ContentView: View {
@EnvironmentObject var globalUI: GlobalUI
@State var selectedTab: TabSelection = .first

var body: some View {
    GeometryReader { geometry in
        VStack(spacing: 0) {
            // Top Bar
            CustomTopBar(selectedTab: selectedTab)
                .ignoresSafeArea(edges: .top)
            
            // Main Viewport
            Viewport(selectedTab: selectedTab)
                .frame(height: geometry.size.height - 100)
            
            // Bottom Tab Bar
            CustomTabBar(selectedTab: $selectedTab)
                .frame(height: 50)
                .padding(.bottom, getSafeAreaBottomInset())
                .background(Color("BrandGreen"))
                .ignoresSafeArea(edges: .bottom)
        }
    }
    .background(Color.black)
    .edgesIgnoringSafeArea(.all)
}
}

// another attempt:

struct ContentView: View {
@EnvironmentObject var globalUI: GlobalUI
@State var selectedTab: TabSelection = .first

func getSafeAreaBottomInset() -> CGFloat {
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
        return 0
    }
    return windowScene.windows.first?.safeAreaInsets.bottom ?? 0
}

var body: some View {
    VStack(spacing: 0) {
        CustomTopBar(selectedTab: selectedTab)
            .expandViewOutOfSafeArea(.top)
        
        GeometryReader { geometry in
            VStack(spacing: 0) {
                Viewport(selectedTab: selectedTab)
                    .onAppear {
                        let newSize = geometry.size
                        if newSize.width > 0 && newSize.height > 0 {
                            globalUI.viewportSize = newSize
                            print("viewport size updated: \(newSize)")
                        }
                    }
                    .onChange(of: geometry.size) { newSize in
                        if newSize.width > 0 && newSize.height > 0 {
                            globalUI.viewportSize = newSize
                            print("viewport size updated: \(newSize)")
                        }
                    }
                    .frame(maxWidth: .infinity, maxHeight: geometry.size.height - getSafeAreaBottomInset() - 100)  // Adjust height to exclude CustomTabBar height

                Spacer()
            }
        }
        
        CustomTabBar(selectedTab: $selectedTab)
            .frame(height: 50)
            .padding(.bottom, getSafeAreaBottomInset())
            .background(Color("BrandGreen")) // Ensures a solid background
    }
    .frame(maxWidth: .infinity, maxHeight: .infinity)
    .background(Color.black)
    .ignoresSafeArea(edges: [.horizontal, .bottom])
}
}

I'd like to create my pet project on SwiftUI for iPhone, iPad and MacOs (Mac Catalyst). My idea to have the next layout on all devices: A top bar (with buttons) above, a bottom tab bar at the bottom, and a Viewport view in the middle, taking all remaining area.

I also would like to get the size of the Viewport and save it in a global variable (ObservableObject): But every time I have some issue on different devices (sometimes the Viewport covers all screen or overlapping with a top bar or with a tab bar.

class GlobalUI: ObservableObject {
  @Published var viewportSize: CGSize = .zero // Store available viewport size
}


struct ContentView: View {
@EnvironmentObject var globalUI: GlobalUI
@State var selectedTab: TabSelection = .first

var body: some View {
    GeometryReader { geometry in
        VStack(spacing: 0) {
            // Top Bar
            CustomTopBar(selectedTab: selectedTab)
                .ignoresSafeArea(edges: .top)
            
            // Main Viewport
            Viewport(selectedTab: selectedTab)
                .frame(height: geometry.size.height - 100)
            
            // Bottom Tab Bar
            CustomTabBar(selectedTab: $selectedTab)
                .frame(height: 50)
                .padding(.bottom, getSafeAreaBottomInset())
                .background(Color("BrandGreen"))
                .ignoresSafeArea(edges: .bottom)
        }
    }
    .background(Color.black)
    .edgesIgnoringSafeArea(.all)
}
}

// another attempt:

struct ContentView: View {
@EnvironmentObject var globalUI: GlobalUI
@State var selectedTab: TabSelection = .first

func getSafeAreaBottomInset() -> CGFloat {
    guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
        return 0
    }
    return windowScene.windows.first?.safeAreaInsets.bottom ?? 0
}

var body: some View {
    VStack(spacing: 0) {
        CustomTopBar(selectedTab: selectedTab)
            .expandViewOutOfSafeArea(.top)
        
        GeometryReader { geometry in
            VStack(spacing: 0) {
                Viewport(selectedTab: selectedTab)
                    .onAppear {
                        let newSize = geometry.size
                        if newSize.width > 0 && newSize.height > 0 {
                            globalUI.viewportSize = newSize
                            print("viewport size updated: \(newSize)")
                        }
                    }
                    .onChange(of: geometry.size) { newSize in
                        if newSize.width > 0 && newSize.height > 0 {
                            globalUI.viewportSize = newSize
                            print("viewport size updated: \(newSize)")
                        }
                    }
                    .frame(maxWidth: .infinity, maxHeight: geometry.size.height - getSafeAreaBottomInset() - 100)  // Adjust height to exclude CustomTabBar height

                Spacer()
            }
        }
        
        CustomTabBar(selectedTab: $selectedTab)
            .frame(height: 50)
            .padding(.bottom, getSafeAreaBottomInset())
            .background(Color("BrandGreen")) // Ensures a solid background
    }
    .frame(maxWidth: .infinity, maxHeight: .infinity)
    .background(Color.black)
    .ignoresSafeArea(edges: [.horizontal, .bottom])
}
}
Share Improve this question asked Mar 3 at 14:08 Alex PiluginAlex Pilugin 7052 gold badges11 silver badges39 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Instead of forcing the Viewport to adopt the size you give it, just let it expand to use all the space available. This is done by applying a frame with maxWidth: .infinity, maxHeight: .infinity.

If you need to know the size of the viewport, use .onGeometryChange to read it. A GeometryReader is not needed.

Btw, when you apply a background color using background(_:ignoresSafeAreaEdges:), the safe area insets are ignored by default. So there is no need to add a modifier to ignore them explicitly.

struct ContentView: View {
    @EnvironmentObject var globalUI: GlobalUI
    @State var selectedTab: TabSelection = .first

    var body: some View {
        VStack(spacing: 0) {
            // Top Bar
            CustomTopBar(selectedTab: selectedTab)
                .ignoresSafeArea(edges: .top)

            // Main Viewport
            Viewport(selectedTab: selectedTab)
                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
                .onGeometryChange(for: CGSize.self) { proxy in
                    proxy.size
                } action: { size in
                    globalUI.viewportSize = size
                }

            // Bottom Tab Bar
            CustomTabBar(selectedTab: $selectedTab)
                .frame(height: 50)
                .background(Color("BrandGreen"))
        }
        .background(.black)
    }
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745090692a4610677.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信