Random Image in XCode Project Using Interface Builder - objective-c

Is there a way, using the Interface Builder, in XCode to display a random image? So let's say I have three images and want a random one to display each time my app is loaded. I'm new to XCode, so I'm looking for the simplest way to do it and IB seems to be the route to take. Thanks!

After thinking about this for a few seconds, I realized the answer is of course you can, but it is such a pain, that I doubt that you would want to. I came up with a approach as an example of how to use IB in such a way.
First, subclass UIImageView, I will call it MyRandomImageView. The only member of MyRandomImageView is #property (retain, nonatomic) NSString *randomImageJSON.
Next, add a UIImageView to your view. In the Identity Inspector, change the Custom Class from UIImageView to MyRandomImageView. Then in the Identity Inspector, under User Defined Runtime Attributes, set randomImageJSON to something like [ "image1.png", "image2.png", "image3.png" ].
The final part is where you have all the trouble. Create -[MyRandomImageView setRandomImageJSON]. It needs to set the _randomImageJSON iVar, use NSJSONSerialization to create the array of strings, randomly pick one of the strings, then set self.image = [UIImage imageNamed:randomImage]
This should do exactly and you wish, a random image from the Interface Builder. Hope that helps.

You have to do this programmatically. Haven't tested the code below, but I'm pretty sure it works.
int imageNumber = arc4random() % 3;
imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:#"%i", imageNumber]]];
You should first declare it in the implementation and link it to your UIImageView in the Interface Builder.

Related

How do I re-define size/origin of app window in code, overriding nib/xib file parameters, with Obj-C, Xcode 11.3.1 on Mac OS X 10.15.2 (Catalina)?

I'll try to keep it short. I want to create a 3D FPS game, just for myself, that can run on multiple platforms, but I figured that to keep it simple, perhaps it is best to start off with something that is exclusively for macOS. I opted for Objective-C because
(a) Window Application projects in Xcode can only be coded either in Obj-C or Swift (since we are dealing with Cocoa API) and
(b) Obj-C is closer to old-school then Swift.
But before I learn to draw/render 2D-shapes on the window's canvas by writing code, I have to learn to invoke an application window with its properties set to my liking. I've spent hours doing research and experimenting with chunks of code. This is what I've tried: I open with
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
Then I go with ...
1)
NSWindow *window = [[[NSApplication sharedApplication] windows] firstObject];
NSRect frame = [window frame];
frame.origin.x = 100;
frame.origin.y = 200;
frame.size.width = 100;
frame.size.height = 500;
[window setFrame: frame display: YES];
... and close with ...
NSApplicationMain(argc, argv); // runs the win display function.
}
return (0) ;
}
But no visible changes. Nothing really gets reset. So instead of (1) I tried ...
2)
NSWindow *window = [[[NSApplication sharedApplication] windows] firstObject];
NSPoint newOrigin;
newOrigin.x = 400;
newOrigin.y = 100;
[window setFrameOrigin : newOrigin];
Still nothing. Then instead of (2) I tried:
3)
NSWindowController* controller = [[NSWindowController alloc]
initWithWindowNibName:#"MainMenu"];
[controller showWindow:nil];
Great. Now it's spitting out something I don't understand, especially since I'm new to Obj-C:
2020-02-08 21:53:49.782197-0800
tryout_macApp2[14333:939233] [Nib Loading] Failed
to connect (delegate) outlet from
(NSWindowController) to (AppDelegate): missing
setter or instance variable
I remember dicing around with an ApplicationDelegate, with CGSizeMake(), etc., but it just made the experience really inundating and frustrating. Nothing happened. Then there are NSView, NSViewController, and other classes, which is really mindboggling and begs the question: why are there so many classes when all I want to do is override the preset origin of the window and the dimensions preset by the MainMenu.xib file? (By the way, this project is derived from a Window Application project provided by Xcode.)
I really can't think of anything else to add to give you the entire picture of my predicament, so if you feel that something is missing, please chime in.
[Edit:] Moving forward to phase 2 of my project here: How do I paint/draw/render a dot or color a pixel on the canvas of my window with only a few lines in Obj-C on Mac OS X using Xcode?.
The short answer is that main() is too early to be trying to do this. Instead, implement -applicationDidFinishLaunching: on your app delegate class, and do it there. Leave main() as it was originally created by Xcode's template.
After that, I would say to obtain the window (if there's only going to be one main one), it's better to add an outlet to your app delegate and then, in the NIB, connect that outlet to the window. Then, you can use that outlet whenever you want to refer to the window.
Also, make sure that Visible at Launch is disabled for the window in the NIB. That's so you configure it as you want before showing it.
For a more complex app, it's probably better to not put a window into the Main Menu NIB. Instead, make a separate NIB for the window. Then, load it using a window controller object and ask that for its window.
I love Objective-C but also feel your pain, it has this testy ability to frustrate you endlessly.
I have not really developed a game but let me try and point you in the right direction. I think you need a UIViewController.
Now each UIViewController has a built in UIView that sort of represents the visible portion of it. You can use this or add a UIView and use that, whichever depends on your implementation. For now I'd suggest add a separate UIView and use that rather. Once you're comfortable you can then move the implementation to the UIViewController's view if you need to.
Anyhow, for now, create a UIView subclass, say MyGame or something, as for now all your code will end up there.
To do all of the above is not easy, especially if its the first time. If you can follow some tutorial it will be great. Even if the tutorial just adds a button, you can use it and replace the button with your view.
Anyhow, now that you've got that running and the view you've added shows up in green or some other neon colour just to verify that you can indeed change its properties, you're good to go.
Now you start. In MyGame, implement the
-(void)drawRect:(CGRect)rect
message, grab the context through
UIGraphicsGetCurrentContext
and start drawing lines and stuff on it, basically the stuff I understand you are interested in doing. You can also, through the same context, change the origin of what you are doing.
Hope this helps.

Navigating to another view iPhone sdk?

Is it possible for me to navigate to a view that has uitextfield and make it so that uitextfield doesn't change everytime I navigate?
If I understood correctly, you want a text that was previously, there...
there is a lot of options but an easy an fast one is have a static NSString....
so in your class you have the uitextfield, in that class you define
static NSString *myString;
then, when the user or you modify it and you have access to it, define it in the static NSString.
Now, to recover it, in the method you initialize that view you do:
myTextField.text = myString;
that, will restore the text.

Changing an NSImage in XCode - this line of code not working

I am having a problem that is eating me alive. I really hope I am just missing something small here. It appears to be a rather "n00b" issue.
I have a blank NSImageView that I want to display a picture when a button is pressed — simple as that.
Here is my line of coding
NSBundle *mb = [NSBundle mainBundle];
NSString *fp = [mb pathForResource:#"tiles" ofType:#"PNG"];
NSImage *image = [[NSImage alloc] initWithContentsOfFile:fp];
if ( [image isValid] ) {
[selection setImage:image];
[selection setImageScaling:NSScaleProportionally];
}
Whereas,
tiles.PNG is a resource in my bundle
and if [image isValid] is satisfied, because I've inserted dummy code into the clause and had that work
selection is defined in my header file as follows
IBOutlet NSImageView *selection;
It is also linked up to the application delegate in IB.
I have a feeling I might not be linking it properly?
WHy wouldn't the image display? If anyone can see an error - or provide me with working code - I would be soooooo thankful
Brian
It's not a linking issue—your app wouldn't even launch (assuming it even links successfully) if you'd failed to link against Cocoa or AppKit.
More probably, either you haven't connected the outlet to your image view in your nib, or you haven't loaded the nib yet. The way to check this would be to NSLog the value of the imageView pointer, using the %p formatter.
I had a similar issue where my view wasn't displaying, and it turned out that the view was hidden. This was a setting in the view properties in Interface Builder. Just a punt, but give it a go.
You need to use the debugger and see what's going on as it runs. Is fp nil? Is image nil? Is selection nil? The debugger is your friend.
did you remember to send -setNeedsDisplay to the NSImageView after you set the image?

Loop through similarly-named Controls

I have view0 through view25. I don't particularly want to have a 25-case switch, so is there a way to do something like this?
- (void)modifyViewNumber:(int)number
{
[view*number* dosomething];
}
Put the views in an array at startup.
You could put a tag on each view and use a for loop with the following method:
- (id)viewWithTag:(NSInteger)aTag
Nobody's suggested NSMatrix yet? This sounds like what it's made for.
The caveat is that the views must all be controls, such as text fields or buttons. Basically, look at its class reference and see whether it inherits from NSControl at some point. If it passes that test, then NSMatrix is an option. (Here's the list of all AppKit classes, to make this easy.)
To make a control into a matrix in IB, create one of the control in the usual way, then option-drag its resize handle. It won't appear to do anything at first, but keep dragging. Instead of resizing, your control will proliferate; it is now a matrix of cells instead of a single control.
Why don't you put the views into an array and obtain references to them using the array index?
- (void)modifyViewNumber:(int)number
{
UIView* view = [views objectAtIndex:number];
[view dosomething];
}
Many people have said use an array--I do this all the time, it's really the only way to go when using an interface builder, but I wanted to add a little.
Sometimes getting the values into the array can be tricky. There is usually a way to get an array of all controls on the screen. usually I'll grab that and iterate over it looking for a type of control with a certain naming pattern and collect those into my own array.
In this way, you can often add a new control with no code change whatsoever (always one of my favorite goals).
Create an array of views. Add each one, in order, then reference it by the array index.
If the numbers are not sequential, use a hash table.
Reflection might be an option, but could be slower (I'm not an Objective C guru, don't even know if that is practical).
I assume you're using Interface Builder to setup those views?
Someone may have to correct me on this, but I believe you can create an NSArray Interface Builder outlet in your class and then assign all your views to it. For example, you could declare "IBOutlet NSArray * views;" in your header file, and then use interface builder bindings to tie all 25 views to that property. They'll automatically be added to the array, and then you can cleanly iterate over it in your code.
Either use a collection or Reflection.
Assuming Interface Builder (otherwise straightforward as answered before). This is the best I could manage:
In .h:
{
UILabel *banner[NUM_BANNERS];
}
#property (nonatomic, retain) IBOutlet UILabel *banner0;
#property (nonatomic, retain) IBOutlet UILabel *banner1;
#property (nonatomic, retain) IBOutlet UILabel *banner2;
etc.
In .m:
- (void)viewDidLoad
{
banner[0] = banner0;
banner[1] = banner1;
banner[2] = banner2;
etc.
Then can access by array index. Remember to release.

Is it possible to design NSCell subclasses in Interface Builder?

I'm trying to subclass NSCell for use in a NSTableView. The cell I want to create is fairly complicated so it would be very useful if I could design it in Interface Builder and then load the NSCell from a nib.
Is this possible? How do I do it?
The question was about a subclass of NSCell; the other answers seem to be doing something else, likely taking advantage of UITableViewCell being a view.
NSCell is not a view. While laying a custom cell out in IB would be a useful thing to be able to do, I think the answer is basically "no, this is not possible". When you subclass NSCell, you're pretty much just doing your own drawing. There isn't support subcells, or parameterized auto layout (ala NSView's springs and struts), which is I suspect what you're looking for.
The only caveat is that you could design an NSCell subclass that did do layout of sub-elements and provided parameters for setting those subelements and all tweakable parameters. Then, you would need to write an IB plugin to make that cell and accompanying inspector available at design time in IB.
This, however, is probably harder than writing a little custom app that does more or less the same thing. Put an NSCell in a control in the middle of a window, and make yourself UI for tweaking the parameters you're interested in. Bindings can make this pretty straightforward for positioning stuff (i.e. bind an x value to a slider), though you will not get direct manipulation of the elements of course. When you're done, you could archive your cell and load the archive at runtime in your real app, or you could just log out the properties and set them in code in your app.
Some answers in this thread have gone off topic because they're talking about Cocoa Touch, when the original question was about Cocoa - the 2 APIs are quite different in this regard and Cocoa Touch makes it easy because UITableViewCell is a view subclass. NSCell isn't, and that's the problem
For information, I had to do something very similar in NSOutlineView recently - which is basically the same, but a little harder if anything because you have to deal with disclosure / collapse of levels. If you're interested in the code, I posted about it here: http://www.stevestreeting.com/2010/08/08/cocoa-tip-using-custom-table-outline-cells-designed-in-ib/
HTH
As Ken says, NSCells and NSViews are different, and you can only lay out NSView hierarchies in NIB, not NSCells (which don't have any explicit hierarchy).
On the other hand, there's nothing preventing you from having a hierarchy of NSViews and using that to draw your NSCell -- you could add them as a subview of your cell's parent view, tell them to display, and remove them from the window and nobody would be the wiser.
In this case, using a NIB would work, although it seems like a ton of hassle. Typically I've just replaced the object that takes NSCells with a custom one that takes my NSViews, but that means writing your own mouse-handling code, which is very touchy.
On the other hand, my approach lets you bind the views' values in NIB, so you don't have to do any extra work, which is cool.
In IB, start an empty XIB. Now go to the pallete and drag in a UITableViewCell, double click to bring up and edit.
include only the custom UITableViewCell (no other UIViews or other top level controls) - make sure it's a real UITableViewCell in IB, or you cannot set a reuse identifier (as opposed to casting a UIView in IB as your custom UITableViewCell class).
Then you can add lables or whatever you like within the cell, as well as setting the reuse identifier or set whatever disclosure indicator you might like.
To use, you provide code like this in the tableView:cellForRow:atIndexPath: method:
YourCustomCellClass *cell = (YourCustomCellClass *)[tableView dequeueReusableCellWithIdentifier:<IDYouSetInXIBFile>];
if ( cell == nil )
{
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:<YourXIBName> owner:self options:nil];
id firstObject = [topLevelObjects objectAtIndex:0];
if ( [ firstObject isKindOfClass:[UITableViewCell class]] )
cell = firstObject;
else cell = [topLevelObjects objectAtIndex:1];
}
If you have any labels or other controls you want to reference in your code, wire them in IB to your custom cell class - NOT the file's owner, which you do not ever need to set using the above code (you can leave it as NSObject).
Edit: I note you are really looking for an NSCell answer, but the code approach to using IB should be identical in Cocoa with the Cocoa Touch code I used above as loadNibNamed is a standard Cocoa call.
Joar Wingfors wrote an article for Stepwise a few years back on a related topic, Subviews in TableView Rows.
The principal technique is to create an NSCell that can host an NSView. If you were to do this, you could then design an NSView subclass in Interface Builder that you could embed anywhere you need that specific cell.
Another possibility, if you can target Leopard, is to see whether you need to use an NSTableView or whether you can use an NSCollectionView. Collection views deal directly in terms of "item views" rather than in cells, so they're much more straightforward to design in Interface Builder.
I found some interesting examples which I do not totally understand, though.
GitX extends the NSTextFieldCell in their PBIconAndTextCell, referencing this post.
WWDC 2009 - Session 110 "Presenting User Data with Table Views and Browsers" talks "Adding subviews" and "Custom cell editors". (I do not have the source code, though.)
Display an NSTextfieldCell containing text and an image within a NSTableView, #70
Display an NSTextfieldCell containing text and an image within a NSTableView, #71
The last 2 examples work with NSTableViewDataSource and NSTableViewDelegate. I would like to use Bindings an ArrayController in the InterfaceBuilder to connect other UI elements like text fields.
I stumbled into another discussion where Abizern points out PXListView by Alex Rozanski which looks very promising!
I am trying to implement a solution for the problem myself. Please find my project on github and the latest rendering problems over here.
I do it like this:
/* example of a silly way to load a UITableViewCell from a standalone nib */
+ (CEntryTableViewCell *)cell
{
// TODO -- this is really silly.
NSArray *theObjects = [[NSBundle mainBundle] loadNibNamed:#"EntryTableViewCell" owner:self options:NULL];
for (id theObject in theObjects)
if ([theObject isKindOfClass:self])
return(theObject);
NSAssert(NO, #"Could not find object of class CEntryTableViewCell in nib");
return(NULL);
}
However it isn't very efficient and if you're loading lot of data it might hurt you. Of course you should be using a reuseIdentifier which should force this code to only run a handful of times per table.
1) Create NSViewController TableViewCell.h
2) Create in TableViewCell.h some procedures like
-(void)setText:(NSString *)text image:(NSImage *)image
3) In Main class #import "TableViewCell.h"
4) In Main class in -(NSView *)tableView:viewForTableColumn:row: write:
NSImage *img = //some image
TableViewCell *cell = [[TableViewCell alloc] initWithWindowNibName:#"TableViewCell"];
cell.view.init;
[cell setText:#"some text" image:img];
return cell;
Hope this will help =)
I want to provide a more modern approach here.
Starting with iOS 5, UITableView has a method
(void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier
Once you registered your NIB containing your cell, just use
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
to get a new cell. If a cell is available for reuse, it will be returned, otherwise a new cell is automatically created, and in that case this means loaded from the NIB file.
Add your UITableViewCell to your tableviewcontroller and declare an IBOutlet property:
#interface KuguTableViewController : UITableViewController {
IBOutlet UITableViewCell *customTypeCell;
}
#property (readonly) UITableViewCell *customTypeCell;
... then in cellForRowAtIndexPath you can just use your cell and set it to be reused:
static NSString *CellIdentifier = #"CustomCell"
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = customTypeCell;
cell.reuseIdentifier = CellIdentifier;