我想在用户点击模态视图时解雇FormSheetPresentation模式视图控制器...我已经看到一堆应用程序这样做(例如在ipad上的ebay)但我无法弄清楚如何从触摸中禁用底层视图当模态视图像这样显示时(他们是否可能将它呈现为一个弹出窗口?)......任何人都有任何建议吗?
我迟到了一年,但这很简单.
让您的模态视图控制器将手势识别器附加到视图的窗口:
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
点击该区域外即可解散.应用程序开发人员很可能(虽然针对Apple的iPad HIG)遮住了背景屏幕,然后显示UIPopoverController
它看起来像UIModalPresentationFormSheets
(或其他UIModal View).
[...] UIModalPresentationCurrentContext样式允许视图控制器采用其父级的表示样式.在每个模态视图中,灰色区域显示基础内容,但不允许在该内容中使用水龙头.因此,与弹出窗口不同,您的模态视图仍必须具有允许用户关闭模态视图的控件.
有关详细信息,请参阅开发人员站点上的iPad程序指南(页46 - "为模态视图配置演示样式")
对于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]; }
这样可以确保您仍然可以与导航栏进行交互,否则点击它会解除模态视图.
为iOS 8更新了答案
显然,在iOS 8中,它UIDimmingView
有一个轻敲手势识别器,它会干扰初始实现,因此我们忽略它并且不要求它失败.
这是速度的时代,所以大多数可能只是复制上面的代码.但是,不幸的是,当涉及到代码时,我遭受强迫症.
这是一个模块化解决方案,使用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]; }
将此代码复制粘贴到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]; } } }