Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
284 views
in Technique[技术] by (71.8m points)

swift - What's best practice for programmatic movement a NavigationView in SwiftUI

I'm working on an app that needs to open on the users last used view even if the app is completly killed by the user or ios.

As a result I'm holding last view used in UserDefaults and automatically moving the user through each view in the stack until they reach their destination.

The code on each view is as follows:

@Binding var redirectionID: Int

VStack() {
  List {
    NavigationLink(destination: testView(data: data, moc: moc), tag: data.id, selection: 
         $redirectionId) {
                        
                           DataRow(data: data)
                        
                         }
        }
}.onAppear() {
  redirectionID = userData.lastActiveView
}

Is there a better / standard way to achieve this? This works reasonably on iOS 14.* but doesn't work very well on iOS 13.* On iOS 13.* The redirection regularly doesnt reach its destination page and non of the preceeding views in the stack seem to be created. Pressing back etc results in a crash.

Any help / advice would be greatly appreciated.

question from:https://stackoverflow.com/questions/65947186/whats-best-practice-for-programmatic-movement-a-navigationview-in-swiftui

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This sounds like the perfect use of if SceneStorage

"You use SceneStorage when you need automatic state restoration of the value. SceneStorage works very similar to State, except its initial value is restored by the system if it was previously saved, and the value is· shared with other SceneStorage variables in the same scene."

@SceneStorage("ContentView.selectedProduct") private var selectedProduct: String?

@SceneStorage("DetailView.selectedTab") private var selectedTab = Tabs.detail

It is only available in iOS 14+ though so something manual would have to be implemented. Maybe something in CoreData. An object that would have variables for each important state variable. It would work like an ObservedObject ViewModel with persistence.

Also. you can try...

"An NSUserActivity object captures the app’s state at the current moment in time. For example, include information about the data the app is currently displaying. The system saves the provided object and returns it to the app the next time it launches. The sample creates a new NSUserActivity object when the user closes the app or the app enters the background."

Here is some sample code that summarizes how to bring it all together. It isn't a minimum reproducible example because it is a part of the larger project called "Restoring Your App's State with SwiftUI" from Apple. But it gives a pretty good picture on how to implement it.

struct ContentView: View {
    // The data model for storing all the products.
    @EnvironmentObject var productsModel: ProductsModel
    
    // Used for detecting when this scene is backgrounded and isn't currently visible.
    @Environment(.scenePhase) private var scenePhase

    // The currently selected product, if any.
    @SceneStorage("ContentView.selectedProduct") private var selectedProduct: String?
    
    let columns = Array(repeating: GridItem(.adaptive(minimum: 94, maximum: 120)), count: 3)
    
    var body: some View {
        NavigationView {
            ScrollView {
                LazyVGrid(columns: columns) {
                    ForEach(productsModel.products) { product in
                        NavigationLink(destination: DetailView(product: product, selectedProductID: $selectedProduct),
                                       tag: product.id.uuidString,
                                       selection: $selectedProduct) {
                            StackItemView(itemName: product.name, imageName: product.imageName)
                        }
                        .padding(8)
                        .buttonStyle(PlainButtonStyle())
                        
                        .onDrag {
                            /** Register the product user activity as part of the drag provider which
                                will  create a new scene when dropped to the left or right of the iPad screen.
                            */
                            let userActivity = NSUserActivity(activityType: DetailView.productUserActivityType)
                            
                            let localizedString = NSLocalizedString("DroppedProductTitle", comment: "Activity title with product name")
                            userActivity.title = String(format: localizedString, product.name)
                            
                            userActivity.targetContentIdentifier = product.id.uuidString
                            try? userActivity.setTypedPayload(product)
                            
                            return NSItemProvider(object: userActivity)
                        }
                    }
                }
                .padding()
            }
            .navigationTitle("ProductsTitle")
        }
        .navigationViewStyle(StackNavigationViewStyle())
        
        .onContinueUserActivity(DetailView.productUserActivityType) { userActivity in
            if let product = try? userActivity.typedPayload(Product.self) {
                selectedProduct = product.id.uuidString
            }
        }
        .onChange(of: scenePhase) { newScenePhase in
            if newScenePhase == .background {
                // Make sure to save any unsaved changes to the products model.
                productsModel.save()
            }
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...