Initial Commit

This commit is contained in:
aminecmi
2024-07-26 15:16:48 +02:00
parent 656680d19f
commit 9f51394788
20 changed files with 1135 additions and 0 deletions

View File

@ -0,0 +1,79 @@
//
// PhotoTumbnailView.swift
// Swipe That Pic
//
// Created by Amine Bou on 27/07/2024.
//
import Foundation
import Photos
import SwiftUI
struct BigPicture: View {
private var image: Image
@State var scale = 1.0
@State var lastScale = 0.0
@State var offset: CGSize = .zero
@State var lastOffset: CGSize = .zero
init(image: Image) {
self.image = image
}
var body: some View {
GeometryReader { proxy in
image
.resizable()
.scaledToFill()
.scaleEffect(scale)
.offset(offset)
.frame(width: proxy.size.width, height: proxy.size.height)
.gesture(
MagnificationGesture(minimumScaleDelta: 0)
.onChanged({ value in
withAnimation(.interactiveSpring()) {
scale = handleScaleChange(value)
}
})
.onEnded({ _ in
lastScale = scale
}).simultaneously(
with: DragGesture(minimumDistance: 0)
.onChanged({ value in
withAnimation(.interactiveSpring()) {
offset = handleOffsetChange(value.translation)
}
})
.onEnded({ _ in
lastOffset = offset
})
).simultaneously(with: TapGesture(count: 2).onEnded({ Void in
scale = 1.0
lastScale = 0.0
offset = .zero
lastOffset = .zero
}))
)
}
// We'll also make sure that the photo will
// be square
.aspectRatio(1, contentMode: .fit)
}
private func handleScaleChange(_ zoom: CGFloat) -> CGFloat {
lastScale + zoom - (lastScale == 0 ? 0 : 1)
}
private func handleOffsetChange(_ offset: CGSize) -> CGSize {
var newOffset: CGSize = .zero
newOffset.width = offset.width + lastOffset.width
newOffset.height = offset.height + lastOffset.height
return newOffset
}
}

View File

@ -0,0 +1,70 @@
//
// ContentView.swift
// Swipe That Pic
//
// Created by Amine Bou on 26/07/2024.
//
import SwiftUI
import SwiftData
import Photos
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@EnvironmentObject var photoLibraryService: PhotosService
@Query private var items: [Item]
var body: some View {
NavigationSplitView {
VStack {
PhotoThumbnailView(asset: $photoLibraryService.one)
Spacer()
HStack {
Button(role: .destructive, action: {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.deleteAssets([photoLibraryService.one as Any] as NSArray)
}, completionHandler: { success, error in
if success {
photoLibraryService.fetchNotKeptPhotos()
} else {
}})
}) {
Label("Supprimer", systemImage: "minus.circle.fill")
}.buttonStyle(.borderedProminent)
Spacer()
Button(action: {
let newItem = Item(localIdentfier: photoLibraryService.one!.localIdentifier)
modelContext.insert(newItem)
photoLibraryService.fetchNotKeptPhotos()
}) {
Label("Garder", systemImage: "plus.circle.fill")
}.buttonStyle(.borderedProminent)
}.disabled(photoLibraryService.one == nil)
}
.toolbar {
if (items.count > 0) {
ToolbarItem {
NavigationLink(destination: IgnoredPictures()) {
Label("Handle kept", systemImage: "tray.and.arrow.up.fill")
}
}
}
}
} detail: {
Text("Select an item")
}.onAppear(perform: {
photoLibraryService.fetchNotKeptPhotos()
})
}
}
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}

View File

@ -0,0 +1,53 @@
//
// ContentView.swift
// Swipe That Pic
//
// Created by Amine Bou on 26/07/2024.
//
import SwiftUI
import SwiftData
import Photos
struct IgnoredPictures: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
var body: some View {
NavigationSplitView {
ScrollView {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 200)), GridItem(.adaptive(minimum: 200))], alignment: .leading) {
ForEach(items) { item in
PhotoThumbnailFromStringView(localIdentifier: item.localIdentfier)
}
}
.toolbar {
ToolbarItem {
Button(action: clearItems) {
Label("Clear", systemImage: "bubbles.and.sparkles.fill")
}
}
}
}
} detail : {
Text("Ignored pictures")
}
}
private func clearItems() {
withAnimation {
do {
try modelContext.delete(model: Item.self)
} catch {
print("did not work")
}
}
}
}
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}

View File

@ -0,0 +1,73 @@
//
// PhotoTumbnailView.swift
// Swipe That Pic
//
// Created by Amine Bou on 27/07/2024.
//
import Foundation
import Photos
import SwiftUI
struct PhotoThumbnailFromStringView: View {
@EnvironmentObject var photoLibraryService: PhotosService
@State private var image: Image?
@State var localIdentifier: String?
func loadImageAsset(
targetSize: CGSize = PHImageManagerMaximumSize
) async {
if (localIdentifier != nil) {
guard let uiImage = try? await photoLibraryService
.fetchImage(
byLocalIdentifier: localIdentifier!,
targetSize: targetSize
) else {
image = nil
return
}
image = Image(uiImage: uiImage)
} else {
image = nil
}
}
var body: some View {
ZStack {
// Show the image if it's available
if let image = image {
GeometryReader { proxy in
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(
width: proxy.size.width,
height: proxy.size.width
)
.clipped()
}
// We'll also make sure that the photo will
// be square
.aspectRatio(1, contentMode: .fit)
} else {
// Otherwise, show a gray rectangle with a
// spinning progress view
Rectangle()
.foregroundColor(.gray)
.aspectRatio(1, contentMode: .fit)
ProgressView()
}
}
// We need to use the task to work on a concurrent request to
// load the image from the photo library service, which
// is asynchronous work.
.task(id: localIdentifier) {
await loadImageAsset()
}
// Finally, when the view disappears, we need to free it
// up from the memory
.onDisappear {
image = nil
}
}
}

View File

@ -0,0 +1,73 @@
//
// PhotoTumbnailView.swift
// Swipe That Pic
//
// Created by Amine Bou on 27/07/2024.
//
import Foundation
import Photos
import SwiftUI
struct PhotoThumbnailView: View {
@EnvironmentObject var photoLibraryService: PhotosService
@State private var image: Image?
@Binding var asset: PHAsset?
func loadImageAsset(
targetSize: CGSize = PHImageManagerMaximumSize
) async {
if (asset != nil) {
guard let uiImage = try? await photoLibraryService
.fetchImage(
byLocalIdentifier: asset!.localIdentifier,
targetSize: targetSize
) else {
image = nil
return
}
image = Image(uiImage: uiImage)
} else {
image = nil
}
}
var body: some View {
ZStack {
// Show the image if it's available
if let image = image {
GeometryReader { proxy in
NavigationLink(destination: BigPicture(image: image)) {
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(
width: proxy.size.width,
height: proxy.size.width
)
.clipped()
}
}
} else {
// Otherwise, show a gray rectangle with a
// spinning progress view
Rectangle()
.foregroundColor(.gray)
.aspectRatio(1, contentMode: .fit)
ProgressView()
}
}
// We need to use the task to work on a concurrent request to
// load the image from the photo library service, which
// is asynchronous work.
.task(id: asset?.localIdentifier) {
await loadImageAsset()
}
// Finally, when the view disappears, we need to free it
// up from the memory
.onDisappear {
image = nil
}
}
}