[내일배움캠프 12주차 (03/24)]

yeseul jang·2026년 3월 24일

내일배움캠프

목록 보기
26/32
알람 울리는중
real

📌 문제: 화면 전환시 애니메이션이 멈춤

  • 처음에 UIView.animate를 사용해 반복 애니메이션을 적용했더니 앱이 실행 중일 때는 정상적으로 동작하는것 처럼 보였다.
  • 하지만 홈으로 나가 앱이 백그라운드 상태로 전환되었다가 다시 돌아오니 애니메이션이 멈췄다.
  • 이 문제가 발생한 이유는 애니메이션을 init에서 한 번만 시작했기 때문일거라 추정함
  • 왜냐하면 아래와 같이 init에서 startAnimations()를 호출하고 뷰가 처음 생성될 때만 애니메이션이 시작되기 때문이다.
init(time: String, date: String, title: String) {
    self.time = time
    self.date = date
    self.title = title
    super.init(frame: .zero)
    
    configureUI()
    setupLayout()
    bindData()
    startAnimations()
}
  • 앱이 백그라운드로 갔다가 다시 활성화될 때는 init이 다시 호출되지 않아서 애니메이션이 중단되더라도 다시 시작되지 않는다!

  • 그래서 해결방안으로 해당화면이 띄워졌을 때 다시 애니메이션을 시작해주는 처리가 필요했다

🔎 방법 1) viewWillAppear에 넣어보기

  • 처음에는 이 문제를 보면서 단순하게 viewWillAppearviewDidAppear에 애니메이션 시작 코드를 넣으면 되는 것 아닌가 생각했음.
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    alarmRingView.startAnimations()
}
  • 하지만 이렇게 하려면 VC에서 해주어야 하는 문제가 있다..🫠 그러면 View 객체로 분리한 보람도 없다..
  • 그리고 이런 식이 되면 VC에서 해주면 여러문제가 발생함

1. 책임이 분리가 되지 않는다.

  • 알람이 울렸다는 뷰였기 때문에 화면에 올리면 따로 VC가 해주어야 할 일이 없어야 맞다. 그런데 애니메이션 시작과 정지를 VC가 대신 관리하게 되면 이 뷰를 사용하는 모든 VC에서 직접 다음과 같은 코드를 작성해야 한다.
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    alarmRingView.startAnimations()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    alarmRingView.stopAnimations()
}
  • 그럼 결국 VC의 책임이 된다.

2. 재사용성 할 수 없다.

  • 이 뷰가 나중에 다른 화면에서도 쓰이게 되면 사용하는 모든 VC에서 동일한 생명주기 코드를 반복해서 작성해야 함. 다른 팀원이 쓰다가 VC내에서 위와 같이 애니메이션 관련 코드를 안넣으면 안움직이는 문제가 발생한다

🔎 방법 2) NotificationCenter를 이용하기

1. 뷰의 생명주기

  • 그럼 어쩌지 고민하다가 뷰도 VC 비슷하게 viewDidLoad() 역할을 하는 함수가 있지 않을까? 생각해서 찾아보니 뷰도 존재했다. 그래서 겸사겸사 좀 정리했다. layoutSubviews만 두어번 써본거 같았는데 구체적으로 어떤 역할인지 알게 되어 좋았다

📍 init

init(frame:)
init(coder:)
  • 시점: 뷰가 만들어질 때
  • UI 초기 설정
  • subview 추가 준비

📍 didMoveToSuperview

override func didMoveToSuperview()
  • 시점: 부모 뷰에 추가될 때
  • superview 생김
  • 아직 화면에 보인다는 보장은 없음

📍 didMoveToWindow

override func didMoveToWindow()
  • 시점: 화면에 실제로 붙을 때
  • window != nil → 화면에 보임
  • window == nil → 화면에서 사라짐

📍 layoutSubviews

override func layoutSubviews()
  • 시점: 레이아웃이 잡힐 때마다 호출
  • frame 변경
  • 회전
  • constraint 변경

📍 흐름정리

init -> didMoveToSuperview -> didMoveToWindow (화면 등장) -> layoutSubviews (레이아웃 계산) -> (필요시 반복) -> didMoveToWindow (화면에서 사라짐)

+) 📍 draw(_:)

override func draw(_ rect: CGRect)
  • 시점:직접 그릴 때만 사용
  • 커스텀 그래픽
  • Core Graphics

UIView 생명주기는 뷰가 생성되고 → 화면에 붙고 → 레이아웃이 잡히는 과정

2. 뷰가 다시 올라오는 시점을 관측하기

  • 결론적으로 홈으로 나갔다가 다시 들어왔을 때 애니메이션이 멈추는 문제를 해결하기 위해 NotificationCenter를 사용했다.
    앱이 다시 활성화될 때 발생하는 알림을 받아서 그 시점에 애니메이션을 다시 시작해주는 방식이다.

  • UIApplication.didBecomeActiveNotification을 기준으로 어떤 행동을 할지 결정함

NotificationCenter.default.addObserver(
    self,
    selector: #selector(handleDidBecomeActive),
    name: UIApplication.didBecomeActiveNotification,
    object: nil
)
  • 앱이 활성 상태(Active)가 되었을 때 지정한 메서드를 호출하도록 등록해주고 그리고 실제로 알림을 받으면 아래 메서드가 실행된다.
@objc private func handleDidBecomeActive() {
    restartAnimations()
}
  • 여기서 중요한 점은 기존 애니메이션 상태가 남아 있으면 어색할 수 있어서 먼저 애니메이션을 정리한 뒤 다시 시작하는 방식으로 구현했다
private func restartAnimations() {
    stopAnimations()
    startAnimations()
}
private func stopAnimations() {
    layer.removeAllAnimations()
    circleContainerView.layer.removeAllAnimations()
    alarmImageView.layer.removeAllAnimations()
    
    circleContainerView.transform = .identity
    alarmImageView.transform = .identity
}
  • removeAllAnimations(): 현재 레이어에 걸려 있는 애니메이션을 모두 제거해서 중복 실행이나 꼬임을 방지

  • transform = .identity: 뷰를 원래 상태로 되돌린 뒤 다시 시작해야 해서
    identity를 사용해서 변형이 전혀 없는 기본 상태로 만듦

  • NotificationCenter.default.removeObserver(self): 메모리 문제나 다른 의도치 않은 오류가 발생할 수 있으므로 deinit에서 반드시 해제해야 함

deinit {
    NotificationCenter.default.removeObserver(self)
}

흐름

  1. 뷰 생성 시 앱 활성화 알림을 observe하게 함.
  2. 사용자가 홈으로 나가 앱이 비활성화.
  3. 다시 앱으로 돌아오면 didBecomeActiveNotification이 발생
  4. 등록된 메서드가 호출되고, 애니메이션을 다시 시작

마무리

  • 이렇게 해서 뷰 자체가 앱 상태 변화에 직접 반응할 수 있게 고침.
  • VC를 거치지 않고 뷰 스스로 필요한 시점에 애니메이션을 다시 시작하게 만들었다!
  • 뷰의 주기를 공부할 수 있는 기회가 되었다.
profile
iOS 개발

0개의 댓글