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
1 Answer
Reset to default 1Instead 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条)