관리 메뉴

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

[iOS - swift] 1. CALayer 마스킹 활용 - 마스킹하는 방법 (layer.mask, .evenOdd) 본문

iOS 응용 (swift)

[iOS - swift] 1. CALayer 마스킹 활용 - 마스킹하는 방법 (layer.mask, .evenOdd)

jake-kim 2023. 2. 12. 17:33

1. CALayer 마스킹 활용 - 마스킹하는 기본 방법 (mask, .evenOdd)<

2. CALayer 마스킹 활용 - 특정 뷰 음영 효과 주는 방법

3. CALayer 마스킹 활용 - 뷰 합치는 방법

마스킹 기초 개념

  • UIView마다 CALayer를 가지고 있고, 이 CALayer의 프로퍼티인 mask에 CAShapeLayer 인스턴스를 주입하면 마스킹이 구현됨
  • 마스킹이라는것은 의미 그대로 가리는 것 (= 지금 UIView의 마스킹된 영역을 없애고 뒤에있는 뷰가 보이도록 하는 것)
  • CAShapeLayer의 path정보를 입력받아야 하는데, 이 path는 UIBezierPath로 직접 path 정보를 주고 그릴 수 있고, 사각형 같은 경우는 addRect(_:)를하여 쉽게 사용이 가능

(마스킹 기초 코드)

extension UIView {
    func mask(rect: CGRect, reversal: Bool) {
        // 1. path 인스턴스로 경로 정보 획득
        let path = CGMutablePath()
        // TODO
        
        // 2. CAShapeLayer 인스턴스에 위 path 인스턴스 사용
        let shapeLayer = CAShapeLayer()
        // TODO
        
        // 3. mask에 shapeLayer 인스턴스 사용
        layer.mask = shapeLayer
    }
}

마스킹 예제

* 예제에 나온 코드는 코드로 UI 작성의 편의를 위해 SnapKit 사용

  • 마스킹 예제를 위해 뷰 준비
    • view위에 UIImageView를 addSubview한 상태
import UIKit
import SnapKit

class ViewController: UIViewController {
    private let imageView = UIImageView(image: UIImage(named: "blog"))
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        imageView.contentMode = .scaleAspectFill
        
        view.addSubview(imageView)
        
        imageView.snp.makeConstraints {
            $0.edges.equalToSuperview()
        }
    }
}

  • 마스킹사용이 쉽게하기위해 extension으로 구현
extension UIView {
    func mask(rect: CGRect) {
        // 1. path 인스턴스로 경로 정보 획득
        let path = CGMutablePath()
        path.addRect(rect)
        
        // 2. CAShapeLayer 인스턴스에 위 path 인스턴스 사용
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path
        
        // 3. mask에 shapeLayer 인스턴스 사용
        layer.mask = shapeLayer
    }
}
  • 마스킹 적용
    • 주의할점) path로 채운 부분이 보여지는 것이고 그 외의 공간은 사라지게 하는 것
imageView.mask(rect: .init(x: 150, y: 120, width: 80, height: 80))

CAShapeLayer의 fillMode

  • CAShapeLayer의 fillMode라는 프로퍼터가 있는데, fillMode를 .evenOdd로 설정
  • .evenOdd란? path가 중복으로 path된 곳만 마스킹한다는 의미

.evenOdd로 지정한 부분을 마스킹 처리하기

  • .evenOdd는 중복으로 path된 곳을 마스킹하므로, mask(rect:) 메소드 수정
  • isRectMasking 파라미터를 사용하여 분기문 추가
    • path로 전체 영역을 그리고, 지정한 부분을 한번 더 그리면 중복으로 그려진 부분만 마스킹 되도록 구현
    • .evenOdd로 설정
extension UIView {
    func mask(rect: CGRect, isRectMasking: Bool) {
        // 1. path 인스턴스로 경로 정보 획득
        let path = CGMutablePath()
        if isRectMasking {
            let allRect = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)
            path.addRect(allRect)
        }
        path.addRect(rect)
        
        // 2. CAShapeLayer 인스턴스에 위 path 인스턴스 사용
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = path
        if isRectMasking {
            shapeLayer.fillRule = .evenOdd
        }
        
        // 3. mask에 shapeLayer 인스턴스 사용
        layer.mask = shapeLayer
    }
}

(결과)

rect 부분만 마스킹된 결과

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

* 참고

https://developer.apple.com/documentation/quartzcore/cashapelayerfillrule/1521843-evenodd

Comments