SwiftUI 更改导航栏背景颜色以进行内联导航BarTitleDisplayMode

SwiftUI changing navigation bar background color for inline navigationBarTitleDisplayMode

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

我尝试了以下提供的解决方案: 但是 none 这些解决方案完全可以满足我的需要。

  1. 此回复中对 post 的解决方案适用于内联: 。尽管如此,当我们第一次打开视图时,它会显示前一个视图的颜色一秒钟,然后再更改为新颜色。我想避免这种情况,并在屏幕上出现所有内容后立即显示颜色。有办法吗?

  2. 这个其他解决方案也行不通:,因为当我在 init() 中设置背景时,它会更改应用程序中所有视图的背景。同样,我需要视图具有不同的背景颜色。

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

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

  5. 最后,我尝试了这个解决方案:,但它也没有达到我想要的效果。

最接近我需要的解决方案是 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))
    }

}

版主注意事项:请不要删除此 post。我知道之前有人问过类似的问题,但我特别需要一个没有解决的问题的答案。乱删前请先阅读,我工作需要这个。另外,我不能在每个解决方案中直接提问,因为我没有至少 50 点的 Whosebug 要求在那里写。

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

我通过让你的修饰符 return 有一个清晰的导航栏来让它工作,然后来自 的解决方案对你有用。我什至在 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)
    }
}

这是一个有点老套的解决方案,但它适用于我(从 iOS 15 开始).large.inline 显示模式。

import SwiftUI

enum Kind: String, CaseIterable {
    case checking
    case savings
    case investment
}

struct PaddedList: View {
    @Binding var name: String
    @Binding var kind: Kind
    
    var body: some View {
        NavigationView {
            List {
                TextField("Account name", text: $name)
                Picker("Kind", selection: $kind) {
                    ForEach(Kind.allCases, id: \.self) { kind in
                        Text(kind.rawValue).tag(kind)
                    }
                }
                .listRowSeparatorTint(.red)
                Spacer()
            }
            .padding(.top, 1) // note top 1 padding!
            .background(.green) // the color "bleeds" through
            .navigationBarTitle("Navigation Bar")
        }
    }
}

struct PaddedList_Previews: PreviewProvider {
    static var previews: some View {
        PaddedList(name: .constant(""), kind: .constant(.checking))
    }
}