Skip to content

Commit f55ce4f

Browse files
authored
Create FreeFormDrawingView.swift
1 parent 1bb4c6b commit f55ce4f

File tree

1 file changed

+383
-0
lines changed

1 file changed

+383
-0
lines changed

visionOS/FreeFormDrawingView.swift

Lines changed: 383 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
//
2+
// ContentView.swift
3+
// PKDraw
4+
5+
import SwiftUI
6+
import PencilKit
7+
8+
struct FreeFormDrawingView: View {
9+
10+
@State private var canvas = PKCanvasView()
11+
@State private var isDrawing = true
12+
@State private var color: Color = .black
13+
@State private var pencilType: PKInkingTool.InkType = .pencil
14+
@State private var colorPicker = false
15+
@Environment(\.undoManager) private var undoManager
16+
17+
@State private var isMessaging = false
18+
@State private var isVideoCalling = false
19+
@State private var isScreenSharing = false
20+
@State private var isRecording = false
21+
@Environment(\.dismiss) private var dismiss
22+
23+
24+
25+
26+
var body: some View {
27+
NavigationStack {
28+
// Drawing View
29+
DrawingView(canvas: $canvas, isDrawing: $isDrawing, pencilType: $pencilType, color: $color)
30+
//.navigationTitle("PKDraw")
31+
.navigationBarTitleDisplayMode(.inline)
32+
.ornament(attachmentAnchor: .scene(.top)) {
33+
HStack(spacing: 64) {
34+
Button {
35+
//
36+
} label: {
37+
VStack(spacing: 8) {
38+
Image(systemName: "message")
39+
Text("Chat")
40+
.font(.caption2)
41+
}
42+
}
43+
44+
Button {
45+
//
46+
} label: {
47+
VStack(spacing: 8) {
48+
Image(systemName: "video")
49+
Text("Call")
50+
.font(.caption2)
51+
}
52+
}
53+
54+
// Screen sharing
55+
Button {
56+
//
57+
} label: {
58+
VStack(spacing: 8) {
59+
Image(systemName: isScreenSharing ? "shared.with.you.slash" : "shared.with.you")
60+
withAnimation {
61+
Text(isScreenSharing ? "Stop" : "Share")
62+
.font(.caption2)
63+
}
64+
}
65+
}
66+
// Screen recording
67+
Button {
68+
isRecording.toggle()
69+
} label: {
70+
//Image(systemName: "rectangle.dashed.badge.record")
71+
VStack(spacing: 8) {
72+
Image(systemName: isRecording ? "rectangle.inset.filled.badge.record" : "rectangle.dashed.badge.record")
73+
withAnimation {
74+
Text(isRecording ? "Stop" : "Record")
75+
.font(.caption2)
76+
}
77+
}
78+
}
79+
}.padding(.horizontal)
80+
.padding(12)
81+
.glassBackgroundEffect()
82+
.buttonStyle(.plain)
83+
}
84+
.ornament(attachmentAnchor: .scene(.leading)) {
85+
// Modify Tools
86+
VStack(spacing: 32) {
87+
Button {
88+
// Clear the canvas. Reset the drawing
89+
canvas.drawing = PKDrawing()
90+
} label: {
91+
Image(systemName: "scissors")
92+
}
93+
94+
Button {
95+
// Undo drawing
96+
undoManager?.undo()
97+
} label: {
98+
Image(systemName: "arrow.uturn.backward")
99+
}
100+
101+
Button {
102+
// Redo drawing
103+
undoManager?.redo()
104+
} label: {
105+
Image(systemName: "arrow.uturn.forward")
106+
}
107+
108+
Button {
109+
// Erase tool
110+
isDrawing = false
111+
} label: {
112+
Image(systemName: "eraser.line.dashed")
113+
}
114+
.foregroundStyle(
115+
LinearGradient(gradient: Gradient(colors: [.white, .yellow]), startPoint: .leading, endPoint: .top)
116+
)
117+
} // Modify tools
118+
.padding(12)
119+
.buttonStyle(.plain)
120+
.glassBackgroundEffect(in: RoundedRectangle(cornerRadius: 32))
121+
}
122+
.toolbar {
123+
// Bottom Ornament
124+
ToolbarItemGroup(placement: .bottomOrnament) {
125+
HStack { // Drawing Tools
126+
Button {
127+
// Pencil
128+
isDrawing = true
129+
pencilType = .pencil
130+
} label: {
131+
VStack(spacing: 8) {
132+
Image(systemName: "pencil.and.scribble")
133+
Text("Pencil")
134+
.foregroundStyle(.white)
135+
}
136+
}
137+
.buttonStyle(.plain)
138+
139+
Button {
140+
// Pen
141+
isDrawing = true
142+
pencilType = .pen
143+
} label: {
144+
VStack(spacing: 8) {
145+
Image(systemName: "applepencil.tip")
146+
Text("Pen")
147+
.foregroundStyle(.white)
148+
}
149+
}
150+
151+
Button {
152+
// Monoline
153+
isDrawing = true
154+
pencilType = .monoline
155+
} label: {
156+
VStack(spacing: 8) {
157+
Image(systemName: "pencil.line")
158+
Text("Monoline")
159+
.foregroundStyle(.white)
160+
}
161+
}
162+
163+
Button {
164+
// Fountain: Variable scribbling
165+
isDrawing = true
166+
pencilType = .fountainPen
167+
} label: {
168+
VStack(spacing: 8) {
169+
Image(systemName: "scribble.variable")
170+
Text("Fountain")
171+
.foregroundStyle(.white)
172+
}
173+
}
174+
175+
Button {
176+
// Marker
177+
isDrawing = true
178+
pencilType = .marker
179+
} label: {
180+
VStack(spacing: 8) {
181+
Image(systemName: "paintbrush.pointed")
182+
Text("Marker")
183+
.foregroundStyle(.white)
184+
}
185+
}
186+
187+
Button {
188+
// Crayon
189+
isDrawing = true
190+
pencilType = .crayon
191+
} label: {
192+
VStack(spacing: 8) {
193+
Image(systemName: "paintbrush")
194+
Text("Crayon")
195+
.foregroundStyle(.white)
196+
}
197+
}
198+
199+
Button {
200+
// Water Color
201+
isDrawing = true
202+
pencilType = .watercolor
203+
} label: {
204+
VStack(spacing: 8) {
205+
Image(systemName: "eyedropper.halffull")
206+
Text("Watercolor")
207+
.foregroundStyle(.white)
208+
}
209+
}
210+
211+
// Color picker
212+
Button {
213+
// Pick a color
214+
colorPicker.toggle()
215+
} label: {
216+
VStack(spacing: 8) {
217+
Image(systemName: "paintpalette")
218+
Text("Colorpicker")
219+
.foregroundStyle(.white)
220+
}
221+
}
222+
} // Drawing Tools
223+
.padding(.horizontal)
224+
.foregroundStyle(
225+
LinearGradient(gradient: Gradient(colors: [.green, .yellow]), startPoint: .leading, endPoint: .bottom)
226+
)
227+
} // Bottom Ornament
228+
}
229+
.ornament(attachmentAnchor: .scene(.trailing)) {
230+
VStack(spacing: 32) {
231+
Button {
232+
// Set ruler as active
233+
canvas.isRulerActive.toggle()
234+
} label: {
235+
Image(systemName: "pencil.and.ruler.fill")
236+
}
237+
Button {
238+
// Tool picker
239+
//let toolPicker = PKToolPicker.init()
240+
@State var toolPicker = PKToolPicker()
241+
toolPicker.setVisible(true, forFirstResponder: canvas)
242+
toolPicker.addObserver(canvas)
243+
canvas.becomeFirstResponder()
244+
} label: {
245+
Image(systemName: "pencil.tip.crop.circle.badge.plus")
246+
}
247+
248+
// Menu for pencil types and color
249+
Menu {
250+
Button {
251+
// Menu: Pick a color
252+
colorPicker.toggle()
253+
} label: {
254+
Label("Color", systemImage: "paintpalette")
255+
}
256+
257+
Button {
258+
// Menu: Pencil
259+
isDrawing = true
260+
pencilType = .pencil
261+
} label: {
262+
Label("Pencil", systemImage: "pencil")
263+
}
264+
265+
Button {
266+
// Menu: pen
267+
isDrawing = true
268+
pencilType = .pen
269+
} label: {
270+
Label("Pen", systemImage: "pencil.tip")
271+
}
272+
273+
Button {
274+
// Menu: Marker
275+
isDrawing = true
276+
pencilType = .marker
277+
} label: {
278+
Label("Marker", systemImage: "paintbrush.pointed")
279+
}
280+
281+
Button {
282+
// Menu: Monoline
283+
isDrawing = true
284+
pencilType = .monoline
285+
} label: {
286+
Label("Monoline", systemImage: "pencil.line")
287+
}
288+
289+
Button {
290+
// Menu: pen
291+
isDrawing = true
292+
pencilType = .fountainPen
293+
} label: {
294+
Label("Fountain", systemImage: "paintbrush.pointed.fill")
295+
}
296+
297+
Button {
298+
// Menu: Watercolor
299+
isDrawing = true
300+
pencilType = .watercolor
301+
} label: {
302+
Label("Watercolor", systemImage: "eyedropper.halffull")
303+
}
304+
305+
Button {
306+
// Menu: Crayon
307+
isDrawing = true
308+
pencilType = .crayon
309+
} label: {
310+
Label("Crayon", systemImage: "pencil.tip")
311+
}
312+
313+
} label: {
314+
Image(systemName: "hand.draw")
315+
}
316+
.sheet(isPresented: $colorPicker) {
317+
ColorPicker("Pick color", selection: $color)
318+
.padding()
319+
}
320+
}.padding(12)
321+
.buttonStyle(.plain)
322+
.glassBackgroundEffect(in: RoundedRectangle(cornerRadius: 32))
323+
}
324+
}
325+
}
326+
327+
func saveDrawing() {
328+
// Get the drawing image from the canvas
329+
let drawingImage = canvas.drawing.image(from: canvas.drawing.bounds, scale: 1.0)
330+
331+
// Save drawings to the Photos Album
332+
UIImageWriteToSavedPhotosAlbum(drawingImage, nil, nil, nil)
333+
}
334+
}
335+
336+
struct DrawingView: UIViewRepresentable {
337+
// Capture drawings for saving in the photos library
338+
@Binding var canvas: PKCanvasView
339+
@Binding var isDrawing: Bool
340+
// Ability to switch a pencil
341+
@Binding var pencilType: PKInkingTool.InkType
342+
// Ability to change a pencil color
343+
@Binding var color: Color
344+
345+
346+
//let ink = PKInkingTool(.pencil, color: .black)
347+
// Update ink type
348+
var ink: PKInkingTool {
349+
PKInkingTool(pencilType, color: UIColor(color))
350+
}
351+
352+
let eraser = PKEraserTool(.bitmap)
353+
354+
func makeUIView(context: Context) -> PKCanvasView {
355+
// Allow finger and pencil drawing
356+
canvas.drawingPolicy = .anyInput
357+
358+
canvas.tool = isDrawing ? ink : eraser
359+
canvas.isRulerActive = true
360+
canvas.backgroundColor = .init(red: 1, green: 1, blue: 0, alpha: 0.1)
361+
362+
363+
// From Brian Advent: Show the default toolpicker
364+
canvas.alwaysBounceVertical = true
365+
366+
let toolPicker = PKToolPicker.init()
367+
toolPicker.setVisible(true, forFirstResponder: canvas)
368+
toolPicker.addObserver(canvas) // Notify when the picker configuration changes
369+
canvas.becomeFirstResponder()
370+
371+
return canvas
372+
}
373+
374+
func updateUIView(_ uiView: PKCanvasView, context: Context) {
375+
// Update tool whenever the main view updates
376+
uiView.tool = isDrawing ? ink : eraser
377+
}
378+
}
379+
380+
381+
#Preview {
382+
FreeFormDrawingView()
383+
}

0 commit comments

Comments
 (0)