Swiftui Mvvm Coordinator/Router/Navigationlink

SwiftUI MVVM Coordinator/Router/NavigationLink

It is better to use generics for your row, as below (tested with Xcode 11.4)

Usage example:

ProductFamilyRow(item: ProductFamilyItem(title: "Test"),
destinationView1: { Text("Details1") },
destinationView2: { Text("Details2") })

Interface:

Update - added block for row highlight. List has auto detection for button or link inside row and highlights if any standard (!key) present. So, to disable such behaviour it needs to hide everything under custom button style.

struct ProductFamilyRowStyle: ButtonStyle {

func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
.colorMultiply(configuration.isPressed ?
Color.white.opacity(0.5) : Color.white) // any effect you want
}
}

struct ProductFamilyRow: View {
let item: ProductFamilyItem
let destinationView1: () -> D1
let destinationView2: () -> D2

init(item: ProductFamilyItem, @ViewBuilder destinationView1: @escaping () -> D1,
@ViewBuilder destinationView2: @escaping () -> D2)
{
self.item = item
self.destinationView1 = destinationView1
self.destinationView2 = destinationView2
}

@State private var selection: Int? = 0

var body: some View {
VStack {
HStack {
Text(item.title)
Button(action: {
self.selection = 1
}) {
Text("Destination 1")
.background( // hide link inside button !!
NavigationLink(destination: destinationView1(),
tag: 1, selection: self.$selection) { EmptyView() }
)
}.foregroundColor(Color.blue)

Button(action: {
self.selection = 2
}) {
Text("Destination 2")
.background(
NavigationLink(destination: destinationView2(),
tag: 2, selection: self.$selection) { EmptyView() }
)
}.foregroundColor(Color.blue)
}

//Image(item.image)
}.frame(maxWidth: .infinity) // to have container centered
.buttonStyle(ProductFamilyRowStyle())
}
}

In MVVM + Coordinator, how to deal with sub view?

When the ground view receives the tap, it tells the ground view model.

  1. The ground view model tells the ground coordinator.
  2. The ground coordinator tells the current detail coordinator (if there is one) to dismiss.
  3. The current detail coordinator dismisses its view.
  4. The ground coordinator creates a new detail coordinator.
  5. The ground coordinator tells the new detail coordinator to present.
  6. The new detail coordinator creates a detail view model and detail
    view. It passes the detail view model to the detail view.
  7. The detail coordinator presents the detail view.

There are some variations in exactly when items are created and destroyed, but the above is the general idea.

Need help getting NavigationLink to work in SwiftUI

Your List is iterating over results which is [User], so item is a User and that should be passed to UserView() which is expecting a single User, not the array of Users.

var body: some View {
NavigationView {
List(results, id: \.id) { item in
NavigationLink(destination: UserView(results: item)) { // here
VStack(alignment: .leading) {
Text(item.name)
.font(.headline)
Text("Age: \(item.age)")
.foregroundColor(Color.red)
}
}
}
.onAppear(perform: loadData)
}
}


I think it has something to do with the UserView_Previews, but I am not sure.

The User_Previews is only unit test data that allows you to test your User View in the Preview Pane in Xcode. It isn't used when you build.

SwiftUI ViewModifier not working as a NavigationLink

The modifier doesn't work because the content argument is not the actual view being modified, but instead is a proxy:

content is a proxy for the view that will have the modifier represented by Self applied to it.

Reference.

This is what a quick debugging over the modifier shows:

(lldb) po content
SwiftUI._ViewModifier_Content()

As the proxy is an internal type of SwiftUI, we can't know for sure why NavigationLink doesn't work with it.

A workaround would be to skip the modifier, and only add the extension over View:

extension View {
func navLink(title: String) -> some View {
NavigationLink(destination: content) {
Text(title)
}
}
}


Related Topics



Leave a reply



Submit