关于present页面,且UIViewController中WebView的H5弹出Camera/ImagePicker大致分3种情况,严格意义上是分2种情况:
1.一个ViewController A present ViewController D
2.把ViewController B放到一个UINavigationController中管理,ViewController A present ViewController B,ViewController B present ViewController D
3.把ViewController B放到一个UINavigationController中管理,ViewController A present这个UINavigationController,B push ViewController C,C push ViewController D
ViewController D中有一个UIWebView,加载h5页面,h5页面有个按钮调用手机的相机、相册UIImagePickerController,弹出了拍照,相册选择的UIActionSheet, 然后点击相机、相册按钮,第一种问题会直接退到A页面,第二种情况会退出到B页面,第三种情况会直接退出到A页面。
最简单的解决方案就是:所有流程都采用push页面的方式,这样的话,在点击相机、相册后会回到系统相册里面,选择后会回到h5页面。
如果项目采用present页面流程已经完善,后面版本涉及到调用h5弹出Camera/ImagePicker,此时在修改弹出页面的方式为push会带来很大工作量,如何解决仍然采用present方式也能实现h5调用相机、相册呢?
问题出现的原因:
点击h5按钮,会弹出UIActionSheet框,如下图所示,
D7C2E385D6D2873598ECFB93DA5E22F3.jpg
点击UIActionSheet中item => UIActionSheet dismiss => 弹出相机或者ImagePicker => 拍照或者选择图片后弹出相机或者ImagePicker dismiss
分析下问题:
UIActionSheet有可能是被当前ViewController D执行presentViewController模态出来的。也有可能是被D的parentViewControler(或者是UINavgationControler了)模态出来的,验证猜想很简单,在ViewController D,UINavgationControler子类或者类别,重写如下方法即可。
7A674912D124F92F4C6836B246E74866.jpg
可以查看控制台上打印的viewControllerToPresent的值,发现并不是UIActionSheet,而是我们很少使用的UIDocumentMenuViewController。
一个presentedViewController要dismiss的时候会调用presentingViewController的如下方法,所以重写ViewController D中dismissViewControllerAnimated
-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
}
断点后发现,dismissViewControllerAnimated执行了两次,问题现象就是,dimiss掉了所有的modal试图,那么好了,问题找到了,UIDocumentMenuViewController关闭后不仅调用了自己的,dismissViewControllerAnimated,还调用了,上层或者上上层presentingViewController的dismissViewControllerAnimated
知道问题的原因就比较好找解决方案了:
1.使dismissViewControllerAnimated只调用一次
当前ViewController 的所有presentedViewController都正常执dismissViewControllerAnimated,当前ViewController本身执行dismissViewControllerAnimated,不进行dismiss,不做处理。
-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
if (self.presentedViewController)
{
[super dismissViewControllerAnimated:flag completion:completion];
}
}
这样当前ViewController不会调用重写的dismissViewControllerAnimated。
2.使UIDocumentMenuViewController找不到presentingViewController
断点后发调用
- (void)presentViewController:(UIViewController )viewControllerToPresent animated:(BOOL)flag
弹出UIDocumentMenuViewController之后,又调用了- (UIViewController)presentingViewController返回的presentingViewController是上一个页面。
苹果的特性:当模态出N个ViewController后,只需要dismiss任意一个,都会dismiss它之后所有的模态视图。
那就可以不让UIDocumentMenuViewController找到上层或上上层的任意presentingViewController - (UIViewController *)presentingViewController
{
if (_flag) {
return nil;
} else {
return [super presentingViewController];
}
} - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion
{
NSLog(@"viewControllerToPresent:%@",viewControllerToPresent);
if ([viewControllerToPresent isKindOfClass:[UIDocumentMenuViewController class]] || [viewControllerToPresent isKindOfClass:[UIImagePickerController class]]) {
_flag = YES;
}
[super presentViewController:viewControllerToPresent animated:flag completion:completion];
}
总之,上面2个方法可以根据自己项目的特征写在最后一个被present的ViewController里,或者UINavigationController类别里。
附demo链接:https://github.com/wangxaioli/Present
被UINavigationController管理的demo:https://github.com/wangxaioli/Push
网友评论