SwiftUI - сохраняются ли представления во время навигации?

У меня ошибка при использовании NavigationLinks и ObservableObject. Я не совсем понимаю почему, потому что я не понимаю, что происходит с представлениями и данными во время навигации. Это некий псевдокод, иллюстрирующий проблему:

class Settings: ObservableObject {
    @Published var data: [Int] = [0, 1, 2, 3, 4, 5]
}

struct ContentView: View {
    @State var new_view: Bool = false
    @ObservedObject var content_view_settings = Settings()
    
    var body: some View {
        NavigationView {
            VStack {
                Button(action: {
                    DeleteLastItem()
                }) {
                    Text("Delete last item")
                }

                Button(action: {
                    self.new_view = true
                }) {
                    Text("New View")
                }
                
                NavigationLink(destination: NewView(new_view_settings: content_view_settings), isActive: $new_view) {
                    EmptyView()
                }
            }
        }
    }
}

struct NewView: View {
    @ObservedObject var new_view_settings: Settings
    @State var index = -1
    
    var body: some View {
        VStack {
            Button(action: {
                self.index = self.new_view_settings.count - 1
            }) {
                Text("change index")
            }

            if self.index > -1 {
                Text("\(self.new_view_settings.data[index])")
            }  
        }
    }
}

Описание проблемы таково:

У меня есть представление с ObservedObject, которое я передаю последующему представлению при навигации. Это вспомогательное представление обращается к последнему элементу массива, но делает это только после того, как индексная переменная проверяется нажатием кнопки. Затем текст отображается только после проверки индекса.

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

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

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


person Steve Frazee    schedule 31.08.2020    source источник


Ответы (1)


Означает ли это, что представление живо, пока не будет снова инициализировано?

Да, представление может быть живым даже после того, как вы вернетесь к родительскому представлению.


Чтобы лучше понять, что происходит, запустите тот же код на симуляторе iPad (желательно в горизонтальном режиме). Вы заметите, что NavigationView разделен на две части: master и detail - таким образом вы можете одновременно видеть и родительский, и дочерний представления.

Теперь, если вы выполните тот же эксперимент из своего вопроса, вы увидите, что дочернее представление остается, даже если вы вернетесь назад. То же самое и на iOS.

Один из способов предотвратить это - проверить, присутствуют ли индексы в массиве:

struct NewView: View {
    @ObservedObject var new_view_settings: Settings
    @State var index = -1

    var body: some View {
        VStack {
            Button(action: {
                //self.index = self.new_view_settings.count - 1
            }) {
                Text("change index")
            }

            // check if `index` is in array
            if self.index > -1 && self.index < self.new_view_settings.data.count {
                Text("\(self.new_view_settings.data[index])")
            }
        }
    }
}

Примечание: в целом я не рекомендую иметь дело с индексами в представлениях SwiftUI - обычно есть лучший способ передачи данных. Работа с индексами рискованна.

person pawello2222    schedule 31.08.2020
comment
Спасибо. Эта проверка действительно решает проблему. Итак, представления все еще живы (возможно), но перезагружаются ли они также при переходе к ним во второй раз? - person Steve Frazee; 01.09.2020
comment
@SteveFrazee Да, если у вас есть @State переменные и т. Д., Которые будут перерисовывать представление. Я действительно рекомендую вам запустить свой код на iPad, чтобы увидеть, как это работает. - person pawello2222; 01.09.2020
comment
Я не имел в виду переменные @State, перерисовывающие представление. Я имел в виду значение индексной переменной по умолчанию. Поведение iPad sim отличается от поведения iPhone. Когда я снова перехожу к представлению на iPhone, индекс возвращается к -1. Когда я снова перехожу к представлению на iPad, оно сохраняет измененное значение. - person Steve Frazee; 01.09.2020
comment
@SteveFrazee Извините, я вас неправильно понял. Ответ положительный - если вы хотите, чтобы ваши изменения сохранялись при повторной навигации в iOS, вы можете использовать @EnvironmentObject. - person pawello2222; 01.09.2020