본문 바로가기
iOS

Pencil Kit (iPadOS) - 기초편

by 융식 2022. 9. 21.

iPad를 구매할 때 Apple Pencil을 대부분 같이 구매하게 됩니다.

만약 iPad를 지원하는 App을 만든다면, 그 App에 Apple Pencil을 이용한 기능을 개발해야한다면? 

 

그래서 오늘 공부할 것은 Pencil Kit! (사실 제가 공부 해보고 싶었음..ㅎ)

 

기초적으로 Apple Pencil을 이용하여 그림을 그릴 수 있는 방법부터 살펴보자!

 

 

Pencil Kit을 이용하여 그림을 그릴 수 있는 가장 중요한 두가지 요소는 

PKCanvasView 와 PKToolPicker

 

항상 그렇듯 공식 문서를 통해 천천히 살펴보자!

 

PKCanvasView는 Apple Pencil을 이용하거나 손가락을 사용해서 그린 콘텐츠를 앱에 표시해줍니다.

class PKCanvasView : UIScrollView

Scroll View를 상속하니 뷰를 그릴 때 크기를 지정해주면 좋지만, 자동적으로 사이즈를 조정해준다고 합니다!


Swift에 Delegate가 빠져서는 안되겠죠?

protocol PKCanvasViewDelegate

 

Canvas view에 그려진 내용이나 선택한 도구의 변경사항에 응답하기 위해 사용됩니다.

총 4가지의 종류가 있습니다.

현재 도면의 내용이 변경되었음을 알리는 메소드
optional func canvasViewDrawingDidChange(_ canvasView: PKCanvasView)

이전에 그린 내용을 표시할 준비가 되었음을 알리는 메소드
optional func canvasViewDidFinishRendering(_ canvasView: PKCanvasView)

사용자가 현재 선택한 도구로 그리기 시작했음을 알리는 메소드
optional func canvasViewDidBeginUsingTool(_ canvasView: PKCanvasView)

사용자가 사용중인 도구로 그리기 종료했음을 알리는 메소드
optional func canvasViewDidEndUsingTool(_ canvasView: PKCanvasView)

Canvas View에서는 그림을 그리는 도화지 역할을 할 뿐만 아니라 그림을 그리는 도구의 케이스 역할도 제공 해줍니다.

@MainActor var tool: PKTool { get set }

어라라? 평소에 자주 보지 못하던 MainActor라는 녀석이 있군요. 이 친구가 어떤 친구인지 잠깐 알아보고 가죠!

https://developer.apple.com/documentation/swift/mainactor

공식문서에 따르면 

메인 디스패치 대기열과 동일한 싱글톤 작업자.

디스패치 큐를 사용할 때 좀더 편리하게 사용하기 위해 만들어진 친구 같군요!
중요한 친구 같으니 나중에 좀 더 집중적으로 알아보도록 합시다!


Canvas View에 Pencil만 이용할 수 있게, 혹은 손으로도 그릴 수 있게 설정이 가능합니다.

var drawingPolicy: PKCanvasViewDrawingPolicy { get set }

enum PKCanvasViewDrawingPolicy : UInt, @unchecked Sendable
// 기본 그리기 설정
case: default

// 어떤 입력도 캔버스 그리기에 허용
case: anyInput

// Apple Pencil 전용
case: pencilOnly

Canvas View에서 제스쳐, 터치 이벤트를 인식할 수 있게해주는 속성

var drawingGestureRecognizer: UIGestureRecognizer { get }

마지막으로 Canvas View에 그려진 데이터를 저장시킬 수 있게해주는 속성

var drawing: PKDrawing { get set }

 

이제 유저가 그림을 그릴 수 있게 도구를 제공해야 겠죠??!

Apple pencil을 사용할 때 필요한 지우개라던지 형광펜이라던지! 모두 PKToolPicker에서 제공합니다!

 

class PKToolPicker : NSObject

PKToolPicker에서는 그리기 도구, 색상 및 추가 옵션을 표시할 수 있는 팔레트를 관리한다고 하네요!

ToolPicker를 만들어주기만 해도 화면 어느 곳에서나 움직여서 사용자가 관리할 수 있게 하다니 너무 편하겠군요..!

 

사용자가 그림을 그리다가 도구를 바꾸면 Canvas View에서 감지를 해 변경할 수 있게 해줘야 겠네요.

 

인스턴스로 초기화 시켜 ToolPicker로 만들어줄 수 있습니다!

var toolPicker: PKToolPicker!

toolPicker = PKToolPicker()

그릴 도구를 선택하고 반영할 수 있게, ToolPicker에 Cavas View를 추가시켜 확인할 수 있게 하는 메소드

func addObserver(_ observer: PKToolPickerObserver)

 

추가가 있으니 해제할 수도 있게! 

func removeObserver(_ observer: PKToolPickerObserver)

이제 ToolPicker의 변경사항이 있을 경우의 ToolPickerObserver 델리게이트를 살펴봅시다!

 

// ToolPicker 자체를 변경할 때 (다른 그리기 도구로 변경할 때)
optional func toolPickerSelectedToolDidChange(_ toolPicker: PKToolPicker)

// ToolPicker에서 제공하는 눈금자를 표시하거나 숨겨졌을 때 발생하는 메소드
optional func toolPickerIsRulerActiveDidChange(_ toolPicker: PKToolPicker)

// ToolPicker가 유저한테서 보여지거나 숨겨질때 발생하는 메소드
optional func toolPickerVisibilityDidChange(_ toolPicker: PKToolPicker)

// ToolPicker에서 그리기 도구가 변경될 때 발생하는 메소드
optional func toolPickerFramesObscuredDidChange(_ toolPicker: PKToolPicker)

마지막으로 ToolPicker가 사용자에게 보여지기 않는다면 의미가 없겠죠?!

그걸 위해 설정해야 하는 메소드!

func setVisible(
    _ visible: Bool,
    forFirstResponder responder: UIResponder
)

보여지기 위해서 파라미터로  Bool 값을 받는것은 알겠는데 뒤에 있는 forFirstResponder 파라미터는 무엇일까요?

ToolPicker와 처음 응답할 수 있게 연결해주는 값이라고 합니다!


그럼 이제 공부했던 것들을 써먹어 봅시다!

 

import UIKit
import PencilKit

class DrawViewController: UIViewController, PKCanvasViewDelegate, PKToolPickerObserver {
    // 캔버스 뷰를 만들어줌
    @IBOutlet weak var canvasView: PKCanvasView!
    // Tool Picker를 만들어줄 변수 생성
    var toolPicker: PKToolPicker!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        canvasView.delegate = self
        // Tool Picker 인스턴스 생성
        toolPicker = PKToolPicker()
        // Tool Picker를 사용자에게 보여줄 수 있게 설정
        toolPicker.setVisible(true, forFirstResponder: canvasView)
        // Tool Picker에 Canvas View가 인식할 수 있게 설정
        toolPicker.addObserver(canvasView)
        // 해당 뷰 컨트롤러에도 같은 설정
        toolPicker.addObserver(self)
        // Tool Picker 위치 설정
        updateLayout(for: toolPicker)
        // Canvas View가 처음 인식할 수 있게 설정
        canvasView.becomeFirstResponder()
        
    }
    // 툴피커가 변경될 때
    func toolPickerFramesObscuredDidChange(_ toolPicker: PKToolPicker) {
        updateLayout(for: toolPicker)
    }
    // 툴피커가 숨겨지거나 안보이게할 때
    func toolPickerVisibilityDidChange(_ toolPicker: PKToolPicker) {
        updateLayout(for: toolPicker)
    }
    // 툴피커는 적당한 크기의 캔버스위에 표시 되지만 작은 크기의 혹은 조정된 크기의 캔버스에는 화면 일부에 표시될 수 있게 함
    func updateLayout(for toolPicker: PKToolPicker) {
        let obscuredFrame = toolPicker.frameObscured(in: view)
        // 툴피커가 캔버스 위에 표시될 때는 실행 취소 및 다시 실행 버튼이 추가.
        if obscuredFrame.isNull {
            canvasView.contentInset = .zero
            navigationItem.leftBarButtonItems = []
        } else {        // 그렇지 않다면 캔버스 뷰의 상단에 툴피커를 위치시키고, 실행 취소 및 다시 실행 버튼이 사라짐
            canvasView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: view.bounds.maxY - obscuredFrame.minY, right: 0)
        }

        canvasView.scrollIndicatorInsets = canvasView.contentInset
    }
    
}

 

 

끝!

 

참고 자료 : https://developer.apple.com/documentation/pencilkit

'iOS' 카테고리의 다른 글

AVCapture Session  (0) 2022.10.17
iOS ARKit LiDAR 에 대해  (0) 2022.09.27
View Life Cycle  (0) 2022.09.06
Watch Connectivity (watchOS)  (0) 2022.07.31
DispatchQueue, sync 와 async  (0) 2022.06.10