From bd1196ec41e31f3ebb877bcb5f9d1278d770f275 Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Mon, 24 Jun 2024 20:16:02 +0700 Subject: [PATCH 1/8] feat: add hand button in element tool bar --- Memola/Canvas/Tool/Core/Tool.swift | 6 +++++- Memola/Canvas/Tool/Core/ToolSelection.swift | 2 +- .../ViewController/CanvasViewController.swift | 2 +- Memola/Features/Memo/Toolbar/Toolbar.swift | 17 +++++++++++++++-- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Memola/Canvas/Tool/Core/Tool.swift b/Memola/Canvas/Tool/Core/Tool.swift index 4c837c2..0f604c2 100644 --- a/Memola/Canvas/Tool/Core/Tool.swift +++ b/Memola/Canvas/Tool/Core/Tool.swift @@ -21,7 +21,7 @@ public class Tool: NSObject, ObservableObject { // MARK: - Photo @Published var selectedPhotoItem: PhotoItem? - @Published var selection: ToolSelection = .none + @Published var selection: ToolSelection = .hand let scrollPublisher = PassthroughSubject() var markers: [Pen] { @@ -32,6 +32,10 @@ public class Tool: NSObject, ObservableObject { self.object = object } + func selectTool(_ selection: ToolSelection) { + self.selection = selection + } + func load() { DispatchQueue.main.async { [weak self] in guard let self else { return } diff --git a/Memola/Canvas/Tool/Core/ToolSelection.swift b/Memola/Canvas/Tool/Core/ToolSelection.swift index 76c3589..ed3fc26 100644 --- a/Memola/Canvas/Tool/Core/ToolSelection.swift +++ b/Memola/Canvas/Tool/Core/ToolSelection.swift @@ -8,7 +8,7 @@ import Foundation enum ToolSelection: Equatable { - case none + case hand case pen case photo } diff --git a/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift b/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift index a21f7fb..00347cb 100644 --- a/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift +++ b/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift @@ -335,7 +335,7 @@ extension CanvasViewController { let enablesDrawing: Bool let enablesPhotoInsertion: Bool switch selection { - case .none: + case .hand: enablesScrolling = true enablesDrawing = false enablesPhotoInsertion = false diff --git a/Memola/Features/Memo/Toolbar/Toolbar.swift b/Memola/Features/Memo/Toolbar/Toolbar.swift index 980f2a9..c341bad 100644 --- a/Memola/Features/Memo/Toolbar/Toolbar.swift +++ b/Memola/Features/Memo/Toolbar/Toolbar.swift @@ -133,7 +133,20 @@ struct Toolbar: View { HStack(spacing: 0) { Button { withAnimation { - tool.selection = tool.selection == .pen ? .none : .pen + tool.selectTool(.hand) + } + } label: { + Image(systemName: "hand.draw.fill") + .fontWeight(.heavy) + .contentShape(.circle) + .frame(width: size, height: size) + .background(tool.selection == .hand ? Color.accentColor : Color.clear) + .foregroundStyle(tool.selection == .hand ? Color.white : Color.accentColor) + .clipShape(.rect(cornerRadius: 8)) + } + Button { + withAnimation { + tool.selectTool(.pen) } } label: { Image(systemName: "pencil") @@ -148,7 +161,7 @@ struct Toolbar: View { HStack(spacing: 0) { Button { withAnimation { - tool.selection = tool.selection == .photo ? .none : .photo + tool.selectTool(.photo) } } label: { Image(systemName: "photo") From f2672710627fbd583c2e76e6c5adb7450da4ee76 Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Mon, 24 Jun 2024 20:38:40 +0700 Subject: [PATCH 2/8] feat: enhance element tool bar animation --- Memola/Features/Memo/Toolbar/Toolbar.swift | 33 ++++++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/Memola/Features/Memo/Toolbar/Toolbar.swift b/Memola/Features/Memo/Toolbar/Toolbar.swift index c341bad..8de0aad 100644 --- a/Memola/Features/Memo/Toolbar/Toolbar.swift +++ b/Memola/Features/Memo/Toolbar/Toolbar.swift @@ -25,6 +25,8 @@ struct Toolbar: View { @FocusState var textFieldState: Bool + @Namespace var namespace + let size: CGFloat init(size: CGFloat, memo: MemoObject, tool: Tool, canvas: Canvas, history: History) { @@ -140,10 +142,17 @@ struct Toolbar: View { .fontWeight(.heavy) .contentShape(.circle) .frame(width: size, height: size) - .background(tool.selection == .hand ? Color.accentColor : Color.clear) .foregroundStyle(tool.selection == .hand ? Color.white : Color.accentColor) .clipShape(.rect(cornerRadius: 8)) } + .hoverEffect(.lift) + .background { + if tool.selection == .hand { + Color.accentColor + .clipShape(.rect(cornerRadius: 8)) + .matchedGeometryEffect(id: "element.toolbar.bg", in: namespace) + } + } Button { withAnimation { tool.selectTool(.pen) @@ -153,11 +162,17 @@ struct Toolbar: View { .fontWeight(.heavy) .contentShape(.circle) .frame(width: size, height: size) - .background(tool.selection == .pen ? Color.accentColor : Color.clear) .foregroundStyle(tool.selection == .pen ? Color.white : Color.accentColor) .clipShape(.rect(cornerRadius: 8)) } .hoverEffect(.lift) + .background { + if tool.selection == .pen { + Color.accentColor + .clipShape(.rect(cornerRadius: 8)) + .matchedGeometryEffect(id: "element.toolbar.bg", in: namespace) + } + } HStack(spacing: 0) { Button { withAnimation { @@ -167,11 +182,21 @@ struct Toolbar: View { Image(systemName: "photo") .contentShape(.circle) .frame(width: size, height: size) - .background(tool.selection == .photo ? Color.accentColor : Color.clear) .foregroundStyle(tool.selection == .photo ? Color.white : Color.accentColor) .clipShape(.rect(cornerRadius: 8)) } .hoverEffect(.lift) + .background { + if tool.selection == .photo { + Color.accentColor + .clipShape(.rect(cornerRadius: 8)) + .matchedGeometryEffect(id: "element.toolbar.bg", in: namespace) + } + if tool.selection != .photo { + Color.clear + .matchedGeometryEffect(id: "element.toolbar.photo.options", in: namespace) + } + } if tool.selection == .photo { HStack(spacing: 0) { Button { @@ -191,6 +216,8 @@ struct Toolbar: View { } .hoverEffect(.lift) } + .matchedGeometryEffect(id: "element.toolbar.photo.options", in: namespace) + .transition(.opacity.animation(.easeIn(duration: 0.1))) } } .background { From 46e5e679b85967577f39f317894e76eedbd6106f Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Mon, 24 Jun 2024 20:44:14 +0700 Subject: [PATCH 3/8] feat: update lock mode logic --- .../Canvas/View/Bridge/ViewController/CanvasViewController.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift b/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift index 00347cb..6b80f14 100644 --- a/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift +++ b/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift @@ -362,7 +362,6 @@ extension CanvasViewController { } func lockModeChanged(_ state: Bool) { - scrollView.isScrollEnabled = !state scrollView.pinchGestureRecognizer?.isEnabled = !state } } From f85610a99b149a45b20eb91248d67c29100a2b99 Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Mon, 24 Jun 2024 20:55:24 +0700 Subject: [PATCH 4/8] feat: reposition lock button --- Memola/Features/Memo/Memo/MemoView.swift | 2 +- Memola/Features/Memo/PenDock/PenDock.swift | 45 ++++++++++++++++++---- Memola/Features/Memo/Toolbar/Toolbar.swift | 35 ++++------------- 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/Memola/Features/Memo/Memo/MemoView.swift b/Memola/Features/Memo/Memo/MemoView.swift index 362bffb..6711e35 100644 --- a/Memola/Features/Memo/Memo/MemoView.swift +++ b/Memola/Features/Memo/Memo/MemoView.swift @@ -32,7 +32,7 @@ struct MemoView: View { .overlay(alignment: .bottomTrailing) { switch tool.selection { case .pen: - PenDock(tool: tool, canvas: canvas) + PenDock(tool: tool, canvas: canvas, size: size) .transition(.move(edge: .trailing)) case .photo: if let photoItem = tool.selectedPhotoItem { diff --git a/Memola/Features/Memo/PenDock/PenDock.swift b/Memola/Features/Memo/PenDock/PenDock.swift index cbf4432..87b555f 100644 --- a/Memola/Features/Memo/PenDock/PenDock.swift +++ b/Memola/Features/Memo/PenDock/PenDock.swift @@ -11,6 +11,7 @@ struct PenDock: View { @ObservedObject var tool: Tool @ObservedObject var canvas: Canvas + let size: CGFloat let width: CGFloat = 90 let height: CGFloat = 30 let factor: CGFloat = 0.9 @@ -19,15 +20,20 @@ struct PenDock: View { @State var opensColorPicker: Bool = false var body: some View { - if !canvas.locksCanvas { - VStack(alignment: .trailing) { - penPropertyTool - penItemList + ZStack(alignment: .bottomTrailing) { + if !canvas.locksCanvas { + VStack(alignment: .trailing) { + penPropertyTool + penItemList + } + .fixedSize() + .frame(maxHeight: .infinity) + .padding(10) + .transition(.move(edge: .trailing).combined(with: .blurReplace)) } - .fixedSize() - .frame(maxHeight: .infinity) - .padding(10) - .transition(.move(edge: .trailing).combined(with: .blurReplace)) + lockButton + .padding(10) + .transition(.move(edge: .trailing).combined(with: .blurReplace)) } } @@ -310,4 +316,27 @@ struct PenDock: View { } } } + + var lockButton: some View { + Button { + withAnimation { + canvas.locksCanvas.toggle() + } + } label: { + ZStack { + if canvas.locksCanvas { + Image(systemName: "lock.open") + .transition(.move(edge: .trailing).combined(with: .blurReplace)) + } else { + Image(systemName: "lock") + .transition(.move(edge: .leading).combined(with: .blurReplace)) + } + } + .contentShape(.circle) + .frame(width: size, height: size) + .background(.regularMaterial) + .clipShape(.rect(cornerRadius: 8)) + } + .hoverEffect(.lift) + } } diff --git a/Memola/Features/Memo/Toolbar/Toolbar.swift b/Memola/Features/Memo/Toolbar/Toolbar.swift index 8de0aad..226c11f 100644 --- a/Memola/Features/Memo/Toolbar/Toolbar.swift +++ b/Memola/Features/Memo/Toolbar/Toolbar.swift @@ -47,12 +47,13 @@ struct Toolbar: View { } } .frame(maxWidth: .infinity, alignment: .leading) - elementTool - HStack(spacing: 5) { + if !canvas.locksCanvas { + elementTool + } + Group { if !canvas.locksCanvas { historyControl } - lockButton } .frame(maxWidth: .infinity, alignment: .trailing) } @@ -217,13 +218,14 @@ struct Toolbar: View { .hoverEffect(.lift) } .matchedGeometryEffect(id: "element.toolbar.photo.options", in: namespace) - .transition(.opacity.animation(.easeIn(duration: 0.1))) + .transition(.blurReplace.animation(.easeIn(duration: 0.1))) } } .background { if tool.selection == .photo { RoundedRectangle(cornerRadius: 8) .fill(Color.white.tertiary) + .transition(.move(edge: .leading).animation(.easeIn(duration: 0.1))) } } } @@ -231,6 +233,7 @@ struct Toolbar: View { RoundedRectangle(cornerRadius: 8) .fill(.regularMaterial) } + .transition(.move(edge: .top).combined(with: .blurReplace)) } var historyControl: some View { @@ -259,30 +262,6 @@ struct Toolbar: View { .transition(.move(edge: .top).combined(with: .blurReplace)) } - var lockButton: some View { - Button { - #warning("TODO: need to revisit toggale logic") - withAnimation { - canvas.locksCanvas.toggle() - } - } label: { - ZStack { - if canvas.locksCanvas { - Image(systemName: "lock.open") - .transition(.move(edge: .trailing).combined(with: .blurReplace)) - } else { - Image(systemName: "lock") - .transition(.move(edge: .leading).combined(with: .blurReplace)) - } - } - .contentShape(.circle) - .frame(width: size, height: size) - .background(.regularMaterial) - .clipShape(.rect(cornerRadius: 8)) - } - .hoverEffect(.lift) - } - func openCamera() { let status = AVCaptureDevice.authorizationStatus(for: .video) switch status { From cfa177f3f32a661e07322075012f72f178f62377 Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Mon, 24 Jun 2024 21:01:12 +0700 Subject: [PATCH 5/8] feat: add symbol effect to lock button --- Memola/Features/Memo/PenDock/PenDock.swift | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Memola/Features/Memo/PenDock/PenDock.swift b/Memola/Features/Memo/PenDock/PenDock.swift index 87b555f..b9bfc1f 100644 --- a/Memola/Features/Memo/PenDock/PenDock.swift +++ b/Memola/Features/Memo/PenDock/PenDock.swift @@ -323,20 +323,13 @@ struct PenDock: View { canvas.locksCanvas.toggle() } } label: { - ZStack { - if canvas.locksCanvas { - Image(systemName: "lock.open") - .transition(.move(edge: .trailing).combined(with: .blurReplace)) - } else { - Image(systemName: "lock") - .transition(.move(edge: .leading).combined(with: .blurReplace)) - } - } - .contentShape(.circle) - .frame(width: size, height: size) - .background(.regularMaterial) - .clipShape(.rect(cornerRadius: 8)) + Image(systemName: canvas.locksCanvas ? "lock.fill" : "lock.open.fill") + .contentShape(.circle) + .frame(width: size, height: size) + .background(.regularMaterial) + .clipShape(.rect(cornerRadius: 8)) } .hoverEffect(.lift) + .contentTransition(.symbolEffect(.replace)) } } From a2ed7c43bbecba5369fe34b1b147495a70742a98 Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Mon, 24 Jun 2024 21:23:42 +0700 Subject: [PATCH 6/8] feat: show loading indicator when photo is being loaded --- Memola/Canvas/Tool/Core/Tool.swift | 2 ++ Memola/Features/Memo/Memo/MemoView.swift | 7 ++++++- Memola/Features/Memo/Toolbar/Toolbar.swift | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Memola/Canvas/Tool/Core/Tool.swift b/Memola/Canvas/Tool/Core/Tool.swift index 0f604c2..a7641a4 100644 --- a/Memola/Canvas/Tool/Core/Tool.swift +++ b/Memola/Canvas/Tool/Core/Tool.swift @@ -20,6 +20,7 @@ public class Tool: NSObject, ObservableObject { @Published var draggedPen: Pen? // MARK: - Photo @Published var selectedPhotoItem: PhotoItem? + @Published var isLoadingPhoto: Bool = false @Published var selection: ToolSelection = .hand @@ -120,6 +121,7 @@ public class Tool: NSObject, ObservableObject { func selectPhoto(_ image: UIImage, for canvasID: NSManagedObjectID) { guard let (resizedImage, dimension) = resizePhoto(of: image) else { return } let photoItem = bookmarkPhoto(of: resizedImage, in: dimension, with: canvasID) + isLoadingPhoto = false withAnimation { selectedPhotoItem = photoItem } diff --git a/Memola/Features/Memo/Memo/MemoView.swift b/Memola/Features/Memo/Memo/MemoView.swift index 6711e35..c81966b 100644 --- a/Memola/Features/Memo/Memo/MemoView.swift +++ b/Memola/Features/Memo/Memo/MemoView.swift @@ -46,7 +46,7 @@ struct MemoView: View { .overlay(alignment: .bottomLeading) { zoomControl } - .disabled(textFieldState) + .disabled(textFieldState || tool.isLoadingPhoto) .overlay(alignment: .top) { Toolbar(size: size, memo: memo, tool: tool, canvas: canvas, history: history) } @@ -61,6 +61,11 @@ struct MemoView: View { EmptyView() } } + .overlay { + if tool.isLoadingPhoto { + loadingIndicator("Loading photo...") + } + } } @ViewBuilder diff --git a/Memola/Features/Memo/Toolbar/Toolbar.swift b/Memola/Features/Memo/Toolbar/Toolbar.swift index 226c11f..15d2546 100644 --- a/Memola/Features/Memo/Toolbar/Toolbar.swift +++ b/Memola/Features/Memo/Toolbar/Toolbar.swift @@ -62,6 +62,7 @@ struct Toolbar: View { .onChange(of: photosPickerItem) { oldValue, newValue in if newValue != nil { Task { + tool.isLoadingPhoto = true let data = try? await newValue?.loadTransferable(type: Data.self) if let data, let image = UIImage(data: data) { tool.selectPhoto(image, for: canvas.canvasID) From 36904f2879911e55a74c06bf6bb3ce38c9c9e6c7 Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Mon, 24 Jun 2024 21:26:31 +0700 Subject: [PATCH 7/8] feat: add hover effect --- Memola/Features/Memo/Memo/MemoView.swift | 1 + Memola/Features/Memo/PenDock/PenDock.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/Memola/Features/Memo/Memo/MemoView.swift b/Memola/Features/Memo/Memo/MemoView.swift index c81966b..528abd8 100644 --- a/Memola/Features/Memo/Memo/MemoView.swift +++ b/Memola/Features/Memo/Memo/MemoView.swift @@ -101,6 +101,7 @@ struct MemoView: View { .clipShape(.rect(cornerRadius: 8)) .padding(10) } + .hoverEffect(.lift) .transition(.move(edge: .bottom).combined(with: .blurReplace)) } } diff --git a/Memola/Features/Memo/PenDock/PenDock.swift b/Memola/Features/Memo/PenDock/PenDock.swift index b9bfc1f..5c4bb84 100644 --- a/Memola/Features/Memo/PenDock/PenDock.swift +++ b/Memola/Features/Memo/PenDock/PenDock.swift @@ -241,6 +241,7 @@ struct PenDock: View { .frame(width: size + 2, height: size + 2) } } + .hoverEffect(.lift) .pickerStyle(.wheel) .frame(width: width * factor - 18, height: 35) .onChange(of: pen.thickness) { _, _ in From 9c8763f0e57a1358f7fdab379c7e5470246d27bb Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Mon, 24 Jun 2024 21:32:45 +0700 Subject: [PATCH 8/8] bug: fix pen dock ui blockage to canvas --- Memola/Features/Memo/PenDock/PenDock.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Memola/Features/Memo/PenDock/PenDock.swift b/Memola/Features/Memo/PenDock/PenDock.swift index 5c4bb84..d9af8ca 100644 --- a/Memola/Features/Memo/PenDock/PenDock.swift +++ b/Memola/Features/Memo/PenDock/PenDock.swift @@ -51,7 +51,6 @@ struct PenDock: View { } } .padding(.vertical, 10) - .padding(.leading, 40) .id(refreshScrollId) } .onReceive(tool.scrollPublisher) { id in @@ -62,7 +61,7 @@ struct PenDock: View { } } } - .frame(maxHeight:( (height * factor + 10) * 6) + 20) + .frame(maxHeight: ((height * factor + 10) * 6) + 20) .fixedSize() .background(alignment: .trailing) { RoundedRectangle(cornerRadius: 8) @@ -72,7 +71,7 @@ struct PenDock: View { .clipShape(.rect(cornerRadii: .init(bottomTrailing: 8, topTrailing: 8))) .overlay(alignment: .bottomLeading) { newPenButton - .offset(x: 60, y: 10) + .offset(x: 15, y: 10) } }