본문 바로가기
2023년 이전/iOS

SwiftUI- - tutorial - Animating Views and Transitions

by JeongUPark 2020. 8. 27.
반응형

Apple에서 제공하는 Swift UI를 공부하면서 정리한 내용 입니다.

 

우선 기본 베이스 코드는 여기에서 project file을 다운받으신 후 StratingPoint/Landmarks/AnimatingViewsAndTransitions.xcodeproj 로 실행하시면 프로젝트를 따라 가실 수 있습니다. (빨리 정리해야지 해야지 하면서 1달만에 정리하는 기분입니다. 더 부지런해져야 겠습니다.)

Add Animations to Individual Views

우선 HikeView.swift의  Image(systemName: "chevron.right.circle") 에 .animation(.easeInOut)을 추가 합니다.

 

Button(action: {
    self.showDetail.toggle()
}) {
    Image(systemName: "chevron.right.circle")
        .imageScale(.large)
        .rotationEffect(.degrees(showDetail ? 90 : 0))
        .scaleEffect(showDetail ? 1.5 : 1)
        .padding()
        .animation(.easeInOut)
}

이렇게 적용하면 오른쪽 preview의 오른쪽 상단의 이미지를 클릭 하면 

애니매이션이 적용되어 이미지의 화살표가 90도 돌아갑니다. ( .animation이 없을 경우에는 바로 화살표가 바로 아래를 가리키게 됩니다.)

 

위의 .animation에서 사용한 easeInOut은 animation의  curve 옵션으로 linear, easeIn, easeOut, easeInOut 가 존재합니다. 

Linear는 일정한 속도로 / easeIn 은 점점 빠르게 / easeOut 은 점점 느리게 / easeInOut 점점빨라졌다 끝에가서 다시 느려지는 옵션입니다.

그리고 .scaleEffect(showDetail ? 1.5 : 1)은 애니메이션 종료에서 이미지의 크기를 1.5배 해줍니다.

 

.easeInOut을 .spring()으로 변경할 수 있는데, 그럴 경우 애니메이션의 속도가 순간적으로 올라갔다가 스프링처럼 점차 오르락 내리락 하게 됩니다. (이 예제에서는 spring을 눈으로 확인하기는 힘들어보입니다., 각 애니메이션 옵션은 여기서 시각적으로 확인 하실 수 있습니다. )

 

애니메이션을 끄는 방법은  .animation(nil) 을 추가해주면 됩니다. 튜토리얼에서는 scaleEffect 전에 추가하라고 하는데  .rotationEffect와 animation 사이 어디든 추가하면 애니메이션이 동작하지 않습니다. (그리고 당연히 .animation을 지우면 애니메이션은 동작하지 않습니다.)

Animate the Effects of State Changes

Button을 다음과 같이 수정합니다.

Button(action: {
    withAnimation{
    self.showDetail.toggle()
    }
}) {
    Image(systemName: "chevron.right.circle")
        .imageScale(.large)
        .rotationEffect(.degrees(showDetail ? 90 : 0))
        .scaleEffect(showDetail ? 1.5 : 1)
        .padding()
}

그러면 화살표를 누를 경우 아래 그래프가 페이드인 형태로 나타납니다.

 

그리고 withAnimation을 다음과 같이 수정하면

withAnimation(.easeInOut(duration: 4))

 

화살표 버튼이 90도 움직이고 그래프가 페이드인 해서 완전히 나타나는데 4초가 걸립니다.

 

Customize View Transitions

HikeDetail에 다음과 같이 .transition을 추가합니다.

if showDetail {
    HikeDetail(hike: hike).transition(.slide)
}

그 후 화살표 버튼을 누르면 아래 그래프사 왼쪽에서 오른쪽으로 나타납니다.

그리고 extension을 활용하여 Transition을 custom 할 수 있습니다. HikeView.swift에서 import 아래 다음을 추가하고

extension AnyTransition {
    static var moveAndFade: AnyTransition {
      AnyTransition.slide
    }
}

아까 사용한.transition을 

HikeDetail(hike: hike).transition(.moveAndFade)

이렇게 변경해도, 똑같이 동작합니다.

그리고 다음처럼 AnyTransition을 변경하면

extension AnyTransition {
    static var moveAndFade: AnyTransition {
        let insertion = AnyTransition.move(edge: .trailing)
            .combined(with: .opacity)
        let removal = AnyTransition.scale
            .combined(with: .opacity)
        return .asymmetric(insertion: insertion, removal: removal)
    }
}

뷰가 등장할때 오른쪽에서 왼쪽으로, 그리고 사라질 때 멀어지면서 사라집니다.

그 이유는 .asymmetric의 원형을 보면 

 /// A composite `Transition` that uses a different transition for
    /// insertion versus removal.
    public static func asymmetric(insertion: AnyTransition, removal: AnyTransition) -> AnyTransition

위의 설명을 해석해보면 다른 전환을 사용하게  합성전환자 인데, 삽입할떄와 제거할때(즉, 나타날때와 사라질떄)에 전환 용도로 사용됩니다.

 

Compose Animations for Complex Effects

마지막으로 그래프 아래 값들을 눌러 그래프를 변경할때 애니메이션을 만들어 보겠습니다.

 

HikeGraph.swift 파일에 다음의 코드를 추가합니다.

extension Animation {
    static func ripple(index: Int) -> Animation {
        Animation.spring(dampingFraction: 0.5)
            .speed(2)
            .delay(0.03 * Double(index))
    }
}

애니매이션에 대한 설정입니다. 스프링형태로 노멀 스피드의 2배로, 그리고 시작 딜레이는 0.03 * Double(index) 로 계산합니다.

 

그리고 나서  HikeGraph:View의 var body 의 return에 있는 GraphCaplsule의 colorMultiply에 다음과 같이 추가 합니다.

GraphCapsule(
	index: index,
	height: proxy.size.height,
	range: data[index][keyPath: self.path],
	overallRange: overallRange)
	.colorMultiply(self.color)
	.transition(.slide)
	.animation(.ripple(index: index))

.transition(.slide) .animation(.ripple(index: index)) 을 추가 함으로서 등장과 변경에 따른 애니매이션이 동작합니다.

 

그런데 이게 여기만 수정하고 동작 시키면 안에 그래프가 위아래로 조금씩 움직이기만 할 뿐 그래프가 변경되지 않습니다. 그 이유는 ForEach에 index 값이 변경되지 않기 때문입니다. 그래서  ForEach를 다음과 같이 수정합니다.

 ForEach(data.indices, id: \.self) { index in

그리고 나서 동작을 수행하면 정상적으로 그래프들이 변경되는 것을 알 수 있습니다.

 

완료 영상은 다음과 같습니다.

 

 

 

반응형