feat: add macos runtime

This commit is contained in:
dscyrescotti
2024-07-06 14:20:31 +07:00
parent 8d33b94789
commit 15e96136f1
41 changed files with 367 additions and 114 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ struct MemolaApp: App {
WindowGroup {
DashboardView()
.persistence(\.viewContext)
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willTerminateNotification)) { _ in
.onReceive(NotificationCenter.default.publisher(for: Platform.Application.willTerminateNotification)) { _ in
withPersistenceSync(\.viewContext) { context in
try context.saveIfNeeded()
}
+5 -1
View File
@@ -85,7 +85,11 @@ extension Canvas {
func save(for memoObject: MemoObject, completion: @escaping () -> Void) {
state = .closing
let previewImage = renderer?.drawPreview(on: self)
#if os(macOS)
#warning("TODO: implement for macos")
#else
memoObject.preview = previewImage?.jpegData(compressionQuality: 0.8)
#endif
withPersistenceSync(\.viewContext) { context in
try context.saveIfNeeded()
}
@@ -135,7 +139,7 @@ extension Canvas {
self.previewTransform = simd_float4x4(transform)
}
func updateClipBounds(_ scrollView: UIScrollView, on drawingView: DrawingView) {
func updateClipBounds(_ scrollView: Platform.ScrollView, on drawingView: DrawingView) {
let ratio = drawingView.ratio
let bounds = scrollView.convert(scrollView.bounds, to: drawingView)
clipBounds = CGRect(origin: bounds.origin.muliply(by: ratio), size: bounds.size.multiply(by: ratio))
+6 -1
View File
@@ -110,7 +110,7 @@ final class Renderer {
viewPortRenderPass.draw(into: commandBuffer, on: canvas, with: self)
}
func drawPreview(on canvas: Canvas) -> UIImage? {
func drawPreview(on canvas: Canvas) -> Platform.Image? {
guard let commandBuffer = commandQueue.makeCommandBuffer() else {
NSLog("[Memola] - Unable to create command buffer")
return nil
@@ -124,6 +124,11 @@ final class Renderer {
guard let cgImage = previewRenderPass.previewTexture?.getImage() else {
return nil
}
#if os(macOS)
#warning("TODO: implement here")
return nil
#else
return UIImage(cgImage: cgImage, scale: 1.0, orientation: .downMirrored)
#endif
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ final class Photo: @unchecked Sendable, Equatable {
var id: UUID = UUID()
var size: CGSize
var origin: CGPoint
var image: UIImage?
var image: Platform.Image?
var url: URL?
var bounds: [CGFloat]
var createdAt: Date
+12 -3
View File
@@ -127,7 +127,7 @@ public class Tool: NSObject, ObservableObject {
}
}
func selectPhoto(_ image: UIImage, for canvasID: NSManagedObjectID) {
func selectPhoto(_ image: Platform.Image, for canvasID: NSManagedObjectID) {
guard let (resizedImage, dimension) = resizePhoto(of: image) else { return }
let photoItem = bookmarkPhoto(of: resizedImage, and: image, in: dimension, with: canvasID)
withAnimation {
@@ -136,7 +136,7 @@ public class Tool: NSObject, ObservableObject {
}
}
private func resizePhoto(of image: UIImage) -> (UIImage, CGSize)? {
private func resizePhoto(of image: Platform.Image) -> (Platform.Image, CGSize)? {
let targetSize = CGSize(width: 512, height: 512)
let size = image.size
let widthRatio = targetSize.width / size.width
@@ -147,6 +147,9 @@ public class Tool: NSObject, ObservableObject {
)
let rect = CGRect(origin: .zero, size: targetSize)
#if os(macOS)
return (image, dimension)
#else
UIGraphicsBeginImageContextWithOptions(targetSize, true, 1.0)
image.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
@@ -155,10 +158,16 @@ public class Tool: NSObject, ObservableObject {
guard let newImage else { return nil }
return (newImage, dimension)
#endif
}
private func bookmarkPhoto(of image: UIImage, and previewImage: UIImage, in dimension: CGSize, with canvasID: NSManagedObjectID) -> PhotoItem? {
private func bookmarkPhoto(of image: Platform.Image, and previewImage: Platform.Image, in dimension: CGSize, with canvasID: NSManagedObjectID) -> PhotoItem? {
#if os(macOS)
#warning("TODO: implement for macos")
let data = Data()
#else
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
#endif
let fileManager = FileManager.default
guard let directory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
return nil
@@ -10,14 +10,14 @@ import SwiftUI
import MetalKit
import Foundation
class CanvasViewController: UIViewController {
class CanvasViewController: Platform.ViewController {
let drawingView: DrawingView
let scrollView: UIScrollView = UIScrollView()
let scrollView: Platform.ScrollView = Platform.ScrollView()
var renderView: MTKView {
drawingView.renderView
}
var photoInsertGesture: UITapGestureRecognizer?
var photoInsertGesture: Platform.TapGestureRecognizer?
let tool: Tool
let canvas: Canvas
@@ -47,6 +47,28 @@ class CanvasViewController: UIViewController {
configureListeners()
}
#if os(macOS)
override func viewWillAppear() {
super.viewWillAppear()
resizeDocumentView()
updateDocumentBounds()
loadMemo()
}
override func viewDidLayout() {
super.viewDidLayout()
drawingView.disableUserInteraction()
drawingView.updateDrawableSize(with: view.frame.size)
renderer.resize(on: renderView, to: renderView.drawableSize)
renderView.draw()
drawingView.enableUserInteraction()
}
override func viewDidDisappear() {
super.viewDidDisappear()
history.resetRedo()
}
#else
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
resizeDocumentView()
@@ -67,11 +89,16 @@ class CanvasViewController: UIViewController {
super.viewDidDisappear(animated)
history.resetRedo()
}
#endif
}
extension CanvasViewController {
func configureViews() {
#if os(macOS)
#warning("TODO: implement for macos")
#else
view.backgroundColor = .white
renderView.autoResizeDrawable = false
renderView.enableSetNeedsDisplay = true
renderView.translatesAutoresizingMaskIntoConstraints = false
@@ -105,9 +132,13 @@ extension CanvasViewController {
scrollView.addSubview(drawingView)
drawingView.backgroundColor = .clear
drawingView.isUserInteractionEnabled = false
#endif
}
func resizeDocumentView(to newSize: CGSize? = nil) {
#if os(macOS)
#warning("TODO: implement for macos")
#else
scrollView.layoutIfNeeded()
let size = canvas.size
@@ -130,17 +161,25 @@ extension CanvasViewController {
scrollView.setContentOffset(point, animated: true)
drawingView.updateDrawableSize(with: view.frame.size)
#endif
}
func centerDocumentView(to newSize: CGSize? = nil) {
#if os(macOS)
#warning("TODO: implement for macos")
#else
let documentViewSize = drawingView.frame.size
let scrollViewSize = newSize ?? view.frame.size
let verticalPadding = documentViewSize.height < scrollViewSize.height ? (scrollViewSize.height - documentViewSize.height) / 2 : 0
let horizontalPadding = documentViewSize.width < scrollViewSize.width ? (scrollViewSize.width - documentViewSize.width) / 2 : 0
self.scrollView.contentInset = UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding)
#endif
}
func updateDocumentBounds() {
#if os(macOS)
#warning("TODO: implement for macos")
#else
var bounds = scrollView.bounds.muliply(by: drawingView.ratio / scrollView.zoomScale)
let xDelta = bounds.minX * 0.0
let yDelta = bounds.minY * 0.0
@@ -152,6 +191,7 @@ extension CanvasViewController {
if canvas.state == .loaded {
canvas.loadStrokes(bounds)
}
#endif
}
}
@@ -231,13 +271,17 @@ extension CanvasViewController: MTKViewDelegate {
extension CanvasViewController {
func configureGestures() {
let photoInsertGesture = UITapGestureRecognizer(target: self, action: #selector(recognizeTapGesture))
let photoInsertGesture = Platform.TapGestureRecognizer(target: self, action: #selector(recognizeTapGesture))
#if os(macOS)
photoInsertGesture.numberOfClicksRequired = 1
#else
photoInsertGesture.numberOfTapsRequired = 1
#endif
self.photoInsertGesture = photoInsertGesture
scrollView.addGestureRecognizer(photoInsertGesture)
}
@objc func recognizeTapGesture(_ gesture: UITapGestureRecognizer) {
@objc func recognizeTapGesture(_ gesture: Platform.TapGestureRecognizer) {
guard let photoItem = tool.selectedPhotoItem else { return }
withAnimation {
tool.selectedPhotoItem = nil
@@ -249,6 +293,8 @@ extension CanvasViewController {
}
}
#if os(macOS)
#else
extension CanvasViewController: UIScrollViewDelegate {
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
drawingView
@@ -294,6 +340,7 @@ extension CanvasViewController: UIScrollViewDelegate {
draggingEnded()
}
}
#endif
extension CanvasViewController {
func magnificationStarted() {
@@ -352,20 +399,32 @@ extension CanvasViewController {
enablesDrawing = false
enablesPhotoInsertion = true
}
#if os(macOS)
#warning("TODO: implement for macos")
#else
scrollView.isScrollEnabled = enablesScrolling
drawingView.isUserInteractionEnabled = enablesDrawing
photoInsertGesture?.isEnabled = enablesPhotoInsertion
enablesDrawing ? drawingView.enableUserInteraction() : drawingView.disableUserInteraction()
#endif
}
}
extension CanvasViewController {
func zoomChanged(_ zoomScale: CGFloat) {
#if os(macOS)
#warning("TODO: implement for macos")
#else
scrollView.setZoomScale(zoomScale, animated: true)
#endif
}
func lockModeChanged(_ state: Bool) {
#if os(macOS)
#warning("TODO: implement for macos")
#else
scrollView.pinchGestureRecognizer?.isEnabled = !state
#endif
}
func gridModeChanged(_ mode: GridMode) {
@@ -5,11 +5,11 @@
// Created by Dscyre Scotti on 5/4/24.
//
import UIKit
import SwiftUI
import MetalKit
import Foundation
class DrawingView: UIView {
class DrawingView: Platform.View {
let tool: Tool
let canvas: Canvas
let history: History
@@ -36,7 +36,10 @@ class DrawingView: UIView {
func updateDrawableSize(with size: CGSize) {
renderView.drawableSize = size.multiply(by: 2)
}
#if os(macOS)
#warning("TODO: to implement touch events")
#else
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
!canvas.hasValidStroke
}
@@ -78,6 +81,7 @@ class DrawingView: UIView {
let point = touch.preciseLocation(in: self)
touchEnded(at: point)
}
#endif
func touchBegan(at point: CGPoint) {
guard !disablesUserInteraction else { return }
+9 -1
View File
@@ -7,14 +7,22 @@
import SwiftUI
struct CanvasView: UIViewControllerRepresentable {
struct CanvasView: Platform.ViewControllerRepresentable {
@ObservedObject var tool: Tool
@ObservedObject var canvas: Canvas
@ObservedObject var history: History
#if os(macOS)
func makeNSViewController(context: Context) -> CanvasViewController {
CanvasViewController(tool: tool, canvas: canvas, history: history)
}
func updateNSViewController(_ nsViewController: CanvasViewController, context: Context) { }
#else
func makeUIViewController(context: Context) -> CanvasViewController {
CanvasViewController(tool: tool, canvas: canvas, history: history)
}
func updateUIViewController(_ uiViewController: CanvasViewController, context: Context) { }
#endif
}
@@ -7,6 +7,7 @@
import SwiftUI
#if os(iOS)
struct CameraView: UIViewControllerRepresentable {
@Binding var image: UIImage?
@@ -44,3 +45,4 @@ struct CameraView: UIViewControllerRepresentable {
}
}
}
#endif
@@ -26,6 +26,9 @@ struct DashboardView: View {
MemosView(memo: $memo)
}
}
#if os(macOS)
#warning("TODO: implement for macOS")
#else
.fullScreenCover(item: $memo) { memo in
MemoView(memo: memo)
.onDisappear {
@@ -35,5 +38,6 @@ struct DashboardView: View {
}
}
}
#endif
}
}
@@ -46,9 +46,14 @@ struct MemosView: View {
memoCard(memoObject, cellWidth)
}
.navigationTitle(horizontalSizeClass == .compact ? "Memos" : "")
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.searchable(text: $query, placement: .toolbar, prompt: Text("Search"))
.toolbar {
#if os(macOS)
#warning("TODO: implement for macos")
#else
if horizontalSizeClass == .regular {
ToolbarItem(placement: .topBarLeading) {
Text("Memola")
@@ -111,6 +116,7 @@ struct MemosView: View {
}
}
}
#endif
}
.onChange(of: sort) { oldValue, newValue in
memoObjects.sortDescriptors = newValue.memoSortDescriptors
@@ -42,6 +42,10 @@ struct MemoGrid<Card: View>: View {
}
}
}
.background(Color(uiColor: .secondarySystemBackground))
#if os(macOS)
.background(Color(color: .windowBackgroundColor))
#else
.background(Color(color: .secondarySystemBackground))
#endif
}
}
@@ -21,8 +21,8 @@ struct MemoPreview: View {
var body: some View {
Group {
if let preview, let previewImage = UIImage(data: preview) {
Image(uiImage: previewImage)
if let preview, let previewImage = Platform.Image(data: preview) {
Image(image: previewImage)
.resizable()
.aspectRatio(contentMode: .fit)
} else {
@@ -46,9 +46,14 @@ struct TrashView: View {
memoCard(memoObject, cellWidth)
}
.navigationTitle(horizontalSizeClass == .compact ? "Trash" : "")
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
.searchable(text: $query, placement: .toolbar, prompt: Text("Search"))
.toolbar {
#if os(macOS)
#warning("TODO: implement for macos")
#else
if horizontalSizeClass == .regular {
ToolbarItem(placement: .topBarLeading) {
Text("Memola")
@@ -56,6 +61,7 @@ struct TrashView: View {
.fontWeight(.bold)
}
}
#endif
}
.onChange(of: query) { oldValue, newValue in
updatePredicate()
@@ -38,9 +38,15 @@ struct Sidebar: View {
.listStyle(.sidebar)
.navigationTitle(horizontalSizeClass == .compact ? "Memola" : "")
.scrollContentBackground(.hidden)
.background(Color(uiColor: .secondarySystemBackground))
#if os(macOS)
.background(Color(color: .windowBackgroundColor))
#else
.background(Color(color: .secondarySystemBackground))
#endif
.navigationSplitViewColumnWidth(min: 250, ideal: 250, max: 250)
#if os(iOS)
.navigationBarTitleDisplayMode(horizontalSizeClass == .compact ? .automatic : .inline)
#endif
}
}
@@ -43,6 +43,7 @@ struct ElementToolbar: View {
.padding(.bottom, 10)
}
}
#if os(iOS)
.fullScreenCover(isPresented: $opensCamera) {
let image: Binding<UIImage?> = Binding {
tool.selectedPhotoItem?.image
@@ -55,8 +56,8 @@ struct ElementToolbar: View {
}
.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)
if let url = URL(string: Platform.Application.openSettingsURLString + "&path=CAMERA/\(String(describing: Bundle.main.bundleIdentifier))") {
Platform.Application.shared.open(url)
}
} label: {
Text("Open Settings")
@@ -65,12 +66,13 @@ struct ElementToolbar: View {
} message: {
Text("Memola requires access to the camera to capture photos. Please open Settings and enable camera access.")
}
#endif
.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) {
if let data, let image = Platform.Image(data: data) {
tool.selectPhoto(image, for: canvas.canvasID)
}
photosPickerItem = nil
@@ -93,7 +95,9 @@ struct ElementToolbar: View {
.foregroundStyle(tool.selection == .hand ? Color.white : Color.accentColor)
.clipShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#endif
.background {
if tool.selection == .hand {
Color.accentColor
@@ -113,7 +117,9 @@ struct ElementToolbar: View {
.foregroundStyle(tool.selection == .pen ? Color.white : Color.accentColor)
.clipShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#endif
.background {
if tool.selection == .pen {
Color.accentColor
@@ -133,7 +139,9 @@ struct ElementToolbar: View {
.foregroundStyle(tool.selection == .photo ? Color.white : Color.accentColor)
.clipShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#endif
.background {
if tool.selection == .photo {
Color.accentColor
@@ -179,7 +187,9 @@ struct ElementToolbar: View {
.frame(width: size, height: size)
.clipShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#endif
Button {
withAnimation {
tool.selectTool(.photo)
@@ -190,7 +200,9 @@ struct ElementToolbar: View {
.frame(width: size, height: size)
.clipShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#endif
}
.background {
RoundedRectangle(cornerRadius: 8)
@@ -211,14 +223,18 @@ struct ElementToolbar: View {
.frame(width: size, height: size)
.clipShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#endif
PhotosPicker(selection: $photosPickerItem, matching: .images, preferredItemEncoding: .compatible) {
Image(systemName: "photo.fill.on.rectangle.fill")
.contentShape(.circle)
.frame(width: size, height: size)
.clipShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#endif
}
}
+2
View File
@@ -158,7 +158,9 @@ struct MemoView: View {
.clipShape(.rect(cornerRadius: 8))
.padding(10)
}
#if os(iOS)
.hoverEffect(.lift)
#endif
.transition(.move(edge: .bottom).combined(with: .blurReplace))
}
}
@@ -201,7 +201,11 @@ struct PenDock: View {
} preview: {
penPreview(pen)
.drawingGroup()
#if os(macOS)
#warning("TODO: implement for macos")
#else
.contentShape(.contextMenuPreview, .rect(cornerRadius: 10))
#endif
}
.onDrag(if: pen.strokeStyle != .eraser) {
tool.draggedPen = pen
@@ -274,7 +278,11 @@ struct PenDock: View {
} preview: {
penPreview(pen)
.drawingGroup()
#if os(macOS)
#warning("TODO: implement for macos")
#else
.contentShape(.contextMenuPreview, .rect(cornerRadius: 10))
#endif
}
.onDrag(if: pen.strokeStyle != .eraser) {
tool.draggedPen = pen
@@ -356,7 +364,9 @@ struct PenDock: View {
.drawingGroup()
}
.buttonStyle(.plain)
#if os(iOS)
.hoverEffect(.lift)
#endif
.popover(isPresented: $opensColorPicker) {
let color = Binding(
get: { pen.color },
@@ -397,8 +407,10 @@ struct PenDock: View {
.frame(width: size + 2, height: size + 2)
}
}
#if os(iOS)
.hoverEffect(.lift)
.pickerStyle(.wheel)
#endif
.frame(width: width * factor - 18, height: 35)
.onChange(of: pen.thickness) { _, _ in
withPersistence(\.viewContext) { context in
@@ -429,8 +441,10 @@ struct PenDock: View {
.frame(width: size + 2, height: size + 2)
}
}
#if os(iOS)
.hoverEffect(.lift)
.pickerStyle(.wheel)
#endif
.frame(width: 50, height: 30)
.onChange(of: pen.thickness) { _, _ in
withPersistence(\.viewContext) { context in
@@ -453,7 +467,9 @@ struct PenDock: View {
}
}
.foregroundStyle(.green)
#if os(iOS)
.hoverEffect(.lift)
#endif
}
func penPreview(_ pen: Pen) -> some View {
@@ -532,7 +548,9 @@ struct PenDock: View {
.background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#endif
.contentTransition(.symbolEffect(.replace))
}
@@ -5,13 +5,13 @@
// Created by Dscyre Scotti on 6/16/24.
//
import UIKit
import SwiftUI
import Foundation
struct PhotoItem: Identifiable, Equatable {
var id: URL
let image: UIImage
let previewImage: UIImage
let image: Platform.Image
let previewImage: Platform.Image
let dimension: CGSize
let bookmark: Data
@@ -14,7 +14,7 @@ struct PhotoPreview: View {
@ObservedObject var tool: Tool
var body: some View {
Image(uiImage: photoItem.previewImage)
Image(image: photoItem.previewImage)
.resizable()
.scaledToFit()
.frame(width: horizontalSizeClass == .compact ? 80 : nil, height: horizontalSizeClass == .compact ? nil : 100)
@@ -40,7 +40,9 @@ struct PhotoPreview: View {
}
}
.foregroundStyle(.red)
#if os(iOS)
.hoverEffect(.lift)
#endif
.offset(x: -12, y: -12)
}
.padding(10)
@@ -66,7 +66,9 @@ struct Toolbar: View {
.background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#endif
.disabled(textFieldState)
.transition(.move(edge: .top).combined(with: .blurReplace))
}
@@ -103,7 +105,9 @@ struct Toolbar: View {
Image(systemName: "arrow.uturn.backward.circle")
.contentShape(.circle)
}
#if os(iOS)
.hoverEffect(.lift)
#endif
.disabled(history.undoDisabled)
Button {
history.historyPublisher.send(.redo)
@@ -111,7 +115,9 @@ struct Toolbar: View {
Image(systemName: "arrow.uturn.forward.circle")
.contentShape(.circle)
}
#if os(iOS)
.hoverEffect(.lift)
#endif
.disabled(history.redoDisabled)
}
.frame(width: size * 2, height: size)
@@ -142,7 +148,9 @@ struct Toolbar: View {
.background(.regularMaterial)
.clipShape(.rect(cornerRadius: 8))
}
#if os(iOS)
.hoverEffect(.lift)
#endif
.contentTransition(.symbolEffect(.replace))
.transition(.move(edge: .top).combined(with: .blurReplace))
}
@@ -8,8 +8,16 @@
import SwiftUI
extension Color {
init(color: Platform.Color) {
#if os(macOS)
self = Color(nsColor: color)
#else
self = Color(uiColor: color)
#endif
}
var components: [CGFloat] {
let color = UIColor(self)
let color = Platform.Color(self)
return color.components
}
@@ -20,6 +28,16 @@ extension Color {
extension Color {
var hsba: (hue: Double, saturation: Double, brightness: Double, alpha: Double) {
#if os(macOS)
#warning("TODO: need double check")
let nsColor = NSColor(self)
var hue: CGFloat = 0
var saturation: CGFloat = 0
var brightness: CGFloat = 0
var alpha: CGFloat = 0
nsColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
return (hue, saturation, brightness, alpha)
#else
let uiColor = UIColor(self)
var hue: CGFloat = 0
var saturation: CGFloat = 0
@@ -27,13 +45,21 @@ extension Color {
var alpha: CGFloat = 0
uiColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
return (hue, saturation, brightness, alpha)
#endif
}
}
extension UIColor {
extension Platform.Color {
var components: [CGFloat] {
#if os(macOS)
#warning("TODO: need double check")
let nsColor: NSColor = self
let ciColor: CIColor = .init(color: nsColor) ?? CIColor(red: 0, green: 0, blue: 0)
return [ciColor.red, ciColor.green, ciColor.blue, ciColor.alpha]
#else
let uiColor: UIColor = self
let ciColor: CIColor = .init(color: uiColor)
return [ciColor.red, ciColor.green, ciColor.blue, ciColor.alpha]
#endif
}
}
@@ -1,13 +1,24 @@
//
// UIImage++.swift
// Image++.swift
// Memola
//
// Created by Dscyre Scotti on 6/15/24.
//
import UIKit
import SwiftUI
import Foundation
extension Image {
init(image: Platform.Image) {
#if os(macOS)
self = Image(nsImage: image)
#else
self = Image(uiImage: image)
#endif
}
}
#if os(iOS)
extension UIImage {
func imageWithUpOrientation() -> UIImage? {
switch imageOrientation {
@@ -22,3 +33,4 @@ extension UIImage {
}
}
}
#endif
+30
View File
@@ -0,0 +1,30 @@
//
// Platform.swift
// Memola
//
// Created by Dscyre Scotti on 7/6/24.
//
import SwiftUI
enum Platform {
#if os(macOS)
typealias View = NSView
typealias Color = NSColor
typealias Image = NSImage
typealias ScrollView = NSScrollView
typealias Application = NSApplication
typealias ViewController = NSViewController
typealias TapGestureRecognizer = NSClickGestureRecognizer
typealias ViewControllerRepresentable = NSViewControllerRepresentable
#else
typealias View = UIView
typealias Color = UIColor
typealias Image = UIImage
typealias ScrollView = UIScrollView
typealias Application = UIApplication
typealias ViewController = UIViewController
typealias TapGestureRecognizer = UITapGestureRecognizer
typealias ViewControllerRepresentable = UIViewControllerRepresentable
#endif
}