Iphone SDK che respinge modale ViewControllers su iPad facendo clic all'esterno di essa

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

  •  26-09-2019
  •  | 
  •  

Domanda

Voglio licenziare una vista modale regolatore FormSheetPresentation quando l'utente tocca al di fuori della vista modale ... ho visto un mucchio di applicazioni fare questo (ebay su iPad, per esempio), ma i cant capire come poiché il punto di vista sotto sono disabilitata da tocchi quando vista modale vengono visualizzati come questo (sono presentandolo come una popover forse?) ... qualcuno ha qualche suggerimento?

È stato utile?

Soluzione

Sono un anno di ritardo, ma questo è abbastanza semplice da fare.

Avere il vostro controller di vista modale allegare un sistema di riconoscimento gesto alla finestra della vista:

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

Il codice di gestione:

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

Questo è tutto. HIG sia dannato, questo è un comportamento utile e spesso intuitive.

Altri suggerimenti

Le altre applicazioni non utilizzano modale Visto se permettono la visualizzazione per essere respinto facendo clic all'esterno di esso. UIModalPresentationFormSheets non può essere respinta in questo modo. (Né, del resto può un UIModal in SDK3.2). Solo il UIPopoverController può essere respinta facendo clic al di fuori dell'area. E 'molto probabile (anche se contro l'iPad di Apple HIG) per gli sviluppatori di app di aver ombreggiato la schermata di sfondo e quindi visualizzata la UIPopoverController in modo che appaia come un UIModalPresentationFormSheets (o altro UIModal View).

  

[...] stile UIModalPresentationCurrentContext consente a un controller di vista adottare lo stile di presentazione del suo genitore. In ogni vista modale, le zone abbassate mostrano il contenuto sottostante, ma non consentono rubinetti in quel contenuto. Pertanto, a differenza di un popover, le vostre opinioni modali devono ancora avere controlli che consentono all'utente di respingere il vista modale.

Vedere l'iPadProgrammingGuide sul sito degli sviluppatori per ulteriori informazioni (pagina 46 - "Configurare lo stile di presentazione per modali Vista")

Per iOS 8, si deve attuare sia la UIGestureRecognizer, e scambiare il (x, y) coordinate della posizione sfruttato quando l'orientamento orizzontale. Non so se questo è dovuto ad un bug di 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;
}

Il codice sopra funziona bene, ma vorrei cambiare l'istruzione if a,

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

Questo fa in modo che si può ancora interagire con la barra di navigazione, altrimenti toccando in esso revoca il vista modale.

risposta aggiornata per iOS 8

A quanto pare, in iOS 8, il UIDimmingView ha un riconoscitore rubinetto gesto, che interferisce con l'implementazione iniziale, quindi l'ignoriamo e non lo richiedono a fallire.


Questa è l'età di velocità, quindi la maggior parte sono probabilmente solo copiando il codice di cui sopra .. Ma, soffro di disturbo ossessivo compulsivo quando si tratta di codice, purtroppo.

Qui è una soluzione modulare che utilizza la risposta di Danilo Campos con le categorie . E 'anche risolve di un bug importante che può verificarsi se si respinge la tua modale attraverso altri mezzi, come detto .

Nota. Il se le dichiarazioni sono lì, perché io uso il controller della vista sia per iPhone e iPad, e solo l'iPad ha bisogno di registrare / annullare la registrazione

UPDATE: Il succo è stato aggiornato, in quanto non ha funzionato correttamente con il impressionante FCOverlay codice, e non hanno permesso gesti per essere riconosciuti nella visualizzazione presentato. Quei problemi vengono risolti. Utilizzando la categoria è facile come:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    if (self.presentingViewController) {
        [self registerForDismissOnTapOutside];
    }
}

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

    [super viewWillDisappear:animated];
}

Copia incolla questo codice nel tuo 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: Se avete qualche altro modo per chiudere il finestra popup modale , non dimenticate di rimuovere il sistema di riconoscimento del rubinetto gesto!

Ho dimenticato questo, ed ha ottenuto gli arresti folli in seguito, dal momento che il sistema di riconoscimento del rubinetto era ancora sparando eventi.

Secondo l'iOS di Apple HIG, 1. la vista modale non ha quella capacità di essere licenziati senza alcun input su se stessa; 2. Usare vista modale nella situazione in cui è necessario un input dell'utente.

Usa UIPresentationController invece:

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

Modificato da LookInside esempio

Questo funziona per me per iOS7 un 8 e navigazione bar.

Se non avete bisogno di barra di navigazione è sufficiente rimuovere location2 e seconda condizione if dopo i tubi.

@MiQUEL questo dovrebbe funzionare anche per te

- (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: Si può anche bisogno di essere un delegato gesto riconoscitore per questa e altre soluzioni di cui sopra al lavoro. Farlo in questo modo:

@interface CommentTableViewController () <UIGestureRecognizerDelegate>

te impostato come delegato per il riconoscitore:

self.recognizer.delegate = self;

e implementare questo metodo delegato:

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

E 'abbastanza fattibile.

Date un'occhiata qui

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

Si tratta di una navigationController (modale) che automatica respingere per ipad (quando si tocca fuori)

Usa il tuo all'interno viewcontroller di esso.

La speranza aiuta.

Lo so che è tardi, ma considerare l'utilizzo di CleanModal (testato con iOS 7 e 8).

https://github.com/orafaelreis/CleanModal

È possibile utilizzare MZFormSheetController in questo modo:

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

In Swift 2 / Xcode Versione 7.2 (7C68) il seguente codice ha funzionato per me.

Attenzione: questo codice dovrebbe essere messo nel file ViewController.swift del FormSheet oppure Page patrimoniale presentato, qui: "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)
            }
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top