如何在iOS 7中使UIImagePicker相机的自定义覆盖层全屏显示?

16

我有兴趣使用自定义叠加层来控制整个屏幕的相机控件,非常类似于默认的相机应用程序在切换到视频功能时所做的操作。

我参考了这个问题以及苹果关于如何为UIImagePickerController使用自定义叠加层的示例等几个SO问题,但它们都无法在iOS 7中产生成功的全屏结果。

到目前为止,摄像头UI看起来是这样的(没有任何按钮):

enter image description here

请注意,屏幕底部有一条黑色条。我注意到如果将cameraAspectRatio更改为1,可以去掉黑色条,但这会扭曲相机缩放,因为它然后具有非常缩小的视图。是否有一种方式可以完全全屏,既不会过度扭曲缩放(我知道需要留下一点),也可以删除黑条?

这是配置自定义叠加层的代码:

{
        self.imagePickerController.showsCameraControls = NO;

        [[NSBundle mainBundle] loadNibNamed:@"overlayView" owner:self options:nil];
        self.overlayView.frame = self.imagePickerController.cameraOverlayView.frame;
        self.imagePickerController.cameraOverlayView = self.overlayView;
        self.overlayView = nil;

        // Device's screen size (ignoring rotation intentionally):
        CGSize screenSize = [[UIScreen mainScreen] bounds].size;


        float cameraAspectRatio = 4.0 / 3.0; //! Note: 4.0 and 4.0 works
        float imageWidth = floorf(screenSize.width * cameraAspectRatio);
        float scale = ceilf((screenSize.height / imageWidth) * 10.0) / 10.0;

        self.imagePickerController.cameraViewTransform = CGAffineTransformMakeScale(scale, scale);

    }

更新:

我发现另一个问题是,在相机预览中显示的图片总是比使用方法[self.imagePickerController takePicture]拍摄保存的图片更小,细节也较少。举个例子:

这是相机预览中键盘的样子,下面有一条黑色的条纹(抱歉,有些模糊):

enter image description here

然而,请注意实际捕获的图像在预览面板中具有更多的细节,正如这里所示,尤其是向上以及左右两侧:

enter image description here

我的问题是,如何能够设置相机预览,使用户在预览中看到的正是它将要捕获并可以在之后向他们呈现的图像?谢谢!

更新2:

以下是viewDidLoad中设置相机控件的整个代码。

- (void)viewDidLoad
{
    [super viewDidLoad];

    //Appearance configuration
    self.navigationItem.hidesBackButton = YES;

    //UIImagePickerController initialization and setup
    self.imagePickerController = [[UIImagePickerController alloc] init];
    self.imagePickerController.delegate = self;
    self.imagePickerController.allowsEditing = NO;
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
        self.imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; //if there is a camera avaliable
    } else {
        self.imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;//otherwise go to the folder
    }
    self.imagePickerController.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeImage, nil];
    if (self.imagePickerController.sourceType == UIImagePickerControllerSourceTypeCamera)
    {
        self.imagePickerController.showsCameraControls = NO;

        [[NSBundle mainBundle] loadNibNamed:@"overlayView" owner:self options:nil];
        self.overlayView.frame = self.imagePickerController.cameraOverlayView.frame;
        self.imagePickerController.cameraOverlayView = self.overlayView;
        self.overlayView = nil;

        // Device's screen size (ignoring rotation intentionally):
        CGSize screenSize = [[UIScreen mainScreen] bounds].size;

        int heightOffset = 0;

        if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
        {
            heightOffset = 120; //whole screen :)
        }

        float cameraAspectRatio = 4.0 / 3.0; //! Note: 4.0 and 4.0 works
        float imageWidth = floorf(screenSize.width * cameraAspectRatio);
        float scale = ceilf(((screenSize.height + heightOffset) / imageWidth) * 10.0) / 10.0;

        self.imagePickerController.cameraViewTransform = CGAffineTransformMakeScale(scale, scale);

    }
}

viewWillAppear方法中,我调用了图片选择器视图:

BOOL modalPresent = (BOOL)(self.presentedViewController);
    //Present the Camera UIImagePicker if no image is taken
    if (!appDelegate.imageStorageDictionary[@"picture1"]){
        if (modalPresent == NO){ //checks if the UIImagePickerController is modally active
            [self presentViewController:self.imagePickerController animated:NO completion:nil];
        }
    }

1
嘿..你能告诉我你是如何实现“拍下一张照片”功能的吗?我希望我的相机在拍摄完照片后仍然保持捕获模式。 - Smit Yash
@daspianist,你解决了这个问题吗?我也遇到了类似的问题。底部出现了一个黑色的条形。 - Khadija Daruwala
9个回答

26

经过多次尝试,这是对我有用的方法,非常感谢其他人的建议。以下事实非常有帮助并应牢记:

相机的分辨率为426 * 320。如果要将相机预览高度拉伸到568的手机屏幕高度,则在使用CGAffineTransformScale时需要乘以1.3333的因子。

请注意,以下内容是硬编码的,基于iPhone 5的点屏幕分辨率设置了各种数字。通过使用诸如screen.height、screen.width和其他变量等对象,可以改进它们使其适用于iPhone 4/4s的尺寸。

    self.imagePickerController.showsCameraControls = NO;

    [[NSBundle mainBundle] loadNibNamed:@"overlayView" owner:self options:nil];
    self.overlayView.frame = self.imagePickerController.cameraOverlayView.frame;
    self.imagePickerController.cameraOverlayView = self.overlayView;
    self.overlayView = nil;

    //For iphone 5+
    //Camera is 426 * 320. Screen height is 568.  Multiply by 1.333 in 5 inch to fill vertical
    CGAffineTransform translate = CGAffineTransformMakeTranslation(0.0, 71.0); //This slots the preview exactly in the middle of the screen by moving it down 71 points
    self.imagePickerController.cameraViewTransform = translate;

    CGAffineTransform scale = CGAffineTransformScale(translate, 1.333333, 1.333333);
    self.imagePickerController.cameraViewTransform = scale;

1
如果你拉伸它以适应高度,宽度会发生什么? - Rob
左右两侧被截断。例如,如果您打开默认相机应用程序并切换到视频,则填充屏幕的尺寸正好是您应用此代码所看到的内容。 - daspianist
@daspianist:您能否编辑此答案,使其适用于iPhone 4s尺寸? - Rashmi Ranjan mallick
我在iOS 8上使用以上方法时遇到了问题,请帮忙。 - Rashmi Ranjan mallick
1
感谢您的帖子,真的很有帮助 :) - Sandeep Khade
显示剩余10条评论

9
在Swift中

var picker: UIImagePickerController = UIImagePickerController();

picker.sourceType = UIImagePickerControllerSourceType.Camera;

picker.showsCameraControls = false;

var screenBounds: CGSize = UIScreen.mainScreen().bounds.size;

var scale = screenBounds.height / screenBounds.width;

picker.cameraViewTransform = CGAffineTransformScale(picker.cameraViewTransform, scale, scale);


我尝试了这段代码,确实使我的默认iOS相机透明了。但唯一的问题是在拍摄照片时所看到的视图比已拍摄照片的预览要放大得多。你知道如何解决这个问题吗? - Kahsn
你尝试过将比例值设为 var scale = screenBounds.width/screenBounds.height 吗? - Vikramaditya
我刚试了一下,它给了我一个非常小的相机屏幕。我听说应该把相机屏幕放大并裁剪掉不适合手机屏幕大小的其余部分,以使其像 Snapchat 相机视图。不确定应该使用什么比例和裁剪哪些部分。 - Kahsn

2

确保在iOS7中考虑到20像素状态栏的变化。如果你在屏幕底部面临着一个20像素的黑屏问题,那么这将是你的问题所在。你可以通过以下任意一个预处理器来检查应用程序是否在iOS7中运行:

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

您可以对您的代码进行以下更改,并查看是否有效

{
        self.imagePickerController.showsCameraControls = NO;

        [[NSBundle mainBundle] loadNibNamed:@"overlayView" owner:self options:nil];
        self.overlayView.frame = self.imagePickerController.cameraOverlayView.frame;
        self.imagePickerController.cameraOverlayView = self.overlayView;
        self.overlayView = nil;

        // Device's screen size (ignoring rotation intentionally):
        CGSize screenSize = [[UIScreen mainScreen] bounds].size;

        int heightOffset = 0;

        if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
        {
            heightOffset = 20;
        }

        float cameraAspectRatio = 4.0 / 3.0; //! Note: 4.0 and 4.0 works
        float imageWidth = floorf(screenSize.width * cameraAspectRatio);
        float scale = ceilf(((screenSize.height + heightOffset) / imageWidth) * 10.0) / 10.0;

        self.imagePickerController.cameraViewTransform = CGAffineTransformMakeScale(scale, scale);

    }

请告诉我它是否有效。我还没有编码测试。


1
感谢您提出高度偏移方法。在20的偏移量下,它有轻微的效果,但当我尝试了120时,它成功地覆盖了整个底部黑色条。然而,问题是使用120偏移量后,相机预览变得非常缩放,尽管它拍摄的照片仍然是缩放的。如果需要,我可以提供一些预览与实际照片的截图。 - daspianist
我在屏幕尺寸上进行了计算,大约需要放大到120才能以相机自然捕捉的宽高比填满整个屏幕...你的选择要么是裁剪拍摄的照片以匹配预览中显示的内容,要么是扭曲图像使其在iPhone5上纵向拉伸以填满屏幕,或者将图像居中以便在顶部和底部有较小的黑边。个人建议使用黑边,这在iPhone 4(或4s)上也会消失。 - Kendall Helmstetter Gelner
请查看此帖子https://dev59.com/aHfZa4cB1Zd3GeqPQVU_ - Kamran Khan
@daspianis 视图的自动布局属性已启用吗?我已经测试了这段代码,它运行良好。https://developer.apple.com/library/ios/samplecode/PhotoPicker/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010196 - Kamran Khan
1
@kamran 我已经为视图启用了自动布局。相机视图确实变得完全拉伸(这很棒-感谢您的建议),所以现在我正在努力找出应该裁剪哪部分图像,以便它看起来与相机视图中的图像预览完全相同。 - daspianist
显示剩余3条评论

1

有一种更简单的方法可以在iOS 9中使您的叠加全屏测试。

let view    = ... your overlayView
let bounds  = UIScreen.mainScreen().bounds
view.center = CGPoint(x: bounds.midX, y: bounds.midY)
view.bounds = bounds
self.imagePicker.cameraOverlayView = view

1
因为屏幕的长宽比与相机本身的4:3比例不同,所以你需要(正如你所提到的)稍微裁剪边缘,以便图像可以延伸到底部。
为了实现这个目标,你需要让图像的宽度足够宽,以便高度可以填满整个屏幕。因此,你的图像宽度和高度必须是:
float cameraAspectRatio = 4.0 / 3.0; //! Note: 4.0 and 4.0 works
float imageWidth = screenSize.height / cameraAspectRatio;

您可能还希望将呈现摄像头图像的视图宽度设置为跨越屏幕两侧的宽度。

感谢分享这个结果。不幸的是,使用它仍然会导致屏幕底部显示黑色条纹。当前叠加视图呈现相机预览图像时设置为适合整个屏幕(320 * 568)- 您的意思是这个视图应该被拉伸得比这更大吗? - daspianist
我认为是这样的,是的。可能当前视图设置为适应宽高比。 - Kendall Helmstetter Gelner
感谢您的建议。我确保了覆盖视图(来自xib)显示为缩放以填充,并将其拉伸到各种大小进行测试,包括非常大的尺寸,例如480*680。然而,我仍然在底部得到黑色条纹。 - daspianist

0

试试这段代码,对我来说运行得很好

UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
cameraUI.showsCameraControls = NO;

CGSize screenSize = [[UIScreen mainScreen] bounds].size;
float cameraAspectRatio = 4.0 / 3.0;
float imageHeight = floorf(screenSize.width * cameraAspectRatio);
float scale = screenSize.height / imageHeight;
float trans = (screenSize.height - imageHeight)/2;
CGAffineTransform translate = CGAffineTransformMakeTranslation(0.0, trans);
CGAffineTransform final = CGAffineTransformScale(translate, scale, scale);
cameraUI.cameraViewTransform = final
;
cameraUI.delegate = self;
[self presentViewController:cameraUI animated:YES completion:nil];

这段代码假设原始相机预览区域的宽高比为4:3。


0

我不知道你为什么在使用屏幕大小。试试这个简单的代码:

if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera])

{

     UIImagePickerController *controller = [[UIImagePickerController alloc] init];
     controller.sourceType = UIImagePickerControllerSourceTypeCamera;
     controller.allowsEditing = NO;
     controller.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:
     UIImagePickerControllerSourceTypeCamera];
     controller.delegate = self;
     [self presentViewController:controller animated:NO completion:^{}];
}

不幸的是,这只适用于我想使用默认的 UIImagePicker,而不适用于自定义覆盖层。 - daspianist
你使用叠加层的目的是什么? - Naeem Paracha
我想使用自定义叠加层,以便在拍摄照片时,相机视图占据整个屏幕。 - daspianist
尝试使用cameraViewTransform实现全屏。 CGFloat camScaleup = 2; imagePicker.cameraViewTransform = CGAffineTransformScale(imagePicker.cameraViewTransform, camScaleup, camScaleup); - Naeem Paracha

0
尝试使用以下代码来保持您的结果图像在自定义大小中。我通过使用自定义方法获取结果图像作为自定义大小来获得结果。
首先为UIImageview创建IBOutlet。
-(void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    NSString *mediaType = info[UIImagePickerControllerMediaType];
[self dismissViewControllerAnimated:YES completion:nil];


if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
    OriginalImage=info[UIImagePickerControllerOriginalImage];
    image = info[UIImagePickerControllerOriginalImage];
//----------------------------------------
     imageview.image = image; //------------- additional method for custom image size
     self resizeImage];


//-----------------------------------------
        if (_newMedia)
            UIImageWriteToSavedPhotosAlbum(image,self,@selector(image:finishedSavingWithError:contextInfo:),nil);

}
else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie])
{
    // Code here to support video if enabled
}

}

//----使用自定义方法调整原始图像大小----------------------

-(void)resizeImage
{
    UIImage *resizeImage = imageview.image;

    float width = 320;
    float height = 320;

    CGSize newSize = CGSizeMake(320,320);
    UIGraphicsBeginImageContextWithOptions(newSize,NO,0.0);
    CGRect rect = CGRectMake(0, 0, width, height);

    float widthRatio = resizeImage.size.width / width;
    float heightRatio = resizeImage.size.height / height;
    float divisor = widthRatio > heightRatio ? widthRatio : heightRatio;

    width = resizeImage.size.width / divisor;
    height = resizeImage.size.height / divisor;

    rect.size.width  = width;
    rect.size.height = height;

    //indent in case of width or height difference
    float offset = (width - height) / 2;
    if (offset > 0) {
        rect.origin.y = offset;
    }
    else {
        rect.origin.x = -offset;
    }

    [resizeImage drawInRect: rect];

    UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

imageview.image = smallImage;
imageview.contentMode = UIViewContentModeScaleAspectFit;

}

0
为了解决类似的问题,我从Storyboard中实例化了一个ViewController,并使用该控制器的视图作为相机叠加层。 我不知道这是否是您要寻找的,但我使用了以下代码:
UIImagePickerController *globalPicker = [[UIImagePickerController alloc] init];
globalPicker.delegate = self;
globalPicker.sourceType = UIImagePickerControllerSourceTypeCamera;controller=[self.storyboard instantiateViewControllerWithIdentifier:@"myVC"];
overlayView = controller.view;
UIView *cameraView=[[UIView alloc] initWithFrame:self.view.bounds];
[cameraView addSubview:overlayView];
[globalPicker setCameraOverlayView:cameraView];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接