[Swift] File, Image 공유하기

민경준·2022년 6월 17일
0

Overview

앱을 제작 하다보면 특정 아이템을 외부로 공유하거나 외부 어플에 Export 하고 싶을 때가 있습니다.
그런 경우에는 UIActivityViewController를 통해 활용할 수 있습니다.

해당 클래스는 iOS 6.0 이상 부터 사용이 가능합니다.
iPod Touch 혹은 iPhone에서는 Modal 스타일로 띄워야 하고
iPad 에서는 Pop Over 스타일로 띄워야 합니다.


How To Use

기본적인 사용법 부터 알아보도록 하겠습니다.

/// 1. item 및 viewController 생성
let activityItems: [Any] = ["Hello, World"]
let activityViewController: UIActivityViewController = .init(activityItems: activityItems, applicationActivities: nil)

/// 1-1. iPad를 사용한다면 popOverPresentationController 설정을 위해
/// popOverPresentation을 띄울 View를 설정해준다.
activityViewController.popOverPresentationController?.sourceView = self.view

/// 2. 기본적으로 제공되는 UIActivityType 중에 사용하지 않을 Type을 지정해준다.
activityViewController.excludedActivityTypes = [.postToTencentWeibo, .postToTwitter]

/// 2-1. activityViewController가 완료되었을 때 호출되는 handler를 정의해준다.
activityViewController.completionWithItemsHandler = { type, completed, items, error in
    if completed {
        print("COMPLETED: ", items)
    } else {
        print("FAILED: ", error.debugDescription)
    }
}

/// 3. activityViewController를 present 한다.
self.present(activityViewController, animated: true)

ActivityType

/// Safari의 읽기 목록에 추가
static let addToReadingList: UIActivity.ActivityType
/// airDrop으로 공유하기
static let airDrop: UIActivity.ActivityType
/// 연락처에 내보내기
static let assignToContact: UIActivity.ActivityType
/// clipboard에 복사하기
static let copyToPasteboard: UIActivity.ActivityType
/// 이메일 보내기
static let mail: UIActivity.ActivityType
/// PDF파일로 열기 (iOS 11.0)
static let markupAsPDF: UIActivity.ActivityType
/// 메세지 보내기
static let message: UIActivity.ActivityType
/// iBooks로 열기
static let openInIBooks: UIActivity.ActivityType
/// Facebook으로 공유하기
static let postToFacebook: UIActivity.ActivityType
/// Flickr에 이미지 공유하기
static let postToFlickr: UIActivity.ActivityType
/// Tencent Weibo에 공유하기
static let postToTencentWeibo: UIActivity.ActivityType
/// Twitter에 공유하기
static let postToTwitter: UIActivity.ActivityType
/// Vimeo에 비디오 공유하기
static let postToVimeo: UIActivity.ActivityType
/// Weibo에 공유하기
static let postToWeibo: UIActivity.ActivityType
/// 프린트하기
static let print: UIActivity.ActivityType
/// 카메라 롤에 저장하기
static let saveToCameraRoll: UIActivity.ActivityType
/// SharePlay로 공유하기 (iOS 15.4)
static let sharePlay: UIActivity.ActivityType

Result


Applied

1. UIActivityItemSource

그리고 위의 방법 말고도 UIActivityViewControllerUIActivityItemSource 프로토콜을 사용하는 방법이 있습니다.

UITableView 또는 UICollectionViewdataSource를 이용하는 방법과 비슷하지만 tableView.dataSource = self 처럼 dataSource를 직접 지정해주는 방식이 없기때문에 사용법을 잘 모를수도 있습니다.

그렇다면 대체 어떻게 사용하는걸까요?

/// 1. Activity Item을 설정할 때 UIActivityItemSource 프로토콜을 준수하고 있는 UIViewController 객체를 아이템으로 지정합니다.
let activityItems: [Any] = [self]

/// 2. 해당 ViewController에 UIActivityItemSource를 상속합니다.
/// 필수 구현 함수 두 가지를 정의해줍니다.
extension ViewController: UIActivityItemSource {
	/// 이 부분은 정확히 어떤 용도로 쓰이는지 확인이 안되네요 ㅠ,ㅠ
	func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return "PlaceHolder"
    }
    
    /// Activity Type에 따라서 어떤 Data를 내보낼지 선택할 수 있습니다.
    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
        if activityType == .postToTwitter {
            return "Download #MyAwesomeApp via @twostraws."
        } else {
            return "Download MyAwesomeApp from TwoStraws."
        }
    }
}

Result

Twitter로 열었을 때


메모장으로 열었을 때


2. UIActivity

UIActivityViewController를 오픈했을 때 사용 할 Activity도 직접 생성할 수 있습니다.

기본적인 사용방법에 대해서 알아보도록 하겠습니다.
UIActivity를 서브클래싱 할 때 반드시 override 해야 할 변수와 함수가 있습니다.

/// action 또는 share 
class var activityCategory: UIActivity.Category 
/// Custom ActivityType
var activityType: UIActivity.ActivityType?
/// Activity의 이름
var activityTitle: String? 
/// Activity에 사용 될 이미지
var activityImage: UIImage?
/// 사용자가 공유 할 Item을 처리할 수 있는지 여부를 정하는 함수
func canPerform(withActivityItems: [Any]) -> Bool
/// 사용자가 공유 할 Item을 미리 받아 볼 수 있는 함수
func prepare(withActivityItems: [Any])

이렇게 총 여섯가지의 변수와 함수에 더해서 perform() 함수와 여러 커스텀 변수들을 선언해주는것으로 서브클래싱을 완성할 수 있습니다.

final class Activity: UIActivity {
    static let type: UIActivity.ActivityType = .init(rawValue: "com.yoursite.yourapp.activity")
	var _activityTitle: String
    var _activityImage: UIImage?
    var activityItems = [Any]()
    var action: ([Any]) -> Void
    
    override var activityTitle: String? {
        get {
            self._activityTitle
        }
    }
    override var activityImage: UIImage? {
        get {
            self._activityImage
        }
    }
    
    /// Activity를 생성할 때 제목과 이미지를 반드시 받고 
    /// 해당 Acitity를 선택했을 때 어떤 동작을 할지에 대한 Handler도 매개 변수로 받는다.
    init(title: String, image: UIImage?, performAction: @escaping ([Any]) -> Void) {
        self._activityImage = image
        self._activityTitle = title
        
        self.action = performAction
    }
    
    /// 직접 Type을 만들어서 지정해준다.
    override var activityType: UIActivity.ActivityType {
        return self.type
    }
	
    /// .action 과 .share 두가지 카테고리가 있다.
    override class var activityCategory: UIActivity.Category {
        return .share
    }
    
    /// 유저가 공유하는 item이 해당 Activity에서 사용가능한지 여부를 묻는 함수 
    override func canPerform(withActivityItems activityItems: [Any]) -> Bool {
        return true
    }
    
    /// 미리 item을 받아볼 수 있는 함수
    override func prepare(withActivityItems activityItems: [Any]) {
        self.activityItems = activityItems
    }
    
    /// 해당 Activity를 눌렀을 때 호출되는 함수
    override func perform() {
    	/// Handler를 호출하여 해당 Activity를 눌렀음을 알린다.
        self.action(self.activityItems)
        /// Activity에서 작업이 완료되었음을 시스템에 알려준다.
        /// 성공적으로 완료되었으면 true
        /// 에러가 있었거나 정상적으로 완료되지 않았을 때는 false를 인자로 넘겨준다.
        /// 해당 Notification을 시스템이 받게되면 UIActivityViewController를 해제한다.
        self.activityDidFinish(true)
    }
}

그리고 해당 ActivityUIActivityViewController를 생성할 때 인자로 넘겨주면 됩니다.

let title: String = "Winter"
let image: UIImage = .init(named: "winter")
let activity: Activity = .init(title: title, image: image) { items in
	guard let item = items.first as? String else { return }
	print("Item Did Shared: ", item)
}
let activityItems: [Any] = [self]
let activityViewController: UIActivityViewController = .init(activityItems: activityItems, applicationActivities: [activity])
self.present(activityViewController, animated: true)

Result

Winter라는 Activity가 추가된것이 보이시나요?
해당 Activity의 카테고리가 share일 때 위의 아이콘에 나오게 되고
action 같은 경우에는 아래에 CopySave to Files 같은 위치에 나오게 됩니다.

그럼 카테고리만 변경하면 어떻게 나오는지 볼까요?
(추가로 이미지는 시스템 이미지로 변경했습니다.)




Reference

https://developer.apple.com/documentation/uikit/uiactivityviewcontroller
https://www.zehye.kr/ios/2020/03/12/12iOS_activity_view_controller/
https://www.hackingwithswift.com/articles/118/uiactivityviewcontroller-by-example
https://www.shrikar.com/blog/ios-sharing-with-uiactivityviewcontroller-in-swift
https://royhelen.tistory.com/25
https://eeyatho.tistory.com/117

Review

간단할 줄 알았는데 생각보다 여러 형태로 커스텀도 가능해서 놀랐다...
Zoom, Facebook, Twitter 처럼 기본적인 타입 말고도 직접 만든 타입을 제공해서
내가 만든 앱에 유저가 아이템을 공유할 수 있도록 할 때 사용하면 좋을거 같다.

profile
iOS Developer 💻

0개의 댓글