简体   繁体   中英

Keyboard Calls OnAppear of Other Views in TabBar SwiftUI 2.0

I am using UITabBarController in SwiftUI 2.0 and Xcode 12 but seems like Keyboard cases some unexpected behavior. As you can see from the below GIF, OnAppear of the other 2 tab's view called when the keyboard appears in the first tab. That is causing the issue as I have an API call written on appear.

Also, is there any way I can turn off the default view offset behavior of Xcode 12.

在此处输入图片说明

Here is my code of Content View.

struct ContentView: View {
    @State private var index:Int = 0
    var menuItems:[String] = ["Item 1", "Item 2", "Item 3"]
    var body: some View {
        NavigationView(content: {
            ZStack{
                MyTabView(selectedIndex: self.$index)
                    .view(item: self.item1) {
                        NewView(title: "Hello1").navigationBarTitle("")
                            .navigationBarHidden(true)
                    }
                    .view(item: self.item2) {
                        NewView(title: "Hello2").navigationBarTitle("")
                            .navigationBarHidden(true)
                    }
                    .view(item: self.item3) {
                        NewView(title: "Hello3").navigationBarTitle("")
                            .navigationBarHidden(true)
                    }
            }.navigationBarHidden(true)
            .navigationBarTitle("")
        })
    }
    
    var item1:MyTabItem {
        
        var item = MyTabItem()
        item.imageName = "pencil.circle"
        item.selectedImageName = "pencil.circle.fill"
        return item
    }
    
    var item2:MyTabItem {
        var item = MyTabItem()
        item.imageName = "pencil.circle"
        item.selectedImageName = "pencil.circle.fill"
        return item
    }
    
    var item3:MyTabItem {
        var item = MyTabItem()
        item.imageName = "pencil.circle"
        item.selectedImageName = "pencil.circle.fill"
        return item
    }
}


struct NewView:View {
    @State var text:String = ""
    var title:String
    var body: some View {
        VStack {
            Spacer()
            Text("Hello")
            TextField(title, text: self.$text)
                .textFieldStyle(RoundedBorderTextFieldStyle())
            
        }.padding()
        .onAppear {
            debugPrint("OnApper \(self.title)")
        }
    }
}

and here is the code for CustomTabView.

class MyTabViewViewModel:ObservableObject {
    var controllers: [UIViewController] = []
    var tabItems:[MyTabItem] = []
}

struct MyTabItem {
    var imageName:String = ""
    var selectedImageName:String = ""
    var hasDarkModeSupport:Bool = true
    var image:UIImage?
    var selectedImage:UIImage?
}

struct MyTabView: UIViewControllerRepresentable {
    
    var viewModel:MyTabViewViewModel = MyTabViewViewModel()
    
    @Binding var selectedIndex: Int
    
    func makeUIViewController(context: Context) -> UITabBarController {
        let tabBarController = UITabBarController()
        tabBarController.viewControllers = self.viewModel.controllers
        tabBarController.delegate = context.coordinator
        tabBarController.selectedIndex = 0
        
        let appearance = tabBarController.tabBar.standardAppearance
        appearance.shadowImage = nil
        appearance.shadowColor = nil
        appearance.backgroundEffect = nil
        tabBarController.tabBar.standardAppearance = appearance
        
        tabBarController.tabBar.shadowImage = UIImage()
        tabBarController.tabBar.backgroundImage = UIImage()
        tabBarController.tabBar.layer.shadowPath = UIBezierPath(rect: tabBarController.tabBar.bounds).cgPath
        tabBarController.tabBar.layer.shadowOffset = CGSize.init(width: 0, height: -3)
        tabBarController.tabBar.layer.shadowRadius = 5
        tabBarController.tabBar.layer.shadowColor = UIColor.black.cgColor
        tabBarController.tabBar.layer.shadowOpacity = 0.25
        tabBarController.tabBar.backgroundColor = UIColor.white
        tabBarController.tabBar.barTintColor = UIColor.white
        
        self.updateTabItems(forTabBarController: tabBarController)
        
        return tabBarController
    }
    
    func updateUIViewController(_ tabBarController: UITabBarController, context: Context) {
        tabBarController.selectedIndex = selectedIndex
        self.updateTabItems(forTabBarController: tabBarController)
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func updateTabItems(forTabBarController tabBarController:UITabBarController) {
        let isDarkModeEnable:Bool = tabBarController.traitCollection.userInterfaceStyle == .dark
        for (index, tabItem) in self.viewModel.tabItems.enumerated() {
            
            tabBarController.tabBar.items?[index].title = ""
            
            if let image = tabItem.image {
                tabBarController.tabBar.items?[index].image = image
                if let selectedImage = tabItem.selectedImage {
                    tabBarController.tabBar.items?[index].selectedImage = selectedImage
                }
            } else {
                if tabItem.hasDarkModeSupport && isDarkModeEnable {
                    if let image = UIImage.init(systemName: "\(tabItem.imageName)-dark") {
                        tabBarController.tabBar.items?[index].image = image
                    } else if let image = UIImage.init(systemName: tabItem.imageName) {
                        tabBarController.tabBar.items?[index].image = image
                    }
                    if let selectedImage = UIImage.init(systemName: "\(tabItem.selectedImageName)-dark") {
                        tabBarController.tabBar.items?[index].selectedImage = selectedImage
                    } else if let selectedImage = UIImage.init(systemName: tabItem.selectedImageName) {
                        tabBarController.tabBar.items?[index].selectedImage = selectedImage
                    }
                } else {
                    if let image = UIImage.init(systemName: tabItem.imageName) {
                        tabBarController.tabBar.items?[index].image = image
                    }
                    if let selectedImage = UIImage.init(systemName: tabItem.selectedImageName) {
                        tabBarController.tabBar.items?[index].selectedImage = selectedImage
                    }
                }
            }
        }
    }
    
    class Coordinator: NSObject, UITabBarControllerDelegate {
        var parent: MyTabView
        
        init(_ tabBarController: MyTabView) {
            self.parent = tabBarController
        }
        
        func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
            parent.selectedIndex = tabBarController.selectedIndex
        }
    }
    
    func view<HostedView:View>(item:MyTabItem, @ViewBuilder sheet: @escaping () -> HostedView) -> MyTabView {
        self.viewModel.controllers.append(UIHostingController.init(rootView: sheet()))
        self.viewModel.tabItems.append(item)
        return self
    }
}

Having the same issue myself

"Hackish" workaround is to wrap the NewView.body in a List:

    @State var text:String = ""
    var title:String
    var body: some View {
        List {
            VStack {
                Spacer()
                Text("Hello")
                TextField(title, text: self.$text)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                
            }.padding()
            .onAppear {
                debugPrint("OnApper \(self.title)")
            }
        }
    }
}

Could also work to use a LazyVStack , but haven't gotten to test it as my project targets 13.x

Same issue here OnAppear calls unexpectedly when Keyboard Appears in SwiftUI

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM