Анимация для пользовательского сегментированного средства выбора в SwiftUI
Я пытаюсь воссоздать сегментированный сборщик с настраиваемыми кнопками. Моя цель состоит в том, чтобы при переключении вкладки фон плавно переходил от бывшей активной вкладки к новой активной вкладке. в основном это работает, но когда я пытаюсь переключиться обратно (например, с 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()
}
}
}
}
Я подумал, может быть, мне нужен новый идентификатор пространства имен, но это ничего не меняет. Любая помощь приветствуется. Заранее спасибо!
БР
1 ответ
Я попробовал ваш пример, и он, похоже, работал нормально (с использованием симулятора iPhone 14 под управлением iOS 16.4 с Xcode 14.3), за исключением того, что сами метки мигали между выборами. Однако в консоли появляется следующее предупреждение/ошибка:
Несколько вставленных представлений в совпадающей группе геометрии Pair(первый: «background2», второй: SwiftUI.Namespace.ID(id: 84)) имеют `isSource: true`, результаты не определены.
Я заметил, что вы также использовали огромный радиус угла на заднем плане, но не думаю, что это могло быть причиной описанной вами проблемы.
Так что, признаюсь, я не знаком с
Я бы предложил более простой способ заставить фон перемещаться между элементами — просто использовать два слоя: один для фона и один для меток, а затем отрегулировать смещение фона по оси 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)
}
}