mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-03-25 10:51:41 +01:00
feat: add photo picker to select photo from library
This commit is contained in:
@@ -83,6 +83,7 @@
|
||||
ECA738F62BE612B700A4542E /* MTLDevice++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA738F52BE612B700A4542E /* MTLDevice++.swift */; };
|
||||
ECA738FC2BE61C5200A4542E /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA738FB2BE61C5200A4542E /* Persistence.swift */; };
|
||||
ECA739082BE623F300A4542E /* PenDock.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA739072BE623F300A4542E /* PenDock.swift */; };
|
||||
ECBE52962C1D5900006BDB3D /* PhotoPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECBE52952C1D5900006BDB3D /* PhotoPreview.swift */; };
|
||||
ECD12A862C19EE3900B96E12 /* ElementObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD12A852C19EE3900B96E12 /* ElementObject.swift */; };
|
||||
ECD12A8A2C19EFB000B96E12 /* Element.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD12A892C19EFB000B96E12 /* Element.swift */; };
|
||||
ECD12A8C2C1AEAA900B96E12 /* PhotoObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECD12A8B2C1AEAA900B96E12 /* PhotoObject.swift */; };
|
||||
@@ -181,6 +182,7 @@
|
||||
ECA738F52BE612B700A4542E /* MTLDevice++.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MTLDevice++.swift"; sourceTree = "<group>"; };
|
||||
ECA738FB2BE61C5200A4542E /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
|
||||
ECA739072BE623F300A4542E /* PenDock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenDock.swift; sourceTree = "<group>"; };
|
||||
ECBE52952C1D5900006BDB3D /* PhotoPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoPreview.swift; sourceTree = "<group>"; };
|
||||
ECD12A852C19EE3900B96E12 /* ElementObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementObject.swift; sourceTree = "<group>"; };
|
||||
ECD12A892C19EFB000B96E12 /* Element.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Element.swift; sourceTree = "<group>"; };
|
||||
ECD12A8B2C1AEAA900B96E12 /* PhotoObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoObject.swift; sourceTree = "<group>"; };
|
||||
@@ -389,6 +391,7 @@
|
||||
ECA7387B2BE5EF3500A4542E /* Memo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ECBE52942C1D58F5006BDB3D /* PhotoPreview */,
|
||||
EC1B783B2BFA0AAC005A34E2 /* Toolbar */,
|
||||
EC5050082BF65D0500B4D86E /* Memo */,
|
||||
EC5050052BF65CCD00B4D86E /* PenDock */,
|
||||
@@ -665,6 +668,14 @@
|
||||
path = Core;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
ECBE52942C1D58F5006BDB3D /* PhotoPreview */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ECBE52952C1D5900006BDB3D /* PhotoPreview.swift */,
|
||||
);
|
||||
path = PhotoPreview;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
ECD12A872C19EF8700B96E12 /* Elements */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -891,6 +902,7 @@
|
||||
EC5050072BF65CED00B4D86E /* PenDropDelegate.swift in Sources */,
|
||||
ECA738A32BE6020A00A4542E /* CGFloat++.swift in Sources */,
|
||||
ECA738C12BE60E5300A4542E /* PenStyle.swift in Sources */,
|
||||
ECBE52962C1D5900006BDB3D /* PhotoPreview.swift in Sources */,
|
||||
ECA738DE2BE610A000A4542E /* ViewPortRenderPass.swift in Sources */,
|
||||
EC7F6BEC2BE5E6E300A34A7B /* MemolaApp.swift in Sources */,
|
||||
EC2BEBF82C0F601A005DB0AF /* Node.swift in Sources */,
|
||||
|
||||
@@ -14,8 +14,12 @@ public class Tool: NSObject, ObservableObject {
|
||||
let object: ToolObject
|
||||
|
||||
@Published var pens: [Pen] = []
|
||||
|
||||
// MARK: - Pen
|
||||
@Published var selectedPen: Pen?
|
||||
@Published var draggedPen: Pen?
|
||||
// MARK: - Photo
|
||||
@Published var selectedImage: UIImage?
|
||||
|
||||
@Published var selection: ToolSelection = .none
|
||||
|
||||
|
||||
@@ -29,10 +29,18 @@ struct MemoView: View {
|
||||
var body: some View {
|
||||
CanvasView(tool: tool, canvas: canvas, history: history)
|
||||
.ignoresSafeArea()
|
||||
.overlay(alignment: .trailing) {
|
||||
if tool.selection == .pen {
|
||||
.overlay(alignment: .bottomTrailing) {
|
||||
switch tool.selection {
|
||||
case .pen:
|
||||
PenDock(tool: tool, canvas: canvas)
|
||||
.transition(.move(edge: .trailing))
|
||||
case .photo:
|
||||
if let image = tool.selectedImage {
|
||||
PhotoPreview(image: image, tool: tool)
|
||||
.transition(.move(edge: .trailing))
|
||||
}
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
.overlay(alignment: .bottomLeading) {
|
||||
|
||||
48
Memola/Features/Memo/PhotoPreview/PhotoPreview.swift
Normal file
48
Memola/Features/Memo/PhotoPreview/PhotoPreview.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// PhotoPreview.swift
|
||||
// Memola
|
||||
//
|
||||
// Created by Dscyre Scotti on 6/15/24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PhotoPreview: View {
|
||||
let image: UIImage
|
||||
@ObservedObject var tool: Tool
|
||||
|
||||
var body: some View {
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.scaledToFill()
|
||||
.frame(width: 100, height: 100)
|
||||
.cornerRadius(5)
|
||||
.overlay {
|
||||
RoundedRectangle(cornerRadius: 5)
|
||||
.stroke(Color.gray, lineWidth: 0.2)
|
||||
}
|
||||
.padding(10)
|
||||
.background(.regularMaterial)
|
||||
.cornerRadius(5)
|
||||
.overlay(alignment: .topLeading) {
|
||||
Button {
|
||||
withAnimation {
|
||||
tool.selectedImage = nil
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.font(.title2)
|
||||
.padding(1)
|
||||
.contentShape(.circle)
|
||||
.background {
|
||||
Circle()
|
||||
.fill(.white)
|
||||
}
|
||||
}
|
||||
.foregroundStyle(.red)
|
||||
.hoverEffect(.lift)
|
||||
.offset(x: -12, y: -12)
|
||||
}
|
||||
.padding(10)
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import PhotosUI
|
||||
import Foundation
|
||||
|
||||
struct Toolbar: View {
|
||||
@@ -15,8 +16,10 @@ struct Toolbar: View {
|
||||
@ObservedObject var canvas: Canvas
|
||||
@ObservedObject var history: History
|
||||
|
||||
@State var memo: MemoObject
|
||||
@State var title: String
|
||||
@State var memo: MemoObject
|
||||
@State var photoItem: PhotosPickerItem?
|
||||
|
||||
@FocusState var textFieldState: Bool
|
||||
|
||||
let size: CGFloat
|
||||
@@ -50,6 +53,19 @@ struct Toolbar: View {
|
||||
}
|
||||
.font(.subheadline)
|
||||
.padding(10)
|
||||
.onChange(of: photoItem) { oldValue, newValue in
|
||||
if newValue != nil {
|
||||
Task {
|
||||
let data = try? await newValue?.loadTransferable(type: Data.self)
|
||||
if let data {
|
||||
withAnimation {
|
||||
tool.selectedImage = UIImage(data: data)
|
||||
}
|
||||
}
|
||||
photoItem = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var closeButton: some View {
|
||||
@@ -105,22 +121,52 @@ struct Toolbar: View {
|
||||
.clipShape(.rect(cornerRadius: 8))
|
||||
}
|
||||
.hoverEffect(.lift)
|
||||
Button {
|
||||
withAnimation {
|
||||
tool.selection = tool.selection == .photo ? .none : .photo
|
||||
HStack(spacing: 0) {
|
||||
Button {
|
||||
withAnimation {
|
||||
tool.selection = tool.selection == .photo ? .none : .photo
|
||||
}
|
||||
} label: {
|
||||
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)
|
||||
if tool.selection == .photo {
|
||||
HStack(spacing: 0) {
|
||||
Button {
|
||||
|
||||
} label: {
|
||||
Image(systemName: "camera.fill")
|
||||
.contentShape(.circle)
|
||||
.frame(width: size, height: size)
|
||||
.clipShape(.rect(cornerRadius: 8))
|
||||
}
|
||||
.hoverEffect(.lift)
|
||||
PhotosPicker(selection: $photoItem, matching: .images) {
|
||||
Image(systemName: "photo.fill.on.rectangle.fill")
|
||||
.contentShape(.circle)
|
||||
.frame(width: size, height: size)
|
||||
.clipShape(.rect(cornerRadius: 8))
|
||||
}
|
||||
.hoverEffect(.lift)
|
||||
}
|
||||
}
|
||||
}
|
||||
.background {
|
||||
if tool.selection == .photo {
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.fill(Color.white.tertiary)
|
||||
}
|
||||
} label: {
|
||||
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(.regularMaterial)
|
||||
.clipShape(.rect(cornerRadius: 8))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.fill(.regularMaterial)
|
||||
}
|
||||
}
|
||||
|
||||
var historyControl: some View {
|
||||
|
||||
Reference in New Issue
Block a user