SwiftUI .onAppear, запускается только один раз

В SwiftUI app у меня есть такой код:

var body: some View {
    VStack {
        Spacer()
        ........
    }
    .onAppear {
      .... I want to have some code here ....
      .... to run when the view appears ....
    }
}

Моя проблема в том, что я хотел бы запустить некоторый код внутри блока .onAppear, чтобы он запускался, когда приложение появляется на экране, после запуска или после того, как некоторое время находится в фоновом режиме. Но похоже, что этот код запускается только один раз при запуске приложения и никогда после него. Я что-то упускаю? Или мне следует использовать другую стратегию, чтобы получить желаемый результат?

3 ответа

Решение

Вам нужно будет наблюдать за событием, когда приложение выходит на передний план, и публиковать его, используя @Published к ContentView. Вот как:

struct ContentView: View {

    @ObservedObject var observer = Observer()

    var body: some View {
        VStack {
            Spacer()
            //...
        }
        .onReceive(self.observer.$enteredForeground) { _ in
            print("App entered foreground!") // do stuff here
        }
    }
}

class Observer: ObservableObject {

    @Published var enteredForeground = true

    init() {
        if #available(iOS 13.0, *) {
            NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIScene.willEnterForegroundNotification, object: nil)
        } else {
            NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
        }
    }

    @objc func willEnterForeground() {
        enteredForeground.toggle()
    }
}

Если вы устанавливаете ссылку на iOS14, вы можете воспользоваться новым scenePhase концепция:

      @Environment(\.scenePhase) var scenePhase

Где бы вы ни находились, Среда, если внедрите это свойство, которое вы можете протестировать по трем условиям:

      switch newPhase {
    case .inactive:
        print("inactive")
    case .active:
        print("active")
    case .background:
        print("background")
}

Итак, все вместе:

      struct ContentView: View {
    @Environment(\.scenePhase) var scenePhase

    var body: some View {
        Text("Hello, World!")
            .onChange(of: scenePhase) { newPhase in
                switch newPhase {
                    case .inactive:
                        print("inactive")
                    case .active:
                        print("active")
                    case .background:
                        print("background")
                }
            }
    }
}

Вы можете использовать.onFirstAppearмодификатор типа:

      Text("Hello World")
    .onFirstAppear {
        /* The code here only runs on the first appear */
    }

Вам просто нужно реализовать простое расширение:

      public extension View {
    func onFirstAppear(perform action: @escaping () -> Void) -> some View {
        modifier(ViewFirstAppearModifier(perform: action))
    }
}

struct ViewFirstAppearModifier: ViewModifier {
    @State private var didAppearBefore = false
    private let action: () -> Void

    init(perform action: @escaping () -> Void) {
        self.action = action
    }

    func body(content: Content) -> some View {
        content.onAppear {
            guard !didAppearBefore else { return }
            didAppearBefore = true
            action()
        }
    }
}
Другие вопросы по тегам