SwiftUI : @Namespace / matchedGeometryEffect() 애니메이션 동기화
.matchedGeometryEffect
에 대해 알아보도록 합시다.
MatchedGeometryEffect
만약 view
계층의 서로 다른 부분에 동일한 view
가 나타나는 등 두 view
사이에 애니메이션 효과를 주려면 .matchedGeometryEffec()
수정자를 사용하는 것이 좋습니다.
이 수정자를 사용하려면 동일한 view
모두에 이 수정자를 연결해줘야 합니다. 이렇게 하면 두 개의 view
상태를 전환 할 때 SwiftUI
가 동기화된 view
에 원활하게 애니메이션을 적용시킬 수 있습니다.
우선 간단하게 가장 쉬운 animation
효과만 적용시켜서 예를 들어보겠습니다.
import SwiftUI
struct ContentView: View {
@State private var isFlipped = false
var body: some View {
HStack {
if isFlipped {
Text("서근 개발노트")
.font(.subheadline)
Image(systemName: "arrow.right")
.font(.title2)
} else {
Image(systemName: "arrow.left")
.font(.title2)
Text("서근 개발노트")
.font(.subheadline)
}
}
.onTapGesture {
withAnimation {
isFlipped.toggle()
}
}
}
}
위 코드를 프리뷰에서 실행해면 뷰를 그냥 페이드인 / 아웃하여 전환하는 것을 확인할 수 있습니다.
하지만 뭔가 부자연 스럽고 뒤에 텍스트가 겹쳐서 보기 싫죠? 이것을 해결해 주는 것이 바로 .matchedGeometryEffec()
수정자 인거죠 :) 이 수정자는 @Namespace
속성 래퍼를 사용하여 뷰에 대한 Name space를 만들어야 합니다.
따라서 다음과 같은 속성을 추가 할 수 있습니다. @Namespace private var animation
그리고 원하는 뷰 아래에 .matchedGeometryEffect(id: "
사용자의 Indentifier를 적어줌", in: animation)
으로 동기화된 효과로 애니메이션을 적용하려는 모든뷰에 적용시켜줘야 합니다. 수정자의 id
부분은 고유 번호로 대체되어야 합니다.
이렇게 말이죠
// 텍스트에 적용
.matchedGeometryEffect(id: "title", in: animation)
// Image에 적용
.matchedGeometryEffect(id: "icon", in: animation)
자 이제 위에 내용을 토대로 아래와 같이 코드를 재 설정 해줄 수 있습니다.
import SwiftUI
struct ContentView: View {
@State private var isFlipped = false
@Namespace private var animation
var body: some View {
HStack {
if isFlipped {
Text("서근 개발노트")
.font(.subheadline)
.matchedGeometryEffect(id: "title", in: animation)
Image(systemName: "arrow.right")
.font(.title2)
.matchedGeometryEffect(id: "icon", in: animation)
} else {
Image(systemName: "arrow.left")
.font(.title2)
.matchedGeometryEffect(id: "icon", in: animation)
Text("서근 개발노트")
.font(.subheadline)
.matchedGeometryEffect(id: "title", in: animation)
}
}
.onTapGesture {
withAnimation {
isFlipped.toggle()
}
}
}
}
이제 두 코드를 아래 결과로 비교해보도록 하겠습니다.
.matchedGeometryEffect 미적용
.matchedGeometryEffect 적용
응용 방법
음악 앱에서 하단에 있는 앨범을 터치하면 작은 모양인 앨범이 화면에 꽉 차도록 확장되죠? 그런 식으로 애니메이션 효과를 적용할 수 있습니다.
import SwiftUI
struct ContentView: View {
@State private var isZoomed = false
@Namespace private var animation
var frame: CGFloat {
isZoomed ? .infinity : 60
}
var body: some View {
VStack {
Spacer()
VStack {
HStack {
Image("Rollin")
.resizable()
.scaledToFill()
//isZoomed이 true이면 프레임 infinity fales이면 60
.frame(width: frame, height: frame)
//isZoomed이 true이면 padding 10 아니면 0
.padding([.top,.bottom], isZoomed ? 10 : 0)
if isZoomed == false {
Text("브레이브 걸스 - Rollin'")
.matchedGeometryEffect(id: "title", in: animation)
.font(.headline)
Spacer()
}
}
if isZoomed != false {
Text("브레이브 걸스 - Rollin'")
.matchedGeometryEffect(id: "title", in: animation)
.font(.headline)
.padding([.vertical, .horizontal], 10)
Spacer()
}
}
.onTapGesture {
withAnimation(.spring()) {
isZoomed.toggle()
}
}
.frame(maxWidth: .infinity)
.frame(height: .infinity)
.background(Color(white: 0.98))
.foregroundColor(.primary)
}
}
}
읽어주셔서 감사합니다🤟
본 게시글의 전체코드 GitHub 👇🏻