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

Malav Soni
  • 2,739
  • 1
  • 23
  • 52
  • 2
    I have already reported to Apple about this behavior. https://feedbackassistant.apple.com/feedback/8732425 – Malav Soni Sep 23 '20 at 12:34
  • 1
    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. – Asperi Sep 23 '20 at 13:56
  • @Asperi but if I don't use onAppear then how should I reload the view when the user changes the tab. – Malav Soni Sep 23 '20 at 14:34
  • Did you fix that issue ? – Hüseyin Oct 02 '20 at 11:01
  • 1
    Nope. Have you experienced the same? – Malav Soni Oct 02 '20 at 11:36
  • 1
    Yes, I have :/ . – Hüseyin Oct 04 '20 at 14:16
  • 1
    Same issue for me!. If we cant rely on `onAppear`, where should we do network calls when view is visible. – Armin Oct 16 '20 at 07:17
  • I did fix that but it's a bit of hacky way. – Malav Soni Oct 16 '20 at 16:58
  • 1
    @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. – Peter Suwara Oct 20 '20 at 02:22
  • 1
    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. – Mahmud Ahsan Nov 22 '20 at 05:34
  • 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. – Khashayar Nov 22 '20 at 10:48
  • I am running into this as well! Can not seem to find any sort of workaround for this right now. – Zac Siegel Dec 09 '20 at 01:16
  • i have posted a workaround maybe a solution lol check it out – Cod3rMax Dec 11 '20 at 10:22
  • @MalavSoni could you share your workaround please? – AsMartynas Jan 24 '21 at 21:24
  • 1
    There's a also a related bug here: https://stackoverflow.com/questions/66938911/swiftui-showing-keyboard-in-a-tab-view-adds-a-weird-space-below-second-tab-view 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 tab – JAHelia Apr 04 '21 at 07:29
  • Ugh, I have analytics that are called via onAppear and they are all fouled up – Paul Bruneau Feb 08 '22 at 15:14

3 Answers3

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

Cod3rMax
  • 878
  • 3
  • 18
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.

Mark Kotevode
  • 31
  • 1
  • 3
0

To avoid reloading your view try with on the TabView

.ignoresSafeArea(.keyboard, edges: .bottom)

It only works on iOS 14