swiftui, как узнать, представлен ли лист в данный момент
У меня есть приложение со многими вложенными представлениями, некоторые из которых отображают лист на основе действий пользователя.
Но у меня также есть лист, который я хотел бы представить в главном представлении на основе таймера (т. е. не действия пользователя). Но вы не можете иметь 2 листа одновременно, поэтому я хотел бы проверить «кое-что», чтобы увидеть, не поднят ли уже лист, а не предъявлять тот, что по таймеру.
Я хотел бы сделать это в общем виде, а не проверять каждое место в коде, где может быть представлен лист.
Какие-либо предложения?
3 ответа
В идеале в базовой структуре должно быть что-то, что можно было бы запросить, чтобы ответить на вопрос «Показывается ли лист?», но, как отметил комментатор, это чревато опасностью.
Поэтому я просто решил оставить это в покое, что поведение «по умолчанию» в порядке (т. е. оно будет откладывать представление листа до тех пор, пока любой другой лист не будет отклонен). В моем случае это предпочтительнее любых других вращений.
РЕДАКТИРОВАТЬ:
Эй! Я только что узнал, что если лист таймера появляется во время отображения предупреждения ... это разрушает приложение. После того, как вы отклоните предупреждение, любая попытка открыть какой-либо лист в любом месте потерпит неудачу. Как будто что-то где-то рассинхронизировалось. Я считаю, что это похоже на:
Затянувшееся всплывающее окно вызывает проблему с оповещениями
Если в вашем приложении есть оповещения, вам не стоит этого делать.
В официальном документе есть среда isPresented, однако она не работает.
@Environment(\.isPresented) private var isPresented
Вот как вы можете обрабатывать листы — приведенный ниже пример полностью работает, просто передайте модель представления в среду перед вызовом
TabsView()
в
App
.
- Создать
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
}
}
- Создайте один необязательный
@Published
var и одна функция в модели представления; переменная сообщит программе, какой лист открыт:
// 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)
}
}
}
- Применение:
- открыть листы, вызвав функцию
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)
}
}
}