स्विफ्ट में निब से एक यूआईईवीवाई लोड करें


148

यहाँ मेरा उद्देश्य-सी कोड है जो मैं अपने अनुकूलित के लिए एक नीब लोड करने के लिए उपयोग कर रहा हूँ UIView:

-(id)init{

    NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:@"myXib" owner:self options:nil];
    return [subviewArray objectAtIndex:0];

}

स्विफ्ट में समान कोड क्या है?

जवाबों:


172

मूल समाधान

  1. मैंने एक XIB और SomeView नामक एक वर्ग बनाया (सुविधा और पठनीयता के लिए समान नाम का उपयोग किया)। मैं दोनों पर आधारित है।
  2. XIB में, मैंने "फ़ाइल का स्वामी" वर्ग को SomeView (पहचान निरीक्षक में) बदल दिया।
  3. मैंने SomeView.swift में एक UIView आउटलेट बनाया, इसे XIB फ़ाइल में शीर्ष स्तर के दृश्य से जोड़ा (सुविधा के लिए इसे "दृश्य" नाम दिया)। फिर मैंने आवश्यकतानुसार XIB फ़ाइल में अन्य नियंत्रणों के लिए अन्य आउटलेट जोड़े।
  4. SomeView.swift में, मैंने XIB को "कोड के साथ init" इनिशियलाइज़र के अंदर लोड किया। "स्व" को कुछ भी निर्दिष्ट करने की आवश्यकता नहीं है। जैसे ही XIB लोड किया जाता है, सभी आउटलेट शीर्ष स्तर के दृश्य सहित जुड़े हुए हैं। केवल एक चीज गायब है, शीर्ष दृश्य को पदानुक्रम में जोड़ना है:

class SomeView: UIView {
   required init(coder aDecoder: NSCoder) {
      super.init(coder: aDecoder)
      NSBundle.mainBundle().loadNibNamed("SomeView", owner: self, options: nil)
      self.addSubview(self.view);    // adding the top level view to the view hierarchy
   }
   ...
}

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

अद्यतन - स्विफ्ट 3 सिंटैक्स का उपयोग करना

निम्नलिखित एक्सटेंशन में एक एक्सिब लोड कर रहा है एक उदाहरण विधि के रूप में लिखा गया है, जिसे तब एक इनिशियलाइज़र द्वारा उपयोग किया जा सकता है जैसे ऊपर:

extension UIView {

    @discardableResult   // 1
    func fromNib<T : UIView>() -> T? {   // 2
        guard let contentView = Bundle(for: type(of: self)).loadNibNamed(String(describing: type(of: self)), owner: self, options: nil)?.first as? T else {    // 3
            // xib not loaded, or its top view is of the wrong type
            return nil
        }
        self.addSubview(contentView)     // 4
        contentView.translatesAutoresizingMaskIntoConstraints = false   // 5 
        contentView.layoutAttachAll(to: self)   // 6 
        return contentView   // 7
    }
}
  1. लौटाए गए रिटर्न मान का उपयोग करना क्योंकि लौटे हुए दृश्य को कॉल करने वाले के लिए कोई दिलचस्पी नहीं है, जब सभी आउटलेट पहले से ही जुड़े हुए हैं।
  2. यह एक सामान्य विधि है जो UIView प्रकार की एक वैकल्पिक वस्तु लौटाती है। यदि यह दृश्य लोड करने में विफल रहता है, तो यह शून्य हो जाता है।
  3. XIB फ़ाइल को वर्तमान वर्ग उदाहरण के समान नाम के साथ लोड करने का प्रयास। यदि वह विफल रहता है, तो शून्य वापस कर दिया जाता है।
  4. शीर्ष स्तर दृश्य को पदानुक्रम में जोड़ना।
  5. यह पंक्ति मानती है कि हम दृश्य को लेआउट करने के लिए बाधाओं का उपयोग कर रहे हैं।
  6. यह विधि शीर्ष, नीचे, अग्रणी और अनुगामी बाधाओं को जोड़ती है - सभी पक्षों पर "स्वयं" के लिए दृश्य संलग्न करना (देखें: https://stackoverflow.com/a/46279424/2274829 विवरण के लिए)
  7. शीर्ष स्तर का दृश्य लौटना

और कॉलर विधि इस तरह दिख सकती है:

final class SomeView: UIView {   // 1.
   required init?(coder aDecoder: NSCoder) {   // 2 - storyboard initializer
      super.init(coder: aDecoder)
      fromNib()   // 5.
   }
   init() {   // 3 - programmatic initializer
      super.init(frame: CGRect.zero)  // 4.
      fromNib()  // 6.
   }
   // other methods ...
}
  1. SomeClass एक UIView उपवर्ग है जो कुछClass.xib फ़ाइल से इसकी सामग्री को लोड करता है। "अंतिम" कीवर्ड वैकल्पिक है।
  2. जब एक स्टोरीबोर्ड में दृश्य का उपयोग किया जाता है, तो इसके लिए एक initializer (अपने स्टोरीबोर्ड दृश्य के कस्टम वर्ग के रूप में SomeClass का उपयोग करने के लिए याद रखें)।
  3. जब दृश्य को प्रोग्रामेटिक रूप से बनाया जाता है, तो इसके लिए एक इनिलाइज़र (यानी: "myView = SomeView ()")।
  4. ऑल-जीरो फ्रेम का उपयोग करना क्योंकि यह दृश्य ऑटो-लेआउट का उपयोग करके निर्धारित किया गया है। ध्यान दें कि एक "init (फ्रेम: CGRect) {..}" विधि स्वतंत्र रूप से नहीं बनाई गई है, क्योंकि ऑटो-लेआउट का उपयोग हमारे प्रोजेक्ट में विशेष रूप से किया जाता है।
  5. & 6. एक्सटेंशन का उपयोग करके xib फ़ाइल लोड करना।

क्रेडिट: इस समाधान में एक सामान्य एक्सटेंशन का उपयोग करना रॉबर्ट के जवाब से प्रेरित था।

संपादित बचने के भ्रम को "contentView" के लिए "दृश्य" बदल रहा है। इसके अलावा सरणी सबस्क्रिप्ट को ".first" में बदल दिया।


7
File's Ownerमौके को हिट करने के लिए वर्ग नाम सेट करना ... धन्यवाद!
अवील सकल

13
UIView के पास संपत्ति का दृश्य नहीं है, इसलिए self.view को कॉल करना एक त्रुटि का कारण बनता है
Nastya Gorban

4
@NastyaGorban self.view वास्तव में आउटलेट प्रॉपर्टी (नामित नाम) में इस मामले को संदर्भित करता है जो GK100 शीर्ष स्तर के दृश्य से .xib में SomeView.swift से जुड़ा हुआ है। उस आउटलेट को जोड़ने से आपको त्रुटि मिलेगी क्योंकि कोई "दृश्य" नहीं है। "NSView कक्षाओं में संपत्ति जैसा कि आप कहते हैं।
Ausiàs

3
मैं लोड करते समय दुर्घटनाग्रस्त हो रहा हूँ (लोडननिम्ड)। Xcode 6.3 और स्विफ्ट का उपयोग करना
karthikPrabhu Alagu

11
fromNib()भीतर से कॉल करना init(coder aDecoder: NSCoder)एक अनन्त लूप बनाता है क्योंकि fromNib()विधि के अंदर निब को लोड करना एक कॉल करता है:init(coder aDecoder: NSCoder)
मैथ्यू केवली

334

मेरा योगदान:

extension UIView {
    class func fromNib<T: UIView>() -> T {
        return Bundle(for: T.self).loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
    }
}

फिर इसे इस तरह से कॉल करें:

let myCustomView: CustomView = UIView.fromNib()

..या और भी:

let myCustomView: CustomView = .fromNib()

20
अब तक का सबसे अच्छा जवाब।
कॉडीमैस

7
सबसे अच्छा जवाब यहाँ। क्लीन एंड सिंपल
मार्क्विअस ड्रैगनगन

3
@ युकेनझोंग - मुझे पसंद है [0] ओवर। जैसा कि एक वैकल्पिक वापसी करेगा। यदि आप इसे लागू नहीं करते हैं, तो यह सुरक्षित नहीं होगा। ... और यह सवाल भी उठता है: क्यों उपरोक्त विकल्पों में से कुछ के रूप में एक वैकल्पिक वापस नहीं? उत्तर: आप कर सकते हैं। कुछ गलत नहीं है उसके साथ। लेकिन ... अगर यह कभी वापस आएगा, तो xib / class का नाम मेल नहीं खाता। यह एक डेवलपर गलती है और इसे तुरंत पकड़ा जाना चाहिए और इसे कभी भी उत्पादन नहीं करना चाहिए। यहाँ मैं इसे कुछ अजीब अवस्था में छोड़ने पर ऐप क्रैश होना पसंद करूंगा। बस मेरे 2 सेंट / वरीयता।
रॉबर्ट गुम्मसन

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

3
इस समाधान ने मेरे लिए उस मामले में काम नहीं किया जब .xib के अंदर एक कस्टम दृश्य था। मैं इस भाग को ठीक करने का सुझाव दूंगा: वापसी बंडल। Main.loadNibNamed (स्ट्रिंग (वर्णन: स्वयं), स्वामी: नील, विकल्प: नील)! [0] as! टी
नदज़ेया

79

अब -> Selfतेजी से वापसी करने में सक्षम होने से यह थोड़ा सरल हो जाता है। अंतिम 5 स्विफ्ट 5 पर पुष्टि की।

extension UIView {
    class func fromNib(named: String? = nil) -> Self {
        let name = named ?? "\(Self.self)"
        guard
            let nib = Bundle.main.loadNibNamed(name, owner: nil, options: nil)
            else { fatalError("missing expected nib named: \(name)") }
        guard
            /// we're using `first` here because compact map chokes compiler on
            /// optimized release, so you can't use two views in one nib if you wanted to
            /// and are now looking at this
            let view = nib.first as? Self
            else { fatalError("view of type \(Self.self) not found in \(nib)") }
        return view
    }
}

यदि आपकी .xibफ़ाइल और उपवर्ग एक ही नाम साझा करते हैं, तो आप उपयोग कर सकते हैं:

let view = CustomView.fromNib()

यदि आपके पास कोई कस्टम नाम है, तो उपयोग करें:

let view = CustomView.fromNib(named: "special-case")

ध्यान दें:

यदि आपको त्रुटि "टाइप YourType नहीं मिला .. के दृश्य में मिल रही है" तो आपने .xibफ़ाइल में दृश्य का वर्ग सेट नहीं किया है

में आपके विचार का चयन करें .xibफ़ाइल, और प्रेस cmd + opt + 4और में classइनपुट, अपनी कक्षा में प्रवेश


1
मैं XCode 7.1 बीटा 3 के तहत काम करने के लिए इसे प्राप्त नहीं कर सकता हूं - यकीन नहीं कि अगर यह एक बीटा चीज है, लेकिन मूल रूप से मैंने स्विफ्ट में एक नीब से सीधे एक कस्टम दृश्य बनाने का हर तरह से प्रयास किया है और मुझे हमेशा एक ही परिणाम मिलता है: वह जिस वर्ग को बना रहा है आउटलेट्स के साथ KVC अनुरूप नहीं है। यकीन नहीं होता कि यह कुछ गलत है, लेकिन मेरी कक्षा बहुत सरल है और फ़ाइल का स्वामी सही है। मैं हर समय ऑब्जेक्टिव-सी के तहत ऐसा करता था।
इकोलोन

1
@Logan यह वास्तव में आपके कोड से संबंधित नहीं है, लेकिन imo कस्टम दृश्यों को Storyboard / XIB से लोड करने का समर्थन करना चाहिए। मेरी टिप्पणी सिर्फ उन लोगों के लिए एक अधिसूचना थी, जो इस तरह के विचार बनाना चाहते हैं
निकिता ने

1
नोट: मुझे अभी भी इस फ़ंक्शन को कॉल करने के दूसरे रूप का उपयोग करने में समस्या है, अर्थात् let myCustomView = UIView.fromNib() as? CustomView। इस मामले में, T.selfके UIViewबजाय हल करता है CustomViewऔर यह नीब खोजने में विफल रहता है। मुझे यकीन नहीं है कि ऐसा क्यों है - हो सकता letहै कि फ़ंक्शन के रूप में कहे जाने वाले साधनों के लिए अनुमानित प्रकार UIView?
इकोनेलोन

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

1
@ एशेलन तुमने मेरा दिन बचा लिया !!! मैंने फ़ाइल के स्वामी का उपयोग करके अपने आउटलेट कनेक्ट किए और यह काम नहीं किया, इसके बजाय दृश्य का उपयोग करके काम किया।
Jan Schlorf

19

निम्नलिखित कोड का प्रयास करें।

var uiview :UIView?

self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView

संपादित करें:

import UIKit

class TestObject: NSObject {

     var uiview:UIView?

    init()  {
        super.init()
       self.uiview = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)[0] as? UIView
    }


}

मुझे इस विधि को ऑब्जेक्ट इनिशियलाइज़ेशन मेथड के अंदर बुलाना होगा जो स्विफ्ट में इनिट () है।
बागुसेलर

12

स्विफ्ट 4 प्रोटोकॉल एक्सटेंशन

public protocol NibInstantiatable {

    static func nibName() -> String

}

extension NibInstantiatable {

    static func nibName() -> String {
        return String(describing: self)
    }

}

extension NibInstantiatable where Self: UIView {

    static func fromNib() -> Self {

        let bundle = Bundle(for: self)
        let nib = bundle.loadNibNamed(nibName(), owner: self, options: nil)

        return nib!.first as! Self

    }

}

दत्तक ग्रहण

class MyView: UIView, NibInstantiatable {

}

यह कार्यान्वयन मानता है कि निब का उविवि वर्ग के समान नाम है। पूर्व। MyView.xib। आप डिफ़ॉल्ट प्रोटोकॉल एक्सटेंशन कार्यान्वयन से भिन्न नाम वापस करने के लिए MyView में nibName () लागू करके इस व्यवहार को संशोधित कर सकते हैं।

Xib में फाइल्स का मालिक MyView है और रूट व्यू क्लास MyView है।

प्रयोग

let view = MyView.fromNib()

3
यह अब तक का सबसे सुरुचिपूर्ण, सीधा समाधान है और मुझे नहीं पता कि यह स्वीकृत उत्तर क्यों नहीं है!
घोड़े की नाल 7

@ घोड़े की नाल 7 क्योंकि इसके प्रश्न के 4 साल बाद लिखा गया।
फ्रीबी

11

मैंने निम्नलिखित कोड द्वारा स्विफ्ट के साथ इसे हासिल किया:

class Dialog: UIView {
    @IBOutlet var view:UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.frame = UIScreen.mainScreen().bounds
        NSBundle.mainBundle().loadNibNamed("Dialog", owner: self, options: nil)
        self.view.frame = UIScreen.mainScreen().bounds
        self.addSubview(self.view)
    }

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

स्विफ्ट में परिभाषित आउटलेट को देखने के लिए अपने XIB व्यू आउटलेट से कनेक्ट करना न भूलें । आप किसी भी अतिरिक्त आउटलेट से कनेक्ट करने के लिए अपने कस्टम वर्ग के नाम पर फर्स्ट रिस्पोंडर सेट कर सकते हैं।

उम्मीद है की यह मदद करेगा!


10

Xcode 7 बीटा 4, स्विफ्ट 2.0 और iOS9 SDK में परीक्षण किया गया। निम्न कोड xib को uiview को निर्दिष्ट करेगा। आप स्टोरीबोर्ड में इस कस्टम xib दृश्य का उपयोग करने में सक्षम हो सकते हैं और IBOutlet ऑब्जेक्ट को भी एक्सेस कर सकते हैं।

import UIKit

@IBDesignable class SimpleCustomView:UIView
{
    var view:UIView!;

    @IBOutlet weak var lblTitle: UILabel!

   @IBInspectable var lblTitleText : String?
        {
        get{
            return lblTitle.text;
        }
        set(lblTitleText)
        {
            lblTitle.text = lblTitleText!;
        }
    }

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

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        loadViewFromNib ()
    }
    func loadViewFromNib() {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: "SimpleCustomView", bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        self.addSubview(view);



    }


}

वैकल्पिक रूप से प्रोग्रामव्यू एक्सेस करें

self.customView =  SimpleCustomView(frame: CGRectMake(100, 100, 200, 200))
        self.view.addSubview(self.customView!);

स्रोत कोड - https://github.com/karthikprabhuA/CustomXIBSwift


9

यदि आपके पास अपने प्रोजेक्ट में बहुत सारे कस्टम विचार हैं, तो आप क्लास जैसे बना सकते हैं UIViewFromNib

स्विफ्ट 2.3

class UIViewFromNib: UIView {

    var contentView: UIView!

    var nibName: String {
        return String(self.dynamicType)
    }

    //MARK:
    override init(frame: CGRect) {
        super.init(frame: frame)

        loadViewFromNib()
    }

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

        loadViewFromNib()
    }

    //MARK:
    private func loadViewFromNib() {
        contentView = NSBundle.mainBundle().loadNibNamed(nibName, owner: self, options: nil)[0] as! UIView
        contentView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

स्विफ्ट 3

class UIViewFromNib: UIView {

    var contentView: UIView!

    var nibName: String {
        return String(describing: type(of: self))
    }

    //MARK:
    override init(frame: CGRect) {
        super.init(frame: frame)

        loadViewFromNib()
    }

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

        loadViewFromNib()
    }

    //MARK:
    func loadViewFromNib() {
        contentView = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?[0] as! UIView
        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        contentView.frame = bounds
        addSubview(contentView)
    }
}

और हर वर्ग से सिर्फ विरासत में मिला है UIViewFromNib, nibNameअगर आप संपत्ति को ओवरराइड कर सकते हैं यदि .xibफ़ाइल का अलग नाम है:

class MyCustomClass: UIViewFromNib {

}

8

उपरोक्त समाधानों पर निर्माण।

यह सभी प्रोजेक्ट बंडलों में काम करेगा और जेनब () से कॉल करते समय जेनेरिक की आवश्यकता नहीं होगी।

स्विफ्ट 2

extension UIView {

    public class func fromNib() -> Self {
        return fromNib(nil)
    }

    public class func fromNib(nibName: String?) -> Self {

        func fromNibHelper<T where T : UIView>(nibName: String?) -> T {
            let bundle = NSBundle(forClass: T.self)
            let name = nibName ?? String(T.self)
            return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
        }
        return fromNibHelper(nibName)
    }
}

स्विफ्ट 3

extension UIView {

    public class func fromNib() -> Self {
        return fromNib(nibName: nil)
    }

    public class func fromNib(nibName: String?) -> Self {
        func fromNibHelper<T>(nibName: String?) -> T where T : UIView {
            let bundle = Bundle(for: T.self)
            let name = nibName ?? String(describing: T.self)
            return bundle.loadNibNamed(name, owner: nil, options: nil)?.first as? T ?? T()
        }
        return fromNibHelper(nibName: nibName)
    }
}

इस तरह इस्तेमाल किया जा सकता है:

let someView = SomeView.fromNib()

या इस तरह:

let someView = SomeView.fromNib("SomeOtherNibFileName")

6

स्विफ्ट 4

".First? CustomView" लिखना न भूलें।

if let customView = Bundle.main.loadNibNamed("myXib", owner: self, options: nil)?.first as? CustomView {    
    self.view.addSubview(customView)
    }

यदि आप कहीं भी उपयोग करना चाहते हैं

सबसे अच्छा समाधान रॉबर्ट Gummesson का जवाब है।

extension UIView {
    class func fromNib<T: UIView>() -> T {
        return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)![0] as! T
    }
}

फिर इसे इस तरह से कॉल करें:

let myCustomView: CustomView = UIView.fromNib()

5
let subviewArray = NSBundle.mainBundle().loadNibNamed("myXib", owner: self, options: nil)
return subviewArray[0]

लेकिन स्विफ्ट के init () में कोई रिटर्न वैल्यू नहीं है। मैं उल्लेख करना भूल गया कि मुझे एक UIView के प्रारंभ में loadNibNamed को कॉल करने की आवश्यकता है।
Bagusflyer

आपका क्या मतलब है "नो रिटर्न वैल्यू"? selfसभी initतरीकों से स्पष्ट रूप से वापस आ गया है ...
ग्रिमैक्स

1
मेरा मतलब है कि मैं init विधि के अंदर loadNibNamed कहता हूं। भरी हुई उविव्यू को ओबजेक में स्वयं को सौंपा गया है। लेकिन स्विफ्ट में, यह नहीं है।
बागुसेलर

5

स्विफ्ट के साथ ऐसा करने का एक अच्छा तरीका एक एनम का उपयोग करना है।

enum Views: String {
    case view1 = "View1" // Change View1 to be the name of your nib
    case view2 = "View2" // Change View2 to be the name of another nib

    func getView() -> UIView? {
        return NSBundle.mainBundle().loadNibNamed(self.rawValue, owner: nil, options: nil).first as? UIView
    }
}

फिर अपने कोड में आप बस उपयोग कर सकते हैं:

let view = Views.view1.getView()

2
ध्यान दें कि यदि आप एक खाली निब फ़ाइल या किसी भी UIView रूट नोड के साथ एक निब फ़ाइल के साथ ऐसा करते हैं तो आप दुर्घटनाग्रस्त हो जाएंगे क्योंकि आप सरणी आकार या स्थिति में तत्व की जाँच नहीं कर रहे हैं 0.
मैथ्यू Cawley

4

मैं इस समाधान को पसंद करता हूं (यदि उत्तर @ GK100 पर आधारित है):

  1. मैंने एक XIB और SomeView नामक एक वर्ग बनाया (सुविधा और पठनीयता के लिए समान नाम का उपयोग किया)। मैं दोनों पर आधारित है।
  2. XIB में, मैंने "फ़ाइल का स्वामी" वर्ग को SomeView (पहचान निरीक्षक में) बदल दिया।
  3. मैंने SomeView.swift में एक UIView आउटलेट बनाया, इसे XIB फ़ाइल में शीर्ष स्तर के दृश्य से जोड़ा (सुविधा के लिए इसे "दृश्य" नाम दिया)। फिर मैंने आवश्यकतानुसार XIB फ़ाइल में अन्य नियंत्रणों के लिए अन्य आउटलेट जोड़े।
  4. SomeView.swift में, मैंने XIB को initया इनिशलाइज़र के अंदर लोड init:frame: CGRectकिया। "स्व" को कुछ भी निर्दिष्ट करने की आवश्यकता नहीं है। जैसे ही XIB लोड किया जाता है, सभी आउटलेट शीर्ष स्तर के दृश्य सहित जुड़े हुए हैं। केवल एक चीज गायब है, शीर्ष दृश्य को पदानुक्रम में जोड़ना है:

    class SomeView: UIView {
      override init(frame: CGRect) {
        super.init(frame: frame)
        NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil)
        self.addSubview(self.view);    // adding the top level view to the view hierarchy
      }
    
      required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        NSBundle.mainBundle().loadNibNamed("SomeObject", owner: self, options: nil)
        self.addSubview(self.view);    // adding the top level view to the view hierarchy
      }
    
    
      ...
    }

मैं फ्रेम के साथ init का उपयोग करना पसंद करता हूं इसलिए मैंने इसे उखाड़ दिया! आप फ्रेम आप में पास मैच के लिए दृश्य चाहते हैं तो टिप्पणी ... ऐड self.view.frame = फ्रेम करने के लिए एक बात है
माइक ई

3

लोगान के जवाब के 3 संस्करण स्विफ्ट

extension UIView {
    public class func fromNib(nibName: String? = nil) -> Self {
        return fromNib(nibName: nibName, type: self)
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
        return fromNib(nibName: nibName, type: T.self)!
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String

        if let nibName = nibName {
            name = nibName
        } else {
            name = self.nibName
        }

        if let nibViews = Bundle.main.loadNibNamed(name, owner: nil, options: nil) {
            for nibView in nibViews {
                if let tog = nibView as? T {
                    view = tog
                }
            }
        }

        return view
    }

    public class var nibName: String {
        return "\(self)".components(separatedBy: ".").first ?? ""
    }

    public class var nib: UINib? {
        if let _ = Bundle.main.path(forResource: nibName, ofType: "nib") {
            return UINib(nibName: nibName, bundle: nil)
        } else {
            return nil
        }
    }
}

3

यहाँ एक प्रोटोकॉल और प्रोटोकॉल एक्सटेंशन (स्विफ्ट 4.2) का उपयोग करके दृश्य को लोड करने का एक साफ और घोषित तरीका है:

protocol XibLoadable {
    associatedtype CustomViewType
    static func loadFromXib() -> CustomViewType
}

extension XibLoadable where Self: UIView {
    static func loadFromXib() -> Self {
        let nib = UINib(nibName: "\(self)", bundle: Bundle(for: self))
        guard let customView = nib.instantiate(withOwner: self, options: nil).first as? Self else {
            // your app should crash if the xib doesn't exist
            preconditionFailure("Couldn't load xib for view: \(self)")
        }
        return customView
    }
}

और आप इसका उपयोग इस तरह कर सकते हैं:

// don't forget you need a xib file too
final class MyView: UIView, XibLoadable { ... }

// and when you want to use it
let viewInstance = MyView.loadFromXib()

कुछ अतिरिक्त विचार :

  1. सुनिश्चित करें कि आपके कस्टम दृश्य की xib फ़ाइल में दृश्य का Custom Classसेट (और वहां से सेट / किए गए कार्य) हैं, न कि फ़ाइल स्वामी का।
  2. आप इस प्रोटोकॉल / विस्तार बाहरी का उपयोग अपने कस्टम दृश्य या आंतरिक में कर सकते हैं। यदि आप अपना दृश्य आरंभ करते समय कुछ अन्य सेटअप कार्य करते हैं, तो आप इसे आंतरिक रूप से उपयोग करना चाह सकते हैं।
  3. आपके कस्टम दृश्य वर्ग और xib फ़ाइल का समान नाम होना चाहिए।

2

मैं बस इस तरह से करता हूं:

if let myView = UINib.init(nibName: "MyView", bundle: nil).instantiate(withOwner: self)[0] as? MyView {
// Do something with myView
}

यह नमूना मुख्य बंडल में nib "MyView.xib" में पहले दृश्य का उपयोग करता है। लेकिन आप या तो इंडेक्स, निब नाम या बंडल (मुख्य रूप से डिफ़ॉल्ट रूप से) में भिन्न हो सकते हैं।

मैं इनिट विधि में विचारों को जाग्रत करता था या ऊपर दिए गए समाधानों के अनुसार जेनेरिक तरीके बनाता था (जो कि वैसे स्मार्ट होते हैं), लेकिन मैं अब ऐसा नहीं करता।

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

मुझे एक फैक्ट्री ऑब्जेक्ट (आमतौर पर viewController जो दृश्य का उपयोग करेगा) को बनाने की अनुमति देना आसान लगता है क्योंकि इसे इसकी आवश्यकता है। कभी-कभी आपको एक स्वामी की आवश्यकता होती है (आमतौर पर जब निर्मित दृश्य को निर्माता से जुड़ा एक आउटलेट मिलता है), कभी-कभी नहीं ..

शायद इसीलिए Apple ने initFromNibअपनी UIView क्लास में एक विधि को शामिल नहीं किया ...

जमीनी स्तर का उदाहरण लेने के लिए, आप नहीं जानते कि आप कैसे पैदा हुए हैं। तुम अभी पैदा हुए हो। तो विचार हैं;)


2

आपको बस अपनी UIViewकक्षा में इनिट विधि को कॉल करना है ।

इसे इस तरह से करें:

class className: UIView {

    @IBOutlet var view: UIView!

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

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

    func setup() {
        UINib(nibName: "nib", bundle: nil).instantiateWithOwner(self, options: nil)
        addSubview(view)
        view.frame = self.bounds
    }
}

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

self.view.addSubview(className())

यह बहुत अच्छा जवाब है, लेकिन कुछ गड़बड़ है, मैं इसे संपादित करूंगा।
C0mrade

यह मेरे द्वारा लागू किया गया तरीका है। लेकिन आप इसे सुधार सकते हैं। अग्रिम धन्यवाद @ C0mrade
अलाप

1

ऊपर दिए गए कुछ उत्तरों के समान लेकिन एक अधिक सुसंगत Swift3 UIView एक्सटेंशन:

extension UIView {
    class func fromNib<A: UIView> (nibName name: String, bundle: Bundle? = nil) -> A? {
        let bundle = bundle ?? Bundle.main
        let nibViews = bundle.loadNibNamed(name, owner: self, options: nil)
        return nibViews?.first as? A
    }

    class func fromNib<T: UIView>() -> T? {
        return fromNib(nibName: String(describing: T.self), bundle: nil)
    }
}

जो कक्षा को स्वयं के नाम से, लेकिन अन्य निब / बंडलों से भी लोड करने में सक्षम होने की सुविधा देता है।


1

आप स्टोरीबोर्ड के माध्यम से ऐसा कर सकते हैं, बस देखने के लिए उचित बाधाओं को जोड़ें। आप इसे आसानी से अपने स्वयं के किसी भी दृष्टिकोण को उप-वर्ग द्वारा कह सकते हैं BaseView:

उद्देश्य सी

BaseView.h


/*!
 @class BaseView
 @discussion Base View for getting view from xibFile
 @availability ios7 and later
 */
@interface BaseView : UIView

@end


BaseView.m


#import "BaseView.h"

@implementation BaseView

#pragma mark - Public

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        [self prepareView];
    }
    return self;
}

#pragma mark - LifeCycle

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self prepareView];
    }
    return self;
}

#pragma mark - Private

- (void)prepareView
{
    NSArray *nibsArray = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
    UIView *view = [nibsArray firstObject];

    view.translatesAutoresizingMaskIntoConstraints = NO;
    [self addSubview:view];
    [self addConstraintsForView:view];
}

#pragma mark - Add constraints

- (void)addConstraintsForView:(UIView *)view
{
    [self addConstraints:@[[NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeBottom
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeBottom
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeTop
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeTop
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeLeft
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeLeft
                                                       multiplier:1.0
                                                         constant:0],
                           [NSLayoutConstraint constraintWithItem:view
                                                        attribute:NSLayoutAttributeRight
                                                        relatedBy:NSLayoutRelationEqual
                                                           toItem:self attribute:NSLayoutAttributeRight
                                                       multiplier:1.0
                                                         constant:0]
                           ]];
}

@end

स्विफ्ट 4

import UIKit

class BaseView : UIView {

    // MARK: - LifeCycle

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

        prepareView()
    }

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

        prepareView()
    }

    internal class func xibName() -> String {
        return String(describing: self)
    }

    // MARK: - Private
    fileprivate func prepareView() {
        let nameForXib = BaseView.xibName()
        let nibs = Bundle.main.loadNibNamed(nameForXib, owner: self, options: nil)
        if let view = nibs?.first as? UIView {
            view.backgroundColor = UIColor.clear
            view.translatesAutoresizingMaskIntoConstraints = false
            addSubviewWithConstraints(view, offset: false)
        }
    }
}

UIView+Subview


public extension UIView {
    // MARK: - UIView+Extensions

    public func addSubviewWithConstraints(_ subview:UIView, offset:Bool = true) {
        subview.translatesAutoresizingMaskIntoConstraints = false
        let views = [
            "subview" : subview
        ]
        addSubview(subview)

        var constraints = NSLayoutConstraint.constraints(withVisualFormat: offset ? "H:|-[subview]-|" : "H:|[subview]|", options: [.alignAllLeading, .alignAllTrailing], metrics: nil, views: views)
        constraints.append(contentsOf: NSLayoutConstraint.constraints(withVisualFormat: offset ? "V:|-[subview]-|" : "V:|[subview]|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views))
        NSLayoutConstraint.activate(constraints)
    }
}

मैं 2 वेरिएंट प्रदान करता हूं कि कैसे बाधाओं को जोड़ा जा सकता है - सामान्य एक और दृश्य प्रारूप भाषा के भीतर - जो भी आप चाहते हैं उसका चयन करें :)

इसके अलावा, डिफ़ॉल्ट रूप से यह माना जाता है कि इस xibनाम का कार्यान्वयन वर्ग नाम के समान नाम है। यदि नहीं - बस बदलेंxibName पैरामीटर ।

यदि आप अपने दृष्टिकोण को उप-वर्ग करते हैं BaseView- तो आप आसानी से किसी भी दृश्य को डाल सकते हैं और आईबी में कक्षा निर्दिष्ट कर सकते हैं।


1
class func loadFromNib<T: UIView>() -> T {
    let nibName = String(describing: self)
    return Bundle.main.loadNibNamed(nibName, owner: nil, options: nil)![0] as! T
}

0

लोगन के उत्तर पर आधारित अधिक शक्तिशाली संस्करण

extension UIView {
    public class func fromNib(nibName: String? = nil) -> Self {
        return fromNib(nibName: nibName, type: self)
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T {
        return fromNib(nibName: nibName, type: T.self)!
    }

    public class func fromNib<T: UIView>(nibName: String? = nil, type: T.Type) -> T? {
        var view: T?
        let name: String

        if let nibName = nibName {
            name = nibName
        } else {
            name = self.nibName
        }

        if let nibViews = nibBundle.loadNibNamed(name, owner: nil, options: nil) {
            if nibViews.indices.contains(nibIndex), let tog = nibViews[nibIndex] as? T {
                view = tog
            }
        }

        return view
    }

    public class var nibName: String {
        return "\(self)".components(separatedBy: ".").first ?? ""
    }

    public class var nibIndex: Int {
        return 0
    }

    public class var nibBundle: Bundle {
        return Bundle.main
    }
}

और आप का उपयोग कर सकते हैं

class BaseView: UIView {
    override class var nibName: String { return "BaseView" }
    weak var delegate: StandardStateViewDelegate?
}

class ChildView: BaseView {
    override class var nibIndex: Int { return 1 }
}

0

सबसे सुविधाजनक कार्यान्वयन। यहां आपको अपनी कक्षा की वस्तु पर सीधे लौटने के लिए दो तरीकों की आवश्यकता है, न कि यूआईवीयूवाई की।

  1. viewId को एक वर्ग के रूप में चिह्नित किया गया है , ओवरराइड की अनुमति देता है
  2. आपके .xib में शीर्ष स्तर का एक से अधिक दृश्य हो सकता है, इस स्थिति को भी सही तरीके से नियंत्रित किया जाता है।

extension UIView {

class var viewId: String {
    return String(describing: self)
}

static func instance(from bundle: Bundle? = nil, nibName: String? = nil,
                    owner: Any? = nil, options: [AnyHashable : Any]? = nil) -> Self? {

    return instancePrivate(from: bundle ?? Bundle.main,
                           nibName: nibName ?? viewId,
                           owner: owner,
                           options: options)
}

private static func instancePrivate<T: UIView>(from bundle: Bundle, nibName: String,
                                              owner: Any?, options: [AnyHashable : Any]?) -> T? {

    guard
        let views = bundle.loadNibNamed(nibName, owner: owner, options: options),
        let view = views.first(where: { $0 is T }) as? T else { return nil }

    return view
}
}

उदाहरण:

guard let customView = CustomView.instance() else { return }

//Here customView has CustomView class type, not UIView.
print(customView is CustomView) // true

0

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

public class CustomView: UIView {

    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var valueLabel: UILabel!

    public convenience init() {
        self.init(frame: CGRect.zero)
    }

    public override convenience init(frame: CGRect) {
        self.init(internal: nil)
        self.frame = frame
    }

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

    fileprivate func commonInit() {
    }
}

fileprivate protocol _CustomView {
}

extension CustomView: _CustomView {
}

fileprivate extension _CustomView {

    // Protocol extension initializer - has the ability to assign to self, unlike
    // class initializers. Note that the name of this initializer can be anything
    // you like, here we've called it init(internal:)

    init(internal: Int?) {
        self = Bundle.main.loadNibNamed("CustomView", owner:nil, options:nil)![0] as! Self;
    }
}

0
  let bundle = Bundle(for: type(of: self))
   let views = bundle.loadNibNamed("template", owner: self, options: nil)
    self.view.addSubview(views?[0] as! UIView)

कोड केवल उत्तर हतोत्साहित करते हैं। कृपया कुछ स्पष्टीकरण जोड़ें कि यह समस्या को कैसे हल करता है, या यह मौजूदा उत्तरों से कैसे भिन्न है। समीक्षा से
निक

0

मैं नीचे दिए गए एक्सटेंशन को पसंद करता हूं

extension UIView {
    class var instanceFromNib: Self {
        return Bundle(for: Self.self)
            .loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
    }
}

इस और शीर्ष उत्तरित एक्सटेंशन के बीच का अंतर यह है कि आपको इसे एक स्थिर या परिवर्तनशील स्टोर करने की आवश्यकता नहीं है।

class TitleView: UIView { }

extension UIView {
    class var instanceFromNib: Self {
        return Bundle(for: Self.self)
            .loadNibNamed(String(describing: Self.self), owner: nil, options: nil)?.first as! Self
    }
}

self.navigationItem.titleView = TitleView.instanceFromNib

आप किस संस्करण के Xcode का उपयोग कर रहे हैं? कृपया सुनिश्चित करें कि आप XCode के नवीनतम संस्करण का उपयोग कर रहे हैं। XCode 11.5 (तारीख के अनुसार नवीनतम संस्करण) के साथ मेरे लिए ठीक काम करता है।
इकोडर

0
    let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
    let shareView = nibs![0] as! ShareView
    self.view.addSubview(shareView)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.