Как обновить количество отображаемых элементов ForEach после изменения размера массива (SwiftUI, Xcode 11 Beta 5)

Я пытаюсь реализовать представление, которое может изменить количество отображаемых элементов (созданных циклом ForEach), если размер массива содержимого изменяется, точно так же, как приложение для покупок может изменить количество элементов, доступных после того, как пользователь потянет для обновления

Вот код, который я пробовал до сих пор. Если я правильно помню, они работали с Xcode beta 4, но с beta 5:

  • Если размер массива увеличивается, цикл все равно будет отображать исходное количество элементов.
  • Уменьшение размера массива вызовет ошибку индекса вне допустимого диапазона

Код:

import SwiftUI

struct test : View {
    @State var array:[String] = []
    @State var label = "not pressed"
    var body: some View {
        VStack{
            Text(label).onTapGesture {
                self.array.append("ForEach refreshed")
                self.label = "pressed"
            }
            ForEach(0..<array.count){number in
                Text(self.array[number])
            }
        }
}
}

#if DEBUG
struct test_Previews: PreviewProvider {
    static var previews: some View {
        test()
    }
}
#endif

Я новичок в SwiftUI и программировании графического интерфейса в целом, и мне кажется, что каждый контент определяется во время запуска и очень сложно вносить изменения впоследствии (например: сбросить представление после того, как пользователь уйдет, а затем вернется к нему). Мы будем очень благодарны за решения проблемы цикла или любые советы по повышению динамичности представлений!


person Nguyễn Khắc Hào    schedule 02.08.2019    source источник


Ответы (1)


В примечаниях к выпуску Beta 5 говорится:

Обратное соответствие Int протоколу Identifiable удалено. Измените любой код, который полагается на это соответствие, чтобы передать .self параметру id соответствующего инициализатора. Постоянные диапазоны Int продолжают приниматься:

List(0..<5) {
   Text("Rooms")
}

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

Вы должны изменить свой ForEach, чтобы получать массив, а не диапазон. В идеале массив Identifiable, чтобы не использовать \.self. Но в зависимости от вашей цели это все еще может работать:

import SwiftUI

struct ContentView : View {
    @State var array:[String] = []
    @State var label = "not pressed"
    var body: some View {
        VStack{
            Text(label).onTapGesture {
                self.array.append("ForEach refreshed")
                self.label = "pressed"
            }
            ForEach(array, id: \.self) { item in
                Text(item)
            }
        }
    }
}

Или, как предложил rob mayoff, если вам нужен индекс:

struct ContentView : View {
    @State var array:[String] = []
    @State var label = "not pressed"
    var body: some View {
        VStack{
            Text(label).onTapGesture {
                self.array.append("ForEach refreshed")
                self.label = "pressed"
            }
            ForEach(array.indices, id: \.self) { index in
                Text(self.array[index])
            }
        }
    }
}```
person kontiki    schedule 02.08.2019
comment
Или ForEach(array.indices, id: \.self), если вам нужны индексы массива. - person rob mayoff; 02.08.2019
comment
Я всегда забываю о .indices. Спасибо, обновил ответ. - person kontiki; 02.08.2019
comment
Будет ли ForEach (array.indices, id: \ .self) обновлять представление при изменении массива? - person zdravko zdravkin; 22.06.2020
comment
Все эти решения не будут работать, если представление внутри ForEach требует привязки. - person Abdalrahman Shatou; 23.01.2021