SwiftUI是否在TabView中禁用特定的tabItem选择?

时间:2019-12-31 22:57:30

标签: ios swift swiftui tabview tabitem

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

我的问题

我想知道如何才能简单地禁用特定的tabItem,以使其不“像选项卡一样”,而只是在显示该表的同时保持在点击[+]项之前的选项卡选择。 SwiftUI可能实现这种效果,还是我应该通过另一种方式来实现这种效果?

我的标签栏的图像:

enter image description here

这是我的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
}

3 个答案:

答案 0 :(得分:1)

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

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

那么,如何跟踪与selectedTab的{​​{1}}属性保持同步的最后选择的标签索引呢?使用AppState框架本身的 API 可能有更多的方法,但是我想到的最简单的解决方案是:

Combine

注意事项:

上述解决方案可能与 禁用特定选项卡项目选择 不完全相同,但是在您关闭工作表后,它将以舒缓的动画恢复到选定的状态标签,然后再显示工作表。 Here is the result

答案 1 :(得分:0)

您可以在工作表中添加一些内容,以将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)
}
}

答案 2 :(得分:0)

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

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

A。 ViewModels(一个用于整个标签视图,另一个用于特定标签)

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。视图(利用ViewModels)

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