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
155 views
in Technique[技术] by (71.8m points)

How can I present successive views in a SwiftUI-based iOS app?

In attempting to learn SwiftUI, I am working on an iOS app that displays a list view of "observation sessions" and allows users to create new sessions from a "New" button. It requires an intermediate step of selecting a configuration that the new session will be based on.

I am able to show reasonable session list and configuration list screens, but my attempts to handle the selected configuration are failing.

The closure sent to the configurations list screen is called successfully as evidenced by a print statement that correctly displays the configuration name. But the remainder of the handler that is supposed to present a third view type fails to work (i.e. it doesn't present the view). In addition, I am getting a warning where I attempt to present the new view that "Result of call to 'sheet(isPresented:onDismiss:content:)' is unused". I'm hoping somebody can explain to me what I'm doing wrong. This is in Xcode 12.3, targeting iOS 14 in the simulator. Here is the SessionListView code where the problem is exhibited:

import SwiftUI

struct SessionsListView: View {
    @ObservedObject var dataManager: DataManager
    @State private var isPresented = false
    @State private var isObserving = false
    var body: some View {
        VStack {
            List {
                ForEach(dataManager.allSavedSessions) {session in
                    NavigationLink(
                        // Navigate to a detail view
                        destination: SessionDetailView(session: session),
                        label: {
                            Text("(session.name)")
                        })
                }
            }
            Spacer()
            Button("New Session") {
                isPresented = true
            }
            .padding()
            .font(.headline)
            .sheet(isPresented: $isPresented) {
                // Present a configuration list view where user must select configuration to use for new session
                // Requires a closure that's called upon selection in the configuration list view, to handle the selection
                NavigationView {
                    ConfigurationsListView(dataManager: dataManager, selectionHandler: { config in
                        isPresented = false
                        isObserving = true
                        handleConfigSelection(config)
                    })
                    .navigationTitle("Configurations")
                    .navigationBarItems(trailing: Button("Cancel") {
                        isPresented = false
                    })
                }
            }
        }
    }
        
    private func handleConfigSelection(_ config: SessionConfiguration) {
        // Use the selected configuration to start an observations session
        print("Selected (config.name). Will attempt to show sheet from (self)")
        isPresented = false
        isObserving = true
        self.sheet(isPresented: $isObserving) { // displaying warning: "Result of call to 'sheet(isPresented:onDismiss:content:)' is unused"
            NavigationView {
                ObservationsView(configuration: config)
                    .navigationBarItems(trailing: Button(action: {}) {
                        Text("Done")
                    })
            }
        }
    }
}

Here's the code I'm using in this simplified demo for the model types.

ObservationSession:


struct ObservationSession: Identifiable {
    let id: UUID = UUID()
    let name: String
}

SessionConfiguration:

import Foundation

struct ObservationSession: Identifiable {
    let id: UUID = UUID()
    let name: String
}

DataManager:

import Foundation

class DataManager: ObservableObject {
    var allSavedSessions: [ObservationSession] {
        return [ObservationSession(name: "Field mouse droppings"), ObservationSession(name: "Squirrels running up trees"), ObservationSession(name: "Squirrel behavior in urban landscapes")]
    }
    
    var allSavedConfigurations: [SessionConfiguration] {
        return [SessionConfiguration(name: "Squirrel Behavior"), SessionConfiguration(name: "Squirrel/Tree Interaction"), SessionConfiguration(name: "Mouse Behavior")]
    }
}
question from:https://stackoverflow.com/questions/65646415/how-can-i-present-successive-views-in-a-swiftui-based-ios-app

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

1 Reply

0 votes
by (71.8m points)

After a night's sleep I figured out an approach that seems to work.

I added a "currentConfiguration" property to my DataManager class of type SessionConfiguration, and set that property in the ConfigurationsListView when a user selects a configuration from the list. Then the SessionsListView can either present the ConfigurationsListView or an ObservationsView depending on a variable that tracks the flow:

import SwiftUI

enum SessionListPresentationFlow {
    case configuration
    case observation
}

struct SessionsListView: View {
    @ObservedObject var dataManager: DataManager
    @State private var isPresented = false
    @State var flow: SessionListPresentationFlow = .configuration
    var body: some View {
        VStack {
            List {
                ForEach(dataManager.allSavedSessions) {session in
                    NavigationLink(
                        // Navigate to a detail view
                        destination: SessionDetailView(session: session),
                        label: {
                            Text("(session.name)")
                        })
                }
            }
            Spacer()
            Button("New Session") {
                isPresented = true
            }
            .padding()
            .font(.headline)
            .sheet(isPresented: $isPresented, onDismiss: {
                if flow == .observation {
                    flow = .configuration
                } else {
                    flow = .configuration
                }
                dataManager.currentConfiguration = nil
                isPresented = false
            }) {
                // Present a view for the appropriate flow
                viewForCurrentFlow()
            }
        }
    }
    
    @ViewBuilder private func viewForCurrentFlow() -> some View {
        if flow == .configuration {
            NavigationView {
                ConfigurationsListView(dataManager: dataManager, selectionHandler: { config in
                    isPresented = false
                    handleConfigSelection(config)
                })
                .navigationTitle("Configurations")
                .navigationBarItems(trailing: Button("Cancel") {
                    isPresented = false
                    flow = .observation
                })
            }
        } else if flow == .observation, let config = dataManager.currentConfiguration {
            NavigationView {
                ObservationsView(configuration: config)
                    .navigationBarItems(leading: Button(action: {isPresented = false}) {
                        Text("Done")
                    })
            }
        } else {
            EmptyView()
        }
    }
    
    private func handleConfigSelection(_ config: SessionConfiguration) {
        flow = .observation
        isPresented = true
    }
}

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

...