iPhone相机,如何避免相机在预览视图上叠加;如何知道何时进入预览视图?

Posted

技术标签:

【中文标题】iPhone相机,如何避免相机在预览视图上叠加;如何知道何时进入预览视图?【英文标题】:iPhone camera, how to avoid cameraOverlay on preivew view; How to know when entering preview view? 【发布时间】:2014-05-21 23:23:32 【问题描述】:

在相机工作流程中,照片被捕获并在下一个屏幕上,我们称之为选择屏幕,您可以选择是要使用这张照片还是重新拍摄。

我怎么知道相机何时进入预览视图

我的问题是我添加了一个按钮来访问相机胶卷,效果很好。障碍是,当拍照并进入预览视图(2.相机视图)时,按钮隐藏了“使用照片”选项。所以我无法选择它。我想在进入预览视图时隐藏按钮,或者只是避开预览视图

在我的代码下面

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

【问题讨论】:

使用UIImagePickerControllercameraOverlayView 属性添加您的自定义按钮。 @omz 做到了,但没有解决问题。添加的按钮仍然位于“使用照片”按钮上方。更新了我的帖子,我是如何使用 cameraOverlayView 属性的。 拍照时是否调用了您的 CameraViewController(基于您的头文件与 CamViewController 不同)?您从didFinishPickingMediaWithInfo: 方法中调用[self performSegueWithIdentifier:@"toCameraView" sender:info] 方法,该方法在您拍照时调用。所以“toCameraView”是您需要实现“使用照片”按钮的地方。 @omz CameraViewController 是在拍照后第一次调用;严格来说,当按下“使用照片”按钮时,就会调用didFinishPickingMediaWithInfo:。相机和拍照功能仅在CamViewController 可用。 “使用照片”按钮是默认按钮,它与UIImagePickerController 一起提供。我想知道为什么叠加层在两个模态视图中可用(您拍摄照片的相机,以及您使用/重拍照片的预览)。人们如何在这里有不同的叠加层...... @jeremyw 见上面的评论。 【参考方案1】:

找到了解决办法。好难找,终于找到了。该解决方案在UIImagePicker cameraOverlayView appears on Retake screen 中进行了描述。此外,我为其他有同样问题的人添加了我的工作代码。

NSNotificationCenter@"_UIImagePickerControllerUserDidCaptureItem"@"_UIImagePickerControllerUserDidRejectItem" 的使用是关键!

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

【讨论】:

@matt 很高兴听到它可以帮助你:) @jerik,使用这两个通知是否被视为私有 API?只是好奇,它是否记录在某个地方? @jerik 您是否已将您的应用程序提交到使用这些通知的 iTunes?苹果批准了你的应用吗? @Bhushan 是的,我将它提交给了苹果应用商店。这些通知的审批流程没有问题。 @jerik 感谢您提供这些信息,它对我帮助很大【参考方案2】:

在 swift 4 中以供将来参考。我使用 isHidden 是因为当用户选择重拍时我无法将其显示回来。

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
        

【讨论】:

这很干净,适用于 Swift 5/XCode 12.3。 2021 年应该有更多的赞成票。谢谢!!!【参考方案3】:

只是在这里发布以供将来的 Swift 参考,如果有人需要的话。

我也遇到了同样的问题,经过一天的研究,经过大量的跟踪和错误,我找到了一种从图像预览中删除 cameraOverlayView 的方法。

当您拍照时,“_UIImagePickerControllerUserDidCaptureItem”会触发。幸运的是,我们可以通过创建一个带有闭包的观察者来利用它。在闭包中,我们可以将 cameraOverlayView 设置为 nil,从而在进入预览之前移除您的自定义视图。

这适用于 Xcode 版本 8.1 (8B62) 和 Swift 3。

我已经添加了代码 sn-p 供您根据需要使用。

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

【讨论】:

你是我的英雄! ? 谢谢! @bluefox 我很高兴该解决方案对您有用 :)【参考方案4】:

@jerik-我看到你修改了你的问题。您可以将 UIImagePickerController 属性 showsCameraControls 设置为 no,这将消除预览屏幕 - 这也会隐藏默认控件(并允许您在不关闭相机的情况下拍摄多张照片),但由于您使用的是 cameraOverlayView,所以它无关紧要.这仅适用于媒体类型为相机的情况,您必须实现 UIImagePickerDelegate 协议的takePicture 函数才能以编程方式拍摄照片。

我还没有尝试过更改预览屏幕控件的技巧。如果您真的想保留预览屏幕,您需要在自己的takePhoto 函数中添加逻辑,以确定您是否应该调用picker.cameraOverlayView = [self addCameraRollButton](拍照)或不(预览屏幕),但我不确定如果您可以动态更改预览屏幕的 cameraOverlayView - 这似乎是 Apple 不希望您搞砸的事情。无论如何,祝你好运!

【讨论】:

这很棘手,基本上我想使用相机的默认按钮,但这意味着要有预览(如你所说)。看起来我必须上路并创建一个完整的叠加层,以及使用默认按钮。但是我不知道在哪里可以找到默认按钮,例如取消、闪光和扭转相机。

以上是关于iPhone相机,如何避免相机在预览视图上叠加;如何知道何时进入预览视图?的主要内容,如果未能解决你的问题,请参考以下文章

将图像视图定位为相机预览上的叠加图像

iPhone 截图

弧形/曲线覆盖在海拔/轴承上的 iPhone 相机

使用 Storyboard 在 UIImagePickerController 之上创建叠加视图

自定义相机视图预览层加上大小的手机问题

如何在 iOS 上拍摄全屏相机照片?