Interface Builder ile oluşturulan bir uç dosyası kullanarak bir UIView nasıl yüklenir


241

Biraz ayrıntılı bir şey yapmaya çalışıyorum, ancak mümkün olması gereken bir şey. İşte burada tüm uzmanlar için bir meydan okuma (bu forum bir sürü siz var :) :)).

Ben NavigationContoller(benim QuestionManagerViewController) yüklemek istiyorum bir Anket "bileşen", oluşturuyorum . "Bileşen", UIViewControlleryanıtlanması gereken soruya bağlı olarak farklı görünümler yükleyebilen "boş" dur.

Bunu yapmamın yolu:

  1. Question1View nesnesini UIViewalt sınıf olarak oluşturun ve bazılarını tanımlayın IBOutlets.
  2. Oluşturun (Interface Builder'ı kullanarak) Question1View.xib (BURADA SORUN OLDUĞUM YERDE ). Hem set UIViewControllerve UIViewsınıf Question1View olması.
  3. Çıkışları görünümün bileşenine bağlarım (IB kullanarak).
  4. Bunun gibi görünmek için initWithNibbenim geçersiz kılma QuestionManagerViewController:

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
        if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) {
            // Custom initialization
        }
        return self;
    }

Kodu çalıştırdığımda, bu hatayı alıyorum:

2009-05-14 15: 05: 37.152 iMobiDines [17148: 20b] *** Yakalanmayan istisna nedeniyle uygulamayı sonlandırma ' NSInternalInconsistencyException', nedeni: ' -[UIViewController _loadViewFromNibNamed:bundle:]"Question1View" ucunu yükledi, ancak görünüm çıkışı ayarlanmadı.' '

Bir viewController sınıfı oluşturmaya gerek kalmadan, nib dosyasını kullanarak görünümü yüklemek için bir yol olduğundan eminim.

Yanıtlar:


224

Ayrıca, uçla bir dizi olarak ilgilenmek yerine görünüme erişmenin daha kolay bir yolu vardır.

1 ) Daha sonra erişmek istediğiniz çıkışları olan özel bir Görünüm alt sınıfı oluşturun. --Benim görüşüm

2 ) ucu yüklemek ve işlemek istediğiniz UIViewController'da, örneğin, yüklenen uçun görünümünü tutacak bir IBOutlet özelliği oluşturun

MyViewController (bir UIViewController alt sınıfı) içinde

  @property (nonatomic, retain) IBOutlet UIView *myViewFromNib;

(sentezlemeyi ve .m dosyanızda serbest bırakmayı unutmayın)

3) IB'de nibinizi açın (buna 'myViewNib.xib' adını vereceğiz), dosyanızın Sahibi'ni MyViewController olarak ayarlayın

4) şimdi dosyanızın Sahip çıkışı myViewFromNib'i uçtaki ana görünüme bağlayın.

5) Şimdi MyViewController'da aşağıdaki satırı yazın:

[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];

Bunu yapar yapmaz, mülkünüzü "self.myViewFromNib" olarak adlandırmak, görünümünüze uçunuzdan erişmenizi sağlayacaktır!


4
Ana kontrolör görünümü (self.view) için işe yarayacak mı? Bazı nedenlerden dolayı standart kontrolör init yönteminden sonra nib'den ana görünümü yüklemem gerekiyor. Sebep - karmaşık alt sınıf yapısı
Lukasz

@Lukasz Ne kadar başarılı oldunuz, ben bile sizin gibi bir şey arıyorum.
Ajay Sharma

Üzgünüm kalın olabilirim, ama ilk noktada kafam karıştı. Özel görünüm alt sınıfında nasıl çıkış oluştururum? Satış noktalarının sadece UIViewControllers için olduğunu düşündüm.
jowie

1
Bu görünüm birden çok üst görünümde göründüğünde durum ne olacak? Her biri için xib'i manuel olarak düzenlemeyi mi öneriyorsunuz? ÇOK KÖTÜ !!!
user2083364

2
Bu, yalnızca özel ucu tek bir görünüm denetleyicisinde kullanmak istiyorsanız çalışır. Farklı görünüm denetleyicilerinde kullanmak istediğinizde, her biri dinamik olarak özel ucun bir örneğini oluşturur, bu çalışmaz.
thgc

120

Hepinize teşekkür ederim. İstediğimi yapmanın bir yolunu buldum.

  1. İhtiyacınız UIViewolan IBOutlets ile oluşturun .
  2. IB'de xib'i oluşturun, istediğiniz gibi tasarlayın ve şu şekilde bağlayın: Dosya Sahibi sınıftadır UIViewController(Özel alt sınıf yok, ancak "gerçek" bir). Dosya Sahibinin görünümü ana görünüme bağlıdır ve sınıfı 1. adımdan) olarak bildirilir.
  3. Kontrollerinizi IBOutlets ile bağlayın .
  4. DynamicViewControllerYüke / xib görüntülemek karar vermek onun mantığını çalıştırabilirsiniz. Karar verildikten sonra, loadViewyöntemde böyle bir şey koydu:

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
                                                      owner:self
                                                    options:nil];
    
    QPickOneView* myView = [ nibViews objectAtIndex: 1];
    
    myView.question = question;

Bu kadar!

Ana paketin loadNibNamedyöntemi, görünümü başlatmaya ve bağlantıları oluşturmaya özen gösterir.

Artık ViewController, bellekteki verilere bağlı olarak bir görünüm veya başka bir görünüm görüntüleyebilir ve "üst" ekranın bu mantıkla uğraşmasına gerek yoktur.


2
Ben çok benzer bir sorun var - Ben sadece bir xib dosyasından bir UIView yüklemek istiyorum. Bu adımlar benim için işe yaramıyor - uygulama, yüklenen görünümü alt görünüm olarak ekledikten kısa bir süre sonra "kötü erişim" ile kilitleniyor.
Justicle

2
İPhone 3.0 için, ilk öğeyi almak üzere objectAtIndex: 0 kullanın. Bu benim için Justicle tarafından tarif edildiği gibi çöküyor. Neden olduğu hakkında bir fikrin var mı?
tba

1
+1 benim için mükemmel çalıştı. Ben bir görünüm denetleyicisi (görünümün bir bölümünü döner bir flip görünüm senaryosu kullanarak im) olan birden çok görünüm eklemek istedim Ben dizi 0 (iPhone 3.0)
Aran Mulholland

42
Bu doğru cevaptır, ancak kod nibViews üzerinden döngüler ve her nesne üzerinde bir sınıf denetimi yapacak şekilde değiştirilmelidir, böylece kesinlikle doğru nesneyi alırsınız. objectAtIndex: 1 tehlikeli bir varsayımdır.
M. Ryan

2
Jasconius haklı. NibViews dizisini şu şekilde döngüye almalısınız: gist.github.com/769539
leviathan

71

Bazı cevapların neden bahsettiğinden emin değilim, ancak bir dahaki sefere Google'da arama yaptığımda bu yanıtı buraya koymam gerekiyor. Anahtar Kelimeler: "UIBiew bir uçtan nasıl yüklenir" veya "UIView bir NSBundle'dan nasıl yüklenir".

İşte Apress Beginning iPhone 3 kitabından neredeyse% 100 kod: (sayfa 247, "Yeni Tablo Görünümü Hücresini Kullanma"):

- (void)viewDidLoad {
    [super viewDidLoad];
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah"
                                                 owner:self options:nil];
    Blah *blah;
    for (id object in bundle) {
        if ([object isKindOfClass:[Blah class]]) {
            blah = (Blah *)object;
            break;
        }
    }   
    assert(blah != nil && "blah can't be nil");
    [self.view addSubview: blah];
} 

Bu sanıyor Eğer bir var UIViewdenilen alt sınıf Blaholarak adlandırılan bir ucunu Blahbir içerdiği UIViewiçin onun sınıf kümesi vardır ki Blah.


Kategori: NSObject + LoadFromNib

#import "NSObject+LoadFromNib.h"

@implementation NSObject (LoadFromNib)

+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {
    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:classToLoad]) {
            return object;
        }
    }
    return nil;
}

@end

Swift Uzantısı

extension UIView {
    class func loadFromNib<T>(withName nibName: String) -> T? {
        let nib  = UINib.init(nibName: nibName, bundle: nil)
        let nibObjects = nib.instantiate(withOwner: nil, options: nil)
        for object in nibObjects {
            if let result = object as? T {
                return result
            }
        }
        return nil
    }
}

Ve kullanımda bir örnek:

class SomeView: UIView {
    class func loadFromNib() -> SomeView? {
        return self.loadFromNib(withName: "SomeView")
    }
}

2
Eğer kullanırsanız isKindOfDemet yapı değişiklikleri korunur.
Dan Rosenstark

1
Bir assertmuhtemelen daha iyi bir çözüm olacaktır. Bu şekilde yalnızca sorunu gizleyeceksiniz.
Sulthan

1
@ Bir iddia, doğası gereği farklıdır. Nerede olursa olsun Blah tipi bir nesne istiyorum . Terfi edilsin mi yoksa reddedilsin mi umrumda değil. Ancak, yorumunuza yanıt olarak bir iddia ekledim, ancak döngü yerine değilfor . Tamamlar.
Dan Rosenstark

LoadFromNib'i parametre olmadan yapabilirsiniz: + (id) loadFromNib {NSArray * bundle = [[NSBundle mainBundle] loadNibNamed: NSStringFromClass ([self class]) sahip: self options: nil]; for (paketteki id nesnesi) {if ([object isKindOfClass: [self class]])) {return nesnesi; dönüş nil; }
JVC

22

Özel görünümün, yani bir Outlet Koleksiyonu'nun birden fazla örneğini yönetmesi gereken herkes için, @Gonso, @AVeryDev ve @Olie cevaplarını bu şekilde birleştirdim ve özelleştirdim:

  1. Bir özel oluşturun ve istenen XIB'de kökün MyView : UIView" Özel Sınıfı " olarak ayarlayın ; UIViewözel sınıf

  2. İhtiyacınız olan tüm çıkışları oluşturun MyView(şimdi yapın çünkü 3. noktadan sonra IB, çıkışları UIViewControlleristediğimiz gibi özel görünüme değil, bağlantıya bağlamanızı önerecektir ); özel sınıf outlet

  3. XIB özel görünümünün UIViewController" Dosya Sahibi " olarak ayarlayın ; resim açıklamasını buraya girin

  4. Gelen UIViewControlleryeni eklemek UIViewsher örneği için MyViewistediğiniz ve bunları bağlamak UIViewControllerbir yaratma Outlet Collection : bu görüşlerin özel görünüm örnekleri için "sarıcı" görünümler olarak hareket edecek; resim açıklamasını buraya girin

  5. Son olarak, içinde viewDidLoadsenin içinde UIViewControllereklenti aşağıdaki satırları:

NSArray *bundleObjects;
MyView *currView;
NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count];
for (UIView *currWrapperView in myWrapperViews) {
    bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
    for (id object in bundleObjects) {
        if ([object isKindOfClass:[MyView class]]){
            currView = (MyView *)object;
            break;
        }
    }

    [currView.myLabel setText:@"myText"];
    [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal];
    //...

    [currWrapperView addSubview:currView];
    [myViews addObject:currView];
}
//self.myViews = myViews; if need to access them later..

Merhaba ... u nasıl tüm programlı yani bunu biliyorum. xib dosyası kullanarak değil mi?
X-Kodlayıcı

19

Yeniden kullanılmak üzere özel bir UIView oluşturmak için UINib'i kullanırdım

UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];

Bu durumda gerekli dosyalardır MyCustomView.xib , MyCustomViewClass.h ve MyCustomViewClass.m Not o [UINib instantiateWithOwner]döner bir dizi, yeniden kullanım için istediğiniz UIView yansıtan eleman kullanmalıdır yüzden. Bu durumda ilk unsurdur.


1
2 sıralamasında "sıralamasıNib" yerine "customNib" anlamına gelmez
Danny

11

Görünüm denetleyicinizin sınıfını UIViewArayüz Oluşturucu'da bir alt sınıf olacak şekilde ayarlamamalısınız . Bu kesinlikle en azından sorunun bir parçasıdır. Bunu ya UIViewControllerbazı alt sınıfları ya da sahip olduğunuz diğer özel sınıflar olarak bırakın.

Bir xib sadece görünümü yükleme gelince, o varsayımı altında olduğunu vardı (o uzatmak olmasa bile görünümü denetleyicisi çeşit olması UIViewControllerArayüzü'nde Dosyanın Sahibi olarak set ihtiyaçlarınız için çok ağır olabilir) Arayüzünüzü tanımlamak için kullanmak istiyorsanız Oluşturucu. Bunu da doğrulamak için biraz araştırma yaptım. Bunun nedeni, aksi takdirde cihazdaki arayüz öğelerinden herhangi birine erişmenin hiçbir yolu olmamasıdır.UIView veya koddaki kendi yöntemlerinizin olaylar tarafından tetiklenmesinin bir yolu olmamasıdır.

UIViewControllerGörünümleriniz için Dosyanızın Sahibi olarak a kullanırsanız , dosyayı initWithNibName:bundle:yüklemek ve görünüm denetleyicisi nesnesini geri almak için kullanabilirsiniz . IB'de, viewçıkışı xib'deki arayüzünüzle görünüme ayarladığınızdan emin olun . Dosyanızın Sahibi olarak başka bir nesne türü kullanırsanız, dosya sahibinin bir örneğini yönteme ileterek ucu yüklemek için NSBundle' loadNibNamed:owner:options:yöntemini kullanmanız gerekir . Tüm özellikleri, IB'de tanımladığınız çıkışlara göre düzgün bir şekilde ayarlanacaktır.


"İPhone Geliştirmeye Başlama: iPhone SDK'sını Keşfetme" adlı Apress kitabını okudum ve bölüm 9: Gezinme Denetleyicileri ve Tablo Görünümleri'nde bu yöntemi tartıştılar. Çok karmaşık görünmüyordu.
Lyndsey Ferguson

Nasıl yapıldığını söylediklerini görmek isterdim. Şu anda bu kitabın bir kopyası yok.
Marc W

10

LoadNibNamed yerine UIViewController'ın initWithNibName öğesini de kullanabilirsiniz. Daha basit, buldum.

UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release];  // release the VC

Şimdi sadece MySubView.xib ve MySubView.h / m oluşturmanız gerekiyor. MySubView.xib içinde Dosya Sahibi sınıfını UIViewController ve görünüm sınıfını MySubView olarak ayarlayın.

subviewÜst xib dosyasını kullanarak konumunu ve boyutunu belirleyebilirsiniz .


8

Bu harika bir soru (+1) ve cevaplar neredeyse yardımcı oldu;) Üzgünüm çocuklar, ama hem Gonso hem de AVeryDev iyi ipuçları verdi, ancak bu yoluyla slogging bir zaman vardı. Umarım, bu cevap başkalarına yardımcı olacaktır.

MyVC tüm bunları tutan görünüm denetleyicisidir.

MySubview bir xib'den yüklemek istediğimiz görünümdür

  • MyVC.xib'de, MySubViewdoğru boyut ve şekil olan ve istediğiniz yere konumlandırılmış bir tür görünümü oluşturun .
  • MyVC.h içinde

    IBOutlet MySubview *mySubView
    // ...
    @property (nonatomic, retain) MySubview *mySubview;
  • MyVC.m'de @synthesize mySubView;ve onu serbest bırakmayı unutmayın dealloc.

  • MySubview.h'de, bir çıkış / mülkünüz olsun UIView *view(gereksiz olabilir, ancak benim için çalıştı.) Sentezleyin ve .m'de serbest bırakın.
  • MySubview.xib içinde
    • dosya sahibi türünü ayarlayın MySubviewve görünüm özelliğini görünümünüze bağlayın.
    • Tüm bitleri düzenleyin ve IBOutletistediğiniz gibi bağlayın
  • MyVC.m'ye geri dönelim

    NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL];
    MySubview *msView = [xibviews objectAtIndex: 0];
    msView.frame = mySubview.frame;
    UIView *oldView = mySubview;
    // Too simple: [self.view insertSubview: msView aboveSubview: mySubview];
    [[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting
    self.mySubview = msView;
    [oldCBView removeFromSuperview];

Benim için zor biraz oldu: diğer cevaplar ipucu benim bakış xib yük, ama MyVC (duh!) Görünümü yerine DEĞİL - Ben kendi başımıza takas zorunda kaldı.

Ayrıca, mySubviewyöntemlerine erişmek için view.xib dosyasındaki özellik olarak ayarlanmalıdır MySubview. Aksi takdirde, düz bir eski olarak geri gelir UIView.

mySubviewDoğrudan kendi xibinden yüklemenin bir yolu varsa , bu kaya olurdu, ama bu beni olması gereken yere getirdi.


1
Bu yöntemi kullandım, teşekkürler. İç içe görünümleri desteklemek için yaptığım bir değişiklik [ self.view insertSubview: msView aboveSubview: mySubview]; eklemek için [ mySubview.superview insertSubview: msView aboveSubview: mySubview];
Jason

8

Bu daha kolay olması gereken bir şey. UIViewControllerSonunda bir loadNib:inPlaceholder:seçici genişletip ekledim. Şimdi söyleyebilirim

self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];

İşte kategori kodu (Gonso tarafından açıklananla aynı rigamarolü yapar):

@interface UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;

@end

@implementation UIViewController (nibSubviews)

- (UIView *)viewFromNib:(NSString *)nibName
{
  NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; 
  for (id view in xib) { // have to iterate; index varies
    if ([view isKindOfClass:[UIView class]]) return view;
  }
  return nil;
}

- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
  UIView *nibView = [self viewFromNib:nibName];
  [nibView setFrame:placeholder.frame];
  [self.view insertSubview:nibView aboveSubview:placeholder];
  [placeholder removeFromSuperview];
  return nibView;
}

@end

7

Hızlı

Aslında bu sorun benim çözüm, görünümü benim gibi kullanmak istediğim CustonViewController bir viewDidLoad görünümü yüklemek oldu:

myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView

Görünümü bir loadView()yöntemle yüklemeyin ! LoadView yöntemi, görünümü özel ViewController'ınıza yüklemek için kullanılır.


6

Ben de benzer bir şey yapmak istedim, bulduğum şey bu: (SDK 3.1.3)

Ben bir düğmeye basın VC B yükleyen bir görünüm denetleyicisi A (kendisi bir Nav denetleyicisi aittir) var:

AViewController.m içinde

BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];

Şimdi VC B'nin arayüzü Bnib'den geliyor, ancak bir düğmeye basıldığında, farklı bir uçtan ayrı bir kullanıcı arayüzü olan bir 'düzenleme moduna' gitmek istiyorum, ancak düzenleme modu için yeni bir VC istemiyorum, Yeni ucun mevcut B VC ile ilişkilendirilmesini istiyorum.

Yani, BViewController.m'de (düğme basma yönteminde)

NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];

Ardından başka bir düğme üzerinde (düzenleme modundan çıkmak için) tuşuna basın:

[editView removeFromSuperview];

ve orijinal Bnib'ime geri döndüm.

Bu iyi çalışıyor, ancak benim EditMode.nib bir UIView obj içinde sadece 1 üst düzey obj var unutmayın. Bu uçtaki Dosya Sahibinin BViewController veya varsayılan NSObject olarak ayarlanması önemli değildir, ancak Dosya Sahibindeki Görünüm Çıkışının hiçbir şeye ayarlanmadığından emin olun. Eğer öyleyse, o zaman bir exc_bad_access çökmesi alıyorum ve xcode 6677 yığın çerçevelerini tekrar tekrar denen bir iç UIView yöntemi gösteren yüklemeye devam ediyor ... sonsuz bir döngüye benziyor. (Ancak View Outlet orijinal Bnib'imde ayarlandı)

Bu yardımcı olur umarım.


Satır aralarını okumak, bu da bana yardımcı oldu.
petert

Bağlantıdaki bilgilere göre, görünüm denetleyicilerini bu şekilde değiştirmemelisiniz.
T. Markle

6

Sevdiğim bir kategori yaptım:

UIView+NibInitializer.h

#import <UIKit/UIKit.h>

@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end

UIView+NibInitializer.m

#import "UIView+NibInitializer.h"

@implementation UIView (NibInitializer)

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    if (!nibNameOrNil) {
        nibNameOrNil = NSStringFromClass([self class]);
    }
    NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil
                                                        owner:self
                                                      options:nil];
    for (id view in viewsInNib) {
        if ([view isKindOfClass:[self class]]) {
            self = view;
            break;
        }
    }
    return self;
}

@end

Ardından şu şekilde arayın:

MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];

Uçunuza sınıfınızın adı dışında bir ad verilmişse bir uç adı kullanın.

Ek davranış için alt sınıflarınızda geçersiz kılmak için şöyle görünebilir:

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
    self = [super initWithNibNamed:nibNameOrNil];
    if (self) {
        self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
    }
    return self;
}

5

Tasarlanabilir seçenekli Swift kullanıcıları için:

  1. Kendi sınıf adımızdan sonra isimlendireceğimiz özel bir UIViewalt sınıf ve bir xib dosyaları oluşturun : bizim durumumuzda MemeView. Meme View sınıfının içinde, @IBDesignablesınıf bildiriminden önce öznitelikle tasarlanabilir olarak tanımlamayı unutmayın
  2. Indetity UIViewInspector panelindeki özel alt sınıfımızla xib'deki Dosya Sahibini ayarlamak için Rember

    resim açıklamasını buraya girin

  3. Xib dosyasında artık arayüzümüzü oluşturabilir, kısıtlamalar yapabilir, çıkışlar, eylemler oluşturabiliriz. resim açıklamasını buraya girin

  4. Xib'i başlatıldıktan sonra açmak için özel sınıfımıza birkaç yöntem uygulamamız gerekiyor

    class XibbedView: UIView {

    weak var nibView: UIView!
    
    override convenience init(frame: CGRect) {
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        self.init(nibName: nibName)
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    init(nibName: String) {
        super.init(frame: CGRectZero)
        let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
        let nib = loadNib(nibName)
        nib.frame = bounds
        nib.translatesAutoresizingMaskIntoConstraints = false
        addSubview(nib)
        nibView = nib
        setUpConstraints()
    }
    
    func setUpConstraints() {
        ["V","H"].forEach { (quote) -> () in
            let format = String(format:"\(quote):|[nibView]|")
            addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView]))
        }
    }
    
    func loadNib(name: String) -> UIView {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: name, bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
    
        return view
    }

    }

  5. Özel sınıfımızda, arayüz oluşturucu üzerinde tam kontrol sahibi olmak için bazı ölçülebilir özellikler de tanımlayabiliriz

    @IBDesignable class MemeView: XibbedView {

    @IBInspectable var memeImage: UIImage = UIImage() {
        didSet {
            imageView.image = memeImage
        }
    }
    @IBInspectable var textColor: UIColor = UIColor.whiteColor() {
        didSet {
            label.textColor = textColor
        }
    }
    @IBInspectable var text: String = "" {
        didSet {
            label.text = text
        }
    }
    @IBInspectable var roundedCorners: Bool = false {
        didSet {
            if roundedCorners {
                layer.cornerRadius = 20.0
                clipsToBounds = true
            }
            else {
                layer.cornerRadius = 0.0
                clipsToBounds = false
            }
        }
    }
    
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var imageView: UIImageView!

}

Birkaç örnek:
resim açıklamasını buraya girin resim açıklamasını buraya girin

Görünümü bir storyboard veya başka bir xib içinde görüntülenirken daha fazla bilgi eklememiz gerekirse, bunu yapabilmemiz için prepareForInterfaceBuilder()bu yöntem yalnızca dosyayı arayüz oluşturucuda açarken yürütülür. Yazdığım her şeyi yaptıysanız, ancak hiçbir şey çalışmıyorsa, uygulamasına kesme noktaları ekleyerek tek bir görünümde hata ayıklamanın bir yoludur. İşte görünümler hiyerarşisi. resim açıklamasını buraya girin
Hiearchy görüntüle

Bu tam bir örnek indirilebilir yardımcı olur umarım burada



let nib = loadNib(nibName)bu bir XibbedView örneğidir, eğer `` addSubview (nib) `` derseniz, başka bir XibbedView örneğine bir XibbedView örneği mi ekliyorsunuz?
William Hu

bu konuda hemfikirim. "Hata ayıklama görünümü hiyerarşisi" görmeye çalıştığınızda XibbedView bir XibbedView içeriyor gibi görünüyor. Görebilirsiniz.
William Hu

@WilliamHu, sağladığınız örneği yüklerseniz, MemeView.xib'in yalnızca bir sürü alt görüntülemeye sahip bir UIView örneği olduğunu görebilirsiniz, dosya sahibi XibbedView'ın bir alt sınıfı olan bir MemeView'dir. XibbedView'ın tek örneği, ana görünümün yalnızca bir görünüm olduğu bir xib dosyası yükleyen sadece MemeView'dur
Andrea

AA tamam o zaman. Test edeceğim. Teşekkür ederim!
William Hu

4

Bu blog gönderisini Aaron Hillegass (yazar, eğitmen, Cocoa ninja) tarafından çok aydınlatıcı buldum . Belirtilen bir başlatıcı aracılığıyla NIB dosyalarını yüklemek için değiştirilmiş yaklaşımını benimsemeseniz bile, muhtemelen en azından devam eden süreci daha iyi anlayacaksınız. Son zamanlarda bu yöntemi büyük başarı için kullanıyorum!


Bağlantı öldü. Şimdi bignerdranch.com/blog/…
joelliusp

3

Önceki cevap, iPhone SDK'nın 2.0 ile 2.1 arasında meydana gelen NIB (XIB) yapısındaki bir değişikliği dikkate almaz. Kullanıcı içerikleri artık 1 yerine 0 dizininden başlıyor.

Tüm 2.1 ve sonraki sürümler için geçerli olan 2.1 makrosunu kullanabilirsiniz (IPHONE'dan önceki iki alt çizgi:

 // Cited from previous example
 NSArray* nibViews =  [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil];
 int startIndex;
 #ifdef __IPHONE_2_1
 startIndex = 0;
 #else
 startIndex = 1;
 #endif
 QPickOneView* myView = [ nibViews objectAtIndex: startIndex];
 myView.question = question;

Uygulamalarımızın çoğunda buna benzer bir teknik kullanıyoruz.

Barney


2
Barney: "Önceki cevap" Yığın Taşması üzerinde anlamsızdır, çünkü cevaplar oylamaya göre konumu değiştirir. “Fred'in cevabı” veya “Barney'nin cevabı” veya “olie'nin cevabı” na atıfta bulunmak daha iyidir.
Olie

3

Ben aynı şeyi (programlı bir XIB dosyasından bir görünüm yükleme) yapmak için neden vardı, ama bunu tamamen bir alt sınıf bir alt sınıf bağlamında UIView(yani herhangi bir şekilde görünüm denetleyicisi dahil olmadan) yapmak gerekiyordu. Bunu yapmak için bu yardımcı yöntemi yarattım:

+ (id) initWithNibName:(NSString *)nibName withSelf:(id)myself {

    NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName
                                                    owner:myself options:nil];
    for (id object in bundle) {
        if ([object isKindOfClass:[myself class]]) {
            return object;
        }
    }  

    return nil;
} 

Sonra benim alt sınıf initWithFrameyönteminden şöyle çağırıyorum :

- (id)initWithFrame:(CGRect)frame {

    self = [Utilities initWithNibName:@"XIB1" withSelf:self];
    if (self) {
        // Initialization code.
    }
    return self;
}

Genel ilgi için gönderildi; herhangi biri bu şekilde yapmadan herhangi bir sorun görürse, lütfen bana bildirin.


Kodunuzu uyguladım, ancak init üzerinde kapsam dışına çıkıyor. Statik yönteminizi bir Utilities sınıfında yaptım. MyView (UIView alt sınıfı) adlı h / m dosyaları ve aynı adlı bir xib yaptım. IB'de xib dosyalarının sahibi VE görünümünü "MyView" olarak ayarladım. Hata ayıklayıcıda kapsam dışı. IB'de bir şey mi eksik?
TigerCoding

Lütfen buradaki yeni SO soruma bir göz atın: stackoverflow.com/questions/9224857/…
TigerCoding

self = [Yardımcı programlar initWithNibName: @ "XIB1" withSelf: self]; Henüz ayarlanmadığında kendini geçiyorsun. Bu kod şu şekilde değil: self = [Yardımcı programlar initWithNibName: @ "XIB1" withSelf: nil]; ?
Ken Van Hoeylandt

@Javy: Buradaki kod örneğim ARC ile çalışmayabilir - ARC etkin olarak test etmedim.
MusiGenesis

3

İşte Swift'te yapmanın bir yolu (şu anda XCode 7 beta 5'te Swift 2.0 yazıyor).

Senin itibaren UIViewsize (benim alt sınıf RecordingFooterView denir) böyle bir yöntem oluşturmak Interface Builder "Özel Sınıf" olarak ayarlamanız alt sınıf:

class func loadFromNib() -> RecordingFooterView? {
    let nib = UINib(nibName: "RecordingFooterView", bundle: nil)
    let nibObjects = nib.instantiateWithOwner(nil, options: nil)
    if nibObjects.count > 0 {
        let topObject = nibObjects[0]
        return topObject as? RecordingFooterView
    }
    return nil
}

Sonra sadece şöyle diyebilirsiniz:

let recordingFooterView = RecordingFooterView.loadFromNib()


2

Yanıtların hiçbiri, bu sorunun kökü olan tek başına XIB'nin nasıl oluşturulacağını açıklamıyor. Xcode 4"Yeni XIB Dosyası Oluştur" seçeneği yoktur .

Bunu yapmak için

1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format

Bu basit görünebilir, ancak "XIB" kelimesi hiçbir yerde görünmediği için birkaç dakika kurtarabilir.


1

@AVeryDev

6) Yüklenen görünümü görünüm denetleyicinizin görünümüne eklemek için:

[self.view addSubview:myViewFromNib];

Muhtemelen, bellek sızıntılarını önlemek için görünümden çıkarmak gerekir.

Açıklığa kavuşturmak için: görünüm denetleyicisinde, bazıları orijinal uç dosyasındaki öğelere (her zamanki gibi) bağlı, bazıları ise yüklenen uçtaki öğelere bağlı birkaç IBOutlet vardır. Her iki uç da aynı sahip sınıfına sahiptir. Yüklenen görünüm orijinal görüntüyü kaplar.

İpucu: Yüklenen uçtaki ana görünümün opaklığını sıfıra ayarlayın, ardından öğeleri orijinal uçtan gizlemez.


dikkat edilmesi gereken iyi bir şey, ancak bir alt görünüm olarak bir görünüm eklemek, önceki üst öğesinden otomatik olarak kaldırır, bu nedenle sızıntı yapmamalıdır.
averydev

1

Birkaç saat geçirdikten sonra, aşağıdaki çözümü çıkardım. Özel oluşturmak için aşağıdaki adımları izleyin UIView.

1) sınıf oluşturma myCustomView miras UIView .
resim açıklamasını buraya girin

2) myCustomView adıyla .xib oluşturun .
resim açıklamasını buraya girin

3) .xib dosyanızın içindeki UIView Sınıfını değiştirin , oraya myCustomView Sınıfını atayın .
resim açıklamasını buraya girin

4) IBOutlet'ler oluşturun
resim açıklamasını buraya girin

5) myCustomView * customView dosyasına .xib yükleyin . Aşağıdaki örnek kodu kullanın.

myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];

Not: Sorunla hala karşılaşan kişiler yorum yapabilir, onlara myCustomView ile örnek proje bağlantısı sağlayacağım


1

En kısa sürüm:

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];

0

Ben Xibs onları görünümü ile aynı görünümleri adlandırma bir kuralı var. Bir görünüm denetleyicisinin yaptığı gibi. Sonra, kod sınıf dersleri yazmak zorunda değilsiniz. Aynı ada sahip bir uç dosyasından bir UIView yüklüyorum.

MyView adlı bir sınıf örneği.

  • Interface Builder'da MyView.xib adlı bir uç dosyası oluşturun
  • Arayüz Oluşturucu'da bir UIView ekleyin. Sınıfını MyView olarak ayarlayın. Kalbinizin içeriğini özelleştirin, MyView'ın örnek değişkenlerini daha sonra erişmek isteyebileceğiniz alt görünümlere bağlayın.
  • Kodunuzda şu şekilde yeni bir MyView oluşturun:

    MyView * myView = [MyView nib_viewFromNibWithOwner: owner];

İşte bunun için kategori:

@implementation UIView (nib)

+ (id) nib_viewFromNib {
    return [self nib_viewFromNibWithOwner:nil];
}

+ (id) nib_viewFromNibWithOwner:(id)owner {
    NSString *className = NSStringFromClass([self class]);
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil];
    UIView *view = nil;
    for(UIView *v in nib) {
        if ([v isKindOfClass:[self class]]) {
            view = v;
            break;
        }
    }
    assert(view != nil && "View for class not found in nib file");
    [view nib_viewDidLoad];
    return view;
}

// override this to do custom setup
-(void)nib_viewDidLoad {

}

Daha sonra kullandığım denetleyiciden eylemlerle düğmeleri bağlarım ve özel görünüm alt sınıfımdaki çıkışları kullanarak etiketlerdeki şeyleri ayarlardım.


0

Swift 4'te bir uç / xib'den bir görünümü programlı olarak yüklemek için:

// Load a view from a Nib given a placeholder view subclass
//      Placeholder is an instance of the view to load.  Placeholder is discarded.
//      If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {

    let nib = loadNib(givenNibName, placeholder: placeholderView)
    return instantiateView(fromNib: nib, placeholder: placeholderView)
}

// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
    //1. Load and unarchive nib file
    let nibName = givenNibName ?? String(describing: type(of: placeholderView))

    let nib = UINib(nibName: nibName, bundle: Bundle.main)
    return nib
}

// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
    //1. Get top level objects
    let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)

    //2. Have at least one top level object
    guard let firstObject = topLevelObjects.first else {
        fatalError("\(#function): no top level objects in nib")
    }

    //3. Return instantiated view, placeholderView is not used
    let instantiatedView = firstObject as! T
    return instantiatedView
}

-1

Bunun için UIView'e bir kategori ekledim:

 #import "UIViewNibLoading.h"

 @implementation UIView (UIViewNibLoading)

 + (id) loadNibNamed:(NSString *) nibName {
    return [UIView loadNibNamed:nibName fromBundle:[NSBundle mainBundle] retainingObjectWithTag:1];
 }

 + (id) loadNibNamed:(NSString *) nibName fromBundle:(NSBundle *) bundle retainingObjectWithTag:(NSUInteger) tag {
    NSArray * nib = [bundle loadNibNamed:nibName owner:nil options:nil];
    if(!nib) return nil;
    UIView * target = nil;
    for(UIView * view in nib) {
        if(view.tag == tag) {
            target = [view retain];
            break;
        }
    }
    if(target && [target respondsToSelector:@selector(viewDidLoad)]) {
        [target performSelector:@selector(viewDidLoad)];
    }
    return [target autorelease];
 }

 @end

burada açıklama: viewcontroller daha az ios ve mac görünümünde yükleme


Hrm. Etiketi nedir?
n13

hangi üst düzey nesnenin tutulacağını bilmek. gerekliydi, ama muhtemelen şu an ARC'ye uymak için kalıntıları çıkarabilirsiniz.
gngrwzrd
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.