IPhone SDK rejetant Modal ViewControllers sur iPad en cliquant en dehors de celui-ci

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

  •  26-09-2019
  •  | 
  •  

Question

Je veux rejeter un contrôleur de vue modal FormSheetPresentation lorsque les robinets utilisateur en dehors de la vue modale ... J'ai vu un tas d'applications faisant cela (ebay sur iPad par exemple), mais je ne peux pas comprendre comment, depuis les vues de dessous sont personnes à mobilité réduite de touches lorsque les vues modales sont affichés comme celui-ci (sont peut-être? ils le présenter comme un popover) ... quelqu'un at-il des suggestions?

Était-ce utile?

La solution

Je suis un an de retard, mais cela est assez simple à faire.

Demandez à votre contrôleur modal vue attacher un geste de reconnaissance à la fenêtre de la vue:

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

Le code de gestion:

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

C'est à ce sujet. HIG être damnés, c'est un comportement utile et souvent intuitive.

Autres conseils

Les autres applications n'utilisent pas Modal vues si elles permettent la vue d'être rejetée en cliquant en dehors de celui-ci. UIModalPresentationFormSheets ne peut pas être rejeté cette façon. (Ni, peut en effet tout UIModal dans SDK3.2). Seul le UIPopoverController peut être rejeté en cliquant en dehors de la zone. Il est très possible (bien que contre l'iPad d'Apple HIG) pour le développeur d'applications pour avoir l'ombre sur l'écran d'arrière-plan, puis affiché la UIPopoverController pour qu'il ressemble à un UIModalPresentationFormSheets (ou autre UIModal View).

  

[...] le style UIModalPresentationCurrentContext permet à un contrôleur de vue d'adopter le style de présentation de son parent. Dans chaque vue modale, les zones grisées indiquent le contenu sous-jacent, mais ne permettent pas de robinets dans ce contenu. Par conséquent, contrairement à un popover, votre point de vue modales doivent encore avoir des contrôles qui permettent à l'utilisateur de rejeter le point de vue modal.

Voir la iPadProgrammingGuide sur le site de développeur pour plus d'informations (Page 46 - "Configuration du style de présentation pour les vues modales")

Pour iOS 8, vous devez à la fois mettre en œuvre le UIGestureRecognizer et remplacez (x, y) les coordonnées de l'emplacement taraudés lorsque l'orientation paysage. Je ne sais pas si cela est dû à un bug 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;
}

Le code ci-dessus fonctionne très bien, mais je changerais l'instruction 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];
    }

Ceci assure que vous pouvez toujours interagir avec la barre de navigation, sinon taraudage il rejette le point de vue modal.

Réponse mise à jour pour iOS 8

Apparemment, dans iOS 8, le UIDimmingView a un geste de reconnaissance du robinet, ce qui interfère avec la mise en œuvre initiale, de sorte que nous ignorons et ne nécessitent pas d'échouer.


Ceci est l'âge de la vitesse, donc la plupart sont probablement copiant le code ci-dessus .. Mais, je souffre de TOC en matière de code, malheureusement.

Voici une solution modulaire qui utilise la réponse de Danilo Campos avec des catégories . Il a également permet de résoudre un bug important qui peut se produire si vous Rejetant votre modal par d'autres moyens, comme mentionné .

NOTE:. si les déclarations sont là parce que j'utilise le contrôleur d'affichage pour iPhone et iPad, et que l'iPad doit inscrire / désinscrire

Mise à jour: L'essentiel a été mis à jour, car il ne fonctionne pas correctement avec l'impressionnant

Copier coller ce code dans votre 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: Si vous avez une autre façon de fermer votre fenêtre modale , ne pas oublier d'enlever le robinet de reconnaissance de geste!

J'ai oublié cela, et se bloque fou plus tard, puisque la reconnaissance de robinet a été mise à feu encore des événements.

Selon iOS HIG d'Apple, 1. la vue modale n'a pas cette capacité à être licenciés sans aucune intervention sur elle-même; 2. utiliser la vue modale dans la situation d'une entrée de l'utilisateur est nécessaire.

Utilisez UIPresentationController au lieu:

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

Modifié de l'exemple LookInside

Cela fonctionne pour moi pour iOS 7 une barre 8 et la navigation.

Si vous n'avez pas besoin de la barre de navigation juste supprimer location2 et deuxième condition dans l'instruction if après les tuyaux.

@MiQUEL cela devrait fonctionner pour vous aussi

- (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: Vous pouvez également besoin d'être un délégué de reconnaissance de geste pour cela et d'autres solutions ci-dessus au travail. Faites-le comme ceci:

@interface CommentTableViewController () <UIGestureRecognizerDelegate>

vous défini comme délégué pour la reconnaissance:

self.recognizer.delegate = self;

et mettre en œuvre cette méthode déléguée:

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

Il est assez faisable.

Jetez un coup d'oeil ici

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

Il est un NavigationController (modal) que l'auto pour ipad rejeter (lorsque vous appuyez sur l'extérieur)

Utilisez votre viewcontroller à l'intérieur.

it helps.

Je sais qu'il est tard, mais envisager d'utiliser CleanModal (testé avec iOS 7 et 8).

https://github.com/orafaelreis/CleanModal

Vous pouvez utiliser MZFormSheetController comme ceci:

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

Swift 2 / Xcode version 7.2 (7C68) le code suivant a fonctionné pour moi.

Attention: ce code doit être mis dans le fichier ViewController.swift de la feuille FormSheet ou page présentée, ici: "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)
            }
        }
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top