swiftui, как узнать, представлен ли лист в данный момент

У меня есть приложение со многими вложенными представлениями, некоторые из которых отображают лист на основе действий пользователя.

Но у меня также есть лист, который я хотел бы представить в главном представлении на основе таймера (т. е. не действия пользователя). Но вы не можете иметь 2 листа одновременно, поэтому я хотел бы проверить «кое-что», чтобы увидеть, не поднят ли уже лист, а не предъявлять тот, что по таймеру.

Я хотел бы сделать это в общем виде, а не проверять каждое место в коде, где может быть представлен лист.

Какие-либо предложения?

3 ответа

В идеале в базовой структуре должно быть что-то, что можно было бы запросить, чтобы ответить на вопрос «Показывается ли лист?», но, как отметил комментатор, это чревато опасностью.

Поэтому я просто решил оставить это в покое, что поведение «по умолчанию» в порядке (т. е. оно будет откладывать представление листа до тех пор, пока любой другой лист не будет отклонен). В моем случае это предпочтительнее любых других вращений.

РЕДАКТИРОВАТЬ:

Эй! Я только что узнал, что если лист таймера появляется во время отображения предупреждения ... это разрушает приложение. После того, как вы отклоните предупреждение, любая попытка открыть какой-либо лист в любом месте потерпит неудачу. Как будто что-то где-то рассинхронизировалось. Я считаю, что это похоже на:

Затянувшееся всплывающее окно вызывает проблему с оповещениями

Если в вашем приложении есть оповещения, вам не стоит этого делать.

В официальном документе есть среда isPresented, однако она не работает.

      @Environment(\.isPresented) private var isPresented

Вот как вы можете обрабатывать листы — приведенный ниже пример полностью работает, просто передайте модель представления в среду перед вызовом TabsView()в App.

  1. Создать Identifiableобъект, который будет обрабатывать все листы в программе:
      // This struct can manage all sheets
struct CustomSheet: Identifiable {

    let id = UUID()
    let screen: TypeOfSheet
    
    // All sheets should fit here
    @ViewBuilder
    var content: some View {
        switch screen {
        case .type1:
            SheetType1()
        case .type2(let text):
            SheetType2(text: text)
        default:
            EmptyView()
        }
    }

    // All types of sheets should fit here
    enum TypeOfSheet {
        case type1
        case type2(text: String)
        case none
    }
}
  1. Создайте один необязательный @Publishedvar и одна функция в модели представления; переменная сообщит программе, какой лист открыт:
      // Code to be included in the view model, so it can
// handle AND track all the sheets
class MyViewModel: ObservableObject {
    
    // This is THE variable that will tell the code whether a sheet is open
    // (and also which one, if necessary)
    @Published var sheetView: CustomSheet?

    func showSheet(_ sheet: CustomSheet.TypeOfSheet) {

        // Dismiss any sheet that is already open
        sheetView = nil

        switch sheet {
        case .none:
            break
        default:
            sheetView = CustomSheet(screen: sheet)
        }
    }
}
  1. Применение:
  • открыть листы, вызвав функцию viewModel.showSheet(...)
  • использовать .sheet(item:)следить за типом открываемого листа
  • использовать viewModel.sheet.screenузнать какой лист открыт
  • листы также могут быть отклонены с помощью viewModel.showSheet(.none)
      // Example: how to use the view model to present and track sheets
struct TabsView: View {
    @EnvironmentObject var viewModel: MyViewModel
    var body: some View {
        TabView {
            VStack {
                Text("First tab. Sheet is \(String(describing: viewModel.sheetView?.screen ?? .none))")
                    .padding()
                Button("Open sheet type 1") {
                    
                    // Show a sheet of the first type
                    viewModel.showSheet(.type1)
                }
            }
            .tabItem {Label("Tab 1", systemImage: "house")}
            
            VStack {
                Text("Second tab. Sheet is \(viewModel.sheetView == nil ? "Hidden" : "Shown")")
                    .padding()
                Button("Open sheet type 2") {
                    
                    // Show a sheet of the second type
                    viewModel.showSheet(.type2(text: "parameter"))
                }
            }
            .tabItem {Label("Tab 2", systemImage: "plus")}

        }
        
        // Open a sheet - the one selected in the view model
        .sheet(item: $viewModel.sheetView) { sheet in
            sheet.content
                .environmentObject(viewModel)
        }
    }
}

Следующий код завершает минимальный воспроизводимый пример:

      // Just some sample views for the sheets
struct SheetType1: View {
    @EnvironmentObject var viewModel: MyViewModel
    var body: some View {
        Text("Takes no parameters. Sheet is \(viewModel.sheetView == nil ? "Hidden" : "Shown")")
    }
}
struct SheetType2: View {
    @EnvironmentObject var viewModel: MyViewModel
    let text: String
    var body: some View {
        Text("Takes a string: \(text). Sheet is \(String(describing: viewModel.sheetView?.screen ?? .none))")
    }
}

@main
struct MyApp: App {

    let viewModel = MyViewModel()

    var body: some Scene {
        WindowGroup {
            TabsView()
                .environmentObject(viewModel)
        }
    }
}
Другие вопросы по тегам