OnAppear calls unexpectedly when Keyboard Appears in SwiftUI
Asked Answered
P

3

30

I am experiencing very odd behavior in SwiftUI 2.0 and iOS14.

When the keyboard appears on the screen, the OnAppear method of other tab's view called automatically.

However, this works fine Xcode 11.7

Here is the issue in action. TextField In TabView

Here is the code which produces the above error.

struct ContentView: View {
    var body: some View {
        TabView {
            DemoView(screenName: "Home")
                .tabItem {
                    Image.init(systemName: "star.fill")
                    Text("Home")
                }
            DemoView(screenName: "Result")
                .tabItem {
                    Image.init(systemName: "star.fill")
                    Text("Result")
                }
            DemoView(screenName: "More")
                .tabItem {
                    Image.init(systemName: "star.fill")
                    Text("More")
                }
        }
    }
}

struct DemoView:View {
    
    @State var text:String = ""
    var screenName:String
    var body: some View {
        VStack{
            Text(screenName)
                .font(.title)
            
            TextField("Buggy Keyboard Issue", text: $text)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                
            Text("Issue : When keyboard appears, onAppear of other 2 tabs call automatically.")
                .font(.footnote)
        }
        .padding()
        .onAppear(perform: {
            debugPrint("OnAppear of : \(screenName)")
        })
    }
}

This seems to be a bug of SwiftUI 2.0 but not sure. Any help will be appreciated.

Thanks

Pilgarlic answered 23/9, 2020 at 11:59 Comment(16)
I have already reported to Apple about this behavior. feedbackassistant.apple.com/feedback/8732425Pilgarlic
SwiftUI onAppear is no equal to UIKit viewDidAppear. They call onAppear once view becomes rendered in view hierarchy (not when it becomes visible to user). When, why, and how many times it is called is absolutely different question, and actually not specified/documented anywhere, so I would just not rely on that behavior, because it is changed from version to version.Ploughshare
@Ploughshare but if I don't use onAppear then how should I reload the view when the user changes the tab.Pilgarlic
Did you fix that issue ?Futhark
Nope. Have you experienced the same?Pilgarlic
Yes, I have :/ .Futhark
Same issue for me!. If we cant rely on onAppear, where should we do network calls when view is visible.Vaporific
I did fix that but it's a bit of hacky way.Pilgarlic
@MalavSoni You will need to load data in the view model on data changes. Use the $published variable on the tab selection, and when it changes, load data then.Odilo
This onAppear is no longer working correctly on iOS 14 even in the latest version 14.2 than it was in iOS 13. So the onAppear related UI/business logic now needs to move elsewhere with different conditions.Putrefy
I am literally waiting to publish my app because of this bug🥺. Any workaround would be highly appreciated. Even any way to force the view somehow to call onAppear again (after it has been called wrongly). In my App I have to call some fucntions in the RootView when user inputs a value in a SubView. In iOS13, I did it by using onAppear in RootView. But since appearance of keyboard calls the onAppear of RootView too soon, the RootView doesn't get updated after user inputed something and came back to the RootView.Inglorious
I am running into this as well! Can not seem to find any sort of workaround for this right now.Grendel
i have posted a workaround maybe a solution lol check it outEbro
@MalavSoni could you share your workaround please?Beckner
There's a also a related bug here: #66939411 basically if you use NavigationView in second tab, the view will be shifted up in that tab after you show the keyboard in the first tabAnnulation
Ugh, I have analytics that are called via onAppear and they are all fouled upPaderna
E
2

Having the same issue myself, I think this is a bug or something like that, however I came up with a solution maybe a workaround until apple will fix it.

The thing that I did is basically I used a LazyVStack, and this seems to be working perfectly.

LazyVStack {
    VStack{
        Text(screenName)
            .font(.title)
        
        TextField("Buggy Keyboard Issue", text: $text)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            
        Text("Issue : When keyboard appears, onAppear of other 2 tabs call automatically.")
            .font(.footnote)
    }
    .padding()
    .onAppear(perform: {
        debugPrint("OnAppear of : \(screenName)")
})
}

Now the OnAppear method of other tab's view it is not called automatically when the keyboard appear.

Solution

Ebro answered 11/12, 2020 at 10:18 Comment(3)
what does the lazyvstack do here?Coplin
that worked for me. I think the other tab views get reloaded when the keyboard appears. lazyvstack causes the view not to be reloaded when the keyboard appears in another tab.Ramsay
this works but come potential problemsHalifax
E
1

Just implemented the following workaround:

struct ContentView: View {
    var body: some View {
        TabView(selection: $selectedTab) {
            TabContentView(tag: 0, selectedTag: selectedTab) {
                Text("Some tab content")
            }
            .tabItem {
                Text("First tab")
            }
            TabContentView(tag: 0, selectedTag: selectedTab) {
                Text("Another tab content")
            }
            .tabItem {
                Text("Second tab")
            }
        }
    }
    
    @State private var selectedTab: Int = 0
}

private struct TabContentView<Content: View, Tag: Hashable>: View {
    init(tag: Tag, selectedTag: Tag, @ViewBuilder content: @escaping () -> Content) {
        self.tag = tag
        self.selectedTag = selectedTag
        self.content = content
    }

    var body: some View {
        Group {
            if tag == selectedTag {
                content()
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
            } else {
                Color.clear
            }
        }
        .tag(tag)
    }

    private let tag: Tag
    private let selectedTag: Tag
    private let content: () -> Content
}

Not sure if it's stable enough but keyboard appearance doesn't trigger onAppear on tabs content anymore.

Espagnole answered 27/4, 2021 at 10:18 Comment(1)
Both of your TabContentViews have a tag of 0.Bowles
E
0

To avoid reloading your view try with on the TabView

.ignoresSafeArea(.keyboard, edges: .bottom)

It only works on iOS 14

Expectorant answered 21/10, 2020 at 15:34 Comment(3)
This didn't work for me, implementation is showed in the bounty gistLaterality
this won't work, it's a bug in SwiftUI I submitted a feedback to Apple but didn't get a reply yet: FB8646389, actually this bug produces a lot of unexpected behaviors in my codeAnnulation
Not working too me tooHalifax

© 2022 - 2024 — McMap. All rights reserved.