如何在SwiftUI中为每个TabView项更改状态栏文本颜色?

3
您可以找到许多(UIKit)解决方案来设置SwiftUI视图状态栏的文本颜色。不幸的是,在我的经验中,这些解决方案似乎在运行时不能令人满意地为TabViews工作。
找到的解决方案: SwiftUI:为特定视图设置状态栏颜色 如何在SwiftUI中每个视图更改状态栏文本颜色? 使用这些解决方案,您将很快发现在尝试在选项卡之间切换时文本颜色并不总是正确设置的。
不幸的是,目前苹果似乎没有直接的解决方案来改变SwiftUI中每个视图的UIStatusBarStyle,就像UIKit中可能做到的那样。
我只是找不到一种稳定的方法来在运行时更改每个选项卡的状态栏文本颜色。

enter image description here

3个回答

2

我看了其他解决方案,但没有尝试。但是以下方法有效,我认为这也使得其他解决方案能够工作。

  1. 进入您的 Info.plist 并将 UIViewControllerBasedStatusBarAppearance 设置为 NO
  2. 从此处开始,我假设您提到的解决方案也可以工作。

如果您不介意在代码中出现弃用警告,这也是一种选择:

TabView {
    Text("1")
        .tabItem { Text("1") }
        .onAppear {
            UIApplication.shared.setStatusBarStyle(.lightContent, animated: false)
        }

    Text("2")
        .tabItem { Text("2") }
        .onAppear {
            UIApplication.shared.setStatusBarStyle(.darkContent, animated: true)
        }
}

但是setStatusBarStyle已过时,因此可能不是最佳选项。


2
我现在可以自己解决了。我找到了一个方法,可以处理与TabViews相关的问题,基于:https://github.com/xavierdonnellon/swiftui-statusbarstyle 我的完整代码示例,其中保存和恢复了先前状态栏样式的状态(在运行时保持对状态栏样式层次结构的关注非常重要):

ExampleApp.swift

import SwiftUI

@main
struct ExampleApp: App {
    
    init() {
        //navigation bar
        let coloredNavAppearance = UINavigationBarAppearance()
        coloredNavAppearance.configureWithTransparentBackground()
        coloredNavAppearance.backgroundColor = .clear
        coloredNavAppearance.backgroundEffect = nil
        coloredNavAppearance.backgroundImage = UIImage()
        coloredNavAppearance.shadowImage = UIImage()
        coloredNavAppearance.shadowColor = .clear
        coloredNavAppearance.titleTextAttributes = [
            .foregroundColor: UIColor.black
        ]
        coloredNavAppearance.largeTitleTextAttributes = [
            .foregroundColor: UIColor.black
        ]
        
        UINavigationBar.appearance().standardAppearance = coloredNavAppearance
        UINavigationBar.appearance().scrollEdgeAppearance = coloredNavAppearance
        UINavigationBar.appearance().compactAppearance = coloredNavAppearance
        
    }
    
    var body: some Scene {
        WindowGroup {
            RootView(content: {
                ContentView()
            })
        }
    }
}

UIApplication+StatusBarStyle.swift

extension UIApplication {
    static var hostingController: HostingController<AnyView>? = nil
    
    static var statusBarStyleHierarchy: [UIStatusBarStyle] = []
    static var statusBarStyle: UIStatusBarStyle = .darkContent
    
    ///Sets the App to start at rootView
    func setHostingController(rootView: AnyView) {
        let hostingController = HostingController(rootView: AnyView(rootView))
        windows.first?.rootViewController = hostingController
        UIApplication.hostingController = hostingController
    }
    
    static func setStatusBarStyle(_ style: UIStatusBarStyle) {
        statusBarStyle = style
        hostingController?.setNeedsStatusBarAppearanceUpdate()
    }
}

class HostingController<Content: View>: UIHostingController<Content> {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return UIApplication.statusBarStyle
    }
}

///By wrapping views in a RootView, they will become the app's main / primary view. This will enable setting the statusBarStyle.
struct RootView<Content: View> : View {
    var content: Content
    
    init(@ViewBuilder content: () -> (Content)) {
        self.content = content()
    }
    
    var body:some View {
        EmptyView()
            .onAppear {
                UIApplication.shared.setHostingController(rootView: AnyView(content))
            }
    }
}

ContentView.swift

import SwiftUI

struct ContentView: View {
    
    @State var tabRoute = TabRoute.green
    
    enum TabRoute: String, Identifiable {
        
        var id: String {
            self.rawValue
        }
        
        case green, black, blue, purple
        
        static var all: [TabRoute] {
            [.green, .black, .blue, .purple]
        }
        
        var bgColor: Color {
            switch self {
            case .green:
                return .green
                
            case .black:
                return .black
                
            case .blue:
                return .blue
                
            case .purple:
                return .purple
                
            }
        }
        
        var statusBarStyle: UIStatusBarStyle {
            switch self {
            case .black:
                return .lightContent
                
            default:
                return .darkContent
                
            }
        }
    }
    
    
    var body: some View {
        
        TabView(selection: $tabRoute) {
            
            ForEach(TabRoute.all) { route in
                NavigationView {
                    ViewExample(isPresented: false, title: route.rawValue, bgColor: route.bgColor, statusBarStyle: route.statusBarStyle)
                        .navigationTitle(route.rawValue)
                        .navigationBarTitleDisplayMode(.inline)
                }
                .navigationViewStyle(StackNavigationViewStyle())
                .tabItem {
                    Text(route.rawValue)
                }
                .tag(TabRoute(rawValue: route.rawValue))
            }
            
        }
        .edgesIgnoringSafeArea(.all)
    }
    
}

struct ViewExample: View {
    
    @Environment(\.presentationMode) var presentationMode

    @State private var sheetIsPresented = false
    
    @State  var isPresented: Bool
    
    var title: String
    
    var bgColor: Color
    
    @State var statusBarStyle: UIStatusBarStyle
    
    var body: some View {
        ZStack {
            bgColor
        }
        .onAppear {
            UIApplication.statusBarStyleHierarchy.append(statusBarStyle)
            UIApplication.setStatusBarStyle(statusBarStyle)
        }
        .onDisappear {
            guard UIApplication.statusBarStyleHierarchy.count > 1 else { return }
            let style = UIApplication.statusBarStyleHierarchy[UIApplication.statusBarStyleHierarchy.count - 1]
            UIApplication.statusBarStyleHierarchy.removeLast()
            UIApplication.setStatusBarStyle(style)
        }
        .edgesIgnoringSafeArea(.all)
    }
}

谢谢分享!这是一个很好的即插即用解决方案。完美地工作。我仍然希望在iOS 17中有一个原生的SwiftUI解决方案。 - codingFriend1

-1
关键点
  • 使用 .preferredColorScheme()
  • .preferredColorScheme()NavigationStack 中使用上一个 View 的设置
实现
import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: ContentViewFirst()) {
                Text("navigation")
            }
            .isDetailLink(false)
        }
    }
}

struct ContentViewFirst: View {
    
    @State var colorScheme:ColorScheme = .dark
    
    var body: some View {
        GeometryReader { geometryReader in
            ZStack {
                Color.black.ignoresSafeArea()
                VStack {
                    NavigationLink(destination: ContentViewSecond(colorScheme: self.$colorScheme)) {
                        Text("ColorScheme = .dark")
                            .font(.largeTitle)
                            .bold()
                            .foregroundColor(.white)
                    }
                    .isDetailLink(false)
                }
            }
        }
        .preferredColorScheme(colorScheme) 
    }
}

struct ContentViewSecond: View {
    
    @Binding var colorScheme:ColorScheme

    var body: some View {
        GeometryReader { geometryReader in
            ZStack {
                Color.white.ignoresSafeArea() 
                VStack {
                    Text("ColorScheme = .light")
                        .font(.largeTitle)
                        .bold()
                }
            }
        }
        .onAppear {
            self.colorScheme = .light
        }
        .onDisappear {
            self.colorScheme = .dark
        }
    }
}

Result


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接