-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add (Apple Intelligence) image playground to the Quick Editor #565
Changes from all commits
32b605b
b540220
12541ac
3e0fba2
4a4510c
aff6072
c58a069
2635ee4
42a1dd0
2d8a300
5bfff19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import ImagePlayground | ||
import PhotosUI | ||
import SwiftUI | ||
|
||
|
@@ -15,27 +16,37 @@ private struct ImagePicker<Label, ImageEditor: ImageEditorView>: View where Labe | |
enum SourceType: CaseIterable, Identifiable { | ||
case photoLibrary | ||
case camera | ||
case playground | ||
|
||
static var allCases: [SourceType] { | ||
var cases: [SourceType] = [.camera, .photoLibrary] | ||
if #available(iOS 18.2, *) { | ||
if EnvironmentValues().supportsImagePlayground { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Works flawlessly |
||
cases.append(.playground) | ||
} | ||
} | ||
return cases | ||
} | ||
|
||
var id: Int { | ||
self.hashValue | ||
} | ||
} | ||
|
||
@State var isPresented = false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't used from anywhere so I am removing it. |
||
@State private var sourceType: SourceType? | ||
|
||
@ViewBuilder var label: () -> Label | ||
let onImageSelected: (UIImage) -> Void | ||
var customEditor: ImageEditorBlock<ImageEditor>? | ||
@State var imagePickerSelectedItem: ImagePickerItem? | ||
@State var playgroundSelectedItem: ImagePickerItem? | ||
|
||
var body: some View { | ||
VStack { | ||
Menu { | ||
ForEach(SourceType.allCases) { source in | ||
Button { | ||
sourceType = source | ||
isPresented = true | ||
} label: { | ||
SwiftUI.Label(source.localizedTitle, systemImage: source.iconName) | ||
} | ||
|
@@ -44,29 +55,58 @@ private struct ImagePicker<Label, ImageEditor: ImageEditorView>: View where Labe | |
label() | ||
} | ||
} | ||
.sheet(item: $sourceType, content: { source in | ||
// This allows to present different kind of pickers for different sources. | ||
displayImagePicker(for: source) | ||
.sheet(item: $imagePickerSelectedItem, content: { item in | ||
if let customEditor { | ||
customEditor(item.image) { editedImage in | ||
self.onImageEdited(editedImage) | ||
} | ||
} else { | ||
ImageCropper(inputImage: item.image) { croppedImage in | ||
Task { | ||
await self.onImageEdited(croppedImage) | ||
} | ||
} onCancel: { | ||
imagePickerSelectedItem = nil | ||
}.ignoresSafeArea() | ||
} | ||
}) | ||
.imagePlaygroundSheetIfAvailable( | ||
isPresented: Binding( | ||
get: { sourceType == .playground }, | ||
set: { if !$0 { sourceType = nil } } | ||
), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doing this to be able to add the new (The system offers a modifier for the playground not a View. But our existing image sources are Views. So they are displayed with different methods.) |
||
sourceImage: nil, | ||
onCompletion: { url in | ||
if let image = UIImage(contentsOfFile: url.relativePath) { | ||
playgroundSelectedItem = ImagePickerItem(id: url.absoluteString, image: image) | ||
} | ||
}, | ||
onCancellation: {} | ||
) | ||
.sheet(item: $playgroundSelectedItem, content: { item in | ||
imageEditor(with: item) | ||
}) | ||
.sheet( | ||
item: Binding( | ||
get: { sourceType != .playground ? sourceType : nil }, | ||
set: { sourceType = $0 } | ||
), | ||
content: { source in | ||
// This allows to present different kind of pickers for different sources. | ||
displayImagePicker(for: source) | ||
.sheet(item: $imagePickerSelectedItem, content: { item in | ||
imageEditor(with: item) | ||
}) | ||
} | ||
) | ||
} | ||
|
||
@ViewBuilder | ||
func imageEditor(with item: ImagePickerItem) -> some View { | ||
if let customEditor { | ||
customEditor(item.image) { editedImage in | ||
self.onImageEdited(editedImage) | ||
} | ||
} else { | ||
ImageCropper(inputImage: item.image) { croppedImage in | ||
Task { | ||
await self.onImageEdited(croppedImage) | ||
} | ||
} onCancel: { | ||
imagePickerSelectedItem = nil | ||
playgroundSelectedItem = nil | ||
}.ignoresSafeArea() | ||
} | ||
} | ||
|
||
private func onImageEdited(_ image: UIImage) { | ||
imagePickerSelectedItem = nil | ||
playgroundSelectedItem = nil | ||
sourceType = nil | ||
onImageSelected(image) | ||
} | ||
|
@@ -87,6 +127,8 @@ private struct ImagePicker<Label, ImageEditor: ImageEditorView>: View where Labe | |
} onCancel: { | ||
sourceType = nil | ||
}.ignoresSafeArea() | ||
case .playground: | ||
EmptyView() | ||
} | ||
} | ||
|
||
|
@@ -105,6 +147,8 @@ extension ImagePicker.SourceType { | |
"camera" | ||
case .photoLibrary: | ||
"photo.on.rectangle.angled" | ||
case .playground: | ||
"apple.image.playground" | ||
} | ||
} | ||
|
||
|
@@ -122,13 +166,12 @@ extension ImagePicker.SourceType { | |
value: "Take a Photo", | ||
comment: "An option in a menu that will display the camera for taking a picture" | ||
) | ||
} | ||
} | ||
|
||
func map() -> UIImagePickerController.SourceType { | ||
switch self { | ||
case .photoLibrary: .photoLibrary | ||
case .camera: .camera | ||
case .playground: | ||
SDKLocalizedString( | ||
"SystemImagePickerView.Source.Playground.title", | ||
value: "Playground", | ||
comment: "An option to show the image playground" | ||
) | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess
NSCameraUsageDescription
was removed during the merge of two demo apps. So I am adding it back.