호댕의 iOS 개발

[iOS] ActivityViewController를 커스텀하고, 이를 통해 공유하기 본문

Software Engineering/iOS

[iOS] ActivityViewController를 커스텀하고, 이를 통해 공유하기

호르댕댕댕 2022. 5. 30. 22:23

 

특정 아이템을 다른 외부 SNS로 공유하거나, 전달할 때, 복사할 때 AcitivityViewController를 사용할 수 있다. 

 

이외에도 즐겨찾기, 북마크, 찾기와 같은 작업을 수행할 수 있다. 

iPad에선 반드시 popover를 통해 ActivityView를 present해야 하며, iPhone과 iPod touch의 경우 modal로 present를 해줘야 한다. 

 

ActivityViews의 H.I.G 문서 보기

시스템에선 다양한 프린트를 포함한, 메시지, 에어플레이 등 다양한 기본 활동을 제공한다. 이는 재정렬될 수는 없다. 

 

사용자 정의 Activity를 표현할 때에는 간단한 이미지를 지정해줘야 한다.

이는 아이콘을 통해 표현이 되어야 하며 그림자를 포함하지 않고 적당한 투명도와 Antialiasing이 필요하다. 이미지의 경우 반드시 중앙에 와야 하며 70px X 70px크기여야 한다. 

 

작업을 간결하게 설명하는 Activity의 제목을 만들어라. 

제목은 ActivityView의 아이콘 아래에 나타난다. 짧은 제목이 효과적이며 제목이 너무 길면 잘리게 된다. 일반적으로 제목에 회사 또는 제품의 이름이 들어가지 않는 것이 좋다. 

 

Activity가 현재 상황에 적합한지 확인하라. 

시스템 제공 작업의 경우 재정렬할 수는 없지만 현재 앱에 적용할 수 없는 작업이라면 제외를 할 수 있다. 

이는 UIActivityViewController의 프로퍼티인 excludedActivityTypes에 제외할 작업을 추가해주면 된다. 

 

ActionButton을 활용하여 ActivityView를 표시하라. 

사람들은 ActionButton을 탭했을 때 시스템에서 제공하는 Activity에 접근할 수 있는 것에 익숙하다. 

따라서 다른 방법을 통해 ActivityView에 접근하도록 하지 마라.

ActionButton

 

ActivityView를 커스텀하기

그렇다면 ActivityView에 어떻게 원하는 아이콘, 제목, 내용을 정할 수 있을까?

이는 UIActivityItemSource 프로토콜을 준수하여 커스텀해줄 수 있다. 

 

해당 프로토콜의 경우 ActivityViewController가 작업할 데이터 항목들을 검색하는데 사용할 수 있는 메서드의 집합이다. 

이 프로토콜을 구현하게 되면 객체는 데이터 공급자가 되어 ViewController에게 items들에 대한 접근 권한을 제공하게 된다. 이 프로토콜의 메서드들은 모두 Main 쓰레드에서 동작을 하기 때문에, 반드시 데이터 객체를 만드는데 시간이 많이 걸리지 않는 경우만 사용을 해야 한다. 

 

(만약 데이터 객체를 만드는데 많은 시간이 걸린다면 UIActivityItemProvider 객체를 생성하는 것을 고려해라)

 

Data Items를 가져오는 필수로 구현해야 하는 메서드

  • activityViewControllerPlaceholderItem(:) -> Any : 데이터를 위한 placeholder를 반환
  • activityViewController(:itemForActivityType:) -> Any : 작업을 수행할 데이터 객체를 반환

Data Items 관련해서 정보를 제공하는 메서드 

  • activityViewController(:subjectForActivityType:) -> String : 만약 Activity의 제목 필드를 구현한다면 item에 대한 subject 객체를 반환함
  • activityViewController(:dataTypeIdentifierForActivityType:) -> String : 데이터로 제공되는 item의 경우 item에 대한 UTI(균일 타입 식별자)를 반환함
  • activityViewController(:thumbnailImageForActivityType:suggestedSize:) -> UIImage : 프리뷰 이미지를 지원하는 경우 썸네일 이미지를 반환함 

빠른 미리보기를 위한 메타 데이터 제공

  • activityViewControllerLinkMetaData(:)-> LPLinkMetaData? : share sheet의 프리뷰 헤더에 들어가는 메타데이터를 반환한다. 

 

이는 LinkPresentation을 import해야 사용할 수 있다.

또한 NSObjectProtocol을 상속받고 있는데, Cocoa의 rootClass인 NSObject는 이 프로토콜을 채택하고 있다. 따라서 NSObject를 상속한 모든 객체는 이 프로토콜의 특성을 가지고 있다

 

import LinkPresentation
import UIKit

final class SharePinNumberActivityItemSource: NSObject, UIActivityItemSource {
    private var title: String
    private var content: String
    
    init(title: String, content: String) {
        self.title = title
        self.content = content
        super.init()
    }
    
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return content
    }
    
    func activityViewController(
        _ activityViewController: UIActivityViewController,
        itemForActivityType activityType: UIActivity.ActivityType?
    ) -> Any? {
        return content
    }
    
    func activityViewController(
        _ activityViewController: UIActivityViewController,
        subjectForActivityType activityType: UIActivity.ActivityType?
    ) -> String {
        return title
    }
    
    func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
        let metaData = LPLinkMetadata()
        guard let iconImage = UIImage(named: "meat") else {
            return LPLinkMetadata()
        }
        metaData.title = title
        metaData.iconProvider = NSItemProvider(object: iconImage)
        metaData.originalURL = URL(fileURLWithPath: content)
        return metaData
    }
}

필수로 구현해야 하는 2개의 메서드에선 content를 반환해주면 된다. 

또한 LPLinkMetaData에서 originalURL에 값을 할당해주어야 아래 그림의 빨간 박스처럼 나오게 된다. 

 

이를 사용하는 ViewController에선 위에서 정의한 UIActivityItemSource 프로토콜을 준수하는

SharePinNumberActivityItemSource를 통해 items 배열을 생성해야 한다. 

 

이를 통해 UIActivityViewControllerinit(activityItems:ApplicationActivities:)에 값을 넣어주면 된다. 

 

private func configureShareButtonDidTap(with shareButtonDidTap: Observable<Void>) {
    shareButtonDidTap
        .observe(on: MainScheduler.instance)
        .subscribe(onNext: { [weak self] in
            guard let pinNumber = self?.pinNumberLabel.text else { return }

            let title = "[우리뭐먹지] 팀원과 PIN 번호를 공유해보세요"
            let content = """
            [우리뭐먹지] 팀원이 공유한 \(pinNumber)

            PIN 번호를 통해 입장하여 오늘의 메뉴를 골라보세요
            """
            let items = [SharePinNumberActivityItemSource(title: title, content: content)]

            let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
            activityViewController.popoverPresentationController?.sourceView = self?.shareButton
            activityViewController.popoverPresentationController?.permittedArrowDirections = .down
            self?.present(activityViewController, animated: true)
        })
        .disposed(by: disposeBag)
}

rx를 통해 공유하기 버튼을 눌렀을 때 ActivityView를 띄우는 메서드이다. 

여기서 popoverPresentationController를 정의해주는 이유는 iPad의 경우 popover를 통해 ActivityView를 띄워줘야 하기 떄문이다. 

Comments