Je souhaite fermer un contrôleur de vue modale FormSheetPresentation lorsque l'utilisateur appuie en dehors de la vue modale ... J'ai vu un tas d'applications le faire (eBay sur ipad par exemple), mais je ne peux pas comprendre comment, puisque les vues inférieures sont désactivées du toucher. quand les vues modales sont affichées comme ceci (le présente-t-il comme une popover peut-être?) ... Quelqu'un a-t-il des suggestions?
Réponses
Trop de publicités?J'ai un an de retard, mais c'est assez simple à faire.
Demandez à votre contrôleur de vue modale de joindre un identificateur de geste à 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 manutention:
- (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 à peu près ça. HIG soit damné, c’est un comportement utile et souvent intuitif.
Les autres applications ne sont pas à l'aide de Modal Vues si elles permettent à la vue de l'être rejeté en cliquant à l'extérieur de l'il. UIModalPresentationFormSheets
ne peut pas être rejeté de cette façon. (ni, en effet, peut-toute UIModal dans SDK3.2). Seulement l' UIPopoverController
peut être rejeté en cliquant à l'extérieur de la région. Il est très possible (bien que contre l'iPad d'Apple HIG) pour le développeur de l'application pour avoir ombragée à l'arrière-plan de l'écran et affiche ensuite l' UIPopoverController
, de sorte qu'il ressemble à un UIModalPresentationFormSheets
(ou d'autres UIModal Vue).
[...] UIModalPresentationCurrentContext style permet une vue contrôleur adopter le style de présentation de son parent. Dans chaque modal de vue, les zones grisées afficher le contenu sous-jacent, mais ne permettent pas de robinets dans le contenu. Par conséquent, contrairement à une fenêtre pop-over, vos points de vue modal doit toujours disposer de contrôles qui permettent à l'utilisateur pour fermer la modale de vue.
Voir la iPadProgrammingGuide sur le site du développeur pour plus d'informations (Page 46 -- "Configuration du Style de Présentation Modale, les points de Vue")
Pour iOS 8, vous devez à la fois implémenter les UIGestureRecognizer
et échanger les coordonnées (x, y) de l'emplacement sélectionné en mode paysage. Pas sûr que cela soit 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 en,
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];
}
Cela garantit que vous pouvez toujours interagir avec la barre de navigation. Sinon, taper dessus ferait disparaître la vue modale.
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];
}
}
}