programing

Iphone SDK는 외부를 클릭하여 ipad에서 Modal ViewController를 닫습니다.

copyandpastes 2021. 1. 16. 11:00
반응형

Iphone SDK는 외부를 클릭하여 ipad에서 Modal ViewController를 닫습니다.


사용자가 모달보기 외부를 탭할 때 FormSheetPresentation 모달보기 컨트롤러를 닫고 싶습니다 ...이 작업을 수행하는 앱 (예 : 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'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];
        }
     }
}

그게 다야. HIG는 저주받을 수 있습니다. 이것은 유용하고 종종 직관적 인 동작입니다.


다른 앱은 외부를 클릭하여보기를 닫을 수있는 경우 모달보기를 사용하지 않습니다. UIModalPresentationFormSheets이렇게 해산 할 수 없습니다. (실제로 SDK3.2의 모든 UIModal은 가능합니다). UIPopoverController영역 외부를 클릭하면의 해제 할 수 있습니다. 앱 개발자가 배경 화면을 음영 처리 한 다음 (또는 다른 UIModal 뷰) UIPopoverController처럼 보이도록 표시하는 것은 매우 가능합니다 (애플의 iPad HIG에 대해 UIModalPresentationFormSheets).

[...] UIModalPresentationCurrentContext 스타일을 사용하면 뷰 컨트롤러가 부모의 프레젠테이션 스타일을 채택 할 수 있습니다. 각 모달보기에서 흐리게 표시된 영역은 기본 콘텐츠를 표시하지만 해당 콘텐츠에서 탭을 허용하지 않습니다. 따라서 팝 오버와 달리 모달 뷰에는 사용자가 모달 뷰를 닫을 수있는 컨트롤이 여전히 있어야합니다.

자세한 내용은 개발자 사이트의 iPadProgrammingGuide를 참조하십시오 (46 페이지- "모달보기를위한 프레젠테이션 스타일 구성").


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

위의 코드는 훌륭하게 작동하지만 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];
    }

이렇게하면 탐색 모음과 계속 상호 작용할 수 있습니다. 그렇지 않으면 탐색 모음을 탭하면 모달보기가 닫힙니다.


iOS 8에 대한 답변 업데이트

분명히 iOS 8에서는 UIDimmingView초기 구현을 방해하는 탭 제스처 인식기가 있으므로이를 무시하고 실패 할 필요가 없습니다.


지금은 속도의 시대이므로 대부분은 아마도 위의 코드를 복사하고있을 것입니다. 그러나 불행히도 코드에 관해서는 OCD에 시달립니다.

다음은 Danilo Campos의 답변을 범주로 사용하는 모듈 식 솔루션입니다 . 또한 언급했듯이 다른 방법을 통해 모달을 해제 할 때 발생할 수있는 중요한 버그를 해결 합니다 .

참고 : iPhone과 iPad 모두에 뷰 컨트롤러를 사용하고 iPad 만 등록 / 등록 취소하면되기 때문에 if 문이 있습니다.

업데이트 : 멋진 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];
}

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:모달 팝업 창 을 닫는 다른 방법이있는 경우 탭 제스처 인식기를 제거하는 것을 잊지 마십시오!

나는 이것을 잊어 버렸고 나중에 탭 인식기가 여전히 이벤트를 발생시키고 있었기 때문에 미친 충돌이 발생했습니다.


Apple의 iOS HIG에 따르면 1. 모달보기는 자체 입력 없이는 해제 할 수있는 기능이 없습니다. 2. 사용자 입력이 필요한 상황에서 모달보기를 사용합니다.


대신 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 예제에서 수정 됨


이것은 ios7 및 8 및 탐색 모음에서 나를 위해 작동합니다.

탐색 모음이 필요하지 않으면 파이프 뒤의 if 문에서 location2 및 두 번째 조건을 제거하십시오.

@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>

set yourself as the delegate for the recognizer:

self.recognizer.delegate = self;

and implement this delegate method:

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

It is pretty doable.

Take a look here

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

It is a NavigationController (modal) that auto dismiss for ipad (when you tap outside)

Use your viewcontroller inside of it.

Hope it helps.


I know it's late but consider using CleanModal (tested with iOS 7 and 8).

https://github.com/orafaelreis/CleanModal


You can use MZFormSheetController like this:

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

In Swift 2/Xcode Version 7.2 (7C68) the following code worked for me.

Attention: this code should be put in the ViewController.swift file of the presented FormSheet or Page Sheet, here: "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)
            }
        }
    }
}

ReferenceURL : https://stackoverflow.com/questions/2623417/iphone-sdk-dismissing-modal-viewcontrollers-on-ipad-by-clicking-outside-of-it

반응형