किसी अन्य दृश्य नियंत्रक में सबव्यू के रूप में व्यू कंट्रोलर जोड़ना


81

मुझे इस समस्या के लिए कुछ पोस्ट मिले हैं लेकिन उनमें से किसी ने भी मेरे मुद्दे को हल नहीं किया है।

जैसा मैं कहूं ..

  1. ViewControllerA
  2. ViewControllerB

मैंने ViewControllerA को ViewControllerA में एक सबव्यू के रूप में जोड़ने की कोशिश की लेकिन, यह " fatal error: unexpectedly found nil while unwrapping an Optional value" की तरह एक त्रुटि फेंक रहा है ।

नीचे कोड है ...

ViewControllerA

var testVC: ViewControllerB = ViewControllerB();

override func viewDidLoad()
{
    super.viewDidLoad()
    self.testVC.view.frame = CGRectMake(0, 0, 350, 450);
    self.view.addSubview(testVC.view);
    // Do any additional setup after loading the view.
}

ViewControllerB इसमें एक लेबल के साथ एक साधारण स्क्रीन है।

ViewControllerB

 @IBOutlet weak var test: UILabel!

override func viewDidLoad() {
    super.viewDidLoad()
    test.text = "Success" // Throws ERROR here "fatal error: unexpectedly found nil while unwrapping an Optional value"
}

संपादित करें

उपयोगकर्ता के उत्तर से सुझाए गए समाधान के साथ, ViewControllerA में ViewControllerB स्क्रीन बंद हो रहा है। ग्रे बॉर्डर वह फ्रेम है जिसे मैंने सबव्यू के लिए बनाया है। यहाँ छवि विवरण दर्ज करें

जवाबों:


178

टिप्पणियों के एक जोड़े:

  1. जब आप दूसरे व्यू कंट्रोलर को इंस्टेंट करते हैं, तो आप कॉल कर रहे हैं ViewControllerB()। यदि वह दृश्य नियंत्रक प्रोग्रामिक रूप से अपना दृश्य बनाता है (जो कि असामान्य है) जो ठीक होगा। लेकिन IBOutletपता चलता है कि इस दूसरे दृश्य नियंत्रक के दृश्य को इंटरफ़ेस बिल्डर में परिभाषित किया गया था, लेकिन कॉल करके ViewControllerB(), आप स्टोरीबोर्ड को उस दृश्य को त्वरित करने और सभी आउटलेट को हुक करने का मौका नहीं दे रहे हैं। इस प्रकार निहित रूप से अलिखित UILabelहै nil, जिसके परिणामस्वरूप आपका त्रुटि संदेश है।

    इसके बजाय, आप अपने डेस्टिनेशन व्यू कंट्रोलर को इंटरफेस बिल्डर में एक "स्टोरीबोर्ड आईडी" देना चाहते हैं और फिर आप instantiateViewController(withIdentifier:)इसे इंस्टेंट करने के लिए उपयोग कर सकते हैं (और सभी आईबी आउटलेट को हुक कर सकते हैं)। स्विफ्ट 3 में:

    let controller = storyboard!.instantiateViewController(withIdentifier: "scene storyboard id")
    

    अब आप इस का उपयोग कर सकते controllerहै view

  2. लेकिन अगर आप वास्तव में करना चाहते हैं addSubview(यानी आप अगले दृश्य में परिवर्तन नहीं कर रहे हैं), तो आप "व्यू कंट्रोलर कंट्रोल" नामक अभ्यास में संलग्न हैं। आप केवल करना नहीं चाहते हैं addSubview। आप कुछ अतिरिक्त कंटेनर व्यू कंट्रोलर कॉल करना चाहते हैं, जैसे:

    let controller = storyboard!.instantiateViewController(withIdentifier: "scene storyboard id")
    addChild(controller)
    controller.view.frame = ...  // or, better, turn off `translatesAutoresizingMaskIntoConstraints` and then define constraints for this subview
    view.addSubview(controller.view)
    controller.didMove(toParent: self)
    

    यह क्यों addChild(पहले कहा जाता है addChildViewController) और didMove(toParent:)(पहले कहा जाता है didMove(toParentViewController:)) आवश्यक हैं, इसके बारे में अधिक जानकारी के लिए , WWDC 2011 के वीडियो # 102 - UIViewController कंटेनर को लागू करना देखें । संक्षेप में, आपको यह सुनिश्चित करने की आवश्यकता है कि आपका दृश्य नियंत्रक पदानुक्रम आपके दृश्य पदानुक्रम के साथ सिंक में रहता है, और यह सुनिश्चित करने के लिए ये कॉल addChildऔर didMove(toParent:)सुनिश्चित करता है।

    कंट्रोलर प्रोग्रामिंग गाइड में कस्टम कंटेनर व्यू कंट्रोलर बनाना भी देखें


वैसे, उपरोक्त दिखाता है कि यह कैसे प्रोग्रामेटिक रूप से करना है। यदि आप इंटरफ़ेस बिल्डर में "कंटेनर दृश्य" का उपयोग करते हैं तो यह वास्तव में बहुत आसान है।

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

फिर आपको इनमें से किसी से संबंधित कॉल के बारे में चिंता करने की ज़रूरत नहीं है, और इंटरफ़ेस बिल्डर आपके लिए इसका ध्यान रखेगा।

स्विफ्ट 2 कार्यान्वयन के लिए, इस उत्तर का पिछला संशोधन देखें ।


1
विस्तृत विवरण के लिए धन्यवाद। जब मैं जोड़ने की कोशिश की ViewControllerBकरने के लिए ViewControllerA, ViewControllerBस्क्रीन बंद जा रहा है। मैंने अपना पोस्ट सिम्युलेटर के स्क्रीनशॉट के साथ संपादित किया है।
सर्जन सिम्हा

यह संभव है। इसीलिए, मेरे उदाहरण में, मैंने frameमैन्युअल रूप से सेट किया है । या यदि आप बंद कर देते हैं translatesFrameIntoConstraints(या जो कुछ भी कहा जाता है), और आप संभवतः प्रोग्राम को भी कसकर जोड़ सकते हैं। लेकिन अगर आप सबव्यू जोड़ रहे हैं, तो आप इसके फ्रेम, एक तरह से या दूसरे को स्थापित करने के लिए ज़िम्मेदार हैं, जैसे आप सभी प्रोग्रामेटिक रूप से जोड़े गए साक्षात्कारों के लिए हैं।
रोब

1
यह है कि आप कैसे जोड़ सकते हैंcontroller.view.frame = UIScreen.mainScreen().bounds
Codetard

3
नियंत्रक नियंत्रक को देखने के दौरान, आपको वास्तव में सुपरवाइवे का संदर्भ लेना चाहिए, न कि स्क्रीन का। सच कहूँ तो, अब जब हमने स्क्रीन मल्टीटास्किंग को विभाजित किया है, तो स्क्रीन के संदर्भ में ऐसा कुछ भी करना जो आम तौर पर असंगत है।
रोब

1
@ हनी - मुझे यकीन नहीं है कि आप क्या मतलब है "यह सब मानते हैं कि viewController का दृश्य पेरेंटव्यूकंट्रोलर का सिर्फ एक सबव्यू है"। परिभाषा के अनुसार, जब आप करते हैं addSubview, तो बाल नियंत्रक का मूल दृश्य उस दृश्य का एक उप-भाग होता है, जिसे आपने इसे जोड़ा था। आप सभी बच्चे नियंत्रक के मूल दृश्य और उस दृश्य के बीच में बाधाओं को जोड़ते हैं, जिसे आपने इसे एक उप-भाग के रूप में जोड़ा है।
राब

50

रोब के लिए धन्यवाद। अपने दूसरे अवलोकन के लिए विस्तृत वाक्यविन्यास जोड़ना:

let controller:MyView = self.storyboard!.instantiateViewControllerWithIdentifier("MyView") as! MyView
controller.ANYPROPERTY=THEVALUE // If you want to pass value
controller.view.frame = self.view.bounds
self.view.addSubview(controller.view)
self.addChildViewController(controller)
controller.didMoveToParentViewController(self)

और व्यूकंट्रोलर को हटाने के लिए:

self.willMoveToParentViewController(nil)
self.view.removeFromSuperview()
self.removeFromParentViewController() 

6
BTW, कॉल addChildViewControllerकरते समय , कॉल करें willMoveToParentViewControlleraddChildViewControllerकॉल करने पर यह आपके लिए कॉल करेगा। देखें जोड़ा जा रहा है और एक बच्चे को दूर करने में देखें नियंत्रक iOS के लिए गाइड प्रोग्रामिंग। इस कारण से, मैं व्यक्तिगत रूप से addChildViewControllerइसे तुरंत इंस्टेंट करने के तुरंत बाद कॉल करूंगा , लेकिन इसे कॉन्फ़िगर करने से पहले।
रॉब

1
जब आप नियंत्रक करते हैं ।ANYPROPERTY = THEVALUE..I यह अनुमान लगा रहा है कि AnyProperty चाइल्डव्यूकंट्रोलर में परिभाषित है। मैंने इसकी कोशिश की और यह मुझे एक त्रुटि दे रहा था। किसी भी विचार को कैसे सुधारना है।
अनुज अरोरा

@ अनुज अरोड़ा आपका अनुमान सही है। AnyPROPERTY को चाइल्ड व्यूकंट्रोलर में परिभाषित किया गया है। आप किसी भी चेक को चाइल्ड व्यूकंट्रोलर में देख सकते हैं लेकिन व्यूडायड में ViewDidLoad में नहीं।
सुनीता

7
This code will work for Swift 4.2.

let controller:SecondViewController = 
self.storyboard!.instantiateViewController(withIdentifier: "secondViewController") as! 
SecondViewController
controller.view.frame = self.view.bounds;
self.view.addSubview(controller.view)
self.addChild(controller)
controller.didMove(toParent: self)

है फोन willMoveaddChildआपके लिए वही है। willMove प्रलेखन देखें । तो अनुक्रम है (1) addChild; (2) चाइल्ड वीसी के दृष्टिकोण को कॉन्फ़िगर करें और इसे पदानुक्रम में जोड़ें; और (3) कॉलdidMove(toParent:)
रोब

4

ViewController जोड़ें और निकालें के लिए

 var secondViewController :SecondViewController?

  // Adding 
 func add_ViewController() {
    let controller  = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController")as! SecondViewController
    controller.view.frame = self.view.bounds
    self.view.addSubview(controller.view)
    self.addChild(controller)
    controller.didMove(toParent: self)
    self.secondViewController = controller
}

// Removing
func remove_ViewController(secondViewController:SecondViewController?) {
    if secondViewController != nil {
        if self.view.subviews.contains(secondViewController!.view) {
             secondViewController!.view.removeFromSuperview()
        }
        
    }
}

फोन मत करो willMove। जैसा कि willMove प्रलेखन कहता है, वह addChildआपके लिए करता है।
रोब

3

func callForMenuView () {

    if(!isOpen)

    {
        isOpen = true

        let menuVC : MenuViewController = self.storyboard!.instantiateViewController(withIdentifier: "menu") as! MenuViewController
        self.view.addSubview(menuVC.view)
        self.addChildViewController(menuVC)
        menuVC.view.layoutIfNeeded()

        menuVC.view.frame=CGRect(x: 0 - UIScreen.main.bounds.size.width, y: 0, width: UIScreen.main.bounds.size.width-90, height: UIScreen.main.bounds.size.height);

        UIView.animate(withDuration: 0.3, animations: { () -> Void in
            menuVC.view.frame=CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width-90, height: UIScreen.main.bounds.size.height);
    }, completion:nil)

    }else if(isOpen)
    {
        isOpen = false
      let viewMenuBack : UIView = view.subviews.last!

        UIView.animate(withDuration: 0.3, animations: { () -> Void in
            var frameMenu : CGRect = viewMenuBack.frame
            frameMenu.origin.x = -1 * UIScreen.main.bounds.size.width
            viewMenuBack.frame = frameMenu
            viewMenuBack.layoutIfNeeded()
            viewMenuBack.backgroundColor = UIColor.clear
        }, completion: { (finished) -> Void in
            viewMenuBack.removeFromSuperview()

        })
    }

2

रोब के लिए धन्यवाद, अद्यतन स्विफ्ट 4.2 वाक्यविन्यास

let controller:WalletView = self.storyboard!.instantiateViewController(withIdentifier: "MyView") as! WalletView
controller.view.frame = self.view.bounds
self.view.addSubview(controller.view)
self.addChild(controller)
controller.didMove(toParent: self)

1
मेरे लिए "नियंत्रक.व्यू = स्व.व्यू.बाउंड" के बजाय "कंट्रोलर.व्यू.फॉर्म्स" का उपयोग करें।
सबरीना

फोन मत करो willMove। जैसा कि willMove प्रलेखन कहता है, वह addChildआपके लिए करता है। दो बार बुलाने से कोई अच्छा नहीं होता।
रोब

0

कृपया कस्टम कंटेनर दृश्य नियंत्रक को लागू करने के लिए आधिकारिक दस्तावेज भी देखें:

https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html#//apple_ref/doc/uid/TP40007457-CH11-SW1

इस दस्तावेज़ में हर निर्देश के लिए बहुत अधिक विस्तृत जानकारी है और यह भी वर्णन करता है कि संक्रमण कैसे जोड़ा जाए।

स्विफ्ट 3 में अनुवादित:

func cycleFromViewController(oldVC: UIViewController,
               newVC: UIViewController) {
   // Prepare the two view controllers for the change.
   oldVC.willMove(toParentViewController: nil)
   addChildViewController(newVC)

   // Get the start frame of the new view controller and the end frame
   // for the old view controller. Both rectangles are offscreen.r
   newVC.view.frame = view.frame.offsetBy(dx: view.frame.width, dy: 0)
   let endFrame = view.frame.offsetBy(dx: -view.frame.width, dy: 0)

   // Queue up the transition animation.
   self.transition(from: oldVC, to: newVC, duration: 0.25, animations: { 
        newVC.view.frame = oldVC.view.frame
        oldVC.view.frame = endFrame
    }) { (_: Bool) in
        oldVC.removeFromParentViewController()
        newVC.didMove(toParentViewController: self)
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.