IPhone SDK увольняет модальные просмотра контроля на iPad, нажав за его пределами

StackOverflow https://stackoverflow.com/questions/2623417

  •  26-09-2019
  •  | 
  •  

Вопрос

Я хочу уволить контроллер модального представления FORMSHETSECTPRESENTATIONATATION, когда пользовательские нажатия за пределами модального вида ... Я видел кучу приложений, выполняющих это (например, eBay на iPad), но я не могу выяснить, как с момента прикосновения. Когда модальные представления отображаются так (они представляют его как поповерку?) ... У кого-нибудь есть какие-либо предложения?

Это было полезно?

Решение

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

У вас есть контроллер модального просмотра прикрепите распознатель жест в окно «Вид»:

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

Вот об этом. Хиг будет проклят, это полезное и часто интуитивное поведение.

Другие советы

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

...] UimodalPresentationCeRRentContext Style позволяет контроллеру представления принять стиль презентации своего родителя. В каждом модальном представлении туммедные зоны показывают базовый контент, но не позволяют меткам в этом содержании. Следовательно, в отличие от популяции, ваши модальные представления все еще должны иметь элементы управления, которые позволяют пользователю уволить модальный вид.

Смотрите iPadProgrammingGuide на сайт разработчика для получения дополнительной информации (стр. 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 (!([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 Имеет распознатель жест крана, который препятствует первоначальному осуществлению, поэтому мы игнорируем его и не требуем отказа от нее.


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

Вот модульное решение, которое использует ответ 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];
        }
    }
}

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

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

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

Вместо этого используйте 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 AN 8 и навигационного бара.

Если вам не нужна панель NAV, просто удалите местоположение2 и второе условие в утверждении 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];
        }
    }
}

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

@interface CommentTableViewController () <UIGestureRecognizerDelegate>

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

self.recognizer.delegate = self;

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

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

Это довольно сделано.

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

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

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

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

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

Я знаю, что поздно, но рассмотреть возможность использования Cleanmodal (проверено с iOS 7 и 8).

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) Для меня работал следующий код.

Внимание: этот код должен быть помещен в файл 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)
            }
        }
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top