Multiple instances of a single content view in NSStackView - objective-c

I am creating an application that need to have a progress window in which i want to dynamically insert a subview for each item being processed, like Finder's copy files panel window. And also remove it dynamically when processing is done.
I want to use the same NSViewController view for all sub views, and I'm using an NSStackView to manage the views.
But, to make the sub views stay in memory I have to keep a strong reference to them, and the only way I know of is to create a property for each sub view i need to display. Like this:
#propery (strong) NSViewController *myViewController1;
#propery (strong) NSViewController *myViewController2;
#propery (strong) NSViewController *myViewController3;
#propery (strong) NSViewController *myViewController4;
....
I would like to know if there is a better, more dynamic way of doing this? Or do I have to create x number of properties for sub views to know i have enough instances to allocate, because I can't tell how many process views the user will need when running the application and exporting items.
I would therefore like to dynamically allocate each subview AND create a strong reference to it. Is that possible? Or is there another way of doing what I want?
Please let me know if I'm being unclear, I'll gladly explain more to get help with this problem.

You can store the references to the view controllers in an NSArray. Arrays keep strong references to the objects which they contain.

Related

Creating Multiple windows of same NSWindowController class type

I want to create a NSWindow of same NSWindowController class type each time user sends an action.
This is my Code.
objController = [[MyController alloc] initWithWindowNibName:#"MyController"];
[objController showWindow:nil];
This is a simple two liner which gives me NSWindow. but the thing is, If I don't make a class level object, the window doesn't get displayed. So, I had to make a class level object. It worked well and gave me NSWindow of type MyController.
But, since it is a class level object, If I want to trigger this action every time user clicks on a button, previous window gets closed. And new window gets appeared.
I don't want this to happen. I want to keep all the previous NSWindows in memory and user can interact with them.
How do I do it ?
I think, this should be something small but at the time I don;t have any solution in my hand.
Kindly help me to get this.
Thank you.
My guess is that by "class level object" you mean a #property of type MyController, which can indeed only hold a single window controller. If you need to store several instances of MyController and do not want to create a separate #property for each of them, you need to put them into an array type, namely a NSMutableArray.
Add a #property NSMutableArray *myControllers to your class and initialize the array (for example in the - init method or your class) with self.myControllers = [NSMutableArray array].
Now you can add newly created window controllers to it with [self.myControllers addObject:] which makes them stay in memory instead of overwriting each other by sharing the same property for storage.

How to access NSArray instance belonging to MyDocument in another xib?

In my document application I subclassed an NSArrayController which I referenced in MyDocument.xib bounding its content to File's Owner.entries.
entries is an NSArray I expose as an attribute of MyDocument class this way:
#interface MyDocument : NSDocument {
NSArray *entries;
}
-(NSArray *)entries;
-(void)setEntries:(NSArray *)newEntries;
#end
This works perfectly fine. Good.
Now I have designed another .xib, CSVEntries.xib which I open via a NSMenuItem through a NSWindowController and again, this works smoothly.
I can't figure out how to make this new window display the content of the MyDocument.entries instance in an NSTableView.
I have tried many things, but the problem basically is that any NSArrayController I put on CSVEntries.xib can not be bound to MyDocument.entries: CVSEntries.xib File's Owner is referring to something else not MyDocument (which is logical, I guess).
I also tried to add a copy of the NSArray instance to the NSWindowController which opens CVSEntries.xib, but since the window get's instantiated only when the user clicks on the menu, I ended up having an empty array.
I am searched around the Internet but could not find an appropriate answer, I just would like to figure out the proper way to approach the issue, I am sure two windows can communicate between eachother, maybe accessing their common parent (NSApplication instance)?
All newbie questions, I know :)
Update
I think I figured out where to look but still not how to. I think I have to make sure second window File's Owner is MyDocument but in the NSWindowController initWithWindowNibName:owner: I can't understand how to specify the MyDocument instance as the owner. Specifying it only in the xib file File's Owner seems not enough.
Update 2
Uhm...I am starting to think that I need to have two NSWindowController instances both instantiated by my NSDocument subclass (default MyDocument). With those in place maybe both the NSWindowControllers will have access to the MyDocument.entries NSArray and the two windows xib files will be able to be bound to File's Owner.entries.
Can someone confirm this? Thanks.
I reply to my own question here, basically stating that I have realized what I was asking is very much dependent on what the File's Owner is bound to. The design I implemented was not correct and I am therefore redesigning it to achieve my purpose.
Thanks everyone anyway, even no answers are an answer :)

How to programmatically access an NSTableView that was created in Interface Builder?

I am new to Objective-C and Cocoa programming (coming from a background of C/C++ development years ago on other platforms). I am writing an application to download remote data on a recurring basis (i.e. every X number of seconds), parse through it, sort/filter it into an NSArray, and display/update said data in an NSTableView. After reading a few books, a lot of Apple OS X Reference material, and experimenting I have managed to implement everything (the remote data download, parse/filter logic, in-memory storage, etc.) except actually updating the NSTableView with the data.
I am not sure if I am just missing something obvious or just how my application should be laid out following the MVC concept, or if Interface Builder's lack of actual code generation is just not what I am used to, but I cannot seem to determine how I can programmatically access/manipulate the NSTableView that was created in Interface Builder.
I tried (in Interface Builder) dragging a NSObject instance of my NSArray-based object in, where-then I can connect my NSTableView's Outlet/datasource, but this results in an another instance of my NSArray-based object (not connecting the NSTableView to my existing, programmatically-declared and instantiated object). Likewise, I thought to set my NSTableView's datasource programmatically, but I have not been able to determine how I can programmatically refer to the NSTableView object stored in the .xib/.nib file other than via a Tag (for which I have not been able to determine what object to call the viewWithTag: method from, after setting my NSTableView's Tag value in Interface Builder).
Any suggestions, advice, or guidance would be greatly appreciated. This feels like one of those things that will be very simple (and once I have it working in front of me, it will make a lot more sense), but I just cannot seem to get a starting point/example working.
You need to attach an instance variable in your table's controller class to the table in interface builder. Declare a table in your class like this:
IBOutlet NSTableView* myTable;
...
#property (nonatomic, retain) IBOutlet NSTableView* myTable;
And be sure to synthesize it.
In the connections tab of the info window in interface builder, connect your controller's new outlet to your table. Then when your view is loaded from the XIB, this outlet will be connected.
Hope that helps some.

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.

Events for custom UIView

What's the best way for registering events for my UIView subclass, so that I can connect them to IBAction-s in interface builder?
Currently I've just got a standard UIView dropped onto my main view and I've set the class to "RadioDial" (my custom class). This displays the view fine, but I have no idea how to get events out of it.
Thanks
Please clarify: do you mean that you would like Interface Builder to offer your view controllers to wire up custom events that your view subclass will be emitting (much like the Button controls allow you to wire up Touch Inside, etc)?
If you need this type of functionality, you will need to use a generalized 'delegate' property on your View combined with a protocol.
#protocol RadioDialDelegate
-(void)dialValueChanged:(id)sender
#end
#interface RadioDial
{
id<RadioDialDelegate> radioDelegate;
}
#property (nonatomic, assign) IBOutlet id<RadioDialDelegate> radioDelegate;
This will allow the controller to wire up to the view (assuming it implements RadioDialDelegate) and receive any events that come out of the view. Alternatively, you can use an untyped delegate and in your View code, use a late bound call:
if([radioDelegate respondsToSelector:#selector(dialValueChanged:)]) {
[radioDelegate dialValueChanged:self];
}
Create a method in your view controller (if nothing else, you should have a RootViewController in you project). Let's say your method is
-(void) buttonClicked { code code code }
In the controller's header file (for example RootViewController.h) you then put:
-(IBAction) buttonClicked;
And in IB you right-click your button/radio dial/whatever. You will see a list of events and you can drag FROM the connector of the event you want your controller to receive, to the object in IB that represents the controler (probably First Responder). This depends on how your IB structure is set up, but it should be straightforward.
Another alternative is to learn how to create UIViews programatically, and forget about IB for the time being. Opinions are divided about whether it's better to learn to use IB at the outset, or whether it's better to learn how to do everything in code and save IB for later. In any case, it's necessary to learn both ways of setting up an interface at some point.