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 的导航栏背景颜色的主要内容,如果未能解决你的问题,请参考以下文章