I have a camera view where users can either take a photo with their camera (which opens with the view) or click the photo library icon and select an image from their library. If they take the photo right then, I am able to bind capturedImage
to UploadPostView, but I cannot figure out how to do the same if they choose a photo from their library. I'm creating something similar to Instagram stories or Snapchat where after you take/select the photo you are able to edit it (UploadPostView) before posting.
Binding the variable in the same way I do for capturedImage does not work. I am not super familiar but I figured it was because I was binding the actual ImagePicker class. but when I try to bind ImagePicker.image or ImagePicker.imageSelection... it also doesn't do anything. Thank you!!
CustomCameraView (where the user takes the photo or selects it from their library)
import SwiftUI
import PhotosUI
struct CustomCameraView: View {
let cameraService = CameraService()
@Environment(\.dismiss) private var dismiss
@StateObject var imagePicker = ImagePicker()
@Binding var capturedImage: UIImage?
var body: some View {
//if photo taken
if (imagePicker.image != nil) || (capturedImage != nil) {
UploadPostView(capturedImage: $capturedImage)
}
//if photo not taken yet
else {
ZStack (alignment: .topLeading) {
CameraView(cameraService: cameraService) { result in
switch result {
case .success(let photo):
if let data = photo.fileDataRepresentation() {
capturedImage = UIImage(data: data)
} else {
print("Error: no image data found")
}
case .failure(let err):
print(err.localizedDescription)
}
}
VStack (alignment: .leading) {
Button {
dismiss()
} label: {
Image("xmark")
.renderingMode(.template)
.resizable()
.frame(width: 28, height: 28)
.foregroundColor(.white)
}
.padding()
Spacer()
HStack {
PhotosPicker(selection: $imagePicker.imageSelection) {
Image("image-square")
.renderingMode(.template)
.resizable()
.frame(width: 32, height: 28)
.foregroundColor(.white)
}
Spacer()
Button {
cameraService.capturePhoto()
} label: {
Image(systemName: "circle")
.font(.system(size: 72))
.foregroundColor(.white)
}
Spacer()
Rectangle()
.foregroundColor(.clear)
.frame(width: 32, height: 28)
}
.padding()
}
}
.cornerRadius(6)
.background(.black)
}
}
}
ImagePicker
import SwiftUI
import PhotosUI
@MainActor
class ImagePicker: ObservableObject {
@Published var image: Image?
@Published var uiImage: UIImage?
@Published var imageSelection: PhotosPickerItem? {
didSet {
if let imageSelection {
Task {
try await loadTransferable(from: imageSelection)
}
}
}
}
func loadTransferable(from imageSelection: PhotosPickerItem?) async throws {
do {
if let data = try await imageSelection?.loadTransferable(type: Data.self) {
if let uiImage = UIImage(data: data) {
self.uiImage = uiImage
self.image = Image(uiImage: uiImage)
}
}
} catch {
print(error.localizedDescription)
image = nil
}
}
}
UploadPostView (where users can edit their photo before uploading)
import SwiftUI
import Kingfisher
import PhotosUI
struct UploadPostView: View {
@Environment(\.dismiss) private var dismiss
@ObservedObject var viewModel = UploadPostViewModel()
@State var caption = ""
@State var rating = 0
@StateObject var imagePicker = ImagePicker()
@Binding var capturedImage: UIImage?
var body: some View {
VStack {
if let image = imagePicker.image {
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 100, maxHeight: .infinity)
.cornerRadius(6)
.clipped()
}
else {
if let image = capturedImage {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 100, maxHeight: .infinity)
.cornerRadius(6)
.clipped()
}
}
HStack {
Spacer()
Button {
if let uiimage = imagePicker.uiImage {
viewModel.uploadPost(caption: caption, image: uiimage, rating: rating)
viewModel.loading = true
} else if let uiimage = capturedImage {
viewModel.uploadPost(caption: caption, image: uiimage, rating: rating)
viewModel.loading = true
}
} label: {
if viewModel.loading {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .white))
.frame(width: 24, height: 24)
.padding()
.background(Color.accentColor)
.foregroundColor(.white)
.clipShape(Circle())
} else {
Image("send-fill")
.renderingMode(.template)
.resizable()
.frame(width: 24, height: 24)
.padding()
.background(Color.accentColor)
.foregroundColor(.white)
.clipShape(Circle())
}
}
}.padding(8)
}
.background(.black)
.onReceive(viewModel.$didUploadPost) { success in
if success {
dismiss()
}
}
}
}