无法在Interface Builder中加载IBDesignable xib

时间:2016-02-03 20:35:00

标签: ios swift xib ibdesignable

我有一个xibchildXib)个文件通过UIView链接到其自定义Owner swift文件。

这是我初始化自定义UIView的方式:

// init for IBDesignable
override init(frame: CGRect) {
    super.init(frame: frame)

    let view = loadViewFromNib()
    view.frame = bounds

    addSubview(view)
}

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

func loadViewFromNib() -> UIView {

    let bundle = NSBundle(forClass: self.dynamicType)
    let nib = UINib(nibName: "CommentCellView", bundle: bundle)
    let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView

    return view
}

如果我想在另一个xibchildXib)中添加此xibparentXib),我会收到以下错误:

error: IB Designables: Failed to render instance of MyRootView: The agent threw an exception.

MyRootView是与parentXib

相关联的文件
error: IB Designables: Failed to update auto layout status: The agent raised a "NSInternalInconsistencyException" exception: Could not load NIB in bundle: 'NSBundle </Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Overlays> (loaded)' with name 'MyIBDesignableCustomViewFilename'

MyIBDesignableCustomViewFilename是链接到childXib的文件。

当我通过点击Debug中的Custom class中的Identity inspector进行调试时,该行无效:

let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView

所有xib个文件都位于Copy Bundle Resources中的Build Phases

知道什么是错的吗?

3 个答案:

答案 0 :(得分:15)

第一步:

我将向您介绍IBDesignable和IBInspectable,并向您展示如何利用新功能。没有比创建演示更好的方法来详细说明功能。因此,我们将一起构建一个名为“Rainbow”的自定义界面。

IBDesignable和IBInspectable

使用IBDesignable和IBInspectable,开发人员可以创建实时在Interface Builder中呈现的界面(或视图)。通常,要应用此新功能,您需要做的就是通过继承UIView或UIControl创建可视类,然后在Swift中使用@IBDesignable关键字为类名添加前缀。如果您使用的是Objective-C,则使用IB_DESIGNABLE宏代替。以下是Swift中的示例代码:

@IBDesignable 
class Rainbow: UIView {
}

在旧版本的Xcode中,您可以编辑用户定义的运行时属性,以在Interface Builder中更改对象的属性(例如layer.cornerRadius)。问题是您必须键入属性的确切名称。 IBInspectable向前迈进了一步。当您使用IBInspectable为可视类的属性添加前缀时,该属性将向Interface Builder公开,以便您可以以一种非常直接的方式更改其值:

enter image description here

再次,如果您在Swift中开发应用程序,您需要做的只是在您选择的属性前加上关键字@IBInspectable。以下是示例代码段:

@IBInspectable var firstColor: UIColor = UIColor.blackColor()
 {
     // Update your UI when value changes
 }



@IBInspectable var firstColor: UIColor = UIColor.blackColor()
{
     // Update your UI when value changes
 }

构建Xcode项目

让我们开始在Xcode中创建一个新项目并选择Single View Application作为模板,并将其命名为RainbowDemo。我们将在此项目中使用Swift作为编程语言,因此在创建项目时不要忘记选择它。

完成后,在Project Navigator中选择Main.storyboard,然后将View对象从Object Library拖到View Controller。将其颜色更改为#38334C(或您想要的任何颜色),并将其大小设置为600乘434.然后将其放在主视图的中心。不要忘记将主视图的颜色更改为视图对象的相同颜色。 提示:如果要更改代码的RGB颜色值,只需打开调色板并切换到“滑块”选项卡以更改RGB值。

迷茫?别担心。在完成项目演示后,您将理解我的意思。

使用Xcode 6,您必须为视图配置自动布局约束,以支持所有类型的iOS设备。自动布局在最新版本的Xcode中非常强大。对于简单约束,只需单击“自动布局”菜单的“问题”选项,然后选择“添加缺失约束”,Xcode将自动配置视图的布局约束。

enter image description here

创建自定义视图类

现在您已经在storyboard中创建了视图,现在是时候创建我们的自定义视图类了。我们将使用Swift类模板进行类创建。将其命名为“Rainbow”。

enter image description here

Then insert the following code in the class:

import UIKit

class Rainbow: UIView {
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
    }
}

如前所述,visual类是UIView的子类。为了在实时渲染中使用我们的自定义类,我们需要覆盖两个初始化器,如上所示。接下来,通过选择助理编辑器来分割视图:

enter image description here

完成后,在助理编辑器中选择主要故事板,以便您可以实时查看正在构建的内容。请记住在Identity检查器下将视图的类名更改为“Rainbow”:

实施IBDesignable控件

为实时渲染启用控件的第一步是通过在类名前加@IBDesignable来将自定义视图设置为Designable:

@IBDesignable 
class Rainbow: UIView {
    ...
}

你可以看到它有点简单。但这个简单的关键字会让您的开发变得更加容易。接下来,我们将添加一些属性来设置圆的颜色。在Rainbow类中插入以下代码行:

@IBInspectable var firstColor: UIColor = UIColor(red: (37.0/255.0), green: (252.0/255), blue: (244.0/255.0), alpha: 1.0)
@IBInspectable var secondColor: UIColor = UIColor(red: (171.0/255.0), green: (250.0/255), blue: (81.0/255.0), alpha: 1.0)
@IBInspectable var thirdColor: UIColor = UIColor(red: (238.0/255.0), green: (32.0/255)

在这里,我们使用默认颜色预定义每个属性,并告诉它在每次用户更改其值时重绘视图。最重要的是,我们使用@IBInspectable关键字为每个属性添加前缀。如果转到视图的属性可检查,您应该直观地找到这些属性:

很酷,对吗?通过将属性指示为IBInspectable,您可以使用颜色选择器直观地编辑它们。

好的,让我们来实现Rainbow类的主要方法,用于在屏幕上绘制一个圆圈。在类中插入以下方法:

func addOval(lineWidth: CGFloat, path: CGPathRef, strokeStart: CGFloat, strokeEnd: CGFloat, strokeColor: UIColor, fillColor: UIColor, shadowRadius: CGFloat, shadowOpacity: Float, shadowOffsset: CGSize) {

    let arc = CAShapeLayer()
    arc.lineWidth = lineWidth
    arc.path = path
    arc.strokeStart = strokeStart
    arc.strokeEnd = strokeEnd
    arc.strokeColor = strokeColor.CGColor
    arc.fillColor = fillColor.CGColor
    arc.shadowColor = UIColor.blackColor().CGColor
    arc.shadowRadius = shadowRadius
    arc.shadowOpacity = shadowOpacity
    arc.shadowOffset = shadowOffsset
    layer.addSublayer(arc)
}

enter image description here

为了使代码清晰可读,我们创建了一种根据调用者提供的参数绘制完整或半圆的常用方法。使用CAShapeLayer类绘制圆形或圆弧非常简单。您可以使用strokeStart和strokeEnd属性控制stoke的开始和结束。通过将stokeEnd的值更改为0.0到1.0之间,可以绘制完整或部分圆。其余属性仅用于设置笔触颜色,阴影颜色等。您可以查看官方文档,了解CAShapeLayer中所有可用属性的详细信息。

接下来,在Rainbow类中插入以下方法:

override func drawRect(rect: CGRect) {
    // Add ARCs
    self.addCirle(80, capRadius: 20, color: self.firstColor)
    self.addCirle(150, capRadius: 20, color: self.secondColor)
    self.addCirle(215, capRadius: 20, color: self.thirdColor)
}

func addCirle(arcRadius: CGFloat, capRadius: CGFloat, color: UIColor) {
    let X = CGRectGetMidX(self.bounds)
    let Y = CGRectGetMidY(self.bounds)

    // Bottom Oval
    let pathBottom = UIBezierPath(ovalInRect: CGRectMake((X - (arcRadius/2)), (Y - (arcRadius/2)), arcRadius, arcRadius)).CGPath
    self.addOval(20.0, path: pathBottom, strokeStart: 0, strokeEnd: 0.5, strokeColor: color, fillColor: UIColor.clearColor(), shadowRadius: 0, shadowOpacity: 0, shadowOffsset: CGSizeZero)

    // Middle Cap
    let pathMiddle = UIBezierPath(ovalInRect: CGRectMake((X - (capRadius/2)) - (arcRadius/2), (Y - (capRadius/2)), capRadius, capRadius)).CGPath
    self.addOval(0.0, path: pathMiddle, strokeStart: 0, strokeEnd: 1.0, strokeColor: color, fillColor: color, shadowRadius: 5.0, shadowOpacity: 0.5, shadowOffsset: CGSizeZero)

    // Top Oval
    let pathTop = UIBezierPath(ovalInRect: CGRectMake((X - (arcRadius/2)), (Y - (arcRadius/2)), arcRadius, arcRadius)).CGPath
    self.addOval(20.0, path: pathTop, strokeStart: 0.5, strokeEnd: 1.0, strokeColor: color, fillColor: UIColor.clearColor(), shadowRadius: 0, shadowOpacity: 0, shadowOffsset: CGSizeZero)

}

drawRect方法的默认实现不执行任何操作。为了在视图中绘制圆圈,我们重写方法以实现我们自己的绘图代码。 addCircle方法有三个参数:arcRadius,capRadius和color。 arcRadius是圆的半径,而capRadius是圆角的半径。

addCircle方法使用UIBezierPath绘制弧,它的工作原理如下:

First it draws a half circle at the bottom
Next it draws a full small circle at the edge of the arc.
Finally, it draws the other half of the circle

在drawRect方法中,我们使用不同的半径和颜色调用addCircle方法三次。此图说明了如何绘制圆圈: enter image description here

提示:如果您需要有关UIBezierPath的更多信息,可以查看Apple的官方文档。

使用IBInspectable属性,您现在可以直接在Interface Builder中更改每个圆圈的颜色,而无需深入了解代码:

显然,您可以进一步将arcRadius公开为IBInspectable属性。我会把它作为锻炼给你。

enter image description here

enter image description here

示例代码点击此处:https://github.com/appcoda/Rainbow-IBDesignable-Demo

答案 1 :(得分:5)

我遇到了同样的问题,我设法解决了这个问题。

Swift 3

let bundle = Bundle(for: MyView.self)
let view = UINib(nibName: "MyView", bundle: bundle).instantiate(withOwner: self) as! MyView

重要的是捆绑

答案 2 :(得分:0)

对于以下两个错误:

error: IB Designables: Failed to render instance of ....
error: IB Designables: Failed to update auto layout status: The agent raised a "NSInternalInconsistencyException" exception: Could not load NIB in bundle ...

我建议进行一次小型快速自我验证可以帮助找出问题应该解决的地方:

  1. 检查.xib文件是否已正确添加到项目中
  2. 检查是否正确设置了nibName,有时会发生拼写错误
  3. 检查插座和插座是否兼有行动适当连接,包括 代码实施