iPhone SDK يرفض عرض Modal ViewControllers على iPad من خلال النقر خارجها

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

  •  26-09-2019
  •  | 
  •  

سؤال

أرغب في رفض وحدة تحكم عرض Modal View Modal FormSeetSeetpresprespresprespresentation عندما ينقر المستخدم خارج طريقة العرض الوسيطة ... لقد رأيت مجموعة من التطبيقات تقوم بذلك (eBay على iPad على سبيل المثال) ولكن لا يمكنني معرفة كيفية تعطيل وجهات النظر أسفلها من اللمسات عندما يتم عرض طرق العرض الوسطى مثل هذا (هل يقدمونها كبوبوفر ربما؟) ... أي شخص لديه أي اقتراحات؟

هل كانت مفيدة؟

المحلول

لقد تأخرت عامًا ، لكن هذا أمر واضح ومباشر.

اطلب من وحدة التحكم في العرض المشروط إرفاق معرفًا لفتة إلى نافذة العرض:

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

هذا عن ذلك. هيغ ملعون ، وهذا سلوك مفيد وغالبا ما يكون بديهية.

نصائح أخرى

لا تستخدم التطبيقات الأخرى طرق عرض مشروط إذا سمحت برفض العرض من خلال النقر خارجها. UIModalPresentationFormSheets لا يمكن رفضها بهذه الطريقة. (ولا ، في الواقع يمكن لأي uimodal في SDK3.2). فقط UIPopoverController يمكن رفضها بالنقر خارج المنطقة. من الممكن للغاية (على الرغم من أنه ضد Apple iPad HIG) لمطور التطبيق لتظليل شاشة الخلفية ثم عرض UIPopoverController بحيث يبدو مثل أ UIModalPresentationFormSheets (أو غيرها من عرض Uimodal).

] في كل عرض مشروط ، تُظهر المناطق الخافتة المحتوى الأساسي ولكن لا تسمح بنقرات في هذا المحتوى. لذلك ، على عكس Popover ، يجب أن لا تزال طرق العرض الوسائط الخاصة بك لديها عناصر تحكم تسمح للمستخدم برفض عرض الوسائط.

راجع 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 لديه معرف إيماءة الصنبور ، والذي يتداخل مع التنفيذ الأولي ، لذلك نحن نتجاهله ولا نطلب من الفشل.


هذا هو عصر السرعة ، لذلك ربما يكون معظمهم مجرد نسخ الكود أعلاه .. لكنني أعاني من الوسواس القهري عندما يتعلق الأمر بالرمز ، لسوء الحظ.

فيما يلي حل معياري يستخدم إجابة Danilo Campos مع الفئات. أيضا يحل خطأ مهم قد يحدث إذا كنت ترفض وسيطتك من خلال وسائل أخرى ، كما ذكر.

ملاحظة: توجد عبارات إذا كنت أستخدم وحدة التحكم في العرض لكل من iPhone و iPad ، ولا يحتاج iPad فقط إلى التسجيل/عدم التسجيل.

تحديث: تم تحديث GIST ، لأنه لم يعمل بشكل صحيح مع الرهيبة 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 A 8 و Navigation Bar.

إذا لم تكن بحاجة إلى شريط NAV ، فما عليك سوى إزالة الموقع 2 والشرط الثاني في العبارة 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;
}

إنه قابل للتنفيذ.

ألق نظرة هنا

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

إنه NavigationController (Modal) الذي يرفضه تلقائيًا لـ iPad (عند النقر في الخارج)

استخدم ViewController بداخله.

أتمنى أن يساعد ذلك.

أعلم أن الأمر متأخرًا ولكن فكر في استخدام CleanModal (تم اختباره باستخدام iOS 7 و 8).

https://github.com/orafaelreis/cleanmodal

يمكنك استخدام MzFormsheetController مثله:

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

في Swift 2/Xcode الإصدار 7.2 (7C68) عملت الرمز التالي بالنسبة لي.

الانتباه: يجب وضع هذا الرمز في ملف ViewController.swift من ورقة النموذج أو صفحة الصفحة المقدمة ، هنا: "PageshetViewController.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)
            }
        }
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top