iOS PDFKit 잉크 주석 자습서

Apple의 PDFKit에 대한 완전한 가이드

Unsplash에 Suganth의 사진

이것은 Apple의 PDFKit에 대한 첫 번째 기사입니다. PDFKit 기본 사항부터 시작하여이 튜토리얼의 끝에서 첫 번째 잉크 주석을 만듭니다.

  • 번째 는 PencilKit, 텍스트 주석 및 자동 저장에 관한 것입니다.
  • 세 번째 문서 페이지를 장치에서 PDF 문서를 생성하고 삽입 및 제거에 관한 것입니다

이 뷰어의 핵심 기능은 손가락이나 Apple Pencil로 PDF 파일에 주석을 추가하는 기능이었습니다. 물론 우리는 그것이 쉬운 일이 아니라는 것을 이해했지만 그것이 얼마나 어려울 지 상상하지 못했습니다.

언뜻보기 PDFKit에 iOS SDK에 포함 된 다른 Apple의 프레임 워크처럼 보입니다. 여기에는 내장 제스처 지원 및 많은 애니메이션이있는 PDF 문서 및 축소판보기가 포함됩니다. 실제로 단 몇 줄의 코드로 작업을 해결하는 것은``마법적인 ''프레임 워크 인 것 같습니다.

그러나 그림, 터치 및 주석이 예상대로 작동하도록 만드는 데 많은 시간을 보냈습니다. 또한 충돌 (에서 Framework) 을 유발하는 몇 가지 버그 와 설명서 및 자습서 또는 예제가 부족한 것도 발견했습니다 .

이것이 제가이 튜토리얼을 만든 이유입니다!

iOS 13 용으로 업데이트 됨

이 튜토리얼은 아이폰 OS (13)이 업데이트되었습니다, 당신은 또한에 대한 몇 가지 유용한 정보를 찾을 수 있습니다 PencilKit, PKCanvasView그리고 PKToolPicker두 번째 부분 텍스트 주석을 추가하는 방법뿐만 아니라.

작업

작업은 매우 간단했습니다.

  • 장치에 PDF 파일을 다운로드합니다.
  • PDF 문서를보고 사용자가 페이지 사이를 이동할 수 있도록합니다.
  • 사용자의 손가락 또는 Apple 연필로 PDF에 잉크 주석 추가
  • PDF 문서를 저장하고 서버에 업로드합니다.

참고 :이 자습서에서는 iPad 앱을 빌드하지만 PDFKit은 iPhone에서도 작동합니다.

PDFView

PDFView는 PDFKit의 핵심 개체입니다. UIView의 하위 클래스이며 주요 목적은 PDF 문서를 표시하는 것입니다. ViewController의 뷰 에 하나를 추가하려면 Storyboard또는 코드 내 초기화를 사용할 수 있습니다 . 개인적으로 저는 Storyboards를 선호 하므로 여기에서 사용하고 있습니다.

ViewController의보기에 PDFView를 추가하려면

  • 새 UIView를 추가하기 만하면됩니다.
  • 설정 제약 (썸네일보기를 위해 왼쪽에 약간의 공간을 남겨 두겠습니다)
  • Inspector 패널 (오른쪽)로 이동하여 Identity 탭을 선택하고 클래스 이름을 입력합니다 PDFView.
ViewController의보기에 PDFView 추가
  • Interface Builder를 사용 @IBOutlet하여 ViewController’s클래스 생성
  • 잊지 마세요 import PDFKit
  • 내 PDFView 설정 코드는 다음과 같습니다.

두 번째 줄은 매우 유용합니다. 기본적으로 PDFView는 기본 제공을 사용 UIScrollView하여 전체 문서에서 연속 스크롤을 활성화합니다. 그러나 PageViewController모드를 활성화 PDFView하여 화면에 한 페이지 만 표시 되도록 할 수 있습니다. 물론 확대 / 축소가 지원되며 기본적으로 활성화됩니다.

.autoScales설정에 버그가 있음을 발견했습니다 . 화면이 회전 할 때 iPad에서는 작동하지 않습니다. 이 문제를 해결하려면 다음 호출을 추가해야합니다.

예기치 않은 동작이있는 또 다른 방법은 .backgroundColor. 코드에서만 작동하며 Interface Builder에서 설정하려고 할 때 작동하지 않습니다. 나는 내가 잘못한 일을 이해하려고 오랜 시간을 보냈다!

마지막으로 이유를 찾을 수있었습니다. 호출 pdfView.document = ...하면 PDFView의 배경색이 기본값으로 재설정 됩니다. 그래서, 전화 pdfView.backgroundColor = ...pdfView.document = ....

PDFView설정을 완료 한 후 예제 PDF 문서를 프로젝트에 추가해 보겠습니다 (왼쪽 패널로 드래그 앤 드롭).

생성 PDFDocument하여 다음 항목에 추가하십시오 PDFView.

간단합니다!

이제 다음과 같은 기본 기능을 사용하여 앱을 실행했습니다.

  • 두 손가락 제스처로 확대 / 축소.
  • 스크롤.
  • 복사 메뉴를 호출하는 긴 압력 제스처.
  • ThumbnailView 지원하다.

참고 :에 너무 많은 코드 줄을 넣는 것이 최선의 생각은 viewDidLoad아니지만 자습서이므로 그냥 두겠습니다!

PDFThumbnailView

썸네일보기를 만드는 것은 쉽습니다. 이전 단계에서했던 것처럼 :

  • 뷰에 새로 추가 UIView합니다 ViewController’s( PDFView’s하위 뷰가 아님).
  • 설정 제약.
  • PDFThumbnailView오른쪽 패널의 Identity Inspector 탭에서 클래스 이름으로 설정 합니다.
  • @IBOutlet귀하 ViewController의 코드를 작성하십시오 .
축소판보기 만들기

이 코드는 명확합니다. 은 (는 PDFThumbnailView)와 같이 인터페이스 빌드 설정을 PDFView존중하지 않는다는 점 을 언급해야 .backgroundColor합니다. 그러나 설정 한 색상을 저장하므로을 호출 할 때마다 재설정 할 필요가 없습니다 pdfView.document = ....

불행히도 PDFThumbnailView사용자가 주석을 추가하려고 할 때 iOS 12에서 충돌이 발생했습니다 . 어떤 경우에는 사용자가 충돌없이 한 시간 동안 그림을 그릴 수 있었지만 다른 경우에는 첫 번째 주석 이후에 앱이 충돌했습니다. iOS 13에서 수정 된 것 같습니다.

터치 이벤트 수신

처음에는 사용자 입력을 구현하기 위해 투명도 UIViewpdfView's하위보기 로 추가하려고했습니다 . 투명한 뷰는 터치 이벤트를받지 않는다는 것을 알았습니다.

그런 다음 알파를 view터치를 받기 위한 최소 알파인 0.01로 설정 합니다. 그러나 여전히 확대 / 축소 및 스크롤 제스처에 문제가있었습니다. 투명 뷰가 한 손가락 제스처를 받고 다른 (두 손가락) 제스처를 상위 뷰 ( pdfView)로 전달하기를 원했습니다. 마침내이 접근 방식이 효과가 없다는 것을 알게되었습니다.

그래서 마지막으로 제 자신 만의 제스처 인식기를 구현했습니다. 한 손가락 제스처에서만 작동하며 제스처에 여러 손가락이있는 경우 실패합니다. 다음은 코드입니다.

먼저, touchesBegan받은 터치를 분석하는 방법을 구현 했습니다.

Apple Pencil과 한 손가락 제스처 인 경우 제스처 인식기 상태를로 설정하고 .began터치 이벤트를 계속 수신합니다. Pencil이 코드가 iOS Simulator에서 작동하도록 줄에 주석을 달 수 있습니다. Pencil불행히도 Mac에서는 시뮬레이션 할 수 없습니다 . 멀티 터치 제스처 인 경우 제스처 인식기 상태를로 설정 .failed하여 다른 pdfView's제스처 인식기가 작동하도록합니다.

에서 touchesBegan나는 또한 새로운 터치 이벤트를 받았음을 대리인에게 알립니다 PDFAnnotation. 이 touchesMoved방법 에서는 새 터치 위치에 대해서만 대리인에게 알립니다.

마지막으로 touchesEnded메서드에서 최종 터치 위치를 보내고 대리인에게 터치가 종료되었음을 알립니다.

프로토콜 참조는 다음과 같습니다. 매우 간단합니다.

PDFView이 코드를 ViewControllerviewDidLoad메소드에 추가하여이 제스처 인식기를 추가하십시오 .

통합 및 예측 터치

UIEvent제스처 인식기의 각 터치 이벤트 ( )에도 통합 및 예측 터치 배열이 있습니다. 장치 하드웨어가 다르고 성능을 향상시키기 위해 일부 터치 이벤트 만 제스처 인식기에 실시간으로 수신됩니다.

앱에 더 나은 정밀도가 필요한 경우 UIEventcoalescedTouches속성 을 확인하여 모든 터치에 액세스 할 수 있습니다 . iOS는 또한 사용자의 손가락이나 연필 움직임을 예측하고 predictedTouchesUIEvent.

Apple의 문서에 따르면 coalescedTouches그리기에 저장 하고 사용할 수 있습니다 . 그러나 predictedTouches다음 터치 이벤트를받을 때 사용 된 도면 데이터를 제거해야합니다 .

제스처 인식기의 대리자에게 현재 터치의 위치뿐만 아니라 통합 및 예측 터치의 배열도 전달하려고했습니다. 그러나 이로 인해 성능 문제가 발생했기 때문에 최종 구현에서 정기적 인 터치 이벤트 만 유지하기로 결정하고 통합 및 예측 터치를 건너 뛰었습니다. 우리의 경우에는 결국 사용자 경험에 영향을 미치지 않았습니다.

그리기 구현

제스처 인식기를 만든 후에는 그리기를 구현해야합니다. 단일 책임 원칙을 따르고 드로잉을 처리 할 다른 클래스를 만들려고합니다. 그것을 부르 자 PDFDrawer:

우리는 PDFDrawer에 대한 참조를 보유해야 PDFView우리가 우리의 주석을 추가하는에를. Path모든 터치 이벤트를 단일 제스처로 저장하는 데 사용됩니다. CurrentAnnotation사용자가 Apple Pencil을 이동할 때마다 경로에 새 포인트를 추가하므로이를 업데이트해야하므로 참조도 필요합니다. CurrentPage사용자가 한 페이지에서 제스처를 시작한 후 실수로 다음 페이지로 이동하는 경우에도 필요합니다.

여기서 어떻게 PDFDocument작동 하는지 설명하겠습니다 . 문서에는 PDFPage클래스 가 나타내는 페이지가 포함되어 있습니다 . 각 페이지에는 자체 클래스 주석이 포함되어 있습니다 PDFAnnotation. 따라서 사용자가 한 페이지에서 제스처를 시작하고 다른 페이지로 터치를 이동하는 경우 초기 페이지에 계속 그림을 그려야합니다.

PDFDrawer제스처 인식기의 대리자이므로 세 가지 DrawingGestureRecognizerDelegate프로토콜 방법을 구현해야 합니다. 먼저 사용자의 첫 번째 터치에서 호출됩니다.

여기에 현재 페이지 참조를 저장 UIBezierPath하고 새 도면에 대해 새로 작성합니다.

사용자가 손가락이나 연필을 움직이면 다음 메서드가 호출됩니다.

현재 페이지 참조가 있는지 확인하고 경로에 새 지점을 추가하고 도면 주석을 추가합니다. drawAnnotation나중에 메서드 구현 을 제공 할 것 입니다.

사용자가 터치를 마치면 정확히 동일한 코드가 호출됩니다. 또는 적어도이 튜토리얼 단계에서는 동일하지만 나중에 지우개를 구현하기 위해 특정 코드를 추가 할 것입니다.

쉽죠? 처음에는 시작, 이동 및 종료 이벤트에서 하나의 주석을 재사용하려고했습니다. 메서드를 UIBezierPath호출하여 매번 현재 주석에 새 주석을 추가했습니다 . 그러나 앱이 프레임 워크 내부 어딘가에서 충돌하는 것을 발견했습니다 ..add(UIBezierPath)PDFAnnotationPDFKit

따라서 .add하나의 주석 을 호출 하는 대신 터치가 움직일 때마다 새 주석을 만들고 이전 주석을 제거합니다. 이 (추가 및 제거)는 iOS 시뮬레이터에서 볼 수 있지만 실제 iPad에서는 원활하게 작동합니다. 이것은 충돌을 방지하는 유일한 해결책입니다.

주석을 만드는 코드는 다음과 같습니다.

이 솔루션을 사용해야하는 또 다른 문제는 클라이언트의 기능 요청입니다. 그들은 풀 컬러로 그리는 것뿐만 아니라 투명한 컬러 (알파 성분 포함)로 그릴 수 있기를 원했습니다.

PDFAnnotation경로가 거의없는 기본값 은 정말 나빴습니다. 투명 경로가 거의 없기 때문에 한 경로가 끝나고 다른 경로가 시작되는 지점이 교차로 인해 선보다 밝았습니다. 모든 그리기는 내부 PDFKit에서 수행되기 때문에 변경할 수 없습니다. 이것이 우리가 대신 솔루션을 사용한 이유입니다.

마지막으로 작업을 수행하려면 다음에서 PDFDrawer속성 을 만들어야합니다 ViewController.

예를 들어 다음과 같은 설정 호출을 viewDidLoad메서드에 추가합니다.

긴 압력 제스처 인식기

위에서 언급했듯이 PDFView에는 많은 내장 제스처 인식기가 포함되어 있습니다. 드문 경우지만 사용자 지정 제스처 인식기가 동작을 복사하는 데 사용되는 내장 된 긴 압력 제스처 인식기와 충돌했습니다. PDFView비활성화 할 설정을 찾지 못 했으므로이 하위 클래스를 구현해야했습니다 PDFView.

사용자 정의 PDFAnnotations

사용자 지정 PDFAnnotations도 실험했습니다. 위에서 언급했듯이 새 터치 이벤트를받은 후 새 PDFAnnotation을 제거하고 추가해야했습니다.

그리기 성능을 개선하기 위해 제스처 시작부터 끝까지 단일 주석을 사용하려고했습니다. 그래서 우리는 경로 속성과 재정의 된 draw()메서드가 있는 사용자 지정 주석 클래스를 만들었습니다 .

그러나 경로를 수정할 때마다 PDFKit이 주석을 다시 그리도록 강제 할 수 없었습니다. 그 이유 PDFAnnotation는 경로 가 아니며 이에 UIView대한 메서드가 없기 때문 입니다.

마지막으로 새 터치 이벤트를 수신 할 때마다 페이지에서 동일한 주석을 추가하고 제거하기로 결정했습니다 (새로 작성하지 않음). 사용자 지정 draw방법을 통해 시각적 그리기 성능을 크게 향상시켜 사용자 경험을 크게 개선했습니다. draw()방법 은 다음과 같습니다 .

드로잉 아티팩트를 제거하기 위해 draw()메서드에 몇 가지 추가 설정을 추가합니다 .

이 메서드를 스레드로부터 안전하게 만들기 위해 메서드 내부에서 path를 복사본으로 대체했습니다 draw().

사용자 지정 주석 및 성능

첫 번째 릴리스 이후 다른 성능 문제를 발견했습니다. 사용자는 맞춤형 주석을 사용하여 PDF 문서를 스크롤 및 확대 / 축소 할 때 멈춤을 경험했습니다. 일부는 메모리 경고로 인해 충돌하기도했습니다. 심지어 시스템 내 앱과 타사 앱에서도 마찬가지입니다.

이 문제를 해결하고 훌륭한 드로잉 경험을 유지하기 위해 사용자가 그리는 동안 사용자 지정 주석을 사용 PDFAnnotation하고 사용자가 터치를 마친 직후에 시스템으로 대체하기로 결정 했습니다. 또한 새로운 경계 계산 메커니즘을 추가하여 최종 주석의 경계 크기를 최소화하고 메모리 사용량을 줄이고 그리기 성능을 개선했습니다. 그리기 과정에서 사용자 지정 주석의 경계로 전체 페이지 크기를 사용했음을 상기시켜 드릴 것입니다. 사용자는 전체 페이지를 빠르고 원활하게 그릴 수 있습니다.

업데이트 된 PDFDrawer gestureRecognizerEnded방법 :

이 방법에서는 여전히 경로를 업데이트 한 다음 이전 (사용자 지정) 주석을 제거하고 최종 (시스템) 주석을 생성합니다.

그게 다야. 그리기 및 미리보기 중 뛰어난 성능. UIBezierPath+.swift내 코드를 사용하려는 경우 프로젝트에 파일 을 추가하는 것을 잊지 마십시오 . 이 튜토리얼의 하단에있는 샘플 코드에서 찾을 수 있습니다.

지우개

지우개 도구도 구현해야했습니다. 그러나에서 annotation(at:CGPoint)제공 하는 메서드는 PDFPage주석의 경계를 기반으로하고 사용자가 bounds 내부의 공백을 탭하더라도 주석을 반환하기 때문에 사용할 수 없습니다 rect.

우리의 경우 (사용자가 Apple Pencil로 텍스트를 쓸 수있는 경우) 사용자가 예상 한대로 작동하지 않았습니다. 우리는 경로의 경계가 아니라 경로가 탭되었는지 확인해야했습니다. 먼저, 주석의 최종 경로 인 PDFAnnotation계산 가능한 hitPath속성 (나중에 자세히 설명)을 사용하여 하위 클래스를 구현했습니다 . 성능상의 이유로 경로가 완료된 경우에만 계산됩니다.

PDFDocument이전에 생성 된 클래스의 모든 주석 PDFAnnotationWithPath이 정규화 되었기 때문에이 접근 방식은 닫았다가 다시 연 후에 작동하지 않는다는 것을 발견했습니다 PDFAnnotation. 그래서 우리는 약간의 성능을 희생하고 대신 확장을 만들어야했습니다 subclass.

여기서의 paths속성을 사용하고 있다는 점에 유의하는 것이 중요합니다 PDFAnnotation. 최종 경로를 거기에 넣어야합니다. 또한 사용자가 지우개로 경로를 더 쉽게 칠 수 있도록 모든 선의 기본 스트로크 너비로 10 포인트를 사용하고 있습니다.

메서드 PDFPage를 호출하는 확장 도 구현했습니다 contains.

마지막으로 PDFDrawer지우개 도구를 지원하기 위해 메서드 를 업데이트해야합니다 .

DrawingTool우리가 가지고있는 모든 그리기 도구를 포함하는 열거 형입니다. 사용자는 UI에서 선 너비와 색상이 다른 다양한 도구를 선택할 수 있습니다. 우리는 그것을의 속성으로 저장하고 PDFDrawer있습니다.

gestureRecognizerEnded메서드에 동일한 변경 사항을 적용합니다 .

removeAnnotationAtPoint구현은 다음과 같습니다 .

문서 저장

PDFDocument필요한 모든 주석을 추가 한 후 저장하는 것도 매우 쉽습니다.

다음은 앱의 최종보기입니다.

샘플 코드

데모 프로젝트의 경우 저장소확인하십시오 .

의도적으로 라이브러리 나 프레임 워크를 만들지 않았습니다. 대부분 PDF로 작업 할 모든 앱 / 프로젝트가 매우 다르고 많은 사용자 지정이 필요하기 때문입니다. 자, 여러분 자신의 프로젝트를위한 기반으로 제 샘플 코드를 자유롭게 사용하십시오.

계속 읽기

PDFKit작업 PencilKit, 텍스트 주석 추가 및 대체 솔루션을 포함하여 에 대한 자세한 내용을 보려면 두 번째 부분을 계속하십시오 .

Suggested posts

N + 1 문제 선택

모든 것은 비용이 있습니다.

N + 1 문제 선택

개발자의 경우 ORM (Object-relational mapping)을 사용하면 삶을 상당히 단순화 할 수 있지만 알아야 할 자체 캐치 세트가 있습니다. 더 심각한 문제 중 하나는 Select N + 1 문제입니다.

Vue 3 및 JavaScript로 날씨 앱 만들기

Vue 3 및 JavaScript로 날씨 앱 만들기

Vue 3는 프런트 엔드 앱을 만들 수있는 사용하기 쉬운 Vue JavaScript 프레임 워크의 최신 버전입니다. 이 기사에서는 Vue 3 및 JavaScript로 날씨 앱을 만드는 방법을 살펴 보겠습니다.

Related posts

Swift— 프로토콜은 어떻게 작동합니까?

프로토콜과 위임은 Swift의 기본 개념입니다. 작동 방식과 사용 방법에 대해 알아 보려면이 빠른 가이드를 읽어보세요.

Swift— 프로토콜은 어떻게 작동합니까?

프로토콜은 Swift에서 매우 간단하지만 기본적인 개념입니다. 프로토콜은 코드 계약과 같습니다.

Mac App Store에없는 Mac에서 iOS / iPadOS 앱을 실행하는 방법

Mac App Store에없는 Mac에서 iOS / iPadOS 앱을 실행하는 방법

M1 칩을 실행하는 Mac이있는 경우 Mac App Store에 나열된 iOS / iPadOS 앱만 실행할 수 있습니다. 하지만 Mac App Store에 나열되지 않은 iOS / iPadOS 앱은 어떻습니까? 공식적으로는 할 수 없습니다.

더 접근하기 쉬운 오디오 플레이어

실명 및 시각 장애가있는 사람들을위한 공감력으로 구축하는 법 배우기

더 접근하기 쉬운 오디오 플레이어

작성자 : Francis Mariano, iOS 엔지니어 Headspace의 대담한 사명 선언문 인 세상의 건강과 행복을 개선합니다. 우리가 그곳에 사는 사람들의 다양한 경험을 인식하게 만드는 것.

SwiftUI를 사용하여 원 안에 텍스트를 표시하는 방법

SwiftUI를 사용하여 원 안에 텍스트를 표시하는 방법

이번에는 SwiftUI를 사용하여 원 안에 텍스트 레이블을 표시하는 몇 가지 방법을 살펴 보겠습니다. ZStack보기를 사용하여 세 가지 다른 방법을 자세히 살펴 보겠습니다.