Matched Geometry Effects

Если у вас имеется представление, которое может отображаться в разных частях экрана, и при этом смену положения представления необходимо делать анимировано, то для этого можно использовать модификатор matchedGeometryEffect, который позволит сделать это очень плавно.

В iOS 13, что бы поменять два представления местами мы могли сделать это следующим образом:

struct ContentView: View {
    
    @State private var isFlipped = false
    
    var body: some View {
        VStack {
            if isFlipped {
                Circle()
                    .fill(Color.red)
                    .frame(width: 44, height: 44)
                Text("Taylor Swift")
                    .font(.headline)
            } else {
                Text("Taylor Swift")
                    .font(.headline)
                Circle()
                    .fill(Color.red)
                    .frame(width: 44, height: 44)
            }
        }
        .onTapGesture {
            withAnimation {
                self.isFlipped.toggle()
            }
        }
    }
}

При таком подходе два представления меняются местами, но при это смена позиций происходит не плавно:

С новым модификатором можно добиться большего. Для этого его нужно вызвать у тех представлений, которые нужно поменять местами, но для начала нужно объявить еще одно свойство с оберткой @Namespace, которое определит глобальное пространство имен для наших представлений. Данное свойство объединяет представления в один общий сценарий для анимации:

@Namespace private var animation

Теперь мы можем вызвать модификатор .matchedGeometryEffect у тех представлений, которые нам необходимо задействовать в анимированной смене своего местоположения. В параметр YourIdentifierHere необходимо передать уникальный идентификатор, который будет использоваться каждой парой наших представлений:

.matchedGeometryEffect(id: "Circle", in: animation)

И соответственно для текста:

.matchedGeometryEffect(id: "Name", in: animation)

В итоге это позволит менять представления местами очень плавно:

Весь код выглядит так:

struct ContentView: View {
    
    @Namespace private var animation
    @State private var isFlipped = false
    
    var body: some View {
        VStack {
            if isFlipped {
                Circle()
                    .fill(Color.red)
                    .frame(width: 44, height: 44)
                    .matchedGeometryEffect(id: "Circle", in: animation)
                Text("Taylor Swift")
                    .font(.headline)
                    .matchedGeometryEffect(id: "Name", in: animation)
            } else {
                Text("Taylor Swift")
                    .font(.headline)
                    .matchedGeometryEffect(id: "Name", in: animation)
                Circle()
                    .fill(Color.red)
                    .frame(width: 44, height: 44)
                    .matchedGeometryEffect(id: "Circle", in: animation)
            }
        }
        .onTapGesture {
            withAnimation {
                self.isFlipped.toggle()
            }
        }
    }
}

Курсы по языку Swift, доступные каждому: learnmetoo.info

Первоисточник