88. Исследуем onAppear/onDisappear
Oleg991Во всех приложениях есть экраны, где нужно при появлении/исчезновении вьюхи выполнить какую-то работу. В этой статье посмотрим на onAppear, onDisappear, которые используются для такой цели в SwiftUI, и сравним их с аналогами из UIKit.
Демонстрация
Пояснение
Будем сравнивать onAppear/onDisappear с такими аналогами из UIKit: viewWillAppear/viewWillDisappear.
Знаю, что там есть еще другие методы, но конкретно в этой статье будут такие.
Подготовка
- Сделаем
UIViewController - Завернем его в
UIViewControllerRepresentable - Сложим все в модификатор
Результат
import SwiftUI
import UIKit
struct BackgroundViewController: UIViewControllerRepresentable {
let viewWillAppear: () -> Void
let viewWillDisappear: () -> Void
func makeUIViewController(context: Context) -> UIViewController {
let viewController = CustomViewController()
viewController.viewWillAppearAction = viewWillAppear
viewController.viewWillDisappearAction = viewWillDisappear
return viewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
final class CustomViewController: UIViewController {
var viewWillAppearAction: (() -> Void)?
var viewWillDisappearAction: (() -> Void)?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
viewWillAppearAction?()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
viewWillDisappearAction?()
}
}
}
struct UIKitLifeCycleModifier: ViewModifier {
let viewWillAppear: () -> Void
let viewWillDisappear: () -> Void
func body(content: Content) -> some View {
content
.background(
BackgroundViewController(
viewWillAppear: viewWillAppear,
viewWillDisappear: viewWillDisappear
)
)
}
}
Код для экрана
struct LifeCycleExampleView: View {
var body: some View {
NavigationView {
VStack(spacing: 20) {
Text("Контент первого экрана")
.onAppear { print("1 onAppear") }
.onDisappear { print("1 onDisappear") }
.modifier(
UIKitLifeCycleModifier(
viewWillAppear: { print("1 viewWillAppear") },
viewWillDisappear: { print("1 viewWillDisappear") }
)
)
NavigationLink(destination: secondDemoView) {
Text("Открыть второй экран")
}
}
}
.font(.title)
}
private var secondDemoView: some View {
Text("Контент второго экрана")
.onAppear { print("2 onAppear") }
.onDisappear { print("2 onDisappear") }
.modifier(
UIKitLifeCycleModifier(
viewWillAppear: { print("2 viewWillAppear") },
viewWillDisappear: { print("2 viewWillDisappear") }
)
)
}
}
#Preview {
LifeCycleExampleView()
}
Результаты (часть 1)
Вот такие принты мы видим в консоли:
1 onAppear 1 viewWillAppear 2 onAppear 1 viewWillDisappear 2 viewWillAppear 1 onDisappear 2 viewWillDisappear 1 viewWillAppear 1 onAppear 2 onDisappear
Выводы
onAppearвызывается раньше, чемviewWillAppearonDisappearвызывается позже, чемviewWillDisappear
Результаты (часть 2)
Вот такие принты мы видим в консоли:
2 viewWillDisappear 1 viewWillAppear 1 onAppear 1 viewWillDisappear 2 viewWillAppear 1 onDisappear
Выводы
Если жест свайпа назад не заканчивается, и верхний экран остается в стеке навигации, то:
- Для экрана, который мы начали закрывать, но не закрыли, вызовется только
viewWillDisappear, аonDisappearне вызовется вообще - Для родительского экрана, который мы частично увидели при свайпе, вызываются все 4 события, но на этот раз первыми будут события из жизненного цикла
UIKit, т.е.viewWillAppear/viewWillDisappear
Заключение
Путем нехитрых экспериментов можно достаточно быстро сравнить поведение SwiftUI-модификаторов для жизненного цикла вьюхи и UIKit-предшественников.
Здорово, если тебе удалось узнать что-то новое, и еще лучше, если это ты сможешь использовать это в своей практике 😉
Код для этой статьи можно посмотреть тут, другие статьи по разработке - тут, а про инвестиции - тут.