새소식

iOS/UIKit

[UIKit] Simple Drawing App with PencilKit

  • -

[참고영상]

https://youtu.be/3d1HNBpqvuM


[학습 목표]

pencilkit을 활용해 아이폰 / 아이패드에서 글씨를 쓰는 페이지를 만들자!


[구현 방법]

1. StoryBoard에 카메라 버튼과 글을 쓰는 모드와 읽기모드로 바꿀 Pencil 버튼 그리고 흰색의 캔버스를 위한 UIView를 구현해주자.

 

2. viewDidLoad()를 셋팅해주자.

override func viewDidLoad() {
    super.viewDidLoad()
    //canvas에 그림을 그리게 할 장치.
    canvasView.delegate = self
    canvasView.drawing = drawing
    canvasView.alwaysBounceVertical = true
    
    //여기서는 애플펜슬만 그림을 그리게 정할것이기에 pencilOnly로 저장해 뒀다.
    //다만, pencilOnly라고 해도 애플 펜슬이 없는 상태에서는 손으로 입력이 된다.
    canvasView.drawingPolicy = .pencilOnly

	//canvas 밑에 그림 그리는 tool들을 나오게 하기 위한 장치.
    toolPicker.addObserver(canvasView)
    toolPicker.setVisible(true, forFirstResponder: canvasView)
    canvasView.becomeFirstResponder()       
}

 

3. 캔버스의 크기를 정할 viewDidLayoutSubview를 만들어주자.

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    
    //canvas의 크기를 설정해주자.
    let canvasScale = canvasView.bounds.width / canvasWidth
    canvasView.minimumZoomScale = canvasScale
    canvasView.maximumZoomScale = canvasScale
    canvasView.zoomScale = canvasScale
    
    //canvas의 위치를 설정해주자.
    canvasView.contentOffset = CGPoint(x: 0, y: -canvasView.adjustedContentInset.top)
}

 

4.  캔버스의 사이즈를 계속해서 그리는 크기만큼 업데이트 시켜주는 함수를 만들자.

func updateContentSizeForDrawing() {
    let drawing = canvasView.drawing
    let contentHeight: CGFloat

    if drawing.bounds.isNull {
        contentHeight = max(canvasView.bounds.height, (drawing.bounds.maxY + self.canvasOverscrollHeight) * canvasView.zoomScale)
    }
    else {
        contentHeight = canvasView.bounds.height
    }
    canvasView.contentSize = CGSize(width: canvasWidth * canvasView.zoomScale, height: contentHeight)
}

 

5. pencil 버튼을 누르면 그림을 그리는 모드로 할 건지 움직이게 할 것인지 선택하게 하자.

@IBAction func toggleFingerOrPencil ( _ sender : Any){
    canvasView.allowsFingerDrawing.toggle()
    pencilFingerButton.title = canvasView.allowsFingerDrawing ? "Finger" : "Pencil"
}

* 여기서는 allowsFingerDrawing을 사용하였지만 해당 부분은 iOS 14 이후로 drawingPolicy로 추천하는 부분이다.

  다만, drawingPolicy의 경우에는 bool 값이 아니기에 해당 버튼의 toggle을 사용하기에 불편한 부분이 있어서 기존에 쓰던

  allowsFingerDrawing을 사용하였다.

 

6. 그린 그림을 카메라에 저장할 함수를 만들자.

@IBAction func saverDrawingToCameraRoll(_ sender: Any) {
    UIGraphicsBeginImageContextWithOptions(canvasView.bounds.size, false, UIScreen.main.scale)

    canvasView.drawHierarchy(in: canvasView.bounds, afterScreenUpdates: true)

    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    if image != nil{
        PHPhotoLibrary.shared().performChanges ({
            PHAssetChangeRequest.creationRequestForAsset(from: image!)
        } , completionHandler: {success,error in
        })
    }
}

 

변수명 설정같은 세밀한 부분들이 있지만 크게 보자면 해당 함수들을 이용한 플로우로 만들어진다.

여기서는 사진을 저장하기 위하여 PhotosUI 를 사용하였지만 단순히 PencilKit 만을 사용하여도 충분히 앱에 재미를 더할 수가 있다. 


[전체 코드]

import UIKit
import PencilKit
import PhotosUI

class ViewController: UIViewController, PKCanvasViewDelegate, PKToolPickerObserver {
    
    @IBOutlet weak var pencilFingerButton: UIBarButtonItem!
    @IBOutlet weak var canvasView: PKCanvasView!
    
    let canvasWidth: CGFloat = 768
    let canvasOverscrollHeight: CGFloat = 500
    let toolPicker = PKToolPicker()
    
    var drawing = PKDrawing()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        canvasView.delegate = self
        canvasView.drawing
        = drawing
        
        canvasView.alwaysBounceVertical = true
        canvasView.drawingPolicy = .pencilOnly
        
        toolPicker.addObserver(canvasView)
        toolPicker.setVisible(true, forFirstResponder: canvasView)
        canvasView.becomeFirstResponder()
        
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        
        let canvasScale = canvasView.bounds.width / canvasWidth
        canvasView.minimumZoomScale = canvasScale
        canvasView.maximumZoomScale = canvasScale
        canvasView.zoomScale = canvasScale
        
        canvasView.contentOffset = CGPoint(x: 0, y: -canvasView.adjustedContentInset.top)
    }
    
    override var prefersHomeIndicatorAutoHidden: Bool {
        return true
    }
    
    @IBAction func toggleFingerOrPencil ( _ sender : Any){
        canvasView.allowsFingerDrawing.toggle()
        pencilFingerButton.title = canvasView.allowsFingerDrawing ? "Finger" : "Pencil"
    }
    
    @IBAction func saverDrawingToCameraRoll(_ sender: Any) {
        UIGraphicsBeginImageContextWithOptions(canvasView.bounds.size, false, UIScreen.main.scale)
        
        canvasView.drawHierarchy(in: canvasView.bounds, afterScreenUpdates: true)
        
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        if image != nil{
            PHPhotoLibrary.shared().performChanges ({
                PHAssetChangeRequest.creationRequestForAsset(from: image!)
            } , completionHandler: {success,error in
            })
        }
    }
    
    func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
        updateViewConstraints()
    }
    
    func updateContentSizeForDrawing() {
        let drawing = canvasView.drawing
        let contentHeight: CGFloat
        
        if drawing.bounds.isNull {
            contentHeight = max(canvasView.bounds.height, (drawing.bounds.maxY + self.canvasOverscrollHeight) * canvasView.zoomScale)
        }
        else {
            contentHeight = canvasView.bounds.height
        }
        canvasView.contentSize = CGSize(width: canvasWidth * canvasView.zoomScale, height: contentHeight)
    }
    
}
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.