관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - Swift] CALayer, mask 개념 (UIView와 CALayer의 차이, 뷰 자르기) 본문

iOS 응용 (swift)

[iOS - Swift] CALayer, mask 개념 (UIView와 CALayer의 차이, 뷰 자르기)

jake-kim 2022. 11. 28. 22:07

UIView와 CALayer 차이

* CA: Core Animation

  • CALayer는 UIKit보다 한 단계 낮은 수준의 인터페이스를 제공하여, UIKit보다 많은 기능을 제공하지만 몇 가지 기능에 대해서 직접 구현 필요
  • OpenGL은 GPU에서 2D, 3D 그래픽을 렌더링하는데 사용되는 저수준 API로 iOS Graphic Hardware(Core Graphic)에 해당
  • UIView는 CALayer의 wrapper 역할
    • Core Animation는 저수준 api이고, UIKit은 고수준 api

CA의 특성

  • Core Animation은 별도의 쓰레드에서 GPU를 사용해 UI를 직접 렌더링
  • UIView와 달리 Responder가 없어, 유저 인터렉션 기능은 직접 구현 및 설정 필요
  • Core Animation으로 작성된 코드는 iOS, Mac 두 플랫폼에서 똑같이 동작 (UIKit - Mobile, AppKit - Mac)

CALayer의 mask 속성

https://developer.apple.com/documentation/quartzcore/calayer/1410861-mask

  • layer의 일부분을 alpha=0으로 만드는 것
    • mask에 CALayer 타입을 넣으면 그 CALayer에 해당되는 부분의 alpha값이 0이되는 것
    • 뷰의 일부분을 자를 수 있는 기능
var mask: CALayer? { get set }

CALayer의 mask를 이용하여 뷰 일부분 자르기

  • 예제에 사용할 사각형 형태의 뷰 준비

예제로 사용할 사각형 뷰

  private let myView: UIView = {
    let view = UIView()
    view.backgroundColor = .green
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
  }()
  
  override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(myView)
    
    NSLayoutConstraint.activate([
      myView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
      myView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
      myView.widthAnchor.constraint(equalToConstant: 200),
      myView.heightAnchor.constraint(equalToConstant: 200)
    ])
  }
  • 테두리를 마스킹하여 원 만들기
    • 위 초록색 뷰의 layer.mask에 바깥 layer를 생성해서 주입해주면 테두리가 잘리므로 computed property로 CALayer 구현
      • computed property로 구현하는 이유는, 뷰가 런타임 시점에 bounds 값이 정해지므로 이 값을 layoutSubviews() 메소드에서 사용하기 위함
    • CALayer의 서브클래스인 CAShapeLayer를 사용하면 구현이 편리
    • UIBezierPath로 바깥 원 경로 획득
  private var circleLayer: CALayer {
    let layer = CAShapeLayer()
    layer.path = UIBezierPath(ovalIn: myView.bounds).cgPath
    return layer
  }
  
  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    myView.layer.mask = circleLayer
  }

(완성)

CALayer로 테두리를 잘라서 원으로 만들기

  • 테두리만 남기기 (= 뷰 안에 마스킹)
    • 마찬가지로 런타임 시점, layoutSubviews()에서 호출되어야 myView이 bounds를 구할 수 있으므로 computed property 사용
    • UIBezierPath(roundedRect:cornerRadius)를 사용하여 구현
  private var borderLayer: CALayer {
    let layer = CAShapeLayer()
    layer.path = UIBezierPath(
      roundedRect: myView.bounds.insetBy(dx: 5, dy: 5),
      cornerRadius: myView.bounds.height / 2
    ).cgPath
    layer.fillColor = UIColor.clear.cgColor
    layer.strokeColor = UIColor.green.cgColor
    return layer
  }
  
  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
//    myView.layer.mask = circleLayer
    myView.layer.mask = borderLayer
  }

(완성)

CALayer를 사용하여 내부 자르기

* 전체 코드: https://github.com/JK0369/ExCALayer

 

* 참고

https://medium.com/@fassko/uiview-vs-calayer-b55d932ff1f5

https://developer.apple.com/documentation/quartzcore/calayer/1410861-mask

Comments