Swift'te XIB dosyasıyla özel bir UIView sınıfını başlatma / başlatma


139

Ben bir dosya ile başlatmak istiyorum MyClass, bir alt sınıf denilen bir sınıf var . Bu sınıfı adlı xib dosyası ile başlatmak nasıl emin değilimUIViewXIBView.xib

class MyClass: UIView {

    // what should I do here? 
    //init(coder aDecoder: NSCoder) {} ?? 
}

5
iOS9 Swift için tam kaynak kodu örneği 2.0 başvurmak github.com/karthikprabhuA/CustomXIBSwift ve ilgili iplik stackoverflow.com/questions/24857986/...
karthikPrabhu Alagu

Yanıtlar:


266

Bu kodu test ettim ve harika çalışıyor:

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

Görünümü başlatın ve aşağıdaki gibi kullanın:

var view = MyClass.instanceFromNib()
self.view.addSubview(view)

VEYA

var view = MyClass.instanceFromNib
self.view.addSubview(view())

GÜNCELLEME Swift> = 3.x ve Swift> = 4.x

class func instanceFromNib() -> UIView {
    return UINib(nibName: "nib file name", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! UIView
}

1
Olmalı var view = MyClass.instanceFromNib()ve self.view.addSubview(view)aksine var view = MyClass.instanceFromNib& self.view.addSubview(view()). Cevabı geliştirmek için sadece küçük bir öneri :)
Logan

1
Benim durumumda daha sonra başlatıldı! self.view.addSubview (view) ise, görünüm var olmalıdır view = MyClass.instanceFromNib ()
Ezimet

2
@Ezimet bu görüş içindeki IBActions hakkında ne. onları idare etmek için. Benim görüşüme göre (xib) nasıl bu düğmenin IBAction tıklama olayını işlemek için.
Kadir Hüseyin

6
Sadece UIView yerine "Sınıfım" döndürmeli mi?
Kesong Xie

2
IBoutlets bu yaklaşımda çalışmıyor ... "Bu sınıf anahtar için kodlama ile uyumlu anahtar değer değil"
Radek Wilczak

82

Sam'in çözümü, farklı demetleri dikkate almamasına rağmen (NSBundle: forClass kurtarmaya geliyor) ve manuel yükleme, yani yazım kodu gerektirmesine rağmen zaten harika.

Xib Outlet'leriniz için tam destek istiyorsanız, farklı Paketler (çerçevelerde kullanın!) Ve Storyboard'da güzel bir önizleme elde edin:

// NibLoadingView.swift
import UIKit

/* Usage: 
- Subclass your UIView from NibLoadView to automatically load an Xib with the same name as your class
- Set the class name to File's Owner in the Xib file
*/

@IBDesignable
class NibLoadingView: UIView {

    @IBOutlet weak var view: UIView!

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

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

    private func nibSetup() {
        backgroundColor = .clearColor()

        view = loadViewFromNib()
        view.frame = bounds
        view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
        view.translatesAutoresizingMaskIntoConstraints = true

        addSubview(view)
    }

    private func loadViewFromNib() -> UIView {
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
        let nibView = nib.instantiateWithOwner(self, options: nil).first as! UIView

        return nibView
    }

}

Xib'inizi her zamanki gibi kullanın, yani Outlets'i File Owner'a bağlayın ve File Owner sınıfını kendi sınıfınıza ayarlayın.

Kullanım: Sadece kendi View sınıfınızı NibLoadingView'dan alt sınıflayın ve sınıf adını Xib dosyasındaki File Owner'a ayarlayın

Artık ek kod gerekmez.

Kredinin vadesi gelen krediler: GH'de DenHeadless'tan küçük değişiklikler yaparak bunu çatalladım. Özgeçmişim: https://gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8


8
Bu çözüm, çıkış bağlantılarını frenler (yüklü görünümü bir alt görünüm olarak eklediğinden) ve çağrı yapmak nibSetup, XIB'ye gömüldüğünde init?(coder:)sonsuz yinelemeye neden olur NibLoadingView.
redent84

3
@ redent84 Yorumunuz ve downvote için teşekkürler. 2. görünümünüz varsa, önceki SubView'in yerini almalıdır (yeni örnek değişkeni verilmemiştir). Sonsuz özyineleme konusunda haklısınız, IB ile mücadele ediyorsanız ihmal edilmelidir.
Frederik Winkelsdorf

1
Belirtildiği gibi "Çıkışları Dosya Sahibi'ne bağlayın ve Dosya Sahibi sınıfını kendi sınıfınıza ayarlayın." çıkışları Dosya Sahibine bağlayın
Yusuf X

1
Ben xib görünümü yüklemek için bu yöntemi kullanmak konusunda her zaman rahatsız. Temel olarak A Sınıfının alt sınıfı olan bir görünümü, A Sınıfının alt sınıfı olan bir görünümde ekliyoruz. Bu yinelemeyi önlemenin bir yolu yok mu?
Prajeet Shrestha

1
@PrajeetShrestha Bunun nedeni, muhtemelen .clearColor()Storyboard'dan yükledikten sonra Arka Plan rengini geçersiz kılan nibSetup () yöntemidir . Ancak, somutlaştırıldıktan sonra kodla yaparsanız çalışması gerekir. Her neyse, daha zarif bir yaklaşım, protokol tabanlı bir yaklaşımdır. Eminim, işte sizin için bir link: github.com/AliSoftware/Reusable . Şimdi (gerçekten yararlı bir proje keşfetmeden önce uyguladığım) UITableViewCells ile ilgili benzer bir yaklaşım kullanıyorum. hth!
Frederik Winkelsdorf

77

Swift 2.0'dan itibaren bir protokol uzantısı ekleyebilirsiniz. Bence bu daha iyi bir yaklaşım çünkü dönüş türü daha Selfziyade UIView, arayan kişinin bakış sınıfına girmesi gerekmiyor.

import UIKit

protocol UIViewLoading {}
extension UIView : UIViewLoading {}

extension UIViewLoading where Self : UIView {

  // note that this method returns an instance of type `Self`, rather than UIView
  static func loadFromNib() -> Self {
    let nibName = "\(self)".characters.split{$0 == "."}.map(String.init).last!
    let nib = UINib(nibName: nibName, bundle: nil)
    return nib.instantiateWithOwner(self, options: nil).first as! Self
  }

}

4
Bu, seçilen cevaba göre daha iyi bir çözümdür çünkü döküm yapmaya gerek yoktur ve gelecekte oluşturacağınız diğer UIView alt sınıflarında da tekrar kullanılabilir.
user3344977

3
Swift 2.1 ve Xcode 7.2.1 ile bunu denedim. Bazen işe yaradı ve muteks kilit ile diğerlerine beklenmedik bir şekilde asıldı. Doğrudan kodda kullanılan son iki satır, her seferinde değiştirilen son satırla değiştirildivar myView = nib.instantiate... as! myViewType
Paul Linsay

@ jr-root-cs Düzenlemeniz yazım hataları / hataları içeriyordu, geri almak zorunda kaldım. Her neyse, lütfen mevcut cevaplara kod eklemeyin. Bunun yerine bir yorum yapın; veya kendi cevabınıza kendi sürümünüzü ekleyin . Teşekkürler.
Eric Aya

Projemde açtığım ve test ettiğim kodu Swift 3(XCode 8.0 beta 6) kullanarak sorunsuz bir şekilde gönderdim . Yazım hatası vardı Swift 2. Bu cevap iyidir ve onlar XC8 kullanacak kullanıcıların olasılığı arama gibi değişiklikler vardır ne zaman neden başka bir yanıt olmalıdır
jr.root.cs

1
@ jr.root.cs Evet bu cevap iyi, bu yüzden kimse değiştirmemeli. Bu Sam'in cevabı, senin değil. Üzerine yorum yapmak istiyorsanız, bir yorum bırakın; yeni / güncellenmiş bir sürüm göndermek istiyorsanız, bunu kendi yayınınızda yapın . Düzenlemeler, diğer kişilerin yayınlarına sürümlerinizi eklememek için yazım hatalarını / girintileri / etiketleri düzeltmeyi amaçlamaktadır. Teşekkürler.
Eric Aya

37

Ve bu da Frederik'in Swift 3.0'daki cevabı

/*
 Usage:
 - make your CustomeView class and inherit from this one
 - in your Xib file make the file owner is your CustomeView class
 - *Important* the root view in your Xib file must be of type UIView
 - link all outlets to the file owner
 */
@IBDesignable
class NibLoadingView: UIView {

    @IBOutlet weak var view: UIView!

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

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

    private func nibSetup() {
        backgroundColor = .clear

        view = loadViewFromNib()
        view.frame = bounds
        view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.translatesAutoresizingMaskIntoConstraints = true

        addSubview(view)
    }

    private func loadViewFromNib() -> UIView {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
        let nibView = nib.instantiate(withOwner: self, options: nil).first as! UIView

        return nibView
    }
}

31

Xib'den görünüm yüklemenin evrensel yolu:

Misal:

let myView = Bundle.loadView(fromNib: "MyView", withType: MyView.self)

Uygulama:

extension Bundle {

    static func loadView<T>(fromNib name: String, withType type: T.Type) -> T {
        if let view = Bundle.main.loadNibNamed(name, owner: nil, options: nil)?.first as? T {
            return view
        }

        fatalError("Could not load view with type " + String(describing: type))
    }
}

UIView alt sınıfının türü olarak çıktı görünümü olarak bana en iyi cevap
jfgrang

26

Swift 3 Cevap: Benim durumumda, özel sınıfımda değiştirebileceğim bir çıkış olmasını istedim:

class MyClassView: UIView {
    @IBOutlet weak var myLabel: UILabel!

    class func createMyClassView() -> MyClass {
        let myClassNib = UINib(nibName: "MyClass", bundle: nil)
        return myClassNib.instantiate(withOwner: nil, options: nil)[0] as! MyClassView
    }
}

.Xib'deyken, Özel Sınıf alanının MyClassView olduğundan emin olun. Dosyanın Sahibi ile uğraşmayın.

Özel Sınıfın MyClassView olduğundan emin olun

Ayrıca, MyClassView'daki çıkışı etikete bağladığınızdan emin olun: MyLabel için çıkış

Başlamak için:

let myClassView = MyClassView.createMyClassView()
myClassView.myLabel.text = "Hello World!"

"MyClas" ucunu yükledim "ancak görünüm çıkışı ayarlanmadı." "Sahibi ayarlanmadıysa
jcharch

22

Hızlı 4

İşte benim durumumda bu özel görünüme veri geçirmek zorunda, bu yüzden görünümü somutlaştırmak için statik işlevi oluşturmak.

  1. UIView uzantısı oluştur

    extension UIView {
        class func initFromNib<T: UIView>() -> T {
            return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as! T
        }
    }
  2. MyCustomView Oluştur

    class MyCustomView: UIView {
    
        @IBOutlet weak var messageLabel: UILabel!
    
        static func instantiate(message: String) -> MyCustomView {
            let view: MyCustomView = initFromNib()
            view.messageLabel.text = message
            return view
        }
    }
  3. Özel sınıfı .xib dosyasında MyCustomView olarak ayarlayın. Gerekirse prizi her zamanki gibi bağlayın. resim açıklamasını buraya girin

  4. Anlık görünüm

    let view = MyCustomView.instantiate(message: "Hello World.")

özel görünümde düğmeler varsa, eylemlerini farklı görünüm denetleyicilerinde nasıl ele alabiliriz?
jayant rawat

protokol temsilci kullanabilirsiniz. buraya bir göz atın stackoverflow.com/questions/29602612/… .
mnemonic23

Aşağıdaki hatayı alarak xib'e bir resim yüklenemedi. " Test.Testing " tanımlayıcı ile paketteki bir uçtan referans verilen " IBBrokenImage " görüntüsü yüklenemedi
Ravi Raja Jangid

-4
override func draw(_ rect: CGRect) 
{
    AlertView.layer.cornerRadius = 4
    AlertView.clipsToBounds = true

    btnOk.layer.cornerRadius = 4
    btnOk.clipsToBounds = true   
}

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

@IBAction func okBtnDidClicked(_ sender: Any) {

    removeAlertViewFromWindow()

    UIView.animate(withDuration: 0.4, delay: 0.0, options: .allowAnimatedContent, animations: {() -> Void in
        self.AlertView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)

    }, completion: {(finished: Bool) -> Void in
        self.AlertView.transform = CGAffineTransform.identity
        self.AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
        self.AlertView.isHidden = true
        self.AlertView.alpha = 0.0

        self.alpha = 0.5
    })
}


func removeAlertViewFromWindow()
{
    for subview  in (appDel.window?.subviews)! {
        if subview.tag == 500500{
            subview.removeFromSuperview()
        }
    }
}


public func openAlertView(title:String , string : String ){

    lblTital.text  = title
    txtView.text  = string

    self.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
    appDel.window!.addSubview(self)


    AlertView.alpha = 1.0
    AlertView.isHidden = false

    UIView.animate(withDuration: 0.2, animations: {() -> Void in
        self.alpha = 1.0
    })
    AlertView.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)

    UIView.animate(withDuration: 0.3, delay: 0.2, options: .allowAnimatedContent, animations: {() -> Void in
        self.AlertView.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)

    }, completion: {(finished: Bool) -> Void in
        UIView.animate(withDuration: 0.2, animations: {() -> Void in
            self.AlertView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)

        })
    })


}

kötü biçimlendirilmiş kod, açıklama yok, bu yüzden downvote.
J. Doe

Bütün bunların soru ile ne ilgisi var?
Ashley Mills
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.