이전 포스팅에서 간략하게 iOS에서의 애니메이션을 알아보았다.

 

이번에는 좀 더 자세히 알아볼 것이다.

 

View를 구성하는 방법

  1. Frame을 이용
    1. code
  2. AutoLayout을 이용
    1. xib
    2. storyboard

크게 이 두가지 방법을 통해 View를 그리게 된다.

 

Frame-Based Layout

각 View들의 frame을 programmatically하게 설정하여 배치

frame에는 상위뷰(Super View)의 좌표계에 대한 자신의 위치 = origin

상위뷰(Super View)의 좌표계에 대한 자신의 크기 = size

를 가진다.

 

(bounds라는 개념도 존재하는데... frame vs bounds는 이후에 포스팅하는 걸로

중요한 점은 frame은 상위뷰의 좌표계, bounds는 자신의 좌표계라는 것이다.)

 

쨋든 이어서

frame의 정보를 일일이 코드로 작성해야 한다.

-> 일일이라고 적어놓으니깐 불편해 보이는데 좋게 말하자면, AutoLayout의 그 많은 Constraint들을 일일이 작성하지 않아도 된다.

이렇게 말하면 좀 편해보이나?

하지만, 가장 중요한 점 = 아이폰은 지금 12까지 나왔다!

각 아이폰 기기 별 크기는 다르다. -> 개발자가 모든 화면별로 사이즈를 대응해서 코드로 작성해줘야 한다. -> 굉장히 굉장하네

 

frame은 상위뷰 좌표계에서 그려지고 동작한다. 만약, View -> View -> View 같은 nestedView Depth가 깊어질 경우

개발자의 머리는 남아나질 않을 것이다. -> 모두 계산해줘야 하니까

 

 

AutoLayout

frame과는 다른 개념이다. frame체계를 머리속에서 지워보자

 

AutoLayout은 view 사이의 관계를 맺음으로써 화면을 그린다.

Constraint을 기반으로 각 View들의 크기와 위치를 계산한다.

 

이전 포스팅에서 간단하게 작성된 것을 보면 될 것 같다.

 

 

Animate

애니메이션 글인데 왜 frame과 AutoLayout을 적었냐 하면... 사용법에 차이가 있어서 설명했다...(라고 할뻔)

이전 포스팅에 있는 예제를 살펴보자

 

UIView.animate(withDuration: 2.0) {
	self.playPauseButton.frame = CGRect(x: 0, y: 0, width: self.playPauseButton.frame.size.width, height: self.playPauseButton.frame.size.height)
}

UIView.animate(withDuration: 2.0) {
	self.view.backgroundColor = .black
}

두 애니메이션 모두 정상적으로 돌아간다.

그렇다면 frame이 아닌 AutoLayout으로 이뤄진 화면에 적용해보자!

View의 높이를 0으로 만들것이다!

 

 

// view1이라는 녹색 View를 붙였다.
func setView() {
	view.addSubview(view1)
	view1.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
	view1.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
	view1.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
	view1.heightAnchor.constraint(equalToConstant: 60).isActive = true
}

 

자 이제 애니메이션을 적용해보자

 

UIView.animate(withDuration: 1.0) {
	self.view1.frame.size.height = 0
}

 

엥? 난 분명 높이를 0으로 줬는데 왜 오히려 커졌다가 줄어들어?

위에서 view1을 UIView(frame: )으로 생성한 것이 아닌 AutoLayout으로 뷰를 그렸기 때문에, 애니메이션 방법도 달라야 한다.

 

높이 Constraint를 변경하기 위해 프로퍼티를 생성한 후, 참조시키자.

 

var heightConstraint = NSLayoutConstraint()
....
func setView() {
	view.addSubview(view1)
	view1.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
	view1.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
	view1.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
	heightConstraint = view1.heightAnchor.constraint(equalToConstant: 60)
	heightConstraint.isActive = true
}

 

UIView.animate(withDuration: 1.0) {
	self.heightConstraint.constant = 0
}

그리고 실행 시켜보자.

 

0으로 되긴 했네... 근데 애니메이팅이 아니라 그냥 뿅??

 

frame이나 alpha, backgroundColor 는 Animatable한 built-in 속성이기 때문에 애니메이팅이 동작한다.

하지만 Constraint는 Animatable한 built-in 속성이 아니기 때문에, layoutIfNeeded()라는 메소드의 호출이 필요하다.

layoutIfNeeded()는 또 이전 포스팅에 있다.

 

호출이 필요한 이유는 Constraint의 변경이 실제로 View에 적용되기 위해서는 일련의 순서가 필요하기 때문이다.

  1. 변경될 값에 맞춰 시스템이 해당 Constraint를 포함하여 연관있는 Constraint들의 모든 값들을 재계산
    1. 앞에서 말했 듯, Constraint는 view들의 관계이기 때문에, 하나의 View가 변경되면 다른 View들도 그에 맞춰 변경되기 때문
  2. layout 엔진이 재계산된 Constraint에 맞춰 모든 View들의 frame들의 값을 재계산하고 배치한다.
  3. 해당 frame들을 화면에 그린다.

그럼 그렇게 다시 해보자!

heightConstraint.constant = 0
UIView.animate(withDuration: 1.0) {
	self.view1.layoutIfNeeded()
}

엥? view1.layoutIfNeeded()까지 했는데 왜 안돼...?

(개인적인 생각으로는 view1의 Constraint들은 대부분 self.view와 연관되어 있다. 그러한 이유로 안되지 싶다...)

 

그럼 코드를 바꿔서 진행해보자

heightConstraint.constant = 0
UIView.animate(withDuration: 1.0) {
	self.view.layoutIfNeeded()
}

정상적으로 원하는 대로 동작한다..!

 


정리

  1. frame방식과 AutoLayout을 통해 View를 그릴 수 있다.
  2. frame과 AutoLayout 각각 다른 코드진행으로 animate할 수 있다.
    1. frame과 alpha 등은 Animatable한 built-in 속성이기 때문에, layoutIfNeeded()가 필요없다
    2. AutoLayout은 Animatable한 built-in 속성이 아니기 때문에, layoutIfNeeded()가 필요하다
  3. animation을 적용한 view에서 layoutIfNeeded()를 호출하는 것이 아니라, 해당 view와 Constraint로 관련되어있는 SuperView에서 호출해야 한다.

 

 

Animation

UIView.animate(...) 를 사용해서 애니메이팅을 구현할 수 있다.

 

animation(애니메이션) 사전적 의미

동작이나 모양이 조금씩 다른 많은 그림이나 인형을 한 장면씩 촬영하여 영사하였을 때에 화상이 연속하여 움직이는 것처럼 보이게 하는 것

어렸을 때, 책 가장자리에 조금조금씩 움직임을 표현하여 100p를 그린 뒤, 책을 빠르게 넘겨 촤르르르륵~ 하는 것이 애니메이션이었다..!

 

iOS에서의 Animation

Changes to several view properties can be animated—that is, changing the property creates an animation starting at the current value and ending at the new value that you specify. The following properties of the 
UIView
 class are animatable:

뷰의 여러 속성을 변경하여 애니메이팅을 할 수 있다. 즉, 속성을 변경하는 것은 현재 값에서 새로운 값으로의 애니메이션을 생성한다.

볼드체가 핵심이다..! 위의 애니메이션 정의만 생각하면 View를 조금조금씩 그려 촤라라라락하는 것처럼 해야하기 때문에, 직접 자잘한 움직임과 위치, 크기 등의 속성을 일일이 지정해줘야한다.

 

B.U.T.하지만 현재 값에서 새로운 값으로의 애니메이션을 생성한다!

 

애니메이팅할 수 있는 UIView의 속성은 다음과 같다.

 

먼저 Frame값을 변경해보자!!

이러한 화면이 있다고 하자!

 

UIView.animate(withDuration: 2.0) {
	self.playPauseButton.frame = CGRect(x: 0, y: 0, width: self.playPauseButton.frame.size.width, height: self.playPauseButton.frame.size.height)
}

위에서 얘기한대로 난 최종 상태값만을 지정해줬을 뿐인데!! 자연스럽게 이동한다!!!

 

다음으로 backgroundColor를 변경해보자

UIView.animate(withDuration: 2.0) {
	self.view.backgroundColor = .black
}

 

 

이 또한 흰색에서 검은색으로 변경한다고 지정했을 뿐인데! 흰색 -> 회색 -> 검은색 으로 자연스럽게 변환된다.!!

 


  • UIView.animate 를 사용한 애니메이팅은 자동으로 MainQueue(UIQueue)에서 동작한다
    • 따라서 따로 DispatchQueue.main.async를 사용하지 않아도 된다.
  • origin값을 변경하는 것이 아니라, Transform을 변경하는 것이다.
    • Transform.identity를 이용하여 원래값을 얻을 수 있다
  • 애니메이팅이 끝난 후, 동작하는 completionHandler가 존재한다.
    • Animation이 중간에 cancel되는 경우가 있어서 파라미터로 Bool 변수를 받는다.

 

+ Recent posts