【问题标题】:SwiftUI Disable specific tabItem selection in a TabView?SwiftUI 在 TabView 中禁用特定的 tabItem 选择?
【发布时间】:2020-04-20 06:31:48
【问题描述】:

我有一个TabView,它在点击 [+](第二个)tabItem 后会显示一张表格。同时,ContentView 也在切换TabView 的选项卡选择,所以当我关闭呈现的工作表时,所选选项卡是一个没有任何内容的空白选项卡。不是理想的用户体验。

我的问题:

我想知道如何简单地禁用特定的tabItem,这样它就不会“表现得像一个选项卡”,而只是呈现工作表,同时在点击 [+] 项之前保持先前的选项卡选择。 SwiftUI 可以做到这一点,还是我应该用另一种方法来实现这种效果?

我的标签栏图片:

这是我的ContentView 的代码,我的TabView 是:

struct SheetPresenter<Content>: View where Content: View {
    @EnvironmentObject var appState: AppState
    @Binding var isPresenting: Bool

    var content: Content
    var body: some View {
        Text("")
            .sheet(isPresented: self.$isPresenting, onDismiss: {
                // change back to previous tab selection
                print("New listing sheet was dismissed")

            }, content: { self.content})
            .onAppear {
                DispatchQueue.main.async {
                    self.isPresenting = true
                    print("New listing sheet appeared with previous tab as tab \(self.appState.selectedTab).")
                }
            }
    }
}

struct ContentView: View {
    @EnvironmentObject var appState: AppState
    @State private var selection = 0
    @State var newListingPresented = false

    var body: some View {

        $appState.selectedTab back to just '$selection'
        TabView(selection: $appState.selectedTab){

            // Browse
            BrowseView()
                .tabItem {
                    Image(systemName: (selection == 0 ? "square.grid.2x2.fill" : "square.grid.2x2")).font(.system(size: 22))
                }
                .tag(0)


            // New Listing
            SheetPresenter(isPresenting: $newListingPresented, content: NewListingView(isPresented: self.$newListingPresented))
                .tabItem {
                    Image(systemName: "plus.square").font(.system(size: 22))
                }
                .tag(1)

            // Bag
            BagView()
                .tabItem {
                    Image(systemName: (selection == 2 ? "bag.fill" : "bag")).font(.system(size: 22))
                }
                .tag(2)

            // Profile
            ProfileView()
                .tabItem {
                    Image(systemName: (selection == 3 ? "person.crop.square.fill" : "person.crop.square")).font(.system(size: 22))
                }
                .tag(3)
        }.edgesIgnoringSafeArea(.top)
    }
}

这里是AppState

final class AppState: ObservableObject {

    @Published var selectedTab: Int = 0
}

【问题讨论】:

    标签: ios swift swiftui tabview tabitem


    【解决方案1】:

    您已经非常接近您想要实现的目标。您只需要保留先前选定的选项卡索引,并在解除工作表时使用该保留值重置当前选定的选项卡索引。这意味着:

    .sheet(isPresented: self.$isPresenting, onDismiss: {
        // change back to previous tab selection
        self.appState.selectedTab = self.appState.previousSelectedTab
    }, content: { self.content })
    

    那么,如何跟踪与AppStateselectedTab 属性保持同步的最后选择的标签索引?使用 Combine 框架本身的 API 可能有更多方法可以做到这一点,但我想到的最简单的解决方案是:

    final class AppState: ObservableObject {
        // private setter because no other object should be able to modify this
        private (set) var previousSelectedTab = -1 
        @Published var selectedTab: Int = 0 {
            didSet {
                previousSelectedTab = oldValue
            }
        }
    }
    

    注意事项:

    上述解决方案可能与 禁用特定选项卡项选择 不完全相同,但在您关闭工作表后,它会恢复为所选的舒缓动画显示工作表之前的选项卡。 Here is the result

    【讨论】:

    • 这达到了我想要的效果。使用属性观察器的简单解决方案!谢谢
    【解决方案2】:

    您可以在关闭工作表中添加一些内容以将 tabView 切换到其他选项卡。也许你可以在这个过程中插入一些动画。

    struct SheetPresenter<Content>: View where Content: View {
    
        @EnvironmentObject var appState: AppState
        @Binding var isPresenting: Bool
        @Binding var showOtherTab: Int
        var content: Content
    
        var body: some View {
            Text("")
            .sheet(isPresented: self.$isPresenting,
                   onDismiss: {
                       // change back to previous tab selection
                       self.showOtherTab = 0
                   } ,
                   content: { self.content})
            .onAppear {
                DispatchQueue.main.async {
                    self.isPresenting = true
                    print("New listing sheet appeared with previous tab as tab \(self.appState.selectedTab).")
                }
            }
        }
    }
    
    struct ContentView: View {
    
        @EnvironmentObject var appState: AppState
        @State private var selection = 0
        @State var newListingPresented = false
    
        var body: some View {
            //  $appState.selectedTab back to just '$selection'
            TabView(selection: $appState.selectedTab){
                // Browse
                Text("BrowseView") //BrowseView()
                    .tabItem {
                        Image(systemName: (selection == 0 ? "square.grid.2x2.fill" : "square.grid.2x2"))
                            .font(.system(size: 22))
                }   .tag(0)
                // New Listing
                SheetPresenter(isPresenting: $newListingPresented,
                               showOtherTab: $appState.selectedTab,
                               content: Text("1232"))//NewListingView(isPresented: self.$newListingPresented))
                    .tabItem {
                        Image(systemName: "plus.square")
                            .font(.system(size: 22))
                    }   .tag(1)
                // Bag
                //  BagView()
                Text("BAGVIEW")
                    .tabItem {
                        Image(systemName: (selection == 2 ? "bag.fill" : "bag"))
                            .font(.system(size: 22))
                }   .tag(2)
                // Profile
                Text("ProfileView") //   ProfileView()
                    .tabItem {
                         Image(systemName: (selection == 3 ? "person.crop.square.fill" : "person.crop.square"))
                             .font(.system(size: 22))
                }   .tag(3)
            }   .edgesIgnoringSafeArea(.top)
        }
    }
    

    【讨论】:

    • 这在没有选择 [+] 选项卡的意义上起作用,但是没有维护先前选择的选项卡,因此它不完全是我正在寻找的。在点击 [+] 之前如何维护选项卡选择?
    • self.showOtherTab = 0 ,可以用tabItem替换0
    【解决方案3】:

    我能够使用 SwiftUI 和 MVVM 复制 Instagram 的 tabview 的以下行为:

    1. 选择中间选项卡时,将打开一个模式视图
    2. 关闭中间选项卡时,再次选择之前选择的选项卡,而不是中间选项卡

    A. ViewModel(一个用于整个 tabview,另一个用于特定选项卡)

    import Foundation
    
    class TabContainerViewModel: ObservableObject {
        
        //tab with sheet that will not be selected
        let customActionTab: TabItemViewModel.TabItemType = .addPost
        
        //selected tab: this is the most important code; here, when the selected tab is the custom action tab, set the flag that is was selected, then whatever is the old selected tab, make it the selected tab
        @Published var selectedTab: TabItemViewModel.TabItemType = .feed {
            didSet{
                if selectedTab == customActionTab {
                    customActionTabSelected = true
                    selectedTab = oldValue
                }
            }
        }
        //flags whether the middle tab is selected or not
        var customActionTabSelected: Bool = false
        
        //create the individual tabItemViewModels that will get displayed
        let tabItemViewModels:[TabItemViewModel] = [
            TabItemViewModel(imageName:"house.fill", title:"Feed", type: .feed),
            TabItemViewModel(imageName:"magnifyingglass.circle.fill", title:"Search", type: .search),
            TabItemViewModel(imageName:"plus.circle.fill", title:"Add Post", type: .addPost),
            TabItemViewModel(imageName:"heart.fill", title:"Notifications", type: .notifications),
            TabItemViewModel(imageName:"person.fill", title:"Profile", type: .profile),
        ]
    }
    
    //this is the individual tabitem ViewModel
    import SwiftUI
    
    struct TabItemViewModel: Hashable {
        let imageName:String
        let title:String
        let type: TabItemType
        
        enum TabItemType {
            case feed
            case search
            case addPost
            case notifications
            case profile
        }
    }
    

    B.视图(利用 ViewModel)

    import SwiftUI
    
    struct TabContainerView: View {
        
        @StateObject private var tabContainerViewModel = TabContainerViewModel()
        
        @ViewBuilder
        func tabView(for tabItemType: TabItemViewModel.TabItemType) -> some View {
            switch tabItemType {
            case .feed:
                FeedView()
            case .search:
                SearchView()
            case .addPost:
                AddPostView(tabContainerViewModel: self.tabContainerViewModel)
            case .notifications:
                NotificationsView()
            case .profile:
                ProfileView()
            }
        }
        
        var body: some View {
            TabView(selection: $tabContainerViewModel.selectedTab){
                ForEach(tabContainerViewModel.tabItemViewModels, id: \.self){ viewModel in
                    tabView(for: viewModel.type)
                        .tabItem {
                            Image(systemName: viewModel.imageName)
                            Text(viewModel.title)
                        }
                        .tag(viewModel.type)
                }
            }
            .accentColor(.primary)
            .sheet(isPresented: $tabContainerViewModel.customActionTabSelected) {
                PicsPicker()
            }
        }
    }
    
    struct TabContainerView_Previews: PreviewProvider {
        static var previews: some View {
            TabContainerView()
        }
    }
    

    注意:在我的调查过程中,我尝试在中间选项卡的 onAppear 中添加代码。但是,我发现 SwiftUI 中存在一个当前错误,即使点击了不同的选项卡也会触发 onAppear。所以上面的方法似乎是最好的方法。

    编码愉快!

    参考资料:

    1. https://www.youtube.com/watch?v=SZj3CjMfT-8

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-23
      • 2021-02-19
      • 1970-01-01
      • 1970-01-01
      • 2020-10-14
      • 2020-07-17
      • 2021-05-18
      相关资源
      最近更新 更多