简体   繁体   English

如何使用 Swift 中的 XIB 文件初始化/实例化自定义 UIView class

[英]How to initialize/instantiate a custom UIView class with a XIB file in Swift

I have a class called MyClass which is a subclass of UIView , that I want to initialize with a XIB file.我有一个名为MyClassUIView ,它是 UIView 的子类,我想用XIB文件对其进行初始化。 I am not sure how to initialize this class with the xib file called View.xib我不确定如何使用名为View.xib

class MyClass: UIView {

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

I tested this code and it works great:我测试了这段代码,效果很好:

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

Initialise the view and use it like below:初始化视图并使用它,如下所示:

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

OR或者

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

UPDATE Swift >=3.x & Swift >=4.x更新 Swift >=3.x & Swift >=4.x

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

Sam's solution is already great, despite it doesn't take different bundles into account (NSBundle:forClass comes to the rescue) and requires manual loading, aka typing code. Sam 的解决方案已经很棒,尽管它没有考虑不同的包(NSBundle:forClass 来救援)并且需要手动加载,也就是输入代码。

If you want full support for your Xib Outlets, different Bundles (use in frameworks!) and get a nice preview in Storyboard try this:如果你想完全支持你的 Xib Outlets、不同的 Bundles(在框架中使用!)并在 Storyboard 中获得一个很好的预览,试试这个:

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

}

Use your xib as usual, ie connect Outlets to File Owner and set File Owner class to your own class.像往常一样使用您的 xib,即将 Outlets 连接到 File Owner 并将 File Owner 类设置为您自己的类。

Usage: Just subclass your own View class from NibLoadingView & Set the class name to File's Owner in the Xib file用法:只需从 NibLoadingView 子类化您自己的 View 类并在 Xib文件中将类名设置为File's Owner

No additional code required anymore.不再需要额外的代码。

Credits where credit's due: Forked this with minor changes from DenHeadless on GH.信用到期的信用:从 GH 上的 DenHeadless 进行了微小的更改,将其分叉。 My Gist: https://gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8我的要点: https : //gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8

As of Swift 2.0, you can add a protocol extension.从 Swift 2.0 开始,您可以添加协议扩展。 In my opinion, this is a better approach because the return type is Self rather than UIView , so the caller doesn't need to cast to the view class.在我看来,这是一个更好的方法,因为返回类型是Self而不是UIView ,所以调用者不需要转换到视图类。

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
  }

}

And this is the answer of Frederik on Swift 3.0这是 Frederik 在 Swift 3.0 上的回答

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

Universal way of loading view from xib:从xib加载视图的通用方式:

Example:例子:

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

Implementation:执行:

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

Swift 3 Answer: In my case, I wanted to have an outlet in my custom class that I could modify: Swift 3 答案:就我而言,我想在我的自定义类中有一个可以修改的插座:

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

When in the .xib, make sure that the Custom Class field is MyClassView.在 .xib 中时,确保自定义类字段是 MyClassView。 Don't bother with the File's Owner.不要打扰文件的所有者。

确保自定义类是 MyClassView

Also, make sure that you connect the outlet in MyClassView to the label:另外,请确保将 MyClassView 中的插座连接到标签: myLabel 的出口

To instantiate it:要实例化它:

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

Swift 4斯威夫特 4

Here in my case I have to pass data into that custom view, so I create static function to instantiate the view.在我的情况下,我必须将数据传递到该自定义视图中,因此我创建了静态函数来实例化该视图。

  1. Create UIView extension创建 UIView 扩展

    extension UIView { class func initFromNib<T: UIView>() -> T { return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as! T } }
  2. Create MyCustomView创建我的自定义视图

    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. Set custom class to MyCustomView in .xib file.在 .xib 文件中将自定义类设置为 MyCustomView。 Connect outlet if necessary as usual.如有必要,照常连接插座。 在此处输入图片说明

  4. Instantiate view实例化视图

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

Below code will do the job if anyone wants to load a custom View with XIB Programmatically.如果有人想以编程方式使用 XIB 加载自定义视图,下面的代码将完成这项工作。

let customView = UINib(nibName:"CustomView",bundle:.main).instantiate(withOwner: nil, options: nil).first as! UIView
customView.frame = self.view.bounds
self.view.addSubview(customView)

Swift 5.2斯威夫特 5.2

Create a class named NibLoadingView with the following contents:创建一个名为NibLoadingView的类,内容如下:

import Foundation
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()
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
        nibSetup()
    }

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

    private func nibSetup() {
        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: type(of: self).description().replacingOccurrences(of: "REPLACEME.", with: ""), bundle: bundle)
        let nibView = nib.instantiate(withOwner: self, options: nil).first as! UIView

        return nibView
    }
}

Find REPLACEME in the above code and replace it with your project's name.在上面的代码中找到REPLACEME并将其替换为您的项目名称。

Now create a XIB & UIView class pair, set XIB's owner to UIView class and subclass NibLoadingView .现在创建一个 XIB 和 UIView 类对,将 XIB 的所有者设置为 UIView 类和子类NibLoadingView

You can now init the class just like ExampleView() , ExampleView(frame: CGRect) , etc.您现在可以像ExampleView()ExampleView(frame: CGRect)等一样初始化该类。

Based on Frederik's answer基于弗雷德里克的回答

create view from.xib从.xib 创建视图

let nib = UINib(nibName: "View1", bundle: nil) //View1 is a file name(View1.swift)
if let view = nib.instantiate(withOwner: self, options: nil).first as? UIView {
  // logic
}

//or

if let view = Bundle.main.loadNibNamed("View1", owner: self, options: nil)?.first as? UIView {
  // logic
}

Since.xib can contains several view, that is why you are working with array here( .first )由于.xib 可以包含多个视图,这就是您在此处使用数组的原因( .first

For example例如

  1. Create View1.xib创建 View1.xib
  2. Create View1.swift where set owner in code to create the instance of class(View1)创建 View1.swift 在代码中设置所有者以创建类的实例(View1)
  3. Set File's Owner in View1.xib as View1 .View1.xibFile's Owner设置为View1 Allows to connect outlets and actions允许连接插座和动作
import UIKit

class View1: UIView {
    @IBOutlet var contentView: UIView!
    
    override init(frame: CGRect) {
            super.init(frame: frame)
            self.commonInit()
        }

        required init?(coder: NSCoder) {
            super.init(coder: coder)
            self.commonInit()
        }
        
        private func commonInit() {
            if let view = Bundle.main.loadNibNamed("View1", owner: self, options: nil)?.first as? UIView {
                addSubview(view)
                view.frame = self.bounds
            }
        }
}

Notes if we move Custom Class from File's owner to Container View we get error(loop).注意如果我们将Custom ClassFile's owner移动到容器视图,我们会得到错误(循环)。 It is because of:这是因为:

System init instance from Container View where we init it again in commonInit()来自Container View的系统初始化实例,我们在 commonInit() 中再次对其进行初始化

.loadNibNamed

Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ff7bf6fbfc8)
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)

        })
    })


}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM