I try to recreate the .toolbar
modifier Apple uses for their NavigationView
. I created an own implementation of a NavigationStackView
but also want to use a .toolbar
modifier.
I got something to work using environment objects and custom view modifiers, but when I don't apply the .toolbar
modifier this won't work because no environment object is set.
Is there a better way to do this? How does Apple do this?
Example:
import Foundation
import SwiftUI
class ToolbarData: ObservableObject {
@Published var view: (() -> AnyView)? = nil
init(_ view: @escaping () -> AnyView) {
self.view = view
}
}
struct NavigationStackView<Content: View>: View {
@ViewBuilder var content: () -> Content
@EnvironmentObject var toolbar: ToolbarData
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 0) {
if (toolbar.view != nil) {
toolbar.view!()
}
}
Spacer()
content()
Spacer()
}
}
}
struct NavigationStackToolbar<ToolbarContent: View>: ViewModifier {
var toolbar: ToolbarContent
func body(content: Content) -> some View {
content
.environmentObject(ToolbarData({
AnyView(toolbar)
}))
}
}
extension NavigationStackView {
func toolbar<Content: View>(_ content: () -> Content) -> some View {
modifier(NavigationStackToolbar(toolbar: content()))
}
}
struct NavigationStackView_Previews: PreviewProvider {
static var previews: some View {
NavigationStackView {
Text("Test")
}
.toolbar {
Text("Toolbar")
}
}
}
Current solution:
import Foundation
import SwiftUI
private struct ToolbarEnvironmentKey: EnvironmentKey {
static let defaultValue: AnyView = AnyView(EmptyView())
}
extension EnvironmentValues {
var toolbar: AnyView {
get { self[ToolbarEnvironmentKey.self] }
set { self[ToolbarEnvironmentKey.self] = newValue }
}
}
struct NavigationStackView<Content: View>: View {
@ViewBuilder var content: () -> Content
@Environment(\.toolbar) var toolbar: AnyView
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 0) {
toolbar
}
Spacer()
content()
Spacer()
}
}
}
extension NavigationStackView {
func toolbar<Content: View>(_ content: () -> Content) -> some View {
self
.environment(\.toolbar, AnyView(content()))
}
}
struct NavigationStackView_Previews: PreviewProvider {
static var previews: some View {
NavigationStackView {
Text("Test")
}
.toolbar {
Text("Toolbar")
}
}
}