Я хочу убрать модуль Modal View Control FormSheetPresentation, когда пользователь выходит за рамки модального представления... Я видел, как куча приложений делает это (например, ebay на ipad), но я не могу понять, как с нижних представлений отключен от прикосновений, когда модальные представления отображаются так (они, возможно, представляют его как поппер?)... у кого есть предложения?
Iphone SDK отклоняет Modal ViewControllers на ipad, щелкнув за его пределами
Ответ 1
Я опаздываю на год, но это довольно просто.
Попросите свой контроллер модального вида подключить распознаватель жестов к окну просмотра:
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 location into the local view coordinate system, and test to see if it 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 view.window is valid.
[self.view.window removeGestureRecognizer:sender];
[self dismissModalViewControllerAnimated:YES];
}
}
}
Что об этом. HIG проклят, это полезное и часто интуитивное поведение.
Ответ 2
Другие приложения не используют модальные представления, если они позволяют отклонять представление, вызывая его за пределами. UIModalPresentationFormSheets
нельзя отклонить таким образом. (и, действительно, любой UIModal в SDK3.2). Только UIPopoverController
можно отклонить, щелкнув за пределами области. Очень возможно (хотя против Apple iPad HIG) для разработчика приложений затенять фоновый экран, а затем отобразить UIPopoverController
, чтобы он выглядел как UIModalPresentationFormSheets
(или другой UIModal View).
[...] Стиль UIModalPresentationCurrentContext позволяет контроллеру представления использовать стиль презентации своего родителя. В каждом модальном представлении затемненные области отображают базовое содержимое, но не допускают кратковременного доступа к этому контенту. Поэтому, в отличие от popover, ваши модальные представления все равно должны иметь элементы управления, которые позволяют пользователю отклонять модальный вид.
См. iPadProgrammingGuide на сайте разработчика для получения дополнительной информации (Страница 46 - "Настройка стиля презентации для модальных просмотров" )
Ответ 3
Для 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 location into the local view coordinate system, and test to see if it 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 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;
}
Ответ 4
Код выше отлично работает, но я бы изменил оператор 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 view.window is valid.
[self.view.window removeGestureRecognizer:sender];
[self dismissModalViewControllerAnimated:YES];
}
Это гарантирует, что вы все еще можете взаимодействовать с навигационной панелью, в противном случае нажатие на нее отменяет модальное представление.
Ответ 5
Скопируйте этот код в свой 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 location into the local view coordinate system, and test to see if it 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 view.window is valid.
[self.view.window removeGestureRecognizer:sender];
[self dismissModalViewControllerAnimated:YES];
}
}
}
Ответ 6
Ответ обновлен для iOS 8
По-видимому, в iOS 8 UIDimmingView
имеет распознаватель жестов, который мешает первоначальной реализации, поэтому мы игнорируем его и не требуем, чтобы он завершился с ошибкой.
Это возраст скорости, поэтому большинство из них, вероятно, просто копируют код выше. Но, к сожалению, я страдаю от OCD, когда дело доходит до кода.
Вот модульное решение, которое использует ответ Danilo Campos с категориями. Он также решает важную ошибку, которая может возникнуть, если вы отклоняете свой модальный метод с помощью других средств как упомянуто.
ПРИМЕЧАНИЕ: Операторы if есть, потому что я использую контроллер вида для iPhone и iPad, и только iPad необходимо зарегистрировать/отменить регистрацию.
UPDATE: Обновлен, потому что он не работает должным образом с awesome 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];
}
Ответ 7
Very important:
Если у вас есть другой способ закрыть свое модальное всплывающее окно, не забудьте удалить распознаватель жестов!
Я забыл об этом и позже получил сумасшедшие сбои, так как распознающий кран все еще запускал события.
Ответ 8
В соответствии с Apple iOS HIG, 1. модальное представление не имеет возможности быть уволенным без каких-либо вкладов; 2. Используйте модальный вид в ситуации, когда требуется ввод пользователя.
Ответ 9
Вместо этого используйте 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
Ответ 10
Это работает для меня для ios7 8 и панели навигации.
Если вам не нужна панель навигации, просто удалите location2 и второе условие в инструкции 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;
}
Ответ 11
Это довольно выполнимо.
Взгляните сюда
fooobar.com/questions/101952/...
Это навигационный контроллер (модальный), который автоматически отключается для ipad (когда вы нажимаете наружу)
Используйте свой монитор управления внутри него.
Надеюсь, что это поможет.
Ответ 12
Я знаю это поздно, но подумайте об использовании CleanModal (протестированного с iOS 7 и 8).
Ответ 13
Вы можете использовать MZFormSheetController следующим образом:
MZFormSheetController *formSheet = [[MZFormSheetController alloc] initWithSize:customSize viewController:presentedViewController];
formSheet.shouldDismissOnBackgroundViewTap = YES;
[presentingViewController mz_presentFormSheetController:formSheet animated:YES completionHandler:nil];
Ответ 14
В 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)
}
}
}
}