UIView उपवर्ग के लिए Nib को लोड करने का सही तरीका


98

मुझे पता है कि यह सवाल पहले पूछा गया है, लेकिन जवाब विरोधाभासी हैं और मैं उलझन में हूं, इसलिए कृपया मुझे ज्वाला न दें।

मैं UIViewअपने पूरे ऐप में एक पुन: प्रयोज्य उपवर्ग रखना चाहता हूं। मैं एक नीब फ़ाइल का उपयोग करके इंटरफ़ेस का वर्णन करना चाहता हूं।

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

हालांकि मैं एक नीब का उपयोग करके यह कैसे कर सकता हूं? फ़्रेम सेट करने के बिना इंटरफ़ेस बिल्डर में दिए गए आकार को बनाए रखना।

मैं इसे इस तरह से करने में कामयाब रहा, लेकिन मुझे यकीन है कि यह गलत है (यह सिर्फ एक बीनने वाला एक दृश्य है):

 - (id)initWithDataSource:(NSDictionary *)dataSource {
        self = [super init];
        if (self){
            self = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@", [self class]] owner:self options:nil] objectAtIndex:0];
            self.pickerViewData = dataSource;
            [self configurePickerView];
        }
        return self;
    }

लेकिन मैं स्वयं को अधिलेखित कर रहा हूं, और जब मैं इसे त्वरित करता हूं:

FSASelectView *selectView = [[FSASelectView alloc] initWithDataSource:selectViewDictionary];
    selectView.delegate = self;

    selectView.frame = CGRectMake(0, self.view.bottom + 50, [FSASelectView width], [FSASelectView height]);

मुझे इसे आईबी से लेने के बजाए फ्रेम को मैन्युअल रूप से सेट करना होगा।

संपादित करें: मैं इस कस्टम दृश्य को एक दृश्य नियंत्रक में बनाना चाहता हूं, और दृश्य के तत्वों को नियंत्रित करने के लिए पहुंच रखता हूं। मुझे नया व्यू कंट्रोलर नहीं चाहिए।

धन्यवाद

संपादित करें: मुझे नहीं पता कि यह सबसे अच्छा अभ्यास है, मुझे यकीन है कि यह नहीं है, लेकिन यह है कि मैंने यह कैसे किया:

FSASelectView *selectView = [[[NSBundle mainBundle] loadNibNamed:[NSString stringWithFormat:@"%@",[FSASelectView class]] owner:self options:nil] objectAtIndex:0];
    selectView.delegate = self;
    [selectView configurePickerViewWithData:ds];
    selectView.frame = CGRectMake(0, self.view.bottom + 50, selectView.width, selectView.height);
    selectView.alpha = 0.9;
    [self.view addSubview:selectView];
    [UIView animateWithDuration: 0.25 delay: 0 options:UIViewAnimationOptionAllowUserInteraction |UIViewAnimationOptionCurveEaseInOut animations:^{
                            selectView.frame = CGRectMake(0, self.view.bottom - selectView.height, selectView.width, selectView.height);
                            selectView.alpha = 1;
                        } completion:^(BOOL finished) {
                        }];

सही अभ्यास अभी भी चाहता था

क्या इसे निब नाम के साथ एक व्यू कंट्रोलर और इनिट का उपयोग किया जाना चाहिए था? क्या मुझे कोड में कुछ UIView आरंभीकरण विधि में nib को सेट करना चाहिए? या क्या मैंने ठीक किया है?


लेकिन मूल प्रश्न में आपके द्वारा उपयोग की गई कोड की पहली पंक्ति लगभग वैसी नहीं है initWithDataSource? वैसे भी यह तब भी काम करेगा जब आप मालिक को 'निल' के रूप में देंगे।
राकेश

यह ध्यान देने योग्य है कि इन दिनों 99.99999% मामलों में, एक बस कंटेनर दृश्यों का उपयोग कर रहा है , जो अब हर जगह और हमेशा आईओएस में उपयोग किया जाता है। कैसे-लेख
Fattie

ध्यान दें कि आप [NSString stringWithFormat: @ "% @", [FSASelectView वर्ग]] को NSStringFromClass ([FSASelectView वर्ग]) के साथ बदल सकते हैं
naomimringiko

जवाबों:


132
MyViewClass *myViewObject = [[[NSBundle mainBundle] loadNibNamed:@"MyViewClassNib" owner:self options:nil] objectAtIndex:0]

मैं इसे पुन: प्रयोज्य कस्टम विचारों को इनिशियलाइज़ करने के लिए उपयोग कर रहा हूँ।


ध्यान दें कि आप "firstObject" का उपयोग कर सकते हैं , यह थोड़ा साफ है। "FirstObject" NSArray और NSMutableArray के लिए एक आसान तरीका है।

टेबल हेडर के रूप में उपयोग करने के लिए xib को लोड करने का एक विशिष्ट उदाहरण यहां दिया गया है। आपकी फ़ाइल में YourClass.m

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    return [[NSBundle mainBundle] loadNibNamed:@"TopArea" owner:self options:nil].firstObject;
}

आम तौर पर, में TopArea.xib, आप फ़ाइल स्वामी पर क्लिक करते हैं और फ़ाइल स्वामी को YourClass पर सेट करते हैं । तब वास्तव में YourClass.h में आपके पास IBOutlet गुण होंगे। मेंTopArea.xib , आप उन आउटलेट्स पर नियंत्रण खींच सकते हैं।

यह मत भूलिए कि TopArea.xib, आपको स्वयं ही दृश्य पर क्लिक करना होगा और उसे किसी आउटलेट तक खींचना होगा, इसलिए यदि आवश्यक हो, तो आप इसका नियंत्रण कर सकते हैं। (एक बहुत ही सार्थक टिप यह है कि जब आप तालिका सेल पंक्तियों के लिए ऐसा कर रहे हैं, तो आपको पूरी तरह से ऐसा करना होगा - आपको अपने कोड में संबंधित संपत्ति से खुद को कनेक्ट करना होगा।)


कोई initWithNibName नहीं है: एक UIView के लिए विधि, यह सिर्फ UIViewController के लिए है
मेरोनिक्स

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

मुझे क्षमा करें, मैंने अपने जल्दबाजी में गलत कोड कॉपी कर लिया है, मैंने जवाब अपडेट कर दिया है, कृपया देखें।
प्रेमसुराज

मैं इसे सही करने जा रहा हूं। मुझे नहीं पता कि यह सबसे अच्छा अभ्यास है लेकिन यह काम करता है।
एडम वेट

@Joe झटका टक्कर के लिए खेद है, लेकिन किसी को यह क्यों करना होगा: "TopArea.xib में यह मत भूलो, आपको स्वयं दृश्य पर क्लिक करना होगा और उसे किसी आउटलेट पर खींचें, ताकि आपके पास इसका नियंत्रण हो , यदि आवश्यक है।" क्या आप इसके बजाय अंतर्निहित दृश्य का उपयोग करने के लिए myViewObject का उपयोग कर सकते हैं क्योंकि यह एक UIView ही है? संपत्ति की आवश्यकता क्यों है?
user1686342

39

यदि आप अपने CustomViewऔर इसके xibस्वतंत्र को रखना चाहते हैं File's Owner, तो इन चरणों का पालन करें

  • File's Ownerखेत को खाली छोड़ दें ।
  • xibअपनी फ़ाइल में वास्तविक दृश्य पर क्लिक करें CustomViewऔर Custom Classइसे CustomView(अपने कस्टम दृश्य वर्ग का नाम) के रूप में सेट करें
  • जोड़े IBOutletमें.hअपने कस्टम दृश्य की फ़ाइल ।
  • में .xibअपने कस्टम दृश्य की फ़ाइल, दृश्य पर क्लिक करें और में जाना Connection Inspector। यहाँ आप अपने सभी IBOutlets को .hफाइल में परिभाषित करेंगे
  • उन्हें उनके संबंधित दृश्य के साथ कनेक्ट करें।

में .mअपने की फ़ाइल CustomViewवर्ग, ओवरराइड initपालन विधि

-(CustomView *) init{
    CustomView *result = nil;
    NSArray* elements = [[NSBundle mainBundle] loadNibNamed: NSStringFromClass([self class]) owner:self options: nil];
    for (id anObject in elements)
    {
        if ([anObject isKindOfClass:[self class]])
        {
            result = anObject;
            break;
        }
    }
    return result;
}

अब जब आप अपना लोड करना चाहते हैं CustomView, तो कोड की निम्न पंक्ति का उपयोग करें [[CustomView alloc] init];


1
IOS 9 के रूप में, यह इस पृष्ठ पर सूचीबद्ध एकमात्र समाधान है जो वास्तव में मेरे लिए काम करता है (अन्य समाधान दुर्घटना का कारण बनता है)।
जीवनोदय

बस ध्यान दें कि यह दृष्टिकोण मौजूदा XIB से आपके कस्टम दृश्य को लोड करने के साथ संगत नहीं है क्योंकि यह कॉल नहीं करता है -init। इसके बजाय यह फोन करेगा initWithFrame:और -awakeFromNib। यदि आप अपने दृश्य को लोड करने के लिए समान कोड लिखते हैं, तो -initविधि में आप एक अनंत लूप दर्ज करेंगे।
डस्ट

25

निम्न चरणों का पालन करें

  1. MyView .h / .m प्रकार का एक वर्ग बनाएं UIView
  2. एक ही नाम का xib बनाएँ MyView.xib
  3. अब फ़ाइल स्वामी वर्ग को xib में UIViewControllerसे बदल दें NSObject। निचे इमेज देखे यहां छवि विवरण दर्ज करें
  4. फाइल ओनर व्यू को अपने व्यू से कनेक्ट करें। निचे इमेज देखे यहां छवि विवरण दर्ज करें

  5. अपने दृश्य की कक्षा बदलें MyView। 3 के समान।

  6. स्थान नियंत्रण IBOutlets बनाते हैं।

यहाँ देखें लोड करने के लिए कोड है:

UIViewController *controller=[[UIViewController alloc] initWithNibName:@"MyView" bundle:nil];
MyView* view=(MyView*)controller.view;
[self.view addSubview:myview];

आशा करता हूँ की ये काम करेगा।

स्पष्टीकरण :

UIViewControllerआपके xib को लोड करने के लिए उपयोग किया जाता है और देखें जो UIViewControllerवास्तव में है MyViewजिसे आपने MyView xib में असाइन किया है ..

डेमो मैं यहाँ एक डेमो हड़पने बना दिया है


लोग इसे क्यों गलत समझ रहे हैं? मैंने अभी तक कोशिश नहीं की है, लेकिन ऐसा लगता है कि यह मुझे क्या चाहिए।
एडम वेट

मैंने डाउन-वोट किया था, लेकिन इससे मुझे आपका जवाब गलत लगा (मुझे लगा कि आप फ़ाइल के मालिक के रूप में एक यूआईईवीवाई उपवर्ग प्रदान कर रहे हैं)। उसके लिए माफ़ करना।
राकेश

2
अपने xib फ़ाइल में फ़ाइलों के स्वामी को अनदेखा करें। तब आप केवल MyView * व्यू = [[UINib InstantiateWithOwner: nil options: nil] lastObject] करके UIViewController बनाने से बच सकते हैं;
लाइटनिंगस्ट्राइक

1
@LightningStryk हां, हां, लेकिन इसे करने का सिर्फ एक और तरीका है।
iphonic

1
एक पुन: प्रयोज्य UIView उपवर्ग का निर्माण करना है, न कि उस उद्देश्य के लिए UIViewController का उपयोग करना।
arielyz

11

2 या कुछ वर्षों के बारे में मेरे अपने सवाल का जवाब यहाँ पर ...

यह एक प्रोटोकॉल एक्सटेंशन का उपयोग करता है ताकि आप इसे सभी वर्गों के लिए किसी अतिरिक्त कोड के बिना कर सकें।

/*

Prerequisites
-------------
- In IB set the view's class to the type hook up any IBOutlets
- In IB ensure the file's owner is blank

*/

public protocol CreatedFromNib {
    static func createFromNib() -> Self?
    static func nibName() -> String?
}

extension UIView: CreatedFromNib { }

public extension CreatedFromNib where Self: UIView {

    public static func createFromNib() -> Self? {
        guard let nibName = nibName() else { return nil }
        guard let view = NSBundle.mainBundle().loadNibNamed(nibName, owner: nil, options: nil).last as? Self else { return nil }
        return view
    }

    public static func nibName() -> String? {
        guard let n = NSStringFromClass(Self.self).componentsSeparatedByString(".").last else { return nil }
        return n
    }
}

// Usage:
let myView = MyView().createFromNib()

8

स्विफ्ट में:

उदाहरण के लिए, आपके कस्टम वर्ग का नाम है InfoView

सबसे पहले, आप फ़ाइलें बनाते हैं InfoView.xibऔर InfoView.swiftइस तरह:

import Foundation
import UIKit

class InfoView: UIView {
    class func instanceFromNib() -> UIView {
    return UINib(nibName: "InfoView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! UIView
}

फिर सेट File's Ownerकरने के लिए UIViewControllerइस तरह:

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

अपने नाम बदलें Viewकरने के लिए InfoView:

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

File's Ownerअपने viewक्षेत्र को अपने साथ जोड़ने और जोड़ने के लिए राइट-क्लिक करें InfoView:

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

सुनिश्चित करें कि वर्ग का नाम है InfoView:

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

और इसके बाद आप बिना किसी समस्या के अपने कस्टम वर्ग में बटन को जोड़ने की क्रिया कर सकते हैं:

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

और अपने में इस कस्टम वर्ग का उपयोग MainViewController:

func someMethod() {
    var v = InfoView.instanceFromNib()
    v.frame = self.view.bounds
    self.view.addSubview(v)
}

2

वैसे आप या तो व्यू कंट्रोलर का उपयोग करके xib को इनिशियलाइज़ कर सकते हैं और viewController.view का उपयोग कर सकते हैं। या इसे वैसे ही करो जैसे तुमने किया। केवल UIViewनियंत्रक के रूप में उपवर्ग बना रहा हैUIView एक बुरा विचार है।

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

अद्यतन: आपके मामले में:

UIViewController *genericViewCon = [[UIViewController alloc] initWithNibName:@"CustomView"];
//Assuming you have a reference for the activity indicator in your custom view class
CustomView *myView = (CustomView *)genericViewCon.view;
[parentView addSubview:myView];
//And when necessary
[myView.activityIndicator startAnimating]; //or stop

अन्यथा आपको एक कस्टम UIViewControllerबनाना होगा (इसे फ़ाइल के मालिक के रूप में बनाने के लिए ताकि आउटलेट ठीक से वायर्ड हो जाएं)।

YourCustomController *yCustCon = [[YourCustomController alloc] initWithNibName:@"YourXibName"].

आप जहां चाहें उस दृश्य को जोड़ सकते हैं।

[parentView addSubview:yCustCon.view];

हालाँकि, एक अन्य दृश्य नियंत्रक (पहले से किसी अन्य दृश्य के लिए उपयोग किया जा रहा है) को पास करना जबकि xib को लोड करने के लिए एक अच्छा विचार नहीं है क्योंकि नियंत्रक की दृश्य संपत्ति को बदल दिया जाएगा और जब आप मूल दृश्य को एक्सेस करना चाहते हैं, तो आप नहीं करेंगे इसका एक संदर्भ है।

EDIT: यदि आप अपने नए xib को फाइल के मालिक के साथ एक ही मुख्य के रूप में सेट करते हैं, तो आपको इस समस्या का सामना करना पड़ेगाUIViewController वर्ग के और दृश्य गुण को नए xib दृश्य में बाँध दिया है, ।

अर्थात;

  • YourMainViewController - प्रबंधन - mainView
  • CustomView - आवश्यकता होने पर xib से लोड करने की आवश्यकता होती है।

नीचे दिए गए कोड के कारण बाद में भ्रम होगा, यदि आप इसे अंदर लिखते हैं तो देखें कि क्या लोड हुआ है YourMainViewController। ऐसा इसलिए है क्योंकि self.viewइस बिंदु से आपके रिवाज का उल्लेख होगा

-(void)viewDidLoad:(){
  UIView *childView= [[[NSBundle mainBundle] loadNibNamed:@"YourXibName" owner:self options:nil] objectAtIndex:0];
}

ठीक है, इसलिए मूल रूप से, सबसे अच्छा अभ्यास बताता है कि हर यूआईवीवाई में एक संबद्ध नियंत्रक होना चाहिए?
एडम वेट

जरुरी नहीं। लेकिन जब एक अलग xib से दृश्य लोड किया जाता है जो समस्या-मुक्त तरीका होगा। प्री- IOS5 ऐप्पल डॉक्स का कहना है कि एक दृश्य नियंत्रक को एक से अधिक दृश्य (xib) और इसके विपरीत को नियंत्रित करने के लिए उपयोग नहीं किया जाना चाहिए।
राकेश

Xib वास्तव में केवल एक सतर्क दृश्य का आकार है
एडम वेट

मुझे व्यक्तिगत रूप से लगता है, यह कोड / xib की जटिलता पर निर्भर करता है कि आपको एक नया नियंत्रक बनाना है या नहीं। यदि आप सफलतापूर्वक होने वाली समस्याओं को संभाल सकते हैं और यदि आप भविष्य के संशोधनों को नहीं देख रहे हैं तो यह समस्या नहीं है। लेकिन एक अलग दृश्य नियंत्रक बनाने से आपके लिए बहुत सारी समस्याएं हल हो जाएंगी। और जब से आपके मामले में इसकी सिर्फ एक गतिविधि संकेतक है तो आप आसानी से एक UIViewController (जैसा कि उत्तर में उल्लेख किया गया है) का उपयोग कर सकते हैं। मैं इसके अनुसार ऐसा करने के लिए अपने उत्तर को अपडेट करूंगा।
राकेश

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