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
【问题讨论】:
使用UIImagePickerController
的cameraOverlayView
属性添加您的自定义按钮。
@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相机,如何避免相机在预览视图上叠加;如何知道何时进入预览视图?的主要内容,如果未能解决你的问题,请参考以下文章