feat: add camera view

This commit is contained in:
dscyrescotti
2024-06-15 13:25:36 +07:00
parent 49e878d0dd
commit 5203f39f96
4 changed files with 99 additions and 2 deletions

View File

@@ -84,6 +84,7 @@
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 */; };
ECBE52992C1D60E5006BDB3D /* CameraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECBE52982C1D60E5006BDB3D /* CameraView.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 */; };
@@ -183,6 +184,7 @@
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>"; };
ECBE52982C1D60E5006BDB3D /* CameraView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraView.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>"; };
@@ -232,6 +234,7 @@
EC1B783A2BF9C68C005A34E2 /* Views */ = {
isa = PBXGroup;
children = (
ECBE52972C1D6087006BDB3D /* CameraView */,
ECFC51252BF8885000D0D051 /* ColorPicker */,
);
path = Views;
@@ -676,6 +679,14 @@
path = PhotoPreview;
sourceTree = "<group>";
};
ECBE52972C1D6087006BDB3D /* CameraView */ = {
isa = PBXGroup;
children = (
ECBE52982C1D60E5006BDB3D /* CameraView.swift */,
);
path = CameraView;
sourceTree = "<group>";
};
ECD12A872C19EF8700B96E12 /* Elements */ = {
isa = PBXGroup;
children = (
@@ -890,6 +901,7 @@
EC0D14212BF79C73009BFE5F /* ToolObject.swift in Sources */,
EC50500D2BF6674400B4D86E /* OnDragViewModifier.swift in Sources */,
EC9AB09F2C1401A40076AF58 /* EraserObject.swift in Sources */,
ECBE52992C1D60E5006BDB3D /* CameraView.swift in Sources */,
ECA7389E2BE601CB00A4542E /* QuadVertex.swift in Sources */,
ECA738B32BE60D9E00A4542E /* CanvasView.swift in Sources */,
ECA738C42BE60E8800A4542E /* MarkerPenStyle.swift in Sources */,

View File

@@ -0,0 +1,44 @@
//
// CameraView.swift
// Memola
//
// Created by Dscyre Scotti on 6/15/24.
//
import SwiftUI
struct CameraView: UIViewControllerRepresentable {
@Binding var image: UIImage?
@Environment(\.dismiss) private var dismiss
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = .camera
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) { }
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
let parent: CameraView
init(_ parent: CameraView) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
parent.image = info[.originalImage] as? UIImage
parent.dismiss()
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
parent.dismiss()
}
}
}

View File

@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSCameraUsageDescription</key>
<string>Memola requires access to the camera to capture photos.</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>UISupportedInterfaceOrientations~ipad</key>

View File

@@ -8,6 +8,7 @@
import SwiftUI
import PhotosUI
import Foundation
import AVFoundation
struct Toolbar: View {
@Environment(\.dismiss) var dismiss
@@ -19,6 +20,8 @@ struct Toolbar: View {
@State var title: String
@State var memo: MemoObject
@State var photoItem: PhotosPickerItem?
@State var opensCamera: Bool = false
@State var isCameraAccessDenied: Bool = false
@FocusState var textFieldState: Bool
@@ -66,6 +69,22 @@ struct Toolbar: View {
}
}
}
.fullScreenCover(isPresented: $opensCamera) {
CameraView(image: $tool.selectedImage)
.ignoresSafeArea()
}
.alert("Camera Access Denied", isPresented: $isCameraAccessDenied) {
Button {
if let url = URL(string: UIApplication.openSettingsURLString + "&path=CAMERA/\(String(describing: Bundle.main.bundleIdentifier))") {
UIApplication.shared.open(url)
}
} label: {
Text("Open Settings")
}
Button("Cancel", role: .cancel) { }
} message: {
Text("Memola requires access to the camera to capture photos. Please open Settings and enable camera access.")
}
}
var closeButton: some View {
@@ -114,6 +133,7 @@ struct Toolbar: View {
}
} label: {
Image(systemName: "pencil")
.fontWeight(.heavy)
.contentShape(.circle)
.frame(width: size, height: size)
.background(tool.selection == .pen ? Color.accentColor : Color.clear)
@@ -138,7 +158,7 @@ struct Toolbar: View {
if tool.selection == .photo {
HStack(spacing: 0) {
Button {
openCamera()
} label: {
Image(systemName: "camera.fill")
.contentShape(.circle)
@@ -175,7 +195,6 @@ struct Toolbar: View {
history.historyPublisher.send(.undo)
} label: {
Image(systemName: "arrow.uturn.backward.circle")
.contentShape(.circle)
}
.hoverEffect(.lift)
@@ -220,6 +239,26 @@ struct Toolbar: View {
.hoverEffect(.lift)
}
func openCamera() {
let status = AVCaptureDevice.authorizationStatus(for: .video)
switch status {
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { status in
withAnimation {
if status {
opensCamera = true
} else {
isCameraAccessDenied = true
}
}
}
case .authorized:
opensCamera = true
default:
isCameraAccessDenied = true
}
}
func closeMemo() {
withAnimation {
canvas.state = .closing