Created
January 6, 2024 22:01
-
-
Save VAndrJ/b25ac4d201dbe539f76e0f58b9e605e2 to your computer and use it in GitHub Desktop.
SwiftUI onAppear bug
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// OnAppearCheck | |
// | |
// Created by VAndrJ on 21.12.2023. | |
// | |
import SwiftUI | |
struct ContentView: View { | |
@State var isLoggedIn: Bool = false | |
var body: some View { | |
if isLoggedIn { | |
TabView { | |
TabViewView(title: "Store", tabIcon: "trash", isLoggedIn: $isLoggedIn) | |
TabViewView(title: "New", tabIcon: "newspaper", isLoggedIn: $isLoggedIn) | |
TabViewView(title: "Gallery", tabIcon: "photo", isLoggedIn: $isLoggedIn) | |
TabViewView(title: "Discover", tabIcon: "magnifyingglass", isLoggedIn: $isLoggedIn) | |
TabViewView(title: "Profile", tabIcon: "person", isLoggedIn: $isLoggedIn) | |
} | |
} else { | |
LoginView { | |
isLoggedIn = true | |
} | |
} | |
} | |
} | |
struct TabViewView: View { | |
let title: String | |
let tabIcon: String | |
@Binding var isLoggedIn: Bool | |
var body: some View { | |
NavigationStack { | |
Text(title) | |
.navigationBarTitle(title) | |
.task { | |
print("ποΈ \(title) some task") | |
} | |
.onAppear { | |
print("π \(title) onAppear") | |
} | |
.onDisappear { | |
print("πΆβπ«οΈ \(title) onDisappear") | |
} | |
.toolbar { | |
ToolbarItem(placement: .navigationBarTrailing) { | |
Button("Logout") { | |
isLoggedIn = false | |
} | |
} | |
} | |
} | |
.task { | |
print("ποΈ \(title) NavigationStack some task") | |
} | |
.onAppear { | |
print("π \(title) NavigationStack onAppear") | |
} | |
.onDisappear { | |
print("πΆβπ«οΈ \(title) NavigationStack onDisappear") | |
} | |
.tabItem { | |
Image(systemName: tabIcon) | |
Text(title) | |
} | |
} | |
} | |
struct LoginView: View { | |
let onLogin: () -> Void | |
var body: some View { | |
Button("Login", action: onLogin) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ContentView.swift | |
// OnAppearCheck | |
// | |
// Created by VAndrJ on 21.12.2023. | |
// | |
import SwiftUI | |
struct ContentView: View { | |
@State var isLoggedIn: Bool = false | |
var body: some View { | |
if isLoggedIn { | |
TabView { | |
TabViewView(title: "Store", tabIcon: "trash") | |
TabViewView(title: "New", tabIcon: "newspaper") | |
TabViewView(title: "Gallery", tabIcon: "photo") | |
TabViewView(title: "Discover", tabIcon: "magnifyingglass") | |
TabViewView(title: "Profile", tabIcon: "person") | |
} | |
.environment(\.logout, LogoutAction(action: logout)) | |
} else { | |
LoginView { | |
isLoggedIn = true | |
} | |
} | |
} | |
func logout() { | |
isLoggedIn = false | |
} | |
} | |
struct LogoutAction { | |
let action: () -> Void | |
func callAsFunction() { | |
action() | |
} | |
} | |
struct LogoutActionKey: EnvironmentKey { | |
static var defaultValue: LogoutAction? = nil | |
} | |
extension EnvironmentValues { | |
var logout: LogoutAction? { | |
get { self[LogoutActionKey.self] } | |
set { self[LogoutActionKey.self] = newValue } | |
} | |
} | |
struct TabViewView: View { | |
let title: String | |
let tabIcon: String | |
@Environment(\.logout) var logout | |
var body: some View { | |
NavigationStack { | |
Text(title) | |
.navigationBarTitle(title) | |
.task { | |
print("ποΈ \(title) some task") | |
} | |
.onAppear { | |
print("π \(title) onAppear") | |
} | |
.onDisappear { | |
print("πΆβπ«οΈ \(title) onDisappear") | |
} | |
.toolbar { | |
ToolbarItem(placement: .navigationBarTrailing) { | |
Button("Logout") { | |
logout?() | |
} | |
} | |
} | |
} | |
.task { | |
print("ποΈ \(title) NavigationStack some task") | |
} | |
.onAppear { | |
print("π \(title) NavigationStack onAppear") | |
} | |
.onDisappear { | |
print("πΆβπ«οΈ \(title) NavigationStack onDisappear") | |
} | |
.tabItem { | |
Image(systemName: tabIcon) | |
Text(title) | |
} | |
} | |
} | |
struct LoginView: View { | |
let onLogin: () -> Void | |
var body: some View { | |
Button("Login", action: onLogin) | |
} | |
} |
@hmlongco , only this workaround helps:
public extension View {
func onDidAppear(perform action: @escaping () -> Void) -> some View {
background(DidAppearView(action: action))
}
}
struct DidAppearView: UIViewControllerRepresentable {
class Coordinator: ActionControllerRepresentableDelegate {
var action: () -> Void
init(action: @escaping () -> Void) {
self.action = action
}
func performAction() {
action()
}
}
let action: () -> Void
func makeUIViewController(context: Context) -> DidAppearViewController {
let controller = DidAppearViewController()
controller.delegate = context.coordinator
return controller
}
func updateUIViewController(_ controller: DidAppearViewController, context: Context) {
}
func makeCoordinator() -> Coordinator {
Coordinator(action: action)
}
}
protocol ActionControllerRepresentableDelegate: AnyObject {
func performAction()
}
class DidAppearViewController: UIViewController {
weak var delegate: ActionControllerRepresentableDelegate?
override func viewDidAppear(_ animated: Bool) {
delegate?.performAction()
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Looks like most of your problem is failing to provide the TabView with its selection state and also not properly providing the appropriate tag for each tab item. The following code should work as expected.