June 29, 2021, TIL (Today I Learned) Drag and Drop

Inwoo Hwang·2021년 8월 26일
0
post-thumbnail

학습내용


Drag and Drop

Screen Shot 2021-07-07 at 7.39.01 PM

같은 앱의 일 경우 해당 앱이 드래그의 source app 그리고 destination app 이 됩니다!

Drag and Drop in a TableViews

TableView의 drag 와 drop 기능을 지원하게 하려면 먼저 ViewController에게 UITableViewDragDelegate 프로토콜을 채택하게 한 뒤

override func viewDidLoad() {
    super.viewDidLoad()
    
    tableView.dragInteractionEnabled = true
    tableView.dragDelegate = self
    tableView.dropDelegate = self

}

위와 같이 드래깅 상호작용을 가능하게 설정한 뒤 dragDelegatedropDelegate 에게 viewcontroller 자신을 할당 해 주어야 합니다.

Dragging Rows from the TableView

tableView(_:itemsForBeginning:at:)

유저의 드래깅 제스처가 발생 되면, TableView는 drag session 을 생성 한 뒤 tableView(_:itemsForBeginning:at:) 매서드를 호출합니다.(만약 유저가 특정한 cell을 drag 할 시 해당 메서드는 선택시에 한 번만 호출 되고 어떤 cell도 선택되지 않는다면 해당 메서드는 단 한 번만 호출 됩니다.) 만약 반환 값이 빈 배열이 아니라면, tableview는 사용자가 명시한 cell을 drag 하기 시작합니다. 만약 특정한 index path로 부터 사용자가 컨텐츠를 드래그하는 것을 방지하려면 빈 배열을 반환하도록 하여야 합니다.

tableView(_:itemForBeginning:at:) 메서드 속에서는 아래와 같은 작업이 필요합니다.

  1. 하나 이상의 NSItemProvider 를 생성한 뒤 이를 활용하여 table의 행의 데이터를 표시하도록 사용하세요.
  2. 각 item provider 개체를 UIDragItem으로 감싸 주어야 합니다.
  3. localObject 프로퍼티에게 값을 할당하는 것을 고려 해 주세요. 이 과정은 필수는 아니지만 동일한 앱 속의 컨텐츠를 드래그하고 드랍하는 속도를 더 빠르게 합니다.
  4. drag item을 해당 메서드를 통해 반환하도록 하세요.

제공된 indexPath를 사용하여 드래그 항목을 생성할 행을 결정합니다. 만약 대상 행이 현재 선택된 행 중 하나인 경우 tableview는 선택된 모든 행을 드래그 합니다. 만약 선택되지 않았던 행인 경우 드래그 작업에 해당 행을 추가합니다.

Understanding a Drag Item as a Promise

Use a drag items to convey data representation promises between a source app and a destination app.

사용자가 사진, 지도, 캘린더일정, 또는 선택된 텍스트를 드래그하면, 사용자의 앱은 드래그한 파일의 기본데이터를 drag item으로 연결 시켜 줍니다.

drag item은 이어지는 작업으로 itemProvider를 활용합니다.

사용자 앱은 itemProvider의 registeredTypeIdentifiers array와 uniform type identifiers(UTIs)를 제공합니다.

UIDragItem이란?

A drag item with its contained item provider and array of uniform type identifiers
  • drag 할 때 data item의 표현
  • 사용자가 drag를 시작하면 app이 data를 dragItem과 연결됨
  • UTI들은 배열의 source app에서 destination app으로 전달할 data의 표현 방식

RegisteredTypeIdentifiers

item provider의 type identifier 배열을 반환 해 줍니다. 이 배열에 있는 type identifier들은 등록된 순서대로 배열에 담겨져 있습니다.

registeredtypeIdentifiers | Apple Developer Document

Uniform Type Identifier

uniform type identifer(UTI)는 개체의 클래스를 타입별로 구별 해주는 문자열입니다. UTI는 보통 파일의 포멧 또는 인메모리 데이터 타입을 식별할 때 사용되고 디렉토리, 데이터볼륨, 또는 패키지의 계층적 레이아웃을 감별할 때도 사용됩니다. 예를 들면 Iphone의 앱은 UITs를 사용해서 클립보드에 복사할 수 있는 데이터의 포맷을 정의합니다. Mac 앱은 UTI를 활용하여 앱이 열 수 있는 파일의 타입을 정의합니다.

Uniform Type Identifier | Cocoa Core Competencies

UTI 배열에서는 발송자 앱이 목적지엡에 보낼 수 있는 특정한 데이터 표현에 대한 약속이 구성되어 있습니다. 여기서 약속(promise)이란 사용자 앱이 drag item을 구성할 때 앱이 특정한 데이터 표현으로 데이터를 제공하겠다는 것을 약속하는 것입니다.

DragItem은 data의 표현을 제공할 뿐 data 자체를 만드는 것이 아닙니다.

  • 사용자가 어떤 항목을 drag할 때 해당 item 자체를 옮기는 게 아니라 dragItem이라는 표현으로 구성됩니다.

사용자가 특정 컨텐츠를 드래그 했을 때 사용자가 느끼기에는 컨텐츠 데이터를 가지고 전달하는 것 처럼 보일 수 있지만 사실은 이 드래그한 item은 사용자가 가지고 가는 컨텐츠의 preview image와 itemprovider가 구성한 이 약속이 드래그된 item에 포함이 되는 것입니다.

TableView, CollectionView가 아닌 Custom View에서 사용할 때:

발송하는 앱의 에서는 UIDragInteractionDeleagate 가 drag item을 구성 해 줍니다.

목적지 앱에서는 UIDropInteractionDelegate가 drag item과 상호작용하여 약속된 데이터를 사용합니다.

Receiving Dropped Content

TableView에서 drop을 처리하기 위해서는 4가지 delegate 메서드

1. tableView(_:canHandle:)

드래그된 컨텐츠가 범위 안에 들어오면 tableview는 dropDelegate 와 상의 후 드래그된 데이터를 받을 수 있는지 없는지 결정합니다.

첫 째로 tableView는 tableView(_:canHandle:) 메서드를 호출하여 드래그된 데이터를 받는 개체의 datasource에 추가될 수 있는지 확인합니다.

2. canLoadObjects(ofClass:)

  • Drag item으로 ofClass type의 인스턴스로 만들 수 있는지 여부
  • ofClass에 지정되는 type은 NSItemProviderReading protocol을 채택해야 합니다.

3. tableView(_:dropSessionDidUpdate:withDestinationIndexPath:)

유저가 드래그 된 개체를 손가락으로 움직이면 tableView는 잠재적 drop 위치를 추적하고 추적하는 정보를 지속적으로 tableView(_:dropSessionDidUpdate:withDestinationIndexPath:) 메서드를 통해 dropDelegate 에게 정보를 전달합니다. 해당 작업은 선택사항이지만 권고하는 작업입니다. 왜냐하면 tableView가 드롭관련한 시각적 효과를 줄 수 있게하기 때문입니다.

위 메서드를 구현할 때 UITableViewDropProposal 개채를 특정한 indexpath에 드랍 했을 때 어떻게 응답할 것인지에 대한 정보를 포함하여 생성해야합니다.

  • dropProposal을 작성해 지정된 위치에서 drop을 처리 방식을 tableView에게 알림
  • tableView는 dropProposal을 사용해 user에게 적절한 피드백을 제공

dropSessionDidUpdate:withDestinationIndexPath 메서드는 유저가 drag하는 동안 반복적으로 매우 자주 호출 되기 때문에 proposal을 될 수 있으면 빠르게 반환 해 주어야 합니다.

4. tableView(_:performDropWith:)

사용자가 드랍을 끝낸 뒤 손가락을 화면에서 때면, tableView는 tableView(_:performDropWith:) 메서드를 호출합니다. 해당 메서드를 활용하여 드랍된 데이터를 처리해주어야 합니다.

해당 메서드를 구현할 때 드래그된 데이터를 불러와야하고 드랍된 tableView의 datasource를 업데이트 해 주어야 하고 tableView에 필요한 행을 추가 해 주어야 합니다.

*만약 드래그된 컨텐츠가 tableView로부터 온 것이라면 API를 활용하여 기존 위치의 행을 지우고 새로운 위치에 행을 추가할 수 있습니다.

만약 tableView 밖으로부터 온 컨텐츠라면 같은 앱 간의 이동이라면 localObject 프로퍼티를 활용할 수 있고 다른 앱으로 부터 온 컨텐츠라면 NSItemProvider 를 활용하여 데이터를 가져오고 데이터를 집어넣을 수 있습니다.

(_:performDropWith:) 메서드를 사용할 때 아래와 같은 작업이 필요합니다:

Screen Shot 2021-07-02 at 11.57.08 AM
  1. 제공된 drop coordinator object 의 item 프로퍼티를 순회하여야 합니다.
  2. 순회하는 도중 각 item을 어떻게 처리할 것인지 결정해야 합니다:
    • 만약 item의 indexPath가 값을 갖고 있다면 해당 아이템은 tableView로부터 온 컨텐츠이고 batchUpdate 를 활용하여 기존 위치의 item을 제거하고 새로운 indexpath에 해당 item을 추가해야합니다.
    • 만약 item의 localObject 프로퍼티가 제공된다면 해당 item은 동일 앱내 다른 곳에서 온 컨텐츠이기 때문에 행을 추가 해 주거나 item을 업데이트 시켜줘야합니다.
    • 만약 위 두 방법이 불가능하다면 드래그할 item의 NSItemProvider 프로퍼티를 활용하여 데이터를 비동기적으로 불러오고 item을 삽입하거나 업데이트해야합니다.
  3. datasource 를 업데이트 하거나 필요한 item을 tableView 내에서 삽입 또는 이동시켜주어야 합니다.

드래깅을 사용하고자 하는 앱 속에 있는 컨텐츠라면 대부분 tableView의 datasource를 직접적으로 업데이트할 수 있습니다. 예를 들면 batchupdate를 활용하여 행을 제거한 뒤 새로 삽입할 수 있습니다. 해당 작업이 끝난 뒤

drop(_:toRowAt:) 메서드를 호출하여 drop coordinator가 행이 더해지는 애니메이션효과를 줄 수 있습니다.

외부 또는 다른 앱으로 밭는 데이터인 경우 NSItemProvider 개체를 활용하여 데이터를 받아야합니다. 새로운 행을 tableView로 추가하려면 placeholder 를 구현 해 주어야 합니다. placeholder 는 실제데이터가 완전히 전달되기 전까지 유저에게 잠시동안 보여지는 행 역할을 합니다. 예를 들면 로딩중이라는 placeholder를 추가해서 컨텐츠가 특정 행으로 추가되고 있다는 것을 보여줄 수 있습니다.

placeholder 를 추가하려면 아래와 같은 작업을 해야합니다:

  1. UITableViewDropCoordinator 오브젝트가 제공하는drop(_:toPlaceholderInsertedAt:withReuseIdentifier:rowHeight:cellUpdateHandler:_) 를 활용하여 placeholder를 행으로 추가할 수 있습니다. cellUpdateHanlder 파라메터에 코드블럭을 활용하여 placeholder의 컨텐츠를 세팅할 수 있습니다.
  2. NSItemProvider 개체로부터 데이터를 비동기적으로 받아와야합니다.

NSItemProvider 개체가 실제 데이터를 반환하면 데이터를 삽입한 뒤 placeholder cell을 받아온 데이터 cell로 바꿔줍니다. 구체적으로 commitInsertion(dataSourceUpdates:) 메서드를 호출해서 위 작업을 하면 됩니다. 해당 메서드의 블럭 안에서 모델을 업데이트하고 tableView의 datasource를 업데이트하면 됩니다. 해당 메서드가 반환되면 tableview는 자동적으로 placeholder를 제거하고 필요한 데이터를 가진 행을 추가합니다. 이 업데이트는 drop coordinator가 위치로 정한 indexpath(destinationIndexPath)에서 이뤄집니다.

[출처]:

Supporting Drag and Drop in TableViews | Apple Developer Document

Drag and Drop [Continue]

itemProvider

The item provider associated with the drag item

var itemProvider: NSItemProvider { get }

item provider는 드래그 앤 드랍 과정에서 데이터 또는 파일을 전달하는 역할을 담당합니다. 해당 프로퍼티는 UIDragItem instance가 생성될 때 UIDragItem의 init(itemProvider:) 를 통해 특정한 item Provider로 세팅이 될 수 있습니다.

itemProvider | Apple Developer Document

local Object

A custom object associated with the drag item

Local object 프로퍼티는 model object와 같은 커스텀 object를 drag item에 결부(connect)시킬 수 있는 옵션을 제공합니다. 드래그가 활성화 된 앱에서만 해당 object를 활용할 수 있습니다.

UIDragDropSession

drag session과 drop sesstion의 상태를 조회하기 위한 인터페이스입니다.

UIDragDropSession | Apple Developer Document

UITableViewDropCoordinator

An interface for coordinating your custom drop-related actions with the table view.

아이템을 드래그해서 드랍 할 때 취해야 할 행위를 지정해주는 인터페이스입니다.

[UITableViewDropCoordinator | Apple Developer Document](

profile
james, the enthusiastic developer

0개의 댓글