아이폰 기본 앱에 있는 공유 버튼은 위 사진과 같이 공유를 할 수 있게 됩니다.
그것을 사용하기 위해서는 UIActivityViewController를 띄우기만 하면 끝입니다.
UIActivityViewController는 무엇일까?
항목 복사, 소셜 미디어 사이트에 콘텐츠 게시, 이메일 또는 SMS를 통한 항목 전송 등과 같은 몇 가지 표준 서비스를 제공하는데 사용하는 ViewController 라고 되어있습니다.
그리고 iPad에서는 popover에서 표현해야 하고 iPhone 및 iPod touch 에서는 모달로 표시해야 합니다.
H.I.G 에서 말하는 Activity View는 무엇일까?
자주 사용하는 앱에 빠르게 액세스할 수 있을 뿐만 아니라 메시징과 같은 공유 활동과 복사 및 인쇄와 같은 작업을 제공합니다.
권장사항
- 활동 보기에서 이미 사용할 수 있는 일반적인 작업의 중복 버전 금지
- 기존 작업과 유사한 앱별 기능을 제공해야 하는 경우 사용자 지정 제목을 지정
- 기호를 사용하여 사용자 지정 활동을 나타내기
- 제공하는 각 사용자 지정 작업에 대해 간결하고 설명적인 제목을 작성
- 단일 동사 또는 동작이 수행하는 작업을 명확하게 전달하는 간단한 동사를 선호
- 작업 제목에 회사 또는 제품 이름을 포함 금지
- 활동이 현재 컨텍스트에 적합한지 확인
- 적용되지 않는 활동 제외
- 공유 버튼을 사용하여 활동 보기를 표시
extension
- 필요한 경우 사람들에게 친숙하게 느껴지는 사용자 지정 인터페이스
- 필요한 경우 확장의 목적을 전달하는 이미지를 제공
- 기본 앱을 사용하여 시간이 오래 걸리는 작업의 진행 상황을 표시
왜 사용해야 할까?
사람들은 단 몇 단계만으로 작업을 수행할 수 있는 확장 프로그램을 높이 평가합니다.
예를 들어, 공유 확장 프로그램은 탭이나 클릭 한 번만으로 소셜 미디어 계정에 이미지를 즉시 게시할 수 있는 경험입니다.
언제 사용해야 하는지?
이 부분은 WWDC17, Extend Your App's Presence With Sharing 을 보면
- 앱의 `Content`와 `experiences`에 대해 생각해보라 합니다.
- 사용자가 어떤 `Content`를 공유하고 싶은지
- 언제 공유하는 것이 의미 있는 `Timing` 인지
- 공유하고 싶을 만큼 `Content`를 충분히 알고 있는지
- 공유 이미지의 버튼을 사용하여 공유를 시작하고 상황에 맞는 배치를 고려
사용하는 방법에 대한 이야기
UIActivityViewController 생성
UIActivityViewController.init(
activityItems: [Any],
applicationActivities: [UIActivity]?
)
activityItems은 실제 데이터 개체(`String`, `Image objects`) 대신 `UIActivityItemProvider`, `UIActivityItemSource` 프로토콜을 채택하는 개체일 수 있습니다.
applicationActivities는 앱에서 지원하는 사용자 지정 서비스를 나타내는 `UIActivity`입니다.
var shareObject = [Any]()
shareObject.append(image)
let activityViewController = UIActivityViewController(activityItems : shareObject, applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
여기서 문제가 발생합니다. 원하는 건 이게 아닌데 말이죠
- 공유되는 이미지가 표시되지 않습니다.
- 제목, 부제목이 표시되지 않습니다.
iOS 13 이후에는 Share Sheet의 thumbnail을 가속화하기 위해 LPLinkMetadata를 제공합니다.
WWDC 19, What's New in Sharing 에서는 새로운 UIActivityViewController라고 이야기하며, 특징에 대해서 이야기합니다.
- 상단에 new preview header
- 누구와 빠르게 공유하고 싶은지 제안
- 작업 목록을 통한 앱과 작업을 명확하게 구분
iOS 13에는 앱에 rich links를 표시할 수 있는 LinkPresentation API가 도입되었습니다.
preview는 앱에서 무엇을 공유하는지 이해를 돕는 역할입니다.
LPLinkMetadata는 header에 표시되는 부분은 이미 Safari에 이미 정보가 있기 때문에 정보를 가져와서 보여주기 보다는 가진 정보를 가속화하여 만들 수 있도록 하는 것입니다. (자세한 부분은 WWDC 영상을 참고)
func activityViewControllerLinkMetadata(_: UIActivityViewController) -> LPLinkMetadata? {
let metadata = LPLinkMetadata()
metadata.originURL = URL(string: "원본 URL")
metadata.url = metadata.originURL
metadata.title = "Banana Bread Recipe"
metadata.iconProvider = NSItemProvider(object: self.webpageIcon!)
return metadata
}
`LPLinkMetadata`를 사용하면서 정보를 즉시 사용할 수 있었고 `Share Sheet`는 추가 메타데이터 가져오기를 수행할 필요가 없게 됩니다.
위 코드를 사용하기 위해서 UIActiviyItemSource를 채택한 ImageProvider를 만들어서 사용했습니다.
final class ShareableImageProvider: NSObject, UIActivityItemSource {
private let image: UIImage
private let index: Int
init(image: UIImage, index: Int) {
self.image = image
self.index = index
super.init()
}
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return image
}
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
return image
}
func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
guard let jpgImage = image.jpegData(compressionQuality: 1.0) else { return nil }
let metadata = LPLinkMetadata()
metadata.title = "\(index+1)번째 이미지를 공유합니다."
metadata.originalURL = URL(filePath: "JPEG File · \(jpgImage.fileSize())")
metadata.imageProvider = NSItemProvider(object: image)
return metadata
}
}
private extension Data {
func fileSize() -> String {
let size = Double(self.count)
if size < 1024 {
return String(format: "%.2f bytes", size)
} else if size < 1024 * 1024 {
return String(format: "%.2f KB", size/1024.0)
} else {
return String(format: "%.2f MB", size/(1024.0*1024.0))
}
}
}
let imageProvider = ShareableImageProvider(image: image, index: currentIndex)
let activityViewController = UIActivityViewController(activityItems : [imageProvider], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
Preview에서 imageProvider를 통해 image를 보여주며, title은 원하는 제목 그리고 originURL은 String을 URL로 변환하면서 부제목으로 사용할 수 있게 됩니다.
UIActivityViewController은 iOS 14 부터 새로운 생성이 도입됩니다.
convenience init(activityItemsConfiguration: any UIActivityItemsConfigurationReading)
위와 같이 만들려면 아래와 같은 Provider를 만들어서 사용할 수 있습니다.
final class ShareableProvider: NSObject {
private var image: UIImage
private var index: Int
init(image: UIImage, index: Int) {
self.image = image
self.index = index
super.init()
}
}
extension ShareableProvider: UIActivityItemsConfigurationReading {
var itemProvidersForActivityItemsConfiguration: [NSItemProvider] {
[NSItemProvider(object: image)]
}
func activityItemsConfigurationMetadataForItem(at index: Int, key: UIActivityItemsConfigurationMetadataKey) -> Any? {
switch key {
case .linkPresentationMetadata:
guard let jpgImage = image.jpegData(compressionQuality: 1.0) else { return nil }
let metadata = LPLinkMetadata()
metadata.title = "\(self.index+1)번째 이미지를 공유합니다."
metadata.originalURL = URL(filePath: "JPEG File · \(jpgImage.fileSize())")
metadata.imageProvider = NSItemProvider(object: image)
return metadata
default:
return nil
}
}
}
private extension Data {
func fileSize() -> String {
let size = Double(self.count) // in bytes
if size < 1024 {
return String(format: "%.2f bytes", size)
} else if size < 1024 * 1024 {
return String(format: "%.2f KB", size/1024.0)
} else {
return String(format: "%.2f MB", size/(1024.0*1024.0))
}
}
}
UIActivityItemsConfigurationReading 의 문서를 보게 되면 optional func 여러 개가 있습니다.
그 중에 두 가지를 사용해봤는데, 사용이 조금 다르다는 것을 볼 수 있었습니다.
🚨`activityItemsConfigurationMetadata` 와 `activityItemsConfigurationMetadataForItem` 다른점
- `func activityItemsConfigurationMetadata(key:)` 를 정의할 때에는 같은 코드여도 key가 title로 적용
- `func activityItemsConfigurationMetadataForItem(at:key:)` 로 정의할 때에는 key가 linkPresentationMetadata로 적용
원하는 preview를 표현하기 위해서는 위와 같은 코드로 작성하게 되었습니다.
고민해봐야 할 문제
아무리 LPLinkMetadata를 사용한다 하여도 여러 개의 개체를 공유할 때에는 기본 사진 앱과 같이 동작되지 않습니다.
UIActivityView 자체를 Custom 해서 제작해야 하는 걸로 추측이 됩니다.
GitHub의 코드들, 구글링, GPT 모두 해결하지 못하였고, 공식 문서의 여러 가지를 시도를 해봤지만 원하는 부분이 해결되지 않았습니다.
(카카오톡에서도 이미지를 외부 공유하기 버튼을 눌러도 기본 앱과 같이 나오지 않는 걸 확인할 수 있습니다)
이왕 이렇게 된 거 UIActivityView를 Custom 하여 원하는 형태로 할 수 있는지 확인하는 포스팅으로 들고 오겠습니다.
'Language > Swift' 카테고리의 다른 글
[Swift] Reactive Programming Combine - 1 (1) | 2024.05.01 |
---|---|
[Swift] Protocol-Oriented Programming: POP (0) | 2024.04.30 |
[Swift] 알고리즘을 대비한 메서드들 (0) | 2024.02.18 |
[Swift] UIImage, CGImage 그리고 CIImage 언제 사용되나 (0) | 2024.02.14 |
[Swift] Timer vs DispatchSourceTimer (1) | 2023.11.26 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!