SwiftUI:当前一个视图列表更改时,NavigationView 详细信息视图会弹出后退堆栈

new*_*our 3 swift swiftui swiftui-navigationview

我的第一个屏幕上有一个ListID 和分数。

在详细信息屏幕中,我单击并调用一个回调,该回调会添加到分数并按List分数进行排序。

当我对列表顶部的项目执行此操作时,没有任何反应。(好的)

当我对列表底部的项目执行此操作时,导航视图会弹出后退堆栈并将我带回到第一页。(坏的)

import SwiftUI

class IdAndScoreItem {
    var id: Int
    var score: Int
    init(id: Int, score: Int) {
        self.id = id
        self.score = score
    }
}

@main
struct CrazyBackStackProblemApp: App {
    var body: some Scene {
        WindowGroup {
            NavigationView {
                ListView()

            }
            .navigationViewStyle(.stack)
        }
    }
}

struct ListView: View {
    @State var items = (1...50).map { IdAndScoreItem(id: $0, score: 0) }
    func addScoreAndSort(item: IdAndScoreItem) {
        items = items
            .map {
                if($0.id == item.id) { $0.score += 1 }
                return $0
            }
            .sorted {
                $0.score > $1.score
            }
    }
    var body: some View {
        List(items, id: \.id) { item in
            NavigationLink {
                ScoreClickerView(
                    onClick: { addScoreAndSort(item: item) }
                )
            } label: {
                Text("id: \(item.id) score:\(item.score)")
            }
        }
    }
}

struct ScoreClickerView: View {
    var onClick: () -> Void
    var body: some View {
        Text("tap me to increase the score")
            .onTapGesture {
                onClick()
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点,以便我在详细信息页面上对列表进行重新排序,这会反映在列表页面上,但导航堆栈不会弹出(当我在列表底部的列表项上执行此操作时)。我尝试添加navigationStyle(.stack)了没有效果。

感谢您的任何帮助!

Asp*_*eri 5

Resort 更改了 ID 的顺序,使列表重新创建内容,导致当前的 NavigationLink 被破坏,因此导航回来。

一种可能的解决方案是将链接与内容分开 - 可以通过引入诸如选择(点击的行)之类的内容以及通过该选择激活的导航链接来完成。

使用 Xcode 14 / iOS 16 进行测试

演示

@State private var selectedItem: IdAndScoreItem?  // selection !!

var isNavigate: Binding<Bool> {   // link activator !!
    Binding(get: { selectedItem != nil}, set: { _ in selectedItem = nil })
}

var body: some View {
    List(items, id: \.id) { item in
        Text("id: \(item.id) score:\(item.score)")   // tappable row
            .frame(maxWidth: .infinity, alignment: .leading)
            .contentShape(Rectangle())
            .onTapGesture {
                selectedItem = item
            }
    }
    .background(
        NavigationLink(isActive: isNavigate) {  // one link !!
            ScoreClickerView {
                if let item = selectedItem {
                    addScoreAndSort(item: item)
                }
            }
        } label: {
            EmptyView()
        }
    )
}
Run Code Online (Sandbox Code Playgroud)