Film şeridindeki harici bir xib dosyasından görünümü yükle


131

Bir storyboard'daki birden çok görüntü denetleyicisinde bir görünüm kullanmak istiyorum. Bu nedenle, görünümü harici bir xib'de tasarlamayı düşündüm, böylece değişiklikler her görünüm denetleyicisine yansıtılır. Ancak bir storyboard'a harici bir xib'den bir görünüm nasıl yüklenebilir ve bu mümkün mü? Durum bu değilse, mevcut duruma uygun başka hangi alternatifler mevcut?



Yanıtlar:


133

Tam örneğim burada , ancak aşağıda bir özet sunacağım.

Yerleşim

Projenize her biri aynı ada sahip bir .swift ve .xib dosyası ekleyin. .Xib dosyası, özel görünüm düzeninizi içerir (tercihen otomatik düzen kısıtlamalarını kullanır).

Swift dosyasını xib dosyasının sahibi yapın.

görüntü açıklamasını buraya girin kod

Aşağıdaki kodu .swift dosyasına ekleyin ve .xib dosyasından çıkışları ve eylemleri bağlayın.

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
    }
}

Onu kullan

Film şeridinizin herhangi bir yerinde özel görünümünüzü kullanın. Sadece bir ekleyin UIViewve sınıf adını özel sınıf adınıza ayarlayın.

görüntü açıklamasını buraya girin


3
LoadNibNamed çağrıları init (kodlayıcı :) değil mi? Yaklaşımınızı uyarlamaya çalışırken bir kaza geçirdim.
Fishman

@Fishman, görünümü programlı olarak yüklemeye çalışırsanız (film şeridinden değil), şu anda bir init(frame:). Daha fazla ayrıntı için bu eğiticiye bakın.
Suragch

7
Kilitlenmenin diğer bir yaygın nedeni, özel görünümü dosyanın sahibine ayarlamamaktır . Cevabımdaki kırmızı daireye bakın.
Suragch

5
Evet, dosya sahibi yerine kök görünüm sınıfını ayarlamıştım ve bu sonsuz döngüye neden oluyordu.
devios1

2
Self.addSubview'den (görüntüle) önce view.autoresizingMask = [.f flexibleWidth, .f flexibleHeight] satırını ekledikten sonra mükemmel çalışıyor.
Pramod Tapaniya

69

Bir süredir Christopher Swasey'nin yaklaşımı bulduğum en iyi yaklaşımdı. Ekibimdeki birkaç kıdemli geliştiriciye bu konuyu sordum ve içlerinden birinin mükemmel çözümü vardı ! Christopher Swasey'in çok anlamlı bir şekilde ele aldığı endişelerin her birini tatmin ediyor ve standart alt sınıf kodu gerektirmiyor (yaklaşımıyla ilgili temel endişem). Orada bir yakaladım ama oldukça sezgisel ve uygulanması kolaydır bunun dışında.

  1. Xib'inizi kontrol etmek için bir .swift dosyasında özel bir UIView sınıfı oluşturun. yaniMyCustomClass.swift
  2. Bir .xib dosyası oluşturun ve istediğiniz gibi biçimlendirin. yaniMyCustomClass.xib
  3. File's Owner.Xib dosyasının adını özel sınıfınız olacak şekilde ayarlayın ( MyCustomClass)
  4. GOTCHA: .xib dosyasında özel görünümünüzün classdeğerini (altında identity Inspector) boş bırakın . Bu nedenle, özel görünümünüzün belirli bir sınıfı olmayacak, ancak belirli bir Dosya Sahibine sahip olacaktır.
  5. Normalde kullandığınız gibi prizlerinizi bağlayın Assistant Editor.
    • NOT: 'a bakarsanız, Connections InspectorReferans Satış Noktalarınızın özel sınıfınıza (yani MyCustomClass) değil, referans verdiğini fark edeceksiniz File's Owner. Beri File's Ownerözel sınıf olmak belirtildiğinde, çıkışları kanca ve iş Propery olacaktır.
  6. Özel sınıfınızın, sınıf ifadesinden önce @IBDesignable olduğundan emin olun.
  7. Özel sınıfınızı NibLoadableaşağıda belirtilen protokole uygun hale getirin .
    • NOT: Özel sınıf .swiftdosya adınız dosya adınızdan farklıysa .xib, nibNameözelliği dosyanızın adı olacak şekilde ayarlayın .xib.
  8. Aşağıdaki örnek gibi uygulayın required init?(coder aDecoder: NSCoder)ve override init(frame: CGRect)çağırın setupFromNib().
  9. İstediğiniz film şeridine bir UIView ekleyin ve sınıfı, özel sınıf adınız (yani MyCustomClass) olacak şekilde ayarlayın.
  10. IBDesignable'ı .xib'inizi tüm hayranlıkla ve merakla film şeridine çizerken izleyin.

İşte başvurmak isteyeceğiniz protokol:

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
    }
}

Ve işte MyCustomClassprotokolü uygulayan bir örnek (.xib dosyası adlandırılırken 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()
    }

}

NOT: Gotcha'yı kaçırırsanız ve class.xib dosyanızın içindeki değeri özel sınıfınız olarak ayarlarsanız , o zaman film şeridinde çizilmez ve EXC_BAD_ACCESSuygulamayı çalıştırdığınızda bir hata alırsınız çünkü sonsuz bir döngüde takılı kalır. kullanarak uçlu gelen sınıfını başlatmak için çalışıyor init?(coder aDecoder: NSCoder)aranmamanızı yöntemi Self.nib.instantiateve aramaların inittekrar.


2
İşte buna başka bir harika yaklaşım, ancak yukarıdakinin daha iyi olduğunu hissediyorum: medium.com/zenchef-tech-and-product/…
Ben Patch

4
Yukarıda bahsettiğiniz yaklaşım mükemmel bir şekilde çalışır ve doğrudan film şeridinde canlı önizleme uygular. Kesinlikle kullanışlı ve harika, büyük!
Igor Leonovich

1
Bilginize: içinde kısıtlama tanımını kullanan bu çözüm, setupFromNib()XIB ile oluşturulan görünümler içeren otomatik boyutlandırma tablo görünümü hücrelerinde bazı garip otomatik mizanpaj sorunlarını çözüyor gibi görünüyor.
Gary

1
Açık ara en iyi çözüm! @IBDesignableUyumluluğu seviyorum . Bir UIView dosyası eklerken Xcode veya UIKit'in neden varsayılan olarak böyle bir şey sağlamadığına inanamıyorum.
fl034

1
bunu çalıştıracak gibi görünmüyor .. .xib dosyamın içinde Dosya Sahibi'ni her ayarladığımda, AYRICA özel sınıf ayarlıyor.
Drewster

32

Kullanmak istediğiniz bir xib oluşturduğunuzu varsayarsak:

1) UIView için özel bir alt sınıf oluşturun (Dosya -> Yeni -> Dosya ... -> Cocoa Touch Class'a gidebilirsiniz. "Alt Sınıfı:" nın "UIView" olduğundan emin olun).

2) Başlangıçta bu görünüme bir alt görünüm olarak xib'i temel alan bir görünüm ekleyin.

Obj-C'de

-(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;
}

Swift 2'de

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)
}

Swift 3'te

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) Film şeridinizde kullanmak istediğiniz her yerde, normalde yaptığınız gibi bir UIView ekleyin, yeni eklenen görünümü seçin, Identity Inspector'a gidin (sağ üstte, içinde çizgiler bulunan bir dikdörtgene benzeyen üçüncü simge), ve "Özel Sınıf" altında "Sınıf" olarak alt sınıfınızın adını girin.


xibView.frame = self.frame;olmalıdır xibView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);, aksi takdirde görünüm film şeridine eklendiğinde xibView bir ofsete sahip olacaktır.
BabyPanda

partiye geç kaldı ama görünen o ki onu xibView.frame = self.bounds olarak değiştirdi, bu ofset olmayan bir çerçeve
Heavy_Bullets

20
Sonsuz özyineleme nedeniyle bir çökme ile sonuçlanır. Ucun yüklenmesi, alt sınıfın başka bir örneğini oluşturur.
David

2
Xib görünümünün sınıfı, bu yeni alt sınıfla aynı olmamalıdır. Xib MyClass ise, bu yeni sınıfı MyClassContainer yapabilirsiniz.
user1021430

yukarıdaki çözüm sonsuz özyinelemeye çarpacak.
JBarros35

27

(1) otomatik düzen, (2) @IBInspectableve (3) çıkışları vidaladığı için "onu bir alt görünüm olarak ekle" çözümünü her zaman yetersiz buldum . Bunun yerine, size awakeAfter:bir NSObjectyöntemin büyüsünü tanıtmama izin verin .

awakeAfterbir NIB / Storyboard'dan gerçekten uyanmış olan nesneyi tamamen farklı bir nesneyle değiştirmenize izin verir. Bu nesne daha sonra hidrasyon sürecinden geçirilir, awakeFromNibçağrılır, görünüm olarak eklenir vb.

Bunu, görünümümüzün "kartondan kesilmiş" bir alt sınıfında kullanabiliriz, bunun tek amacı görünümü NIB'den yüklemek ve Storyboard'da kullanmak üzere geri göndermek olacaktır. Gömülebilir alt sınıf daha sonra orijinal sınıf yerine Storyboard görünümünün kimlik denetçisinde belirtilir. Bunun çalışması için aslında bir alt sınıf olması gerekmez, ancak IB'nin herhangi bir IBInspectable / IBOutlet özelliğini görmesini sağlayan şey onu bir alt sınıf yapmaktır.

Bu ekstra standart şablon yetersiz görünebilir ve bir anlamda öyledir, çünkü ideal olarak UIStoryboardbunu sorunsuz bir şekilde hallederdi - ancak orijinal NIB'yi ve UIViewalt sınıfı tamamen değiştirilmemiş bırakma avantajına sahiptir . Oynadığı rol temelde bir adaptör veya köprü sınıfının rolüdür ve üzücü olsa bile ek bir sınıf olarak tasarım açısından mükemmel bir şekilde geçerlidir. Diğer taraftan, sınıflarınızla sıkı sıkıya sarılmayı tercih ediyorsanız, @ BenPatch'in çözümü, diğer bazı küçük değişikliklerle bir protokol uygulayarak çalışır. Hangi çözümün daha iyi olduğu sorusu programcı tarzına indirgeniyor: Nesne kompozisyonu mu yoksa çoklu kalıtım mı tercih ediliyor?

Not: NIB dosyasındaki görünümde ayarlanan sınıf aynı kalır. Gömülebilir alt sınıf yalnızca film şeridinde kullanılır. Alt sınıf, görünümü kodda başlatmak için kullanılamaz, bu nedenle herhangi bir ek mantığı olmamalıdır. Bu olmalıdır yalnızca ihtiva awakeAfterkanca.

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

⚠️ Buradaki önemli bir dezavantaj, film şeridinde başka bir görünümle ilgili olmayan genişlik, yükseklik veya en boy oranı kısıtlamalarını tanımlarsanız, bunların manuel olarak kopyalanması gerektiğidir. İki görünümü ilişkilendiren kısıtlamalar en yakın ortak ataya yüklenir ve görünümler içten dışa film şeridinden uyandırılır, bu nedenle bu kısıtlamalar süpervizör görünümünde sulandırıldığında takas zaten gerçekleşmiştir. Yalnızca söz konusu görünümü içeren kısıtlamalar doğrudan bu görünüme yüklenir ve bu nedenle, kopyalanmadıkları sürece takas gerçekleştiğinde atılır.

Burada olan şeyin, film şeridindeki görünümde yüklü olan kısıtlamaların, uç dosyasında tanımlanmış kendi kısıtlamalarına sahip olabilecek yeni örneklenmiş görünüme kopyalanması olduğuna dikkat edin . Bunlar etkilenmez.

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
  }
}  

instantiateViewFromNibiçin tür güvenli bir uzantıdır UIView. Tek yaptığı, türle eşleşen bir tane bulana kadar NIB'nin nesneleri arasında döngü yapmaktır. Genel türün dönüş değeri olduğuna dikkat edin , bu nedenle türün çağrı sitesinde belirtilmesi gerekir.

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
  }
}

Bu akıl almaz. Yine de yanılmıyorsam, bu yalnızca "film şeridinde" çalışır - çalışma zamanında kodda böyle bir sınıf oluşturmaya çalışırsanız, işe yaradığını düşünmüyorum. İnanıyorum.
Şişko

Alt sınıf, tüm amaçlar ve amaçlar için orijinal sınıfın yanı sıra kodda da çalışmalıdır. Görünümü koddaki bir uçtan yüklemek isterseniz, aynı tekniği kullanarak doğrudan somutlaştırmanız yeterlidir. Alt sınıfın yaptığı tek şey, görünümü bir uçtan oluşturmak için kodu almak ve film şeridinin kullanması için bir kancaya koymaktır.
Christopher Swasey

Aslında yanılıyordum - onu örnekleyebilseydiniz tam olarak işe yarardı, ama yapamazsınız, çünkü NIB'deki görünüm, türü olarak süper sınıfa sahip olacak, bu yüzden instantiateViewFromNibhiçbir şey döndürmeyecek. Her iki durumda da önemli bir IMO değil, alt sınıf sadece hikaye tahtasına bağlanmak için bir yaratıcılıktır, tüm kod orijinal sınıfta olmalıdır.
Christopher Swasey

1
Harika! Bir şey beni şaşırttı çünkü xibs ile çok az deneyimim var (sadece hikaye tahtaları ve programatik yaklaşımlarla çalıştım), birisine yardımcı olması durumunda bunu burada bırakarak: .xib dosyasında, en üst düzey görünümü seçmeniz ve ayarlamanız gerekir sınıf türü MyCustomView. Benim xib'imde sol iç kenar çubuğu varsayılan olarak eksikti; açmak için, alt / sol tarafın yakınında "Farklı görüntüle: iPhone 7" özellikleri denetiminin yanında bir düğme vardır.
xaphod

5
Başka bir nesne ile değiştirildiğinde kısıtlamaları frenler. :(
invoodoo

6

Şu anda en iyi çözüm, görünümü bir xib'de tanımlanmış özel bir görünüm denetleyicisi kullanmak ve görünüm denetleyicisini eklerken Xcode'un film şeridi içinde oluşturduğu "görünüm" özelliğini silmektir (adını ayarlamayı unutmayın. özel sınıf olsa da).

Bu, çalışma zamanının otomatik olarak xib'i aramasını ve yüklemesini sağlayacaktır. Bu numarayı her türlü konteyner görünümü veya içerik görünümü için kullanabilirsiniz.


5

alternativeKullanmak XIB viewsiçin View Controller ayrı bir storyboard kullanmayı düşünüyorum .

Ardından özel görünüm kullanıldığı yerde ana Film şeridinde container viewile Embed Segueve sahip StoryboardReferenceBuna özel görünüm denetleyicisi ana film şeridindeki diğer iç görünüm yerleştirilmelidir görünümü.

Ardından, bu gömülü ViewController ile ana görünüm denetleyicisi arasında segmente hazırlanmak suretiyle delegasyon ve iletişim kurabiliriz . Bu yaklaşım, UIView görüntülemekten farklıdır , ancak aynı hedefe ulaşmak için çok daha basit ve daha verimli bir şekilde (programlama açısından) kullanılabilir, yani ana film şeridinde görünen yeniden kullanılabilir özel görünüme sahip olmak

Ek avantaj, CustomViewController sınıfında mantığınızı uygulayabilmeniz ve ayrı (projede bulunması daha zor) denetleyici sınıfları oluşturmadan ve Component kullanarak ana UIViewController'a ortak kod yerleştirmeden tüm delegasyon ve görünüm hazırlığını ayarlayabilmenizdir. Bunun yeniden kullanılabilir bileşenler için iyi olduğunu düşünüyorum. Diğer görünümlere yerleştirilebilen Müzik Çalar bileşeni (widget benzeri).


Gerçekten de xib özel görünümü kullanmaktan çok daha basit bir çözüm :(
Wilson

1
özel UIView'de apple'ın xib yaşam döngüsü oluşturma zinciriyle çevrelere girdikten sonra, işte böyle devam ettim.
LightningStryk

Özel görünümü dinamik olarak eklemek isterseniz, yine de ayrı bir görünüm denetleyicisiyle gitmemiz gerekiyor (en çok tercih edilen XIB)
Satyam

5

En popüler cevaplar işe yarasa da kavramsal olarak yanlışlar. Hepsi File's ownersınıfın çıkışları ve UI bileşenleri arasında bağlantı olarak kullanılır . File's owneryalnızca üst düzey nesneler için kullanılması gerekir, UIViews değil . Check out Apple geliştirici belgesini . UIView'a sahip olmak File's ownerbu istenmeyen sonuçlara yol açar.

  1. Kullanmanız gereken yeri kullanmak zorunda contentViewkalıyorsunuz self. Bu sadece çirkin değil, aynı zamanda yapısal olarak da yanlış çünkü ara görünüm, veri yapısının UI yapısını aktarmasını engelliyor. Bildirimsel kullanıcı arayüzünün tam tersi gibi.
  2. Her Xib için yalnızca bir UIView'a sahip olabilirsiniz. Bir Xib'in birden çok UIViews'a sahip olması gerekir.

Kullanmadan yapmanın zarif bir yolu var File's owner. Lütfen bu blog gönderisine bakın . Nasıl doğru şekilde yapılacağını açıklıyor.


neden kötü olduğunu açıklamamış olman hiç önemli değil. Neden göremiyorum. yan etkisi yoktur.
JBarros35

1

İşte başından beri istediğiniz cevap. Sadece CustomViewsınıfınızı oluşturabilir, sınıfınızın ana örneğini tüm alt görünümler ve çıkışlarla birlikte bir xib'de alabilirsiniz. Ardından, bu sınıfı film şeridinizdeki veya diğer xib'lerdeki herhangi bir örneğe uygulayabilirsiniz.

Dosya Sahibi ile uğraşmanıza, prizleri bir proxy'ye bağlamanıza veya xib'i özel bir şekilde değiştirmenize veya kendi alt görünümü olarak özel görünümünüzün bir örneğini eklemenize gerek yok.

Sadece şunu yap:

  1. BFWControls çerçevesini içe aktarın
  2. Adresinin üst sınıfı değiştirin UIViewiçin NibView(veya UITableViewCellhiç NibTableViewCell)

Bu kadar!

Film şeridinde tasarım zamanında özel görünümünüze (xib'den alt görünümler dahil) başvurmak için IBDesignable ile bile çalışır.

Bu konuda daha fazla bilgiyi buradan okuyabilirsiniz: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155

Açık kaynaklı BFWControls çerçevesini buradan edinebilirsiniz: https://github.com/BareFeetWare/BFWControls

NibReplaceableMerak ediyorsanız, işte onu çalıştıran kodun basit bir özeti :
 https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4

Tom 👣


Şık çözüm, teşekkürler!
AVEE

Yerel olarak sağlanması gerektiğinde bir çerçeve kurmak istemiyorum.
JBarros35

0

Bu çözüm, sınıfınız XIB ile aynı ada sahip olmasa bile kullanılabilir. Örneğin, bir XIB adı controllerA.xib olan bir temel görünüm denetleyici sınıfı denetleyicinizA varsa ve bunu denetleyiciB ile alt sınıflara ayırdıysanız ve bir storyboard'da denetleyiciB'nin bir örneğini oluşturmak istiyorsanız, şunları yapabilirsiniz:

  • film şeridinde görünüm denetleyicisini oluşturun
  • denetleyicinin sınıfını denetleyiciye ayarlayınB
  • film şeridindeki denetleyicinin B görünümünü sil
  • aşağıdakileri yapmak için controllerA'daki yük görünümünü geçersiz kıl:

*

- (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

Ben Patch'in yanıtında açıklanan adımlara göre Objective-C için Çözüm .

UIView için uzantı kullanın:

@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

Dosyaları oluşturun MyView.h, MyView.mve MyView.xib.

Öncelikle MyView.xib, Ben Patch'in cevabının söylediği gibi MyView, bu XIB içindeki ana görünüm yerine Dosya sahibi için sınıf ayarlayın .

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

Ve daha sonra, programlı olarak görünümünüzü oluşturun:

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

Uyarı! Xcode> = 9.2: https://forums.developer.apple.com/thread/95616'daki bu hata nedeniyle WatchKit Uzantısını kullanırsanız bu görünümün önizlemesi Storyboard'da gösterilmeyecektir.


Swift versiyonunu sağlamalıydım. Çok azı şu anda hala ObjC kullanıyor olabilir
Satyam
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.