When you take a picture using the UIViewControllerRepresentable the user has to confirm the image taken by pressing "Use Photo" or "Retake" photo. I've been trying to get rid of this feature by using a third party library called JPSVolumeButtonHandler to detect volume button presses and calling takePicture() whenever the user presses one of the volume buttons, because apparently when takePicture() is called directly there is no confirmation option, and I don't want to use any gui buttons to take a picture. I think the normal event handler for this interferes with the JPS library and my solution to this was opening the imagepicker when the user presses one of the volume buttons and then calling takePicture() automatically after a few seconds.
I might have to try and use AVFoundation if nothing else works
How can we call the takePicture() method from the ContentView struct when the user presses the volume buttons?
I know we can do something like this to make the program take a picture automatically when we start the program. Calling startCaptureTimer from makeUIViewController
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var parent: ImagePicker
private var timer: Timer? // add Timer
init(parent: ImagePicker) {
self.parent = parent
}
// add this function to start the Timer which calls takePicture
--> func startCaptureTimer(for picker: UIImagePickerController) {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: 4, repeats: false, block: { timer in
picker.takePicture()
})
}
//Coordinator continued...
}
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
picker.sourceType = .camera
picker.allowsEditing = false
picker.showsCameraControls = false // hide default controls
--> context.coordinator.startCaptureTimer(for: picker) // start capture timer
return picker
}
But how can we call the method from ContentView so that when the library detects a volume button press, it takes a picture?
import SwiftUI
import UIKit
import JPSVolumeButtonHandler
struct ContentView: View {
@State private var isImagePickerPresented = false
@State private var capturedImage: UIImage?
@State private var isCameraAuthorized = false
@State private var volumeHandler: JPSVolumeButtonHandler?
var body: some View {
VStack {
Button("Take Photo") {
isImagePickerPresented.toggle()
}
.sheet(isPresented: $isImagePickerPresented, onDismiss: {
}) {
ImagePicker(image: $capturedImage, onImageCapture: { imageData in
if let imageData = imageData {
uploadImage(imageData: imageData)
}
})
}
.onAppear {
volumeHandler = JPSVolumeButtonHandler(up: {
}, downBlock: {
--->
})
volumeHandler?.start(true)
isImagePickerPresented.toggle()
}
Button(action: {
if let imageData = capturedImage?.jpegData(compressionQuality: 0.5) {
uploadImage(imageData: imageData)
}
}) {
VStack {
Text("Cool")
.font(.system(size: 70))
.padding(69)
.overlay(
RoundedRectangle(cornerRadius: 15)
.stroke(Color.black, lineWidth: 8)
)
}
.contentShape(Rectangle())
}
.background(Color.red)
.foregroundColor(.white)
.cornerRadius(15)
}
.background(
Image("gigachad")
.scaledToFit()
.edgesIgnoringSafeArea(.all)
)
}
}
@main
struct BacknForth: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Here's the rest/entire imagepicker code
import SwiftUI
import UIKit
import JPSVolumeButtonHandler
struct ImagePicker: UIViewControllerRepresentable {
@Binding var image: UIImage?
var onImageCapture: ((Data?) -> Void)
@Environment(\.presentationMode) var presentationMode
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
picker.sourceType = .camera
picker.allowsEditing = false
picker.showCameraControls = false
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var parent: ImagePicker
init(parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let uiImage = info[.originalImage] as? UIImage {
parent.image = uiImage
let imageData = uiImage.jpegData(compressionQuality: 0.5)
parent.onImageCapture(imageData)
}
parent.presentationMode.wrappedValue.dismiss() // Dismiss the ImagePicker
}
}
}
Creating an object in .onAppear out of UIImagePickerController and then trying to access takePicture() doesn't work because nothing happens when you run it. I'm assuming because it's not the same instance. I've heard that you can create like a custom view and then make your own ui elements that calls takePicture(), but I only want to use the volume buttons and I don't know if that solves it. Also perhaps @Binding could be used for this but I'm unsure.