본문으로 바로가기

[iOS][iPadOS] Drag and Drop

category ios 개발/iOS 2022. 5. 5. 13:07

드래그 앤 드롭에 대해서 공부해봅시다

 

드래그 앤 드롭이 뭐냐면

dran and drop

 

이런것임ㅇㅇ

 

아이패드의 경우에는 이렇게 다른앱간으로도 드래그앤드롭이 가능하고 단일앱에서도 드래그앤드롭이 가능함

아이폰은 iOS 15 이전에는 단일앱에서만 가능했지만 iOS 15 이후부턴 다른앱간으로 드래그 앤 드롭이 가능하다함

 

? 아이폰은 한번에 두개의 앱을 동시에 실행하지 못하는데 어찌가능함 ?

아이폰에서의 드래그 앤 드롭

이렇게 가능함

 

일단 드래그할 앱을 소스앱이라 하고 드롭할 앱을 목적지앱이라 하겠음

일단 소스앱에서 이미지, 텍스트같은 아이템(데이터)을 드래그하게 되면 아이템을 인코딩하여 NSItemProvider 안에 포장하고 이것을 [UIDragItem]으로 다시한번 포장해서 목적지앱에 드롭하면 목적지앱이 포장을 풀고 디코딩하는 방식으로 이루어져있음 (SwiftUI에선 그냥 NSItemProvider배열을 보내는것 같드라...)

 

이때 소스앱은 Uniform Type Identifier을 제공하여 드래그된 객체의 유형을 정의함

자 여기서 Uniform Type Identifier이 무엇이냐 하면 

'A Uniform Type Identifier (UTI) is a text string used on software provided by Apple Inc. to uniquely identify a given class or type of item'

번역하면

'UTI(Uniform Type Identifier)는 주어진 클래스나 항목 유형을 고유하게 식별하기 위해 Apple Inc.가 제공하는 소프트웨어에 사용하는 텍스트 문자열입니다.'

음... 뭔소린지 모르겠음...

그냥 쉽게말해 어떠한 파일의 형식을 식별하는 문자열이라고 생각하면 될듯함

uti 더 알아보기

https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/understanding_utis/understand_utis_intro/understand_utis_intro.html#//apple_ref/doc/uid/TP40001319

 

Introduction to Uniform Type Identifiers Overview

Introduction to Uniform Type Identifiers Overview One of the challenges facing application developers is the proliferation of methods to identify types of data. For example, some text files may be assigned a 'TEXT' file type (as originally designed for Mac

developer.apple.com

 

하여튼 중요한건 이게 아니고 NSItemProvider에 대해서 더 알아보겠음

소스앱에서 데이터를 NSItemProvider에 감싸줄려면 해당 데이터의 클래스에 NSItemProviderWriting이라는 프로토콜을 채택해줘야함

반대로 목적지앱에서 NSItemProvider을 풀어서 디코딩할려면 NSItemProviderReading이란 프로토콜을 채택해줘야함

 

근데 우리는 이미지를 드래그 앤 드롭 할거잖슴

그런데 NSString, NSAttributedString, NSURL, UIColor, UIImage는

이미 위 두 프로토콜을 채택하고있음 ㄱㅇㄷ

만약 커스텀 클래스를 만들었고 이 클래스를 전달하고싶다면 꼭 위 두 프로토콜을 채택해줘야함

 

그럼 일단 함 시작해보자

 

1. drag and drop Interaction 만들기

class ViewController: UIViewController, UIDragInteractionDelegate, UIDropInteractionDelegate {
    
    @IBOutlet weak var imageView: UIImageView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        let dragInteraction = UIDragInteraction(delegate: self)
        let dropInteraction = UIDropInteraction(delegate: self)
        imageView.addInteraction(dragInteraction)
        imageView.addInteraction(dropInteraction)
        imageView.isUserInteractionEnabled = true
        
    }
}

일단 이미지뷰에 dragInteraction이랑 dropInteraction을 추가해줌

여기서 delegate를 self로 해주기때문에 UIDragInteractionDelegateUIDropInteractionDelegate를 채택해줘야함

그리고 필수로 이미지뷰의 isUserInteractionEnabledtrue로 해줘야함

이거 안해주면 이미지뷰는 어떠한 상호작용도 안일어남ㅇㅇ

 

2. 일단은 드래그먼저

UIDragInteractionDelegate을 채택해주면 필수구현메서드가 하나 있음

func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
        guard let image = imageView.image else { return [] }
        let provider = NSItemProvider(object: image)
        let item = UIDragItem(itemProvider: provider)
        return [item]
    }

이 메서드인데 하나하나 뜯어보겠음

아까 드래그를 할때 아이템을 NSItemProvider로 감싼뒤 UIDragItem배열로 한번더 감싸서 리턴한다고 햇잖슴

이 메서드가 하는 일이 그거임

imageView의 이미지를 NSItemProvider의 오브젝트로 넣어준다음 이 provider를 가지고있는 UIDragItem 배열을 리턴해주면 끝임

그럼 이렇게 드래그가 가능해짐

벗 보다시피 드롭은 아직 안되는데 아직 드롭관련 메서드를 구현 안해줘서 그럼

파일앱에서 드롭이 가능한 이유는 드롭관련 메서드가 구현 돼있기때문이겠지

 

3. 이번엔 드롭

드롭에선 두가지 메서드를 구현해줘야함

func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
        return UIDropProposal(operation: .copy)
    }

이 메서드는 아이템이 드롭됐을때 어떻게 처리할건지에 대한 메서드임

operation에 작업유형을 설정할수 잇는데

.cancel - 데이터가 전송되지 않도록 지정하여 드래그를 취소하도록 지정하는 유형

.forbidden - 이동 또는 복사 작업이 일반적으로 가능하지만 드롭을 허용하지 않겠다 하는 유형

.copy - 드래그 항목으로 표시되는 데이터를 대상 보기로 복사하도록 지정하는 유형

.move - 드래그 항목으로 표시되는 데이터를 복사하는 것이 아니라 이동하는 유형

이 있음

func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
        for item in session.items {
            let itemProvider = item.itemProvider
            guard itemProvider.canLoadObject(ofClass: UIImage.self) else { continue }

            itemProvider.loadObject(ofClass: UIImage.self) { image, _ in
                guard let image = image as? UIImage else {
                    return
                }
                DispatchQueue.main.async {
                    self.imageView.image = image
                }
            }
        }
    }

두번째 메서드인데 이 메서드는 아이템이 드롭됐을때 처리할 동작들을 구현하는 메서드임

드롭시 데이터가 UIDropSession 타입으로 오는데 이 타입의 items프로퍼티에 소스앱에서 보낸 UIDragItem 배열이 있음

UIDragItem에서 itemProvider을 꺼내고 이 itmeProvider의 데이터를 canLoadObject메서드로  UIImage타입으로 변환할수 있는지 확인을 한다음 변환이 가능하면 변환을 loadObject메서드로 UIImage로 변환시킨다음 imageView의 이미지에 넣어주는 작업을 하고있음

 

여기서 주의할점은 loadObject는 글로벌큐에서 동작하기 때문에 imageView에 넣어주는 작업은 main큐로 보내서 작업을 해아한다는 점임

 

하여튼 이 두가지 메서드를 다 구현하면 

이렇게 드롭이 가능해지는거임

 

끝!

 

전체코드

https://github.com/jmindeveloper/DragAndDrop

 

GitHub - jmindeveloper/DragAndDrop: iOS & iPadOS Drag and Drop

iOS & iPadOS Drag and Drop. Contribute to jmindeveloper/DragAndDrop development by creating an account on GitHub.

github.com

 

참고

https://www.raywenderlich.com/11632532-ipados-multitasking-using-multiple-windows-for-your-app

 

iPadOS Multitasking: Using Multiple Windows for Your App

In this iPadOS Multitasking tutorial, you’ll learn how to get the most out of iPad screens and multitasking features for your app.

www.raywenderlich.com

https://www.raywenderlich.com/3121851-drag-and-drop-tutorial-for-ios

 

Drag and Drop Tutorial for iOS

In this drag and drop tutorial you will build drag and drop support into UICollectionViews and between two separate iOS apps.

www.raywenderlich.com

https://zeddios.tistory.com/1024

 

iPadOS ) Drag and Drop (1)

안녕하세요 :) Zedd입니다. 오늘은 드래그 앤 드롭에 대해서 공부해보려고 합니다. www.developer.apple.com/documentation/uikit/drag_and_drop 드래그 앤 드롭도 꽤나 큰 기능(?)인 것 같아요. 문서화가 아주 챱..

zeddios.tistory.com

https://developer.apple.com/documentation/uikit/uidropoperation

 

Apple Developer Documentation

 

developer.apple.com