iPhone Camera, How to Avoid Cameraoverlay on Preivew View; How to Know When Entering Preview View

iPhone camera, how to avoid cameraOverlay on preivew view; How to know when entering preview view?

Found a solution. Was hard to find, but finally got it. The solution is described at UIImagePicker cameraOverlayView appears on Retake screen. Additionally I add my working code for others who have the same problem.

The use of the NSNotificationCenter with @"_UIImagePickerControllerUserDidCaptureItem" and @"_UIImagePickerControllerUserDidRejectItem" is the key!

CamViewController.h

#import <UIKit/UIKit.h>
#import "CameraViewController.h"
#import <AssetsLibrary/AssetsLibrary.h>

@interface CamViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
@property (nonatomic, strong) UIImage *image;
@property (nonatomic, strong) UIImage *lastTakenImage;
@property (nonatomic, strong) UIImagePickerController *picker;
- (IBAction)takePhoto:(id)sender;
- (IBAction)selectPhoto:(id)sender;
@end

CamViewController.h

#import "CamViewController.h"

@interface CamViewController ()

@end

@implementation CamViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}

int isAction = 0; // Photo, 1: CameraRoll, 2: Cancel

- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"_UIImagePickerControllerUserDidCaptureItem" object:nil ];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"_UIImagePickerControllerUserDidRejectItem" object:nil ];

[super viewDidLoad];
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {

UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle:@"Error"
message:@"Device has no camera"
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles: nil];

[myAlertView show];

}
isAction = 0;
[self cameraRoll];
}

-(void)handleNotification:(NSNotification *)message {
if ([[message name] isEqualToString:@"_UIImagePickerControllerUserDidCaptureItem"]) {
// Remove overlay, so that it is not available on the preview view;
self.picker.cameraOverlayView = nil;
}
if ([[message name] isEqualToString:@"_UIImagePickerControllerUserDidRejectItem"]) {
// Retake button pressed on preview. Add overlay, so that is available on the camera again
self.picker.cameraOverlayView = [self addCameraRollButton];
}
}

-(void)viewDidAppear:(BOOL)animated {
// isAction = 0: Photo, 1: CameraRoll, 2: Cancel
DLog(@"###### isAction> %d", isAction);

switch (isAction) {
case 1:
[self selectPhoto:nil];
break;
case 2:
[self dismissViewControllerAnimated:NO completion:nil];
break;
default:
[self takePhoto:nil];
break;
}
}

- (IBAction)takePhoto:(id)sender {
self.picker = [[UIImagePickerController alloc] init];
self.picker.delegate = self;
self.picker.allowsEditing = YES; // if this is NO or missing, the image the image will not be in info[UIImagePickerControllerEditedImage]
self.picker.sourceType = UIImagePickerControllerSourceTypeCamera;
self.picker.cameraOverlayView = [self addCameraRollButton];

[self presentViewController:self.picker animated:YES completion:NULL];
}

-(void)prepareCameraRoll {
isAction = 1;
[self dismissViewControllerAnimated:NO completion:nil];

}

- (IBAction)selectPhoto:(id)sender {

self.picker = [[UIImagePickerController alloc] init];
self.picker.delegate = self;
self.picker.allowsEditing = YES;
self.picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

[self presentViewController:self.picker animated:YES completion:NULL];
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

self.image = info[UIImagePickerControllerEditedImage];

isAction = 0;
[picker dismissViewControllerAnimated:YES completion:NULL];
[self performSegueWithIdentifier:@"toCameraView" sender:info];

}

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {

isAction = 2; // Cancel
[picker dismissViewControllerAnimated:YES completion:NULL];

}

# pragma mark - CameraRoll function and presentation
- (UIView *)addCameraRollButton {
float startY = ([[UIScreen mainScreen] bounds].size.height == 568.0) ? 500.0 : 410.0;

UIButton *rollButton = [UIButton buttonWithType:UIButtonTypeCustom];
rollButton.frame = CGRectMake(230.0, startY, 60.0, 60.0);
rollButton.backgroundColor = [UIColor clearColor];

[rollButton setImage:self.lastTakenImage forState:UIControlStateNormal];
rollButton.imageView.contentMode = UIViewContentModeScaleAspectFill;

[rollButton addTarget:self action:@selector(prepareCameraRoll) forControlEvents:UIControlEventTouchUpInside];

return rollButton;
}

-(void)cameraRoll {
// have to import assetlibrary framework!!!
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
[assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if (nil != group) {
// be sure to filter the group so you only get photos
[group setAssetsFilter:[ALAssetsFilter allPhotos]];

[group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *asset, NSUInteger index, BOOL *stop) {
if (asset) {
ALAssetRepresentation *repr = [asset defaultRepresentation];
// UIImage *img = [UIImage imageWithCGImage:[repr fullResolutionImage]];
UIImage *img = [UIImage imageWithCGImage:[repr fullScreenImage]];
[self setLastTakenImage:img];
*stop = YES;
}
}];
}

*stop = NO;
} failureBlock:^(NSError *error) {
NSLog(@"error: %@", error);
}];
}

#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
CameraViewController *cvc = [segue destinationViewController];

cvc.image = self.image;
DLog(@"%@, cvcimage", cvc.image);
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}

@end

Positioning a UIImagePickerController cameraOverlayView on top of the camera preview

Maybe not the best solution. Using view debugger you can inspect the view hierarchy. Now add the view into the child camera view port controller

imagePicker = CustomPicker()
imagePicker.sourceType = .camera
present(imagePicker, animated: true, completion: nil)

class CustomPicker: UIImagePickerController {
let overlay = UIView()

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let camController = children.first?.children.first?.children.first, overlay.superview == nil {
overlay.backgroundColor = UIColor(white: 1, alpha: 0.5)
camController.view.addSubview(overlay)
overlay.addPinConstraints(top: 0, left: 0, bottom: 0, right: 0)
}
}
}

extension UIView {
func addPinConstraints(top: CGFloat? = nil, left: CGFloat? = nil, bottom: CGFloat? = nil, right: CGFloat? = nil) {
guard let parent = superview else { return }
translatesAutoresizingMaskIntoConstraints = false
if let left = left {
leadingAnchor.constraint(equalTo: parent.leadingAnchor, constant: left).isActive = true
}
if let right = right {
trailingAnchor.constraint(equalTo: parent.trailingAnchor, constant: -right).isActive = true
}
if let top = top {
topAnchor.constraint(equalTo: parent.topAnchor, constant: top).isActive = true
}
if let bottom = bottom {
bottomAnchor.constraint(equalTo: parent.bottomAnchor, constant: -bottom).isActive = true
}
}
}

Screenshot of the view inspector,
here child -> child -> child is the camera view port
Sample Image

UIImagePicker cameraOverlayView appears on Retake screen

Solved by signing up to NSNotificationCenter, and listening to @"_UIImagePickerControllerUserDidCaptureItem" and @"_UIImagePickerControllerUserDidRejectItem".

camera overlay view - just for preview?

I've figured out a way for you to achieve the desired result though it's a bit... not so standard. :)

The idea is to rearrange a bit the order of the inner views in the structure UIImagePickerController uses.

OK, so we create an UIImagePickerController object, initialize it and add an overlay view to it. May I have your attention please! The UIView object (UIImageView in the example code) is hidden from the very beginning. Don't miss that. Finally we present the image picker controller as modal view controller. This code should be somewhere in your applicationDidFinishLaunching:, viewWillAppear: or similar appropriate about to launch methods.

UIImagePickerController *anImagePickerController = [UIImagePickerController new];
anImagePickerController.delegate = self;
anImagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;

UIImageView *anImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Watermark.png"]];
anImageView.frame = CGRectMake(0, 1, anImageView.image.size.width, anImageView.image.size.height);
anImageView.hidden = YES;
anImagePickerController.cameraOverlayView = anImageView;

[viewController presentModalViewController:anImagePickerController animated:NO];
[anImagePickerController release];

[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:@selector(timerFireMethod:)
userInfo:anImageView
repeats:YES];
[anImageView release];

Before the overlay view (anImageView) is released a NSTimer is created, initialized with anImageView (NSTimer userInfo property) and scheduled right away. Here's the method it calls:

- (void)timerFireMethod:(NSTimer*)theTimer {
UIView *cameraOverlayView = (UIView *)theTimer.userInfo;
UIView *previewView = cameraOverlayView.superview.superview;

if (previewView != nil) {
[cameraOverlayView removeFromSuperview];
[previewView insertSubview:cameraOverlayView atIndex:1];

cameraOverlayView.hidden = NO;

[theTimer invalidate];
}
}

The whole NSTimer thing is added to the flow to ensure that the reordering work around will happen exactly when the UIImagePickerController will be totally ready for that.

This is it. It works, it's not standard, it's rough and quick. Again feel free to optimize and make it 'righter' (oh please do, my aim was to show you the way).

How to constraint overlay to UIImagePickerController camera preview only

Actually, this may be a little annoy, there is no way to get the sizes of those bars or the camera preview area, and you can't achieve the subviews of UIImagePickerController and so you can't add constraints for that.
But, you can use the fact that the preview camera area aspect ratio is known as 4/3 always and the overlay width should be the width of the screen and the height should be the (widthScreen / 3) * 4

another way is to set imagePicker.showsCameraControls = false and build custom controls with the overlay on fullScreen.

(use the imagePicker.cameraViewTransform in order to set the camera preview area frame full screen).



Related Topics



Leave a reply



Submit