나만의 공부 노트

11강 정리 본문

(2017) 스탠포드 강의

11강 정리

va95 2021. 2. 23. 20:46

학습 내용

 

드래그 & 드랍

드래그 앤 드랍(iOS 11 이후)은 앱 사이 또는 단일 앱에서 정보를 공유하는 방법입니다.

길게 눌러서 드래그 앤 드랍을 사용할 수 있고, 사용하는 도중에 다른 멀티 터치 동작을 수행할 수 있습니다.

(앱이 아닌 시스템이 드래그 앤 드랍 제스처를 관리하고, 아이템들 또한 시스템이 ascyn = 비동기적으로 처리해줍니다. wow..)

아이패드는 멀티테스크를 지원하므로 두 가지 이상의 앱을 한 화면에 보여줄 수가 있으며, 앱 사이 또는 단일 앱에서 드래그 앤 드랍이 가능합니다.

아이폰은 오직 단일 앱에서만 드래그 앤 드랍이 가능합니다.

 

기본적인 용어로

source app : item을 드래그 한 앱

destination app : item이 드랍된 앱

drag activity : system-mediated gestures을 사용한 실제 사용자의 action

drag session : 시스템에서 관리하고, 드래그 앤 드랍 아이템을 관리

가 있습니다.

 

let dragItem = UIDragItem(itemProvider: NSItemProvider(object: 데이터 객체 = NSItemProviderWriting))

드래그 아이템은 위와 같은 모습으로 구성되어있고, NSItemProvider안에 기본적인 종류의 데이터 타입 또는 커스텀 타입을 전달할 수 있습니다.

이 때 promise라는 용어가 쓰이는 데 그 이유는 drag item을 구성할 때 특정 데이터 표현(UTI)을 제공하지만, 아직 이를 생성하는 작업을 수행하지 않음을 의미합니다.(드랍이 일어났을 때 데이터를 생성하는 작업이 async = 비동기적으로 수행됩니다. itemProvider라는 구조를 사용하는 것도 이 때문입니다.)

 

NSItemProvider에는 이미 NSAttributedString, NSString, UIImage, NSURL, UIImage 등과 같은 수많은 표준 타입들이 NSItemProviderWriting 프로토콜을 채택하고 있어 해당 데이터 객체를 바로 넘겨주며 사용이 가능합니다.

만약 커스텀 타입을 전달하고 싶다면 NSItemProviderWriting 프토토콜을 채택해야 합니다.

NSItemProvider 안에 있는 registeredTypeIdentifiers 배열에 UTI(uniform type identifiers)들을 넣어주어야 순차적으로 어떤 변수가 명시적으로 어떤 타입의 데이터가 들어갔는지 소통이 가능합니다.(표준 타입들은 이미 구현되어 있습니다!)

 

단일 앱에서 드래그 앤 드랍을 사용한다면, dragItem.localObject에 객체를 담으면 itemProvider를 이용하지 않고 Any 타입으로 데이터를 넘겨주기에 자원을 아낄 수 있습니다.

 

 

 

코드

드래그 앤 드랍은 UIView와 관련된 것들이기 때문에 UIView 객체에 interaction을 등록해야합니다.

제스처와 매우 비슷한 원리로 작동하기에 드래그 앤 드랍을 마치 기능이 많은 제스처로 생각해도 무방합니다.

 

1. 드래그 앤 드랍 기능 사용 명시

view.isUserInteractionEnable = true(default로 되어있습니다.)

 

2. 드래그 앤 드랍 interaction 등록

let drag/dropInteraction = UIDrag/DropInteraction(delegate: theDelegate)

view.addInteraction(drag/dropInteraction)

* drag/drop = drag 또는 drop 에서 하나를 사용하라는 의미입니다.

 

3. 델리게이트 메소드 구현

delegate를 수행하는 객체는 UIDrag/DropInteractionDelegate를 채택 및 준수합니다.

다양한 델리게이션 메소드를 delegate가 수행할 수 있도록 메소드를 구현해줍니다.

 

 

 

Drag

드래그 제스쳐가 발생하게 되면 델리게이트에 dragInteraction이라는 메소드가 호출됩니다.

view로부터 드래그 될 UIDragItem 항목들이 반환됩니다.

다양한 타입들이 들어갈 수 있으며, 몇몇의 데이터 타입의 경우(통상 NS로 시작하는) as? 로 캐스팅할 필요가 있습니다.

 

드래그하는 중에 추가적인 터치 제스쳐가 발생하는 경우 dragInteraction의 메소드에 itemsForAddingTo라는 외부 이름으로 인자를 전달할 수 있습니다.

 

 

 

Drop

1. dropInteraction을 설정한 뷰 위에서 드래그를 하고 있다면, delegate 객체가 dropInteraction이라는 메소드(인자 : canHandle)를 받습니다.

뷰 안에 들어올 때, 딱 한번 수행됩니다.

session에 다양한 정보들(dragItem들 목록, 전달된 객체의 종류 등등)이 들어있어 이 변수에서 꺼내쓰면 됩니다.

예를 들어 return session.canLoadObjects(ofClass: NSURL.self) && session.canLoadObjects(ofClass: UIImage.self)처럼 사용이 가능합니다.

 

2. 만약 거부하지 않았고 여전히 뷰 위에서 드래그를 움직이고 있을 때 지속적으로 dropInteraction 메소드(인자 : sessionDidUpdate)를 받게 되며, UIDropProposal을 반환합니다.

UIDropProposal은 .copy, .move(단일 앱에서만 가능, 다른 앱이라면 copy로 동작), .cancle을 할 수 있습니다.

손가락이 어떤 위치에 있는지 알고 싶다면 session.location(in: view)를 사용합니다.

 

3. 실제로 드랍을 수행했을 때, dropInteraction 메소드(인자 : performDrop)를 받게 되며, 이 메서드를 통해 session에 있는 아이템을 꺼내와 작업을 진행합니다.

session.loadObjects(ofClass: NSItemProviderReading.Type, completion: ([NSItemProviderReading]) -> Void)처럼 사용합니다.

이때 실질적으로 sourceApp에서 보낸 데이터들이 새롭게 생성되며 destionationApp에 보내지게 됩니다.

async = 비동기적으로 함수가 수행되며, 다른 쓰레드에서 ofClass에 맞는 타입들만 가져온 뒤, 완료가 되면 메인 쓰레드에서 completion을 수행합니다.

completion의 인자는 NSItemProviderReading이므로 as? 타입 캐스팅을 한 후, 데이터를 사용할 수 있습니다.

*만약 다른 앱에서 URL을 가져왔고, completion에서 url로 fetch한다면 메인 쓰레드를 막는 사태가 일어날 수 있으므로 다시 메인 쓰레드가 아닌 다른 쓰레드로 보내서 작업 처리를 해야합니다.

 

        

 

겹쳐있는 뷰 고르기

Command + Shift + 클릭을 하면 여러 겹쳐있는 뷰들 중 하나를 고를 수 있는 목록창이 뜹니다.

 


테이블뷰와 컬렉션뷰

둘 다 스크롤뷰의 하위 클래스이며 무한한 영역을 가질 수 있습니다.

테이블뷰는 리스트 형태이며 row라는 구조를 가지고 있습니다.

컬렉션뷰는 2D로 구성되어 있고, item이라는 구조를 가지고 있습니다.(row가 없으며 보통 flow layout이라는 형태를 많이 씁니다. 커스텀 레이아웃을 작성할 수도 있습니다.)

 

MVC와 관련하여 언급하였던 것처럼 View는 데이터를 가지고 있지 않습니다.

따라서 뷰는 델리게이트 방법을 이용하여 뷰 컨트롤러에게 위임을 하며, 뷰 컨트롤러는 dataSoure를 위임받아 해당 메소드를 구현하여 적절히 뷰를 재구성해주어야 합니다. (blind communication 구조)

델리게이션 메소드를 사용할 때 테이블 뷰는 indexPath 인자에서 section과 row를, 컬렉션 뷰는 indexPath 인자에서 section과 item이라는 변수를 사용하여 개념적으로 혼동이 생기지 않도록 사용합니다.

cellForRowAt의 경우 계속해서 새로운 셀을 만드는 것이 아니라, 기존에 사용하던 셀을 재사용하도록 합니다.

여기서 우리만의 커스텀 셀을 만들 수도 있습니다.(컬렉션 뷰는 기본 제공 셀이 없습니다.)

 

*재사용 셀을 사용할 때 멀티 쓰레딩을 이용한다면 주의해야합니다.

*메인 쓰레드가 아닌 다른 쓰레드에서 데이터를 가져오고, 다 가져오면 메인 쓰레드에서 UI를 업데이트 하는 클로저를 실행했을 때, 재사용된 셀에 이전의 데이터가 늦게 도착한다면 덧씌워질 수 있기 때문입니다.

 

테이블 셀 뷰의 높이

1. tableView.rowHeight <- 고정된 행의 높이

static 테이블뷰라면 cell의 고유 높이로 지정, dynamic 테이블뷰라면 rowHeight로 지정됩니다.

 

2. autolayout && estimatedRowHeight

dynamic 테이블뷰에서 rowHeight = UITableView.automaticDimension로 셀의 높이가 변할 수 있다고 선언해야합니다.

그 후, tableView.estimatedRowHeight = CGFloat를 통해 전체 추정 높이를 주거나 또는 func tableView(..., estimatedRowHeightForRowAt indexPath:,...)를 통해 개별 추정 높이를 주어야합니다.

추정 높이를 설정해주어야하는 이유는 오토레이아웃을 사용해서 전체 셀의 높이를 다 구하면 보이지 않는 셀까지 연산해야되서 자원을 많이 잡아먹기 때문에 최적화를 위해서 사용합니다.

그 다음, 오토레이아웃으로 셀의 높이를 정확히 계산할 수 있도록 잡아줍니다.

 

3. func tableView(..., rowHeightForRowAt indexPath:,...)를 사용하여 개별 셀의 높이를 지정합니다.

 

컬렉션 셀 뷰의 크기

기본적으로 컬렉션 셀 뷰는 높이와 너비를 모두 설정해야합니다.

 

1. 컬렉션 셀 뷰의 고정 크기를 지정(오직 스토리보드에서만)

collection view의 size inspector에서 estimate Size를 none으로 사용하면 고정 크기를 사용합니다.

collection cell view의 크기는 무시됩니다.

 

2. autolayout && estimated size

스토리보드에서 collection view의 size inspector에서 estimate Size를 automatic, custom으로 사용하면 collection cell view의 고정 크기 또는 autolayout을 통한 크기를 사용합니다.

추정 크기는 테이블 뷰와 비슷하게 사용하면 됩니다.

 

 3. func collectionView(..., layout: UICollectionViewLayout,sizeForItemAt indexPath:,...)를 사용하여 개별 아이템의 크기를 지정합니다.

또는 layout 인수를 통해 기본 크기를 가져올 수도 있습니다.

 

헤더, 푸터

테이블 뷰는 헤더에 델리게이션 메소드를 통해 손쉽게 타이틀, 뷰를 설정할 수 있습니다.

하지만 컬렉션 뷰는 헤더, 푸터 같은 supplemantary뷰를 사용할 때 셀처럼 델리게이션 메소드를 통해 재사용할 수 있는 뷰를 넘겨주어야 합니다.

특히, dequeueResuableSupplementaryView(ofKind:)를 통해 재사용하며 반환 인자는 Collection Reusable View입니다.

*참고로 UICollectionCellView는 Collection Reusable View를 상속 받은 하위 클래스입니다.

 

테이블, 컬렉션 뷰와 드래그 앤 드랍

테이블 뷰와 컬렉션 뷰는 드래그 앤 드랍을 지원해줍니다.

드래그 앤 드랍을 도와주는 변수, 함수들이 있어서 해당 함수를 이용하면 편리하게 코드를 작성할 수 있습니다.

 

아이패드의 스플릿 뷰

아이패드가 항상 regular기 때문에 master와 detail을 모두 보여준다고 생각하면 안됩니다.

멀티 태스크를 사용해서 길이가 좁아지는 경우도 발생하기 때문입니다.

따라서 master 뷰에 네비게이션 컨트롤러를 달아주는 것이 collapse되었을 때 관리하기 편하게 만들어줌으로 넣어주는 것이 좋습니다.

 

edit cell

기본적으로 셀을 edit할 수 있는지 물어보는 delegation 메소드 -> Bool을 구현하지 않아도 defualt로 true값을 가지고 있습니다.

따라서 func tableView(..., commit editingStyle: UITableViewCell.EditingStyle, ...)만 구현해주면, edit 기능을 구현할 수 있습니다.

'(2017) 스탠포드 강의' 카테고리의 다른 글

13강 정리  (0) 2021.02.26
12강 정리  (0) 2021.02.23
FS3강 정리  (0) 2021.02.21
10강 정리  (0) 2021.02.21
9강 정리  (0) 2021.02.21