PDFKit Programming Guide - PDF Kit Tasks


PDF Kit Tasks

https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/PDFKitGuide/PDFKit_Prog_Tasks/PDFKit_Prog_Tasks.html

PDFView 구현하기

대부분의 개발자는 자체 뷰에 단순히 PDF정보를 표시하고 싶어할것이다. PDFView는 해당 상황에 맞춰준다.

PDFView 사용자 인터페이스 엘리먼트는 인터페이스 빌더에서 가용하므로 PDF 내용을 표시할 때 어플리케이션에서 어디든 사용할 수 있다. PDFView 를 보이게 하려면 /Developer/Extras/Palettes/PDFKit.palette 내의 PDFKit 팔레트를 추가한다. 

To add the PDFKit palette in Interface Builder, select the Palettes tab in the Preferences panel. Click the Add… button, navigate to the /Developer/Extras/Palettes folder, and select the PDFKit palette. Next, select the Customize Toolbar menu item in the Tools/Palettes menu and drag the PDFKit palette to the toolbar to make it visible. 


PDFView를 nib파일에 추가한 후 어플리케이션에서 PDFDocument 메소드를 호출함으로서 문서를 추가한다.
다음 코드를 사용할 수 있다.

PDFDocument* pdfDoc;
pdfDoc = [[PDFDocument alloc] initWithURL:[NSURL fileURLWithPath:[self fileName]]];
[_pdfView setDocument:pdfDoc];

PDF 데이터가 다른 형태로 저장되어 있다면 PDFDocument를 initWithData 를 사용할 수 있다. 사용자는 또한 드래그앤 드랍할 수 도 있다.

PDFView 다룸으로서 PDF문서를 보여주고 네비게이트하는 기본적 기능은 모두 지원된다. 단순히 스크롤하고 링크는 자동적으로 작동한다. 그리고 컨텍스츄얼 메뉴를 얻어 확대하고 네비게이션할수 있다.

프리뷰에서의 PDF킷

OS X v10.4에서 사용하는 프리뷰는 PDF 킷을 사용한다. 그러므로 이 어플리케이션을 사용하여 자체 PDF뷰를 통해 가능한 것을 확인할 수 있다. PDF킷내의 다양한 메소드는 프리뷰내에서 비교할만한 메뉴아이템이다. 표 2-1은 다양한 메뉴 아이템과 이들 API를 나타낸다.

표 2-1 프리뷰 메뉴 아이템과 PDF킷 메소드
PDF Display - PDFView: - setDisplayMode: setDisplayBox:
Zoom In PDFView: - zoomIn:
Zoom Out PDFView: - zoomOut:
Go
Next PDFView: goToNextPage:
Prev PDFView: goToPreviousPage:
Goto Page PDFView: goToPage:
First PDFView: goToFirstPage:
Last PDFView: goToLastPage:
Back PDFView: goBack:
Forward PDFView: goForward:
Toos
Rotate Left PDFPage: setRotation, rotation
Rotate Right PDFPage: setRotation rotation +90
Annotation initWithBounds 

아웃라인 생성하기

많은 PDF문서가 아웃라인을 포함하므로 어플리케이션은 이 정보 또한 보여주기를 원할 것이다.

몇몇 PDF문서는 아웃라인 정보를 담을 수 없으므로 사용자 인터페이스에 선택적인 요소로 아웃라인을 표시해야 할 것이다. 예를 들어, 아웃라인을 드로어에 표시하며 필요치 않을 땐 닫게한다.

NSOutlineView 클래스를 사용하여 PDF아웃라인을 표시할 수 있다. 이 클래스의 인스턴스는 자동적으로 아웃라인 계층도를 표시하고 적절한 PDF페이지로의 링크를 표시한다. 리스팅 2-1은 어떻게 할 수 있는지 보여준다.

리스팅 2-1 PDF아웃라인 정보 로드하기
_outline = [[_pdfView document] outlineRoot];
if (_outline) {
 [_noOutlineText removeFromSuperview];
 _noOutlineText = nil;
 [_outlineView reloadData];
else {
 [[_outlineView enclosingScrollView] removeFromSuperview];
 _outlineView = nil;
}

어떻게 코드가 동작하는 지는 다음과 같다.
1. 루트 아웃라인 엘리먼트를 얻는다. _outline 은 PDFOutline의 인스턴스이다.
2. 만약 루트 아웃라인이 있다면, "No Outline" 텍스트를 제거한다.
3. PDF아웃라인 정보를 아웃라인 뷰에 로드한다. 아웃라인 뷰는 델리게이트 메소드를 호출하여 아웃라인 계층 내에서 엘리먼트를 확인한다.
4. 만약 루트 아웃라인이 존재하지 않는다면 아웃라인 뷰를 제거하고, 플레이스 홀더 텍스트를 제거한다.

reloadData 메소드를 호출하면 아웃라인 뷰는 다양한 데이터 소스 델리게이트를 호출하여 아웃라인을 가져온다. 이들 델리게이트메소드는 NSOutlineViewDataSource 프로토콜에 정의되어 있다. 어플리케이션은 반드시 이들 메소드를 구현하여 적절한 PDF데이터가 아웃라인에 추가되게 한다.

리스팅 2-2 는 자식의 갯수를 ㅇㄷ는 메소드를 보여주는데, 아웃라인 메소드인 numberOfChildren으로 수행한다. 만약 아이템 파라미터가 널이면 이 메소드는 루트 아웃라인의 자식의 갯수를 반환한다.

리스팅 2-2 자식의 갯수를 감지하는 델리게이트 메소드
- (int) outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item {
 if (item == null) {
    if (_outline) return [_outline numberOfChildren];
    return 0;
 }
 else {
    return [(PDFOutline*)item numberOfChildren];
}

리스팅 2-3은 PDFOutline 메소드인 childAtIndex 를 호출하여 특정 자식 아웃라인을 얻는 델리게이트 메소드를 보여준다. 만약 아이템 파라미터가 널이면 메소드는 루트 아웃라인의 자식을 반환한다.

리스팅 2-3 자식 엘리먼트를 얻는 델리게이트 메소드
- (id)outlineView:(NSOutlineView*)outlineView child:(int)index ofItem:(id)item {
 if (!item) {
    if (_outline) return [_outline childAtIndex:index];
    return null;
 }
 return [(PDFOutline*)item childAtIndex:index];
}

리스팅 2-4 아웃라인 엘리먼트가 확장가능한지를 확인하는 델리게이트 메소드를 보여준다.
리스팅 2-4 엘리먼트가 자식을 가지는지를 확인하는 델리게이트 메소드
- (BOOL) outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item {
  if (!item) {
    if (_outline) return ([_outline numberOfChildren] > 0);
    return NO;
  }
  return ([(PDFOutline*)item numberOfChildren]>0);
}

리스팅 2-5 는 shows a delegate method for obtaining an outline element’s label, which calls the PDFOutline method label. The label is simply the string that is displayed in the outline view (for example, a chapter title).

Listing 2-5  Delegate method for obtaining an element’s contents

- (id) outlineView: (NSOutlineView *) outlineView
        objectValueForTableColumn: (NSTableColumn *) tableColumn
        byItem: (id) item
{
    return [(PDFOutline *)item label];
}

When the user selects an outline element, your application should update the PDF display to show the page corresponding to that element. The simplest way to do so is to call the PDFView’s goToDestination method, as shown in Listing 2-6

Listing 2-6  Displaying the page associated with an outline element

- (IBAction) takeDestinationFromOutline: (id) sender
{
    [_pdfView goToDestination: [[sender itemAtRow:
                                [sender selectedRow]] destination]];
}

In addition, if the user scrolls or otherwise moves through the document, your application should update the outline to highlight the outline element that corresponds to the currently displayed page. You can do so by installing a notification handler to be called each time the page changes (that is, when PDFViewPageChangedNotification is posted). Listing 2-7 hows how you might do so. 

Listing 2-7  Updating the outline when the page changes

- (void) pageChanged: (NSNotification *) notification
{
    unsigned int    newPageIndex;
    int             numRows;
    int             i;
    int             newlySelectedRow;
 
    if ([[_pdfView document] outlineRoot] == NULL)// 1
        return;
 
    newPageIndex = [[_pdfView document] indexForPage: // 2
                                            [_pdfView currentPage]];
 
    // Walk outline view looking for best firstpage number match.
    newlySelectedRow = -1;
    numRows = [_outlineView numberOfRows];
    for (i = 0; i < numRows; i++)// 3
    {
        PDFOutline  *outlineItem;
 
        // Get the destination of the given row....
        outlineItem = (PDFOutline *)[_outlineView itemAtRow: i];
 
        if ([[_pdfView document] indexForPage:
                    [[outlineItem destination] page]] == newPageIndex)
        {
            newlySelectedRow = i;
            [_outlineView selectRow: newlySelectedRow
                                        byExtendingSelection: NO];
            break;
        }
        else if ([[_pdfView document] indexForPage:
                    [[outlineItem destination] page]] > newPageIndex)
        {
            newlySelectedRow = i - 1;
            [_outlineView selectRow: newlySelectedRow
                                        byExtendingSelection: NO];
            break;
        }
    }
 
    if (newlySelectedRow != -1)// 4
        [_outlineView scrollRowToVisible: newlySelectedRow];
}

Here is how the code works:

  1. Checks to see if a root outline exists. If not, then there is no outline to update, so simply return. 

  2. Obtains the index value for the current page. The PDFView method currentPage returns the PDFPage object, and the PDFDocument method indexForPage returns the actual index for that page. This index value is zero-based, so it doesn’t necessarily correspond to a page number. 

  3. Iterate through each visible element in the outline, checking to see if one of the following occurs:

    • The index of an outline element matches the index of the new page. If so, highlight this element (using the NSTableView method selectRow:byExtendingSelection).

    • The index of the outline element is larger than the index of the page. If so, a match was not possible as the index corresponds to a hidden child of a visible element. In this case, use selectRow to highlight the parent outline element (the current row -1 ). 

  4. Call the NSTableView method scrollRowToVisible to adjust the outline view (if necessary) to make the highlighted element visible. 

Searching a PDF Document

Users often need to search through a PDF document. PDF Kit offers two methods for doing so: 

  • Searching string-by-string through the document. That is, when the user searches for string, PDF Kit returns the first occurrence of the string. Additional searches (“Find Again”) return successive instances of string. This search method is synchronous.

  • Obtaining a listing of all occurrences of string in a document. This search method may be synchronous or asynchronous. 

For simple string-by-string searching, your application can simply call the PDFDocument method findString:fromSelection:withOptions:

- (PDFSelection *) findString:(NSString *)string
         fromSelection:PDFSelection *selection:withOptions:(int)options

To display the selection returned, you can call the PDFView method setCurrentSelection, which highlights the selection, followed by scrollSelectionToVisible

You can specify the following options:

  • NSCaseInsensitiveSearch: Ignore case when making a match.

  • NSLiteralSearch: Search for contiguous words, separated by spaces. 

  • NSBackwardsSearch: Search backwards from the current selection. 

By passing NULL for the selection, you can begin the search from the beginning (or end) of the document. By passing the most recent match for the selection, you can implement “Find Again” behavior. If the findString call returns NULL, that means that either the string was not found, or the search reached the end (or beginning) of the document. 

To obtain all the occurrences of a given string, you can use either of the following PDFDocument methods:

  • findString:withOptions: to synchronously obtain an NSArray object holding all the matches

  • beginFindString:withOptions: to asynchronously begin a search for all occurrences. PDF Kit calls your delegate method each time a match is found. 

Unless you are sure that the search will be brief, you should choose to use beginFindString:withOptions:

As this is an asynchronous search, PDFDocument includes two other useful find-related methods:

  • isFinding to determine if a search in currently in progress

  • cancelFindString to terminate a current search. 

Listing 2-8 shows how you might initiate a search:

Listing 2-8  Beginning an asynchronous search

- (void) doFind: (id) sender
{
    if ([[_pdfView document] isFinding])// 1
        [[_pdfView document] cancelFindString];
 
    if (_searchResults == NULL)// 2
        _searchResults = [NSMutableArray arrayWithCapacity: 10];
 
    [[_pdfView document] beginFindString: [sender stringValue] // 3
                withOptions: NSCaseInsensitiveSearch];
}

Here is how the code works:

  1. Cancels any current searches. 

  2. Allocates a mutable array to hold the search results if one does not already exist. 

  3. Calls the PDFDocument method beginFindString:withOptions: with the desired search string.

During the search, PDF Kit sends out notifications that your application can react to:

  • PDFDocumentDidBeginFindNotification

  • PDFDocumentDidEndFindNotification

  • PDFDocumentDidBeginPageFindNotification

  • PDFDocumentDidEndPageFindNotification

  • PDFDocumentDidFindMatchNotification

The first two notifications are sent when the search starts, or finishes a search. You can use these notifications to set up and remove progress bars or any other initializations.

The begin and end page notifications are sent when the search begins or ends searching a page in the document. You can use these notifications to update a progress bar or page counter. 

The find match notification is sent whenever a match is found for the search string. Typically you will want to obtain the string selection and store it in an array for later display. However, in most cases it may be easier to use the PDFDocument delegate method didMatchString, which automatically passes you the matching selection. Listing 2-9 shows how you might implement a delegate to take each matching string and add it to an array of search results in an NSTableView. 

Listing 2-9  Adding search results to a table view

- (void) didMatchString: (PDFSelection *) instance
{
    // Add page label to our array.
    [_searchResults addObject: [instance copy]];
 
    // Force a reload.
    [_searchTable reloadData];
}

Here _searchResults is an instance of NSMutableArray, and _searchTable is an instance of NSTableView. 

To make sure that the NSTableView displays the search results correctly, you need to implement delegate data source methods (similar to those required for NSOutlineView). These delegate methods are defined in the NSTableDataSource protocol. 

Listing 2-10 shows a delegate method for determining the number of rows in the table view. This method simply obtains the number of items in the search results by calling the NSMutableArray method count.

Listing 2-10  Determining the number of rows in the table

- (int) numberOfRowsInTableView: (NSTableView *) aTableView
{
    return ([_searchResults count]);
}

Listing 2-11 shows a delegate data source method for obtaining the value of a particular column.

Listing 2-11  Obtaining the value for a column

- (id) tableView: (NSTableView *) aTableView objectValueForTableColumn:
            (NSTableColumn *) theColumn
            row: (int) rowIndex
{
    if ([[theColumn identifier] isEqualToString: @"page"])
        return ([[[[_searchResults objectAtIndex: rowIndex] pages]
                     objectAtIndex: 0] label]);
    else if ([[theColumn identifier] isEqualToString: @"section"])
    {
        NSString    *label = [[[_pdfView document] outlineItemForSelection:
                         [_searchResults objectAtIndex: rowIndex]] label];
        return label;
    }
    else
        return NULL;
}

The table view calls this method whenever it needs to determine the value of a particular table element (such as when preparing the table for display). 

The search results table in this example contains only two columns: the page containing the hit and the outline section that contains the hit. (In a more sophisticated example, you may want to display a portion of the text containing the matching string, as Preview does.) You reference these columns using the identifier tags you specified for them in the NSTableColumn inspector window in Interface Builder (as shown in Figure 2-1).

Figure 2-1  Assigning column identifiers in Interface Builder

Assigning column identifiers in Interface Builder


For this example: 

  • If the column identifier is “page,” return the page number of the hit by calling the PDFSelection method pages.

  • If the column identifier is “section,” return the outline label for the selection by calling the PDFDocument method outlineItemForSelection.

When the user selects an item in the search results, your application should display the corresponding page. Listing 2-12 shows how you might do so using an NSTableView notification. 

Listing 2-12  Handling a selection in the table

- (void) tableViewSelectionDidChange: (NSNotification *) notification
{
    int rowIndex;
 
    // What was selected.  Skip out if the row has not changed.
    rowIndex = [(NSTableView *)[notification object] selectedRow];// 1
    if (rowIndex >= 0)
    {
        [_pdfView setCurrentSelection: // 2
                    [_searchResults objectAtIndex: rowIndex]];
        [_pdfView scrollSelectionToVisible: self];// 3
    }
}

A table view sends the NSTableViewSelectionDidChangeNotification when an item is selected, and calls your delegate method tableViewSelectionDidChange. Here is how the code works:

  1. Checks to see if the selection is valid. 

  2. Sets the current selection to the one that the user clicked.

  3. Updates the PDF view to show the page containing the selection. 

For more information about implementing these delegate methods, see Table View Programming Guide for Mac in Cocoa User Experience Documentation. 


PDFKit Programming Guide - PDF 킷 컨셉


PDF 킷 컨셉


https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/PDFKitGuide/PDFKit_Prog_Conc/PDFKit_Prog_Conc.html

이 챕터는 PDF컨셉과 PDF킷 클래스 오버뷰를 제공한다. PDF의 요소에 익숙하다면 PDF기본을 건너뛰고 PDF킷 클래스로 갈 수 있다.

PDF 기본

PDF는 어도비의 포터블 문서 포맷에서 사용하는 문서이다. PDF스펙은 포스트스크립트 드로잉 언어를 기반으로 되어있어 대부분의 텍스트와 이미지의 조합은 물론 인터렉티브 엘리먼트까지 조합할 수 있다.

PDF의 기본 빌딩 블록은 다큐먼트 그 자체이다. 다큐먼트 내에서 다양한 페이지를 가지고 아웃라인을 가질 수 있다. 페이지에는 텍스트와 어노테이션등이 있을 수 있다.

PDF포맷에대한 더 자세한 사항은 PDF스펙을 살펴본다. 다음으로 부터 다운로드할 수 있다.

단순히 PDF문서를 보여주기를 원한다면 PDF스펙에서 제공하는 세부사항은 알 필요가 없다.

문서

PDF의 기본 빌딩블록은 다큐먼트 자체이다. 다큐먼트는 전형적으로 파일로서 디스크에 저장된다.
다큐먼트는 버저닝을 지원하고, 작성자, 생성 날짜와 같은 메타데이터로 태그 할 수 있다.

다큐먼트는 암호화되어 보려면 비밀번호를 필요로 하게 할 수 있다. 암호화에는 두 레벨이 존재한다.
- 사용자 레벨 암호화: 만약 사용자가 성공적으로 사용자 레벨 퍼미션을 취득하면 이들은 다큐먼트를 볼 수 있지만 인쇄나 문서 복사는 제한된다.
- 작성자 레벨 암호화: 오너 레벨 퍼미션을 가지는 사용자는 문서를 볼 수 있고 모든 사용 퍼미션을 갖는다.

다양한 암호화된 PDF문서는 더미 사용자 암호를 가지는데 빈 문자열 이다. 대부분의 PDF다큐먼트 파서는 자동적으로 빈 문자열로 시도하고 만약 이것이 성공하면 단순히 문서를 보여준다. 그러므로 기술적으로 암호화된 문서는 암호를 위한 사용자 입력이 필요치 않을 수 있다.

페이지

PDF다큐먼트는 여러 페이지로 구성된다. 이들은 물리적인 책 내의 페이지와 개념적으로 동일하다. 그리고 이들은 사용자가 스크린상에서 보는 것이다. 그러므로 물리적 페이지와는 달리 PDF페이지는 파이퍼링크와 어노테이션을 가질 수 있다. 페이지는 크로핑이 가능하고 이 것은 표시중에 등록 마크와 같은 추가적인 요소를 감추게 할 수 있다.

페이지의 대부분의 객체는 뷰 공간보다는 페이지 공간 내에 지정된다. 그러므로 좌표 시스템은 포인트 (인치당 72이 포인트) 로 나타내며 페이지의 아래 왼쪽을 원점으로 하며 뷰가 기준이 아니다. 페이지 공간은 줌, 디스플레이 모드, 등과 같은 것을 신경쓰지 않는다. 아이템은 32포인트 사각형을 가진다면 디스플레이 사이즈에 상관없이 이 바운드가 유지된다. 그림 1-1은 두 좌표 시스템을 비교한다.

View space versus page space
그림 101 뷰 공간 대 페이지 공간

PDFView클래스는 뷰 공간에서 페이지 공간으로 변환하는데 다양한 컨버젼 메소드를 포함한다.

아웃라인

아웃라인은 컨텐트의 인터렉티브 테이블과 같은 것으로서, 챕터나 문서 구조 계층을 보여준ㄴ다. 아웃라인은 문서의 구조를 사용자에게 쉽게 보여주고 특정 위치로 이동할 수 있게 한다.

그림 1-2 PDF문서를 위한 아웃라인
An outline for a PDF document

모든 PDF문서가 아웃라인을 가지는 것은 아니다.

어노테이션

어노테이션은 PDF에서 볼 수 있는  "추가적" 엘리먼트로서 표준 텍스트와 이미지에 추가된다. 몇몇 어노테이션은 라인, 서클, 과 같은 다른 사용자가 인터펙티브 작용에서 추가할 수 있는 시각적 기능을 의미한다.

어노테이션의 몇몇 예시는 다음을 포함한다.
- "스티키 노트" 텍스트 표시하기
- 노트 아이콘으로서 이 위에 클릭하면 텍스트를 표시한다.
- 사용자 텍스트를 입력받는 편집가능 텍스트 필드
- 버튼, 체크박스와 같은. 이런 어노테이션은 편집가능 텍스트 필드와 함께 사용자로부터 채워질 폼에서 유용할 수 있다.
- 서클, 임의 라인, 그리고 박스
- 다른 문서로의 링크, 또는 문서의 다른 섹션으로의 링크
- 하이라이팅, 스트라이크 스루, 그리고 다른 텍스트 마크업

그림 1-3 은 PDF킷 내에서 가용한 어노테이션 형식을 보여준다.

그림 1-3 PDF킷에서 가용한 몇몇 어노테이션
Some annotations available in PDF Kit

PDF킷이 지원하는 그리고 문서에 보여줄 수 있는 어노테이션이 있다. 그러나, PDF 킷은 어피어런스 스트림을 사용하는 것으로 지정하면 추가적인 어노테이션 형식을 지원한다. 어피어런스 스트림은 특정 어노테이션 형식을 기반으로 한 스펙보다는 드로잉 시퀀스를 기반으로 그리기ㅣ를 수행하게 하는 것이다. 예를 들어, 20포인트 반지름으로 원을 지정하는 것보다 어피어런스 스트림은 그 사이즈의 서클을 드로잉하기 위한 명령어를 갖는다.

어노테이션은 종종 사용자가 표시할 수 있는 연관된 컨텐츠를 가질 수 있다. 예를 들어, 텍스트 어노테이션은 전형적으로 PDF에 아이콘을 보여준다. 사용자가 그 위에 클릭하면 이 텍스트를 표시하기 위한 윈도우가 보여진다. 

PDF킷은 어노테이션 컨텐트를 표시하기 위한 메커니즘을 제공하지 않는다. 어플리케이션은 어노테이션 상에 사용자가 클릭할 때 컨텐트를 보여주는 윈도우를 생성해야 한다.

일러두기: 현재, 링크 어노테이션은 예외로 하고 모든 어노테이션은 PDF킷을 사용하여 생성한것은 저장후에 수정할 수 없다.

셀렉션

PDF다큐먼트는 워드 프로세싱 어플리케이션과 같이 사용자가 텍스트 블록을 선택할 수 있게 한다. 그러나, 이들은 텍스트 셀렉션에 더 많은 자유도를 제공하는데 반드시 선형적으로 연속이 아니어도 된다는 점이다. 예를 들어, PDF 킷을 사용하여 사용자는 페이지 내의 텍스트 블록을 선택하지만 이들이 연속일 필요는 없다. 그림 1-4에서 보여진 것처럼 이런 셀렉션은 다중 컬럼 페이지, 테이블 또는 다른 특이한 포매팅을 갖는 문서에 유용하다.

그림 1-4 PDF문서에서의 임의 텍스트 셀렉션

Arbitrary text selection in a PDF document

 
프리뷰에서 셀렉션을 선택할 떄 옵션 키를 누른 상태로 블록 셀렉션을 실험해 볼 수 있다.
셀렉션은 셀렉션 객체로 저장되는데 이는 페이지또는 셀렉션을 포함하는 페이지들과 같은 추가적인 데이터를 저장할 수 있다. 이 정보는 사용자에게 다중 셀렉션을 보여줄 때 유용하다. (예를 들어, 검색 결과 리스트)

PDF 킷 클래스
 
PDF킷은 다른 클래스들 여러개로 분할 된다. PDFView와 PDFSelection을 제외하고는 이들 클래스는 PDF 스펙에서의 다양한 객체에 연계된다.

그림 1-5 PDF 킷 클래스 계층도
The PDF Kit class hierarchy

PDFView 클래스

PDFView 클래스는 웹킷의 웹뷰 클래스와 유사하게 어플리케이션 킷 NSView클래스를 상속한다. PDFView 객체를 직접적으로 사용하여 쉽게 인터페이스 빌더를 사용하여 윈도우에 위치시킬 수 있다. 팔레트는 다음에서 가져온다. /Developer/Extras/Palettes/PDFKit.palette.

PDFView는 직접 사용할 유일한 클래스가 될 것이다. 이는 어플리케이션에서 PDF데이터를 표시하고 사용자가 내용을 선택할 수 있게하고 문서를 통한 열람을 가능하게 한다. 줌 레벨을 설정하고, 텍스트 데이터를 파스테보드에 복사한다. 사용자는 PDFView 로 문서를 드래그 앤 드랍할 수 있다.

PDFView 는 PDF 유틸리티 클래스를 호출하여 그 기능을 구현 할 수 있다. 만약 추가적인 기능을 원하면 유틸리티 클래스를 ㅅ사용하거나 서브클래스한다. 

그림 1-6 PDFView 로 사용가능한 유틸리티 클래스
Utility classes as used by PDFView



PDF 킷 유틸리티 클래스들

PDF킷 유틸리티 클래스는 파운데이션과 같은 어플리케이션 과 같은 작용의 혼합을 제공한다. 이들 클래스는 NSObject의 하위 클래스로서 그림 1-5에 보여진다.

PDF 문서
주요 PDF킷 유틸리티 클래스는 PDFDocument로서 PDF데이터나 PDF 파일을 나타낸다. 다른 유틸리티 클래스는 PDFDocument의 매소드를 통해 초기화,되는데 PDFPage PDFOutline 같은 것이나 PDFSelection 과 PDFDestination이 있다.

PDF 데이터나 PDF파일로 PDFDocument를 초기화한다. 페이지 카운트를 물어보고 페이지를 추가 삭제한다. 검색을 수행하고 선택된 컨텐트를 NSString 객채로 변환한다.

PDFPage
예상하듯, PDFPage는 PDF문서내의 페이지를 나타낸다. PDFDocument 객체로 부터 하나를 물어봄으로서 PDFPage객체를 초기화한다. PDF페이지 객체는 사용자가 화면상에 보는 것이고 뷰는 한번에 한 페이지 이상을 한번에 표시할 수 있다. PDFPage를 사용하여 PDF컨텐트를 렌더하고, 어노테이션을 추가하고, 캐릭터 숫자를 세고, 셀렉션을 지정하고, 페이지의 텍스트 내용을 NSStirng이나 NSAttributedString 객체로 얻는다.

PDFOutline
실제적인 문서 내용을 보여주는 것에 더해서 PDF킷은 PDF내에 포함된 아웃라인 정보를 표시할 수 있다. PDFOutline객체는 아웃라인 계층내의 부모나 자식 엘리먼트를 표시한다.

아웃라인은 PDFOutline 객체의 계층을 구성한다. 최상위 레벨은 루트 아웃라인 객체로서 다른 아웃 라인 객체를 위한 컨테이너로서 작용한다. 루트 아웃라인은 사용자에게 보여지지 않는다.

PDFSelection
PDFSelection객체는 PDF문서의 텍스트의 범위를 나타내는 것이다. 직접적으로 셀렉션을 만들 수는 없다. 셀렉션 메소드로부터 반환하는 값을통해 PDFSelection객체를 얻는데 PDFPage나 PDFDocument 상에 인보크하는 것으로서 성공적으로 찾으면 반환된다.

PDF 뷰상의 셀렉션은 다중 페이지로 확장할 수 있고 비 연속적이거나 둘 모두 일 수도 있다. 예를 들어, 두 컬럼 페이지 연속으로 단일 컬럼내의 텍스트만을 선택할 수 있다. 셀렉션으로부터 텍스트와 페이지를 얻고, 셀렉션을 결합하고, 양 방향으로 셀렉션을 확장한다.

PDFAnnotation
PDFAnnotation은 PDF파일 내의 주 텍스트 내용 보다는 내용의 다양성을표시할기 위한 객체이다. 링크, 폼 요소, 하이라이팅 서클, 등등이다. 각 어노테이션은 페이상의 특정 위치에 연계되고 사용자에게 상호성을 제공한다.

PDFAnnotation은 그림 1-3에서 보여지는 콘크리트 클래스의 추상 슈퍼클래스이다. 다양한 콘크리트 클래스를 어노테이션 형식으로 PDF킷이 지원한다. 

PDFBorder
PDFBorder 객체는 PDFAnnotation 객체의 보더를 위한 드로잉 작용을 캡슐화한다. PDF 보더는 이런 라인 스타일 (솔리드, 대시드, 베벨드), 라인 너비, 코너 반지름을 지정할 수 있게 한다.



PDFKit Programming Guide - Introduction PDFKit


PDF Kit Programming가이드 소개


https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/PDFKitGuide/PDFKit_Prog_Intro/PDFKit_Prog_Intro.html#//apple_ref/doc/uid/TP40001863-CH203-SW1

PDF킷은 어플리케이션에 PDF문서를 보여주고 관리하기 위한 기술이다. 어도비의 PDF 소픽을 구현함으로서 PDF킷은 개발 시간을 단축시킨다. 애플의 자체 앱인 사파리나 프리뷰도 PDF 를 보여주기 위해 PDF 킷을 사용한다.

PDF 킷은 OS X v10.4 이상에서 사용할 수 있다.

누가 이 문서를 읽어야 하나

이 문서의 독자는 어플리케이션에서 PDF문서를 보여주고 편집하고 검색하기를 원하는 코코아 개발자를 위한 문서이다. 이 문서는 코코아와 오브젝티브씨 프로그래밍 언어에 익숙하다고 가정한다.

PDF은 아주 유연하여 PDF문서에 아주약간의 제어만으로 처리할 수 있게 한다. 단순히 PDF 문서를 보여주거나 풀피쳐드 PDF 편집기를 만들 수 있다.

이 문서의 구조

다음 챕터로 구성된다.
PDF킷 개념은 PDF기능의 전체보기와 다양한 PDF킷 클래스의 개념과 아웃라인을 설명한다.
PDF킷 작업은 PDF킷을 사용하여 공통의 기능을 어떻게 구현하는지를 보여준다.
 
같이 보기

어도비의 PDF 스펙은 PDF문서를 설명하는 주요 리소스이다. 이 것은 다음 웹사이트에서 확인할 수 있다.




UIKit - App and Environment - Managing Your App's Life Cycle UIKit


Managing Your App's Life Cycle

https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle?language=objc

앱이 포어그라운드 나 백그라운드에 있을 때 시스템 알림에 반응하고 다른 시스템 연관 이벤트를 처리한다.

오버뷰

앱의 현 상태를 통해 현재 할 수 있는 지 할 수 없는 지를 결정한다. 예를 들어, 포어그라운드 앱은 사용자가 보고 있는 앱이므로 CPU를 포함한 시스템 리소스에 우선권을 갖는다. 이에 반해, 백그라운드 앱은 반드시 가급적 적은양만을 수행해야 하며, 하지 않는 것이 좋다. 왜냐하면 오프스크린이기 때문이다. 상태에서 상태로 변함으로서 반드시 이런 작용을 적절히 수행해야 한다.

앱의 상태가 변화할 때, UIKit은 적절한 델리게이트 객체의 메소드를 호출함으로서 알린다.
- iOS 13이후, UISceneDelegate 객체를 사용하여 씬 기반 앱의 라이프 사이클 이벤트에 반응한다.
- iOS 12 이전, UIApplicationDelegate 객체를 사용하여 라이프 사이클 이벤트에 반응한다.

일러두기
앱에 씬 지원을 추가하면 iOS는 언제나 씬 델리게이트를 사용한다. iOS 12이전은 앱델리게이트를 사용한다.

씬 기반 라이프 사이클 이벤트에 반응하기

만약 앱이 씬을 지원하면 UIKit은 각각에 라이프 사이클 이벤트를 전달한다. 씬은 장치에서 구동중인 앱의 UI의 한 인스턴스이다. 사용자는 각 앱에 대한 다중 씬을 생성하고 이들을 분리하여 보이고 감춘다. 각 씬이 자체적인 라이프 사이클을 가지므로 각각은 실행의 다른 상태를 가질 수 있다. 예를 들어, 한 씬은 포어그라운드라면 다른 것은 백그라운드거나 멈춘 상태이다.

중요
씬 지원은 옵트 인 기능이다. 이 기본 지원ㅇ르 활성화 하려면 앱이 지원하는 씬을 지정 부분에서 설명된 것 처럼 Info.plist  파일에 UIApplicationSceneManifest키를 추가한다.
https://developer.apple.com/documentation/uikit/app_and_environment/scenes/specifying_the_scenes_your_app_supports?language=objc

다음 그림은 씬의 상태 전이를 보여준다. 사용자나 시스템이 앱에 대한 새로운 씬을 요청할 때, UIKit은 이를 생성하고 붙여지지 않은 상태로 넣는다. 사용자 요청 씬은 빠르게 포어그라운드로 이동하며 온스크린에 보여진다. 시스템요청 씬은 보통 백그라운드로 가며 이벤트를 처리한다. 예를 들어, 시스템은 백그라운드 내에서 씬을 실행하여 위치 이벤트를 처리한다. 사용자가 앱의 UI를 닫으면, UIKit은 백그라운드 상태로 이동하고 일시정지 상태로 만든다. UIKit은 백그라운드나 중지된 씬과는 교신을 언제든 끊을 수 있으며 씬이 붙여지지 않은 상태로 되돌아 올때 리소스를 다시 요청할 수 있게 한다.

An illustration showing the state transitions for a scene-based app. Scenes start in the unattached state and move to the foreground-active or background state. The foreground-inactive state acts as a transition state.


씬 트랜지션을 사용하여 다음 작업을 수행한다.
- UIKit이 앱의 장면과 연결할 때, 씬의 초기 UI를 설정하고 씬이 필요로하는 데이터를 로드한다.
- 포어그라운드 활성상태로 전이 될 때, UI를 설정하고 사용자와 상호작용할 준비를 한다. 포어그라운드에서 UI를 실행하기 위한 준비하기를 살펴본다. 
https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_foreground?language=objc
- 포어그라운드 활성 상태에서 떠날 때 데이터를 저장하고 앱의 작용을 없앤다. 백그라운드에서 UI를 실행하기 위한 준비하기를 살펴본다.
https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background?language=objc
- 백그라운드 상태로 들어갈 때 치명적인 작업을 끝낸다. 가급적 많은 메모리를 해제하며 앱 스냅샷을 준비한다. 백그라운드에서 UI를 실행하기 위한 준비하기를 살펴본다.
https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background?language=objc
- 씬이 연결을 끊으면 씬과 연계된 모든 공유된 자원을 해제한다.
- 씬과 연계된 이벤트에 더해 UIApplicationDelegate 객체를 사용하여 앱의 실행에 반응한다. 앱 실행시 무엇을 할지에 대한 정보는 앱 실행에 반응하기 부분을 살펴본다.
https://developer.apple.com/documentation/uikit/app_and_environment/responding_to_the_launch_of_your_app?language=objc

앱기반 라이프 사이클에 반응하기

iOS12 이전에는 앱은 씬을 지원하지 않으니 UIKit은 모든 라이프 사이클 이벤트를 UIApplicationDelegate객체로 전달한다. 앱 ㅇ델리게이트는 앱의 윈도우, 분리된 스크린에 표시된 모든 것을 관리한다. 결과적으로, 앱 상태 전이는 앱의 전체 UI에 영향을 끼치며, 외부 디스플레이에 콘텐트를 포함한다.

다음 그림은 앱 델리게이트 객체에 연관되는 상태 전이를 보여준다. 실행 이후 시스템은 앱을 비활성 또는 백그라운드 상태로 바꾸는데, 온스크린에 보여질 UI에 기반해 결정한다. 포어그라운드에서 실행할 때, 시스템은 자동적으로 활성 상태로 전이한다. 이후에, 상태는 앱이 종료되기 전 까지 활성 비활성을 오간다.

An illustration showing the state transitions for an app without scenes. The app launches into the active or background state. An app transitions through the inactive state.


앱 트랜지션은 다음과 같은 작업을 할 때 사용된다.
- 시작시, 앱의 자료구조와 UI를 초기화한다. 앱 시작에 반응하기를 살펴본다.
https://developer.apple.com/documentation/uikit/app_and_environment/responding_to_the_launch_of_your_app?language=objc
- 활성상태일 때, UI 설정을 마치고 사용자에 상호작용을 준비한다. 포어그라운드에서 실행할 UI를 준비한다.
https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_foreground?language=objc
- 비활성화 상태일 때, 데이터를 저장하고 앱 작용을 없앤다. 백그라운드에서 UI를 실행을 위한 준비하기 부분을 살펴본다.
- 백그라운드 상태로 진입할 때, 치명적인 작업을 완료 하고, 가급적 많은 메모리를 해제하며 앱 스냅샷 준비를 한다.
- 종료 시, 모든 작업을 중지하고 모든 공유된 리소스를 해제한다. 
https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623111-applicationwillterminate?language=objc

다른 중요한 이벤트에 반응하기

라이프 사이클 이벤트에 더해 앱은 반드시 다음 테이블에 나열된 이벤트를 다룰 준비를 해야 한다. UIApplicationDelegate 객체를 사용하여 이들 이벤트에 대응한다. 몇몇 상황에서, 알림을 통해 이들 이벤트를 다룰 수도 있어서 앱의 다룬 부분에서도 대응 할 수 있다.

메모리 경고: 앱의 메모리 사용이 너무 높을 때 받는다. 앱의 메모리 양을 줄인다. 메모리 경고에 반응하기 부분을 살펴본다.
https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle/responding_to_memory_warnings?language=objc
보호된 데이터가 가용/비가용: 사용자가 장치를 잠금 또는 해제 할 때 받는다. 
https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623044-applicationprotecteddatadidbecom?language=objc
https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623019-applicationprotecteddatawillbeco?language=objc
작업건내기: NSUserActivity 객체가 처리되어야 할 때 받는다. 
https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622963-application?language=objc
시간대 변경: 시간대가 변경되었을 때, 폰 캐리어가 시간대 업데이트를 한 경우이다. 
https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622992-applicationsignificanttimechange?language=objc
URL열기: 앱이 리소스를 열기 요청을 받았을 때이다. 
https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623112-application?language=objc

Scenes - Specifying the Scenes Your App Supports UIKit


Specifying the Scenes Your App Supports

https://developer.apple.com/documentation/uikit/app_and_environment/scenes/specifying_the_scenes_your_app_supports?language=objc

각 씬에서 관리하는 객체들과 초기 사용자 인터페이스를 담은 앱의 장면에 대해 시스템에 알린다.

오버뷰

iOS 13 이후에 사용자는 앱의 UI의 다중 복사본을 생성할 수 있고 이들 간에 토글이 가능하다. iPad에서 사용자는 앱의 UI를 각 카피당 사이드 바이 사이드로 표시할 수 있다. 앱의 UI의 각 복사본은 윈도우, 뷰, 그리고 뷰컨트롤러를 관리하는데 씬 객체를 사용할 수 있다.

사용자가 새로운 장면을 요청할 때, UIKit 은 연관된 씬 객체를 생성하고 초기 설정을 처리한다. 이를 위해 UIKit은 제공되는 정보에 의존한다. 앱은 반드시 제공되는 씬의 형식과 이들 씬을 관리하는데 사용되는 객체를 지정해야 한다. Info.plist에서 정적으로 정의하거나 런타임에서 실시간 처리를 할 수 있다.

중요
씬은 옵트 인이다. 하지만 앱의 UI 가 동시에 다중 복사본을 보여줘야 한다면 반드시 제공해야 한다.

프로젝트 설정에서 씬 지원 활성화

앱은 반드시 씬에서 명시적으로 지정해야 하는데 앱의 설정 세팅을 업데이트 함으로서 수행한다.
1. Xcode 프로젝트를 연다
2. 앱의 타겟에 대한 일반 설정으로 이동
3. 디플로이먼트 정보 섹션의 "다중 윈도우 지원" 체크박스

멀티플 윈도우 옵션을 활성화 할 때 Xcode 는 UIApplicationSceneManifest 키를 Info.plist 파일로 추가한다. 이 키의 존재는 시스템이 지원하는 씬을 알 수 있게 한다. 이 키의 값은 딕셔너리인데, UIApplicationSupportsMultipleScenes키만을 포함한다.

UIApplicationSupportsMultipleScenes 키의 값은 시스템에서 앱이 다중 동시 신을 지원하는지를 알게 한다. Xcode는 초기에 이 값을 YES로 하지만, 한번에 하나만 보여줘야 한다면 이것을 비활성화 할 수 있다. 다중 씬을 지원하는 것은 다른 것에 방해되지 않도록 장면을 다루는 추가적인 작업을 수행해야 한다. 예를 들어, 만약 씬이 같은 데이터 구조체를 사용한다면 이들은 반드시 앱의 데이터의 결합을 유지하는 이들 구조에 대한 접근을 유의해야 한다.

각 씬의 세부 설정

UIKit 은 제공하는 정보를 제공하여 앱의 씬의 생성을 다룬다. 단순한 방법은 이 정보를 앱의 Info.plist 파일에서 제공하는 것이다.

1. Xcode 프로젝트를 열고 Info.plist 파일을 선택
2. 어플리케이션 씬 메니페이스 엔트리의 + 버튼 클릭. 이 엔트리는 UIApplicationSceneManifest 키와 연계된다. 만약 나타나지 않으면 프로젝트 설정에서 씬 지원 활성화하기 부분에서 설명되어 있다. https://developer.apple.com/documentation/uikit/app_and_environment/scenes/specifying_the_scenes_your_app_supports?language=objc#3262273
3. 메뉴로 부터 씬 설정을 살펴본다.
4. 씬 설정 엔트리에서 + 버튼을 클릭
5. 어플리케이션 세션 롤을 선택하여 앱에 메인 씬을 추가한다.
6. 제공된 엔트리 씬 세부 정보를 채운다.

대부분의 앱은 오직 하나의 메인 씬만을 필요로 하지만, 다중 장면을 추가하고 각각을 다르게 설정한다. 예를 들어, 알림 연계 콘텐트를 보여주기위해 두번째 장면을 포함할 수 있다. UIKit은 각 씬에 다음 정보를 필요로 한다.

- 씬의 클래스 이름, UIWindowScene
- 씬을 관리하는 데 사용하는 커스텀  델리게이트 객체의 클래스 이름. 클래스는 반드시 UIWindowSceneDelegate 프로토콜을 받아야 함
- 씬을 내부적으로 확인하는데 사용하는 유니크한 이름
- 씬의 초기 UI를 포함하는 스토리보드 이름. .storyboard  제외한 이름을 지정
더 세부적인 사항은 UISceneConfigurations 를 살펴본다.
https://developer.apple.com/documentation/bundleresources/information_property_list/uiapplicationscenemanifest/uisceneconfigurations?language=objc

씬에 대한 인터페이스 생성

스토리보드를 사용하여 씬에 대한 UI를 지정한다. UISceneStoryboardFile 에 지정할 스토리보드는 씬에서 보여질 초기 뷰컨트롤러를 포함한다. 씬 객체를 생성하고, UI킷은 해당 씬에 자동적으로 윈도우를 생성하고 스토리보드에 있는 초기 뷰컨트롤러를 설치한다. 이 뷰 컨트롤러는 UIWindowSceneDelegate 객체의 메소드를 사용하여 프로그래밍으로 변경할 수도 있다.

중요
스토리보드에서 반드시 초기 뷰 컨트롤러를 지정해야 함을 잊지 않기 바란다. UIKit은 UI를 설정할 때 뷰 컨트롤러의 표시에서 이를 의존한다.

씬의 설정을 동적으로 변경하기

씬 객체를 생성하기 전에, UIKit은 application:configurationForConnectingSceneSession:options: 메소드를 호출하여 신과 연계된 세부사항을 변경할 수 있게 해준다. 이 메소드를 사용하여 UIKit이 제공하는 옵션에 기반한 씬 설정을 조절할 수 있다. 예를 들어, 시스템이 씬에 대해 알림을 전달할 때, 알림 연관 인터페이스와 다른 스토리 보드를 지정할 수 있다.

만약 동적으로 씬을 지정하지 않는다면, UIKit은 앱의 Info.plist 파일내의 정보를 사용하여 씬을 생성한다.

씬 기반 라이프 사이클 시맨틱 적용

씬 변화를 위한 지원을 추가하는 것은 어떻게 라이브 사이클 이벤트에 반응할지가 변경됨을 뜻한다. 씬이 없는 앱은 앱 델리게이트 객체가 포어그라운드나 백그라운드에 트랜지션을 다룬다. 앱에 씬 지원을 추가할 때, UIKit은 이 책임을 씬 델리게이트로 전환한다. 씬 라이프 사이클은 다른 것에 독립적이고, 앱 자체에도 독립적이므로 씬 델리게이트 객체는 반드시 트랜지션을 처리해야  한다.

만약 앱이 iOS12 를 지원한다면 앱 델리게이트와 씬 델리게이트 양쪽에서 라이프 사이클 트랜지션을 다룰 수 있다. UIKit 은 오직 하나의 델리게이트 객체에만 알린다. iOS 13이후에는 UIKit은 신 델리게이트 객체로, iOS 12이 전은 앱 델리게이트에 알린다.

라이프 사이클 이벤트를 어떻게 다루는지에 대한 사항은 앱의 라이프 사이클 관리하기를 살펴본다.
https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle?language=objc


1 2 3 4 5 6 7 8 9 10 다음