Reputation: 8718
I know this is possible, saw this in some apps (iGotYa is I believe the most famous). I know how to set up everything for taking photos, saving it and everything. But how can it be done programmatically? just having the user click some button (in the regular view controller) and it will automatically take a picture using the front camera and saving it (or not, just getting it as a UIImage)
Thanks!
Upvotes: 29
Views: 55648
Reputation: 1610
Here's a C# implementation for the same task, for Xamarin iOS.
public void TakePhoto(Action<byte[]> getImageBytesAction)
{
var picker = new UIImagePickerController();
picker.PrefersStatusBarHidden();
picker.SourceType = UIImagePickerControllerSourceType.Camera;
picker.CameraDevice = UIImagePickerControllerCameraDevice.Rear;
picker.ShowsCameraControls = false;
picker.CameraCaptureMode = UIImagePickerControllerCameraCaptureMode.Photo;
picker.CameraFlashMode = UIImagePickerControllerCameraFlashMode.Off;
picker.FinishedPickingMedia += (object sender, UIImagePickerMediaPickedEventArgs e) =>
{
var photo = e.OriginalImage;
picker.DismissModalViewController(false);
//var imageSource = ImageSource.FromStream(() => photo.AsJPEG().AsStream());
//invoke the action when finished taking picture
var correctedImage = new UIImage(photo.CGImage, 2.0f, UIImageOrientation.Up); //scaling 2.0 makes image half
getImageBytesAction.Invoke(correctedImage.AsJPEG().ToArray());
};
//open photo picker with TakePicture trigger on load completion (added a delay to let the camera adjust light and focus)
GetTopViewController().PresentViewController(picker, false, async () => { await Task.Delay(1000); picker.TakePicture(); });
}
Upvotes: 0
Reputation: 3288
Swift 5.2
For reference https://gist.github.com/hadanischal/33054429b18287c12ed4f4b8d45a1701
Info.plist
<key>NSCameraUsageDescription</key>
<string>Access camera</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Access PhotoLibrary</string>
AVFoundationHelper
import AVFoundation
enum CameraStatus {
case notDetermined
case restricted
case denied
case authorized
}
protocol AVFoundationHelperProtocol: AnyObject {
// MARK: - Check and Respond to Camera Authorization Status
var authorizationStatus: CameraStatus { get }
// MARK: - Request Camera Permission
func requestAccess(completionHandler handler: @escaping (Bool) -> Void)
}
final class AVFoundationHelper: AVFoundationHelperProtocol {
// MARK: - Check and Respond to Camera Authorization Status
var authorizationStatus: CameraStatus {
let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)
switch cameraAuthorizationStatus {
case .notDetermined:
return CameraStatus.notDetermined
case .authorized:
return CameraStatus.authorized
case .restricted:
return CameraStatus.restricted
case .denied:
return CameraStatus.denied
@unknown default:
return CameraStatus.notDetermined
}
}
// MARK: - Request Camera Permission
func requestAccess(completionHandler handler: @escaping (Bool) -> Void) {
AVCaptureDevice.requestAccess(for: .video, completionHandler: { accessGranted in
handler(accessGranted)
})
}
}
ViewController
import UIKit
final class ViewController: UIViewController {
@IBOutlet var cameraAccessButton: UIButton!
@IBOutlet var photoImageView: UIImageView!
private var model: AVFoundationHelperProtocol = AVFoundationHelper()
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func cameraButtonPressed(_: Any) {
let status = model.authorizationStatus
switch status {
case .notDetermined:
model.requestAccess { hasAccess in
if hasAccess {
DispatchQueue.main.async {
self.showCameraReader()
}
} else {
self.alertCameraAccessNeeded()
}
}
case .restricted, .denied:
alertCameraAccessNeeded()
case .authorized:
showCameraReader()
}
}
private func alertCameraAccessNeeded() {
let appName = "This app Name"
let alert = UIAlertController(title: "This feature requires Camera Access",
message: "In iPhone settings, tap \(appName) and turn on Camera access",
preferredStyle: UIAlertController.Style.alert)
let actionSettings = UIAlertAction(title: "Settings", style: .default, handler: { _ -> Void in
guard let settingsAppURL = URL(string: UIApplication.openSettingsURLString) else { return }
UIApplication.shared.open(settingsAppURL)
})
let actionCancel = UIAlertAction(title: "Cancel", style: .destructive, handler: { _ -> Void in
})
alert.addAction(actionSettings)
alert.addAction(actionCancel)
present(alert, animated: true, completion: nil)
}
}
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
private func showCameraReader() {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .camera
imagePicker.allowsEditing = true
imagePicker.delegate = self
present(imagePicker, animated: true)
} else if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .photoLibrary
imagePicker.allowsEditing = true
imagePicker.delegate = self
present(imagePicker, animated: true)
} else {
// TODO: Implement proper alert
alertCameraAccessNeeded()
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
picker.dismiss(animated: true)
guard let image = info[.editedImage] as? UIImage else {
print("No image found")
return
}
photoImageView.image = image
// print out the image size as a test
print(image.size)
}
}
Objective C
In .h file
@interface ABCViewController : UIViewController
@property (strong, nonatomic) IBOutlet UIImageView *imageView;
- (IBAction)takePhoto: (UIButton *)sender;
- (IBAction)selectPhoto:(UIButton *)sender;
@end
In .m file
@interface ABCViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
- (IBAction)takePhoto:(UIButton *)sender {
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"Device has no camera"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles: nil];
[myAlertView show];
} else {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:picker animated:YES completion:NULL];
}
}
- (IBAction)selectPhoto:(UIButton *)sender {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:picker animated:YES completion:NULL];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *chosenImage = info[UIImagePickerControllerEditedImage];
self.imageView.image = chosenImage;
[picker dismissViewControllerAnimated:YES completion:NULL];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissViewControllerAnimated:YES completion:NULL];
}
Upvotes: -1
Reputation: 371
Here is the code for Objective -C Custom Camera. You can add features, buttons according to your wish.
#import "CustomCameraVC.h"
@interface CustomCameraVC () {
BOOL frontCamera;
}
@property (strong,nonatomic) AVCaptureSession *captureSession;
@property (strong,nonatomic) AVCaptureStillImageOutput *stillImageOutput;
@property (strong,nonatomic) AVCaptureVideoPreviewLayer *videoPreviewLayer;
@property (weak, nonatomic) IBOutlet UIView *viewCamera;
@end
@implementation CustomCameraVC
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:YES];
frontCamera = NO;
[self showCameraWithFrontCamera:frontCamera];
}
-(void)showCameraWithFrontCamera:(BOOL)flag {
self.captureSession = [[AVCaptureSession alloc]init];
self.captureSession.sessionPreset = AVCaptureSessionPresetPhoto;
AVCaptureDevice *captureDevice;
if(flag) {
captureDevice= [self frontCamera];
}
else {
captureDevice= [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
}
NSError *error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
[self.captureSession addInput:input];
self.stillImageOutput = [AVCaptureStillImageOutput new];
self.stillImageOutput.outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};
[self.captureSession addOutput:_stillImageOutput];
self.videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
self.videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.videoPreviewLayer.connection.videoOrientation = AVCaptureVideoOrientationPortrait;
[self.viewCamera.layer addSublayer:self.videoPreviewLayer];
[self.captureSession startRunning];
self.videoPreviewLayer.frame = self.viewCamera.bounds;
}
- (AVCaptureDevice *)frontCamera {
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices) {
if ([device position] == AVCaptureDevicePositionFront) {
return device;
}
}
return nil;
}
- (IBAction)btnCaptureImagePressed:(id)sender {
AVCaptureConnection * videoConnection = [_stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
[_stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef _Nullable sampleBuffer, NSError * _Nullable error) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:sampleBuffer];
UIImage *image = [[UIImage alloc]initWithData: imageData];
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
}];
}
@end
Upvotes: 2
Reputation: 2301
You can also do it without AVFoundation and it is in my opinion an easier way to implement it using only the UIImagePickerController. There are 3 conditions:
Below is a simple example that you woul typically trigger after a button push
- (IBAction)takePhoto:(id)sender
{
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
picker.showsCameraControls = NO;
[self presentViewController:picker animated:YES
completion:^ {
[picker takePicture];
}];
}
Upvotes: 15
Reputation: 27383
VLBCameraView is a library that uses AVFoundation to take photo.
A preview is shown in the view, which you can then call the method VLBCameraView#takePicture programmatically to take a photo.
Comes with CocoaPods.
Upvotes: 2
Reputation: 13459
This is very simple, just use the AVFoundation reference guide:
If you don't want the user to see the preview input you can just skip the set preview layer part of the code.
Edit: To be more detailed.
1)You set your capture configuration using the AVFoundation.
2)You SKIP the part where the video preview layer is set.
3)You call the captureStillImageAsynchronouslyFromConnection:completionHandler: method whenever you want the picture to be taken.
Note: If you want the flash to not be heard and such then you might be violating the user rights in some countries (japan for example). One workaround I know of to do so is by capturing a frame of a video (does not trigger flash).
Upvotes: 25