coiai Logo

This article is an English translation of the original Japanese post. Read the Japanese original →

[SwiftUI] All I Wanted Was to Export a View as a PNG

December 16, 2023

Categories: 未分類

All I wanted to do was export a View as a PNG on macOS.

It turned out to be a fair bit of a struggle, so I wanted to leave a record of it here. There was plenty of trial and error along the way, but at this point I'm just tired, so I'll leave the finished code below. I referenced a bunch of different sites along the way, so once I've had some sleep I'll come back and add the links.

Here's the code:

//
//  CanvasView.swift
//  FolderCustomizer
//
//  Created by coiai on 2023/12/15.
//

import SwiftUI

struct CanvasView: View {
    @State private var inputText = ""
    @State private var presented = true
    @State private var folderColor: FolderColorPicker.FolderColor = .Blue

    var body: some View {
        VStack {
            CanvasToSave(inputText: $inputText, folderColor: $folderColor)
        }
        .inspector(isPresented: $presented) {
            Button("Save Image") {

            }

            Form {
                Section("Text") {
                    TextField("Text", text: $inputText)
                        .font(.body)
                        .padding()
                }
                Section("Folder Color") {
                    FolderColorPicker(folderColor: $folderColor)
                }
            }
            .inspectorColumnWidth(min: 200, ideal: 300, max: 400)
            .toolbar {
                Spacer()
                Button {
                    presented.toggle()
                } label: {
                    Label("toggle inspector", systemImage: "info.circle")
                }
            }
            Spacer()
        }
    }
}

struct CanvasToSave: View {
    @Binding var inputText: String
    @Binding var folderColor: FolderColorPicker.FolderColor

    var body: some View {
        Canvas { context, size in
            let rect = CGRect(origin: .zero, size: size).insetBy(dx: 5, dy: 5)
            // キャンバスの中心の点
            let midPoint = CGPoint(x: size.width / 2, y: size.height / 2)


            let text = Text(inputText)
                .font(.largeTitle)
                .fontWeight(.bold)
                .foregroundColor(.white)

            context.draw(text, at: midPoint, anchor: .center)
            context.blendMode = GraphicsContext.BlendMode.softLight

            let image = Image(folderColor.rawValue)
            context.draw(image, in: rect.insetBy(dx: 0, dy: 0))
        } symbols: {
            RotateObjView()
                .tag(0)
        }
        .frame(width: 512, height: 512)
    }
}

#Preview {
    CanvasView()
}