我有一个超级简单的SwiftUI主从应用程序:
import SwiftUI
struct ContentView: View {
@State private var imageNames = [String]()
var body: some View {
NavigationView {
MasterView(imageNames: $imageNames)
.navigationBarTitle(Text("Master"))
.navigationBarItems(
leading: EditButton(),
trailing: Button(
action: {
withAnimation {
// simplified for example
self.imageNames.insert("image", at: 0)
}
}
) {
Image(systemName: "plus")
}
)
}
}
}
struct MasterView: View {
@Binding var imageNames: [String]
var body: some View {
List {
ForEach(imageNames, id: \.self) { imageName in
NavigationLink(
destination: DetailView(selectedImageName: imageName)
) {
Text(imageName)
}
}
}
}
}
struct DetailView: View {
var selectedImageName: String
var body: some View {
Image(selectedImageName)
}
}
我还要在SceneDelegate上为导航栏的颜色设置外观代理”。
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.shadowColor = UIColor.systemYellow
navBarAppearance.backgroundColor = UIColor.systemYellow
navBarAppearance.shadowImage = UIImage()
UINavigationBar.appearance().standardAppearance = navBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView()
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
现在,我想做的是使导航栏的背景颜色更改为在出现详细视图时清除。我仍然需要该视图中的后退按钮,因此隐藏导航栏并不是一个理想的解决方案。我还希望更改仅适用于“详细信息”视图,因此当我弹出该视图时,外观代理应该接管,如果我推送到另一个控制器,则外观代理也应接管。
我一直在尝试各种各样的事情:
-更改didAppear
上的外观代理
-将详细信息视图包装为UIViewControllerRepresentable
(成功有限,我可以进入导航栏并更改其颜色,但是由于某种原因,导航控制器不止一个)
在SwiftUI中有一种简单的方法吗?
答案 0 :(得分:6)
我更喜欢使用ViewModifer。下面是我的自定义ViewModifier
struct NavigationBarModifier: ViewModifier {
var backgroundColor: UIColor?
init(backgroundColor: UIColor?) {
self.backgroundColor = backgroundColor
let coloredAppearance = UINavigationBarAppearance()
coloredAppearance.configureWithTransparentBackground()
coloredAppearance.backgroundColor = backgroundColor
coloredAppearance.titleTextAttributes = [.foregroundColor: UIColor.white]
coloredAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
UINavigationBar.appearance().standardAppearance = coloredAppearance
UINavigationBar.appearance().compactAppearance = coloredAppearance
UINavigationBar.appearance().scrollEdgeAppearance = coloredAppearance
UINavigationBar.appearance().tintColor = .white
}
func body(content: Content) -> some View {
ZStack{
content
VStack {
GeometryReader { geometry in
Color(self.backgroundColor ?? .clear)
.frame(height: geometry.safeAreaInsets.top)
.edgesIgnoringSafeArea(.top)
Spacer()
}
}
}
}}
您也可以使用不同的文本颜色和颜色为栏初始化它,我现在已经添加了静态颜色。
您可以从任何一个调用此修饰符。就您而言
NavigationLink(
destination: DetailView(selectedImageName: imageName)
.modifier(NavigationBarModifier(backgroundColor: .green))
)
答案 1 :(得分:0)
我最终创建了一个自定义包装,其中显示了未附加到当前UINavigationController的UINavigationBar。就像这样:
final class TransparentNavigationBarContainer<Content>: UIViewControllerRepresentable where Content: View {
private let content: () -> Content
init(content: @escaping () -> Content) {
self.content = content
}
func makeUIViewController(context: Context) -> UIViewController {
let controller = TransparentNavigationBarViewController()
let rootView = self.content()
.navigationBarTitle("", displayMode: .automatic) // needed to hide the nav bar
.navigationBarHidden(true)
let hostingController = UIHostingController(rootView: rootView)
controller.addContent(hostingController)
return controller
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) { }
}
final class TransparentNavigationBarViewController: UIViewController {
private lazy var navigationBar: UINavigationBar = {
let navBar = UINavigationBar(frame: .zero)
navBar.translatesAutoresizingMaskIntoConstraints = false
let navigationItem = UINavigationItem(title: "")
navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "chevron.left"),
style: .done,
target: self,
action: #selector(back))
let appearance = UINavigationBarAppearance()
appearance.backgroundImage = UIImage()
appearance.shadowImage = UIImage()
appearance.backgroundColor = .clear
appearance.configureWithTransparentBackground()
navigationItem.largeTitleDisplayMode = .never
navigationItem.standardAppearance = appearance
navBar.items = [navigationItem]
navBar.tintColor = .white
return navBar
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.navigationBar)
NSLayoutConstraint.activate([
self.navigationBar.leftAnchor.constraint(equalTo: view.leftAnchor),
self.navigationBar.rightAnchor.constraint(equalTo: view.rightAnchor),
self.navigationBar.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor)
])
}
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
guard let parent = parent else {
return
}
NSLayoutConstraint.activate([
parent.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
parent.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
parent.view.topAnchor.constraint(equalTo: self.view.topAnchor),
parent.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
])
}
@objc func back() {
self.navigationController?.popViewController(animated: true)
}
fileprivate func addContent(_ contentViewController: UIViewController) {
contentViewController.willMove(toParent: self)
self.addChild(contentViewController)
contentViewController.view.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(contentViewController.view)
NSLayoutConstraint.activate([
self.view.topAnchor.constraint(equalTo: contentViewController.view.safeAreaLayoutGuide.topAnchor),
self.view.bottomAnchor.constraint(equalTo: contentViewController.view.bottomAnchor),
self.navigationBar.leadingAnchor.constraint(equalTo: contentViewController.view.leadingAnchor),
self.navigationBar.trailingAnchor.constraint(equalTo: contentViewController.view.trailingAnchor)
])
self.view.bringSubviewToFront(self.navigationBar)
}
}
需要做一些改进,例如显示自定义导航栏按钮,支持“向后轻扫”和其他几项功能。
答案 2 :(得分:0)
在我看来,这是 SwiftUI 中最直接的解决方案。
问题:framework adds back buttom in the DetailView 解决方案:Custom back button and nav bar are rendered
struct DetailView: View {
var selectedImageName: String
@Environment(\.presentationMode) var presentationMode
var body: some View {
CustomizedNavigationController(imageName: selectedImageName) { backButtonDidTapped in
if backButtonDidTapped {
presentationMode.wrappedValue.dismiss()
}
} // creating customized navigation bar
.navigationBarTitle("Detail")
.navigationBarHidden(true) // Hide framework driven navigation bar
}
}
如果框架驱动的导航栏没有隐藏在细节视图中,我们会得到两个这样的导航栏:double nav bars
使用 UINavigationBar.appearance() 在一些场景中是非常不安全的,比如我们想在一个弹出窗口中同时显示这些主视图和细节视图。我们应用程序中的所有其他导航栏都有可能获得详细信息视图的相同导航栏配置。
struct CustomizedNavigationController: UIViewControllerRepresentable {
let imageName: String
var backButtonTapped: (Bool) -> Void
class Coordinator: NSObject {
var parent: CustomizedNavigationController
var navigationViewController: UINavigationController
init(_ parent: CustomizedNavigationController) {
self.parent = parent
let navVC = UINavigationController(rootViewController: UIHostingController(rootView: Image(systemName: parent.imageName).resizable()
.aspectRatio(contentMode: .fit)
.foregroundColor(.blue)))
navVC.navigationBar.isTranslucent = true
navVC.navigationBar.tintColor = UIColor(red: 41/255, green: 159/244, blue: 253/255, alpha: 1)
navVC.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.red]
navVC.navigationBar.barTintColor = .yellow
navVC.navigationBar.topItem?.title = parent.imageName
self.navigationViewController = navVC
}
@objc func backButtonPressed(sender: UIBarButtonItem) {
self.parent.backButtonTapped(sender.isEnabled)
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UINavigationController {
// creates custom back button
let navController = context.coordinator.navigationViewController
let backImage = UIImage(systemName: "chevron.left")
let backButtonItem = UIBarButtonItem(image: backImage, style: .plain, target: context.coordinator, action: #selector(Coordinator.backButtonPressed))
navController.navigationBar.topItem?.leftBarButtonItem = backButtonItem
return navController
}
func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {
//Not required
}
}
这是查看完整代码的 link。