Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

larswik

macrumors 68000
Original poster
Sep 8, 2006
1,552
11
I am almost finished with my super cool Experience Point calculator for our RoleMaster RPG. (yep, I am a geek at heart!). I did a release build and the images files were not there. Chown33 explained to me that I need to us the NSBundle and I read the documentation but I did not understand it completely. I then scoured the web and everything tutorial wise is for Xcode 3 or iPhones.

Here is how I access my images right now.
Code:
-(void)setRoundImages{
    NSImage *rOneOff = [[NSImage alloc] initWithContentsOfFile:@"/Volumes/Media_Projects/Programing/Objective_C/Xp Calc2/images/rOneOff2.png"];
    [roundOneLight setImage:rOneOff];
    [rOneOff release];

I have all my images in an image folder within my project. If I understand NSBundle correctly I use it to gather my images and bring them into the program to use. Do I bring in just the image folder or each image by it's self?

If anyone has any links to any tutorials that explain this better or sample files, I would be thankful. There are so many Methods in the NSBundle class and I am getting lost.

This is the last step before I can do the final working build! Then I can share it!

-Lars
 
NSBundle is a class for managing bundles of files, like Applications and so on. [NSBundle mainBundle] represents the currently running application's bundle.

You need to drag all of your images into the source list of your Xcode project if they're not already there.

Once they're there, Xcode will copy the images into your application bundle on build.

Once there, you can use an NSImage method to get them out - so the code:
Code:
-(void)setRoundImages{
    NSImage *rOneOff = [[NSImage alloc] initWithContentsOfFile:@"/Volumes/Media_Projects/Programing/Objective_C/Xp Calc2/images/rOneOff2.png"];
    [roundOneLight setImage:rOneOff];
    [rOneOff release];

... becomes ...

Code:
-(void)setRoundImages{
    NSImage *rOneOff = [NSImage imageNamed:@"rOneOff2.png"];
    [roundOneLight setImage:rOneOff];

FWIW, [NSImage imageNamed:] is basically a shortcut for:

Code:
    NSString *path = [[NSBundle mainBundle] pathForResource:@"rOneOff2.png" ofType:@""];
    NSImage *image = [[NSImage alloc] initWithPath:path];
    return [image autorelease];
 
Forget File Structures

I have all my images in an image folder within my project. If I understand NSBundle correctly I use it to gather my images and bring them into the program to use. Do I bring in just the image folder or each image by itself?

This is the last step before I can do the final working build! Then I can share it!

-Lars

There's your problem right there. You are trying to manage your assets (images) using the operating system's file structure.

Apple has been on a crusade for about a decade to have each application handle its own data and files. All interaction with the files will be through the application, not through the operating system. See iTunes and iPhoto for examples of this. It's even more stringent on iOS.

In this case, you need to let Xcode handle your assets. How to do this? Get your asset files (using the operating system) and drag and drop them into Xcode in your Project Navigator panel. Xcode will ask if you want to copy them into its Xcode secret location. Say Yes.

Now when you're coding in Xcode and you need an image, it shows up in your Project Navigator. That item in your Project Navigator points to the actual file (that Xcode copied to its secret location).

Note that now there are two copies of these files - the original and the one that Xcode copied to its secret location. You can do whatever you want to the original (if you answered Yes to Xcode copying it) and it won't affect Xcode or your code.
 
Thanks guys. I was looking for the secret folder but could not find it. I did drag in a couple of images into that folder. Later I wanted to change it and tried to delete the items. It asked me if I wanted to remove the reference or delete it. I selected Delete. I made change in Photoshop and tried to drag it back into the project but it said it already existed/ I had to rename it and drag it in again. Is there a way to get to that folder to physically delete the items?

-Lars
 
Thanks guys. I was looking for the secret folder but could not find it. I did drag in a couple of images into that folder. Later I wanted to change it and tried to delete the items. It asked me if I wanted to remove the reference or delete it. I selected Delete. I made change in Photoshop and tried to drag it back into the project but it said it already existed/ I had to rename it and drag it in again. Is there a way to get to that folder to physically delete the items?

-Lars

In Xcode right click on a file and select reveal in finder, and it will open the folder the file is in. You can delete the file there. If that doesn't work, the "secret location" is just your project folder, or possibly a subfolder of that. If you know the filename, you can always resort to a spotlight search.

Back you your earlier questions about NSBundle.

If you right click on any .app file, you can open the package contents. That is what is refered to as a Bundle. NSBundle gives you the location of inside that file. You can do this with any .app file, even the programs in your applications folder.

There is a folder in that file, resources. That is where xcode will copy all of your images. To access them.

1. Use NSBundle to get the mainbundle.

2. Send the mainbundle the -resourcePath message to get the path to the resource folder in your .app

3. Append to the returned NSString the filename of your image. IIRC you need to include the leading "/" like @"/image.png"

4. Use that NSString with the initWithContentsOfFile: method

I would suggest NSLogging after each step so you can see what is happening, and read the docs to NSBundle again.

Edit:
I just reread your last post. Is the image file you are editing with photoshop in you project folder(in finder not xcode)? If so, that might be your problem. Xcode can't copy the file there, because it is already there. Move the file out of your project folder. Then when you drag it into Xcode, it will copy back into your project folder.
 
3. Append to the returned NSString the filename of your image. IIRC you need to include the leading "/" like @"/image.png"

There are NSString methods for working with pathnames. Do not do pathname dissection/assembly manually.

And the whole "build the pathname" strategy is flawed. There are NSBundle methods for returning resources. See Resource Programming Guide under heading "Loading Image Resources".
 
And the whole "build the pathname" strategy is flawed. There are NSBundle methods for returning resources.

Just an aside...

This is precisely the kind of thing I was talking about in Lars' other recent thread.

This kind of "build the pathname" strategy is usually a legacy of learning how to do something similar this way in a lower level language like C.

And that takes some "unlearning" to avoid.

When the tool you are most familiar with is a hammer, everything starts to look like a nail, even if you have other tools at your disposal.

B
 
Hey, slightly different question but applies to my same project. I use this code
Code:
 [buttonOne setKeyEquivalent:@"1"];
to assign a key equivalent for my calculator program. I reassigned a few keys to fit what I needed for an Experience Points calculator.

I can not seem to be able to find the key equivalent to the 'clear' button on the keypad, the located above the '7' button. Does anyone off hand know if it can be assigned and what it is?

Thanks

-Lars
 
Hey, slightly different question but applies to my same project. I use this code
Code:
 [buttonOne setKeyEquivalent:@"1"];
to assign a key equivalent for my calculator program. I reassigned a few keys to fit what I needed for an Experience Points calculator.

I can not seem to be able to find the key equivalent to the 'clear' button on the keypad, the located above the '7' button. Does anyone off hand know if it can be assigned and what it is?

Thanks

-Lars

Code:
    unichar clearKeyEquiv[] = { NSClearLineFunctionKey };
    [button setKeyEquivalent:[NSString stringWithCharacters:clearKeyEquiv length:1]];

These NS...Key constants are documents in Function-Key Unicodes of the Constants section of the NSEvent class reference.
 
NSBundle is a class for managing bundles of files, like Applications and so on. [NSBundle mainBundle] represents the currently running application's bundle.

You need to drag all of your images into the source list of your Xcode project if they're not already there.

Once they're there, Xcode will copy the images into your application bundle on build.

Once there, you can use an NSImage method to get them out - so the code:
Code:
-(void)setRoundImages{
    NSImage *rOneOff = [[NSImage alloc] initWithContentsOfFile:@"/Volumes/Media_Projects/Programing/Objective_C/Xp Calc2/images/rOneOff2.png"];
    [roundOneLight setImage:rOneOff];
    [rOneOff release];

... becomes ...

Code:
-(void)setRoundImages{
    NSImage *rOneOff = [NSImage imageNamed:@"rOneOff2.png"];
    [roundOneLight setImage:rOneOff];

FWIW, [NSImage imageNamed:] is basically a shortcut for:

Code:
    NSString *path = [[NSBundle mainBundle] pathForResource:@"rOneOff2.png" ofType:@""];
    NSImage *image = [[NSImage alloc] initWithPath:path];
    return [image autorelease];

This is not actually quite true. It will search your bundle, yes, but it also searches a bunch of other places as well (system shared images, and the named images table for example), and sets the name of the image if not already set, and impacts some caching decisions. Check the docs for details.

Your NSBundle code is also not quite right. Don't put ".png" in the resource name, put @"png" as your type instead of an empty string. OR, even better, use NSBundle's -URLForImageResource: method. It automatically adapts if you change image types, is more filesystem efficient (NSURL caches fs info and is the modern way to deal with paths), and is less characters.

A shorter way to do this may be forthcoming. Ping me after Lion is released or check the docs.
 
Basic Operations

Just an aside...

This is precisely the kind of thing I was talking about in Lars' other recent thread.

This kind of "build the pathname" strategy is usually a legacy of learning how to do something similar this way in a lower level language like C.

And that takes some "unlearning" to avoid.

When the tool you are most familiar with is a hammer, everything starts to look like a nail, even if you have other tools at your disposal.

B

Yes. This was one of the biggest hurdles for me in moving from procedural programming (Pascal, PL/I, BASIC, Fortran, APL, COBOL) to Objective-C. In those other languages, there is a small set of basic operations (add, subtract, print, save, GOTO, For/Next), maybe 40 or 50 in total. To write a program, you thought in those small steps and constructed solutions.

In Cocoa / Objective-C, almost all of the solutions have already been constructed. There are probably 2,000 "basic operations" in Cocoa (those would be the various methods of the various classes). To write a program, you string together sets of these pre-constructed mini-solutions.
 
Your NSBundle code is also not quite right. Don't put ".png" in the resource name, put @"png" as your type instead of an empty string.

That snippet is an (extremely simplified) example of what [NSImage imageNamed:] might do if you pass in "name.png". It *might* do what I did or it might split the extension out. Neither of us know what it actually does.

Obviously, if you're using NSBundle directly, split them up if you know what resource type you're working with up front.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.