Анимация для пользовательского сегментированного средства выбора в 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)
    }
}
Другие вопросы по тегам