Swift加载xib文件的正确方式

14

我在Swift项目中加载xib文件时遇到了一些奇怪的问题。这让我感到非常沮丧,因为我已经知道如何在Obj-C中做到这一点。但是由于Swift是Swift,所以你不能像以前那样做...:/

因此,我创建了IconTextFiled.xib和IconTextField.swift文件(继承自UITextField)。在xib文件中,我在Identity Inspector中填写了字段类,并在storyboard中为某些textField重复此操作。所以我们只需在init方法中添加从xib加载的内容即可吗?不。

在Objc中,我会这样做

- (instancetype)initWithCoder:(NSCoder *)coder
{
    self = [super initWithCoder:coder];
    if (self) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"IconTextField" owner:self options:nil];
        self = [nib objectAtIndex:0];
    }
    return self;
}

所以我想,如果我将其翻译成Swift,那就好了。

required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    let nib:NSArray = NSBundle.mainBundle().loadNibNamed("IconTextField", owner: self, options: nil)
    self = nib.objectAtIndex(0)
}

但那并不起作用。我不知道为什么,但它试图创建更多的对象并崩溃了。

最后我找到了扩展。

extension IconTextField {
    class func loadFromNibNamed(nibNamed: String, bundle : NSBundle? = nil) -> IconTextField? {
        return UINib(
            nibName: nibNamed,
            bundle: bundle
            ).instantiateWithOwner(nil, options: nil)[0] as? IconTextField
    }
}

所以在ViewController中看起来像是这样

@IBOutlet var password: IconTextField!
override func viewDidLoad() {
    super.viewDidLoad()
    password = IconTextField.loadFromNibNamed("IconTextField")
}

再次失败。您能告诉我如何加载和使用xib文件吗?

更新

好的,按照丹尼尔的回答:

我的当前代码:

class IconTextField: UITextField {
    @IBOutlet var icon: UIImageView!
    @IBOutlet weak var view: UIView!

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        NSLog("initWithCoder \(self)")
        NSBundle.mainBundle().loadNibNamed("IconTextField", owner: self, options: nil)
        self.addSubview(view)
    }   
}

输入图像的描述

这两个变量与这些视图相关联。

控制台输出很大,以EXC_BAD_ACCESS结尾。

2014-10-24 10:09:09.984 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7be7bfb0;>
2014-10-24 10:09:09.984 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7be7ddf0;>
2014-10-24 10:09:09.985 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7be7fa20;>
2014-10-24 10:09:09.985 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7be814f0;>
2014-10-24 10:09:09.986 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7be830c0;>
2014-10-24 10:09:10.083 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7d183270;>
2014-10-24 10:09:10.084 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7d187cd0;>
2014-10-24 10:09:10.084 testproject[20337:3757479] initWithCoder <stensgroup.IconTextField: 0x7d189960;>

应该只有两个initWithCoder。似乎func loadNibNamed正在调用initWithCoder


我必须问一下...你为什么要对一个UITextField这样做?我曾经看到过对UITableViewCells这样做,但从未见过对文本字段这样做。 - Daniel T.
因为我想创建可重用的控件。例如,IconTextField 将具有 UIImageView 作为图标。这样我就不必每次都附加 UIImageView - Błażej
3个回答

8
这对我有效:
class IconTextField: UITextField {
    @IBOutlet weak var view: UIView!
    @IBOutlet weak var test: UIButton!

    required init(coder: NSCoder) {
        super.init(coder: coder)
        NSBundle.mainBundle().loadNibNamed("IconTextField", owner: self, options: nil)
        self.addSubview(view)
        assert(test != nil, "the button is conected just like it's supposed to be")
    }
}

一旦调用loadNibNamed:owner:options:,则viewtest按钮将按预期连接到输出口。添加nib的视图作为其自身的子视图层次结构,使nib内容可见。

这也是我在Objective-C中经常做的事情。 - Daniel T.
好的,我刚刚更新了我的问题。我不知道我做错了什么。 - Błażej
我刚刚在Xcode 6.1中使用了您的最新代码(复制粘贴),它可以正常工作。我还从println得到了非常不同的输出:initWithCoder <My.IconTextField: 0x7afcb140; baseClass = UITextField; frame = (69 45; 97 30); text = ''; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7af73ed0>> - Daniel T.
输出没问题,我只是想避免太多的文本 :) 你的故事板设置是什么?你能托管你的项目吗? - Błażej
为了测试,我尝试在Obj-c中运行它,反应是一样的。奇怪的是这种加载xib只适用于UITableViewCell? - Błażej
抱歉我的问题有点初学者的样子,但是我想知道那个iconTextField类是否在某个iconTextField.swift文件中?如果不是,它在哪里? - Josh

6

我更喜欢从nib加载,通过在协议扩展中实现loadFromNib()函数,如下所示:

(如此解释:https://dev59.com/XF8e5IYBdhLWcg3wqryt#33424509

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
  }

}

在我看来,这是最好的选择,我也做了同样的事情(使用协议)。不过你可以通过这样做一些小的修改使它更易读:let nibName = "\(Self.self)"使用协议意味着您可以轻松地让任何视图子类免费采用此功能,而无需继承。 - ssh88

1
你可以使用这个:
if let customView = Bundle.main.loadNibNamed("MyCustomView", owner: self, options: nil)?.first as? MyCustomView {
        // Set your view here with instantiated customView
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接