I need to be able to open the camera in SwiftUI and take a photo in an app - swift

I'm working on an app in SwiftUI where I need access to the camera on the iPhone to take a picture while in the app. I followed a tutorial (https://www.youtube.com/watch?v=W60nnRFUGaI) on how to access photos on the device from imagePicker but can't find anywhere on how to access the actual camera view to take a photo or video.
Here is the code I've tried
import SwiftUI
struct ContentView: View {
#State private var showImagePicker: Bool = false
#State private var image: Image? = nil
var body: some View {
VStack {
image?.resizable()
.scaledToFit()
Button("Open Photo Library") {
self.showImagePicker = true
}.padding()
.background(Color.blue)
.foregroundColor(Color.white)
.cornerRadius(10)
}.sheet(isPresented: self.$showImagePicker) {
PhotoCaptureView(showImagePicker: self.$showImagePicker, image: self.$image)
}
}
}
import Foundation
import SwiftUI
class ImagePickerCoordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#Binding var isShown: Bool
#Binding var image: Image?
init(isShown: Binding<Bool>, image: Binding<Image?>) {
_isShown = isShown
_image = image
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
image = Image(uiImage: uiImage)
isShown = false
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
isShown = false
}
}
struct ImagePicker: UIViewControllerRepresentable {
#Binding var isShown: Bool
#Binding var image: Image?
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
}
func makeCoordinator() -> ImagePickerCoordinator {
return ImagePickerCoordinator(isShown: $isShown, image: $image)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
return picker
}
}
import SwiftUI
struct PhotoCaptureView: View {
#Binding var showImagePicker: Bool
#Binding var image: Image?
var body: some View {
ImagePicker(isShown: $showImagePicker, image: $image)
}
}
#if DEBUG
struct PhotoCaptureView_Previews: PreviewProvider {
static var previews: some View {
PhotoCaptureView(showImagePicker: .constant(false), image: .constant(Image("")))
}
}
#endif

Change this:
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
return picker
}
To this:
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
if !UIImagePickerController.isSourceTypeAvailable(.camera){
picker.sourceType = .photoLibrary
} else {
picker.sourceType = .camera
}
return picker
}
Remember you will only be able to open the camera in an actual physical device. The simulator doesn't support camera view only image picker.
Hope it works!

I basically the same code, just with some adaptations:
struct CameraView: UIViewControllerRepresentable {
#Binding var showCameraView: Bool
#Binding var pickedImage: Image
func makeCoordinator() -> CameraView.Coordinator {
Coordinator(self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<CameraView>) -> UIViewController {
let cameraViewController = UIImagePickerController()
cameraViewController.delegate = context.coordinator
cameraViewController.sourceType = .camera
cameraViewController.allowsEditing = false
return cameraViewController
}
func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<CameraView>) {
}
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: CameraView
init(_ cameraView: CameraView) {
self.parent = cameraView
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
parent.pickedImage = Image(uiImage: uiImage)
parent.showCameraView = false
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
parent.showCameraView = false
}
}
}
Then you can use this as you did with your code (:

Related

Take a picture (ID Card in this case) through a frame with rounded edges using SwiftUI

I have managed to take a correct picture of an ID Card, however, to help the user to frame it I need to do it through a frame with rounded edges exactly as shown in the image. I have tried many approaches without success. Could someone give me some guidance?
import UIKit
import SwiftUI
import AVFoundation
struct CameraView: UIViewControllerRepresentable {
#Binding var isShowing: Bool
#Binding var capturedImage: UIImage?
func makeUIViewController(context: UIViewControllerRepresentableContext<CameraView>) -> UIViewController {
let cameraView = UIImagePickerController()
cameraView.sourceType = .camera
cameraView.delegate = context.coordinator
return cameraView
}
func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<CameraView>) {
}
func makeCoordinator() -> Coordinator {
return Coordinator(isShowing: $isShowing, capturedImage: $capturedImage)
}
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#Binding var isShowing: Bool
#Binding var capturedImage: UIImage?
init(isShowing: Binding<Bool>, capturedImage: Binding<UIImage?>) {
_isShowing = isShowing
_capturedImage = capturedImage
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
capturedImage = image
isShowing = false
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
isShowing = false
}
}
}
The way to handle this is to use the UIImagePickerController.cameraOverlayView property. You assign a UIView to this which is presented over the top of the camera.
Here's a (bad looking) example, using SwiftUI View and a UIHostingController…
struct Overlay: View {
var body: some View {
VStack(spacing: 0) {
Rectangle().fill(.black.opacity(0.5))
.frame(height: 100)
HStack(spacing: 0) {
Rectangle().fill(.black.opacity(0.5))
.frame(width: 20)
Color.clear
Rectangle().fill(.black.opacity(0.5))
.frame(width: 20)
}
.frame(maxWidth: .infinity)
.frame(height: 200)
Rectangle().fill(.black.opacity(0.5))
Color.clear
.frame(height: 200)
}
}
}
and then
func makeUIViewController(context: UIViewControllerRepresentableContext<CameraView>) -> UIViewController {
let cameraView = UIImagePickerController()
cameraView.sourceType = .camera
cameraView.delegate = context.coordinator
let overlayView = UIHostingController(rootView: Overlay()).view
overlayView?.backgroundColor = .clear
overlayView?.isUserInteractionEnabled = false
overlayView?.frame = (cameraView.cameraOverlayView?.frame)!
cameraView.cameraOverlayView = overlayView
return cameraView
}
gives…
There are more details in the Apple documentation

Crop Image From Image Picker in SwiftUI

I am using UIImagePickerController to select image from camera or gallery in my SwiftUI app. I set allowsEditing to true. After setting it to true it is showing a square to select the photo like this:
I want to select image of size (e.g width = 150 and height = 150) instead of that square. Can I do that in SwiftUI. If yes, how can I achieve this?
This is my code:
import Foundation
import SwiftUI
public struct ImagePickerView: UIViewControllerRepresentable {
private let sourceType: UIImagePickerController.SourceType
private let onImagePicked: (UIImage) -> Void
#Environment(\.presentationMode) private var presentationMode
public init(sourceType: UIImagePickerController.SourceType, onImagePicked: #escaping (UIImage) -> Void) {
self.sourceType = sourceType
self.onImagePicked = onImagePicked
}
public func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = self.sourceType
picker.delegate = context.coordinator
picker.allowsEditing = true
return picker
}
public func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
public func makeCoordinator() -> Coordinator {
Coordinator(
onDismiss: { self.presentationMode.wrappedValue.dismiss() },
onImagePicked: self.onImagePicked
)
}
final public class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
private let onDismiss: () -> Void
private let onImagePicked: (UIImage) -> Void
init(onDismiss: #escaping () -> Void, onImagePicked: #escaping (UIImage) -> Void) {
self.onDismiss = onDismiss
self.onImagePicked = onImagePicked
}
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let img = info[.editedImage] as? UIImage {
self.onImagePicked(img)
} else if let image = info[.originalImage] as? UIImage {
self.onImagePicked(image)
}
self.onDismiss()
}
public func imagePickerControllerDidCancel(_: UIImagePickerController) {
self.onDismiss()
}
}
}
I had tried allows editing which gives me the square to select the photo but I need my given width and height selection of the photo. This is the desired result:

Cannot convert value of type 'UIImage.Type' to expected argument type 'UIImage'

It may be a beginner question :). Why do I get this error? "Cannot convert value of type 'UIImage.Type' to expected argument type 'UIImage'"
I don't know why! I have reviewed the code over and over again but no luck!
I am using Xcode version 13.1 and swiftui.
How can I solve this ?
import Foundation
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable{
#Binding var pickedImage: Image
#Binding var showImagePicker: Bool
#Binding var ImageData: Data
func makeCoordinator() -> ImagePicker.Coordinator{
Coordinator(parent: self)
}
func makeUIViewController(context: Context) -> UIImagePickerController{
let picker = UIImagePickerController()
picker.delegate = context.coordinator
picker.allowsEditing = true
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context){
}
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate{
var parent: ImagePicker
init( parent: ImagePicker){
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
let uiImage = info[.editedImage] as! UIImage
parent.pickedImage = Image(uiImage: uiImage)
if let mediaData = uiImage.jpegData(compressionQuality: 0.5){
parent.ImageData = mediaData
}
parent.showImagePicker = false
}
}
}
I get the error here
parent.pickedImage = Image(uiImage: uiImage)

SwiftUI UIViewControllerRepresentable, what does Coordinator(self) mean?

What does Coordinator(self) mean when making a UIViewControllerRepresentable (to use a UIKit component in SwiftUI)? I am a little confused on what the “self” represents.
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
#Binding var image: UIImage?
#Environment(\.presentationMode) var presentationMode
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
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
}
parent.presentationMode.wrappedValue.dismiss()
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
}
}
self always means "the current instance". So when you write...
struct ImagePicker: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
... self means "this ImagePicker instance".
You are saying this because of the way the initializer for Coordinator is declared:
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
}
Coordinator needs a parent in order to be initialized; you are initializing a Coordinator and you are telling it who the parent should be, namely you (i.e. self, this ImagePicker).
If these concepts give you trouble, you might need to study up on what types and instances are (object-oriented programming) and/or how Swift initializers and initialization are expressed.
Coordinator(self) is a mistake that is in unfortunately is in many examples. self represents the struct which is immediately discarded after SwiftUI update so it makes no sense of the coordinator object to hang on to it. Any function calls on self won't do anything. The proper way to use a Coordinator is shown in Apple's Fruta sample, PaymentButton.swift and in your code would look something like:
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
#Binding var image: UIImage?
#Environment(\.presentationMode) var presentationMode
func makeCoordinator() -> Coordinator {
Coordinator()
}
func makeUIViewController(context: Context) -> UIImagePickerController {
context.coordinator.imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
context.coordinator.didPickImage = { image in
self.image = image
}
}
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
lazy var imagePicker: UIImagePickerController = {
let picker = UIImagePickerController()
picker.delegate = self
return picker
}()
var didPickImage: ((Void) -> UIImage)?
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let uiImage = info[.originalImage] as? UIImage {
didPickImage?(uiImage)
}
}
}
}

Set property on ViewController in UIViewControllerRepresentable

How can I change a property on a UIViewController presented via. a UIViewControllerRepresentable ?
Sample code of how I would expect it to work, however it doesn't. How can I make it work?
(color is just a example, please don't focus on that)
class MyViewController: UIViewController {
var color: UIColor? = nil {
didSet {
guard isViewLoaded else { return }
view.layer.backgroundColor = color?.cgColor
}
}
override func viewDidLoad() {
view.layer.backgroundColor = color?.cgColor
}
}
struct MyView: UIViewControllerRepresentable {
#State private var color: UIColor?
func makeUIViewController(context: UIViewControllerRepresentableContext<MyView>) -> MyViewController {
let viewController = MyViewController()
viewController.color = color // always nil?
return viewController
}
func updateUIViewController(_ uiViewController: MyViewController,
context: UIViewControllerRepresentableContext<MyView>) {
uiViewController.color = color // always nil?
}
}
extension MyView {
func color(_ color: UIColor) -> MyView {
self.color = color // does nothing?
return self
}
}
struct ContentView: View {
var body: some View {
MyView()
.color(.magenta)
}
}
Here is possible approach (if you expect that color can be modified externally, as it is seen). Tested with Xcode 11.4 / iOS 13.4
struct MyView: UIViewControllerRepresentable {
#Binding var color: UIColor?
func makeUIViewController(context: UIViewControllerRepresentableContext<MyView>) -> MyViewController {
let viewController = MyViewController()
viewController.color = color // always nil?
return viewController
}
func updateUIViewController(_ uiViewController: MyViewController,
context: UIViewControllerRepresentableContext<MyView>) {
uiViewController.color = color // always nil?
}
}
struct ContentView: View {
#State private var color: UIColor? = .magenta
var body: some View {
MyView(color: $color)
// MyView(color: .constant(.magenta)) // alternate usage
}
}
Another solution based on idea from Asperi's answer:
struct MyView: UIViewControllerRepresentable {
private class State: ObservableObject {
var color: UIColor?
}
#Binding private var state: State
init() {
_state = .constant(State())
}
func makeUIViewController(context: UIViewControllerRepresentableContext<MyView>) -> MyViewController {
return MyViewController()
}
func updateUIViewController(_ uiViewController: MyViewController,
context: UIViewControllerRepresentableContext<MyView>) {
uiViewController.color = state.color
}
}
extension MyView {
func color(_ color: UIColor) -> MyView {
self.state.color = color
return self
}
}
Or a even simpler version, we just use #Binding for the wrapped ViewController directly
struct MyView: UIViewControllerRepresentable {
#Binding private var viewController: MyViewController
init() {
_viewController = .constant(MyViewController())
}
func makeUIViewController(context: UIViewControllerRepresentableContext<MyView>) -> MyViewController {
return viewController
}
func updateUIViewController(_ uiViewController: MyViewController,
context: UIViewControllerRepresentableContext<MyView>) {
}
}
extension MyView {
func color(_ color: UIColor) -> MyView {
self.viewController.color = color
return self
}
}