0

简单用例:带有“最近”部分的状态列表,显示您最近导航到的那些状态。当点击链接时,动画开始但随后中止,可能是由于详细视图中的 onAppear 处理程序更改了模型的 recents 属性 - 这是由框架观察到的。日志消息表明框架不满意,但不清楚如何让它在添加到最近的延迟前约 2 秒的延迟......


import SwiftUI


class USState: Identifiable{
    typealias ID = String
    var id: ID
    var name: String
    init(_ name: String, id: String){
        self.id = id
        self.name = name
    }
}

/** The model is a small set of us states for example purposes.
    It also publishes a recents property which has a lifo stack of states that have been viewed.
 */
class StateModel: ObservableObject{
    var states: [USState]
    var stateMap: [USState.ID: USState]
    
    @Published var recents = [USState]()
    
    init(){
        states = [
            USState("California", id: "CA"),
            USState("Georgia", id: "GA"),
            USState("New York", id: "NY"),
            USState("New Jersey", id: "NJ"),
            USState("Montana", id: "MT")
        ]
        stateMap = [USState.ID: USState]()
        for state in states{
            stateMap[state.id] = state
        }
    }

    func addRecent(_ state: USState){
        recents.removeAll(where: {$0.id == state.id})
        recents.insert(state, at: 0)
    }
    
    func allExceptRecent() -> [USState]{
        states.filter{ state in
            recents.contains{
                state.id == $0.id
            } == false
        }
    }
}

/** A simple view to serve as the destination of a state link
 */
struct StateView: View{
    @EnvironmentObject var stateModel: StateModel
    var usState: USState
    var body: some View{
        Text(usState.name)
            .onAppear{
                DispatchQueue.main.async {
                    withAnimation {
                        stateModel.addRecent(usState)
                    }
                }
            }
    }
}

/** A list of states broken into two sections, those that have been recently viewed, and the remainder.
    Desired behavior is that when a state is tapped, it should navigate to its respective detail view and update the list of recents.
    The issue is that the recents updating appears to confuse SwiftUI and the navigation is aborted.
 */
struct SidebarBounce: View {

    @EnvironmentObject var model: StateModel

    @SceneStorage("selectionStore") private var selectionStore: USState.ID?
    
    struct Header: View{
        var text: String
        var body: some View{
            Text(text)
                .font(.headline)
                .padding()
        }
    }

    struct Row: View{
        var text: String
        var body: some View{
            VStack{
                HStack{
                    Text(text)
                        .padding([.leading, .trailing])
                        .padding([.top, .bottom], 8)
                    Spacer()
                }
                Divider()
            }
        }
    }
    
    var body: some View{
        ScrollView{
            LazyVStack(alignment: .leading, spacing: 0){

                Section(
                    header: Header(text: "Recent")
                ){
                    ForEach(model.recents){place in
                        NavigationLink(
                            destination: StateView(usState: place),
                            tag: place.id,
                            selection: $selectionStore
                        ){
                            Row(text: place.name)
                        }
                            .id("Recent \(place.id)")
                    }
                }
                Section(
                    header: Header(text: "All")
                ){
                    ForEach(model.allExceptRecent()){place in
                        NavigationLink(
                            destination: StateView(usState: place),
                            tag: place.id,
                            selection: $selectionStore
                        ){
                            Row(text: place.name)
                        }
                            .id("All \(place.id)")
                    }
                }
            }
        }
    }
}

struct BounceWrap: View{
    let model = StateModel()
    var body: some View{
        NavigationView{
            SidebarBounce()
                .navigationTitle("Aborted Navigation")
            Text("Nothing Selected")
        }
            .environmentObject(model)
    }
}

@main
struct DemoApp: App {
    var body: some Scene {
        WindowGroup {
            BounceWrap()
        }
    }
}

注意:这必须作为应用程序(iPhone 或模拟器)而不是在预览中运行。

4

1 回答 1

0

在执行动画之前,我尝试将点击状态设置为最近的状态,从下面的 GIF 中可以看出,问题似乎已经解决:

动画修复

要在执行 NavigationView 之前将状态设置为最近,我们需要实现一个编程 NavigationView,如下所示:

struct SidebarBounce: View {
    @EnvironmentObject var model: StateModel
    @SceneStorage("selectionStore") private var selectionStore: USState.ID?
    
    @State private var clickedState: USState? // <-- see here
    @State private var expandState = false // <-- and here
    .
    .
    .
                Section(
                    header: Header(text: "All")
                ){
                    ForEach(model.allExceptRecent()){place in
                        Button(action: {
                            withAnimation {
                                model.addRecent(place) // <-- Making state recent
                            }
                            clickedState = place // <-- programmatically executing NavigationLink below
                            expandState = true // <-- programmatically executing NavigationLink below
                        }, label: {
                            Row(text: place.name)
                        })
                    }
                }
            }

            // Programmatic NavigationLink that is triggered by a Bool value vvv
            NavigationLink(
                destination: clickedState == nil ? nil : StateView(usState: clickedState!),
                isActive: $expandState,
                label: EmptyView.init)
        }
    }
}

我认为发生这种情况的原因是因为StateModel对象在两个视图中是相同的,并且从视图更新它会导致所有视图更新,即使它不是活动视图。

如果出现任何其他问题,请尝试为每个视图设置一个唯一的ViewModel,每个ViewModel都侦听StateModel (Singleton)中发生的更改,并且视图侦听来自ViewModel的更改并将其反映到 UI 中。

于 2021-03-17T18:58:32.670 回答