top regular human guidelines compact bar iphone ipad modal-view

regular - El SDK de Iphone descarta ViewControllers Modal en ipad haciendo clic fuera de él



navigation bar guidelines ios (14)

Quiero descartar un controlador de vista modal FormSheetPresentation cuando el usuario toca fuera de la vista modal ... He visto un montón de aplicaciones haciendo esto (eBay en el iPad, por ejemplo), pero no puedo entender cómo las vistas de abajo están deshabilitadas por los toques cuando las vistas modales se muestran así (¿lo están presentando como un popover quizás?) ... ¿alguien tiene alguna sugerencia?


Acordando con iOS HIG de Apple, 1. la vista modal no tiene la capacidad de ser descartada sin ningún aporte en sí mismo; 2. use la vista modal en la situación en que se requiere la entrada de un usuario.


Copie y pegue este código en su 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]; } } }


El código anterior funciona muy bien, pero cambiaría la instrucción 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]; }

Esto asegura que aún pueda interactuar con la barra de navegación; de lo contrario, al tocarla se descarta la vista modal.


En Swift 2 / Xcode Version 7.2 (7C68) el siguiente código funcionó para mí.

Atención: este código debe colocarse en el archivo ViewController.swift de FormSheet o Hoja de páginas presentados, aquí: "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) } } } }


Es bastante factible.

Echa un vistazo aquí

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

Es un NavigationController (modal) que descarta automáticamente para ipad (cuando tocas afuera)

Use su controlador de visualización dentro de él.

Espero eso ayude.


Esto funciona para mí para ios7 an 8 y la barra de navegación.

Si no necesita la barra de navegación, simplemente elimine la ubicación2 y la segunda condición en la instrucción if después de las tuberías.

@MiQUEL esto debería funcionar para ti también

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

Editar: también puede ser necesario ser un delegado reconocedor de gestos para que esta y otras soluciones anteriores funcionen. Hazlo así:

@interface CommentTableViewController () <UIGestureRecognizerDelegate>

configúrate como delegado para el reconocedor:

self.recognizer.delegate = self;

e implementar este método delegado:

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


Las otras aplicaciones no usan Vistas modales si permiten que la vista se descarte haciendo clic fuera de ella. UIModalPresentationFormSheets no puede descartarse de esta manera. (ni, de hecho, puede ningún UIModal en SDK3.2). Solo se puede descartar el UIPopoverController haciendo clic fuera del área. Es muy posible (aunque contra el iPad HIG de Apple) que el desarrollador de la aplicación haya sombreado la pantalla de fondo y luego haya mostrado UIPopoverController para que se vea como UIModalPresentationFormSheets (u otra vista de UIModal).

[...] UIModalPresentationCurrentContext style permite que un controlador de vista adopte el estilo de presentación de su principal. En cada vista modal, las áreas oscurecidas muestran el contenido subyacente pero no permiten toques en ese contenido. Por lo tanto, a diferencia de un popover, sus vistas modales aún deben tener controles que permitan al usuario descartar la vista modal.

Consulte la iPadProgrammingGuide en el sitio del desarrollador para obtener más información (Página 46 - "Configuración del estilo de presentación para vistas modales")


Llego un año tarde, pero esto es bastante sencillo de hacer.

Haga que su controlador de vista modal adjunte un reconocedor de gestos a la ventana de la 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];

El código de manejo:

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

Eso es todo. HIG maldito, este es un comportamiento útil ya menudo intuitivo.


Para iOS 8, debe implementar UIGestureRecognizer e intercambiar las coordenadas (x, y) de la ubicación girada cuando está en orientación horizontal. No estoy seguro de si esto se debe a un error de 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; }


Puedes usar MZFormSheetController así:

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



Use UIPresentationController en su lugar:

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

Modificado desde el ejemplo de LookInside


Very important: si tiene otra forma de cerrar su ventana emergente modal , ¡no olvide quitar el reconocedor de gestos de toque!

Me olvidé de esto y me volví locas más tarde, ya que el reconocedor de tap seguía disparando eventos.


Respuesta actualizada para iOS 8

Aparentemente, en iOS 8, UIDimmingView tiene un reconocedor de gestos de toque, que interfiere con la implementación inicial, por lo que lo ignoramos y no exigimos que falle.

Esta es la era de la velocidad, por lo que la mayoría probablemente solo esté copiando el código anterior. Pero, sufro de TOC cuando se trata de código, desafortunadamente.

Aquí hay una solución modular que usa la respuesta de Danilo Campos con categorías . También resuelve un error importante que puede ocurrir si está descartando su modal por otros medios, como se mencionó .

NOTA: Las declaraciones if están ahí porque utilizo el controlador de vista para iPhone y iPad, y solo el iPad necesita registrarse / anular el registro.

ACTUALIZACIÓN: La esencia se ha actualizado, ya que no funcionaba correctamente con el impresionante código FCOverlay , y no permitió que los gestos se reconocieran en la vista presentada. Esos problemas son fijos. Usar la categoría es tan fácil como:

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