【问题标题】:SwiftUI TabView - run code in subview after sequential tapsSwiftUI TabView - 在连续点击后在子视图中运行代码
【发布时间】:2023-01-29 23:37:46
【问题描述】:

当用户多次点击同一个选项卡时,我试图在 TabView 中实现该行为,例如在 iOS AppStore 应用程序中。第一次点击:切换到该视图,第二次点击:弹出到根,第三次点击:如果需要滚动到顶部。

下面的代码适用于切换,每次点击都会调用didTap()

import SwiftUI

enum Tab: String {
    case one
    case two
}

struct AppView: View {
    @State private var activeTab = Tab.one

    var body: some View {
        TabView(selection: $activeTab.onChange(didTap)) {
            One()
                .tabItem {
                    Label("one", systemImage: "1.lane")
                }
                .tag(Tab.one)

            Two()
                .tabItem {
                    Label("two", systemImage: "2.lane")
                }
                .tag(Tab.two)
        }
    }
    
    func didTap(to value: Tab) {
        print(value) // this captures every tap
    }
}

extension Binding {
    func onChange(_ handler: @escaping (Value) -> Void) -> Binding<Value> {
        Binding(
            get: { self.wrappedValue },
            set: { newValue in
                self.wrappedValue = newValue
                handler(newValue)
            }
        )
    }
}

我正在努力解决的问题是如何告诉OneTwo 它被第二次或第三次窃听了? (如何弹出和滚动不是问题)。

我看过这个:TabView, tabItem: running code on selection or adding an onTapGesture 但它没有解释如何在其中一个视图中运行代码。

有什么建议么?

【问题讨论】:

    标签: ios swiftui tabview


    【解决方案1】:

    您可以在数组中记录额外的抽头(具有相同的值)。数组计数为您提供同一选项卡上的点击次数。

    编辑:现在有明确的子视图结构。

    struct ContentView: View {
        
        @State private var activeTab = Tab.one
    
        @State private var tapState: [Tab] = [Tab.one] // because .one is default
        
        var body: some View {
            TabView(selection: $activeTab.onChange(didTap)) {
                
                SubView(title: "One", tapCount: tapState.count)
                    .tabItem {
                        Label("one", systemImage: "1.lane")
                    }
                    .tag(Tab.one)
    
                SubView(title: "Two", tapCount: tapState.count)
                    .tabItem {
                        Label("two", systemImage: "2.lane")
                    }
                    .tag(Tab.two)
            }
        }
        
        func didTap(to value: Tab) {
            print(value) // this captures every tap
            if tapState.last == value {
                tapState.append(value) // apped next tap if same value
                print("tapped (tapState.count) times")
            } else {
                tapState = [value] // reset tap state to new tab selection
            }
        }
    }
    
    
    struct SubView: View {
        
        let title: String
        let tapCount: Int
        
        var body: some View {
            VStack {
                Text("Subview (title)").font(.title)
                Text("tapped (tapCount) times")
            }
        }
    }
    

    【讨论】:

    • 捕获点击次数是个好主意。但是我如何将它传递给 TabView 中的视图,以便他们可以做出相应的响应?
    • 刚刚编辑,将 tapState 传递到子视图中
    • 完美,您的最终编辑。
    • 最后我最终使用了这里描述的方法:notificare.com/blog/2022/11/25/a-better-tabview-in-swiftui
    【解决方案2】:

    经过大量的试验和错误,我想出了以下完全符合我想要做的事情。

    受本文启发:https://notificare.com/blog/2022/11/25/a-better-tabview-in-swiftui/

    enum Tab: String {
        case one
        case two
    }
    
    struct ContentView: View {
        @State var selectedTab = Tab.one
        @State var oneNavigationPath = NavigationPath()
        @State var twoNavigationPath = NavigationPath()
    
        var body: some View {
            ScrollViewReader { proxy in
                TabView(selection: tabViewSelectionBinding(proxy: proxy)) {
                    SubView(title: "One", path: $oneNavigationPath)
                        .tabItem {
                            Label("one", systemImage: "1.lane")
                        }
                        .tag(Tab.one)
    
                    SubView(title: "Two", path: $twoNavigationPath)
                        .tabItem {
                            Label("two", systemImage: "2.lane")
                        }
                        .tag(Tab.two)
                }
            }
        }
    
        private func tabViewSelectionBinding(proxy: ScrollViewProxy) -> Binding<Tab> {
            Binding<Tab>(
                get: { selectedTab },
                set: { newValue in
                    if selectedTab == newValue {
                        switch selectedTab {
                        case .one:
                            if oneNavigationPath.isEmpty {
                                withAnimation {
                                    proxy.scrollTo(Tab.one, anchor: .bottom)
                                }
                            } else {
                                withAnimation {
                                    oneNavigationPath = NavigationPath()
                                }
                            }
                        case .two:
                            if twoNavigationPath.isEmpty {
                                withAnimation {
                                    proxy.scrollTo(Tab.two, anchor: .bottom)
                                }
                            } else {
                                withAnimation {
                                    twoNavigationPath = NavigationPath()
                                }
                            }
                        }
                    }
    
                    selectedTab = newValue
                }
            )
        }
    }
    
    struct SubView: View {
        let title: String
        let items = Array(1...100)
        
        @Binding var path: NavigationPath
    
        var body: some View {
            NavigationStack(path: $path) {
                VStack {
                    Text("Subview (title)").font(.title)
                    
                    List {
                        EmptyView()
                            .id(Tab(rawValue: title.lowercased()))
                        
                        ForEach(items, id: .self) { item in
                            NavigationLink(value: item) {
                                Text("Item (item)")
                            }
                        }
                    }
                    .navigationDestination(for: Int.self) { item in
                        Text("Item (item)")
                    }
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2020-08-15
      • 2020-09-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多