[SwiftUI] MatchedGeometryEffect

Junyoung Park·2022년 8월 20일
0

SwiftUI

목록 보기
32/136
post-thumbnail
post-custom-banner

How to use MatchedGeometryEffect in SwiftUI | Advanced Learning #4

MatchedGeometryEffect

구현 목표


  • 스크린 상의 서로 다른 도형(Shape)을 매칭시키는 효과
  • id, namespace가 동일한 서로 다른 두 도형을 같다고 인식하게 만듦
  • 애니메이션 구현에 매우 적합

구현 태스크

  1. 서로 다른 두 개의 도형을 표시, offset을 주지 않고 이동 효과를 주기
  2. 클릭한 텍스트 밑의 보더 라인 이동 이펙트

핵심 코드

Circle().matchedGeometryEffect(id: "rectangle", in: namespace)
...
RoundedRectangle(cornerRadius: 25).matchedGeometryEffect(id: "rectangle", in: namespace)
...
if selected == category {
                        RoundedRectangle(cornerRadius: 10)
                            .fill(Color.red.opacity(0.5))
                            .matchedGeometryEffect(id: "category_background", in: namespace2)
                            .frame(width: 35, height: 2)
                            .offset(y: 10)
                    }

소스 코드

import SwiftUI

struct MatchedGeometryEffect: View {
    @State private var isClicked: Bool = false
    @Namespace private var namespace
    var body: some View {
        VStack {
            if !isClicked {
                Circle()
                    .matchedGeometryEffect(id: "rectangle", in: namespace)
                    .frame(width: 100, height: 100)

            }
            Spacer()
            if isClicked {
                RoundedRectangle(cornerRadius: 25)
                    .matchedGeometryEffect(id: "rectangle", in: namespace)
                    .frame(width: 300, height: 200)
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.red)
        .onTapGesture {
            withAnimation(.easeInOut) {
                isClicked.toggle()
            }
        }
    }
}
  • CircleRoundedRectangle이 서로 다른 위치에 구현되어 있고 isClicked라는 @State 불리언 변수를 통해 어떤 이미지를 뷰에 보여줄지 결정한다.
  • matchedGeometryEffect 모디파이어를 통해 '같은' 네임 스페이스를 가지고 있기 때문에 서로 같은 shape로 컴파일러가 인식, 이동 가능
struct MatchedGeometryEffectExample2: View {
    let categories: [String] = ["Home", "Popular", "Saved"]
    @State private var selected: String = ""
    @Namespace private var namespace2
    var body: some View {
        HStack {
            ForEach(categories, id: \.self) { category in
                ZStack(alignment: .bottom) {
                    if selected == category {
                        RoundedRectangle(cornerRadius: 10)
                            .fill(Color.red.opacity(0.5))
                            .matchedGeometryEffect(id: "category_background", in: namespace2)
                            .frame(width: 35, height: 2)
                            .offset(y: 10)
                    }
                    Text(category)
                        .foregroundColor(selected == category ? .red : .black)
                }
                .frame(maxWidth: .infinity)
                .frame(height: 55)
                .onTapGesture {
                    withAnimation(.spring()) {
                        selected = category
                    }
                }
            }
        }
        .padding()
    }
}
  • selected를 통해 어떤 텍스트를 골랐는지 고른 뒤 matchedGeometryEffect를 통해 어떤 텍스트 밑의 보더 라인을 보여줄지 결정 가능
  • 선택 텍스트가 바뀔 때 해당 텍스트의 위치대로 보더 라인 도형이 이동

구현 화면


profile
JUST DO IT
post-custom-banner

0개의 댓글