SwiftUI 中自定义分段选择器的动画

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

我正在尝试使用自定义按钮重新创建分段选择器。我的目标是切换标签时,背景从以前的活动标签平滑过渡到新的活动标签。它主要工作,但当我试图切换回来(例如,从 tab3 到 tab2 或 tab1)时,动画消失/或不起作用。我错过了什么吗?

struct CustomSegmentedPickerView: View {

private var titles = ["products", "causes", "info"]
private var colors = [Color.primaryAccent, Color.primaryAccent, Color.primaryAccent]
@State private var currentIndex: Int = 0
@Namespace var namespace
@Namespace var namespace2



var body: some View {
    VStack (alignment: .center){
        ZStack {
            HStack {
                ForEach (0 ..< titles.count) {index in
                    Button {
                        withAnimation(.default){
                            self.currentIndex = index
                        }
                    } label: {
                    ZStack {
                        if index == currentIndex {
                            Rectangle()
                                .frame(height: 40)
                                .cornerRadius(770)
                                .foregroundColor(
                                    self.colors[self.currentIndex].opacity(0.3))
                                .matchedGeometryEffect(id: "background", in: namespace)
                            
                        } else {
                                Rectangle()
                                    .frame(height: 40)
                                    .cornerRadius(770)
                                    .matchedGeometryEffect(id: "background2", in: self.namespace2)
                                    .foregroundColor(.clear)
                        }
                            Text(self.titles[index])
                                .foregroundColor(.black)
                        }
                    }
                }
            }
            .padding()
        }
    }
 }
}

我想也许我需要一个新的命名空间 ID,但它并没有改变任何东西。任何帮助表示赞赏。提前致谢!

BR

animation swiftui picker matchedgeometryeffect
1个回答
0
投票

我试过你的例子,它实际上似乎工作正常(使用运行 iOS 16.4 和 Xcode 14.3 的 iPhone 14 模拟器),除了标签本身会在选择之间闪烁。但是,控制台中报告了以下警告/错误:

匹配几何组对中的多个插入视图(第一个:“background2”,第二个:SwiftUI.Namespace.ID(id:84))具有“isSource:true”,结果未定义。

我注意到你也在背景上使用了一个巨大的圆角半径,但我不认为这可能是你所描述的问题的原因。

所以我不得不承认,我不熟悉

.matchedGeometryEffect
。但如果很难让它工作,这听起来是尝试更简单的解决方案的好理由。

我建议,让背景在项目之间移动的更简单方法是使用两层,一层用于背景,一层用于标签,然后根据选择调整背景的 x 偏移量。以下是我认为你想要的:

struct CustomSegmentedPickerView: View {

    private let titles = ["products", "causes", "info"]
    private let colors = [Color.green, Color.blue, Color.red]
    @State private var currentIndex: Int = 0

    /// - Returns the width of a picker item
    private func itemWidth(availableWidth: CGFloat) -> CGFloat {
        availableWidth / CGFloat(titles.count)
    }

    /// - Returns the x-offset for the current selection
    private func xOffsetForSelection(availableWidth: CGFloat) -> CGFloat {
        itemWidth(availableWidth: availableWidth) * CGFloat(currentIndex)
    }

    var body: some View {
        GeometryReader { proxy in
            ZStack(alignment: .leading) {

                // The background that moves between the items
                RoundedRectangle(cornerRadius: 20)
                    .foregroundColor(colors[currentIndex].opacity(0.3))
                    .frame(
                        width: itemWidth(availableWidth: proxy.size.width),
                        height: proxy.size.height
                    )
                    .offset(x: xOffsetForSelection(availableWidth: proxy.size.width))

                // The labels for the items
                HStack {
                    ForEach(Array(titles.enumerated()), id: \.element) { i, element in
                        Text("\(element)")
                            .frame(maxWidth: .infinity)
                            .onTapGesture {
                                withAnimation { currentIndex = i }
                            }
                    }
                }
            }
        }
        .padding(.horizontal)
        .frame(height: 40)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.