我在ios 8 beta 4中创建了一个测试项目,它作为主视图控制器和第二个视图控制器创建为带有xib文件的UIViewController子类。
我在主控制器上放了一个按钮来显示第二个控制器:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
@IBAction func testVCBtnTapped() {
let vc = TestVC()
presentViewController(vc, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
我运行应用程序并按下按钮显示第二个控制器 - 一切都很好
转到xcode beta 5,我运行应用程序,当我按下按钮时,屏幕变黑。
因为我知道他们搞砸了init代码,所以我尝试使用覆盖来查看它会修复它:
class TestVC: UIViewController {
override init() {
super.init()
}
required init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
同样的问题。将所需和覆盖更改为xcode接受的所有可能组合无效。
如果我使用故事板创建另一个控制器并且转向它一切都很好。
有什么想法吗?
编辑 - 新信息
显然是一个快速的beta 5问题
答案 0 :(得分:25)
我不知道这是不是一个错误,但它肯定是一个变化。当然,他们可能会改变它......无论如何,种子5中的规则是:
视图控制器将不自动查找其.xib,因为它具有相同的名称。您必须明确提供名称。
因此,在您的情况下,任何初始化此视图控制器的尝试都必须最终使用显式的笔尖名称(nibName:bundle:
)调用"TestVC"
,我认为。
如果您希望能够通过调用
进行初始化let vc = TestVC()
正如您在呈现视图控制器中所做的那样,只需在呈现的视图控制器中覆盖init()
即可调用super.init(nibName:"TestVC", bundle:nil)
,这就是您所需要的(除了我认为您还需要{{} 1}}塞子讨论了here)。
编辑你绝对正确,这是一个仅限Swift的问题。好眼力。在Objective-C中,使用init(coder:)
(或init
)初始化仍然可以正常工作;视图控制器正确找到它的同名.xib文件。
另一个编辑决定因素不是Objective-C或Swift是否调用new
。 视图控制器本身是用Objective-C还是Swift编写的。
最终编辑解决方法是声明您的Swift视图控制器:
init
括号中的名称消除了导致问题的名称错误。可能在下一个版本中,这将被修复,您可以再次将@objc(ViewController) ViewController : UIViewController { // ...
带走。
另一个最终编辑坏消息:我提交的错误报告回来了“按预期工作”。他们指出,我所要做的就是在周围模块之后命名 .xib 文件,例如:如果我的应用程序名为 NibFinder ,那么如果我将 .xib 文件命名为 NibFinder.ViewController.xib ,则在实例化时会自动找到它{ {1}}。
这是真的,但在我看来,它只是重述了这个错误; Swift查找过程在模块名称前面加上。因此,苹果公司表示我应该为我的 .xib 文件添加并添加相同的模块名称,而我说Apple应该关闭并在执行搜索时删除模块名称。
编辑真的很棒这个错误在iOS 9 beta 4中得到修复,所有这些变通办法都变得不必要了。
答案 1 :(得分:4)
如果您需要iOS8的支持。 您可以在UIViewController上使用扩展
extension UIViewController {
static func instanceWithDefaultNib() -> Self {
let className = NSStringFromClass(self as! AnyClass).componentsSeparatedByString(".").last
let bundle = NSBundle(forClass: self as! AnyClass)
return self.init(nibName: className, bundle: bundle)
}
}
然后,只需以这种方式创建实例
let vc = TestVC.instanceWithDefaultNib()
答案 2 :(得分:3)
这个技巧对我有用。如果您有基本视图控制器类,则可以覆盖返回类名的nibName属性(等于xib文件名)。
class BaseViewController: UIViewController {
override var nibName: String? {
get {
guard #available(iOS 9, *) else {
let nib = String(self.classForCoder)
return nib
}
return super.nibName
}
}
}
从此基类继承的所有视图控制器都可以加载其xib。 例如MyViewController:BaseViewController可以加载MyViewController.xib
答案 3 :(得分:2)
我最近遇到了这个问题,我的解决方案是重写baseviewController中的nibName方法。我的解决方法如下:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
init() {
super.init(nibName: nil, bundle: Bundle.main)
}
override var nibName: String? {
get {
if #available(iOS 9, *) {
return super.nibName
} else {
let classString = String(describing: type(of: self))
guard Bundle.main.path(forResource: classString, ofType: "nib") != nil else {
return nil
}
return classString
}
}
}
答案 4 :(得分:0)
这是基于Francesco答案的代码。它包含检查nib文件是否退出,因此当你加载没有关联xib的UIViewController时你不会崩溃(你可以在iOS8上重现它)
override var nibName: String? {
get {
let classString = String(describing: type(of: self))
guard nibBundle?.path(forResource: classString, ofType: "nib") != nil else {
return nil
}
return classString
}
}
override var nibBundle: Bundle? {
get {
return Bundle.main
}
}
答案 5 :(得分:0)
Swift3:
Cars
belongs_to :car_city
CarCities
has_many :cars
答案 6 :(得分:0)
我在Xcode 10.1和iOS 12上遇到了非常相似的问题。
此代码有效:
class ExampleViewController: UIViewController {
init() {
super.init(nibName: "ExampleViewController", bundle: nil)
}
}
但是将nil传递为nibName
使其无法加载XIB。
通过@objc(“ ExampleViewController”)显式指定类名时,此问题已得到解决。
问题是由以字母“ UI”开头的模块名称引起的(该项目称为UIKitExample)。当我将目标重命名为其他名称时,此问题得以解决。