일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Codegen
- 아이폰
- roundingMode
- SWIFTUI
- IOS
- human interface guidelines
- 독서후기
- Failed to register bundle identifier
- Navigation
- contentInset
- delegation
- NotificationCenter
- mvvm
- 부트캠프
- Modality
- 스타트업주니어로살아남기
- 독후감
- @available
- viewcontroller
- NumberFormatter
- Info.plist
- xcode
- Mock
- UIResponder
- 스위프트
- 야곰아카데미
- Structures and Classes
- 책후기
- SWIFT
- View Life Cycle
- Today
- Total
호댕의 iOS 개발
[iOS] 인스타그램 스토리 공유하기 (+ UIGraphicsImageRenderer) 본문
사이드 프로젝트를 하면서 화면의 특정 이미지를 인스타그램 스토리에 공유할 수 있는 기능을 구현해야했다.
인스타 공유 기능을 왜 추가했을까?
현재 만들고 있는 사이드 프로젝트의 경우 `클라이밍`을 즐기는 사람들을 대상으로 모임을 생성하고 모임에 참석할 수 있도록 해주는 서비스이다.
그런데 우리 팀에서 클라이밍을 하는 사람들 모두 클라이밍 계정을 따로 가지고 있을 정도로 본인들이 푼 문제에 대해 기록을 남기고 싶어하는 니즈가 있다고 파악을 했다.
따라서 앱 내 모임을 참석했을 때 보상으로 주는 홀드 이미지를 인스타에 공유할 수 있도록 하는 기능을 붙이게 됐다.
홀드 이미지를 스토리에 공유할 수 있는 것도 나름의 기록을 남기는 것이기 때문이다.
그럼 어떻게 인스타 공유를 할 수 있을까?
Meta for Developers 문서에 보면 `스토리에 공유하기` 탭이 존재한다.
여기서 iOS의 경우 기본적으로 맞춤 URL Scheme을 사용해 인스타그램 앱을 실행하고 콘텐츠를 전달한다고 설명하고 있다.
Paste Board에 공유하고자 하는 컨텐츠를 할당하고 이를 전달하도록 하는 것이다.
데이터의 경우 다음과 같은 형태로 전달된다.
1. URL Scheme 등록
일단 인스타그램 공유 관련 맞춤 URL Scheme을 Info.plist에 등록해야 한다.
Queried URL Schemes Key에 instagram-stories를 value로 추가해주면 된다.
2. 공유하고자 하는 이미지를 구성
내가 공유하려고 했던 이미지는 화면의 이미지와는 약간 달랐다.
거의 비슷하긴 했지만 화면 비율이나 들어가는 Label들이 차이가 있었다.
따라서 인스타 공유를 위한 View를 따로 구현해주었다.
// MARK: - 인스타 공유 이미지
private let shareContainerView = UIView().then {
$0.contentMode = .scaleAspectFit
}
private let shareBackground = UIImageView().then {
let imageName = [
"bg_insta_1",
"bg_insta_2",
"bg_insta_3",
"bg_insta_4"
].randomElement() ?? "bg_insta_1"
$0.image = UIImage(named: imageName)
}
private let shareHoldImage = UIImageView().then {
$0.contentMode = .scaleAspectFit
}
private let sharePlaceLabel = UILabel().then {
$0.text = "장소"
$0.textColor = .white
$0.textAlignment = .center
$0.font = .pretendard(family: .semiBold, size: 24)
}
private let shareDateLabel = UILabel().then {
$0.text = "날짜"
$0.textColor = .gray5
$0.textAlignment = .center
$0.font = .pretendard(family: .regular, size: 16)
}
그리고 레이아웃의 경우 앱의 화면 구성에는 필요하지 않았기에 스크린 밖에 공유하려는 이미지가 위치할 수 있도록 해서 메모리에 UIView는 올리더라도, 화면에서 보이지는 않도록 했다.
(이 부분에 대해선 더 좋은 방법이 있을 것 같긴 한데, 일단 생각나는 방법이 이와 같아서 이렇게 적용해봤다)
3. 이미지를 렌더링해서 뽑아내기
이제 이미지를 렌더링해서 Pasteboard에 올린 후
instagram-stories://share
위 URL로 인스타그램을 열어 데이터만 전달해주면 된다.
일단 UIView로 구성되어 있고 ContainerView 위에 label과 imageView가 올라가 있는 형태라 이를 합쳐서 UIImage로 변경해줘야 한다.
그래서 사용한 것이 바로 UIGraphicsImageRenderer이다.
let renderer = UIGraphicsImageRenderer(
size: viewController.shareContainerView.bounds.size
)
let renderImage = renderer.image { _ in
viewController.shareContainerView.drawHierarchy(
in: viewController.shareContainerView.bounds,
afterScreenUpdates: true
)
}
guard let imageData = renderImage.pngData() else { return }
일단 Size의 경우 shareContainerView 전체를 잡아야 하는 만큼 shareContainerView의 bounds의 사이즈만큼 UIGraphicsImageRenderer의 사이즈를 잡아줬다. init(size: CGSize)를 사용하면 지정한 크기의 이미지를 만들기 위한 이미지 렌더러를 생성하게 된다.
그리고 UIView의 drawHierarchy 메서드를 사용하면 현재 화면에 표시되는 View의 전체 계층의 스냅샷을 렌더링하게 된다.
afterScreenUpdates 파라미터의 경우 최근에 변경된 사항까지 통합해서 렌더링을 할 것인지에 대해 설정하는 것이다. 나는 변경사항도 통합해서 UIImage로 렌더링하기 위해 true로 값을 줬다.
이렇게 해서 renderer.image로 렌더링된 UIImage를 반환하게 된다.
UIImage로 계층화된 View를 통합해서 스냅샷을 찍어 렌더링을 완료했다면 이제 이를 PNG나 JPG 양식으로 변경해서 Pasteboard에 할당해줘야 한다.
위에 있는 이미지에서도 이미 나온 내용이긴 한데 이미지Asset의 경우 JPG, PNG 형식의 데이터만 지원한다고 나와있기 때문이다.
나는 UIImage에서 pngData()를 통해 PNG 형식의 데이터로 변환해줬다.
4. Pasteboard에 변환한 데이터 할당하기
let pasteboardItems : [String:Any] = [
"com.instagram.sharedSticker.stickerImage": imageData
]
let pasteboardOptions = [
UIPasteboard.OptionsKey.expirationDate: Date().addingTimeInterval(300)
]
UIPasteboard.general.setItems([pasteboardItems], options: pasteboardOptions)
나는 통으로 전달할 이미지를 렌더링했기 때문에 Sticker Image로 보내주면 된다.
만약 다른 데이터를 전달한다면 아래 코드처럼 Key값을 적어주면 된다.
com.instagram.sharedSticker.backgroundImage // 배경 이미지 Asset
com.instagram.sharedSticker.backgroundTopColor // 배경 레이어 상단 색상
com.instagram.sharedSticker.backgroundBottomColor // 배경 레이어 하단 색상
그리고 UIPasteboard에 값을 할당할 때 옵션을 정해줘야 한다.
이 옵션은 pasteboard에서 해당 데이터를 언제 만료시킬지 정해주는 것이다.
이외에도 UIPasteboard.OptionKey에서 localOnly 타입 프로퍼티를 사용해 다른 기기에서 저장한 데이터를 사용할 수 있도록 할 지에 대한 여부도 정해줄 수 있다.
setItems를 통해 데이터를 할당해줬으면 이제 인스타그램으로 데이터만 넘기기만 하면 끝이다.
5. 인스타그램 앱을 열어 데이터 전달하기
UIApplication.shared.open(storyShareURL, options: [:], completionHandler: nil)
이 코드를 사용해 instagram-stories://share 이 URL을 열게 되면 Pasteboard에 할당했던 데이터를 스토리에 공유할 수 있도록 인스타그램 앱이 열리게 된다.
그럼 인스타그램에서 공유를 하기만 하면 끝~~~
공유하는 방법 자체가 엄청 어렵고 복잡하진 않지만 무조건 방법을 알아야 적용할 수 있는 기능이었던 것 같다.
뷰 생성을 제외한 전체 코드
private func configureShareInstaButton() {
shareInstaButton.rx.tap
.withUnretained(self)
.subscribe(onNext: { viewController, _ in
if let storyShareURL = URL(string: "instagram-stories://share") {
if UIApplication.shared.canOpenURL(storyShareURL) {
viewController.shareHoldImage.image = viewController.holdImageView.image
viewController.sharePlaceLabel.text = viewController.placeLabel.text
viewController.shareDateLabel.text = viewController.dateLabel.text
let renderer = UIGraphicsImageRenderer(
size: viewController.shareContainerView.bounds.size
)
let renderImage = renderer.image { _ in
viewController.shareContainerView.drawHierarchy(
in: viewController.shareContainerView.bounds,
afterScreenUpdates: true
)
}
guard let imageData = renderImage.pngData() else { return }
let pasteboardItems : [String:Any] = [
"com.instagram.sharedSticker.stickerImage": imageData
]
let pasteboardOptions = [
UIPasteboard.OptionsKey.expirationDate: Date().addingTimeInterval(300)
]
UIPasteboard.general.setItems([pasteboardItems], options: pasteboardOptions)
UIApplication.shared.open(storyShareURL, options: [:], completionHandler: nil)
}
}
})
.disposed(by: disposeBag)
}
참고 문서
- https://developers.facebook.com/docs/instagram/sharing-to-stories
- https://developer.apple.com/documentation/uikit/uigraphicsimagerenderer
- https://developer.apple.com/documentation/uikit/uiview/1622589-drawhierarchy
'Software Engineering > iOS' 카테고리의 다른 글
[iOS] AB 테스트를 위한 툴, Hackle (0) | 2022.11.07 |
---|---|
[iOS] Universal Links에 대해 알아보자 (0) | 2022.10.27 |
[iOS] 푸쉬 알림(Push Notification) 어떻게 할까? (OneSignal, FCM, Foreground에서 알림 받지 않기 등등) (0) | 2022.09.29 |
[Xcode] Developer Mode(개발자 모드)가 필요합니다...? (0) | 2022.09.19 |
[iOS] 특정 모서리만 둥글게 처리하는 방법 (layer.maskedCorners) (0) | 2022.09.13 |