Swift'te uçtan bir UIView yükleyin


148

İşte benim özelleştirilmiş için bir uç yüklemek için kullandığım Objective-C kodu UIView :

-(id)init{

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

}

Swift'de eşdeğer kod nedir?

Yanıtlar:


172

Orijinal Çözüm

  1. Bir XIB ve SomeView adlı bir sınıf (kolaylık ve okunabilirlik için aynı adı kullandı) oluşturdum. İkisini de bir UIView'e dayandım.
  2. XIB'de, "Dosya Sahibi" sınıfını SomeView (kimlik denetçisinde) olarak değiştirdim.
  3. SomeView.swift içinde bir UIView çıkış oluşturdum, XIB dosyasındaki en üst düzey görünüme bağlayarak (kolaylık sağlamak için "görünüm" olarak adlandırılır). Daha sonra XIB dosyasındaki diğer denetimlere gerektiği gibi başka çıkışlar ekledim.
  4. SomeView.swift'te, "kodla init" başlatıcısının içine XIB yükledim. "Kendine" hiçbir şey atamaya gerek yoktur. XIB yüklenir yüklenmez, üst seviye görünüm de dahil olmak üzere tüm çıkışlar bağlanır. Eksik olan tek şey, görünüm hiyerarşisine üstten görünüm eklemektir:

.

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

Bu şekilde kendini uçtan yükleyen bir sınıf olsun unutmayın. Daha sonra projede (arayüz oluşturucu veya programlı olarak) UIView kullanılabilir olduğunda bir sınıf olarak SomeView kullanabilirsiniz.

Güncelleme - Swift 3 sözdizimini kullanarak

Aşağıdaki uzantıya bir xib yüklemek, örnek yöntem olarak yazılır; bu yöntem, daha sonra yukarıdaki gibi bir başlatıcı tarafından kullanılabilir:

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. Döndürülen görünümden dolayı çıkarılabilir bir dönüş değeri kullanmak, tüm çıkışlar zaten bağlı olduğunda arayan kişinin ilgisini çekmez.
  2. Bu, UIView türünde isteğe bağlı bir nesne döndüren genel bir yöntemdir. Görünümü yükleyemezse, nil döndürür.
  3. Geçerli sınıf örneğiyle aynı ada sahip bir XIB dosyası yüklenmeye çalışılıyor. Bu başarısız olursa, sıfır döndürülür.
  4. Üst düzey görünümü görünüm hiyerarşisine ekleme.
  5. Bu satırda, görünümü düzenlemek için kısıtlamalar kullandığımız varsayılmaktadır.
  6. Bu yöntem, üst, alt, önde gelen ve sondaki kısıtlamaları ekler - görünümü her taraftan "kendime" ekler (Ayrıntılar için bkz. Https://stackoverflow.com/a/46279424/2274829 )
  7. Üst seviye görünümüne geri dönme

Ve arayan yöntemi şöyle görünebilir:

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, içeriğini SomeClass.xib dosyasından yükleyen bir UIView alt sınıfıdır. "Son" anahtar kelime isteğe bağlıdır.
  2. Görünüm bir film şeridinde kullanıldığında bir başlatıcı (film şeridi görünümünüzün özel sınıfı olarak SomeClass'ı kullanmayı unutmayın).
  3. Görünümün programlı olarak oluşturulduğu zaman için bir başlatıcı (yani: "let myView = SomeView ()").
  4. Bu görünüm otomatik düzen kullanılarak düzenlendiği için tamamen sıfır çerçevesini kullanma. Otomatik yerleşim yalnızca projemizde kullanıldığından, bir "init (frame: CGRect) {..}" yönteminin bağımsız olarak oluşturulmadığını unutmayın.
  5. & 6. Uzantıyı kullanarak xib dosyasını yükleme.

Kredi: Bu çözümde genel bir uzantı kullanmak Robert'ın aşağıdaki cevabından ilham aldı.

Düzenle Karışıklığı önlemek için "view" ifadesini "contentView" olarak değiştirmek. Ayrıca dizi alt betiği ".first" olarak değiştirildi.


7
Sınıf adını File's Ownerisabetli olarak ayarlamak ... Teşekkürler!
Aviel Gross

13
UIView mülk görünümüne sahip değil, bu yüzden self.view çağrısı hataya neden oluyor
Nastya Gorban

4
@NastyaGorban self.view aslında bu durumda GK100'ün .xib'deki üst düzey görünümden SomeView.swift'e bağladığı outlet özelliğine ("görünüm") atıfta bulunur. "Dediğiniz gibi NSView sınıflarında mülkiyet.
Ausiàs

3
Uç (loadNibNamed) yüklenirken çökme alıyorum. Xcode 6.3 ve Swift kullanma
karthikPrabhu Alagu

11
çağırmadan fromNib()içinden init(coder aDecoder: NSCoder)iç Nib yüklenirken olarak sonsuz döngü oluşturur fromNib(): yöntemine bir çağrı yaparinit(coder aDecoder: NSCoder)
Matthew Cawley

334

Benim katkım:

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

Sonra şöyle deyin:

let myCustomView: CustomView = UIView.fromNib()

..ya da:

let myCustomView: CustomView = .fromNib()

20
Şimdiye kadar en iyi cevap.
CodyMace

7
En iyi cevap burada. Temiz ve Basit
Markiz Draggon

3
@YuchenZhong - Ben bir isteğe bağlı dönecekti. Eğer paketini açmaya zorlarsanız, daha güvenli olmaz. ... ve soru şu: Cevap: Yapabilirsiniz. Bunda yanlış bir şey yok. Ama ... hiç sıfır döndürürse, xib / sınıfının adı eşleşmez. Bu bir geliştirici hatasıdır ve hemen yakalanmalı ve asla üretime geçmemelidir. Burada bazı garip durumda bırakarak app çökmesini tercih ederim. Sadece benim 2 sent / tercih.
Robert Gummesson

1
@allenlinli - Yöntem, CustomView olması gerektiği gibi UIView'in statik bir uzantısıdır. Derleyici, açık tür ek açıklamasını kullanarak türü girdiği için çalışır. CustomView bir UIView alt sınıfı olduğundan ve tür zaten çıkartılmış olduğundan, onu tekrar çıkarmamız gerekmez, bu nedenle ikinci örneğimde gösterildiği gibi UIView atlanabilir. Bunu söyledikten sonra, çağrıyı da sizin yaptığınız gibi yapabilirsiniz.
Robert Gummesson

3
Bu çözüm, .xib içinde özel bir görünüm olduğunda benim için işe yaramadı. Ben bu kısmı düzeltmek için öneriyoruz: dönüş Bundle.main.loadNibNamed (Dize (tanımlayan: kendini), sahibi: nil, seçenekleri: nil)! [0] olarak! T
Nadzeya

79

Artık -> Selfhızlı bir şekilde geri dönebilmek , bunu biraz basitleştirmeye yardımcı oluyor. En son Swift 5'te onaylandı.

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

Senin Eğer .xibdosya ve alt sınıf aynı adı paylaşan, şunları kullanabilirsiniz:

let view = CustomView.fromNib()

Özel bir adınız varsa, şunu kullanın:

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

NOT:

"YourType türünde görünüm bulunamadı .." hatasını alıyorsanız, görünümün sınıfını .xib dosyada

.xibDosyadan görünümünüzü seçin cmd + opt + 4ve classgirişte ve tuşlarına basarak sınıfınızı girin


1
Bunu XCode 7.1 beta 3 altında çalışmak için alamıyorum - bir beta şey olup olmadığından emin değilim ama temelde Swift'te bir uçtan özel bir görünüm oluşturmak için her yolu denedim ve her zaman aynı sonucu elde ediyorum: oluşturduğu sınıf çıkışlarla KVC uyumlu değildir. Yanlış yaptığım bir şey olup olmadığından emin değilim ancak sınıfım oldukça basit ve Dosya Sahibi doğru. Bunu her zaman Objective-C altında yapardım.
Echelon

1
@Logan gerçekten kodunuzla ilgili değil, ancak imo özel görünümleri Storyboard / XIB'den yüklemeyi desteklemelidir. Benim yorumum sadece bu tür görüşler oluşturmak isteyenler için bir bildirimdi
Nikita

1
Not Bu işlevi çağırmanın ikinci biçimini kullanarak bir sorunum var, yani let myCustomView = UIView.fromNib() as? CustomView. Bu durumda, yerine T.selfkarar verir ve ucu bulamaz. Bunun neden olduğundan emin değilim - belki fonksiyon a olarak adlandırılan araçlar için çıkarılan tip ? UIViewCustomViewletUIView
Echelon

2
(İyi olde günlerde yaptığımız gibi) çıkışları bağlamak için File's Owner kullanmaya çalışmanın bunun çökmesine neden olacağını belirtmek önemlidir. IB'de, Dosya Sahibi nil / boş olmalı ve çıkışlar bunun yerine görünüme bağlanmalıdır.
Echelon

1
@Echelon günümü kurtardın !!! Çıkışlarımı Dosya Sahibini kullanarak bağladım ve çalışma yerine görünümü kullanarak işe yaramadı.
Jan Schlorf

19

aşağıdaki kodu deneyin.

var uiview :UIView?

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

Düzenle:

import UIKit

class TestObject: NSObject {

     var uiview:UIView?

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


}

Bu yöntemi Swift init () olan nesne başlatma yöntemi içinde çağırmak gerekir.
Bagusflyer

12

Swift 4 Protokol Uzantıları

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

    }

}

Benimseme

class MyView: UIView, NibInstantiatable {

}

Bu uygulama, Ucu'nun UIView sınıfıyla aynı ada sahip olduğunu varsayar. Ör. MyView.xib. Bu davranışı, varsayılan protokol uzantısı uygulamasından farklı bir ad döndürmek için MyView'da nibName () uygulayarak değiştirebilirsiniz.

Xib'de dosyaların sahibi MyView ve kök görünüm sınıfı MyView olur.

kullanım

let view = MyView.fromNib()

3
Bu açık ara en zarif ve anlaşılır çözüm ve neden kabul edilen cevap olmadığı konusunda hiçbir fikrim yok!
at

@ nal 7 çünkü sorudan 4 yıl sonra yazılmış.
Freeubi

11

Swift ile bunu aşağıdaki kodla başardım:

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

Senin XIB bağlamak unutmayın görünüm için çıkış görüntüsü Swift tanımlanan çıkış. Ek çıkışları bağlamaya başlamak için İlk Yanıtlayıcı'yı özel sınıf adınıza da ayarlayabilirsiniz.

Bu yardımcı olur umarım!


10

Xcode 7 beta 4, Swift 2.0 ve iOS9 SDK'da test edildi. Aşağıdaki kod uiview'e xib atayacaktır. Bu özel xib görünümünü film şeridinde kullanabilir ve IBOutlet nesnesine de erişebilirsiniz.

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



    }


}

Özel görünüme programlı olarak erişin

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

Kaynak kodu - https://github.com/karthikprabhuA/CustomXIBSwift


9

Projenizde çok sayıda özel görünümünüz varsa, aşağıdaki gibi sınıf oluşturabilirsiniz UIViewFromNib

Hızlı 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)
    }
}

Hızlı 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)
    }
}

Ve her sınıfta sadece devralınan UIViewFromNib, dosyanın farklı bir adı nibNamevarsa özelliği geçersiz kılabilirsiniz .xib:

class MyCustomClass: UIViewFromNib {

}

8

Yukarıdaki çözümler üzerine inşa.

Bu, tüm proje paketlerinde çalışır ve fromNib () öğesini çağırırken jeneriklere gerek yoktur.

Hızlı 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)
    }
}

Hızlı 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)
    }
}

Bu şekilde kullanılabilir:

let someView = SomeView.fromNib()

Veya bunun gibi:

let someView = SomeView.fromNib("SomeOtherNibFileName")

6

Hızlı 4

".First as? CustomView" yazmayı unutmayın.

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

Herhangi bir yerde kullanmak istiyorsanız

En İyi Çözüm Robert Gummesson'un cevabıdır.

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

Sonra şöyle deyin:

let myCustomView: CustomView = UIView.fromNib()

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

Ancak Swift init () içinde döndürülen bir değer yoktur. Bir UIView başlatılmasında loadNibNamed'i çağırmam gerektiğini söylemeyi unuttum.
Bagusflyer

Ne demek "dönüş değeri yok"? selfdolaylı olarak tüm inityöntemlerden döndürülür ...
Grimxn

1
Demek istediğim init yöntemi içinde loadNibNamed diyorum. yüklenen UIView, ObjC'de kendisine atanır. Ama çabucak değil.
Bagusflyer

5

Swift ile bunu yapmanın en iyi yolu bir numaralandırma kullanmaktır.

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

Daha sonra kodunuzda şunları kullanabilirsiniz:

let view = Views.view1.getView()

2
Bunu boş bir uç dosyası veya hiçbir UIView kök düğümü olan bir uç dosyası ile yaparsanız, dizi boyutunu veya 0 konumundaki öğeyi aklı kontrol etmediğiniz için çökeceğinizi unutmayın.
Matthew Cawley

4

Bu çözümü tercih ediyorum (@ GK100 ise cevaba dayanarak):

  1. Bir XIB ve SomeView adlı bir sınıf (kolaylık ve okunabilirlik için aynı adı kullandı) oluşturdum. Her ikisini de bir UIView'e dayandım.
  2. XIB'de, "Dosya Sahibi" sınıfını SomeView (kimlik denetçisinde) olarak değiştirdim.
  3. SomeView.swift içinde bir UIView çıkış oluşturdum, XIB dosyasındaki en üst düzey görünüme bağlayarak (kolaylık sağlamak için "görünüm" olarak adlandırılır). Daha sonra XIB dosyasındaki diğer denetimlere gerektiği gibi başka çıkışlar ekledim.
  4. SomeView.swift'te, XIB'yi initveya init:frame: CGRectbaşlatıcı içine yükledim . "Kendine" hiçbir şey atamaya gerek yoktur. XIB yüklenir yüklenmez, üst seviye görünüm de dahil olmak üzere tüm çıkışlar bağlanır. Eksik olan tek şey, görünüm hiyerarşisine üstten görünüm eklemektir:

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

i bu çerçeve sökülmüş çerçeve ile init kullanmayı tercih! not etmek için bir şey ... görünümün geçtiğiniz çerçeveyle eşleşmesini istiyorsanız self.view.frame = frame'i ekleyin
Mike E

3

Logan'ın cevabının Swift 3 versiyonu

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

Bir protokol ve protokol uzantısı (Swift 4.2) kullanarak bir görünümü programlı olarak yüklemek için temiz ve bildirici bir yoludur:

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

Ve bunu şöyle kullanabilirsiniz:

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

Bazı ek noktalar :

  1. Özel görünümünüzün xib dosyanızın, Custom ClassDosya Sahibinin değil, görünümün (ve oradan ayarlanan çıkışların / işlemlerin) olduğundan emin olun .
  2. Bu protokolü / uzantıyı özel görünümünüzün dışında veya dahili olarak kullanabilirsiniz. Görünümünüzü başlatırken başka bir kurulum işiniz varsa dahili olarak kullanmak isteyebilirsiniz.
  3. Özel görünüm sınıfınız ve xib dosyanızın aynı ada sahip olması gerekir.

2

Sadece şu şekilde yapıyorum:

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

Bu örnek, ana paketteki "MyView.xib" ucundaki ilk görünümü kullanır. Ancak dizini, uç adını veya paketi (varsayılan olarak ana) değiştirebilirsiniz.

Ben görünüm init yöntemi görünümleri uyanık ya da (bu arada akıllı) yukarıdaki çözümler gibi genel yöntemler yapmak için kullanılır, ama artık yapmıyorum.

Bu şekilde aynı görünüm mantığını ve kodu tutarken farklı düzenler veya özellikler kullanabilirim.

Bir fabrika nesnesinin (genellikle görünümü kullanacak olan viewController) gerektiği gibi oluşturmasına izin vermek daha kolay olur. Bazen bir sahipe ihtiyacınız olur (Genellikle oluşturulan görünüm, içerik oluşturucuya bağlı bir prize sahip olduğunda), bazen değil.

Muhtemelen Apple initFromNib, UIView sınıfına bir yöntem eklemedi ...

Yer seviyesinde bir örnek almak için nasıl doğduğunuzu bilmiyorsunuz. Sadece doğdun. Görüşler de öyle;)


2

Yapmanız gereken tek şey UIViewsınıfınızda init yöntemini çağırmak .

Bu şekilde yapın:

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

Şimdi, bu görünümü görünüm denetleyicisine alt görünüm olarak eklemek istiyorsanız, görünüm denetleyicisi.swift dosyasında bu şekilde yapın:

self.view.addSubview(className())

harika bir cevap ama yanlış bir şey var, ben onu düzenleyeceğim.
C0mrade

Uyguladığım yol bu. Ama doğaçlama yapabilirsiniz. Şimdiden teşekkürler @ C0mrade
Alap Anerao

1

Yukarıdaki cevapların bazılarına benzer, ancak daha tutarlı bir Swift3 UIView uzantısı:

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

Bu, sınıfı kendi adındaki bir uçtan değil, aynı zamanda diğer uçlardan / demetlerden de yükleyebilme kolaylığı sağlar.


1

Bunu storyboard ile yapabilirsiniz, sadece görünüm için uygun kısıtlamalar ekleyin. Bunu, kendi görünümünüzden herhangi bir görünümü alt sınıflandırarak kolayca yapabilirsiniz BaseView:

Objective-C

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

Hızlı 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)
    }
}

Kısıtlamaları nasıl ekleyeceğimi 2 ortak veriyorum - ortak olan ve görsel format dili içinde - istediğinizi seçin :)

Ayrıca, varsayılan olarak, xibadın uygulama sınıfı adıyla aynı ada sahip olduğu varsayılmıştır . Hayır ise - sadece değiştirinxibName parametreyi .

Görünümünüzü alt sınıflara ayırırsanız BaseView- kolayca IB'de herhangi bir görünüm koyabilir ve sınıf belirleyebilirsiniz.


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

Logan'ın cevabına dayanan daha güçlü versiyon

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

Ve şöyle kullanabilirsiniz

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

En uygun uygulama. Burada UIView'e değil, doğrudan sınıfınızın nesnesine geri dönmek için iki yönteme ihtiyacınız vardır.

  1. viewId sınıf olarak işaretlendi , geçersiz kılmaya izin veriyor
  2. .Xib'iniz en üst seviyenin birden fazla görünümünü içerebilir, bu durum da doğru şekilde ele alınır.

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

Misal:

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

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

0

Swift UIView alt sınıfının tamamen kendi içinde olmasını ve bir Nib kullanmanın uygulama ayrıntılarını göstermeden init veya init (frame :) kullanarak örneklenebilme özelliğine sahipseniz, bunu başarmak için bir protokol uzantısı kullanabilirsiniz. Bu çözüm, diğer çözümlerin çoğunun önerdiği şekilde iç içe UIView hiyerarşisini önler.

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)

Yalnızca kod yanıtları önerilmez. Lütfen bunun sorunu nasıl çözdüğüne veya mevcut cevaplardan nasıl farklı olduğuna dair biraz açıklama ekleyin. Yorumdan
Nick

0

Aşağıdaki uzantıyı tercih ederim

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

Bu ve en çok yanıtlanan uzantı arasındaki fark, sabit veya değişken depolamanıza gerek olmamasıdır.

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

Hangi Xcode sürümünü kullanıyorsunuz? Lütfen XCode'un en son sürümünü kullandığınızdan emin olun. XCode 11.5 (tarihte olduğu gibi son sürüm) ile benim için iyi çalışıyor.
iCoder

0
    let nibs = Bundle.main.loadNibNamed("YourView", owner: nil, options: nil)
    let shareView = nibs![0] as! ShareView
    self.view.addSubview(shareView)
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.