SwiftUI: создание пользовательского сегментированного элемента управления с помощью .matchedGeometry()

В настоящее время я пытаюсь создать какой-то собственный сегментированный элемент управления. Мой код сейчас выглядит так:

      @AppStorage("selectedcountry") private var selectedCountry: Country = .france
@Namespace private var animation

HStack {
    ForEach(Country.allCases, id: \.self) { country in
        Button {
            withAnimation {
                selectedCountry = country
            }
        } label: {
            Text(country.flag)
                .padding()
        }
        .frame(maxWidth: .infinity)
        .background {
            if selectedCountry == country {
                RoundedRectangle(cornerRadius: 10)
                    .fill(Color.accentColor)
                    .matchedGeometryEffect(id: "country", in: animation)
            }
        }
    }
}

Элемент RoundedRectangle(cornerRadius: 10) должен перемещаться, как в обычных сегментированных элементах управления iOS. Но почему-то у меня просто не получается эффект согласованной геометрии.

Какой-нибудь совет, который вы могли бы мне дать?

1 ответ

Вот как заставить это работать:

  • Должен быть только один маркер, который можно установить в качестве фона позадиHStack.
  • Идентификатор фонового маркера должен совпадать с идентификатором выбранного элемента.
  • Необходимо определить источник соответствия геометрическому эффекту. В любой момент времени источником может быть только один элемент.

Так:

      HStack {
    ForEach(Country.allCases, id: \.self) { country in
        Button {
            withAnimation {
                selectedCountry = country
            }
        } label: {
            Text(country.flag)
                .padding()
        }
        .matchedGeometryEffect(id: country, in: animation, isSource: selectedCountry == country)
        .frame(maxWidth: .infinity)
    }
}
.background {
    RoundedRectangle(cornerRadius: 10)
        .fill(Color.accentColor)
        .matchedGeometryEffect(id: selectedCountry, in: animation, isSource: false)
}

Я не мог понять, почему маркер прыгал из одной позиции в другую, а не двигался анимированным образом. Но это потому, что обновляемая переменная@AppStorageвместо . Когда вы измените его на@State, анимация работает нормально:

      @State private var selectedCountry: Country = .france

См. SwiftUI: сохраните состояние переключения и сохраните анимацию для некоторых обходных путей.

Другие вопросы по тегам