xcode 6 IB_DESIGNABLE- not loading resources from bundle in Interface builder - objective-c

I am trying to make a custom control that updates live in Interface Builder using the new IB_DESIGNABLE option described here.
- (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];
}
When I run in the simulator, I get a red box with my image in the center (as expected):
But when I try use the Interface Builder, it only shows up as a red box (no image in the middle):
When I debug it: Editor->Debug Selected Views, it reveals that anything loaded from the bundle is nil. The plistPath and tsliderOff both appear as nil.
I made sure that btn_slider_off.png was included in the Targets->myFrameWork->Build Phases->Copy Bundle Resources.
Any idea why Interface Builder doesn't see the png file during editing, but shows ok when running? "Creating a Custom View that Renders in Interface Builder" is a little limited if I can't load any images to render...
edit based on solution by rickster
rickster pointed me to the solution - the problem is that the files don't live in mainBundle, they live in [NSBundle bundleForClass:[self class]]; And it appears that [UIImage imageNamed:#"btn_slider_off.png"] automatically uses mainBundle.
The following code works!
#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];

As of when this question was first asked, creating an IB-designable control required packaging it in a framework target. You don't have to do that anymore — the shipping Xcode 6.0 (and later) will preview IB-designable controls from your app target, too. However, the problem and the solution are the same.
Why? [NSBundle mainBundle] returns the primary bundle of the currently running app.
When you call that from a framework, you're getting a different bundle returned based on which app is loading your framework. When you run your app, your app loads the framework. When you use the control in IB, a special Xcode helper app loads the framework. Even if your IB-designable control is in your app target, Xcode is creating a special helper app to run the control inside of IB.
The solution? Call +[NSBundle bundleForClass:] instead (or NSBundle(forClass:) in Swift). This gets you the bundle containing the executable code for whichever class you specify. (You can use [self class]/self.dynamicType there, but beware the result will change for subclasses defined in different bundles.)
If you're using the framework approach — which can be useful for some apps even though it's no longer required for IB-designable controls — it's best to put image resources in the same framework with the code that uses them. If your framework code expects to use resources provided at run time by whatever app loads the framework, the best thing to do for making it IB-designable is to fake it. Implement the prepareForInterfaceBuilder method in your control and have it load resources from a known place (like the framework bundle or a static path in your Xcode workspace).

I ran into a similar problem in Swift. As of Xcode 6 beta 3, you don't need to use a framework to get live rendering. However, you still have to deal with the bundle problem for Live View so that Xcode knows where to find the assets. Assuming "btn_slider_off" is an image set in Images.xcassets then you can do this in Swift for live rendering and it will work when the app is run normally as well.
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)
}

Warning about the above bundle/path resolution while in IB_DESIGNABLE:
XCode will not resolve a resource if the call contained in the initialization of the UIView. I could only get XCode to resolve a path while in drawRect:
I found a great / easy workaround to the above bundle resolution techniques, just simply designate a IBInspectable property to be a UIImage, then specify the image in Interface Builder. Ex:
#IBInspectable var mainImage: UIImage?
#IBInspectable var sideImage: UIImage?

I've fixed it using the following line of code:
let image = UIImage(named: "image_name", inBundle: NSBundle(forClass: self.dynamicType), compatibleWithTraitCollection: nil)
After implementing it this way the images show up correctly in the Interface Builder

IN SWIFT 3.0 the code of #rickster has changed to:
// 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)

I also had the same problem : looking at this answer and following WWDC 2014 whats new in Interface Builder.
I solved it like that:
- (void)prepareForInterfaceBuilder{
NSArray *array = [[NSProcessInfo processInfo].environment[#"IB_PROJECT_SOURCE_DIRECTORIES"] componentsSeparatedByString:#":"];
if (array.count > 0) {
NSString *str = array[0];
NSString *newStr = [str stringByAppendingPathComponent:#"/MyImage.jpg"];
image = [UIImage imageWithContentsOfFile:newStr];
}
}

Related

Loading images from xcassets into NSImage

In Xcode 12.4 on macOS Big Sur, I'm developing an Objective-C plug-in of a third-party app running on macOS 10.12 and newer. I would like to load an image from an asset catalog.
I've added abc.xcassets into the project tree, added several PDF images. One of the image is named test.
I would like to show the test image as an alert icon. For this purpose, I'm trying to load the image into an NSImage instance in the main plug-in's class. I've tried all the alternatives I found but I still get nil instead of the image.
+ (void)initialize {
NSImage *test1 = [NSImage imageNamed:#"test"];
NSImage *test2 = [NSImage imageNamed:#"test" bundleForClass:[self class]];
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
NSImage *test3 = [[NSImage alloc] initWithContentsOfFile:
[testBundle pathForImageResource:#"test"]];
// Later used in
// [alert setIcon:test1];
}
However, all the test1, test2 and test3 are nil after running this code. What am I missing? How to do it properly?
PS: The test image is listed by Xcode's UI builder to select in buttons' Image property.
This worked:
[testBundle imageForResource:#"test"]

Image is not visible on Watch App programatically

I have a very weird issue. I have a watch app and I am trying to display an image programatically. When I debug the code, I can see the image is correctly loaded but on simulator or real device I cannot see the image. I have added that image in both watch and extension bundle resources.
Firstly I use the code below to open the view.
NSArray *objects =[NSArray arrayWithObjects:#"MainPageView",#"InterfaceController",#"PoseImageView",nil];
[self presentControllerWithNames:objects contexts:nil];
Inside PoseImageView I am trying to display an image with:
UIImage *imgTest = [UIImage imageNamed:[NSString stringWithFormat:#"1.jpg", strAngle]]; // I can see 1.jpg in debugger
[_imgPose setImage: imgTest];
The image is not there on watch or simulator. I am wondering how to fix the issue.
Note: From assets its not loading as well.
Note2: I can only display the image if the view is initial view.
You are trying to use imageNamed for .jpg which will not work.
Try using
NSString *filePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"imageName"] ofType:#"jpg"];
UIImage *theImage = [UIImage imageWithContentsOfFile:filePath];

iOS 8.2 upgrade not show image in UIImageView, but only in app loaded through iTunes or TestFlight not Xcode

As weird as it sounds...
App loaded through test flight or iTunes does not show image on the screen, but it does when it is loaded through Xcode. I believe the UIImageView is there, and the UIImage is loaded.
App loaded through the Xcode works.
If the device is connected to the Xcode and debugging, device connected with USB cable. App works despite the load method...
Core data seems to works in all cases.
Any idea, somebody has seen something similar. I am quite lost.
Also if you have any tip on how to debug this on the device I will appreciate it.
The few code I think it is involve.
I am loading the UIImage from disk using
UIImage * previewImage = [UIImage imageWithContentsOfFile:path];
The image is located in Documents Directory fileName is the input
NSURL *documentsFolderURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSString *documentsFolderPath = [documentsFolderURL path];
if (![_documentsDirectory isEqualToString:documentsFolderPath]) [self generateLocaFolderPath];
NSString *returnedPath = [_documentsDirectory stringByAppendingPathComponent:fileName];
return returnedPath;
and setting the image in the imageView with
previewView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, frameImageThumbnail.size.width, frameImageThumbnail.size.height)];
[previewView setImage:previewImage];
Everything else seems to work well...
My first question!!! Thanks for reading!
_ edit _
I had the strong believe now that the problem is not in the UIImage, I have use the same methods in other parts of the app and it is working fine.
But now I believe the problem is in the animation I perform to the UIView that contains the UIImageView.
[_view setFrame:frameImageThumbnail];
[_view setClipsToBounds:YES];
[_view setAutoresizesSubviews:YES];
[_image setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[_view addSubview:_image];
[UIView animateWithDuration:0.4
delay:0.1
options:UIViewAnimationOptionCurveEaseInOut
animations:^(void){
CGRect destination1Frame = _view.frame;
//... some complicated calculations with the rect ...
[_view setFrame:destination1Frame];
[UIView transitionFromView:_customView toView:_view duration:0 options: UIViewAnimationOptionShowHideTransitionViews completion:^(BOOL finisehdCompletion){
//stop showing video in _customView
}];
}];
I am using NSAutolayout in the storyboard. Though my application only allows one landscape orientation. No idea if all this meshing up with the frames it is making.
dizzy to the poor auto layout guy...
Make sure you have / your image is set in Copy Bundle Resources. You can check if your image path is valid also. But I think the image is not set to copy at Build Phases.
I had the same problem.. Haven't find a correct solution.
But I deleted the image(check if it's deleted in ur Bundle resources), then put the image again, with ANOTHER name.
It worked for me. It's a temporary solution, idk if it's an apple bug or what.
Ok After more than 20 ipa files build and installed in my devices. In all this process to get rid of the problem I removed the _view from the storyboard, and initiallize the view programmatically. I remember that _view was originally set to (weak, nonatomic) property because was made with the storyboard, this super nice ctrl+.
That was I change I made, weak to Strong, and works now.
I don't know why it didn't work only when the program was install through iTunes with the ipa file or through the test flight app. That is still a mystery to me.

How To Load Images From NSHomeDirectory

So, I'm currently saving images in my UIImageView via this method:
- (void)applicationDidEnterBackground:(UIApplication*)application {
NSString *image1 = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/image1.png"];
NSString *image2 = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/image2.png"];
}
The images save just fine, but I have no idea how to set the UIImageViews images in viewDidLoad. This is what I've been trying:
- (void)viewDidLoad
{
self.imageView.image =[UIImage imageNamed:[(NSHomeDirectory *)Documents objectForKey:#"image1"]];
self.imageView2.image =[UIImage imageNamed:[(NSHomeDirectory *)Documents objectForKey:#"image1"]];
}
But, obviously that is not working. I'm having trouble understanding the basics here. Any help would be great, thanks!
The -imageNamed: method looks in the application's main bundle if the named image hasn't been cached yet. From the docs:
The name of the file. If this is the first time the image is being loaded, the method looks for an image with the specified name in the application’s main bundle.
(See UIImage class reference)
You want -imageWithContentsOfFile: instead.

My UITabBar doesn't show up

Okay guys, maybe you can help me out with this one. I'm about ready to pull my hair out.
Recently I decided to upgrade my app and make it look better, and with that I wanted to move it into full support for iPad platforms as well. For a while everything worked great. Just press copy MainWindow.xib for iPad, add the views that I used on the iPhone configurations, and everything should be great, but that didn't work too well. Take a look:
Here is the iPhone screenshot:
Here is the iPad screenshot:
Where's the tab bar? I don't understand! I added the initial view when I was first putting it together, but when I linked all of the IBOutlets to the proper pieces, the tab bar no longer shows up.
Screenshot of IB:
Tab Bar properties:
Tint: A bluish color
Image Tint: A goldish color
Mode: Scale to fill
Tag: 0
User Interaction Enabled: (Checked)
Multiple Touch: (Unchecked)
Alpha: 1
Opaque: (Checked)
Hidden (Unchecked)
Clears Graphic Context: (Checked)
Clip Subviews: (Unchecked)
Autoresize Subviews: (Checked)
Stretching: (x,y,w,h):(0,0,1,1)
The viewController.h file is a delegate for UITabBar, UITextField, and UITextView
ViewDidLoad (bar is the IBOutlet for the tab bar):
- (void)viewDidLoad
{
[super viewDidLoad];
[self playMovieIntro];
NSURL *url = [NSURL URLWithString:#"http://www.faithlifefellowship.us/Audio/Sermons/NewSermonBanner.png"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
if(!image == NULL)
{
NewSermonBanner.image = image;
}
series = [[Series alloc] init];
SeriesName = #"";
NSRange range = [[[UIDevice currentDevice] name] rangeOfString:#"iPad"];
int i = 0;
if(range.location != NSNotFound)
i = 1;
bar.selectedItem = hometab;
//set delegates
[bar setDelegate:self];
[personalName setDelegate:self];
[personalEmail setDelegate:self];
[content setDelegate:self];
[prContent setDelegate:self];
[prName setDelegate:self];
[prEmail setDelegate:self];
}
I'm stumped. If you have any ideas or need any more information, let me know.
Thanks!
I'm going to give you a few things I'm getting in order to fix this. It will be tons easier if you could upload the source code for me/us to download and be able to pinpoint the problem.
Sometimes (I can't remember exactly when) I've had my navigation bar not show up because it was missing a connection.
Make sure you are not hiding the tab bar anywhere in code, though it doesn't seem to be the case since it shows up on iPhone.
Otherwise I'm gonna take a guess and say it's something in the NIB. Here are some things you can try:
Check all your connections to outlets
Make sure your objects in the NIB are of the correct class
Verify that the tab bar's "hidden" property is not check in Interface Builder
Compare and verify all the structure of the NIB file between iPhone and iPad
These are just some ideas :) again if you can post the code it would be fantastic.
Let us know how it goes,
Felipe
I had this problem with a app that support both iPhone and iPad. Make sure 'is initial view controller' is checked for the UITabBarController when you examine the view controller using the object inspector. When you do this, xcode will display a 'inbound' arrow on the left side of the view controller if you're using storyboards.