Search code examples
iosswiftswiftuimobile-developmentswiftui-navigationlink

How to jump from one detail page to another and return to the list page in SwiftUI?


When I use the "next article" button to jump to the article details page with index 3, I want to go directly back to the article list page instead of the article details page with index 2.I tried to search for methods to return to the specified page and destroy the page, but I didn't find them.How to achieve this effect in swiftui?Thanks.I guess the same scenario will happen in other mobile development, right? The ArticleListView is :

struct ArticleListView: View {
    
    @EnvironmentObject var modelData:ModelData
    
    var body: some View {
        NavigationView{
            List{
                ForEach(modelData.articleList){ article in
                    NavigationLink(destination:ArticleDetail(index:article.index)){
                        ArticleItem(index:article.index);
                    }
                }
            }
            .listStyle(PlainListStyle())
        }
    }
}

The ArticleDetail is like this:

struct ArticleDetail: View {
    
    @EnvironmentObject var modelData:ModelData
    var index:Int
    
    var body: some View {
        
            VStack{
                Text(modelData.articleList[index].htmlText)
                
                NavigationLink(destination:ArticleDetail(index:self.index+1)){
                    Text("next article")
                }
            }
        
    }
}

The Article/ArticleItemView/ModelData is like this:

struct Article:Identifiable{
    var id = UUID()
    var index:Int
    var htmlText:String
}

struct ArticleItem: View {
    
    @EnvironmentObject var modelData:ModelData
    var index:Int
    var body: some View {
        Text(modelData.articleList[index].htmlText)
    }
}

final class ModelData:ObservableObject {
    @Published var articleList = [Article(index:0,htmlText: "first test text "),Article(index:1,htmlText: "second test text"),Article(index:2,htmlText: "third test text")]
}


Solution

  • This solution has some potential scalability issues, but it gets the basic job done:

    
    struct Article {
        var id = UUID()
    }
    
    struct ContentView: View {
        
        var articles = [Article(), Article(), Article(), Article()]
        @State private var activeId : UUID?
        
        func activeBinding(id: UUID) -> Binding<Bool> {
            .init { () -> Bool in
                activeId == id
            } set: { (newValue) in
                activeId = newValue ? id : nil
            }
        }
        
        var body: some View {
            
            NavigationView {
                VStack(alignment: .leading, spacing: 20) {
                    ForEach(articles, id: \.id) { article in
                        NavigationLink(destination: ArticleView(article: article,
                                                                articles: articles,
                                                                popToTop: { activeId = nil }),
                                       isActive: activeBinding(id: article.id)) {
                            Text("Link to article: \(article.id)")
                        }
                    }
                }
            }
        }
    }
    
    struct ArticleView : View {
        var article : Article
        var articles : [Article]
        var popToTop: () -> Void
        
        var body : some View {
            VStack(alignment: .leading, spacing: 20) {
                Text("Current: \(article.id)")
                
                Button("Pop") {
                    popToTop()
                }
                
                ForEach(articles, id: \.id) { listArticle in
                    NavigationLink(destination: ArticleView(article: article, articles: articles, popToTop: popToTop)) {
                        Text("Link to article: \(listArticle.id)")
                    }
                }
            }
        }
    }
    
    

    On the main page, the top-level article ID is stored in a @State variable. That is tied with a custom binding to an isActive property on the top-level link. Basically, when the article is active, the link is presented and when activeId is nil, the link becomes inactive, and pops to the top.

    Because that's the top level view, any views lower in the stack will get popped off if that top-level NavigationLink is inactive.

    popToTop is a function that gets passed down to the subsequent article views and gets called if the "Pop" button is pressed.