[iOS] CABasicAnimation 활용 (2) - 로딩 애니메이션 (UIBezierPath, CAShapeLayer)

2021. 1. 8. 00:33iOS

반응형

오늘은 저번 시간에 이어 CABasicAnimation을 활용해서 새로운 애니메이션을 만들어 보았어요..!!

 

카카오톡에서 더보기 탭의 여러 메뉴들을 클릭하고 데이터를 받아올 때, 로딩화면이 있더라구요..?

 

저는 항상 이런것도 디자이너한데 부탁해서 Air BnB-Lottie를 이용해서 구현했는데, CABasicAnimation을 사용하고 보니깐 이것도 할 수 있겠다 생각이 들어서 바로 만들어봤어요.

 

먼저 화면을 먼저 보여드리면 다음과 같아요.

요런화면 어떤 요청을 보낼 때 본적 있죠?

 

오늘은 저걸 만들어볼게요!!

 

구현 방법

이 화면에서는 구현해야 할 UI요소가 몇가지일까요..?

 

저는 크게 2가지로 구성을 했는데요.

 

  1. 진행 트랙을 보여주는 뒤의 Background Layer
  2. 화면이 돌아가며 진행을 보여주는 Indicator Layer

이렇게 크게 2가지에요.

 

Q. 어 왜..? 제일 뒤의 회색 화면은 말을 안해요..?

👉 바로 저 부분은 UIView의 backgroundColor와 UIView.layer를 이용해서 기본으로 구성했어요!

 

그럼 우선 기본 UI를 먼저 구성해볼게요!

 

처음으로 UIView을 Custom으로 생성해주세요.

// 이름은 자유롭게 해주세요!
class LoadingIndicator: UIView {
}

이제 다음으로 제일 뒤의 화면 배경을 먼저 만들어줄게요! (아직 Layer를 이용하는 것이 아닌 젤 뒤 배경만이에요!)

 

여기서는 CALayer를 이용하지 않고 UIView.backgroundColor, UIView.layer.borderRadius를 활용할거에요.

override init(frame: CGRect) {
    super.init(frame: frame)
    backgroundColor = .darkGray
    clipsToBounds = true
    layer.cornerRadius = frame.width/2
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    backgroundColor = .darkGray
    clipsToBounds = true
    layer.cornerRadius = frame.width/2
}

이렇게 하면 둥근 배경의 화면만 떴을거에요!

 

이제는 위에서 언급했던 2가지의 구성요소 있죠?

그 중 첫번째, 진행 트랙을 보여주는 backgroundLayer를 UIBezierPath, CAShapeLayer를 이용해서 생성해줄거에요.

private lazy var backgroundLayer: CAShapeLayer = {
    let layer = CAShapeLayer()
    layer.lineWidth = 5
    layer.strokeColor = UIColor.black.cgColor
    layer.fillColor = UIColor.clear.cgColor
    layer.lineCap = .round
    layer.frame = bounds
    return layer
}()

private lazy var circlePath: UIBezierPath = {
    let center = CGPoint(x: bounds.width/2, y: bounds.height/2)
    let path = UIBezierPath(arcCenter: center,
                            radius: bounds.width/2*2/3,
                            startAngle: .pi/2,
                            endAngle: 2*.pi - .pi/2,
                            clockwise: true)
    return path                            
}()

override func draw(_ rect: CGRect) {
    backgroundLayer.path = circlePath.cgPath
    
    layer.addSublayer(backgroundLayer)
}

draw를 사용해서 커스텀으로 UIView를 그려주었는데요. 

개발자 문서에 보면 이 메소드를 이용해서 UIView의 도화지 삼아서 render 시킬 수 있다고 나와있어요!

 

나머지 그려주는 부분에는 특별한 것이 없어요.

만약 CAShapeLayer를 이용해서 그림을 그려주는 것이 이해가 되지 않으면 앞 글을 보고오면 도움이 될 것 같아요..!!

 

[iOS] CABasicAnimation 활용(1) - Counting Progress (UIBezierPath, CAShapeLayer)

이전 포스팅에서는 간단하게 CABasicAnimation을 이용해서 할 수 있는 애니메이션을 해보았어요..!! [iOS] CABasicAnimation란 요즘 Core Animation에 관심을 가지면서 Core Animation를 공부해보기위해 간단히 사..

dongminyoon.tistory.com

 

여기서 radius를 현재 뷰의 반지름으로 설정하지 않은 이유는 젤 위의 애니메이션을 보시면 아시겠죠..?!

실제 트랙으로 나타나는 뷰가 원래 UIView보다 더 작아요! 

 

다음으로 마지막 요소인 Indicator Layer를 만들어볼게요.

여기선 아까 backgroundLayer에서 만들어준 circlePath를 활용할거에요.

 

단 다른 점은 stokeEnd를 이용해서 끝을 잘라줄거라는거~~

private lazy var indicatorLayer: CAShapeLayer = {
    let layer = CAShapeLayer()
    layer.strokeColor = UIColor.white.cgColor
    layer.fillColor = UIColor.clear.cgColor
    layer.lineWidth = 5
    layer.lineCap = .round
    layer.frame = bounds
    // 이 부분을 이용해서 끝을 잘라주었어요!
    layer.strokeEnd = 0.35
    return layer
}()

override func draw(_ rect: CGRect) {
    // circlePath는 아까 위에서 만들었죠. 
    // 똑같이 사용하면 됩니다.
    indicatorLayer.path = circlePath.cgPath
    
    layer.addSublayer(indicatorLayer)
}

자 이렇게까지하면 이제 모든 UI요소들은 만들어주었어요.

 

마지막으로 이제 애니메이션만 추가해주면 되겠죠?

여기서 CABasicAnimation을 이용할거랍니다!

func startAnimation() {
    let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
    
    rotationAnimation.fromValue = 0
    rotationAnimation.toValue = 2*.pi
    
    rotationAnimation.duration = 2
    rotationAnimation.repeatCount = HUGE
    
    indicatorLayer.add(rotationAnimation, forKey: "rotate")
}

func cancelAnimation() {
    layer.sublayers?.forEach { layer in
        layer.removeAllAnimations()
        layer.removeFromSuperlayer()
    }
}

keyPath에는 많은 요소들이 있었는데 중앙을 기준으로 둥글게 둥글게 돌려주면 되어서 transform.rotation을 활용하였습니다.

 

이렇게까지 해주고 ViewController에서 한 번 생성하고 startAnimation()까지 호출해볼게요!

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let loadingIndicator = LoadingIndicator(frame: CGRect(x: 100, y: 100, width: 200, height: 200))
        loadingIndicator.startAnimation()
        
        self.view.addSubview(loadingIndicator)
    }
}

여기까지 해주시면 제일 위와 같은 완성된 화면을 보실 수 있을거에요!!

 

 

재밌어서 이것저것 만들어보고 있었는데, 다음에는 또 다른 걸 만들어서 와볼게요 ㅎㅎㅎ

 

그럼 오늘 포스팅은 여기까지 해볼게요! :D

궁금한 점이나 이런 것도 해보면 좋겠어요 등등 다양한 의견있으시면 댓글 달아주세요.

감사합니다!

반응형

'iOS' 카테고리의 다른 글

[iOS] TDD와 Unit Test  (2) 2021.02.06
[iOS] Xcode Target이란?  (2) 2021.02.01
[iOS] CABasicAnimation 활용 (1) - Counting Progress (UIBezierPath, CAShapeLayer)  (1) 2021.01.04
[iOS] CABasicAnimation란  (0) 2020.12.31
[iOS] CALayer란  (2) 2020.12.31