Notice
Recent Posts
Recent Comments
Link
관리 메뉴

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

[iOS - swift] CADisplayLink 사용 방법 (디스플레이 주사율, 타이머, RunLoop.Mode) 본문

iOS 응용 (swift)

[iOS - swift] CADisplayLink 사용 방법 (디스플레이 주사율, 타이머, RunLoop.Mode)

jake-kim 2023. 5. 23. 00:03

CADisplayLink 개념

  • 디스플레이의 주사율에 맞춰서, 특정 함수를 호출시킬 수 있는 타이머 개체
    • 보통 타이머는 시간에 따라 호출되지만, CADisplayLink는 디스플레이 프레임(frame) 기준으로 발생하는 타이머
    • 디스플레이의 갱신 주기에 맞춰서 특정 코드를 실행하도록 예약하는것
    • 사용처) 애니메이션 및 그래픽 렌더링에 동기화하여 작업을 수행할 때 사용
  • CADisplayLink
    • 화면 갱신 주기와 관련된 작업을 수행하는데 사용되므로, 디스플레이와 연결(link)를 제공한다는 의미

CADisplayLink 사용방법

* 예제 시작 코드

import UIKit

class ViewController: UIViewController {
    private let button: UIButton = {
        let button = UIButton()
        button.setTitle("button", for: .normal)
        button.setTitleColor(.systemBlue, for: .normal)
        button.setTitleColor(.blue, for: .highlighted)
        button.addTarget(self, action: #selector(tap), for: .touchUpInside)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(button)
        NSLayoutConstraint.activate([
            button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        ])
    }
    
    @objc private func tap() {
        startDisplayLink()
    }
}
  • displayLink 선언
private var displayLink: CADisplayLink?
  •  버튼을 탭했을 때 DisplayLink 실행
    • CADisplayLink(target:selector:)로 초기화
    • preferredFramesPerSecond을 10으로 설정하면 초당 콜백이 10번 호출
@objc private func tap() {
    startDisplayLink()
}

private func startDisplayLink() {
    stopDisplayLink()
    displayLink = CADisplayLink(target: self, selector: #selector(update))
    displayLink?.preferredFramesPerSecond = 10
    displayLink?.add(to: .main, forMode: .default)
}

@objc private func update() {
    print("frame", view.frame)
}

private func stopDisplayLink() {
    displayLink?.invalidate()
    displayLink = nil
}

displayLink?.add(to: .main, forMode: .default) 파라미터인 RunLoop와 RunLoop.Mode 개념

  • RunLoop
    • 이벤트 처리, 타이머, 소켓 통신 등 애플리케이션의 이벤트 루프를 관리하는 객체
    • RunLoop는 스레드에 바인딩되어 해당 스레드의 이벤트 처리를 담당
    • (메인 스레드에는 기본적으로 메인 RunLoop가 생성)
  • RunLoop.Mode
    • RunLoop의 모드를 식별하기 위한 값
    • .default: 대부분의 애니메이션 및 사용자 이벤트 처리 작업이 이 모드에서 수행
    • .common: default와 유사하지만 조금 더 낮은 우선순위
    • .tracking: 스크롤 뷰와 같은 추적 작업을 수행할 때 사용되는 모드 (default보다 더 높은 우선순위)
    • .modal: 모달 작업 수행할 때 사용되는 모드
    • .eventTracking: 사용자 이벤트 트래킹을 위해 사용되는 모드 (터치 이벤트를 처리하는데 사용)

타임아웃 처리

  • CADisplayLink는 무한히 실행될 수 있으므로 DispatchWorkItem를 사용하여 타임아웃처리도 추가
private func startDisplayLink() {
    stopDisplayLink()
    displayLink = CADisplayLink(target: self, selector: #selector(update))
    displayLink?.preferredFramesPerSecond = 10
    displayLink?.add(to: .main, forMode: .default)
    
    let workItem = DispatchWorkItem(block: { [weak self] in
        self?.stopDisplayLink()
    })
    dispatchWorkItem = workItem
    
    DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: workItem)
}

@objc private func update() {
    print("frame", view.frame)
}

private func stopDisplayLink() {
    displayLink?.invalidate()
    displayLink = nil
    dispatchWorkItem?.cancel()
}

주의사항

  • displaylink는 강한 참조를 이루기 때문에, 명시적으로 invalidate()를 호출해주지 않으면 메모리 해제가 되지 않음

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

* 참고

https://developer.apple.com/documentation/foundation/runloop/mode

https://developer.apple.com/documentation/quartzcore/cadisplaylink/1621323-add

Comments