Created
June 28, 2022 00:08
-
-
Save MarcoEidinger/a5f44d79f0dfc6619db5126e903903f9 to your computer and use it in GitHub Desktop.
Working example of using PhotosPicker in a SwiftUI application (verified on Xcode 14 Beta 2 and iOS 16 simulator)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct CircularProfileImage: View { | |
let imageState: ProfileModel.ImageState | |
var body: some View { | |
ProfileImage(imageState: imageState) | |
.frame(width: 100, height: 100) | |
.clipShape(Circle()) | |
.background { | |
Circle().fill( | |
LinearGradient(colors: [.yellow, .orange], startPoint: .top, endPoint: .bottom) | |
) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct ContentView: View { | |
@State var viewModel: ProfileModel = .init() | |
var body: some View { | |
EditableCircularProfileImage(viewModel: viewModel) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PhotosUI | |
import SwiftUI | |
struct EditableCircularProfileImage: View { | |
@ObservedObject var viewModel: ProfileModel | |
var body: some View { | |
CircularProfileImage(imageState: viewModel.imageState) | |
.overlay(alignment: .bottomTrailing) { | |
PhotosPicker(selection: $viewModel.imageSelection, matching: .images, photoLibrary: .shared()) { | |
Image(systemName: "pencil.circle.fill") | |
.symbolRenderingMode(.multicolor) | |
.font(.system(size: 30)) | |
.foregroundColor(.accentColor) | |
} | |
} | |
.buttonStyle(.borderless) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
@main | |
struct MyPhotoAppApp: App { | |
var body: some Scene { | |
WindowGroup { | |
ContentView() | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct ProfileImage: View { | |
let imageState: ProfileModel.ImageState | |
var body: some View { | |
switch imageState { | |
case .empty: | |
Image(systemName: "person.fill") | |
.font(.system(size: 40)) | |
.foregroundColor(.white) | |
case .loading: | |
ProgressView() | |
case let .success(image): | |
image.resizable().scaledToFit() | |
case .failure: | |
Image(systemName: "exclamationmark.triangle.fill") | |
.font(.system(size: 40)) | |
.foregroundColor(.white) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PhotosUI | |
import SwiftUI | |
class ProfileModel: ObservableObject { | |
enum ImageState { | |
case empty, loading(Progress), success(Image), failure(Error) | |
} | |
@Published private(set) var imageState: ImageState = .empty | |
@Published var imageSelection: PhotosPickerItem? { | |
didSet { | |
if let imageSelection { | |
let progress = loadTransferable(from: imageSelection) | |
imageState = .loading(progress) | |
} else { | |
imageState = .empty | |
} | |
} | |
} | |
private func loadTransferable(from imageSelection: PhotosPickerItem) -> Progress { | |
return imageSelection.loadTransferable(type: Data.self) { result in | |
DispatchQueue.main.async { | |
guard imageSelection == self.imageSelection else { return } | |
switch result { | |
case let .success(data?): | |
guard let uiImage = UIImage(data: data) else { | |
self.imageState = .empty | |
return | |
} | |
self.imageState = .success(Image(uiImage: uiImage)) | |
case .success(.none): | |
self.imageState = .empty | |
case let .failure(error): | |
self.imageState = .failure(error) | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment