Christophe Prat
Christophe Prat

Reputation: 196

SwiftUI: How to present view when clicking on a button?

I'm trying to make an app using Apple's SwiftUI and I need to have two buttons that present two different views in a single List row.

I use Xcode beta 7 and MacOS Catalina beta 7. I've tried to add a Button that present the view but, I couldn't click it and when I tried on a simple Button outside the List and clicked it, the AddList() view didn't appear. I've also tried adding a navigationButton inside navigationButton but it didn't work too. Adding a tapAction doesn't work too when you click on it, the view still does not appear

NavigationView {
            List(0..<5) { item in
                NavigationLink(destination: ContentOfList()) {
                    Text("hello") // dummy text
                    Spacer()
                    Text("edit")
                        .tapAction {
                            AddList() // This is the view I want to present
                    }
                }
                }.navigationBarItems(trailing: NavigationLink(destination: AddList(), label: { // doesn't work within navigationBarItems
                    Image(systemName: "plus.circle.fill")
                }))
        }

I expect the AddList() view to appear but in the two cases, it doesn't.

Upvotes: 6

Views: 20340

Answers (3)

Chuck H
Chuck H

Reputation: 8266

Much improved version (SwiftUI, iOS 13 beta 7)

The same solution works for dismissing Modals presented with the .sheet modifier.

import SwiftUI

struct DetailView: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var body: some View {
        Button(
            "Here is Detail View. Tap to go back.",
            action: { self.presentationMode.wrappedValue.dismiss() }
        )
    }
}

struct RootView: View {
    var body: some View {
        VStack {
            NavigationLink(destination: DetailView())
            { Text("I am Root. Tap for Detail View.") }
        }
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            RootView()
        }
    }
}

Upvotes: 7

kontiki
kontiki

Reputation: 40489

Update: The NavigationButton was very short lived. In beta3 it is already deprecated. I am updating the code to use its replacement: NavigationLink.

You can present a view from all three places. Here's how:

enter image description here

import SwiftUI

struct ContentView: View {

    var body: some View {
        NavigationView {
            TopView().navigationBarTitle(Text("Top View"))
        }
    }
}

struct TopView: View {
    @State private var viewTypeA = true

    let detailViewA = DynamicNavigationDestinationLink(id: \String.self) { data in
            ListA(passedData: data)
    }

    let detailViewB = DynamicNavigationDestinationLink(id: \String.self) { data in
            ListB(passedData: data)
    }

    var body: some View {
            List(0..<5) { item in
                NavigationLink(destination: ListC(passedData: "FROM ROW #\(item)")) {
                    HStack {
                        Text("Row #\(item)")
                        Spacer()
                        Text("edit")
                            .tapAction {
                                self.detailViewA.presentedData?.value = "FROM TAP ACTION Row #\(item)"
                        }
                    }
                }
            }.navigationBarItems(trailing: Button(action: {
                            self.detailViewB.presentedData?.value = "FROM PLUS CIRCLE"
            }, label: {
                    Image(systemName: "plus.circle.fill")
                }))
    }
}

struct ListA: View {
    let passedData: String

    var body: some View {
        VStack {
            Text("VIEW A")
            Text(passedData)
        }
    }
}

struct ListB: View {
    let passedData: String

    var body: some View {
        VStack {
            Text("VIEW B")
            Text(passedData)
        }
    }
}

struct ListC: View {
    let passedData: String

    var body: some View {
        VStack {
            Text("VIEW C")
            Text(passedData)
        }
    }
}

Upvotes: 5

frogcjn
frogcjn

Reputation: 838

improved version. (Swift, iOS 13 beta 4)

class NavigationModel : BindableObject {
    var willChange = PassthroughSubject<Void, Never>()
    var presentedData: String? {
        didSet {
            willChange.send()
        }
    }
    func dismiss() { if presentedData != nil {
        presentedData = nil
    } }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            MasterView()
        }.environmentObject(NavigationModel())
    }
}

struct MasterView: View {

    @EnvironmentObject
    var navigationModel: NavigationModel
    var destinationLink = DynamicNavigationDestinationLink<String, String, DetailView>(id: \.self) { data in DetailView(data: data) }

    var body: some View {
        List(0..<10) { index in
            Button("I am root. Tap for more details of #\(index).") {
                self.navigationModel.presentedData = "#\(index)"
            }
        }
        .navigationBarTitle("Master")
        .onReceive(navigationModel.willChange) {
            self.destinationLink.presentedData?.value = self.navigationModel.presentedData
        }
    }
}

struct DetailView: View {
    @EnvironmentObject
    var model: NavigationModel

    let data: String

    var body: some View {
        Button("Here are details of \(data). Tap to go back.") {
            self.model.dismiss()
        }
        .navigationBarTitle("Detail \(data)")
    }
}

struct Empty : Hashable {
}


#if DEBUG
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

Upvotes: 1

Related Questions