Lecture 7: ViewModifier Animation

강의 링크

# Animation 골든룰!

  • 애니메이션은 변화가 발생했을 때만 일어난다!
  1. View 가 이미 UI 에 들어가 있는 상태에서 ViewModifier 의 인자 가 바뀌었을 때
  1. Shape 이 바뀌었을 때
  1. UI 내부의 View 가 생기거나 사라질 때
    • UI 상의 View container 에 추가되는 경우
    • UI 상의 View container 에서 삭제되는 경우
    • if-else, ForEach 문

# 애니메이션을 구현하는 3가지 방법!

  1. .animation(Animation) View Modifier 를 이용한 implicit 한 방식
    • duration, delay, repear, curve 등을 직접 지정할 수 있음
    • .animation 앞의 모든 ViewModifier 들에 대해 애니메이션이 적용된다
    • container 에 적용 시 내부의 모든 View 에 분배되므로, 가장 최하단의 자식 View 혹은 독립적으로 작동하는 View 에 주로 적용
Text("👻")
    .opacity(scary? 1 : 0)  // (O) animated
    .animation(Animation.easeInOut(duration: 1)  // easeInOut -> curve!
    .rotationEffect(Angle.degrees(upsideDown ? 180 : 0))  // (X) animated

  1. withAnimation(Animation) { } 함수를 이용한 explicit 한 방식
    • 여러 개의 변화가 발생했을 때 전부에 대해 애니메이션 효과를 추가
    • explicit 애니메이션은 implicit 애니메이션을 오버라이딩 하지 않음!
withAnimation(.linear(duration: 2)) {
    // do something that will cause ViewModifier/Shape arguments to change somewhere 
}
  1. UI 상의 ContainerView 를 새로 추가/삭제하는 transition
    • 작동 원리는 한 쌍의 Viewmodifier 가 변화하는 것
    • 즉, 변화 이전 modifier 과 변화 이후 modifier 한 쌍의 인자가 변화하는 것을 나타낸 게 애니메이션!
    • ViewBuilder 내부의 ForEach 혹은 if-else 문을 통해 가능
    • implicit 방식과 달리 container 에 적용 시 분산되지 않고 해당 container 자체에 적용됨

# 애니메이션 작동 원리

  1. 애니메이션 시스템이 애니메이션의 시작부터 끝까지의 과정을 작은 조각으로 분할한다
  1. shape 혹은 ViewModifier 가 시스템에게 자신 내부의 어떤 컨텐츠가 애니메이션화되어야 하는 지 알려준다
  1. 애니메이션 시스템이 애니메이션 구현을 위해 shape 혹은 ViewModifier 를 다시 호출해 단계에 따라 적절한 명령을 내린다.

# var animatableData

  • 이러한 shape/ViewModifierAnimatableModifieranimatableData 변수를 통해 animation 시스템 과 상호작용!
  • 위에서 보다시피 양방향으로 상호작용하기 때문에 animatableData 는 읽기와 쓰기가 모두 가능
    • get : 애니메이션 시스템이 애니메이션의 시작점/끝점을 호출
    • set : shape/ViewModifier 에게 애니메이션 시스템이 그려야할 조각을 알려주는 것

# Cardify

  • content 를 받아서 카드화한 View 를 반환하는 커스텀 ViewModifier 만들기
    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Pie(startAngle: Angle(degrees: 0 - 90), endAngle: Angle(degrees: 110 - 90))
                    .padding(DrawingConstants.circlePadding)
                    .opacity(0.4)
                Text(card.content)
                    .font(Font.system(size: 32))
            }  // 카드화 할 content 를 담고 있는 View
            .cardify(isFaceUp: card.isFaceUp)
        }
    }
  • 카드를 만드는 데 필요한 정보만 받아와서 구현!
struct Cardify: ViewModifier {
    var isFaceUp: Bool
    
    func body(content: Content) -> some View {
        ZStack {
            let shape = RoundedRectangle(cornerRadius: DrawingConstants.cornerRadius)
            if isFaceUp {
                shape.fill().foregroundColor(.white)
                shape.strokeBorder(lineWidth: DrawingConstants.lineWidth)
                content
            } else {
                shape.fill()
            }
        }
    }
    
    private struct DrawingConstants {
        static let cornerRadius: CGFloat = 10
        static let lineWidth: CGFloat = 2.5
    }
}

// 호출을 쉽게 하기 위한 extension! syntax sugar:)
extension View {
    func cardify(isFaceUp: Bool) -> some View {
        self.modifier(Cardify(isFaceUp: isFaceUp))
    }
}

# .font() 는 애니메이션 적용이 불가한 ViewModifier


# 왜 짝이 맞는데 돌질 못하니...

  • 두 번째 카드의 경우 회전해야하는 이모지 즉, content 가 카드를 뒤집음과 동시에 UI 상에 처음 나타나게 되므로 이미 isMatched 인 상태에서 처음 등장해 애니메이션 효과를 줄 변화가 없다...! 따라서 골든룰에 위배되어 애니메이션 효과가 발동하지 않는다!
  • 따라서 다음과 같이 content 는 항상 존재하게 하되 opacity 를 조절하는 방식으로 바꾸어주었더니 해결되었다!
struct Cardify: ViewModifier {
    var isFaceUp: Bool
    
    func body(content: Content) -> some View {
        ZStack {
            ...
            if isFaceUp {
                shape.fill().foregroundColor(.white)
                shape.strokeBorder(lineWidth: DrawingConstants.lineWidth)
            } else {
                shape.fill()
            }
            content.opacity(isFaceUp ? 1 : 0)  // 수정 부분!
        }
    }
    ...
}

☀️ 느낀점

  • 애니메이션 효과가 알쏭달쏭하게 느껴진다...개념 파트만 다시 들어봐야할 것 같다.






# Animation

  • only changes can be animated
  1. ViewModifier arguments
  2. Shapes
  3. The existence (or not) of a View in the UI
  • A change to a ViewModifier's arguments ahs to happen after the View is initially put in the UI
    == only changes in a ViewModifier's arguments since it jointed the UI are animates

  • A View coming on-screen is only animated if it's joining a container that is already in the UI

  • A View going off-screen is only animated if it's leavinf a container that is staying in the UI

  • ForEach and if-else in ViewBuilders are common ways to make Views come and go

how do you make an animation go?

  • implicitly: by using view modifier .animation(Animation)
  • explicitly: by wrapping withAnimation(Animation) {} around code that might change things
  • by making View be included or excluded from the UI

Implicit Animation

  • mark a view so that all the ViewModifier argumemnts that precede the animation modifier will always be animated!
Text("👻")
    .opacity(scary? 1 : 0)  // (O) animated
    .animation(Animation.easeInOut)
    .rotationEffect(Angle.degrees(upsideDown ? 180 : 0))  // (X) animated

Explicit Animation

  • Explicit animations create an animation transaction during which all eligible changes made as a result of executing a block of code will be animated together
withAnimation(.linear(duration: 2)) {
    // do something that will cause ViewModifier/Shape arguments to change somewhere 
}
  • explicit animation does not implicit animations!

Transitions

  • transform specify how to animate the arrival/departure of Views

  • Only works for Views that are inside CTAAOS(Containers That ARe Already On-Screen

  • Under the covers, a transition is nothing more than a pair of ViewModifiers. One of the modifiers is the "before" modification of teh View that's on the move. The other modifier is the "after" modification od the View that's on the move. Thus, a transition is just a version of a "changes in arguments to ViewModifiers" animation.

  • An asymmetric transition has 2 pairs of ViewModifiers

Animation

  • the animation system takes the duration the animation is happening over and they're dividing it up into little pieces depending on what the curve is...chop it up into little pieces
  • then a shape or the view modifier let's the system know what information inside it needs to be animated...it has to let the animate system know..
  • then during animation, the animation system just calls them back and says plz draq urself with this start angle, and now with this start angle and on and on, moving the angle and asking to redraw itself repeatedly: regenerate ur body content with this aniamted value of ur piece of information

how do shape and viewmodifiers communicate with the animation system? Through this one var animatableData!

  • var animatableData: Type
  • must implement the protocol VectorArithemetic : b/c whataever this animatable data is, it's the thing that the animation system is going to chop up into little pieces and hand back to the shape or the ViewModifier thru the whole animation process

Shape and ViewModifier Animation

  • because it's communicationg both ways, this animatableData is a read-write var.

  • the setting of this var is the animation system telling the Shape/VM which 'piece' to draw : draw this draw that..

  • the getting of this var is the animation system getting the start/end points of an animation : when u set an argument it's gonna change that animatable data probably...

  • it's usally computed var..!

Reasons why font needs to be fixed

좋은 웹페이지 즐겨찾기