xcode 6 IB_DESIGNABLE-不在"接口"构建器中从bundle中加载资源

ari*_*orf 28 objective-c interface-builder xcode6

我正在尝试使用此处描述的新IB_DESIGNABLE选项创建一个在Interface Builder中实时更新的自定义控件.

- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();

CGRect myFrame = self.bounds;
CGContextSetLineWidth(context, 10);
CGRectInset(myFrame, 5,5);
[[UIColor redColor] set];
UIRectFrame(myFrame);

NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath;

plistPath = [bundle pathForResource:@"fileExample" ofType:@"plist"];
UIImage *tsliderOff = [UIImage imageNamed:@"btn_slider_off.png"];

[tsliderOff drawInRect:self.bounds];
}
Run Code Online (Sandbox Code Playgroud)

当我在模拟器中运行时,我得到一个红色的盒子,我的图像在中心(正如预期的那样): 在此输入图像描述

但是当我尝试使用Interface Builder时,它只显示为一个红色框(中间没有图像): 在Interface Builder中呈现

当我调试它:Editor-> Debug Selected Views时,它显示从bundle中加载的任何内容都是nil.plistPath和tsliderOff都显示为nil.

我确保btn_slider_off.png包含在Targets-> myFrameWork-> Build Phases-> Copy Bundle Resources中.

知道为什么Interface Builder在编辑期间没有看到png文件,但在运行时显示正常吗?如果我无法加载任何图像来渲染,"创建在Interface Builder中渲染的自定义视图"有点受限制...


根据rickster的解决方案进行编辑

rickster向我指出了解决方案 - 问题是文件不在mainBundle中,它们存在于[NSBundle bundleForClass:[self class]]中; 看来[UIImage imageNamed:@"btn_slider_off.png"]会自动使用mainBundle.

以下代码有效!

#if !TARGET_INTERFACE_BUILDER
NSBundle *bundle = [NSBundle mainBundle];
#else
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
#endif
NSString *fileName = [bundle pathForResource:@"btn_slider_off" ofType:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:fileName];
[image drawInRect:self.bounds];
Run Code Online (Sandbox Code Playgroud)

ric*_*ter 48

截至首次提出此问题时,创建IB可设计控件需要将其打包到框架目标中.您不必再这样做了 - 运送Xcode 6.0(及更高版本)也将从您的应用目标预览IB可设计的控件.但是,问题和解决方案是相同的.

为什么?[NSBundle mainBundle]返回当前正在运行的应用程序的主要包.当您从框架中调用它时,您将根据加载框架的应用程序返回另一个包.当您运行应用程序时,您的应用程序会加载框架.当您在IB中使用控件时,一个特殊的Xcode助手应用程序会加载框架.即使您的IB可设计控件在您的应用目标中,Xcode也会创建一个特殊的帮助应用程序来运行IB内部的控件.

解决方案?+[NSBundle bundleForClass:]改为打电话(或NSBundle(forClass:)在Swift中).这将为您提供包含您指定的任何类的可执行代码的包.(您可以使用[self class]/ self.dynamicType那里,但要注意结果将针对不同包中定义的子类进行更改.)

如果您正在使用框架方法 - 这对某些应用程序很有用,即使IB设计控件不再需要它 - 最好将图像资源与使用它们的代码放在同一个框架中.如果您的框架代码期望使用在运行时通过任何应用程序加载框架提供的资源,那么使IB可设计的最佳方法就是伪造它.prepareForInterfaceBuilder在您的控件中实现该方法,并让它从已知位置(如框架包或Xcode工作区中的静态路径)加载资源.

  • 谢谢![NSBundle bundleForClass:[self class]]解决了这个问题 (3认同)

kas*_*lat 13

我在Swift遇到了类似的问题.从Xcode 6 beta 3开始,您不需要使用框架来获取实时渲染.但是,您仍然必须处理Live View的捆绑问题,以便Xcode知道在哪里可以找到资产.假设"btn_slider_off"是Images.xcassets中设置的图像,那么您可以在Swift中执行此操作以进行实时渲染,并且当应用程序也正常运行时它将起作用.

let name = "btn_slider_off"
let myBundle = NSBundle(forClass: self.dynamicType)
// if you want to specify the class name you can do that instead
// assuming the class is named CustomView the code would be
// let myBundle = NSBundle(forClass: CustomView.self)
let image = UIImage(named: name, inBundle: myBundle, compatibleWithTraitCollection: self.traitCollection)
if let image = image {
    image.drawInRect(self.bounds)
}
Run Code Online (Sandbox Code Playgroud)