如何使用 IBDesignable UIImageView 在 prepareForInterfaceBuilder 中加载图像
Posted
技术标签:
【中文标题】如何使用 IBDesignable UIImageView 在 prepareForInterfaceBuilder 中加载图像【英文标题】:How to load an image in prepareForInterfaceBuilder with a IBDesignable UIImageView 【发布时间】:2014-07-10 08:29:52 【问题描述】:我想在 IB 可设计UIImageView
中加载示例图像,以便在编辑界面时显示在界面生成器中。以下代码不起作用,因为 IB 中的视图占位符仍然为空(视图区域仅包含 UIImageView 文本):
@IBDesignable
class TestImageView : UIImageView
override func prepareForInterfaceBuilder()
//let bundle = NSBundle.mainBundle()
let bundle = NSBundle(forClass: nil)
let imagePath = bundle.pathForResource("Test", ofType: "jpg")
self.image = UIImage(contentsOfFile: imagePath)
注意:
在 IB 中,视图的自定义类是正确的 (TestImageView) Test.jpg 存在于项目中(如果我在 IB 中手动设置UIImageView
的图像属性,图像就会显示出来)。
我尝试了两种不同的方法来获取代码中存在的包
这是使用 Xcode 6 beta 3 测试的。
更新:在这两种情况下,我得到的包路径都是"/Applications/Temporary/Xcode6-Beta3.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Overlays"
。在该路径中,图像显然不存在。
【问题讨论】:
向苹果 rdar://17762946 发布了雷达 说,你不应该在 prepareForInterfaceBuilder 中调用 super 吗? 【参考方案1】:尝试像这样获取类的捆绑包:
let bundle = NSBundle(forClass: self.dynamicType)
或者像这样指定类名
let bundle = NSBundle(forClass: TestImageView.self)
假设您的图像在捆绑包中,例如 Images.xcassets,然后您可以使用以下方法加载它:
self.image = UIImage("Test", inBundle: bundle, compatibleWithTraitCollection: self.traitCollection)
请记住在尝试使用之前检查您的图像是否为零。我无法使用 bundle.pathForResource 获取图像路径以与普通图像资产一起正常工作。似乎也没有 UIImage 调用,您只指定名称和包,因此您必须使用特征集合。
这个问题与:
xcode 6 IB_DESIGNABLE- not loading resources from bundle in Interface builder
Apple 的回应...
工程部门已确定此问题的行为符合预期 关于以下内容:
我们真的不能比指定捆绑更容易。你 可能会说,“哦,让我们一起调酒 -[NSBundle mainBundle]”,但很多 引用捆绑包的呼叫站点不经过那里(或去 通过 CF API)。有人可能会说“好吧,那我们在 最少调酒-[UIImage imageNamed:]”。这里的问题是那里 不是主要捆绑包的单一替代品。你可能有多个 一次加载实时视图包(框架或应用程序),所以我们 不能只选择一个作为主要捆绑包。
开发人员需要了解捆绑包以及如何从 捆。开发人员应该使用 UIImage(named:inBundle:compatibleWithTraitCollection:) 用于所有图像查找。
【讨论】:
感谢回答,但正确的方法应该是我自己的回答中描述的方法。 我提供了捆绑示例,因为我已向 Apple 发布了关于无法在 Live View 中找到资源的错误报告 (17656550)。我将 Apple 的回复添加到我的回答中。 很好的调查,显然您必须非常了解视图在呈现为实时视图时所处的“范围”。 这在 Xcode 9 发行说明 (developer.apple.com/library/content/releasenotes/DeveloperTools/…) 中作为一个已知问题进行了说明:“在 prepareForInterfaceBuilder() 中使用 Swift 图像文字会导致 IBDesignablesAgentCocoaTouch 崩溃,因为实时视图是在不同的包中编译的. (28676479) 解决方法:使用 UIImage(named:inBundle:compatibleWithTraitCollection:) 进行所有图像查找,并使用 Bundle(for: type(of: self)) 查找实时视图类的包。"【参考方案2】:为 Swift 4.2 更新
当您实例化 UIImage(使用 UIImage(named : "SomeName"))时,应用程序将在您的 主包 中查找资产,这通常可以正常工作。但是当您在 设计时,InterfaceBuilder 将可设计视图的代码(用于编译而设计)在一个单独的包中。
所以解决方案是:动态定义您的捆绑包,因此您的文件可以在设计、编译和运行时找到:
// DYNAMIC BUNDLE DEFINITION FOR DESIGNABLE CLASS
let dynamicBundle = Bundle(for: type(of: self))
// OR ALTERNATIVELY BY PROVDING THE CONCRETE NAME OF YOUR DESIGNABLE VIEW CLASS
let dynamicBundle = Bundle(for: YourDesignableView.self)
// AND THEN SUCCESSFULLY YOU CAN LOAD THE RESSOURCE
let image = UIImage(named: "Logo", in: dynamicBundle, compatibleWith: nil)
【讨论】:
你是那个英雄。为什么它需要这个? @ScottyBlades:运行应用程序时,所有要编译的代码文件通常都在主包中。但是当您在设计时,InterfaceBuilder 将可设计视图的代码(用于在设计时编译)保存在一个单独的包中。 如何在 Interface Builder 中做到这一点?我尝试在 UIImageView 中使用“Bundle_name/image_name”,但没有成功。【参考方案3】:让我们弹出 swift 3 答案
let bundle = Bundle(for: self.classForCoder)
... UIImage(named: "AnImageInYourAssetsFolderPerhaps", in: bundle, compatibleWith: self.traitCollection)!
【讨论】:
【参考方案4】:对于 Swift 4(和 3)使用这个:
let image = UIImage(named: "foo", in: Bundle(for: type(of: self)), compatibleWith: traitCollection)
这种方法得到了普遍的捆绑
【讨论】:
【参考方案5】:我能够解决从当前NSProcessInfo
object 获取 Interface Builder 项目路径的问题。然后您可以从IB_PROJECT_SOURCE_DIRECTORIES
key 中收集正确的路径。
override func prepareForInterfaceBuilder()
let processInfo = NSProcessInfo.processInfo()
let environment = processInfo.environment
let projectSourceDirectories : AnyObject = environment["IB_PROJECT_SOURCE_DIRECTORIES"]!
let directories = projectSourceDirectories.componentsSeparatedByString(":")
if directories.count != 0
let firstPath = directories[0] as String
let imagePath = firstPath.stringByAppendingPathComponent("PrepareForIBTest/Test.jpg")
let image = UIImage(contentsOfFile: imagePath)
self.image = image
bjhomer 在Apple Developer Forums post 中建议的 WWDC 2014 411 会议“Interface Builder 中的新增功能”中描述了该技术。
此外,我需要说需要切换到UIView
子类,因为UIImageView
的外观在实时视图中似乎没有变化。
【讨论】:
在我看来,正确的回复要么是这篇文章中的#kasplat,要么是那篇文章中的@rickster:***.com/questions/24603232/… 我接受了这个答案,因为它基于 WWDC 会话,所以在我看来,它就像来自 Apple 的官方信息。你怎么看? 看来苹果现在提供了一个更简单的方法:prepareForInterfaceBuilder
只能带你到此为止,并且需要非常具体的操作。另一方面,NSBundle:forClass
灵活且封装良好。也许 Apple 现在正在提供更好、更轻量级的解决方案来应对 bug (17656550)?【参考方案6】:
这将加载您的 IB_Designable,或者如果您没有 - 默认图像。
- (void)prepareForInterfaceBuilder
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
imagePic = imagePic ? [imagePic imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] : [UIImage imageNamed:@"goodIcon" inBundle:bundle compatibleWithTraitCollection:self.traitCollection];
【讨论】:
【参考方案7】:对于 Swift 2 和 Xcode 7,界面已更改。应该使用
let bundle = NSBundle(forClass: self.dynamicType)
let image = UIImage(named: "imageName", inBundle: bundle, compatibleWithTraitCollection: self.traitCollection)
let imageView = UIImageView(image: image)
我在my project 中使用它,它适用于 IB 和设备。
【讨论】:
谢谢。但是我不明白它与@kasplat 答案有何不同。 UIImage的init不同。以上是关于如何使用 IBDesignable UIImageView 在 prepareForInterfaceBuilder 中加载图像的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 @IBDesignable 和 @IBInspectable 制作 uitextfield 的自定义子类并在那里我可以从左侧设置占位符填充
如何使用创建的自定义 IBDesignable 类从情节提要中更改按钮的角半径
如何使用 IBDesignable UIImageView 在 prepareForInterfaceBuilder 中加载图像