일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 자바스크립트
- JavaScript
- error
- 코드트리
- react
- TypeScript
- SWIFT
- 코딩테스트
- git
- 프로젝트
- 회고
- 프론트엔드
- 알고리즘
- ios
- frontend
- Front-end
- globalcommunity
- UIKit
- react-query
- 프로그래머스
- 코딩테스트실력진단
- Apple Developer Academy
- velog
- iOSDeveloper
- tshaped
- NextJs
- Xcode
- 코드트리챌린지
- AppleDeveloperAcademy
- swiftUI
- Today
- Total
Moon Work
SwiftUI에서 화면 캡쳐하기(with UIKit / UIGraphicsImageRenderer) 본문
SwiftUI에서 화면 캡쳐하기
최근에 하는 프로젝트에서 image를 만들어서 instagram으로 공유하는 기능을 구현해야 했다. 먼저는 화면의 이미지를 image로 렌더해야 하는 기능을 구현해야 해서 UIGraphicsImageRenderer를 활용한 캡쳐 기능을 구현하였다.
구현 순서
1. Target으로하는 View를 구현한다.
- 해당 뷰를 그대로 이미지로 만들기 때문에 따로 View를 만들거나 아래와 같이 바뀌는 내용이 없다면 extension에 computed property로 구현해도 된다.
// 렌더해야 하는 뷰
extension RendererTestView {
private var TargetImageView: some View {
ZStack{
Color.white
VStack {
Text("HELLO\nWORLD")
.font(.title)
.multilineTextAlignment(.center)
}
.padding(40)
.background(.purple)
.foregroundColor(.white)
.shadow(color: .purple, radius: 10)
}
}
}
2. TargetImageView의 사이즈를 GeometryReader를 통해 저장한다.
GeometryReader { geo in
TargetImageView
.onAppear {
self.geoSize = CGSize(width: geo.size.width, height: geo.size.height)
}
}
3. 렌더 버튼을 클릭할 경우 아래와 같이 asImage를 통해 이미지를 렌더한다.
Button {
renderImage = TargetImageView.asImage(size: self.geoSize)
} label: {
Text("generate image")
}
4. View에 대한 extension에 아래와 같이 UIHostingController를 통해 해당 View를 UIView로 사용가능하게 한다.
extension View {
func asImage(size: CGSize) -> UIImage {
let controller = UIHostingController(rootView: self)
controller.view.bounds = CGRect(origin: .zero, size: size)
let image = controller.view.asImage(size: size)
return image
}
}
5. UIView에 대해서 역시 Extension을 통해 asImage를 구현한다. UIGraphicsImageRender를 통해 해당 뷰의 크기만큼 이미지를 렌더해서 return해준다.
extension UIView {
func asImage(size: CGSize) -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = 1
return UIGraphicsImageRenderer(size: size, format: format).image { context in
self.drawHierarchy(in: self.layer.bounds, afterScreenUpdates: true)
}
}
}
6. 받은 이미지를 보여주거나 처리한다.
struct RendererTestView: View {
@State var geoSize: CGSize = .init(width: 0, height: 0)
@State var renderImage: UIImage?
var body: some View {
VStack {
GeometryReader { geo in
TargetImageView
.onAppear {
self.geoSize = CGSize(width: geo.size.width, height: geo.size.height)
}
}
// Image를 받은 경우 unWrapping해서 보여준다.
if let uiImage = renderImage {
Image(uiImage: uiImage)
} else {
Spacer()
.frame(height: 200)
}
Button {
renderImage = TargetImageView.asImage(size: self.geoSize)
} label: {
Text("generate image")
}
}
.padding()
}
}
UIGraphicsImageRenderer
[iOS] - UIGraphicsImageRenderer
자세히 - Apple documentCore Graphics 지원 이미지를 만들기 위한 그래픽 렌더러입니다.애플문서에는 다음과 같이 설명되어 있는데UIGraphicsImageRenderer 이니까 UIKit과 관련 있는 클래스 인것 같은데 Core Gra
velog.io
Core Graphic에서 지원지원하는 렌더러로 UIGraphicsImageRenderer를 통해 UIImage를 렌더할 수 있다.
[UIGraphicsImageRendererFormat](https://developer.apple.com/documentation/uikit/uigraphicsimagerendererformat)
: 렌더하는image
의 scale 등을 설정할 수 있다.[drawHierarchy(in:afterScreenUpdates:)](https://developer.apple.com/documentation/uikit/uiview/1622589-drawhierarchy)
: 현재 화면에서 그려진 화면을 render합니다.
UIHostingController
UIHostingController | Apple Developer Documentation
A UIKit view controller that manages a SwiftUI view hierarchy.
developer.apple.com
UIHostingController는 SwiftUI뷰를 UIKit에서 사용할 수 있게 도와준다. 제네릭으로 가지는 Content에 SwiftUI뷰를 넣어서 선언하면 해당 뷰는 UIKit에서 사용가능한 뷰로 활용 가능하게 된다. 여기서는 해당 뷰에 대해 UIGraphicsImageRenderer라는 UIKit 기능을 사용해야 해서 UIHostingController를 사용하게 되었다.
전체 코드
open class UIHostingController<Content> : UIViewController where Content : View
//
// RendererTestView.swift
// test
//
// Created by Seungui Moon on 10/9/23.
//
import SwiftUI
struct RendererTestView: View {
@State var geoSize: CGSize = .init(width: 0, height: 0)
@State var highresImage: UIImage = UIImage()
@State var renderImage: UIImage?
var body: some View {
VStack {
GeometryReader { geo in
TargetImageView(cgSize: geo.size)
.onAppear {
self.geoSize = CGSize(width: geo.size.width, height: geo.size.height)
}
}
if let uiImage = renderImage {
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
.frame(width: 200,height: 200)
}
Button {
renderImage = TargetImageView(cgSize: self.geoSize).asImage(size: self.geoSize)
} label: {
Text("generate image")
.frame(height: 200)
}
}
.padding()
}
}
#Preview {
RendererTestView()
}
extension UIView {
func asImage(size: CGSize) -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = 1
return UIGraphicsImageRenderer(size: size, format: format).image { context in
self.drawHierarchy(in: self.layer.bounds, afterScreenUpdates: true)
}
}
}
extension View {
func asImage(size: CGSize) -> UIImage {
let controller = UIHostingController(rootView: self)
controller.view.bounds = CGRect(origin: .zero, size: size)
let image = controller.view.asImage(size: size)
return image
}
}
struct TargetImageView: View {
@State var cgSize: CGSize
var body: some View {
ZStack{
Color.white
VStack {
Text("HELLO\nWORLD")
.font(.title)
.multilineTextAlignment(.center)
}
.padding(40)
.background(.purple)
.foregroundColor(.white)
.shadow(color: .purple, radius: 10)
}
}
}
구현 화면
'iOS' 카테고리의 다른 글
Code Signing & Provisioning Profile 총정리: 하나의 앱을 여러 팀원과 공유하기 (1) | 2024.10.21 |
---|---|
WeatherKit Attribution 이슈: Guideline 5.2.5 - Legal - Intellectual Property (3) | 2024.10.05 |
[SwiftUI] @State와 @ObservableObject 알아보기 (0) | 2023.03.21 |
[Swift] 싱글톤(Singleton) 정리 (0) | 2023.03.06 |
Xcode .gitignore 파일 추가하기 (0) | 2023.03.01 |