iPhone camera, how to avoid cameraOverlay on preiv

2020-01-29 06:00发布

问题:

On the camera workflow, the photo is captured and on the next screen, let's call it choose-screen, you can choose if you want to use this photo or retake it.

How do I know, when the camera enteres the preview view?

My issue is that I have added a button to access the camera roll, which works fine. The obstacle is, when taking a photo and entering the preview view (2. Camera View), the button hides the "use photo" option. So I cannot select it. I want to hide the button when entering the preview view or just avoid the preview view.

Below my code

CamViewScreen.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;
- (IBAction)takePhoto:(id)sender;
- (IBAction)selectPhoto:(id)sender;
@end

CamViewScreen.m

#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
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    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)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 {
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    picker.allowsEditing = NO;
    picker.sourceType = UIImagePickerControllerSourceTypeCamera;
    picker.cameraOverlayView = [self addCameraRollButton];  // suggestion from omz

    // [self addCameraRollButton:picker.view];

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

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

}

- (IBAction)selectPhoto:(id)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.image = chosenImage;

    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 - for the cameraOverlayView // suggestion from omz
- (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;
}

# pragma mark - CameraRoll function and presentation
- (void)addCameraRollButton:(UIView *)picker {
    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];

    [picker addSubview:rollButton];
}

-(void)cameraRoll {
    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);
}
@end

回答1:

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


回答2:

@jerik-I saw you amended your question. You can set the UIImagePickerController property showsCameraControls to no, which will eliminate the preview screen- this hides the default controls as well (and lets you take multiple pictures without dismissing the camera), but since you are using cameraOverlayView, it's immaterial. This only works if the media type is camera, and you will have to implement the takePicture function of the UIImagePickerDelegate protocol to take the picture programatically.

I haven't tried the hack of changing the controls of the preview screen. If you really want to keep the preview screen, you need to add logic to your own takePhoto function that determines if you should call picker.cameraOverlayView = [self addCameraRollButton] (taking a picture) or not (the preview screen), but I'm not sure if you can change the cameraOverlayView for the preview screen dynamically - it seems like something Apple wouldn't want you messing with. Anyway, good luck!



回答3:

Just posting here for future Swift references, if anyone needs it.

I've been having the same issue, and after a day of research, with a lot of trail and error, I found a way that removes the cameraOverlayView from the image preview.

When you take a photo the "_UIImagePickerControllerUserDidCaptureItem" triggers. Luckily we can utilize this by creating an observer with a closure. Inside the closure we can set the cameraOverlayView to nil, and thereby removing your custom view before entering preview.

This works in Xcode Version 8.1 (8B62), with Swift 3.

I have added the code snippet for you to utilize if needed.

NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "_UIImagePickerControllerUserDidCaptureItem"), object:nil, queue:nil, using: { note in
    self.imagePicker.cameraOverlayView = nil
)}


回答4:

In swift 4 for future references. I'm using isHidden because I had problems to show it back when the user select Retake.

NotificationCenter.default.addObserver(forName: Notification.Name("_UIImagePickerControllerUserDidCaptureItem"),
                                               object: nil,
                                               queue: nil)
        {
            _ in

            overlayView.isHidden = true
            overlayView.isUserInteractionEnabled = false
        }

        NotificationCenter.default.addObserver(forName: Notification.Name("_UIImagePickerControllerUserDidRejectItem"),
                                               object: nil,
                                               queue: nil)
        {
            _ in
            overlayView.isHidden = false
            overlayView.isUserInteractionEnabled = true
        }