반응형

SwiftUI Study – withAnimation과 Transaction을 안전하게 사용하는 법 (의도치 않은 애니메이션·상태 충돌 방지)

 

1. 왜 중요한가 (문제 배경)

SwiftUI는 상태 변화가 일어나면 자동으로 애니메이션을 적용할 수 있는 매우 강력한 구조를 갖고 있다.

하지만 다음과 같은 상황에서 예기치 않은 문제가 자주 발생한다.

  • 화면 전체가 원하지 않게 애니메이션됨
  • 특정 상태만 애니메이션하려 했는데 다른 뷰까지 함께 애니메이션
  • 빠르게 연속 상태 변경 시 애니메이션이 꼬임
  • withAnimation이 중첩되며 트랜잭션 충돌 발생
  • transition(삽입/삭제)과 animation이 상호간섭

문제의 핵심은 다음이다.

애니메이션 범위와 Transaction 컨텍스트를 명확히 이해하지 않으면

작은 UI 변경에도 전체 화면이 흔들리는 부작용이 생긴다.

 

2. 잘못된 패턴 예시

❌ 예시 1: withAnimation을 너무 넓은 범위에 적용

withAnimation {
    isExpanded.toggle()   // ❌ 이 변경과 관련된 모든 뷰가 애니메이션됨
    count += 1            // ❌ 불필요한 숫자 변화까지 애니메이션됨
}

문제점

- 예상치 못한 뷰까지 애니메이션이 전파됨

- 디버깅 어려움

- 특히 리스트나 조건부 렌더링에서 흔히 붕괴 발생

❌ 예시 2: transition과 animation의 충돌

if isVisible {
    Text("Hello")
        .transition(.opacity)  // ❌
}
.animation(.easeInOut, value: isVisible)  // 충돌 가능

문제점

- 삽입/삭제 애니메이션과 상태 기반 애니메이션이 싸워서

텍스트가 깜빡이거나 두 번 애니메이션됨

❌ 예시 3: 빠른 상태 변경으로 애니메이션 큐가 꼬임

Button("증가") {
    withAnimation {
        value += 1     // ❌ 빠른 클릭 시 애니메이션이 누적되어 꼬임
    }
}

문제점

- 여러 애니메이션이 겹침

- UI 반응성 저하

- 값 변화와 애니메이션 타이밍 불일치

❌ 예시 4: 뷰 위치 변경이 의도치 않게 animation 디폴트 영향

.animation(.default, value: offset)  // ❌ 모든 offset 변경에 애니메이션 적용

문제점

- 스크롤 기반 offset 변경 시 화면이 흔들림

- 레이아웃 변경까지 애니메이션되며 UX 붕괴

 

3. 올바른 패턴 예시

✅ 예시 1: 애니메이션이 필요한 상태만 정확히 지정

withAnimation(.spring()) {
    isExpanded.toggle()   // 이 상태 변경만 애니메이션됨
}

장점

- 다른 상태 변화는 애니메이션되지 않음

- 부작용 최소화

- 애니메이션 범위 명확

✅ 예시 2: transition은 insertion/removal에만 사용

if isVisible {
    Text("Hello")
        .transition(.move(edge: .bottom))
}

.animation(.easeInOut, value: isVisible)

장점

- 삽입/삭제 모션과 상태 변화 애니메이션이 명확히 분리

- 깜빡임, 중복 애니메이션 방지

✅ 예시 3: Transaction으로 애니메이션 비활성화

.transaction { t in
    t.animation = nil   // 특정 뷰는 애니메이션 제외
}

사용 예:

Text("즉시 변경")
    .transaction { t in t.animation = nil }
    .onChange(of: value) { _ in immediateChange() }

장점

- 특정 구간만 animation을 끌 수 있음

- 리스트 내 부분적 애니메이션 비활성화 가능

✅ 예시 4: 애니메이션 큐가 꼬일 때는 interpolatingSpring 또는 interactiveSpring

withAnimation(.interpolatingSpring(stiffness: 180, damping: 18)) {
    value += 1
}

장점

- 자연스럽고 강건한 모션

- 반복 클릭 상황에서도 안정적

✅ 예시 5: 상태 기반 애니메이션은 value를 명확히 지정

.rotationEffect(.degrees(isRotated ? 180 : 0))
.animation(.easeInOut, value: isRotated)

장점

- 의도한 상태 변화에만 애니메이션 적용

- 불필요한 다른 상태 변경은 애니메이션 없음

 

4. 실전 적용 팁

✔ 팁 1 – withAnimation은 “하나의 의도된 상태 변화”만 감싸기

여러 상태를 한 번에 감싸면 부작용 발생.

✔ 팁 2 – transition은 삽입/삭제 + animation은 상태 변화로 역할 분리

같이 섞으면 애니메이션 충돌이 매우 흔함.

✔ 팁 3 – Transaction으로 특정 뷰는 애니메이션 제외 가능

리스트 스크롤 시 셀 흔들림 방지에 유용.

✔ 팁 4 – 빠른 연속 애니메이션에는 spring 계열이 안정적

대량의 상태 업데이트에도 자연스럽게 반응.

✔ 팁 5 – 애니메이션 디버깅은 .animationModifier 로그로 검증

어떤 상태가 애니메이션을 트리거했는지 쉽게 추적 가능.

 

5. 정리

  • SwiftUI 애니메이션은 상태 기반이므로, 애니메이션 범위를 명확히 지정하지 않으면 원치 않는 뷰까지 영향을 준다.
  • transition과 animation은 각각의 역할을 분리하여 사용해야 충돌이 없다.
  • Transaction을 활용하면 애니메이션을 부분적으로 끌 수 있어 복잡한 화면도 안정적으로 구현 가능하다.
  • 실전 환경에서는 spring 기반 애니메이션이 가장 강건하고 예측 가능하다.
반응형
Posted by 까칠코더
,