Iphone SDK通过点击它外面的ipad上解除Modal ViewControllers

我想解雇一个FormSheetPresentation模态视图控制器,当用户在模态视图之外轻击…我已经看到了一堆应用程序这样做(例如在iPad的ebay),但我不知道如何,因为下面的意见是从触摸禁用当模式视图显示像这样(他们提出了一个popover也许?)…任何人有任何build议?

我迟了一年,但是这很简单。

让你的模态视图控制器将一个手势识别器附加到视图的窗口中:

UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapBehind:)]; [recognizer setNumberOfTapsRequired:1]; recognizer.cancelsTouchesInView = NO; //So the user can still interact with controls in the modal view [self.view.window addGestureRecognizer:recognizer]; [recognizer release]; 

处理代码:

 - (void)handleTapBehind:(UITapGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateEnded) { CGPoint location = [sender locationInView:nil]; //Passing nil gives us coordinates in the window //Then we convert the tap's location into the local view's coordinate system, and test to see if it's in or outside. If outside, dismiss the view. if (![self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil]) { // Remove the recognizer first so it's view.window is valid. [self.view.window removeGestureRecognizer:sender]; [self dismissModalViewControllerAnimated:YES]; } } } 

就是这样。 HIG被诅咒,这是一个有用的,往往是直觉的行为。

其他应用程序如果允许通过点击外部视图被解散,则不使用模态视图。 UIModalPresentationFormSheets不能被这种方式解雇。 (也不能,SDK3.2中的任何UIModal)。 只有UIPopoverController可以通过点击该区域以外的地方解除。 对于应用程序开发人员来说,很有可能(虽然对苹果的iPad HIG)已经UIPopoverController了背景屏幕,然后显示UIPopoverController ,使其看起来像UIModalPresentationFormSheets (或其他UIModal视图)。

UIModalPresentationCurrentContext样式允许视图控制器采用其父级的呈现样式。 在每个模式视图中,暗淡区域显示底层内容,但不允许在该内容中点击。 因此,与popup窗口不同,您的模态视图必须仍然具有允许用户忽略模态视图的控件。

有关详细信息,请参阅开发者网站上的“iPad编程指南”(第46页 – “为模态视图configuration演示风格”)

对于iOS 8,您必须实现UIGestureRecognizer ,并在水平方向交换所点击位置的(x,y)坐标。 不知道这是否是由于iOS 8的错误。

 - (void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // add gesture recognizer to window UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapBehind:)]; [recognizer setNumberOfTapsRequired:1]; recognizer.cancelsTouchesInView = NO; //So the user can still interact with controls in the modal view [self.view.window addGestureRecognizer:recognizer]; recognizer.delegate = self; } - (void)handleTapBehind:(UITapGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateEnded) { // passing nil gives us coordinates in the window CGPoint location = [sender locationInView:nil]; // swap (x,y) on iOS 8 in landscape if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) { if (UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) { location = CGPointMake(location.y, location.x); } } // convert the tap's location into the local view's coordinate system, and test to see if it's in or outside. If outside, dismiss the view. if (![self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil]) { // remove the recognizer first so it's view.window is valid [self.view.window removeGestureRecognizer:sender]; [self dismissViewControllerAnimated:YES completion:nil]; } } } #pragma mark - UIGestureRecognizer Delegate - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { return YES; } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; } - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { return YES; } 

上面的代码工作很好,但我会改变if语句,

  if (!([self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil] || [self.navigationController.view pointInside:[self.navigationController.view convertPoint:location fromView:self.navigationController.view.window] withEvent:nil])) { // Remove the recognizer first so it's view.window is valid. [self.view.window removeGestureRecognizer:sender]; [self dismissModalViewControllerAnimated:YES]; } 

这确保您仍然可以与导航栏进行交互,否则点击它将取消模态视图。

将这个代码复制到你的ModalViewController中:

 - (void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; //Code for dissmissing this viewController by clicking outside it UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapBehind:)]; [recognizer setNumberOfTapsRequired:1]; recognizer.cancelsTouchesInView = NO; //So the user can still interact with controls in the modal view [self.view.window addGestureRecognizer:recognizer]; } - (void)handleTapBehind:(UITapGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateEnded) { CGPoint location = [sender locationInView:nil]; //Passing nil gives us coordinates in the window //Then we convert the tap's location into the local view's coordinate system, and test to see if it's in or outside. If outside, dismiss the view. if (![self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil]) { // Remove the recognizer first so it's view.window is valid. [self.view.window removeGestureRecognizer:sender]; [self dismissModalViewControllerAnimated:YES]; } } } 

为iOS 8更新的答案

很明显,在iOS 8中, UIDimmingView有一个轻敲手势识别器,它干扰了最初的实现,所以我们忽略它,不要求它失败。


这是速度的年代,所以大多数可能只是复制上面的代码。但是,不幸的是,当涉及到代码时,我遭受强迫症。

这是一个模块化的解决scheme,使用Danilo Campos的答案与类别 。 它也解决了一个重要的错误,如果你是通过其他方式解散你的模态,可能会发生, 如前所述 。

注: if语句在那里,因为我使用视图控制器为iPhone和iPad,只有iPad需要注册/取消注册。

更新:要点已被更新,因为它不能与真棒FCOverlay代码正常工作,并且它不允许手势在所呈现的视图中被识别。 这些问题是固定的。 使用这个类别就像下面这样简单:

 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (self.presentingViewController) { [self registerForDismissOnTapOutside]; } } - (void)viewWillDisappear:(BOOL)animated { if (self.presentingViewController) { [self unregisterForDismissOnTapOutside]; } [super viewWillDisappear:animated]; } 

Very important:如果您有其他方式closures模式popup窗口 ,请不要忘记移除轻击手势识别器!

我忘记了这一点,并在以后发生疯狂的崩溃,因为轻拍识别器还在发射事件。

对于苹果的iOS HIG来说,1.模态视图没有这个能力,没有任何投入就可以被解散; 2.在需要用户input的情况下使用模态视图。

改用UIPresentationController:

 - (void)presentationTransitionWillBegin { [super presentationTransitionWillBegin]; UITapGestureRecognizer *dismissGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(dismissGestureTapped:)]; [self.containerView addGestureRecognizer:dismissGesture]; [[[self presentedViewController] transitionCoordinator] animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) { } completion:nil]; } - (void) dismissGestureTapped:(UITapGestureRecognizer *)sender{ if (sender.state==UIGestureRecognizerStateEnded&&!CGRectContainsPoint([self frameOfPresentedViewInContainerView], [sender locationInView:sender.view])) { [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } } 

从LookInside例子中修改

这适用于我的ios7 8和导航栏。

如果你不需要导航栏,只需在pipe道后面的if语句中删除location2和second条件即可。

@MiQUEL这应该也适合你

 - (void)handleTapBehind:(UITapGestureRecognizer *)sender { if (sender.state == UIGestureRecognizerStateEnded) { CGPoint location1 = [sender locationInView:self.view]; CGPoint location2 = [sender locationInView:self.navigationController.view]; if (!([self.view pointInside:location1 withEvent:nil] || [self.navigationController.view pointInside:location2 withEvent:nil])) { [self.view.window removeGestureRecognizer:self.recognizer]; [self dismissViewControllerAnimated:YES completion:nil]; } } } 

编辑:您可能还需要成为一个手势识别代表这个和其他上述解决scheme的工作。 这样做:

 @interface CommentTableViewController () <UIGestureRecognizerDelegate> 

设置自己作为识别器的代表:

 self.recognizer.delegate = self; 

并实现这个委托方法:

 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{ return YES; } 

这是非常可行的。

看看这里

https://stackoverflow.com/a/26016458/4074557

它是一个导航控制器(模式),自动closures的iPad(当你轻敲外面)

在里面使用你的viewcontroller。

希望能帮助到你。

我知道这是晚了,但考虑使用CleanModal(与iOS 7和8testing)。

https://github.com/orafaelreis/CleanModal

你可以像这样使用MZFormSheetController :

 MZFormSheetController *formSheet = [[MZFormSheetController alloc] initWithSize:customSize viewController:presentedViewController]; formSheet.shouldDismissOnBackgroundViewTap = YES; [presentingViewController mz_presentFormSheetController:formSheet animated:YES completionHandler:nil]; 

在Swift 2 / Xcode版本7.2(7C68)下面的代码为我工作。

注意:这段代码应该放在所呈现的FormSheet或Page Sheet的ViewController.swift文件中,在这里:“PageSheetViewController.swift”

 class PageSheetViewController: UIViewController, UIGestureRecognizerDelegate { override func viewDidAppear(animated: Bool) { let recognizer = UITapGestureRecognizer(target: self, action:Selector("handleTapBehind:")) recognizer.delegate = self recognizer.numberOfTapsRequired = 1 recognizer.cancelsTouchesInView = false self.view.window?.addGestureRecognizer(recognizer) } func gestureRecognizer(sender: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer:UIGestureRecognizer) -> Bool { return true } func handleTapBehind(sender:UIGestureRecognizer) { if(sender.state == UIGestureRecognizerState.Ended){ var location:CGPoint = sender.locationInView(nil) // detect iOS Version 8.0 or greater let Device = UIDevice.currentDevice() let iosVersion = Double(Device.systemVersion) ?? 0 let iOS8 = iosVersion >= 8 if (iOS8) { // in landscape view you will have to swap the location coordinates if(UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation)){ location = CGPointMake(location.y, location.x); } } if(!self.view.pointInside(self.view.convertPoint(location, fromView: self.view.window), withEvent: nil)){ self.view.window?.removeGestureRecognizer(sender) self.dismissViewControllerAnimated(true, completion: nil) } } } } 
Interesting Posts