是否可以在页面更改时向 TabView 子级添加自定义过渡?

问题描述 投票:0回答:1

我有一个像这样的受控选项卡视图

    TabView(selection: $activeTab) {
      IntroductionView().tag(1)
      HomeView().tag(2)
      SettingsView().tag(3)
      ProfileView().tag(4)
    }

我想在

activeTab
变化时为每个视图添加过渡,比如简单的不透明度交叉淡入淡出?

尝试了类似的事情

    TabView(selection: $activeTab) {
      IntroductionView().tag(1).transition(.opacity)
      HomeView().tag(2).transition(.opacity)
      SettingsView().tag(3).transition(.opacity)
      ProfileView().tag(4).transition(.opacity)
    }
    .animation(.smooth, value: activeTab)

但这似乎没有效果。有一些像thisone这样的答案通过

.tabViewStyle(.page(indexDisplayMode: .never))
添加某种可滚动/幻灯片动画 修改器,但这并不完全是我想要的。

也许有一种方法可以利用/扩展 UIKit 来添加交叉淡入淡出过渡?

ios swift swiftui tabs uikit
1个回答
0
投票

如果您希望能够使用视图修饰符为每个选项卡指定

Transition
(就像在示例代码中所做的那样),最好编写自己的
TabView

这是我在here编写的自定义“选项卡视图”的改编版本,它使选项卡看起来更像选项卡。我还添加了

tabTransition
修饰符,让您可以选择
Transition
。请注意,这取决于 View Extractor

struct CustomTabView<Content: View, Selection: Hashable>: View {
    
    @Binding var selectedTab: Selection
    
    @ViewBuilder let content: () -> Content
    
    var body: some View {
        Extract(content) { views in
            ForEach(views) { view in
                if view.id(as: Selection.self) == selectedTab {
                    view
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .transition(view[TabTransitionTrait.self])
                }
            }
        }
        .safeAreaInset(edge: .bottom) {
            HStack {
                Spacer()
                ExtractMulti(content) { views in
                    ForEach(views) { view in
                        Group {
                            if let label = view[CustomTabItemTrait.self] {
                                label
                            } else {
                                Text("Unnamed")
                            }
                        }
                        .onTapGesture {
                            if let selection = view.id(as: Selection.self) {
                                selectedTab = selection
                            }
                        }
                        .foregroundStyle(
                            view.id(as: Selection.self) == selectedTab ?
                                AnyShapeStyle(Color.accentColor) : AnyShapeStyle(.opacity(1))
                        )
                        Spacer()
                    }
                }
            }
        }
    }
}

extension View {
    func customTabItem<Content: View>(@ViewBuilder content: () -> Content) -> some View {
        _trait(CustomTabItemTrait.self, AnyView(content()))
    }
}

struct CustomTabItemTrait: _ViewTraitKey {
    static let defaultValue: AnyView? = nil
}

extension View {
    func tabTransition<T: Transition>(_ transition: T) -> some View {
        _trait(TabTransitionTrait.self, AnyTransition(transition))
    }
}

struct TabTransitionTrait: _ViewTraitKey {
    static let defaultValue: AnyTransition = .identity
}

使用示例:

struct ContentView: View {
    @State var selectedTab = 0
    var body: some View {
        CustomTabView(selectedTab: $selectedTab.animation()) {
            Color.blue
                .id(0) // you must use .id instead of .tag to specify the selection value
                .customTabItem {
                    Label("Baz", systemImage: "circle")
                }
                .tabTransition(.push(from: .leading))
            
            Color.yellow
                .id(1)
                .customTabItem {
                    Label("Foo", systemImage: "globe")
                }
                .tabTransition(.push(from: .bottom))
            
            Color.green
                .id(2)
                .customTabItem {
                    Label("Bar", systemImage: "rectangle")
                }
                .tabTransition(.opacity)
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.