可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I would like to load a sample image in an IB designable UIImageView
, to be shown in Interface Builder while editing the interface. The following code does not work, as the view placeholder in IB remains empty (the view area contains only the UIImageView text):
@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)
}
}
Note that:
- in IB the Custom Class class for the view is correct (TestImageView)
- Test.jpg is present in the project (if I manually set the image property of the
UIImageView
in IB the image shows up).
- I tried the two different methods of getting the bundle present in the code
This was tested with Xcode 6 beta 3.
Update: in both cases the bundle path I get is "/Applications/Temporary/Xcode6-Beta3.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Overlays"
. In that path the image is obviously not present.
回答1:
Updated for Swift 4.2
When you instantiate an UIImage (with UIImage(named : "SomeName") the app will look for the asset in your main bundle, which works fine usually. But when you are at design time, the InterfaceBuilder holds the code of the designable views (for compiling while designing) in a separate bundle.
So the solution is: Define your bundle dynamically, hence your files can be found in design, compile and run time:
// 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)
回答2:
Try getting the bundle of the class like this:
let bundle = NSBundle(forClass: self.dynamicType)
or specifying the class name like this
let bundle = NSBundle(forClass: TestImageView.self)
Assuming that your image is in the bundle, for example Images.xcassets, you can then load it using:
self.image = UIImage("Test", inBundle: bundle, compatibleWithTraitCollection: self.traitCollection)
Remember to check whether your image is nil before trying to use it. I have not been able to get the image path using bundle.pathForResource to work correctly with normal image assets. There also doesn't appear to be a UIImage call where you specify just the name and bundle, so you have to use trait collection.
This question is related to:
xcode 6 IB_DESIGNABLE- not loading resources from bundle in Interface builder
Response from Apple...
Engineering has determined that this issue behaves as intended based
on the following:
We can't really make this any easier than specifying the bundle. You
might say, "oh, let's swizzle -[NSBundle mainBundle]", but lots of
call sites that reference a bundle don't go through there (or go
through the CF API). One might say then "ok, well then how about we at
least swizzle -[UIImage imageNamed:]". The problem here is that there
is no single replacement for the main bundle. You might have multiple
live view bundles (either frameworks or apps) loaded in at once, so we
can't just pick one to be the main bundle.
Developers need to be aware of bundles and how to get images from a
bundle. Developers should be using
UIImage(named:inBundle:compatibleWithTraitCollection:) for all image lookups.
回答3:
Lets pop in the swift 3 answer
let bundle = Bundle(for: self.classForCoder)
... UIImage(named: "AnImageInYourAssetsFolderPerhaps", in: bundle, compatibleWith: self.traitCollection)!
回答4:
I was able to fix the issue getting the Interface Builder project path from the current NSProcessInfo
object. You can then gather the correct path from the 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
}
}
This technique is described in the WWDC 2014 411 session "What's New in Interface Builder" as suggested by bjhomer in this Apple Developer Forums post.
Moreover, I need to say that it was required to switch to a UIView
subclass, because it seems that the appereance of the UIImageView
does not change in live views.
回答5:
For Swift 4 (and 3) use this:
let image = UIImage(named: "foo", in: Bundle(for: type(of: self)), compatibleWith: traitCollection)
This approach gets the bundle universally
回答6:
This will load your IB_Designable, or if you have none - the default image.
- (void)prepareForInterfaceBuilder
{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
imagePic = imagePic ? [imagePic imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] : [UIImage imageNamed:@"goodIcon" inBundle:bundle compatibleWithTraitCollection:self.traitCollection];
}
回答7:
For Swift 2 and Xcode 7, the interface has been changed. Should use
let bundle = NSBundle(forClass: self.dynamicType)
let image = UIImage(named: "imageName", inBundle: bundle, compatibleWithTraitCollection: self.traitCollection)
let imageView = UIImageView(image: image)
I use it in my project, it works fine for both IB and device.