[SwiftUI] 자연스러운 애니메이션을 위한 matchedGeometryEffect() 활용

양재현·2026년 2월 23일
post-thumbnail

애니메이션은 그 자체로 자연스러운 효과를 주지만, 단순히 나타나고 사라지는(Fade-in/out) 방식은 자칫 사용자의 시선 흐름을 툭 끊어버릴 수도 있다.

단순히 화면을 교체하는 게 아니라 화면의 형태를 유지하면서 자연스럽게 사용자의 시선을 가이드 한다면 사용자에게 '연속적인 경험'을 줄 수 있을 것이다.

그리고 그것을 MatchedGeometryEffect가 도와줄 것이다.

핵심 원리

딱 두 가지만 알면 된다.

@Namespace : 애니메이션이 일어날 공유 공간을 만든다. 같은 공간 안에 있는 이름표들끼리는 상대적인 위치 좌표를 계산할 수 있게 된다.

matchedGeometryEffect() : 연결하고 싶은 두 뷰에 똑같은 id를 붙여준다.

// 1. 공간을 만들고
@Namespace private var animationSpace 

// 2. 출발지와 도착지에 같은 이름표를 붙인다
.matchedGeometryEffect(id: "uniqueID", in: animationSpace)

이렇게 적용하면 같은 공간안에 있는 두 뷰가 애니메이션으로 전환되는 찰나에, 실제로 뷰가 이동되는 느낌을 주게 된다.

원리는 A뷰 -> B뷰로 전환될 때 두 뷰 사이의 거리와 크기 차이를 SwiftUI가 수학적으로 계산해서 그 찰나를 계속해서 메꿔주는 것이다.

즉, matchedGeometryEffect는 뷰를 실제로 옮기는 게 아니라, 두 뷰 사이의 거리와 크기 차이를 보간(Interpolate)해서 눈속임을 하는 기술이다.

예제 1 : 카테고리 바 이동

import SwiftUI

struct CategoryMenu: View {
    @Namespace private var namespace // 1. 공간을 만들고
    @State private var selected = "A"
    let menus = ["A", "B", "C"]

    var body: some View {
        HStack(spacing: 30) {
            ForEach(menus, id: \.self) { menu in
                Text(menu)
                    .padding(10)
                    .background {
                        if selected == menu {
                            Capsule()
                                .fill(.blue.opacity(0.2))
                                // 2. 출발지와 도착지에 같은 이름표를 붙인다
                                .matchedGeometryEffect(id: "background", in: namespace)
                        }
                    }
                    .onTapGesture {
                        withAnimation { selected = menu }
                    }
            }
        }
    }
}

비교 영상

왼쪽이 matchedGeometryEffect를 사용하지 않은거고
오른쪽이 사용한 것이다.

예제 2 : 썸네일 확대

struct HeroAnimation: View {
    @Namespace private var namespace // 1. 공간을 만들고
    @State private var isFull = false

    var body: some View {
        VStack {
            if !isFull {
                // 작은 썸네일
                RoundedRectangle(cornerRadius: 20)
                    // 2. 출발지와 도착지에 같은 이름표를 붙인다
                    .matchedGeometryEffect(id: "card", in: namespace)
                    .frame(width: 100, height: 100)
            } else {
                // 전체 화면
                RoundedRectangle(cornerRadius: 0)
                    // 2. 출발지와 도착지에 같은 이름표를 붙인다
                    .matchedGeometryEffect(id: "card", in: namespace)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
            }
        }
        .onTapGesture {
            withAnimation { isFull.toggle() }
        }
    }
}

비교 영상

왼쪽이 matchedGeometryEffect를 사용하지 않은거고
오른쪽이 사용한 것이다.

마무리

matchedGeometryEffect 를 이용한다면 뷰가 전환될 때 사용자에게 '연속적인' 경험을 아주 간편하게 줄 수 있다는 점을 깨달았다.

이런 경험 하나 하나가 축적된다면 사용자에게 편안함을 줄 수 있지 않을까 생각한다.

그리고 이름에는 'Geometry'가 붙고 이 뜻은 '기하학'이며, 공식 홈페이지에는 '보간'이라는 수학적 개념이 나오면서 왜 이런식으로 네이밍을 했는지 원리와 엮어서 보다보니 퍼즐 맞추는 것처럼 이해가 되었다. 앞으로도 이런 네이밍과 개념, 원리 등을 엮어서 볼 수 있는 시선을 가진다면 학습 능률이 올라갈 수 있겠다는 생각이 들었다.

🍎 참고

Apple 공식문서 - matchedGeometryEffect()

0개의 댓글