Нужна помощь в реализации представления NavigationView в SwiftUI с использованием контейнера List

Я пытаюсь реализовать список ссылок навигации, который позволит мне перемещаться между представлениями до 3 уровней ссылок навигации.

Основной вид -> Просмотр2 -> Просмотр3 -> Просмотр4

Когда я дойду до View4, в представлении не будет кнопки «Назад» по умолчанию на панели навигации, но вместо этого будет кнопка, которая позволит мне вернуться к «Основному представлению».

Используя решение, упомянутое здесь, я смог (вроде) достичь того, что хотел, внеся небольшие изменения в решение, так что бит isActive реализован как массив бит isActive, каждый из которых соответствует количеству навигационных ссылок, которые повторяются в контейнер списка.

Единственная проблема, которую я заметил, заключается в том, что операция просмотра pop-to-root с использованием битов isActive будет работать только в том случае, если я использую ее с оболочкой свойств @State, но ничего не будет делать, если мои биты isActive содержатся в экземпляре класса ObservableObject. Моя проблема с использованием оболочки @State заключается в том, что мне придется предварительно определить фиксированный размер массива и заполнить его «ложными» битами, которые мне не нужны, поскольку количество элементов, которые я могу перебирать в контейнере List, может варьироваться. поэтому я хочу, чтобы размер массива isActive был таким же, как количество элементов, которые я повторяю в контейнере List.

Вот упрощенная реализация кода NavigationView для всплывающего сообщения в корневой каталог для справки. Верхняя половина NavigationLinks - это те, которые используют битовый массив isActive фиксированного размера с оболочкой @State, которая работает, в то время как нижняя половина NavigationLinks - это те, которые используют наблюдаемый объект, который может иметь гибкий размер битового массива isActive, но Кажется, я не могу понять, почему этот подход не работает.

Главный вид:

struct ContentView: View {
    @ObservedObject var itemGroup: ItemGroup
    // If I use @State wrapper, I need to pre allocate a fixed array size and populate
    // it with 'false' values. 
    @State var isActive: [Bool] = [false, false, false, false]
    
    var body: some View {
        NavigationView {
            VStack {
                
            // MARK: Navigation links using isActive property with @State wrapper 
            // (this implementation works but the isActive array size needs to be defined
            // with a fixed size, I would prefer the size to be variable)
            List(1..<5) { item in
                NavigationLink(destination: View2(isActive: $isActive[item-1], sub: "sub\(item)"), isActive: $isActive[item-1]) {
                    Text("Go to view 2 using @State, sub\(item)")
                        .padding()
                        .background(Color.blue)
                        .foregroundColor(.white)
                }
            }
            
            // MARK: Navigation links using isActive property defined within an observable 
            // class object 
            // (I would have preferred this method since I can have the
            // flexibility of allocating an isActive array based on the number of
            // NavigationLink views generated by the list container but the pop-to-root
            // operation won't work with this approach)
            //  Note: The range 1..<5 is used here for illustration purposes only but is actually an array of objects whose array size may vary
            List(1..<5) { item in
                NavigationLink(destination: View2(isActive: $itemGroup.isActive[item-1], sub: "sub\(item)"), isActive: $itemGroup.isActive[item-1]) {
                    Text("Go to view 2, sub\(item)")
                        .padding()
                        .background(Color.blue)
                        .foregroundColor(.white)
                }
            }
            .navigationBarTitle("Main view")
            }   
        }   
    }
}

class ItemGroup: ObservableObject {
    @Published var isActive: [Bool] = []
    
    init() {
        //  Note: The range 1..<5 is used only for illustration purposes only.
        //        In my use case, this is actually an array of objects whose array size may vary.
        for _ in 1..<5 {
            isActive.append(false)
        }
    }
}

View2:

struct View2: View {
    @Binding var isActive: Bool
    @State var sub: String
    var body: some View {
        NavigationLink(destination: View3(isActive: $isActive)) {
            Text("Go to view 3")
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
        }
        .navigationBarTitle("View 2, \(sub)")
    }
}

View3:

struct View3: View {
    @Binding var isActive: Bool
    var body: some View {
        NavigationLink(destination: View4(isActive: $isActive)) {
            Text("Go to view 4")
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
        }
        .navigationBarTitle("View 3")
    }
}

View4 (это тот, у которого есть функция просмотра pop-to-root):

struct View4: View {
    @Binding var isActive: Bool
    
    var body: some View {
        VStack {
            Text("View 4")
            Button("Back to Main View", action: {
                isActive = false
            })
            .padding()
            .background(Color.blue)
            .foregroundColor(.white)
        }
        .navigationBarHidden(true)
    }
}

Вот демонстрации двух разных реализаций в формате gif.

Навигация с использованием массива isActive с оболочкой свойств @State (нажмите ссылку)

Навигация с использованием массива isActive внутри наблюдаемого объекта (щелкните ссылку)

Может ли кто-нибудь помочь мне понять, почему нижняя половина NavigationLinks не работает и какие изменения мне нужно сделать, чтобы она работала?

Цените помощь. Спасибо.

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

Louise C. 06.04.2021 05:18

Голос против был не моим, но я проголосовал против него. Я считаю, что ваш вопрос правильно сформулирован и ясен. Проблема сосредоточена вокруг взаимосвязи между ObservableObject и List - если вы измените List на ForEach, все будет работать, как ожидалось. Хотелось бы, чтобы у меня было легкое решение, но я не нашел что-то надежно работающее. Но я продолжу поиски.

jnpdx 06.04.2021 07:01

Спасибо @jnpdx за голосование и за то, что он сообщил мне, что ObservableObject и List плохо сочетаются друг с другом. Я не знаю об этом поведении, но я прочитаю об этом, чтобы узнать, почему. Думаю, сейчас я могу работать с ForEach вместо List. Чтобы учесть отсутствующую графику «>», которую предоставляет контейнер List, я, вероятно, могу просто включить изображение символа SF для «>», чтобы имитировать, как будет выглядеть контейнер List. Я буду продолжать следить за более легким обходным путем или исправлением, которое вы сможете найти в будущем. Цените помощь.

Louise C. 06.04.2021 07:31
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
32
0

Другие вопросы по теме