61. Примеры для MatchedGeometryEffect
В этой статье покажу несколько вариантов настроек модификатора matchedGeometryEffect с использованием одинаковой анимации linear(duration: 0.5). Этот модификатор определяет группу вьюшек с синхронизированной геометрией, используя идентификатор и неймспейс, которые мы предоставляем.
Дано
- Есть 2 фигуры: зеленый круг и черный прямоугольник
- При нажатии на кнопку "Сменить фигуру" нужно показать другую вьюху
- Под зеленым кругом и над черным прямоугольником есть спейсер фиксированной высоты (для наглядности разницы в анимациях)
struct MatchedGeometryEffectExample: View {
@State private var showCircle = false
var body: some View {
ZStack {
VStack(spacing: 4) {
noEffectOption // без matchedGeometryEffect
}
Button("Сменить фигуру") {
withAnimation(.linear(duration: 0.5)) {
showCircle.toggle()
}
}
.buttonStyle(.borderedProminent)
}
}
/// Не использует `matchedGeometryEffect`
@ViewBuilder
private var noEffectOption: some View {
if showCircle {
greenCircle
} else {
blackRectangle
}
}
private var greenCircle: some View {
VStack(spacing: 0) {
Circle()
.fill(.green)
.frame(width: 100, height: 100)
spacerWithFrame
}
}
private var blackRectangle: some View {
VStack(spacing: 0) {
spacerWithFrame
Rectangle()
.frame(width: 300, height: 50)
}
}
private var spacerWithFrame: some View {
Spacer().frame(height: 100)
}
Как видим на гифке, при нажатии на кнопку одна фигура исчезает, а другая появляется, при этом обе фигуры сохраняют свои положения (т.к. в обоих случаях есть фиксированный спейсер).
Дефолтный matchedGeometryEffect
Добавим matchedGeometryEffect с дефолтными настройками. Для этого нам понадобится задать идентификатор и namespace для анимации (код для фигур прежний):
struct MatchedGeometryEffectExample: View {
@State private var showCircle = false
@Namespace private var defaultAnimation // <- добавили
private let defaultAnimationId = "defaultAnimation" // <- добавили
var body: some View {
ZStack {
VStack(spacing: 4) {
defaultOption // matchedGeometryEffect без настроек
}
Button("Сменить фигуру") {
withAnimation(.linear(duration: 0.5)) {
showCircle.toggle()
}
}
.buttonStyle(.borderedProminent)
}
}
/// Использует `matchedGeometryEffect`, параметры по умолчанию
@ViewBuilder
private var defaultOption: some View {
if showCircle {
greenCircle
.matchedGeometryEffect(
id: defaultAnimationId,
in: defaultAnimation
)
} else {
blackRectangle
.matchedGeometryEffect(
id: defaultAnimationId,
in: defaultAnimation
)
}
}
}
Теперь при анимации вьюхи двигаются, меняя свое положение, что на мой взгляд выглядит интереснее, чем простое изменение прозрачности как в первом случае.
Настраиваем все кроме источника
В этом примере настроим два дополнительных параметра в модификаторе: properties и anchor:
@ViewBuilder
private var customAnchorAndPropertiesOption: some View {
if showCircle {
greenCircle
.matchedGeometryEffect(
id: animationTwoId,
in: animationTwo,
properties: .position, // <- добавили
anchor: .trailing // <- добавили
)
} else {
blackRectangle
.matchedGeometryEffect(
id: animationTwoId,
in: animationTwo,
anchor: .leading // <- добавили
)
}
}
В очередной раз видим новое поведение, осталось 2 сценария.
Настраиваем источником вычислений круг
@ViewBuilder
private var customOptionCircleIsSource: some View {
if showCircle {
greenCircle
.matchedGeometryEffect(
id: animationFourId,
in: animationFour
)
} else {
blackRectangle
.matchedGeometryEffect(
id: animationFourId,
in: animationFour,
isSource: false // <- добавили, по умолчанию тут true
)
}
}
Видим, что при возврате к исходному состоянию круг не движется, а просто появляется как было в самом первом примере без использования matchedGeometryEffect.
Настраиваем источником вычислений прямоугольник
@ViewBuilder
private var customOptionRectangleIsSource: some View {
if showCircle {
greenCircle
.matchedGeometryEffect(
id: animationThreeId,
in: animationThree,
isSource: false // <- добавили, по умолчанию тут true
)
} else {
blackRectangle
.matchedGeometryEffect(
id: animationThreeId,
in: animationThree
)
}
}
Видим, что при возврате к исходному состоянию прямоугольник не движется, а просто появляется как было в самом первом примере без использования matchedGeometryEffect.
Заключение
С использованием matchedGeometryEffect можно сделать много интересных анимаций - экспериментируйте и предлагайте дизайнерам 🙂
Код для этой статьи можно посмотреть тут, а другие статьи - тут.