我正在尝试使用自定义按钮重新创建分段选择器。我的目标是切换标签时,背景从以前的活动标签平滑过渡到新的活动标签。它主要工作,但当我试图切换回来(例如,从 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
我试过你的例子,它实际上似乎工作正常(使用运行 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)
}
}