एक दृश्य को देखते हुए, मैं इसका व्यू-कंट्रोलर कैसे प्राप्त करूं?


141

मैं एक के लिए एक सूचक है UIView। मैं इसकी पहुँच कैसे करूँ UIViewController? [self superview]एक और है UIView, लेकिन UIViewControllerठीक नहीं है?


मुझे लगता है कि इस धागे के उत्तर हैं: iPhone पर UIView से UIViewController प्राप्त करें?
उशोक्स

मूल रूप से, मैं अपने विचारकंट्रोलर के दृष्टिकोण को कॉल करने का प्रयास कर रहा हूं क्योंकि मेरे विचार को खारिज कर दिया जा रहा है। दृश्य को उस दृश्य से खारिज किया जा रहा है जो एक टैप का पता लगा रहा है और कॉल कर रहा है [सेल्फ रिमूवफ्रॉमसुपरविए]; व्यूकंट्रोलर viewWillAppear / WillDisappear / DidAppear / DidDisappear को ही नहीं कह रहा है।
महबूदज़

मेरा मतलब है कि मैं viewWillDisappear को कॉल करने की कोशिश कर रहा हूं क्योंकि मेरा विचार खारिज किया जा रहा है।
महबूदज़

जवाबों:


42

हां, वह superviewदृश्य है जिसमें आपका दृश्य शामिल है। आपके विचार को यह नहीं पता होना चाहिए कि वास्तव में इसका दृश्य नियंत्रक क्या है, क्योंकि यह MVC सिद्धांतों को तोड़ देगा।

दूसरी ओर, नियंत्रक जानता है कि यह किस दृश्य के लिए जिम्मेदार है ( self.view = myView), और आमतौर पर, यह दृश्य नियंत्रक से निपटने के लिए तरीकों / घटनाओं को दर्शाता है।

आमतौर पर, आपके दृष्टिकोण के लिए एक संकेतक के बजाय, आपके पास अपने नियंत्रक के लिए एक संकेतक होना चाहिए, जो बदले में या तो कुछ नियंत्रित तर्क को निष्पादित कर सकता है, या इसके दृश्य के लिए कुछ पास कर सकता है।


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

आप दृश्य के बारे में कुछ हद तक सही हैं, इसके माता-पिता के बारे में जानना, यह सुपर-स्पष्ट डिज़ाइन निर्णय नहीं है, लेकिन यह पहले से ही कुछ कार्यों को करने के लिए स्थापित है, सीधे पर्यवेक्षी सदस्य चर का उपयोग करके (माता-पिता की जाँच करें, माता-पिता से हटाएं, आदि)। PureMVC के साथ हाल ही में काम करने के बाद, मैं डिज़ाइन एब्सट्रैक्शन के बारे में थोड़ा और अधिक नट-पिक बन गया हूं :) मैं iPhone के UIView और UIViewController कक्षाओं और PureMVC के व्यू और मध्यस्थ वर्गों के बीच समानांतर बनाऊंगा - अधिकांश समय, व्यू क्लास की आवश्यकता नहीं है इसके MVC हैंडलर / इंटरफ़ेस (UIViewController / Mediator) के बारे में जानें।
दिमितर दिमित्रोव

9
मुख्य शब्द: "सबसे"।
ग्लेन मेनार्ड

278

के लिए UIResponderप्रलेखन से nextResponder:

UIResponder वर्ग डिफ़ॉल्ट रूप से nil लौटने के बजाय, अगले प्रत्युत्तर को स्वचालित रूप से संग्रहीत या सेट नहीं करता है। अगले उत्तरदाता को सेट करने के लिए उपवर्गों को इस विधि को ओवरराइड करना चाहिए। UIView इस विधि को लागू करता है UIViewController वस्तु को वापस करके जो इसे प्रबंधित करता है (यदि इसमें एक है) या इसका पर्यवेक्षण (यदि यह नहीं है) ; UIViewController अपने दृष्टिकोण के पर्यवेक्षण को वापस करके विधि को लागू करता है; UIWindow एप्लिकेशन ऑब्जेक्ट लौटाता है, और UIApplication nil देता है।

इसलिए, यदि आप किसी दृश्य को nextResponderतब तक पुनरावृत्ति करते हैं, जब तक वह टाइप का न हो UIViewController, तो आपके पास किसी भी दृश्य का जनक दृश्य नियंत्रक है।

ध्यान दें कि इसमें अभी भी पैरेंट व्यू कंट्रोलर नहीं हो सकता है। लेकिन केवल अगर दृश्य एक व्यू कॉन्ट्रॉलर के दृश्य पदानुक्रम का हिस्सा नहीं है।

स्विफ्ट 3 और स्विफ्ट 4.1 एक्सटेंशन:

extension UIView {
    var parentViewController: UIViewController? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder?.next
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}

स्विफ्ट 2 एक्सटेंशन:

extension UIView {
    var parentViewController: UIViewController? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder!.nextResponder()
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}

उद्देश्य-सी श्रेणी:

@interface UIView (mxcl)
- (UIViewController *)parentViewController;
@end

@implementation UIView (mxcl)
- (UIViewController *)parentViewController {
    UIResponder *responder = self;
    while ([responder isKindOfClass:[UIView class]])
        responder = [responder nextResponder];
    return (UIViewController *)responder;
}
@end

यह मैक्रो श्रेणी प्रदूषण से बचा जाता है:

#define UIViewParentController(__view) ({ \
    UIResponder *__responder = __view; \
    while ([__responder isKindOfClass:[UIView class]]) \
        __responder = [__responder nextResponder]; \
    (UIViewController *)__responder; \
})

बस एक बात: यदि आप श्रेणी प्रदूषण के बारे में चिंता करते हैं, तो इसे स्थूल के बजाय एक स्थिर कार्य के रूप में परिभाषित करें। इसके अलावा क्रूर टाइपकास्ट खतरनाक है, और उसके ऊपर मैक्रो सही नहीं हो सकता है लेकिन निश्चित नहीं है।
मोजुबा

मैक्रो काम करता है, मैं केवल मैक्रो संस्करण का व्यक्तिगत रूप से उपयोग करता हूं। मैं बहुत सारी परियोजनाएं शुरू करता हूं और एक हेडर होता है जिसे मैं इन मैक्रो यूटिलिटी फंक्शंस के झुंड के साथ हर जगह छोड़ देता हूं। मुझे समय बचाता है। यदि आपको मैक्रोज़ पसंद नहीं हैं, तो आप इसे एक फ़ंक्शन में रूपांतरित कर सकते हैं, लेकिन स्थैतिक कार्य थकाऊ लगते हैं, क्योंकि तब आपको हर उस फ़ाइल में एक डालनी होगी जिसका आप उपयोग करना चाहते हैं। इसके बजाय लगता है जैसे आप एक हेडर में घोषित एक गैर स्थिर समारोह चाहते हैं और एक में कहीं परिभाषित किया गया है?
mxcl

कुछ प्रतिनिधियों या सूचनाओं से बचने के लिए अच्छा लगा। धन्यवाद!
फेरन मायलिनच

आपको इसे विस्तार करना चाहिए UIResponder;) बहुत ही एडिटिंग पोस्ट।
स्कॉट्टीब्लाड्स

32

एक लाइन में @andrey उत्तर ( स्विफ्ट 4.1 में परीक्षण किया गया ):

extension UIResponder {
    public var parentViewController: UIViewController? {
        return next as? UIViewController ?? next?.parentViewController
    }
}

उपयोग:

 let vc: UIViewController = view.parentViewController

parentViewControllerपरिभाषित नहीं किया जा सकता है publicयदि एक्सटेंशन आपके साथ एक ही फ़ाइल में है जिसे UIViewआप इसे सेट कर सकते हैं fileprivate, यह संकलन करता है लेकिन यह काम नहीं करता है! 😐

यह ठीक काम कर रहा है। मैंने इसका उपयोग सामान्य हेडर .xib फ़ाइल के अंदर बैक बटन एक्शन के लिए किया है।
मैकडोनल_11

23

केवल डिबग उद्देश्यों के लिए, आप _viewDelegateउनके दृश्य नियंत्रकों को प्राप्त करने के लिए विचारों पर कॉल कर सकते हैं । यह निजी एपीआई है, इसलिए ऐप स्टोर के लिए सुरक्षित नहीं है, लेकिन डिबगिंग के लिए यह उपयोगी है।

अन्य उपयोगी तरीके:

  • _viewControllerForAncestor- पहला कंट्रोलर मिलता है जो सुपरवाइज चेन में एक व्यू को मैनेज करता है। (साभार n00neimp0rtant)
  • _rootAncestorViewController - पूर्वज नियंत्रक प्राप्त करें जिसका दृश्य पदानुक्रम वर्तमान में विंडो में सेट किया गया है।

यह ठीक है कि मैं इस सवाल पर क्यों आया। जाहिरा तौर पर, 'नेक्स्टप्रोंडर' यही काम करता है, लेकिन मैं उस अंतर्दृष्टि की सराहना करता हूं जो यह उत्तर प्रदान करती है। मैं एमवीसी को समझता हूं और पसंद करता हूं, लेकिन डिबगिंग एक अलग जानवर है!
mbm29414 17

4
ऐसा प्रतीत होता है कि यह केवल दृश्य नियंत्रक के मुख्य दृश्य पर काम करता है, इसके किसी भी साक्षात्कार पर नहीं। _viewControllerForAncestorजब तक कि यह पहले एक व्यू कंट्रोलर से संबंधित न हो जाए, तब तक पर्यवेक्षणियों को आगे बढ़ाएगा।
n00neimp0rtant

धन्यवाद @ n00neimp0rtant! मैं इस जवाब को बढ़ा रहा हूं ताकि लोग आपकी टिप्पणी देखें।
बरोबर

अधिक विधियों से अपडेट का उत्तर दें।
लियो नाटन

1
यह डीबगिंग के समय मददगार होता है।
evanchin

10

UIViewController का संदर्भ लेने के लिए UIView होने के कारण, आप UIResponder (जो UIView और UIViewController के लिए सुपर क्लास है) का विस्तार कर सकते हैं, जो उत्तरदाता श्रृंखला के माध्यम से ऊपर जाने की अनुमति देता है और इस प्रकार UIViewController (अन्यथा शून्य)।

extension UIResponder {
    func getParentViewController() -> UIViewController? {
        if self.nextResponder() is UIViewController {
            return self.nextResponder() as? UIViewController
        } else {
            if self.nextResponder() != nil {
                return (self.nextResponder()!).getParentViewController()
            }
            else {return nil}
        }
    }
}

//Swift 3
extension UIResponder {
    func getParentViewController() -> UIViewController? {
        if self.next is UIViewController {
            return self.next as? UIViewController
        } else {
            if self.next != nil {
                return (self.next!).getParentViewController()
            }
            else {return nil}
        }
    }
}

let vc = UIViewController()
let view = UIView()
vc.view.addSubview(view)
view.getParentViewController() //provide reference to vc

4

स्विफ्ट 3 में तेज और सामान्य तरीका:

extension UIResponder {
    func parentController<T: UIViewController>(of type: T.Type) -> T? {
        guard let next = self.next else {
            return nil
        }
        return (next as? T) ?? next.parentController(of: T.self)
    }
}

//Use:
class MyView: UIView {
    ...
    let parentController = self.parentController(of: MyViewController.self)
}

2

यदि आप कोड से परिचित नहीं हैं और आप ViewController को दिए गए दृश्य के अनुरूप देखना चाहते हैं, तो आप कोशिश कर सकते हैं:

  1. डिबग में ऐप चलाएं
  2. स्क्रीन पर नेविगेट करें
  3. प्रारंभ निरीक्षक देखें
  4. उस दृश्य को पकड़ो जिसे आप ढूंढना चाहते हैं (या एक बच्चा दृश्य और भी बेहतर)
  5. दाएँ फलक से पता मिलता है (उदाहरण 0x7fe523bd3000)
  6. डिबग कंसोल में कमांड लिखना शुरू करें:
    po (UIView *) 0x7fe523bd3000
    पो [[(UIView *) 0x7fe523bd3000 अगला छापेदार]
    पो [[(उविव्यू *) ०xb फै ५२३ डब्लू ३००० नेक्स्टपोन्डर] नेक्स्टपोन्डर]
    पो [[[(यूआईवाईईवाई *) ० एक्स 23 एफ ५२३ डब्लू ३००० नेक्स्टपोन्डर] नेक्स्टपोन्डर] नेक्स्टपोन्डर]
    ...

ज्यादातर मामलों में आपको UIView मिलेगा, लेकिन समय-समय पर UIViewController आधारित कक्षा होगी।


1

मुझे लगता है कि आप नल को व्यू कंट्रोलर को प्रोपेगेट कर सकते हैं और इसे हैंडल कर सकते हैं। यह अधिक स्वीकार्य दृष्टिकोण है। किसी व्यू कंट्रोलर को उसके व्यू से एक्सेस करने के लिए, आपको व्यू कंट्रोलर के संदर्भ को बनाए रखना चाहिए, क्योंकि कोई दूसरा तरीका नहीं है। इस थ्रेड को देखें, यह मदद कर सकता है: व्यू कंट्रोलर को किसी व्यू से एक्सेस करना


यदि आपके पास कुछ दृश्य हैं, और कोई भी सभी दृश्य बंद कर देता है, और आपको viewWillDisappear को कॉल करने की आवश्यकता है, तो उस दृश्य के लिए टैप का पता लगाना आसान नहीं होगा, जो व्यू कंट्रोलर को टैप सौंपने की तुलना में और व्यू कंट्रोलर की जाँच कर सकता है सभी विचारों को देखने के लिए कि किस पर टैप किया गया था?
महबूदज़

0

स्विफ्ट 3.0 के लिए अधिक प्रकार का सुरक्षित कोड

extension UIResponder {
    func owningViewController() -> UIViewController? {
        var nextResponser = self
        while let next = nextResponser.next {
            nextResponser = next
            if let vc = nextResponser as? UIViewController {
                return vc
            }
        }
        return nil
    }
}

0

काश, यह असंभव है जब तक आप दृश्य को उप-वर्ग नहीं करते हैं और इसे एक उदाहरण संपत्ति या एक जैसे प्रदान करते हैं जो दृश्य में दृश्य में जोड़े जाने पर दृश्य नियंत्रक संदर्भ को इसके अंदर संग्रहीत करता है ...

ज्यादातर मामलों में - इस पोस्ट की मूल समस्या के आस-पास काम करना बहुत आसान है क्योंकि अधिकांश व्यू कंट्रोलर प्रोग्रामर के लिए अच्छी तरह से ज्ञात संस्थाएँ हैं, जो व्यूकंट्रोलर के व्यू में किसी भी सबव्यू को जोड़ने के लिए जिम्मेदार थे;; कभी भी उस संपत्ति को जोड़ने की जहमत नहीं उठाई।


0

थोड़ा देर से, लेकिन यहां एक ऐसा विस्तार है जो आपको व्यूकंट्रोलर सहित किसी भी प्रकार का उत्तर खोजने में सक्षम बनाता है।

extension NSObject{
func findNext(type: AnyClass) -> Any{
    var resp = self as! UIResponder

    while !resp.isKind(of: type.self) && resp.next != nil
    {
        resp = resp.next!
    }

    return resp
  }                       
}

-1

यदि आप एक ब्रेकपॉइंट सेट करते हैं, तो आप दृश्य पदानुक्रम को प्रिंट करने के लिए डीबगर में पेस्ट कर सकते हैं:

po [[UIWindow keyWindow] recursiveDescription]

आपको उस झंझट में कहीं न कहीं अपने विचार के माता-पिता को खोजने में सक्षम होना चाहिए :)


recursiveDescriptionकेवल दृश्य पदानुक्रम को प्रिंट करता है, न कि नियंत्रकों को देखता है।
एलन ज़ीनो

1
उस से आप देख सकते हैं कि व्यू कंटोलर @AlanZeino
अक्षय
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.