आईओएस 13 पर डिटेक्टिंग शीट को खारिज कर दिया गया था


120

IOS 13 से पहले, पूरे स्क्रीन को कवर करने के लिए उपयोग किए जाने वाले दृश्य नियंत्रक प्रस्तुत किए गए। और, जब खारिज किया जाता है, तो पैरेंट व्यू कंट्रोलर viewDidAppearफंक्शन निष्पादित किया जाता है।

अब iOS 13 डिफॉल्ट के रूप में व्यू कंट्रोलर्स को एक शीट के रूप में पेश करेगा, जिसका अर्थ है कि कार्ड आंशिक रूप से अंतर्निहित व्यू कंट्रोलर को कवर करेगा, जिसका अर्थ है कि viewDidAppearइसे कॉल नहीं किया जाएगा, क्योंकि पेरेंट व्यू कंट्रोलर वास्तव में कभी गायब नहीं हुआ है।

क्या यह पता लगाने का एक तरीका है कि प्रस्तुत दृश्य नियंत्रक शीट खारिज कर दी गई थी ? कुछ अन्य फ़ंक्शन मैं किसी प्रकार के प्रतिनिधि का उपयोग करने के बजाय मूल दृश्य नियंत्रक में ओवरराइड कर सकता हूं ?


5
अच्छी तरह से डेवलपर.
मैट

तो क्या एक बार में सभी मॉडल शीट को रूट vc पर खारिज करने का कोई तरीका है?
वेस्ली


आपको यह जानने की आवश्यकता क्यों है कि इसे कब खारिज किया गया? यदि यह डेटा को फिर से लोड करना है और UI को अपडेट करना है, तो सूचनाएं या KVO एक अच्छा विकल्प हो सकता है।
martn_st

जवाबों:


61

क्या यह पता लगाने का एक तरीका है कि प्रस्तुत दृश्य नियंत्रक शीट खारिज कर दी गई थी?

हाँ।

कुछ अन्य फ़ंक्शन मैं किसी प्रकार के प्रतिनिधि का उपयोग करने के बजाय मूल दृश्य नियंत्रक में ओवरराइड कर सकता हूं?

नहीं। "कुछ प्रकार के प्रतिनिधि" आप इसे कैसे करते हैं। अपने आप को प्रस्तुति नियंत्रक के प्रतिनिधि और ओवरराइड करें presentationControllerDidDismiss(_:)

https://developer.apple.com/documentation/uikit/uiadaptivepresentationcontrollerdelegate/3229889-presentationcontrollerdiddismiss


सामान्य रनटाइम-जनरेट किए गए ईवेंट की कमी आपको यह बताती है कि एक प्रस्तुत दृश्य नियंत्रक, फ़ुलस्क्रीन या नहीं, खारिज कर दिया गया है, वास्तव में परेशानी है; लेकिन यह कोई नया मुद्दा नहीं है, क्योंकि हमेशा गैर-पूर्णस्क्रीन प्रस्तुत दृश्य नियंत्रक होते हैं। यह अभी है (iOS 13 में) उनमें से अधिक हैं! मैं कहीं और इस विषय पर एक अलग सवाल-जवाब समर्पित करता हूं: यूनिफाइड यूविविकंट्रोलर "फ्रंटेस्ट" बन गया?


6
यह पर्याप्त नहीं है। यदि आपके पास आपके प्रस्तुत वीसी में एक नेबर है और एक कस्टम बार बटन है जो आपके दृश्य को प्रोग्रामेटिक रूप से खारिज करता है, तो प्रस्तुति नियंत्रक ने खारिज नहीं किया है।
इरिना

15
हाय @ इरीना - यदि आप अपने विचार को प्रोग्रामेटिक रूप से खारिज करते हैं, तो आपको कॉलबैक की आवश्यकता नहीं है क्योंकि आपने अपने विचार को प्रोग्रामेटिक रूप से खारिज कर दिया है - आप जानते हैं कि आपने ऐसा किया था क्योंकि आपने ऐसा किया था। प्रतिनिधि विधि केवल उस स्थिति में है जब उपयोगकर्ता इसे करता है।
मैट

6
@ उत्तर के लिए धन्यवाद। जब दृश्य को प्रोग्रामिक रूप से खारिज कर दिया जाता है तो इसे कॉल नहीं किया जाता है (जैसा कि इरिना कहती है), और आप सही हैं कि हम जानते हैं कि हमने यह किया है। मुझे लगता है कि iOS13 में नए मोडल प्रेजेंटेशन स्टाइल के साथ सिर्फ एक तरह का 'व्यूवेलपियर' प्राप्त करने के लिए बॉयलरप्लेट कोड की अनावश्यक मात्रा है। यह विशेष रूप से गड़बड़ हो जाता है जब आप एक आर्किटेक्चर के माध्यम से रूटिंग का प्रबंधन कर रहे हैं जहां रूटिंग को निकाला जाता है (MVVM + संयोजकों में, या उदाहरण के लिए VIPER में एक राउटर प्रकार)
एडम वाइट

3
@ एडमडाइट मैं सहमत हूं लेकिन यह समस्या नई नहीं है। हमारे पास वर्षों से यह समस्या थी, पॉपओवर के साथ, गैर-फ़ुलस्क्रीन प्रस्तुत दृश्य नियंत्रकों के साथ, अलर्ट के साथ, और इसके आगे। मैं इसे Apple के "घटनाओं" के प्रदर्शनों की सूची में एक गंभीर दोष के रूप में मानता हूं। मैं सिर्फ यह कह रहा हूं कि वास्तविकता क्या है और क्यों है। मैं सीधे इस मुद्दे से जूझता हूं: stackoverflow.com/questions/54602662/…
मैट

1
presentationControllerDidDismiss (_ :)। चाइल्ड वीसी में बैक बटन पर क्लिक करने पर मुझे फोन नहीं किया जाता है। कोई मदद करता है?
कृष्णा मीणा

42

यहां एक पैरेंट व्यू-कंट्रोलर का एक कोड उदाहरण दिया गया है, जिसे तब देखा जाता है जब चाइल्ड व्यू-कंट्रोलर इसे शीट के रूप में प्रस्तुत करता है (यानी, डिफ़ॉल्ट iOS 13 तरीके से) खारिज कर दिया जाता है:

public final class Parent: UIViewController, UIAdaptivePresentationControllerDelegate
{
  // This is assuming that the segue is a storyboard segue; 
  // if you're manually presenting, just see the delegate there.
  public override func prepare(for segue: UIStoryboardSegue, sender: Any?)
  {
    if segue.identifier == "mySegue" {
      segue.destination.presentationController?.delegate = self;
    }
  }

  public func presentationControllerDidDismiss(
    _ presentationController: UIPresentationController)
  {
    // Only called when the sheet is dismissed by DRAGGING.
    // You'll need something extra if you call .dismiss() on the child.
    // (I found that overriding dismiss in the child and calling
    // presentationController.delegate?.presentationControllerDidDismiss
    // works well).
  }
}

Jerland2 के जवाब उलझन में है, के बाद से (क) मूल प्रश्नकर्ता जब चादर है एक समारोह कॉल प्राप्त करना चाहता था खारिज कर दिया (जबकि वह presentationControllerDidAttemptToDismiss है, जो कहा जाता है जब उपयोगकर्ता की कोशिश करता है लागू किया और विफल रहता है , और (ख) की स्थापना isModalInPresentation चादर खारिज करने के लिए) पूरी तरह से ऑर्थोगोनल है और वास्तव में प्रस्तुत शीट को अविभाज्य बना देगा (जो ओपी चाहता है कि इसके विपरीत है)।


6
यह अच्छा काम करता है। बस एक टिप है कि अगर आप अपने बुलाया वीसी पर एक नौसेना नियंत्रक का उपयोग करते हैं, तो आपको नौसेना नियंत्रक को प्रेजेंटेशन कांट्रैक्टर, डेलीगेट के रूप में असाइन करना चाहिए (न कि वीसी के पास शीर्ष व्यू कॉन्ट्रोलर है)।
instAustralia

@instAustralia क्या आप बता सकते हैं कि एक दस्तावेज का संदर्भ या संदर्भ क्यों है? धन्यवाद।
अहमद ओसामा

प्रस्तुतिकंट्रोलरडीडिस्मिस इसे कैसे प्राप्त करें जब उपयोगकर्ता बैक बटन दबाए?
कृष्णा मीणा

@AhmedOsama - नेविगेशन नियंत्रक प्रस्तुति नियंत्रक है और इसलिए यह प्रतिनिधि है क्योंकि यह बर्खास्तगी का जवाब देने वाला होगा। मैंने वीसी की कोशिश की जो नव नियंत्रक में भी अंतर्निहित है, लेकिन यह वह जगह है जहां मेरे वास्तविक बटन मौजूद हैं और जवाब देने के लिए। मैं इसे सीधे Apple डॉक्स में नहीं ढूँढ सकता, लेकिन यह यहाँ संदर्भित है sarunw.com/posts/modality-changes-in-ios13
instAustralia

26

वापस आने viewWillAppearऔर viewDidAppearसेट होने का एक और विकल्प

let vc = UIViewController()
vc.modalPresentationStyle = .fullScreen

यह विकल्प पूर्ण स्क्रीन को कवर करता है और खारिज करने के बाद, तरीकों से ऊपर कॉल करता है


2
धन्यवाद PiterPan। यह काम कर रहा है। यह महान और सबसे तेजी से हल है।
एरकम KUCET

पूर्व डिफ़ॉल्ट व्यवहार को पुनर्स्थापित करने के लिए इस तेज़ और विश्वसनीय तरीके के लिए धन्यवाद। यह ठीक करने के लिए तुरन्त जगह तय करने में सक्षम है और फिर तर्कसंगत तरीके से नए व्यवहार के लिए एक संक्रमण की योजना बनाएं।
इयान लवजॉय

14
यह फिक्स के बजाय वर्कअराउंड है। सभी के लिए सिर्फ iOS 12 स्टाइल शीट पर वापस लौटना महान नहीं है। आईओएस 13 वाले शांत हैं! :)
मैट

1
iPad के लिए इसका उपयोग करने में सावधानी बरतें, क्योंकि iPad एक पृष्ठशैली के रूप में पेश करने के लिए चूक करता है जब इसे औपचारिक रूप से प्रस्तुत किया जाता है। यह पूर्ण स्क्रीन के रूप में उपस्थित करने के लिए आईपैड के लिए बाध्य करेगा
wyu

मेरे लिए काम नहीं। मैं मोडल कंट्रोलर खोलता हूं। इसे खारिज करने के साथ बंद करें, लेकिन वसीयत को नहीं बुलाया जाएगा। क्यों? धन्यवाद
neo999

20

भविष्य के पाठकों के लिए यहां कार्यान्वयन के साथ एक अधिक पूर्ण उत्तर है:

  1. रूट व्यू कंट्रोलर में सेग के लिए तैयारी निम्नलिखित जोड़ें (मान लें कि आपके मोडल में एक नौसेना नियंत्रक है)
    // Modal Dismiss iOS 13
    modalNavController.presentationController?.delegate = modalVc
  1. मोडल व्यू कंट्रोलर में निम्नलिखित डेलीगेट + मेथड जोड़ें
// MARK: - iOS 13 Modal (Swipe to Dismiss)

extension ModalViewController: UIAdaptivePresentationControllerDelegate {
    func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {


        print("slide to dismiss stopped")
        self.dismiss(animated: true, completion: nil)
    }
}
  1. मोडल व्यू कंट्रोलर में यह सुनिश्चित करें कि प्रतिनिधि के बुलाए जाने के लिए निम्नलिखित संपत्ति सही है
    self.isModalInPresentation = true
  1. फायदा

1
self.isModalInPresentation = true तो खींचें बर्खास्तगी काम नहीं है। उस रेखा प्रतिनिधि विधि को अभी भी ठीक कहा जाता है। धन्यवाद।
योगेश पटेल

2
यह (क) के बाद से उलझन में है कि मूल प्रश्नकर्ता शीट को खारिज करने के बाद फ़ंक्शन कॉल करना चाहता था (जबकि आपने प्रेजेंटेशनकंट्रोलरडीडएटेमटेप्टोइज़िम लागू किया है, जिसे तब कहा जाता है जब उपयोगकर्ता शीट को खारिज करने की कोशिश करता है और विफल रहता है), और (बी) सेटिंग isModalInPresentation पूरी तरह से ऑर्थोगोनल है और वास्तव में प्रस्तुत शीट को अविभाज्य बना देगा (जो ओपी चाहता है कि इसके विपरीत है)।
मैट

1
presentationControllerDidDismiss
@Matt के

5

तीव्र

IOS13viewWillAppear में कॉल करने के लिए सामान्य समाधान

class ViewController: UIViewController {

        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            print("viewWillAppear")
        }

        //Show new viewController
        @IBAction func show(_ sender: Any) {
            let newViewController = NewViewController()
            //set delegate of UIAdaptivePresentationControllerDelegate to self
            newViewController.presentationController?.delegate = self
            present(newViewController, animated: true, completion: nil)
        }
    }

    extension UIViewController: UIAdaptivePresentationControllerDelegate {
        public func presentationControllerDidDismiss( _ presentationController: UIPresentationController) {
            if #available(iOS 13, *) {
                //Call viewWillAppear only in iOS 13
                viewWillAppear(true)
            }
        }
    }

1
यह केवल हैंडल को ऊपर से स्लाइड का उपयोग करके खारिज करता है, फ़ंक्शन को कॉल करके नहीं dismiss(_)
पेड्रो पाउलो अमोरिम

3

DRAG OR CALL DISMISS FUNC नीचे दिए गए कोड के साथ काम करेगा।

1) रूट व्यू कंट्रोलर में, आप बताते हैं कि कोड के रूप में इसका प्रेजेंटेशन व्यू कंट्रोलर कौन सा है

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "presenterID" {
        let navigationController = segue.destination as! UINavigationController
        if #available(iOS 13.0, *) {
            let controller = navigationController.topViewController as! presentationviewcontroller
            // Modal Dismiss iOS 13
            controller.presentationController?.delegate = self
        } else {
            // Fallback on earlier versions
        }
        navigationController.presentationController?.delegate = self

    }
}

2) फिर से रूट व्यू कंट्रोलर में, आप बताएं कि जब आप इसका प्रेजेंटेशन व्यू कंट्रोलर डिसमिस हो जाएगा तो आप क्या करेंगे

public func presentationControllerDidDismiss(
  _ presentationController: UIPresentationController)
{
    print("presentationControllerDidDismiss")
}

1) प्रेजेंटेशन व्यू कंट्रोलर में, जब आप इस चित्र में कैंसिल या सेव बटन को हिट करते हैं। नीचे कोड कहा जाएगा

self.dismiss(animated: true) {
        self.presentationController?.delegate?.presentationControllerDidDismiss?(self.presentationController!)
    }

यहाँ छवि विवरण दर्ज करें


1
क्या यह आवश्यक है कि नेविगेशन के लिए Controller.topViewController को प्रेजेंटेशन में देखें। मुझे लगता है यह नहीं है
Fitsyu

मैं रद्द करें बटन चाइल्ड वीसी से खारिज करने के बाद माता-पिता के वीसी में डेटा कैसे लोड कर सकता हूं?
कृष्णा मीणा

3

बर्खास्त किया जा रहा है कि UIViewController पर देखेंWillDisappear को ओवरराइड करें। यह आपको isBeingDismissedबूलियन ध्वज के माध्यम से बर्खास्तगी के लिए सचेत करेगा ।

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if isBeingDismissed {
        print("user is dismissing the vc")
    }
}

** यदि उपयोगकर्ता नीचे स्वाइप करने के माध्यम से आधा है और कार्ड को वापस ऊपर स्वाइप करता है, तो यह अभी भी खारिज होने के रूप में पंजीकृत होगा, भले ही कार्ड खारिज न हो। लेकिन यह एक किनारे का मामला है जिसकी आपको परवाह नहीं है।


क्या के बारे मेंself.dismiss(animated: Bool, completion: (() -> Void)?)
iGhost

-1

किसी को प्रस्तुत दृश्य नियंत्रक के लिए पहुँच नहीं है, तो वे सिर्फ दृश्य नियंत्रक पेश करने में निम्न विधि ओवरराइड और बदल सकते हैं modalPresentationStyleकरने के लिए fullScreenया रणनीतियों में से एक इस दृष्टिकोण के साथ ऊपर उल्लेख जोड़ सकते हैं

 override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
    if let _ = viewControllerToPresent as? TargetVC {
        viewControllerToPresent.modalPresentationStyle = .fullScreen
    }
    super.present(viewControllerToPresent, animated: flag, completion: completion)
}

यदि प्रस्तुत दृश्य नियंत्रक नेविगेशन नियंत्रक है और आप रूट नियंत्रक की जांच करना चाहते हैं, तो उपरोक्त स्थिति को बदल सकते हैं

if let _ = (viewControllerToPresent as? UINavigationController)?.viewControllers.first as? TargetVC {
   viewControllerToPresent.modalPresentationStyle = .fullScreen
}

-2

यदि आपने फुलस्क्रीन में मोडलप्रेशनेंटेशन स्टाइल का उपयोग किया है, तो नियंत्रक का व्यवहार हमेशा की तरह वापस आ जाता है।

ConsultarController नियंत्रककॉंसलर = this.Storyboard.InstantiateViewController ("ConsultarController") ConsultarController के रूप में; नियंत्रककंसुल्टार ।मॉडलप्रदर्शन वेंटीलेशन = UIModalPresentationStyle.FullScreen; यह .NavigationController.PushViewController (नियंत्रककंसलर, सच);


मौजूदा उत्तरों को दोहराता है।
मैट

-3

मेरे दृष्टिकोण से, Apple को सेट नहीं करना चाहिए pageSheetडिफ़ॉल्ट हैmodalPresentationStyle

मैं fullScreenशैली को वापस डिफ़ॉल्ट रूप से लाना चाहूंगाswizzling

ऐशे ही:

private func _swizzling(forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {
    if let originalMethod = class_getInstanceMethod(forClass, originalSelector),
       let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) {
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
}

extension UIViewController {

    static func preventPageSheetPresentationStyle () {
        UIViewController.preventPageSheetPresentation
    }

    static let preventPageSheetPresentation: Void = {
        if #available(iOS 13, *) {
            _swizzling(forClass: UIViewController.self,
                       originalSelector: #selector(present(_: animated: completion:)),
                       swizzledSelector: #selector(_swizzledPresent(_: animated: completion:)))
        }
    }()

    @available(iOS 13.0, *)
    private func _swizzledPresent(_ viewControllerToPresent: UIViewController,
                                        animated flag: Bool,
                                        completion: (() -> Void)? = nil) {
        if viewControllerToPresent.modalPresentationStyle == .pageSheet
                   || viewControllerToPresent.modalPresentationStyle == .automatic {
            viewControllerToPresent.modalPresentationStyle = .fullScreen
        }
        _swizzledPresent(viewControllerToPresent, animated: flag, completion: completion)
    }
}

और फिर इस लाइन को अपने पास रखें AppDelegate

UIViewController.preventPageSheetPresentationStyle()

1
यह सरल है लेकिन मैं इससे सहमत नहीं हो सकता। यह आईओएस 13 वर्ष की अनाज आप कर रहे हैं के खिलाफ जाता है hacky है और, बात करने के लिए और अधिक, माना जाता आईओएस 13. हम से प्रतिक्रिया एप्पल की उम्मीद में "कार्ड" प्रस्तुतियों उपयोग करने के लिए नहीं है "उसके चारों ओर काम"; यह "उस पर चढ़ जाओ"।
मैट

आपकी बात से सहमत, यह समाधान कार्ड प्रस्तुति शैली का उपयोग करने में मदद नहीं करता है क्योंकि Apple हमें प्रोत्साहित करता है। हालाँकि, इसे डिफ़ॉल्ट शैली के रूप में सेट करना कहीं न कहीं कोड गलती की मौजूदा लाइनों को presentingViewControllerबनाएगा क्योंकि ट्रिगर नहीं होगाviewWillAppear
जैकोब

1
हां, लेकिन जैसा कि मैंने पहले ही अपने जवाब में कहा है, यह हमेशा से ही नॉनफुलस्क्रीन प्रस्तुतियों के लिए एक मुद्दा था (जैसे कि आईपैड पर पॉपओवर और पेज / फॉर्म शीट), इसलिए यह कोई नई बात नहीं है। यह सिर्फ इतना है कि अब यह अधिक है। पर भरोसा viewWillAppearएक में था भावना हमेशा गलत। बेशक मुझे पसंद नहीं है कि Apple साथ आए और मेरे नीचे से फर्श को काटे। लेकिन जैसा कि मैं कहता हूं, हमें बस उसी के साथ रहना है और चीजों को एक नए तरीके से करना है।
मैट

मेरी परियोजना में, कुछ परिदृश्य हैं जो मुझे नहीं पता कि एक दृश्य नियंत्रक (कहा जाता है presentedController) कहां प्रस्तुत किया गया है और न ही यह पता है कि वास्तव में क्या है presentingViewController। उदाहरण के लिए: कुछ मामलों में मुझे उपयोग करना है UIViewController.topMostViewController()जो मुझे वर्तमान विंडो पर शीर्ष सबसे अधिक दृश्य नियंत्रक लौटाता है। इसलिए मैं viewWillAppearअपने विचार नियंत्रकों के लिए सही व्यवहार (ताज़ा डेटा, UI) करने के लिए वर्तमान व्यवहार रखने के लिए स्विज़लिंग करना चाहूंगा । यदि आपके पास कोई संकल्प है, तो कृपया मदद करें।
जेकोब

खैर, मैं अपने जवाब के अंत में जिस समाधान को जोड़ता हूं, वह हल करने के लिए काम करता है, मुझे विश्वास है। प्रस्तुति के समय कॉन्फ़िगर करने के लिए कुछ काम लगते हैं, लेकिन मूल रूप से यह गारंटी देता है कि प्रस्तुतकर्ता (अलर्ट के एक प्रस्तुतकर्ता सहित) प्रस्तुत दृश्य नियंत्रक खारिज होने पर सुनता है।
मैट

-6

यह presentingViewController.viewWillAppear कॉल करने के लिए सरल नहीं होगा? खारिज कर रहा है?

self.presentingViewController?.viewWillAppear(false)
self.dismiss(animated: true, completion: nil)

4
यह तुम्हारा कहना नहीं है।
मैट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.