如何使用 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】:

我能够解决从当前NSProcessInfoobject 获取 Interface Builder 项目路径的问题。然后您可以从IB_PROJECT_SOURCE_DIRECTORIESkey 中收集正确的路径。

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 中加载图像

如何创建 IBDesignable 自定义视图?

IBDesignable UIButton 子类

如何在 UIView 和 UIImageView 中重用相同的 IBDesignable 代码