Crypto App을 만들어보며 2

Dophi·2022년 12월 31일
0

코드 따라하기

목록 보기
2/5

소개글

이전 글 에 이어서 감탄할만한 요소를 공유드리고자 합니다.
해당 영상은 아래 링크를 통해 볼 수 있으며, SwiftUI에 관심이 있다면 한번쯤 보시는 것을 추천드립니다!

영상 링크
코드

애니메이션

영상에서 정말 애니메이션 효과를 잘 다루는 것 같았습니다.
단순히 애니메이션 관련 함수를 많이 안다기보다도, 우리가 잘 알고있는 동작들을 조합해서 저런 연출을 만들어낼 수 있구나! 라는 생각이 들었습니다.

슬라이드

용어를 뭐라 해야할지 몰라서 일단 슬라이드라고 했습니다.

위 영상에서 검색바 바로 윗부분을 보면
(Market Cap, 24th Volume, BTC Dominance) 요소들이
(24th Volume, BTC Dominance, Portfolio Value) 로
슬라이드 되면서 변하는 모습을 볼 수 있습니다.

보기에는 정말 굉장해 보이는 애니메이션이지만, 사실 코드는 정말 간단합니다.

// HomeStatsView.swift
// 각각의 요소 화면에 띄우기
HStack {
    ForEach(vm.statistics) { stat in
       StatisticView(stat: stat)
          .frame(width: UIScreen.main.bounds.width / 3)
    }
}
.frame(width: UIScreen.main.bounds.width, 
	   alignment: showPortfolio ? .trailing : .leading)
       

// HomeView.swift
// 버튼 눌렀을 때 showPortfolio값 바꿔주기
CircleButtonView(iconName: "chevron.right")
    .rotationEffect(Angle(degrees: showPortfolio ? 180 : 0))
    .onTapGesture {
         withAnimation(.spring()) {
             showPortfolio.toggle()
         }
     }

애니메이션의 비밀은 바로 alignment만 바꿔주는 것입니다.
이해를 위해 세가지 케이스를 봐보겠습니다.

  1. alignment: .center

  1. alignment: .leading

  1. alignment: .trailing

삼항연산자를 이용해서 alignment만 바꿔주면 슬라이드같이 보이는 효과가 나타나게 됩니다.

파동

위와 같은 영상이지만, 이번에는 플러스 버튼에 파동처럼 원이 퍼지는 효과에 대해 설명드리겠습니다.
이 코드 역시 매우 간단합니다.

// HomeView.swift
.background(
    CircleButtonAnimationView(animate: $showPortfolio)
)

// CircleButtonAnimationView.swift
struct CircleButtonAnimationView: View {
    @Binding var animate: Bool
  
    var body: some View {
        Circle()
            .stroke(lineWidth: 5.0)
            .scale(animate ? 1.0 : 0.0)
            .opacity(animate ? 0.0 : 1.0)
            .animation(animate ? .easeOut(duration: 1.0) : .none)
    }
}

효과를 넣고 싶은 컴포넌트의 background에 위와 같은 코드를 넣기만 하면 됩니다.
단순히 원의 크기나 투명도같은 속성만 건드려준건데, 파동같은 애니메이션을 만들어낼 수 있다는 점이 신기했습니다.

그래프

가장 신기했던 애니메이션 중 하나입니다.
상세 화면에 들어가게 되면 그래프가 왼쪽에서 오른쪽으로 그려지는 듯한 모습을 볼 수 있습니다.

우선 해당 뷰의 전체 코드를 보여드리겠습니다.

// ChartView.swift
private var chartView: some View {
    GeometryReader { geometry in
        Path { path in
            for index in data.indices {
                let xPosition = geometry.size.width / CGFloat(data.count) * CGFloat(index + 1)
                    
                let yAxis = maxY - minY
                let yPosition = (1 - CGFloat((data[index] - minY) / yAxis)) * geometry.size.height
                    
                if index == 0 {
                    path.move(to: CGPoint(x: xPosition, y: yPosition))
                }
                path.addLine(to: CGPoint(x: xPosition, y: yPosition))
            }
        }
        .trim(from: 0, to: percentage)
        .stroke(lineColor, style: StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round))
        .shadow(color: lineColor, radius: 10, x: 0.0, y: 10)
        .shadow(color: lineColor.opacity(0.5), radius: 10, x: 0.0, y: 20)
        .shadow(color: lineColor.opacity(0.2), radius: 10, x: 0.0, y: 30)
        .shadow(color: lineColor.opacity(0.1), radius: 10, x: 0.0, y: 40)
    }
}

복잡해보이긴 하지만 대부분 그래프 설정에 관한 코드이고 애니메이션 관련 코드는 .trim(from: 0, to: percentage) 뿐입니다.

아래와 같이 percentage값만 0에서 100으로 몇초간 걸쳐서 바뀌게 한다면 이런 애니메이션이 가능합니다.

// ChartView.swift
@State private var percentage: CGFloat = 0

var body: some View {
  ...
  .onAppear {
      DispatchQueue.main.asyncAfter(deadline: .now()+0.2) {
          withAnimation(.linear(duration: 2.0)) {
              percentage = 1.0
          }
      }
  }
}

로딩 화면

마지막으로 로딩 화면입니다.
글자를 자세히 보면 왼쪽에서 오른쪽으로 가면서 글자가 하나씩 튀어오르는 듯한 애니메이션을 볼 수 있습니다.
이 애니메이션도 몇가지 간단한 로직만 추가하면 어렵지 않게 구현 가능합니다.

우선은 원하는 문자열을 문자 한개씩 쪼개야합니다.

// LaunchView.swift
@State private var loadingText: [String] = 
	"Loading your portfolio...".map {String($0)}

이후 각 단어의 y위치, 즉 offset만 살짝씩 변화시켜주면 됩니다.

// LaunchView.swift
HStack(spacing: 0) {
    ForEach(loadingText.indices) { index in
        Text(loadingText[index])
            .offset(y: counter == index ? -5 : 0)
    }
}

여기서 중요한 점은 counter 변수가 0에서 문자열의 길이만큼까지 천천히 증가해야합니다.
그러기 위해서 Combine의 타이머를 사용해서 counter값을 0.1초마다 증가시킬 수 있습니다.

// LaunchView.swift
 private let timer = Timer
 						.publish(every: 0.1, on: .main, in: .common)
                        .autoconnect()
 
...
.onReceive(timer) { _ in
	withAnimation(.spring()) {
		let lastIndex = loadingText.count
		if counter == lastIndex {
			counter = 0
			loops += 1
			if loops >= 2 {
				showLaunchView = false
			}
		} else {
			counter += 1
		}
	}
}

그 외에도 위 코드의 로직을 간단히 말하자면,

  • counter값을 0.1초마다 증가시킨다
  • 반복시키기위해 counter값이 문자열의 길이와 같아지면 0으로 초기화한다
  • 2번 반복하고 나면 로딩화면을 끝낸다

입니다.

이번에 설명한 네가지 애니메이션 말고도 영상에서는 다양한 애니메이션을 구현하십니다.
클론 코딩을 해보면 배울 점이 많으니, 직접 영상을 보면서 구현해보는 것을 추천드립니다!

profile
개발을 하며 경험한 것들을 이것저것 작성해보고 있습니다!

0개의 댓글