स्टोरीबोर्ड में बाहरी xib फ़ाइल से दृश्य लोड करें


131

मैं एक स्टोरीबोर्ड में कई व्यू-कंट्रोलर के दृश्य का उपयोग करना चाहता हूं। इस प्रकार, मैंने बाहरी एक्सिब में दृश्य को डिजाइन करने के बारे में सोचा ताकि हर दृश्यमंत्री में परिवर्तन दिखाई दे। लेकिन एक स्टोरीबोर्ड में कोई बाहरी एक्सब से दृश्य कैसे लोड कर सकता है और क्या यह संभव है? यदि यह मामला नहीं है, तो अन्य विकल्प क्या स्थिति के अनुकूल हैं?



1
इस वीडियो को देखें: youtube.com/watch?v=o3MawJVxTgk
malhobayyeb

जवाबों:


132

मेरा पूरा उदाहरण यहां है , लेकिन मैं नीचे एक सारांश प्रदान करूंगा।

ख़ाका

अपने प्रोजेक्ट में एक ही नाम के साथ एक .swift और .xib फ़ाइल जोड़ें। .Xib फ़ाइल में आपके कस्टम दृश्य लेआउट (अधिमानतः ऑटो लेआउट की कमी का उपयोग करके) होता है।

Xib फ़ाइल के स्वामी की स्विफ्ट फ़ाइल बनाएं।

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

निम्न कोड को .swift फ़ाइल में जोड़ें और .xib फ़ाइल से आउटलेट्स और क्रियाओं को हुक करें।

import UIKit
class ResuableCustomView: UIView {

    let nibName = "ReusableCustomView"
    var contentView: UIView?

    @IBOutlet weak var label: UILabel!
    @IBAction func buttonTap(_ sender: UIButton) {
        label.text = "Hi"
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        guard let view = loadViewFromNib() else { return }
        view.frame = self.bounds
        self.addSubview(view)
        contentView = view
    }

    func loadViewFromNib() -> UIView? {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: nibName, bundle: bundle)
        return nib.instantiate(withOwner: self, options: nil).first as? UIView
    }
}

इसका इस्तेमाल करें

अपने स्टोरीबोर्ड में कहीं भी अपने कस्टम दृश्य का उपयोग करें। बस UIViewअपने कस्टम वर्ग के नाम में वर्ग नाम जोड़ें और सेट करें।

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


3
क्या यह ऐसा नहीं है कि loadNibNamed कॉल init (कोडर :)? मेरे पास आपके दृष्टिकोण को अनुकूलित करने के लिए एक क्रैश है।
मछुआरा

@ फ़िशमैन, यदि आप प्रोग्राम को स्टोरीबोर्ड से (बल्कि स्टोरीबोर्ड से) लोड करने की कोशिश करते हैं, तो यह क्रैश हो जाएगा क्योंकि यह वर्तमान में ए नहीं है init(frame:)। देखें इस ट्यूटोरियल अधिक जानकारी के लिए।
Sur बजे सुरगछ

7
दुर्घटनाग्रस्त होने का एक और सामान्य कारण फ़ाइल के स्वामी के लिए कस्टम दृश्य सेट नहीं करना है । मेरे उत्तर में लाल वृत्त देखें।
22 पर सुरगछ

5
हाँ, मैंने फ़ाइल स्वामी के बजाय रूट दृश्य की श्रेणी निर्धारित की थी और यह एक अनंत लूप पैदा कर रहा था।
devios1

2
View.autoresizingMask = [.flexibleWidth, .flexibleHeight] लाइन को self.addSubview (दृश्य) से पहले जोड़ने के बाद यह पूरी तरह से काम कर रहा है।
प्रमोद तपनिया

69

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

  1. अपने xib को नियंत्रित करने के लिए एक .swift फ़ाइल में एक कस्टम UIView वर्ग बनाएँ। अर्थातMyCustomClass.swift
  2. एक .xib फ़ाइल बनाएं और इसे अपनी इच्छानुसार स्टाइल करें। अर्थातMyCustomClass.xib
  3. File's Ownerअपने कस्टम वर्ग होने के लिए .xib फ़ाइल का सेट करें ( MyCustomClass)
  4. GOTCHA: .xib फ़ाइल रिक्त में अपने कस्टम दृश्य के लिए classमान (के तहत identity Inspector) छोड़ दें । इसलिए आपके कस्टम दृश्य में कोई निर्दिष्ट वर्ग नहीं होगा, लेकिन इसमें एक निर्दिष्ट फ़ाइल का स्वामी होगा।
  5. अपने आउटलेट को हुक करें जैसा कि आप आम तौर पर करते थे Assistant Editor
    • नोट: यदि आप देखते हैं तो Connections Inspectorआप देखेंगे कि आपके रेफ़रिंग आउटलेट आपके कस्टम वर्ग (यानी MyCustomClass) का संदर्भ नहीं देते हैं, बल्कि संदर्भ का File's Owner। चूँकि File's Ownerआपका कस्टम वर्ग होना निर्दिष्ट है, इसलिए आउटलेट हुक अप करेंगे और उचित कार्य करेंगे।
  6. सुनिश्चित करें कि आपके कस्टम क्लास के पास क्लास स्टेटमेंट से पहले @IBDesignable है।
  7. NibLoadableनीचे बताए गए प्रोटोकॉल के अनुरूप अपनी कस्टम क्लास बनाएं ।
    • नोट: यदि आपका कस्टम क्लास .swiftफ़ाइल नाम आपके .xibफ़ाइल नाम से अलग है , तो nibNameसंपत्ति को अपनी फ़ाइल का नाम सेट करें .xib
  8. लागू करें required init?(coder aDecoder: NSCoder)और नीचे दिए गए उदाहरण की तरह override init(frame: CGRect)कॉल करने setupFromNib()के लिए।
  9. अपने वांछित स्टोरीबोर्ड में एक UIView जोड़ें और कक्षा को अपने कस्टम वर्ग नाम (यानी MyCustomClass) के रूप में सेट करें।
  10. देखो कार्रवाई में IBDesignable के रूप में यह अपने आप में .xib खींचता है यह विस्मय और आश्चर्य है।

यहाँ वह प्रोटोकॉल है जिसे आप संदर्भित करना चाहेंगे:

public protocol NibLoadable {
    static var nibName: String { get }
}

public extension NibLoadable where Self: UIView {

    public static var nibName: String {
        return String(describing: Self.self) // defaults to the name of the class implementing this protocol.
    }

    public static var nib: UINib {
        let bundle = Bundle(for: Self.self)
        return UINib(nibName: Self.nibName, bundle: bundle)
    }

    func setupFromNib() {
        guard let view = Self.nib.instantiate(withOwner: self, options: nil).first as? UIView else { fatalError("Error loading \(self) from nib") }
        addSubview(view)
        view.translatesAutoresizingMaskIntoConstraints = false
        view.leadingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
        view.topAnchor.constraint(equalTo: self.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
        view.trailingAnchor.constraint(equalTo: self.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
        view.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
    }
}

और यहाँ एक उदाहरण है MyCustomClassकि प्रोटोकॉल को लागू करता है (.xib फ़ाइल का नाम दिया जा रहा है MyCustomClass.xib):

@IBDesignable
class MyCustomClass: UIView, NibLoadable {

    @IBOutlet weak var myLabel: UILabel!

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupFromNib()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupFromNib()
    }

}

ध्यान दें: यदि आपको गोत्र याद आता है और classआपकी .xib फ़ाइल के अंदर का मान आपके कस्टम वर्ग के लिए सेट है, तो यह स्टोरीबोर्ड में नहीं आयेगा और EXC_BAD_ACCESSजब आप ऐप चलाते हैं तो आपको एक त्रुटि मिलेगी क्योंकि यह एक अनंत लूप में फंस जाता है जिस init?(coder aDecoder: NSCoder)विधि से कॉल Self.nib.instantiateऔर फिर से कॉल किया जाता है उसका उपयोग करके कक्षा को नीब से आरंभ करने का प्रयास करना init


2
यहाँ इसके लिए एक और शानदार तरीका है, लेकिन मुझे लगता है कि ऊपर वाला अभी भी बेहतर है: मध्यम.com
बेन पैच

4
आपके द्वारा ऊपर वर्णित दृष्टिकोण पूरी तरह से काम करता है और स्टोरीबोर्ड में लाइव पूर्वावलोकन सही पर लागू होता है। यह बिल्कुल आसान है और बहुत बड़ा है!
इगोर लियोनोविच

1
FYI करें: यह समाधान, बाधा परिभाषा का उपयोग करते हुए setupFromNib(), कुछ अजीब ऑटो-लेआउट मुद्दों को ठीक करने के लिए लगता है, जिसमें ऑटो-साइज़िंग टेबल व्यू सेल हैं जिसमें XIB-created views हैं।
गैरी

1
अब तक का सबसे अच्छा उपाय! मुझे @IBDesignableअनुकूलता पसंद है। विश्वास नहीं कर सकता कि XIV या UIKit कुछ ऐसा क्यों प्रदान नहीं करता है जो कि UIView फ़ाइल जोड़ते समय डिफ़ॉल्ट है।
fl034

1
यह एक काम करने के लिए प्रतीत नहीं हो सकता है .. हर बार जब मैं अपने .xib फ़ाइल के अंदर फ़ाइल स्वामी सेट करता हूँ, तो यह भी कस्टम वर्ग सेट करता है।
ड्रूस्टर

32

मान लें कि आपने एक xib बनाया है जिसका आप उपयोग करना चाहते हैं:

1) UIView का एक कस्टम उपवर्ग बनाएँ (आप फ़ाइल में जा सकते हैं -> नया -> फ़ाइल ... -> लोआओ टच क्लास। सुनिश्चित करें कि "उपवर्ग:" "" उइवेउ ") है।

2) आरंभ में इस दृश्य में एक दृश्य के रूप में xib पर आधारित एक दृश्य जोड़ें।

ओब्ज-सी में

-(id)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        UIView *xibView = [[[NSBundle mainBundle] loadNibNamed:@"YourXIBFilename"
                                                              owner:self
                                                            options:nil] objectAtIndex:0];
        xibView.frame = self.bounds;
        xibView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        [self addSubview: xibView];
    }
    return self;
}

स्विफ्ट 2 में

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    let xibView = NSBundle.mainBundle().loadNibNamed("YourXIBFilename", owner: self, options: nil)[0] as! UIView
    xibView.frame = self.bounds
    xibView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
    self.addSubview(xibView)
}

स्विफ्ट 3 में

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    let xibView = Bundle.main.loadNibNamed("YourXIBFilename", owner: self, options: nil)!.first as! UIView
    xibView.frame = self.bounds
    xibView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    self.addSubview(xibView)
}

3) जहां भी आप इसे अपने स्टोरीबोर्ड में उपयोग करना चाहते हैं, सामान्य रूप से एक यूआईईवीवाई जोड़ें, नए जोड़े गए दृश्य का चयन करें, पहचान निरीक्षक पर जाएं (ऊपरी दाएं पर तीसरा आइकन जो इसमें लाइनों के साथ एक आयत की तरह दिखता है) और "कस्टम क्लास" के तहत "उपवर्ग" के रूप में अपना उपवर्ग का नाम दर्ज करें।


xibView.frame = self.frame;होना चाहिए xibView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);, अन्यथा स्टोरीबोर्ड में दृश्य जोड़ने पर xibView में एक ऑफसेट होगा।
बेबीपांडा

पार्टी के लिए देर से लेकिन ऐसा लगता है कि उन्होंने इसे xibView.frame = self.bounds में बदल दिया, जो ऑफसेट के बिना एक फ्रेम है
हेवी_न्यूलेट्स

20
अनंत पुनरावृत्ति के कारण एक दुर्घटना में परिणाम। नीब लोड करना उपवर्ग का एक और उदाहरण बनाता है।
डेविड

2
Xib दृश्य की कक्षा इस नए उपवर्ग के समान नहीं होनी चाहिए। यदि xib MyClass है, तो आप इस नए वर्ग MyClassContainer को बना सकते हैं।
user1021430

उपरोक्त समाधान अनंत पुनरावृत्ति के लिए दुर्घटनाग्रस्त हो जाएगा।
JBarros35

27

मैंने हमेशा "इसे एक सबव्यू के रूप में जोड़ें" समाधान असंतोषजनक पाया है, क्योंकि यह (1) ऑटोलॉयउट, (2) @IBInspectable, और (3) आउटलेट के साथ शिकंजा है । इसके बजाय, मैं आपको awakeAfter:एक NSObjectविधि के जादू से परिचित कराता हूं ।

awakeAfterआपको वास्तव में पूरी तरह से एक अलग वस्तु के साथ एक एनआईबी / स्टोरीबोर्ड से जागने वाली वस्तु को स्वैप करने देता है। उस वस्तु को तब जलयोजन प्रक्रिया के माध्यम से रखा awakeFromNibजाता है, उस पर बुलाया जाता है, एक दृश्य के रूप में जोड़ा जाता है, आदि।

हम अपने दृश्य के "कार्डबोर्ड कट-आउट" उपवर्ग में इसका उपयोग कर सकते हैं, जिसका एकमात्र उद्देश्य एनआईबी से दृश्य लोड करना और स्टोरीबोर्ड में उपयोग के लिए इसे वापस करना होगा। तब एम्बेड करने योग्य उपवर्ग मूल वर्ग के बजाय स्टोरीबोर्ड दृश्य के पहचान निरीक्षक में निर्दिष्ट होता है। यह वास्तव में काम करने के लिए एक उपवर्ग होने की जरूरत नहीं है, लेकिन यह एक उपवर्ग बना रही है जो आईबी को किसी भी IBInspectable / IBOutlet गुणों को देखने की अनुमति देता है।

यह अतिरिक्त बॉयलरप्लेट सब-अप्टीमल लग सकता है - और एक मायने में, क्योंकि आदर्श UIStoryboardरूप से इस मूल को संभालना होगा - लेकिन मूल एनआईबी और UIViewउपवर्ग को पूरी तरह से अनअमोडिफाइड छोड़ने का फायदा है । यह जो भूमिका निभाता है वह मूल रूप से एक एडेप्टर या ब्रिज क्लास की है, और यह पूरी तरह से वैध, डिज़ाइन-वार है, अतिरिक्त वर्ग के रूप में, भले ही यह अफसोसजनक हो। दूसरी तरफ, यदि आप अपनी कक्षाओं से पारंगत होना पसंद करते हैं, तो कुछ अन्य छोटे बदलावों के साथ एक प्रोटोकॉल को लागू करके @ बेनपैच का समाधान काम करता है। प्रोग्रामर शैली के मामले में कौन से समाधान बेहतर उबाल है: क्या कोई वस्तु रचना या एकाधिक विरासत पसंद करता है।

नोट: NIB फ़ाइल में दृश्य पर सेट किया गया वर्ग समान रहता है। एम्बेड करने योग्य उपवर्ग केवल स्टोरीबोर्ड में उपयोग किया जाता है। उपवर्ग का उपयोग कोड में दृश्य को तुरंत करने के लिए नहीं किया जा सकता है, इसलिए इसमें कोई अतिरिक्त तर्क नहीं होना चाहिए, स्वयं। यह चाहिए केवल शामिल awakeAfterहुक।

class MyCustomEmbeddableView: MyCustomView {
  override func awakeAfter(using aDecoder: NSCoder) -> Any? {
    return (UIView.instantiateViewFromNib("MyCustomView") as MyCustomView?)! as Any
  }
}

Is यहाँ एक महत्वपूर्ण कमी यह है कि यदि आप स्टोरीबोर्ड में चौड़ाई, ऊँचाई, या पहलू अनुपात को परिभाषित करते हैं जो किसी अन्य दृश्य से संबंधित नहीं है, तो उन्हें मैन्युअल रूप से कॉपी करना होगा। दो दृष्टिकोणों से संबंधित बाधाएं निकटतम सामान्य पूर्वजों पर स्थापित की जाती हैं, और विचार स्टोरीबोर्ड से अंदर-बाहर से जगाए जाते हैं, इसलिए जब तक उन बाधाओं को पर्यवेक्षी पर हाइड्रेटेड नहीं किया जाता है तब तक स्वैप पहले ही हो चुका है। बाधाएँ जो केवल प्रश्न में दृश्य को शामिल करती हैं, सीधे उस दृश्य पर स्थापित की जाती हैं, और जब स्वैप की प्रतिलिपि बनाई जाती है, तो इस तरह से फेंक दिया जाता है।

ध्यान दें कि यहां क्या हो रहा है , स्टोरीबोर्ड में दृश्य पर स्थापित बाधाएं नए तात्कालिक दृश्य में कॉपी की जाती हैं , जो पहले से ही अपनी स्वयं की बाधाओं हो सकती हैं, इसकी नीब फ़ाइल में परिभाषित की गई हैं। जो अप्रभावित हैं।

class MyCustomEmbeddableView: MyCustomView {
  override func awakeAfter(using aDecoder: NSCoder) -> Any? {
    let newView = (UIView.instantiateViewFromNib("MyCustomView") as MyCustomView?)!

    for constraint in constraints {
      if constraint.secondItem != nil {
        newView.addConstraint(NSLayoutConstraint(item: newView, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: newView, attribute: constraint.secondAttribute, multiplier: constraint.multiplier, constant: constraint.constant))
      } else {
        newView.addConstraint(NSLayoutConstraint(item: newView, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: constraint.constant))
      }
    }

    return newView as Any
  }
}  

instantiateViewFromNibके लिए एक प्रकार-सुरक्षित एक्सटेंशन है UIView। यह सब एनआईबी की वस्तुओं के माध्यम से लूप है जब तक कि यह उस प्रकार से मेल खाने वाले को नहीं पाता है। ध्यान दें कि जेनेरिक प्रकार रिटर्न वैल्यू है, इसलिए कॉल साइट पर टाइप को निर्दिष्ट करना होगा।

extension UIView {
  public class func instantiateViewFromNib<T>(_ nibName: String, inBundle bundle: Bundle = Bundle.main) -> T? {
    if let objects = bundle.loadNibNamed(nibName, owner: nil) {
      for object in objects {
        if let object = object as? T {
          return object
        }
      }
    }

    return nil
  }
}

वह मनमौजी है। अगर मैं गलत नहीं हूँ, लेकिन यह केवल "स्टोरीबोर्ड पर" काम करता है - यदि आप रनटाइम के दौरान कोड में ऐसी कक्षा बनाने की कोशिश करते हैं, तो मुझे नहीं लगता कि यह काम करता है। मुझे विश्वास है।
फेटी

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

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

1
महान! एक बात ने मुझे उलझा दिया क्योंकि मुझे एक्सिब्स के साथ बहुत कम अनुभव है (मैंने कभी स्टोरीबोर्ड्स और प्रोग्रामेटिक एप्रोच के साथ काम किया था), इसे छोड़ते हुए अगर यह किसी की मदद करता है: .xib फाइल में, आपको शीर्ष स्तर के दृश्य का चयन करने की आवश्यकता है, और सेट करें। इसके वर्ग प्रकार को MyCustomView। मेरे xib में बाईं ओर का आंतरिक-साइडबार डिफ़ॉल्ट रूप से गायब था; इसे चालू करने के लिए, नीचे / बाईं ओर "नियंत्रण के रूप में देखें: iPhone 7" लक्षण के बगल में एक बटन है।
xaphod

5
जब यह किसी अन्य वस्तु द्वारा प्रतिस्थापित किया जाता है तो यह बाधा उत्पन्न करता है। :(
मंगलाचरण

6

वर्तमान में सबसे अच्छा समाधान यह है कि एक कस्टम व्यू कंट्रोलर का उपयोग केवल अपने दृश्य के साथ एक एक्सिब में परिभाषित किया जाए, और केवल "व्यू" प्रॉपर्टी को हटा दें, जो व्यूबोर्ड को स्टोरीबोर्ड के अंदर बनाता है, जब व्यू कंट्रोलर को इसमें जोड़ा जाता है (नाम का सेट करना न भूलें कस्टम वर्ग हालांकि)।

यह रनटाइम को स्वचालित रूप से xib के लिए देखेगा और लोड करेगा। आप इस ट्रिक का इस्तेमाल किसी भी तरह के कंटेनर व्यूज या कंटेंट व्यू के लिए कर सकते हैं।


5

मैं अलग स्टोरीबोर्ड मेंalternative उपयोग XIB viewsकरने के बारे में सोचता हूं ।View Controller

फिर कस्टम स्टोरीबोर्ड में कस्टम व्यू के स्थान के container viewसाथ उपयोग किया जाता है Embed Segueऔर StoryboardReferenceइस कस्टम व्यू कंट्रोलर के पास होता है जिसे व्यू को मुख्य स्टोरीबोर्ड में अन्य दृश्य के अंदर रखा जाना चाहिए।

फिर हम इस एम्बेड ViewController और मुख्य दृश्य नियंत्रक के बीच तैयारी के माध्यम से प्रतिनिधि और संचार स्थापित कर सकते हैं । यह दृष्टिकोण अलग है तब UIView प्रदर्शित करता है, लेकिन बहुत सरल और अधिक कुशलता से (प्रोग्रामिंग दृष्टिकोण से) एक ही लक्ष्य को प्राप्त करने के लिए उपयोग किया जा सकता है, अर्थात पुन: प्रयोज्य कस्टम दृश्य जो मुख्य स्टोरीबोर्ड में दिखाई देता है

अतिरिक्त लाभ यह है कि आप CustomViewController वर्ग में तर्क को लागू कर सकते हैं और वहां अलग-अलग (प्रोजेक्ट में खोजने के लिए कठिन) नियंत्रक वर्गों को बनाए बिना सभी प्रतिनिधिमंडल और दृश्य तैयारी सेट करते हैं, और घटक के उपयोग के बिना मुख्य UIViewController में बॉयलरप्लेट कोड रखे बिना। मुझे लगता है कि यह पुन: प्रयोज्य घटकों के लिए अच्छा है। संगीत प्लेयर घटक (विजेट जैसा) जो अन्य दृश्यों में एम्बेड करने योग्य है।


वास्तव में कस्टम दृश्य xib का उपयोग करने की तुलना में बहुत अधिक सरल समाधान :(
विल्सन

1
कस्टम UIView पर ऐप्पल के xib जीवनचक्र निर्माण श्रृंखला के साथ हलकों में जाने के बाद इस तरह से मैंने जाना समाप्त कर दिया।
लाइटनिंगस्ट्रीम

यदि आप कस्टम व्यू को गतिशील रूप से जोड़ना चाहते हैं, तो हमें अभी भी अलग-अलग व्यू कंट्रोलर (सबसे अधिमानतः XIB)
सत्यम

5

यद्यपि शीर्ष सबसे लोकप्रिय उत्तर ठीक काम करते हैं, वे वैचारिक रूप से गलत हैं। वे सभी File's ownerवर्ग के आउटलेट और यूआई घटकों के बीच कनेक्शन के रूप में उपयोग करते हैं। File's ownerकेवल शीर्ष-स्तरीय ऑब्जेक्ट्स के लिए उपयोग नहीं किया जाना चाहिए UIView। की जाँच करें एप्पल डेवलपर दस्तावेज़ । के रूप में UIView होने File's ownerइन अवांछित परिणाम के लिए होता है।

  1. आपको वह उपयोग करने के लिए मजबूर किया contentViewजाता है जहां आप उपयोग करने वाले होते हैं self। यह न केवल बदसूरत है, बल्कि संरचनात्मक रूप से भी गलत है क्योंकि मध्यवर्ती दृश्य डेटा संरचना को यूआई संरचना से यह बताता है। यह घोषणात्मक यूआई के विपरीत जैसा है।
  2. आपके पास केवल XIV प्रति एक UIView हो सकता है। माना जाता है कि एक Xib के कई UIView हैं।

उपयोग किए बिना इसे करने का सुंदर तरीका है File's owner। कृपया इस ब्लॉग पोस्ट की जाँच करें । यह बताता है कि इसे सही तरीके से कैसे किया जाए।


यह बिल्कुल भी मायने नहीं रखता है कि आपने इसकी व्याख्या क्यों नहीं की। मैं क्यों नहीं देख सकता। कोई साइड इफेक्ट नहीं हैं।
JBarros35

1

यहाँ जवाब है कि आप सभी के साथ चाहते हैं। आप बस अपनी CustomViewकक्षा बना सकते हैं , सभी साक्षात्कार और आउटलेट के साथ एक xib में इसका मास्टर उदाहरण है। फिर आप उस क्लास को अपने स्टोरीबोर्ड या अन्य xibs में किसी भी इंस्टेंस पर लागू कर सकते हैं।

फाइल के ओनर के साथ फिड करने की जरूरत नहीं है, या आउटलेट्स को प्रॉक्सी से कनेक्ट करने या किसी अजीबोगरीब तरीके से xib को संशोधित करने, या अपने कस्टम व्यू के इंस्टेंस को खुद के सबव्यू के रूप में जोड़ने की आवश्यकता है।

बस यह करें:

  1. आयात BFWControls रूपरेखा
  2. से अपने सुपर क्लास बदलें UIViewकरने के लिए NibView(या से UITableViewCellकरने के लिए NibTableViewCell)

बस!

यहां तक ​​कि यह स्टोरीबोर्ड में डिज़ाइन समय पर आपके कस्टम दृश्य (xib से सबवेक्शंस सहित) को संदर्भित करने के लिए IBDesignable के साथ काम करता है।

आप इसके बारे में और अधिक यहाँ पढ़ सकते हैं: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155

और आप यहाँ खुला स्रोत BFWControls ढांचा प्राप्त कर सकते हैं: https://github.com/BareFeetWare/BFWControls

और यहाँ NibReplaceableकोड का एक सरल अर्क है जो इसे चलाता है, यदि आप उत्सुक हैं तो
 https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4

टॉम 👣


निफ्टी समाधान, धन्यवाद!
अवे

मैं एक ढांचा स्थापित नहीं करना चाहता जब इसे मूल रूप से प्रदान किया जाना चाहिए।
JBarros35

0

इस समाधान का उपयोग तब भी किया जा सकता है, जब आपकी कक्षा का XIB जैसा नाम न हो। उदाहरण के लिए, यदि आपके पास एक बेस व्यू कंट्रोलर क्लास कंट्रोलर है जिसमें XIB नाम कंट्रोलर है। xib

  • स्टोरीबोर्ड में व्यू कंट्रोलर बनाएं
  • नियंत्रक के वर्ग को नियंत्रकबी में सेट करें
  • स्टोरीबोर्ड में कंट्रोलरबी का दृश्य हटाएं
  • कंट्रोलर में ओवर लोड लोड देखें:

*

- (void) loadView    
{
        //according to the documentation, if a nibName was passed in initWithNibName or
        //this controller was created from a storyboard (and the controller has a view), then nibname will be set
        //else it will be nil
        if (self.nibName)
        {
            //a nib was specified, respect that
            [super loadView];
        }
        else
        {
            //if no nib name, first try a nib which would have the same name as the class
            //if that fails, force to load from the base class nib
            //this is convenient for including a subclass of this controller
            //in a storyboard
            NSString *className = NSStringFromClass([self class]);
            NSString *pathToNIB = [[NSBundle bundleForClass:[self class]] pathForResource: className ofType:@"nib"];
            UINib *nib ;
            if (pathToNIB)
            {
                nib = [UINib nibWithNibName: className bundle: [NSBundle bundleForClass:[self class]]];
            }
            else
            {
                //force to load from nib so that all subclass will have the correct xib
                //this is convenient for including a subclass
                //in a storyboard
                nib = [UINib nibWithNibName: @"baseControllerXIB" bundle:[NSBundle bundleForClass:[self class]]];
            }

            self.view = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
       }
}

0

बेन पैच की प्रतिक्रिया में वर्णित चरणों के अनुसार उद्देश्य-सी के लिए समाधान ।

UIView के लिए एक्सटेंशन का उपयोग करें:

@implementation UIView (NibLoadable)

- (UIView*)loadFromNib
{
    UIView *xibView = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] firstObject];
    xibView.translatesAutoresizingMaskIntoConstraints = NO;
    [self addSubview:xibView];
    [xibView.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES;
    [xibView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES;
    [xibView.leftAnchor constraintEqualToAnchor:self.leftAnchor].active = YES;
    [xibView.rightAnchor constraintEqualToAnchor:self.rightAnchor].active = YES;
    return xibView;
}

@end

फ़ाइलें बनाएँ MyView.h, MyView.mऔर MyView.xib

पहले अपनी तैयारी करें MyView.xibक्योंकि बेन पैच की प्रतिक्रिया कहती है कि MyViewइस XIB के अंदर मुख्य दृश्य के बजाय फ़ाइल के स्वामी के लिए सेट करें।

MyView.h:

#import <UIKit/UIKit.h>

IB_DESIGNABLE @interface MyView : UIView

@property (nonatomic, weak) IBOutlet UIView* someSubview;

@end

MyView.m:

#import "MyView.h"
#import "UIView+NibLoadable.h"

@implementation MyView

#pragma mark - Initializers

- (id)init
{
    self = [super init];
    if (self) {
        [self loadFromNib];
        [self internalInit];
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self loadFromNib];
        [self internalInit];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self loadFromNib];
    }
    return self;
}

- (void)awakeFromNib
{
    [super awakeFromNib];
    [self internalInit];
}

- (void)internalInit
{
    // Custom initialization.
}

@end

और बाद में बस अपने विचार को प्रोग्रामेटिक रूप से बनाएं:

MyView* view = [[MyView alloc] init];

चेतावनी! इस दृश्य का पूर्वावलोकन स्टोरीबोर्ड में नहीं दिखाया जाएगा यदि आप Xcode में इस बग के कारण WatchKit एक्सटेंशन का उपयोग करते हैं> = 9.2: https://forums.developer.apple.com/thread/95616


स्विफ्ट संस्करण प्रदान करना चाहिए था। इस समय अभी भी बहुत कम लोग ओबीसी का उपयोग कर रहे हैं
सत्यम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.