Adding an Icon to the Navigation Bar, moving from Introspect to SwiftUIIntrospect #325
-
Hi, I'm trying to implement this example from Stack Overflow. Here's the code I have working with Introspect. import Foundation
import SwiftUI
import Introspect
extension View {
@ViewBuilder func navigationTitleWithTrailingIcon(title: String, view: some View) -> some View {
self
.navigationTitle(title)
.introspectNavigationController { navController in
let bar = navController.navigationBar
let hosting = UIHostingController(rootView: view)
guard let hostingView = hosting.view else { return }
bar.subviews.first(where: \.clipsToBounds)?.addSubview(hostingView)
hostingView.backgroundColor = .clear
hostingView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hostingView.trailingAnchor.constraint(equalTo: bar.trailingAnchor),
hostingView.bottomAnchor.constraint(equalTo: bar.bottomAnchor, constant: -8)
])
}
}
} This builds but crashes: import Foundation
import SwiftUI
import SwiftUIIntrospect
extension View {
@ViewBuilder func navigationTitleWithTrailingIcon(title: String, view: some View) -> some View {
self
.introspect(.navigationView(style: .stack), on: .iOS(.v16, .v17)) { navController in
let bar = navController.navigationBar
let hosting = UIHostingController(rootView: view)
guard let hostingView = hosting.view else { return }
bar.subviews.first(where: \.clipsToBounds)?.addSubview(hostingView)
hostingView.backgroundColor = .clear
hostingView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hostingView.trailingAnchor.constraint(equalTo: bar.trailingAnchor),
hostingView.bottomAnchor.constraint(equalTo: bar.bottomAnchor, constant: -8)
])
}
}
} Could someone point me in the right direction? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
The crash is:
This happens when the view you're trying to constrain is nowhere to be found in the hierarchy. The problem is in this line: bar.subviews.first(where: \.clipsToBounds)?.addSubview(hostingView) Notice the So the solution is: import SwiftUI
import SwiftUIIntrospect
@main
struct App: SwiftUI.App {
var body: some Scene {
WindowGroup {
NavigationView {
ScrollView {
ForEach(1 ... 50, id: \.self) { index in
Text("Index: \(index)")
}
.frame(maxWidth: .infinity)
}
.navigationTitle("Large title")
.navigationBarTrailingIcon(ProfilePicture())
}
}
}
}
struct ProfilePicture: View {
var body: some View {
Circle()
.fill(
LinearGradient(
gradient: Gradient(colors: [.red, .blue]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.frame(width: 40, height: 40)
.padding(.horizontal)
}
}
@propertyWrapper
final class Weak<T: AnyObject> {
private weak var _wrappedValue: T?
var wrappedValue: T? {
get { _wrappedValue }
set { _wrappedValue = newValue }
}
init(wrappedValue: T? = nil) {
self._wrappedValue = wrappedValue
}
}
struct NavigationBarTrailingIconModifier<Icon: View>: ViewModifier {
let icon: Icon
@Weak
var latestHostingView: UIView?
init(_ icon: Icon) {
self.icon = icon
}
func body(content: Content) -> some View {
content.introspect(.navigationView(style: .stack), on: .iOS(.v16, .v17), scope: .ancestor) { navController in
let bar = navController.navigationBar
guard
let barContainer = bar.subviews.first(where: \.clipsToBounds)
else {
return
}
let hosting = UIHostingController(rootView: icon)
let hostingView = hosting.view! // can be force unwrapped to force VC to load view on the spot
hostingView.backgroundColor = .clear
latestHostingView?.removeFromSuperview()
latestHostingView = hostingView
barContainer.addSubview(hostingView)
hostingView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hostingView.trailingAnchor.constraint(equalTo: bar.trailingAnchor),
hostingView.bottomAnchor.constraint(equalTo: bar.bottomAnchor, constant: -8)
])
}
}
}
extension View {
func navigationBarTrailingIcon(_ view: some View) -> some View {
self.modifier(NavigationBarTrailingIconModifier(view))
}
} |
Beta Was this translation helpful? Give feedback.
-
@davdroman Thank you so much for taking the time for explaining this so thoroughly. I appreciate it a lot! |
Beta Was this translation helpful? Give feedback.
The crash is:
This happens when the view you're trying to constrain is nowhere to be found in the hierarchy.
The problem is in this line:
Notice the
?
. That line can be a no-op, and that's ho…