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()
}
}
}