隐藏/显示iPhone相机虹膜/快门动画

10

我无法在我的应用程序中隐藏iPhone相机快门开启动画。

我正在使用UIImagePickerController来访问iPhone相机,并使用自己的覆盖控制器。

有没有办法删除相机启动时的初始快门(也称为Iris)动画?

谢谢。

[编辑]

对于那些想知道如何更改相机虹膜动画的人,可以在相机虹膜动画开始之前调用以下函数。

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    // Here is were I make the camera preview fit the entire screen. 
    // This might violate the "don't change the view hierarchy"-rule. 
    // So I am not sure if it is valid for App Store commitment.
    // However, the NSLogs are used to
    // figure out which subview is the actual Camera Preview which turns out 
    // to be the PLPreviewView. (uncomment to se the printouts).
    // Change it's size to fit the entire screen (and scale it accordingly
    // to avoid distorted image

    NSLog(@"WillShowViewController called...");

    NSLog(@"VC:view:subviews\n %@\n\n", [[viewController view] subviews]);

    NSLog(@"VC:view:PLCameraView:subviews\n %@\n\n", [[[[viewController view] subviews] objectAtIndex: 0] subviews]);

    NSLog(@"VC:view:PLCameraView:PLPreviewView:subviews\n %@\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 0] subviews]);
    NSLog(@"VC:view:PLCameraView:PLCropOverLay:subviews\n %@\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 1] subviews]);
    NSLog(@"VC:view:PLCameraView:UIImageView:subviews\n %@\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 2] subviews]);

}

在上述函数中,您可以使用普通的NSMutableArray语法(如objectAtIndex)遍历每个层级。
希望这可以帮助您。
问候,
Ankur

1
我也想知道这个问题的答案。一直在搜索,但没有看到任何信息。如果你找到了答案,请告诉我们! - Cory Imdieke
是的,我通过调整UIImagePickerController的视图层次结构找到了解决方案。 - Ankur
似乎下面的答案无法通过应用程序审核。 - Raptor
有些应用程序不显示快门/光圈,并且立即启动(无需等待预期的快门动画持续时间)。此外,ZBar库也不显示快门动画。因此,我认为我们可以找到更好的解决方案。 - AmineG
3
是的,有一种更好的方法可以访问iPhone相机API,我在之前的应用程序中找到了它。它使用AVCaptureSession。请查看developer.apple.com上的示例程序SquareCam。它演示了如何直接访问相机API而不显示快门动画。 - Ankur
显示剩余2条评论
7个回答

5

使用这个答案为起点,我最终解决了这个问题:

注意:这显然不符合3.3.1的规定。

  1. 在你的UIImagePickerController上监听UINavigationControllerDidShowViewControllerNotification和全局的PLCameraViewIrisAnimationDidEndNotification

  2. 遍历视图层次结构(从主UIWindow开始),寻找PLCameraView。保存该视图在主UIWindow上的索引,因为稍后会用到它。

  3. 从其superView中删除PLCameraView。如果需要,插入你自己的视图在全局索引0处。

  4. 当虹膜动画完成时,移除你的视图,并在原始索引处重新添加PLCameraView


1

发现一个类似的问题:我想在UIImagePickerController的self.cameraOverlayView中通过按钮触发拍照时让快门出现。找到了这个页面,进行了一些额外的研究,最终得出了解决方案。

摘要:

@interface MyController : UIImagePickerController
...    
- (id) init {
...
    self.cameraOverlayView = _my_overlay_;
    self.showsCameraControls = NO;
...
}
... 
- (void) onMyShutterButton {
    [self takePicture]; 
        // You want the shutter animation to happen now.
        // .. but it does not.
}

解决方案:

// Some constants for the iris view and selector
NSString* kIrisViewClassName = @"PLCameraIrisAnimationView";
SEL kIrisSelector = NSSelectorFromString(@"animateIrisOpen");

@implementation MyController {
...
    UIView* iris_;
}
- (void) viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    // Find the iris view in the siblings of your overlay view
    for (UIView* view in self.cameraOverlayView.superview.subviews) {
        if ([kIrisViewClassName isEqualToString:[[view class] description]]) {
            // It will be hidden by 'self.showsCameraControls = NO'.
            view.hidden = false;   
            // Extra precautions - as this is undocumented.  
            if ([view respondsToSelector:kIrisSelector]) {
                iris_ = view;
            }
            break;
        }
    }
}
- (void) animateIrisOpen {
    if (iris_) {
        [iris_ performSelector:kIrisSelector];
    }
}
...
- (void) onMyShutterButton {
    [self takePicture]; 
    [self animateIrisOpen];   // Voila - the shutter happens
}

0

下面的函数在相机光圈动画开始之前被调用。

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    // Here is were I make the camera preview fit the entire screen. 
    // This might violate the "don't change the view hierarchy"-rule. 
    // So I am not sure if it is valid for App Store commitment.
    // However, the NSLogs are used to
    // figure out which subview is the actual Camera Preview which turns out 
    // to be the PLPreviewView. (uncomment to se the printouts).
    // Change it's size to fit the entire screen (and scale it accordingly
    // to avoid distorted image

    NSLog(@"WillShowViewController called...");

    NSLog(@"VC:view:subviews\n %@\n\n", [[viewController view] subviews]);

    NSLog(@"VC:view:PLCameraView:subviews\n %@\n\n", [[[[viewController view] subviews] objectAtIndex: 0] subviews]);

    NSLog(@"VC:view:PLCameraView:PLPreviewView:subviews\n %@\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 0] subviews]);
    NSLog(@"VC:view:PLCameraView:PLCropOverLay:subviews\n %@\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 1] subviews]);
    NSLog(@"VC:view:PLCameraView:UIImageView:subviews\n %@\n\n", [[[[[[viewController view] subviews] objectAtIndex: 0] subviews] objectAtIndex: 2] subviews]);

}

在上述函数中,您可以使用常规的NSMuatableArray语法,如objectAtIndex,逐层遍历每个层级。

希望这能对您有所帮助。

此致敬礼,

Ankur


谢谢Harsh格式化代码。我不知道那个快捷键。 - Ankur

0
进一步阐述Catalin的答案(顺便说一句,他的回答很好),我发现如果你稍微改变"animateIrisOpen"方法,演示效果会略有提升...但是这是可以察觉到的。
- (void) animateIrisOpen {
    if (iris_) {
        iris_.hidden = NO;
        [iris_ performSelector:kIrisSelector];
    }
}

0
Joshwa的答案在虹膜动画期间完全隐藏了整个相机视图。对于我的目的,我需要相机视图可见,只是没有虹膜动画。通过对他的方法进行一些微调,我能够实现这一点。正如其他人所指出的那样,由于我们正在干扰视图层次结构并监听未记录的通知,因此这可能或可能不被允许在应用商店上使用。
需要3个ivars:
UIImagePickerController *imagePickerController;
UIView *plCameraIrisAnimationView;  // view that animates the opening/closing of the iris
UIImageView *cameraIrisImageView;  // static image of the closed iris

隐藏关闭的虹膜图像并删除动画视图。我尝试简单地隐藏动画视图,但动画仍然可见:
- (void)receivedNavigationControllerDidShowViewControllerNotification:(NSNotification *)notification {
    UIView *view = imagePickerController.view;
    [plCameraIrisAnimationView release];
    plCameraIrisAnimationView = nil;
    cameraIrisImageView = nil;
    while (view.subviews.count && (view = [view.subviews objectAtIndex:0])) {
        if ([[[view class] description] isEqualToString:@"PLCameraView"]) {
            for (UIView *subview in view.subviews) {
                if ([subview isKindOfClass:[UIImageView class]]) {
                    cameraIrisImageView = (UIImageView *)subview;
                }
                else if ([[[subview class] description] isEqualToString:@"PLCropOverlay"]) {
                    for (UIView *subsubview in subview.subviews) {
                        if ([[[subsubview class] description] isEqualToString:@"PLCameraIrisAnimationView"]) {
                            plCameraIrisAnimationView = [subsubview retain];
                        }
                    }
                }
            }
        }
    }
    cameraIrisImageView.hidden = YES;
    [plCameraIrisAnimationView removeFromSuperview];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"UINavigationControllerDidShowViewControllerNotification" object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedPLCameraViewIrisAnimationDidEndNotification:) name:@"PLCameraViewIrisAnimationDidEndNotification" object:nil];
}

当动画结束时,取消隐藏虹膜图像并重新添加动画视图:
- (void)receivedPLCameraViewIrisAnimationDidEndNotification:(NSNotification *)notification {
    cameraIrisImageView.hidden = NO;

    UIView *view = imagePickerController.view;
    while (view.subviews.count && (view = [view.subviews objectAtIndex:0])) {
        if ([[[view class] description] isEqualToString:@"PLCameraView"]) {
            for (UIView *subview in view.subviews) {
                if ([[[subview class] description] isEqualToString:@"PLCropOverlay"]) {
                    [subview insertSubview:plCameraIrisAnimationView atIndex:1];
                    [plCameraIrisAnimationView release];
                    plCameraIrisAnimationView = nil;
                    break;
                }
            }
        }
    }

    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"PLCameraViewIrisAnimationDidEndNotification" object:nil];
}

是的,它在iOS5上不起作用。我尝试将其替换为AVCaptureSessionDidStartRunningNotification和AVCaptureSessionDidStopRunningNotification,但它在iOS5上工作而在iOS4上没有相同的行为。 - AmineG

0

我已经尝试过一些方法,比如将视图生命周期的各种组合发送给图像选择器(例如viewWillAppear、viewDidAppear等),但我不记得哪些方法最终起作用了。


0

非常抱歉回复这么晚。我找到了解决方案,我在摄像头视图层次结构中尝试了一下并添加了自己的层位于所有内容的顶部。动画就在那里进行,在快门打开后,最顶层的层就被删除了。如果有人需要代码方面的进一步帮助,请告诉我,我将提供确切的步骤和语法。

-Ankur


你能否在问题中发布你的代码,因为我遇到了相同的问题,需要隐藏快门动画。 - Janak Nirmal

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