2010-04-12 1 views
53

Я хочу отклонить модуль ModS ViewCheetPresentation, когда пользователь выходит за пределы модального представления ... Я видел, как множество приложений делает это (например, ebay на ipad), но я не могу понять, как с тех пор, как нижние представления отключены от касаний, когда модальные представления отображаются так (они представляют его как поппер, возможно?) ... у кого есть предложения?Iphone SDK, отклоняющий Modal ViewControllers на ipad, щелкнув за его пределами

ответ

127

Я год поздно, но это довольно просто сделать.

Попросите модальный контроллер представления прикрепить жест распознаватель к окну отображения вида:

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 быть проклятым, это полезное и часто интуитивное поведение.

+0

У меня есть modalViewController с navigationController, и эти коды не работают. Я также попытался связать событие жестов с self.view.parentViewController.view без везения. Нужна дополнительная помощь ... –

+2

Тест для ввода кода инициализации UITapGestureRecognizer в _viewDidAppear: _ – Olof

+28

1. Это не сработало, когда я инициализировал gestureRecognizer в viewDidLoad, но работал, когда инициализировался в viewDidAppear. – Jon

11

Другие приложения не используют модальные представления, если они позволяют отклонить представление, щелкнув его. UIModalPresentationFormSheets не может быть отклонен таким образом. (и, действительно, любой UIModal в SDK3.2). Только UIPopoverController может быть уволен, щелкнув за пределами области. Очень возможно (хотя и против iPad от Apple iPad) для разработчика приложений затенять фоновый экран, а затем отобразить UIPopoverController, чтобы он выглядел как UIModalPresentationFormSheets (или другой UIModal View).

[...] UIModalPresentationCurrentContext style позволяет контроллеру представления использовать стиль презентации своего родителя. В каждом модальном представлении затемненные области отображают базовое содержимое, но не допускают кратковременного доступа к этому контенту. Поэтому, в отличие от popover, ваши модальные представления все равно должны иметь элементы управления, которые позволяют пользователю отклонять модальное представление.

Смотрите iPadProgrammingGuide на сайте разработчика для получения дополнительной информации (стр 46 - «Настройка стиль презентации для модальных Просмотров»)

+1

Apple, предлагает модальный должен содержать свои близкие элементы управления, но то противоречит тому, что с помощью touch-out-modal-to-reject в приложении iTunes. приложение GAP делает то же самое – CVertex

2

В соответствии с iOS HIG от Apple, 1. модальное представление не имеет возможности быть уволенным без какого-либо ввода на себя; 2. Используйте модальный вид в ситуации, когда требуется ввод пользователя.

8

Скопируйте и вставьте этот код в 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]; 
     } 
    } 
} 
+0

copy & paste ответить? – dwery

+1

Я намеревался, что ваш ответ тот же, комментарии в в качестве одобренного в этом Q. – dwery

+1

viewDidAppear - это мое дополнение. Вы можете голосовать. Модераторы могут видеть свою работу! – Samidjo

10

Код выше работает, но я меняю бы, если заявление,

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]; 
    } 

Это гарантирует, что вы можете взаимодействовать с навигационная панель, в противном случае нажатие на нее отклоняет модальный вид.

3

Very important: Если у вас есть другой способ закрыть ваше окно модальное всплывающее окно, не забудьте удалить распознаватель жестов!

Я забыл об этом и получил сумасшедшие сбои позже, так как распознаватель крана все еще запускал события.

8

Ответа обновлялся прошивка 8

Видимо, в прошивке 8, UIDimmingView стержневой жест распознаватель, который препятствует первоначальной реализации, поэтому мы его игнорируем и не требуем, чтобы потерпеть неудачу.


Это возраст скорости, поэтому большинство из них, вероятно, просто скопировав код выше .. Но, я страдаю от обсессивно-компульсивного расстройства, когда дело доходит до кода, к сожалению.

Here is a modular solution that uses Danilo Campos's answer with categories. Он также решает важную ошибку, которая может возникнуть, если вы отклоняете свой модальный через другие средства, as mentioned.

ПРИМЕЧАНИЕ: Операторы if есть, потому что я использую контроллер вида для iPhone и iPad, и только iPad необходимо зарегистрировать/отменить регистрацию.

UPDATE: Суть была обновлена, так как он не работает должным образом с удивительным 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]; 
} 
+0

В вашем связанном коде я бы изменил строку '[gesture locationInView: nil]' to '[gesture locationInView: view]', а затем удалит строку, которая преобразует точку. Кажется, это работает лучше в iOS8. –

+0

@ArieLitovsky Спасибо, ты абсолютно прав. Глядя на мой собственный проект, я уже сделал это изменение, но не обновил Gist, так как я начал использовать зависимости проекта, например BlocksKit. – Mazyod

11

Для прошивки 8, вы оба должны реализовать UIGestureRecognizer и поменять (х, у) координата повернутого места, когда в альбомной ориентации. Не уверен, что это связано с ошибкой 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; 
} 
+0

Ты спас мой день! – MiQUEL

+1

Я нашел проблему с этим кодом. Если в представлении есть кнопка, и отображается клавиатура, когда я нажимаю кнопку, UITapGestureRecognizer интерпретирует неправильную координату (все было перемещено при появлении клавиатуры), поэтому представление отклоняется. – MiQUEL

+1

привет, я не вижу, что обнаружил, что он находится за пределами «представленного» вида, но способен обнаруживать нажатие в «представленном» виде. – nuttynibbles

1

Использование 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]; 
    } 
} 

Modified из примера LookInside

0

Это довольно выполнимо.

Посмотрите здесь

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

Это NavigationController (модальный), который автоматически освобождает для Ipad (при нажатии снаружи)

Используйте ViewController внутри него.

Надеюсь, это поможет.

1

Это работает для меня для ios7 8 и навигационной панели.

Если вам не нужна панель навигации, просто удалите location2 и второе условие в инструкции if после труб.

@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]; 
     } 
    } 
} 

Edit: Вы можете также должны быть жест распознаватель делегат для этого и других вышеуказанных решений для работы.Делают это так:

@interface CommentTableViewController() <UIGestureRecognizerDelegate> 

установить себя в качестве делегата для распознавателя:

self.recognizer.delegate = self; 

и реализовать этот метод делегата:

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

Вы можете использовать MZFormSheetController так:

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

В Swift 2/Xcode версии 7.2 (7C68) следующий код работает для меня.

Внимание: этот код должен быть помещен в файл ViewController.swift представленного FormSheet или Лист, здесь: «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) 
      } 
     } 
    } 
} 
Смежные вопросы