In a NavigationSplitView, the Navigation split is just an array of NavigationLinks via a for each loop. This works as expected.
The Detail split is intended to be a View that changes based upon the selected content via the link. Oddly, it works with a SwiftUI native view like, 'Text', but fails with a custom View.
For clarity, I have reduced the code to it's bare minimums to demonstrate the issue.
ContentView.swift
struct ContentView: View {
@StateObject var controller = DetailViewTestingController()
var body: some View {
NavigationSplitView {
VStack {
List(selection: $controller.selection) {
ForEach(controller.listOfObjects, id: \.self.id) { _object in
NavigationLink(value: _object.id) {
Text(_object.title ?? "no title")
}
.buttonStyle(PlainButtonStyle())
.padding(.vertical)
}
}
}
.navigationSplitViewColumnWidth(240)
} detail: {
ZStack {
VStack{
if (controller.selection ?? -1 >= 0) {
Text("Direct: \(controller.listOfObjects[controller.selection!].title ?? "none")")
Text("Found: \(controller.detailObject.title ?? "none")")
DetailView(detailObject: $controller.detailObject)
}
}
}
}
}
}
DetailView.swift
struct DetailView: View {
@Binding var detailObject : DetailObject
var body: some View {
Text("Object Title: \(detailObject.title ?? "none")")
}
}
DetailObject.swift
public class DetailObject : Identifiable, Hashable, ObservableObject {
public var id: Int
public var title : String?
public init(id: Int, title: String? = nil) {
self.id = id
self.title = title
}
// MARK: Hashable Protocol
open func hash(into hasher: inout Hasher) {
hasher.combine(id)
hasher.combine(title)
}
public static func == (lhs: DetailObject, rhs: DetailObject) -> Bool {
if (lhs.id == rhs.id) { return false }
if (lhs.title == rhs.title) { return false }
return true
}
}
DetailViewTestingController.swift
open class DetailViewTestingController : ObservableObject {
@Published public var listOfObjects : [DetailObject] = []
@Published public var detailObject : DetailObject = DetailObject(id: 0, title: "")
@Published public var selection : Int? {
didSet {
detailObject = DetailObject(id: 0, title: "")
if (selection == nil) { return }
for d in listOfObjects {
if (d.id == selection) {
detailObject = d
return
}
}
}
}
public init() {
listOfObjects.append(DetailObject(id: 0, title: "one"))
listOfObjects.append(DetailObject(id: 1, title: "two"))
listOfObjects.append(DetailObject(id: 2, title: "three"))
listOfObjects.append(DetailObject(id: 3, title: "four"))
}
}
The expectation is that the detailObject in passed into the DetailView would update the contents of the DetailView as it does in the TextView ( and moving the entire contents into the main code, it works, but fails when in the subview.
It is my understanding that the @Binding should accomplish this. I have also tried using @ObservedObject, with no change in behavior. I am 100% certain this is simply something stupid that I am not seeing.
Passing it as
@EnvironmentObjectinstead of@Bindingwill fix the problem.DetailView.swift
ContentView.swift