我正在尝试使用描述为here的新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];
}
当我在模拟器中运行时,我得到一个红色的盒子,我的图像位于中心(正如预期的那样):
但是当我尝试使用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];
答案 0 :(得分:48)
在第一次提出这个问题时,创建一个可设计IB的控件需要将其打包到框架目标中。您不必再这样做了 - 运送Xcode 6.0(及更高版本)也将从您的应用目标预览IB可设计的控件。但问题和解决方案是一样的。
为什么呢? [NSBundle mainBundle]
返回当前正在运行的应用的主要捆绑包。
当您从框架中调用它时,您将根据加载框架的应用程序返回不同的包。当您运行应用程序时,您的应用程序会加载框架。当您在IB中使用控件时,一个特殊的Xcode助手应用程序会加载框架。即使您的IB可设计控件在您的应用目标中,Xcode也会创建一个特殊的帮助应用程序来运行IB内部的控件。
解决方案?改为调用+[NSBundle bundleForClass:]
(或在Swift中调用NSBundle(forClass:)
)。这将为您提供包含您指定的任何类的可执行代码的包。 (您可以在那里使用[self class]
/ self.dynamicType
,但要注意,不同包中定义的子类的结果会发生变化。)
如果您正在使用框架方法 - 这对某些应用程序很有用,即使IB设计控件不再需要它 - 最好将图像资源放在同一个应用程序中框架与使用它们的代码。如果您的框架代码期望使用在运行时通过任何应用程序加载框架提供的资源,那么使IB可设计的最佳方法就是伪造它。在您的控件中实现prepareForInterfaceBuilder
方法,并让它从已知位置加载资源(如框架包或Xcode工作区中的静态路径)。
答案 1 :(得分: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)
}
答案 2 :(得分:3)
在IB_DESIGNABLE:
中有关上述捆绑/路径解析的警告如果UIView初始化中包含的调用,XCode将无法解析资源。在drawRect:
中,我只能让XCode解析路径我找到了上述捆绑解析技术的一个很好/简单的解决方法,只需将IBInspectable
属性指定为UIImage
,然后在Interface Builder中指定图像。例如:
@IBInspectable var mainImage: UIImage?
@IBInspectable var sideImage: UIImage?
答案 3 :(得分:3)
我已使用以下代码行修正了它:
let image = UIImage(named: "image_name", inBundle: NSBundle(forClass: self.dynamicType), compatibleWithTraitCollection: nil)
以这种方式实现后,图像在Interface Builder中正确显示
答案 4 :(得分:1)
在SWIFT 3.0中,@ rickster的代码已更改为:
// DYNAMIC BUNDLE DEFINITION FOR DESIGNABLE CLASS
let theBundle : Bundle = Bundle(for: type(of: self))
// OR ALTERNATEVLY BY PROVDING THE CONCRETE NAME OF DESIGNABLE CLASS
let theBundle : Bundle = Bundle(for: RH_DesignableView.self)
// AND THEN SUCCESSFULLY YOU CAN LOAD THE RESSOURCE
let theImage : UIImage? = UIImage(named: "Logo", in: theBundle, compatibleWith: nil)