はじめに
初投稿です.
今回はSwiftUIで,指定した範囲の画面をスクリーンショットしてPDF出力を行い,ファイル内に保存する機能をまとめています.
~やること~
■画面のスクリーンショット機能の実装
■スクリーンショットのデータをPDFとして保存する機能の実装
CGRectによるPDF取得範囲の指定
はじめにスクリーンショットしたい範囲の選択を行います.
取得したサイズと同じサイズの透明な幕を作るイメージです.
この透明な幕が今回取得するサイズになります.
struct RectangleGetter: View {
@Binding var rect: CGRect
var body: some View {
GeometryReader { geometry in
self.createView(proxy: geometry)
}
}
func createView(proxy: GeometryProxy) -> some View {
DispatchQueue.main.async {
self.rect = proxy.frame(in: .global)
}
return Rectangle().fill(Color.clear)
}
}
使い方
CGRectの変数にゼロを格納しておきます.
@State private var rect: CGRect = .zero
今回HStackで囲まれた範囲を取得します.
@State private var rect: CGRect = .zero
HStack {
Image(systemName: "camera")
.font(.title)
.foregroundColor(.white)
Text("Hello, World!?")
.font(.title)
.foregroundColor(.white)
}
.padding()
.frame(width:400,height:200)
.background(Color.yellow)
.cornerRadius(8)
.background(RectangleGetter(rect: $rect))
取得した範囲をUIImageに変換
SwiftUIのViewをUIImageに変換します.
extension UIView {
func getImage(rect: CGRect) -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: rect)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
UIImageを格納する変数を作成しておきます.
@State var uiImage: UIImage? = nil
CGRectで取得した範囲をUIImageに変換します.
self.uiImage = UIApplication.shared.windows[0].rootViewController?.view!.getImage(rect: self.rect)
UIImageをPDFとして一時保存
変換したUIImageをPDFとして保存します.また保存時拡張子にPDFを選択します.
本来このfuncでは引数にUIViewを用いますが今回は取得しているのがUIImageなのでUIImageViewを用いてます.
(UIImageは使えない)
func createPdfFromView(hosting: UIImageView, saveToDocumentsWithFileName fileName: String) {
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, hosting.bounds, nil)
UIGraphicsBeginPDFPage()
guard let pdfContext = UIGraphicsGetCurrentContext() else { return }
hosting.layer.render(in: pdfContext)
UIGraphicsEndPDFContext()
if let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
let documentsFileName = documentDirectories + "/" + fileName + ".pdf"
pdfData.write(toFile: documentsFileName, atomically: true)
}
}
変換したUIImageをPDFとして保存する。この際、”PDF名前”には自身が保存する際のファイル名を入力します.
※「PDF名前」は保存する際にデフォルトで値が入ります.
createPdfFromView(hosting:UIImageView(image:uiImage),saveToDocumentsWithFileName:"PDF名前")
PDFが一時保存されているディレクトリからフルパスを取得します.
PDFが一時保存されている場所からフルパスを取得します.
func fileSave(fileName: String) -> URL {
let dir = FileManager.default.urls( for: .documentDirectory, in: .userDomainMask ).first!
let filePath = dir.appendingPathComponent(fileName, isDirectory: false);
return filePath
自身で指定した”PDF名前”に拡張子がついた”PDF名前.pdf”をfileNameに格納します.
var url = fileSave(fileName: "PDF名前.pdf")
PDF出力ボタンを押した際に画面下部から表示されるアクティブシート
ファイルへ保存する際下から表示されるアクティブシートを作成する。activetyItems;[Any]に保存する対象を入力します.
struct ActivityView: UIViewControllerRepresentable {
let activityItems: [Any]
let applicationActivities: [UIActivity]?
func makeUIViewController(
context: UIViewControllerRepresentableContext<ActivityView>
) -> UIActivityViewController {
return UIActivityViewController(
activityItems: activityItems,
applicationActivities: applicationActivities
)
}
func updateUIViewController(
_ uiViewController: UIActivityViewController,
context: UIViewControllerRepresentableContext<ActivityView>
) {
// Nothing to do
}
}
今回url変数に格納先を指定しているため[Any]にはurlを入れます.
.sheet(isPresented: self.$showActivityView) {
ActivityView(activityItems: [url],applicationActivities: nil)
}
以下のように,ファイルに保存できるようになります.
完成です.
全体のコードは以下の通りです.
import SwiftUI
struct ContentView: View {
@State private var rect: CGRect = .zero
@State var uiImage: UIImage? = nil
@State private var showActivityView: Bool = false
var body: some View {
var url = fileSave(fileName: "PDF名前.pdf")
VStack {
HStack {
Image(systemName: "camera")
.font(.title)
.foregroundColor(.white)
Text("Hello, World!?")
.font(.title)
.foregroundColor(.white)
}
.padding()
.frame(width: 400, height: 200)
.background(Color.yellow)
.cornerRadius(8)
.background(RectangleGetter(rect: $rect))
Button(action: {
self.showActivityView.toggle()
self.uiImage = UIApplication.shared.windows[0].rootViewController?.view!.getImage(rect: self.rect)
createPdfFromView(hosting: UIImageView(image: uiImage), saveToDocumentsWithFileName: "PDF名前")
}) {
Text("PDF出力")
.padding()
.foregroundColor(Color.white)
.background(Color.red)
.cornerRadius(8)
}.sheet(isPresented: self.$showActivityView) {
ActivityView(
activityItems: [url],
applicationActivities: nil
)
}
}
}
}
// ----------------------------------------
struct RectangleGetter: View {
@Binding var rect: CGRect
var body: some View {
GeometryReader { geometry in
self.createView(proxy: geometry)
}
}
func createView(proxy: GeometryProxy) -> some View {
DispatchQueue.main.async {
self.rect = proxy.frame(in: .global)
}
return Rectangle().fill(Color.clear)
}
}
// ------------------------------------------
extension UIView {
func getImage(rect: CGRect) -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: rect)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
func createPdfFromView(hosting: UIImageView, saveToDocumentsWithFileName fileName: String) {
let pdfData = NSMutableData()
UIGraphicsBeginPDFContextToData(pdfData, hosting.bounds, nil)
UIGraphicsBeginPDFPage()
guard let pdfContext = UIGraphicsGetCurrentContext() else { return }
hosting.layer.render(in: pdfContext)
UIGraphicsEndPDFContext()
if let documentDirectories = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
let documentsFileName = documentDirectories + "/" + fileName + ".pdf"
pdfData.write(toFile: documentsFileName, atomically: true)
}
}
struct ActivityView: UIViewControllerRepresentable {
let activityItems: [Any]
let applicationActivities: [UIActivity]?
func makeUIViewController(
context: UIViewControllerRepresentableContext<ActivityView>
) -> UIActivityViewController {
return UIActivityViewController(
activityItems: activityItems,
applicationActivities: applicationActivities
)
}
func updateUIViewController(
_ uiViewController: UIActivityViewController,
context: UIViewControllerRepresentableContext<ActivityView>
) {
// Nothing to do
}
}
func fileSave(fileName: String) -> URL {
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let filePath = dir.appendingPathComponent(fileName, isDirectory: false)
return filePath
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}