从nib实例化视图时抛出错误

26

我试图制作一个@IBDesignable的UIView子类,按照这个(链接)教程进行。第一个自定义视图没问题。但是当我尝试制作另一个视图时,遇到错误。首先我得到了failed to update auto layout status: the agent crashedFailed to render instance of ...。不知何故,我开始能够使用这些错误来构建和运行项目,但然后我遇到了新的错误-EXC_BAD_ACCESSlet view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView行。下面是整个方法:

func loadViewFromNib() -> UIView {
        
        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: "advancedCellView", bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
        
        return view
    }

使用第一个自定义的UIView没有问题。我使用相同的代码..

enter image description here

有任何想法吗?谢谢


1
你写道:“首个自定义视图很好。” 这是否意味着您实际上能够加载视图,但如果尝试再次加载它,则会出现错误? - Aaron Rasmussen
1
此外,为什么要使用NSBundle:forClass:而不是直接访问NSBundle.mainBundle()呢?这个nib存储在一个不同的bundle中吗? - Aaron Rasmussen
1
这表明问题不在加载和实例化“nib”的代码中,而是在“xib”文件本身中的某些内容。您是否在界面构建器中为“xib”的视图子类更改了“class”? - Aaron Rasmussen
我的意思是,首先我创建了一个“简单”的自定义UIView子类,它运行得很好。我可以在没有任何错误的情况下使用它。然后,我创建了一个新的xib文件,在其中设计了其他组件,并且还创建了它的UIView子类(都在屏幕截图中)。我按照相同的步骤进行操作,但是出现了我在问题中描述的错误。我会考虑你的其他建议并尝试修复它。谢谢您。 - Lachtan
如果您提供有关 nib 设置的一些详细信息,特别是顶级视图和文件所有者的 Identity 和 Attributes 检查器中的信息,那么回答这个问题会更容易。此外,“无法呈现实例...”的完整错误可能有助于缩小范围。 - Josh Heald
4个回答

51
您发布的loadViewFromNib()方法看起来不错,您正确获取了bundle并将nib名称指定为字符串字面量,因此应该可以找到nib。正如其他人评论的那样,您的nib设置可能有问题。以下是需要检查的几个事项:
  1. 您应该在Identity Inspector中将AdvancedCellView设置为文件所有者自定义类。
  2. 检查模块是否正确-通常您只想让它保持空白。
  3. 视图的自定义类别不应被设置,只需将其保留为默认值(UIView)。如果侧边栏中的名称比“ View”更具描述性,则可能不正确。
  4. 检查nib中只有一个顶级对象。当您折叠视图树时,侧边栏应该像这样,没有任何其他条目。

Nib hierarchy with only one top level view

N.B. 此答案与OP链接的教程相关,并不是设置nib的唯一正确方法。


1
考虑到 OP 的代码是手动加载 nib 并直接设置 view 属性,因此不清楚在 nib 文件中设置 File's Owner 的类是否必要。无论如何,它都不会影响运行时行为,只会影响 Interface Builder 中的连接能力。但是,如果视图的类应该在 nib 中设置(如果 OP 确实希望它是 UIView 的子类),则说它不需要设置是不正确的;因为这些信息被解档器用于确定要实例化哪个类,所以这将影响运行时行为。 - jlehr
@jlehr 确实,并不总是不能在nib中设置类,但OP是在跟随教程,在init(frame:)init(coder:)方法中从nib加载视图。在这种情况下,为视图设置自定义类将导致加载nib时出现无限循环,除非您有其他代码来防止这种情况发生。 - Josh Heald
好的,我正在思考 - 在什么情况下,UIView 的子类需要从nib中加载其自身类型的另一个实例(暂且不考虑nib加载通常是视图控制器而不是视图的职责)?也许我漏掉了什么,但那个教程听起来有点可疑。 - jlehr
我现在也有点困惑。教程的方法是让ViewController的nib/storyboard通过在IB中设置UIView占位符的自定义类来加载nib。这意味着在自定义类上调用init,从自定义nib中拆分出UIView并将其添加到视图层次结构中。个人认为这是最好的方法,因为视图是自包含的,所以它可以在设计时和运行时同时加载。 - Josh Heald
一个视图也可以使用 awakeAfterUsingCoder() 方法来返回一个替代自身的对象,我曾经看到过这种方法用于从 nib 文件中加载一个设置了自定义类的视图。但是我从未见过这种方法与 IBDesignable 一起使用的情况,而且还有很多其他问题需要解决。 - Josh Heald

12

我在查看教程时发现,如果在自定义的xib中以子类形式继承View元素,则会出错。请确保仅将“文件所有者(File's Owner)”设置为您的自定义类。

输入图片说明


2

instantiateWithOwner(options:)方法返回的是一个数组,而不是一个视图,因此强制向下转型为UIView永远不会起作用。相反,尝试将其转换为实际类型[AnyObject]

数组中的元素对应于nib文件中的顶级对象,因此您感兴趣的视图应该是数组元素之一。鉴于您的nib文件名称,在所有可能性中只有一个顶级对象在数组中--您正在尝试加载的单元格。确保nib文件中只有一个顶级对象,并且它确实是UIView子类的实例。

请注意,您的实现可能效率低下。如果要加载多个单元格,则应缓存UINib实例,而不是每次创建一个新实例。请注意,诸如UITableViewController之类的框架类具有内置方法来注册nibs,可以自动处理这些细节,因此您实际上可能不需要自己执行此操作。


1
instantiateWithOwner(options:) 方法返回从 nib 中解包的顶层对象数组,就像你所说的那样。但是,如果这些对象没有设置自定义类,它们将是 UIView,并且向下转换将正常工作。我不认为它应该是一个强制向下转换,但这是另一个问题。 - Josh Heald
嘿,你说得对 - 看起来我错过了那个 [0]。我会更新我的答案。 - jlehr

1
尝试用这个方法代替。这个方法对我总是有效:

let picker = NSBundle.mainBundle().loadNibNamed("advancedCellView", owner: nil, options: nil)
let view = picker[0] as! UIView

return view

让我知道是否有效


谢谢回复。它在第一个自定义UIView中也不起作用。抛出“Terminating app due to uncaught exception 'NSUnknownKeyException',reason:'[< NSObject 0x7f9a83413f30> setValue:forUndefinedKey:]:此类不能进行键值编码兼容性检查。关键字为captionLabel.'” - Lachtan
那是另一个问题。你必须在Storyboard中将标签连接到你的自定义视图类。 - brl214

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