- макет TabView (для компактной ширины)
- макет боковой панели + содержимого с использованием HStack (для обычной ширины). Я использую пользовательскую боковую панель, поскольку здесь требуется больше макетов и настроек)
Важно: Различные макеты — это не протоколы SwiftUI Layout, а представления, которые не только управляют размещением различных представлений, но также управляют тем, какое подпредставление отображается и т. д. (например, TabView содержит подпредставления и показывает представление, когда выбрана соответствующая вкладка).
Проблема:
if useTabLayout {
TabLayoutContainer(...)
} else {
SidbarLayoutContainer(...)
}
Когда я переключаюсь между двумя макетами контейнеров, страницы создаются заново, что означает, что их модели представления @StateObject повторно инициализируются, а состояние представления теряется.
Чего я хочу:
- PageOne, PageTwo и PageThree должны сохранять свое состояние
- переключение между макетом вкладок и макетом боковой панели не должно воссоздать страницы
- должен измениться только внешний контейнер
- Использование .id(...) на страницах (например, PageOne().id("one")). Модель представления все еще создается заново.
- Создание модели представления страницы в Root-View и внедрение ее на страницы как @ObservedObject. Хотя это работает, для этого требуется, чтобы Root-View знал о просмотрах страниц и их содержании. Кажется, это не совсем чистое решение.
- Создание страниц только один раз: пусть pageOne = PageOne() и case .pageOne: возвращает pageOne. Кажется, это тоже работает. Но есть ли какие-либо недостатки в таком сохранении страницы?
- Учитывая AnyLayout (но он работает только для реальных макетов, таких как VStackLayout, а не для сложных контейнеров, таких как TabView)
Каков правильный подход SwiftUI для сохранения состояния дочернего представления (@StateObject) жив при переключении между двумя совершенно разными представлениями контейнера (TabView и Sidebar+Content)?
Демо-код
enum MyPage: Hashable {
case pageOne, pageTwo, pageThree
}
struct MyRootView: View {
@State var useTabLayout = true
@State var selectedPage: MyPage = .pageOne
var body: some View {
VStack {
Toggle("TabLayout", isOn: $useTabLayout)
if useTabLayout {
TabLayoutContainer(selectedPage: $selectedPage)
} else {
SidbarLayoutContainer(selectedPage: $selectedPage)
}
}
}
}
struct TabLayoutContainer: View {
@Binding var selectedPage: MyPage
var body: some View {
TabView(selection: $selectedPage) {
PageFactory.view(for: .pageOne)
.tag(MyPage.pageOne)
PageFactory.view(for: .pageTwo)
.tag(MyPage.pageTwo)
PageFactory.view(for: .pageThree)
.tag(MyPage.pageThree)
}
}
}
struct SidbarLayoutContainer: View {
@Binding var selectedPage: MyPage
var body: some View {
HStack {
// Sidebar
VStack {
Button("One") { selectedPage = .pageOne }
Button("Two") { selectedPage = .pageTwo }
Button("Three") { selectedPage = .pageThree }
}
// Content
VStack {
PageFactory.view(for: selectedPage)
}
}
}
}
@ViewBuilder
static func view(for page: MyPage) -> some View {
switch page {
case .pageOne: PageOne()
case .pageTwo: PageTwo()
case .pageThree: PageThree()
}
}
struct PageOne: View {
@StateObject var viewModel: ViewModelOne = .init()
var body: some View {
Text("Page One")
}
class ViewModelOne: ObservableObject {
init() {
print("Initialized ViewModelOne")
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/798 ... -between-d
Мобильная версия