SWIFTUI/Grammar

SwiftUI : @Namespace / matchedGeometryEffect() 애니메이션 동기화

서근 2021. 4. 30. 16:17
반응형

.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 👇🏻

 

Seogun95/SwiftUI_matchedGeometryEffect

SwiftUI matchedGeometryEffect. Contribute to Seogun95/SwiftUI_matchedGeometryEffect development by creating an account on GitHub.

github.com