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

Ste*_*Fox 126 ios swift

我有一个名为class MyClass的类UIView,我想用XIB文件初始化.我不知道如何使用调用的xib文件初始化此类View.xib

class MyClass: UIView {

    // what should I do here? 
    //init(coder aDecoder: NSCoder) {} ?? 
}
Run Code Online (Sandbox Code Playgroud)

Ezi*_*met 250

我测试了这段代码,效果很好

class MyClass: UIView {

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

}
Run Code Online (Sandbox Code Playgroud)

初始化视图并使用如下所示

    var view = MyClass.instanceFromNib()
    self.view.addSubview(view)
Run Code Online (Sandbox Code Playgroud)

要么

    var view = MyClass.instanceFromNib
    self.view.addSubview(view())
Run Code Online (Sandbox Code Playgroud)

UPDATE 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
}
Run Code Online (Sandbox Code Playgroud)

  • 它应该返回"MyClass"而不仅仅是UIView吗? (4认同)
  • @Ezimet该视图中的IBActions怎么样?在哪里处理它们.就像我在视图中有一个按钮(xib)如何处理该按钮的IBAction click事件. (2认同)
  • IBoutlet无法以这种方式工作...我得到了:“此类不符合密钥的键值编码要求” (2认同)

Fre*_*orf 79

Sam的解决方案已经很好了,尽管它没有考虑不同的捆绑(NSBundle:forClass来救援)并且需要手动加载,即输入代码.

如果你想完全支持你的Xib Outlets,不同的Bundles(在框架中使用!)并在Storyboard中获得一个很好的预览,试试这个:

// NibLoadingView.swift
import UIKit

// Usage: Subclass your UIView from NibLoadView to automatically load a xib with the same name as your class

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

}
Run Code Online (Sandbox Code Playgroud)

像往常一样使用你的xib,即将Outlets连接到File Owner并将File Owner类设置为你自己的类.

用法:只需从NibLoadingView继承自己的View类.

不再需要其他代码.

信用额到期的信用额度:来自DenHeadless对GH的微小变化.我的要点:https://gist.github.com/winkelsdorf/16c481f274134718946328b6e2c9a4d8

  • 这个解决方案制动出口​​连接(因为它将加载的视图添加为子视图)并且从`init?(coder:)`调用`nibSetup`如果在XIB中嵌入`NibLoadingView`将导致无限递归. (6认同)
  • @ redent84感谢您的评论和downvote.如果你有第二眼,它应该替换以前的SubView(没有给出新的实例变量).你对无限递归是正确的,如果与IB斗争,应该省略. (3认同)
  • 我总是对使用这种方法从 xib 加载视图感到不舒服。我们基本上是在一个属于 A 类子类的视图中添加一个属于 A 类子类的视图。是否有某种方法可以防止这种迭代? (2认同)
  • @PrajeetShrestha 这可能是由于nibSetup() 将背景颜色覆盖为`.clearColor()` - 从故事板加载后。但是如果您在实例化后通过代码执行它,它应该可以工作。无论如何,如上所述,一种更优雅的方法是基于协议的方法。当然,这里有一个链接给你:https://github.com/AliSoftware/Reusable。我现在对 UITableViewCells 使用类似的方法(在我发现那个真正有用的项目之前我已经实现了)。嗯! (2认同)

Sam*_*Sam 75

从Swift 2.0开始,您可以添加协议扩展.在我看来,这是一种更好的方法,因为返回类型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
  }

}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个比选定答案更好的解决方案,因为不需要进行强制转换,并且它也可以在您将来创建的任何其他UIView子类中重用. (4认同)
  • 我尝试使用Swift 2.1和Xcode 7.2.1.它在某些时候工作,并且会通过互斥锁将其无法预测地挂在其他人身上.直接在代码中使用的最后两行每次都在最后一行修改为`var myView = nib.instantiate ... as!myViewType` (3认同)

小智 36

这就是Frederik在Swift 3.0上的答案

@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
    }
}
Run Code Online (Sandbox Code Playgroud)


Мак*_*нов 31

从xib加载视图的通用方法:

例:

let myView = Bundle.loadView(fromNib: "MyView", withType: MyView.self)
Run Code Online (Sandbox Code Playgroud)

执行:

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))
    }
}
Run Code Online (Sandbox Code Playgroud)


Jer*_*emy 26

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
    }
}
Run Code Online (Sandbox Code Playgroud)

在.xib中时,请确保"自定义类"字段为MyClassView.不要打扰文件所有者.

确保Custom Class是MyClassView

另外,请确保将MyClassView中的插座连接到标签: myLabel的插座

要实例化它:

let myClassView = MyClassView.createMyClassView()
myClassView.myLabel.text = "Hello World!"
Run Code Online (Sandbox Code Playgroud)


mne*_*c23 17

斯威夫特4

在我的情况下,我必须将数据传递到该自定义视图,因此我创建静态函数来实例化视图.

  1. 创建UIView扩展

    extension UIView {
        class func initFromNib<T: UIView>() -> T {
            return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as! T
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建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
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 在.xib文件中将自定义类设置为MyCustomView.如有必要,照常连接插座. 在此输入图像描述

  4. 实例化视图

    let view = MyCustomView.instantiate(message: "Hello World.")
    
    Run Code Online (Sandbox Code Playgroud)


Rad*_*che 6

斯威夫特 5.3

创建一个名为NibLoadingView以下内​​容的类:

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 public weak var view: UIView!
    
    private var didLoad: Bool = false

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

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        self.nibSetup()
    }
    
    open override func layoutSubviews() {
        super.layoutSubviews()
        
        if !self.didLoad {
            self.didLoad = true
            self.viewDidLoad()
        }
    }
    
    open func viewDidLoad() {
        self.setupUI()
    }

    private func nibSetup() {
        self.view = self.loadViewFromNib()
        self.view.frame = bounds
        self.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        self.view.translatesAutoresizingMaskIntoConstraints = true

        addSubview(self.view)
    }

    private func loadViewFromNib() -> UIView {
        guard let nibName = type(of: self).description().components(separatedBy: ".").last else {
            fatalError("Bad nib name")
        }
        
        if let defaultBundleView = UINib(nibName: nibName, bundle: Bundle(for: type(of: self))).instantiate(withOwner: self, options: nil).first as? UIView {
            return defaultBundleView
        } else {
            fatalError("Cannot load view from bundle")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在创建 XIB 和 UIView 类对,将 XIB 的所有者设置为 UIView 类和子类NibLoadingView

ExampleView()您现在可以像、ExampleView(frame: CGRect)等一样或直接从故事板初始化类。

viewDidLoad您也可以像在 中一样使用UIViewController。您所有的出口和布局都在那一刻呈现。

基于弗雷德里克的回答