SwiftUI AspectRatio does not work for images from camera - swift

I'm not sure if this a SwiftUI specific issue. Anyway, I'm have an UIImagePickerController that I implemented into a SwiftUI view by using the UIViewControllerRepresentableProtocol:
struct ContentView: View {
#State var showCameraView = false
#State var showImagePicker = false
#State var UserImage = Image("user")
var body: some View {
VStack {
UserImage
.resizable()
.frame(width: 200, height: 200)
.scaledToFit()
.background(Color.gray)
.cornerRadius(200)
.clipped()
Button(action: {self.showImagePicker = true}) {
Text("Choose from camera roll")
}
.padding(.top, 10)
}
.sheet(isPresented: $showImagePicker) {
ImagePicker(showImagePicker: self.$showImagePicker, pickedImage: self.$UserImage)
}
}
}
struct ImagePicker: UIViewControllerRepresentable {
#Binding var showImagePicker: Bool
#Binding var pickedImage: Image
func makeCoordinator() -> ImagePicker.Coordinator {
Coordinator(self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let imagePicker = UIImagePickerController()
imagePicker.delegate = context.coordinator
return imagePicker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
return
}
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: ImagePicker
init(_ imagePicker: ImagePicker) {
self.parent = imagePicker
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
parent.pickedImage = Image(uiImage: uiImage)
parent.showImagePicker = false
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
parent.showImagePicker = false
}
}
}
It works fine when picking images that are not taken by the device's camera. However, every time I pick an Image that was taken by the camera itself, it seems that the .aspectRatio modifier does not get applied because the loaded Image's dimensions are distorted in this case. Does anybody spot something wrong in my code or know a solution?

Had the same issue. I used a modified version of the one proposed by #ninjahamster where I simply didn't resize the image. Any other solution is welcome.
func resizeImage(image: UIImage) -> UIImage {
let width = image.size.width
let height = image.size.height
UIGraphicsBeginImageContext(CGSize(width: width, height: height))
image.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}

I've had the same issue. I didn't find a solution, but a workaround.
I resized the image with a function in the coordinator class, bevor I return it to the view. Works for me.
func resizeImage(image: UIImage) -> UIImage {
let scale = 300 / image.size.width
let newHeight = image.size.height * scale
UIGraphicsBeginImageContext(CGSize(width: 300, height: newHeight))
image.draw(in: CGRect(x: 0, y: 0, width: 300, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}

I met the same issue. I didn't realize it is a lacking from SwiftUI!! Now I have applied ninjahamster's workaround.
My code here, in case anyone need some reference. See the changes in func didFinishPickingMediaWithInfo.
import SwiftUI
final class ImagePickerCoordinator: NSObject {
#Binding var image: UIImage?
#Binding var takePhoto: Bool
init(image: Binding<UIImage?>, takePhoto: Binding<Bool>) {
_image = image
_takePhoto = takePhoto
}
}
struct ImagePicker: UIViewControllerRepresentable {
#Binding var image: UIImage?
#Binding var takePhoto: Bool
func makeCoordinator() -> ImagePickerCoordinator {
ImagePickerCoordinator(image: $image, takePhoto: $takePhoto)
}
func makeUIViewController(context: Context) -> UIImagePickerController {
let pickerController = UIImagePickerController()
pickerController.delegate = context.coordinator
return pickerController
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
switch self.takePhoto {
case true:
uiViewController.sourceType = .camera
uiViewController.showsCameraControls = true
case false:
uiViewController.sourceType = .photoLibrary
}
uiViewController.allowsEditing = false
}
}
extension ImagePickerCoordinator: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let imageOriginal = info[.originalImage] as? UIImage {
// image = imageOriginal
image = resizeImage(image: imageOriginal)
}
if let imageEdited = info[.editedImage] as? UIImage {
image = imageEdited
}
picker.dismiss(animated: true, completion: nil)
}
func resizeImage(image: UIImage) -> UIImage {
let width = image.size.width
let height = image.size.height
UIGraphicsBeginImageContext(CGSize(width: width, height: height))
image.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}

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)

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

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 (:

how to nest a label with a imageview in swift 3

The following code can take a picture and save it to the photo gallery. This code also features a label that goes over the imageview but its just goes over the top of the image and is not saved on the image. How can I nest the label so that when the photo is saved the text and the image are both in the final photo in the photo gallery.
import UIKit
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var image: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func camera(_ sender: UIButton) {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera){
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.camera
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true, completion: nil)
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
label.center = CGPoint(x: 160, y: 222)
label.textAlignment = .center
label.text = "I'am a test label"
self.view.addSubview(label)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) {
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage
{
image.contentMode = .scaleToFill
image.image = pickedImage
}
picker.dismiss(animated: true, completion: nil)
}
#IBAction func save(_ sender: UIButton) {
let imageData = UIImagePNGRepresentation(image.image!)
let compressedImage = UIImage(data: imageData!)
UIImageWriteToSavedPhotosAlbum(compressedImage!, nil, nil, nil)
}}
You should draw the image with text like this:
let str = "I'am a test label" as NSString
let rect = CGRect(origin: CGPoint.zero, size: image.image!.size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
image.image!.draw(in: rect)
//str.draw(in: <#The Rect You want#>, withAttributes: <#Specify The Font And Color Or Other Property#>)
str.draw(in: CGRect(origin: CGPoint.zero, size: CGSize(width: 200, height: 40)), withAttributes: [NSFontAttributeName:UIFont.systemFont(ofSize: 20), NSForegroundColorAttributeName:UIColor.green])
let img = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let imageData = UIImagePNGRepresentation(img)
let compressedImage = UIImage(data: imageData!)
UIImageWriteToSavedPhotosAlbum(compressedImage!, nil, nil, nil)