В SwiftUI мы можем создавать плавные переходы между экранами, из одного состояния в другое, с помощью Matched Geometry Effect (эффект совпадающей геометрии). Используя уникальные идентификаторы, мы можем смешивать геометрию двух видов с одним и тем же идентификатором, создавая анимированный переход. Подобные переходы могут быть полезны для навигации или изменения состояния элементов пользовательского интерфейса.
Чтобы реализовать его в пользовательском интерфейсе, необходимо:
- Определить пространство имен, которое будет использоваться для синхронизации геометрии представлений;
- Определить начальное и конечное состояния представлений, которые будут анимированы;
- Использовать соответствующий модификатор представления для определения начального и конечного состояний, в которых будет происходить переход к согласованной геометрии;
- Запустите переход.
Давайте добавим анимированный переход с использованием эффекта совпадающей геометрии к следующему виду:
struct AnimatedExampleView: View { // Variable to trigger the animated transition @State var isExpanded: Bool = true var body: some View { VStack { if isExpanded { smallSizeView() } else { largeSizeView() } } .padding() // On tap the transition is triggered .onTapGesture { withAnimation { isExpanded.toggle() } } } @ViewBuilder func smallSizeView() -> some View { // Initial state of the view RoundedRectangle(cornerRadius: 25) .fill(.black) .frame(width: 300,height: 300) .overlay { Text("Hello Developer") .font(.headline) .foregroundStyle(.white) } } @ViewBuilder func largeSizeView() -> some View { // Final state of the view RoundedRectangle(cornerRadius: 25) .fill(.black) .overlay { Text("Hello Developer") .font(.headline) .foregroundStyle(.white) } } }
Чтобы включить Matching Geometry Effect, необходимо:
- Определить идентификатор для каждого представления, которое будет занимать место в переходе
- Определить пространство имен, в котором будут определены идентификаторы группы представлений, с помощью обертки свойства @Namespace
- Применить модификатор
.matchedGeometryEffect(id:in:properties:anchor:isSource:)
к каждому из видов, которые будут частью анимированного перехода
struct AnimatedExampleView: View { @State var isExpanded: Bool = true // Identifier for the rectangle view private var rectangleId = "Rectangle" // Namespace for the expansion effect @Namespace var expansionAnimation var body: some View { VStack { if isExpanded { smallSizeView() } else { largeSizeView() } } .padding() .onTapGesture { withAnimation { isExpanded.toggle() } } } @ViewBuilder func smallSizeView() -> some View { RoundedRectangle(cornerRadius: 25) .fill(.black) .frame(width: 300,height: 300) // Added the matched geometry modifier to the view .matchedGeometryEffect(id: rectangleId, in: expansionAnimation) .overlay { Text("Hello Developer") .font(.headline) .foregroundStyle(.white) } } @ViewBuilder func largeSizeView() -> some View { RoundedRectangle(cornerRadius: 25) .fill(.black) // Added the matched geometry modifier to the view .matchedGeometryEffect(id: rectangleId, in: expansionAnimation) .overlay { Text("Hello Developer") .font(.headline) .foregroundStyle(.white) } } }
После этого при запуске анимации представления, имеющие одинаковый идентификатор и находящиеся в одном пространстве имен, будут плавно переходить друг в друга.
Посмотрите еще видео про Matched Geometry Effect: