SwiftUI 更改内联 navigationBarTitleDisplayMode 的导航栏背景颜色

Posted

技术标签:

【中文标题】SwiftUI 更改内联 navigationBarTitleDisplayMode 的导航栏背景颜色【英文标题】:SwiftUI changing navigation bar background color for inline navigationBarTitleDisplayMode 【发布时间】:2021-11-10 17:51:51 【问题描述】:

我刚开始在 SwiftUI 中编码,遇到了一个问题。我需要为导航栏(NavigationView)的背景赋予不同的颜色。当我从一个视图转到下一个视图时,颜色会发生变化。我需要让 navigationBarTitleDisplayMode 成为“内联”。

我尝试了以下解决方案: SwiftUI update navigation bar title color 但这些解决方案都不能完全满足我的需要。

    该帖子的回复中的解决方案适用于内联: Using UIViewControllerRepresentable。然而,当我们第一次打开视图时,它会在更改为新颜色之前显示前一个视图的颜色一秒钟。我想避免这种情况,并在所有内容出现在屏幕上时立即显示颜色。有没有办法做到这一点?

    这个其他解决方案也不起作用:Changing UINavigation's appearance in init(),因为当我在 init() 中设置背景时,它会改变应用程序中所有视图的背景。同样,我需要视图具有不同的背景颜色。

    我尝试了类似于此解决方案的方法:Modifying Toolbar,但它不允许我更改导航栏的颜色。

    我尝试的另一个解决方案是:Creating navigationBarColor function,它基于:NAVIGATIONVIEW DYNAMIC BACKGROUND COLOR IN SWIFTUI。此解决方案适用于 navigationBarTitleDisplayMode "large",但是当将 navigationBarTitleDisplayMode 设置为 "inline" 时,它将以不同的颜色显示导航栏的背景颜色,就好像它被灰色/透明层覆盖一样。例如,它在“大”模式下显示的颜色是: Red color in large mode 但相反,它显示了这种颜色: Red color in inline mode

    最后,我尝试了这个解决方案:Subclassing UIViewController and configuring viewDidLayoutSubviews(),但它也没有达到我想要的效果。

我需要的最接近的解决方案是 1. 和 4.,但它们仍然不能 100% 工作。

是否有人知道如何使这些解决方案中的任何一个内联用于 navigationBarTitleDisplayMode,能够在不同布局中更改导航栏的背景颜色,并在视图显示后显示新颜色(无延迟)?

谢谢!

顺便说一句,我使用的是 XCode 12.5。


这是我正在使用的示例代码,以示例 4. 作为模型:

FirstView.swift

import SwiftUI

struct FirstView: View 
    @State private var selection: String? = nil
    
    var body: some View 
        
        NavigationView 
            GeometryReader  metrics in
                VStack 
                    Text("This is the first view")
                    
                    NavigationLink(destination: SecondView(), tag: "SecondView", selection: $selection) 
                        EmptyView()
                    
                    Button(action: 
                            self.selection = "SecondView"
                        print("Go to second view")
                    ) 
                        Text("Go to second view")
                    
                
            
        .navigationViewStyle(StackNavigationViewStyle())
        
    


struct FirstView_Previews: PreviewProvider 
    static var previews: some View 
        FirstView()
    

SecondView.swift

在这个屏幕上,如果我使用

.navigationBarTitleDisplayMode(.large)

颜色会正确显示:Navigation bar with red color 但是使用

.navigationBarTitleDisplayMode(.inline)

上面有一个模糊:Navigation bar with some sort of blur over red color

import SwiftUI

struct SecondView: View 
    @State private var selection: String? = nil
    
    var body: some View 
        GeometryReader  metrics in
            VStack 
                Text("This is the second view")
                
                NavigationLink(destination: ThirdView(), tag: "ThirdView", selection: $selection) 
                    EmptyView()
                
                Button(action: 
                        self.selection = "ThirdView"
                    print("Go to third view")
                ) 
                    Text("Go to third view")
                
            
        
        .navigationBarColor(backgroundColor: Color.red, titleColor: .black)
        .navigationBarTitleDisplayMode(.inline)
    


struct SecondView_Previews: PreviewProvider 
    static var previews: some View 
        SecondView()
    

ThirdView.swift

此视图在使用时正确显示颜色

.navigationBarTitleDisplayMode(.large)

但如果改为

.navigationBarTitleDisplayMode(.inline)

它也会在颜色上显示模糊。

import SwiftUI

struct ThirdView: View 
    var body: some View 
        GeometryReader  metrics in
            Text("This is the third view")
        
        .navigationBarColor(backgroundColor: Color.blue, titleColor: .black)
        .navigationBarTitleDisplayMode(.large)
    


struct ThirdView_Previews: PreviewProvider 
    static var previews: some View 
        ThirdView()
    

NavigationBarModifierView.swift

import SwiftUI

struct NavigationBarModifier: ViewModifier 

    var backgroundColor: UIColor?
    var titleColor: UIColor?
    

    init(backgroundColor: Color, titleColor: UIColor?) 
        self.backgroundColor = UIColor(backgroundColor)
        
        let coloredAppearance = UINavigationBarAppearance()
        coloredAppearance.configureWithTransparentBackground()
        coloredAppearance.backgroundColor = UIColor(backgroundColor)
        coloredAppearance.titleTextAttributes = [.foregroundColor: titleColor ?? .white]
        coloredAppearance.largeTitleTextAttributes = [.foregroundColor: titleColor ?? .white]
        coloredAppearance.shadowColor = .clear
        
        UINavigationBar.appearance().standardAppearance = coloredAppearance
        UINavigationBar.appearance().compactAppearance = coloredAppearance
        UINavigationBar.appearance().scrollEdgeAppearance = coloredAppearance
        UINavigationBar.appearance().tintColor = titleColor
    

    func body(content: Content) -> some View 
        ZStack
            content
            VStack 
                GeometryReader  geometry in
                    Color(self.backgroundColor ?? .clear)
                        .frame(height: geometry.safeAreaInsets.top)
                        .edgesIgnoringSafeArea(.top)
                    Spacer()
                
            
        
    


extension View 

    func navigationBarColor(backgroundColor: Color, titleColor: UIColor?) -> some View 
        self.modifier(NavigationBarModifier(backgroundColor: backgroundColor, titleColor: titleColor))
    



版主注意:请不要删除此帖子。我知道以前有人问过类似的问题,但我特别需要一个没有解决的答案。乱删前请先阅读,我工作需要这个。此外,我不能在每个解决方案中内联提问,因为我在 *** 中没有写到那里所需的最低 50 分。

【问题讨论】:

我们无法用所提供的信息真正回答您的问题。请参阅:How do I ask a good question? 和 How to create a Minimal, Reproducible Example。我的建议是整理一个示例来展示您的尝试,然后让用户进行操作。否则,这将成为没有任何细节的一般性讨论,并且没有任何解决方案。 @Yrb 示例已添加。谢谢! 为什么GeometryReaders有你的所有观点? @Yrb 添加了 GeometryReaders 是因为我们需要测量屏幕的大小,因为这是平面设计师制作的屏幕。我们经常需要测量比例等等。我尝试从所有地方删除 GeometryReaders,但问题仍然存在 【参考方案1】:

我想我有你想要的。它非常敏感...这是一个 hack,而且不是非常强大,所以按原样...

我让你的修改器返回一个清晰的导航栏,然后this answer 的解决方案为你工作。我什至在 ThirdView() 中添加了一个 ScrollView,以确保滚动不会影响 in。另外请注意,您会丢失所有其他内置效果,例如半透明等。

编辑:我检查了代码。 .navigationViewStyle 在错误的位置。它喜欢在 NavigaionView() 之外,其他一切都需要在里面。另外,我删除了在 FirstView() 中设置条形颜色的部分代码,因为它是多余的和丑陋的。我本来不想把它留在里面的。

struct NavigationBarModifier: ViewModifier 

    var backgroundColor: UIColor?
    var titleColor: UIColor?
    

    init(backgroundColor: Color, titleColor: UIColor?) 
        self.backgroundColor = UIColor(backgroundColor)
        
        let coloredAppearance = UINavigationBarAppearance()
        coloredAppearance.configureWithTransparentBackground()
        coloredAppearance.backgroundColor = .clear // The key is here. Change the actual bar to clear.
        coloredAppearance.titleTextAttributes = [.foregroundColor: titleColor ?? .white]
        coloredAppearance.largeTitleTextAttributes = [.foregroundColor: titleColor ?? .white]
        coloredAppearance.shadowColor = .clear
        
        UINavigationBar.appearance().standardAppearance = coloredAppearance
        UINavigationBar.appearance().compactAppearance = coloredAppearance
        UINavigationBar.appearance().scrollEdgeAppearance = coloredAppearance
        UINavigationBar.appearance().tintColor = titleColor
    

    func body(content: Content) -> some View 
        ZStack
            content
            VStack 
                GeometryReader  geometry in
                    Color(self.backgroundColor ?? .clear)
                        .frame(height: geometry.safeAreaInsets.top)
                        .edgesIgnoringSafeArea(.top)
                    Spacer()
                
            
        
    


extension View 
    func navigationBarColor(backgroundColor: Color, titleColor: UIColor?) -> some View 
        self.modifier(NavigationBarModifier(backgroundColor: backgroundColor, titleColor: titleColor))
    


struct FirstView: View 
    @State private var selection: String? = nil
    
    var body: some View 
         NavigationView 
            GeometryReader  _ in
                VStack 
                    Text("This is the first view")
                    
                    NavigationLink(destination: SecondView(), tag: "SecondView", selection: $selection) 
                        EmptyView()
                    
                    Button(action: 
                        self.selection = "SecondView"
                        print("Go to second view")
                    ) 
                        Text("Go to second view")
                    
                
                .navigationTitle("First")
                .navigationBarTitleDisplayMode(.inline)
                .navigationBarColor(backgroundColor: .red, titleColor: .black)
            
        
        .navigationViewStyle(StackNavigationViewStyle())
    

    

struct SecondView: View 
    @State private var selection: String? = nil
    
    var body: some View 
        VStack 
            Text("This is the second view")
            
            NavigationLink(destination: ThirdView(), tag: "ThirdView", selection: $selection) 
                EmptyView()
            
            Button(action: 
                self.selection = "ThirdView"
                print("Go to third view")
            ) 
                Text("Go to third view")
            
        
        .navigationTitle("Second")
        .navigationBarTitleDisplayMode(.inline)
        .navigationBarColor(backgroundColor: .blue, titleColor: .black)
    


struct ThirdView: View 
    var body: some View 
        ScrollView 
            ForEach(0..<50)  _ in
                Text("This is the third view")
            
        
        .navigationTitle("Third")
        .navigationBarTitleDisplayMode(.inline)
        .navigationBarColor(backgroundColor: .green, titleColor: .black)
    

【讨论】:

谢谢!让我试一试 太棒了,我认为这可能真的有效。我快速尝试了一下,做了一些小改动,看起来不错。不过,我还有两个问题。你知道如何让它在 iPad 上看起来像在 iPhone 上一样吗?我曾经用 .navigationViewStyle(StackNavigationViewStyle()) 解决这个问题,但它在这里没有做任何事情。如果第一次在 iPad 中加载,它会出现一个后退按钮,按下该按钮会从左侧调出视图,有点像侧边栏。 第二个问题是,您是否在构建时收到有关约束的警告,以及您是否知道如何修复它们(在这里变得很挑剔,因为它是对我正在从事的项目的干净改造)。我收到以下警告:[LayoutConstraints] 无法同时满足约束。以下列表中的至少一个约束可能是您不想要的。试试这个:(1)查看每个约束并尝试找出您不期望的; (2) 找到添加了不需要的约束的代码并修复它。 关于iPad问题,SO上有答案。我离开我的电脑去检查。它很容易解决。至于约束,你可以忽略它。它是 SwiftUI 引擎盖下的东西,不会影响任何东西。 我编辑了答案。

以上是关于SwiftUI 更改内联 navigationBarTitleDisplayMode 的导航栏背景颜色的主要内容,如果未能解决你的问题,请参考以下文章

Swift:SwiftUI 视图的多个条件的内联评估

SwiftUI DatePicker 应该是可编辑的内联

SwiftUI:动画更改依赖于@ObjectBinding

更改内联 SVG 的笔触颜色

更改 jquery datepickers 内联

SwiftUI:使用“if else”更改视图