Сбой SwiftUI TabView при отображении вкладки, содержащей данные, которые изменились, когда указанная вкладка была невидимой

#ios #swift #swiftui #tabview

#iOS #swift #swiftui #tabview

Вопрос:

SwiftUI TabView содержит до двух вкладок. Каждый отображает данные из одной и той же модели. Если данные отсутствуют, отображается progressView, если данные присутствуют, отображается текущее значение. В примере модели данные устанавливаются асинхронно через 2 секунды, что вызывает проблему. (В моем реальном приложении данные устанавливаются в результате асинхронного вызова.)

После запуска приложения отображается первая вкладка, содержащая progressView, и данные отображаются через 2 секунды. Пока все, кажется, работает. При переключении с первой вкладки на вторую приложение вылетает где-то на глубине 75 уровней в коде отображения SwiftUI:

(AG::Graph::add_indirect_attribute:) с EXC_BAD_ACCESS .

Что я делаю не так и как я могу обойти проблему, если это ошибка SwiftUI?

Следующий код вызывает сбой:

 import SwiftUI
import Combine

class Model: ObservableObject{
    @Published var value:Double?
    
    init(){
        DispatchQueue.main.asyncAfter(deadline: .now()   3){
            self.value = 2.0
            print("value set")
        }
    }
}

@main
struct TabViewUpdatingTestApp: App {
    @StateObject var model = Model()
    
    var body: some Scene {
        WindowGroup {
            ContentView(model: model)
        }
    }
}

struct Tab: View {
    let name:String
    @ObservedObject var model:Model
    
    var body: some View {
        if let mvalue = model.value{
            Text("name: (name), value: (mvalue)")
        }else{
            ProgressView().font(.largeTitle)
        }
    }
}

struct ContentView: View {
    @ObservedObject var model:Model
    
    var body: some View {
        TabView {
            Tab(name: "Tab 1", model: model)
                .tabItem {
                    Label("Tab 1", systemImage: "1.circle")
                }
            Tab(name: "Tab 2", model: model)
                .tabItem {
                    Label("Tab 2", systemImage: "2.circle")
                }
        }
    }
}
 

Ответ №1:

Для меня это похоже на ошибку. Возможным обходным решением может быть принудительное обновление TabView :

 struct ContentView: View {
    @ObservedObject var model: Model

    var body: some View {
        TabView {
            Tab(name: "Tab 1", model: model)
                .tabItem {
                    Label("Tab 1", systemImage: "1.circle")
                }
            Tab(name: "Tab 2", model: model)
                .tabItem {
                    Label("Tab 2", systemImage: "2.circle")
                }
        }
        .id(model.value) // add here
    }
}
 

В качестве альтернативы, вместо того, чтобы перерисовывать представление при model.value изменениях, вы можете создать какую-либо другую переменную, которая изменяется только один раз (поэтому вы обновляете ее не TabView каждый раз, а только один раз в начале).

Комментарии:

1. Спасибо! Это предотвращает сбой в моем реальном коде, и поскольку это значение устанавливается только один раз, нет опасности слишком большого количества перерисовок. Кроме того, я не знал о модификаторе id. В будущем может быть полезно выполнить некоторые принудительные перерисовки.