Best way to handle multiple NSTableView(s) - cocoa

What is considered the best way of handling multiple NSTableViews without using Cocoa Bindings?
In my app, I have two NSTableViews that are sufficiently closely related that I'm using the same object as the delegate and dataSource for both. The problem is both tableViews invoke the same methods. I currently discriminate between the two tableViews on the basis of NSControl -tag.
The deeper I get into this code, the uglier the use of -tag looks. I end up creating largely duplicate code to distinguish between the tableViews in each delegate/dataSource method. The code ends up being distinctly non-object oriented.
I could create a separate object to handle one or the other tableView, but the creation of said object would be a largely artificial construct just to provide a distinct delegate/dataSource.
Is everyone just using Cocoa Bindings now? I'm avoiding Bindings as I would like to hone my Cocoa skills on techniques that are transferrable between Mac OS and iPhone.

Every delegate/dataSource method for NSTableView passes the instance of NSTableView that's calling it as the first parameter (except for the ones that pass NSNotification objects, in which case the NSNotification's object is the table view instance). Some examples include:
- (int)numberOfRowsForTableView:(NSTableView*)aTableView;
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(NSInteger)rowIndex
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
If you're using one controller object as a delegate/data source for multiple tables, you can just use that parameter to differentiate between them.

for the method :
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
you can use :
NSTableView *theTable = (NSTableView *)[aNotification object];
if(theTable==listeDesMots)
...

It sounds like you should be using a different delegate object for each view, but the same data source. In other words a single model for distinct view and controller objects.
I don't think this is an artificial distinction because the objects have sufficiently different purposes, but you want to use the same data. The bigger rule you are violating now is that each object should have a single purpose. Each objects' purpose could be to retreive and display the data in a specific way.
Good Luck!

Related

Can multiple NSArrayControllers control the same array?

I want to keep several NSPopUpButtons synchronised with the same model object. I'm just getting familiar with bindings and have implemented the following scheme.
Is this a terrible idea?
Here each NSPopUpButton has their own NSArrayController. Each NSArrayController gets their content from the same NSMutableArray data source in my model layer.
I have noticed some strange problems when adding and removing objects to the model array (the array of animals in the example above) and was wondering how viable this approach is or whether there is a better way to keep several views in sync with the same model?
You should be fine doing this, as long as you keep your changes to the model in the main thread.
Each NSArrayController will listen for KVO notifications from your NSMutableArray and will update themselves accordingly.
If you change the array through one of the NSArrayController it will update your NSMutableArray, which again will trigger a KVO notification that will be caught by the other two NSArrayControllers.
It is important that you update your NSMutableArray in a KVO compliant manner. Namely you should be using the array proxy returned by mutableArrayValueForKey:

programmatically using NSTableView

I need to create/use a NSTableView programmatically. From the documentation, it seems that I would implement the NSTableViewDataSource protocol. But the function tableView:objectValueForTableColumn:row: suggest (because of the row index) that I would have to manually take care of the sorting. Is that right? Also, as this is called on every redisplay, that might be slow because I am using Python and it would mean a Python call for every row/column.
I wonder wether it make sense to use Cocoa binding and wether that would be more simple. In any case, I would have to do that programmatically and I am a bit stumbled about how to that. From other examples, I guess I would create a NSArrayController and bind it all together somehow.
Also, I want to have it working on older MacOSX, so I guess I have to use the cell-based NSTableView, whatever that means.
The data source will be static and is not editable, i.e. I can just provide a NSArray with the data.
There are three ways to use NSTableViews: 1) delegate methods; 2) NSArrayController; or 3) Bindings. My best advice to you is to learn all three of these in Xcode on a Cocoa ObjC project first before attempting to do this in python. Note: I'd also recommend that you first learn how to do these via nibs and then figure out how to do it programmatically (again in Xcode on a Cococa ObjC project before attempting it in python).
If you understand how Interface Builder (view in Xcode 4, app pre-Xcode4) bindings work then for the following code "Bind To" corresponds to myController, "Controller Key" would be "selection", and the Model Key Path would be "fullPath".
[myView bind: #"valuePath"
toObject: myController
withKeyPath: #"selection.fullPath"
options: nil];
You just need to sort your array once, then when the delegate method is called access the appropriate index in the array.
You really should have a good read of the Table View Programming Guide.

Why would I use NSObjectController?

Although I have searched for many information about Cocoa Bindings, I still remain relatively unsatisfied with information I have and got. It seems that topic is somewhat troublesome for many and many are just avoiding this pattern, which I believe should not be.
Of course, it may seem that bindings are sometimes too complicated or perhaps designed with too much overhead...
However, I have one very direct and specific question: Why is NSObjectController needed if I can establish bindings directly?
For example, the code:
[controller bind:#"contentObject" toObject:self withKeyPath:#"numberOfPieSlices" options:nil];
[slicesTextField bind:#"value" toObject:controller withKeyPath:#"content" options:nil];
[stepperControl bind:#"value" toObject:controller withKeyPath:#"content" options:nil];
Does exactly the same as:
[slicesTextField bind:#"value" toObject:self withKeyPath:#"numberOfPieSlices" options:nil];
[stepperControl bind:#"value" toObject:self withKeyPath:#"numberOfPieSlices" options:nil];
In my case here, we are talking about property of the class inside which everything is happening, so I am guessing the need for NSObjectController is when:
key path for controller is object and binding of other controls is needed to its properties, not to its value as with primitives and wrappers around them is the case (numberOfPiesSlices in my case is NSInteger)
or when binding is needed from other outside objects, not only between objects within one
Can anybody confirm or reject this?
One of the benefits/points of bindings is to eliminate code. To that end, NSObjectController etc. have the benefit that they can be used directly in interface builder and set up with bindings to various UI elements.
Bindings only represent part of the functionality on offer. The *ObjectController classes can also automatically take care of a lot of the other more repetitive controller (as in Model, View, Controller) code that an application usually needs. For example they can:
connect to your core data store and perform the necessary fetches, inserts and deletes
manage the undo / redo stack
Pick up edited but not committed changes to your UI and save them (e.g. if a window is closed while focus is still on an edited text field - this was a new one to me, I found it from mmalc's answer in the thread below).
If you're doing none of this, then it probably isn't worth using NSObjectController. Its subclasses (NSArrayController etc) are more useful.
Also see here for a discussion of your exact question!
Why is NSObjectController needed if I can establish bindings directly?
I read this question a few days ago while looking for some information about NSObjectController, and today while continuing my search, I found the following passage which seemed relevant to the question:
There are benefits if the object being bound to implements
NSEditorRegistration. This is one reason why it’s a good idea to bind
to controller objects rather than binding directly to the model.
NSEditorRegistration lets the binding tell the controller that its
content is in the process of being edited. The controller keeps track
of which views are currently editing the controller’s content. If the
user closes the window, for example, every controller associated with
that window can tell all such views to immediately commit their
pending edits, and thus the user will not lose any data. Apple supply some generic controller objects (NSObjectController,
NSArrayController, NSTreeController) that can be used to wrap your
model objects, providing the editor registration functionality.
Using
a controller also has the advantage that the bindings system isn’t
directly observing your model object — so if you replace your model
object with a new one (such as in a detail view where the user has
changed the record that is being inspected), you can just replace the
model object inside the controller, KVO notices and the binding
updates.

Forwarding property accessors to an ivar's method

I have a panel that elaborates on the selected table row. Obviously, the panel's display often needs updating, and I've pushed that task off to an NSWindowController subclass.
I would like the custom NSWindowController to have #propertys for things like the string values in the text fields and the image values in the NSImageViews. Because I need no novel code in the accessors, I want to #synthesize them. Unfortunately (for me), there is no option to use key-paths in #synthesize property=ivar.
The obvious solution is to write my own accessors of the form
- (void)setTitle:(NSString *)title
{
[titleTextField setStringValue:title];
}
- (NSString *)title
{
return [titleTextField stringValue];
}
but I'd rather not have to do that by hand for each of several properties.
Is there a simpler way? Perhaps a generic way to set up a property to forward to a specific property (except objectValue etc. aren't actually proper properties) of another object?
If you implement valueForUndefinedKey: in your class, you should get a last chance to resolve any key-value path lookups and forward them all to the other object. This is certainly not performant, though, and will only give you significant gains if most of the property names that you're passing through match those of the target object.
I don't think doing any kind of forwarding is going to be useful since the property names on the textfield are going to be different to the ones on your class.
Abstracting away from the textfield by exposing a string property is of course a good idea, but if you want to avoid writing this boiler plate you may find it acceptable to simply expose the text field itself as a property. You can see this in some of Apples APIs, particularly on the iPhone where in SDK 3.0 instead of having properties like text on a table cell they now have textLabel. It's simpler and allows more customization if a callee wants to customize a label or text field in some way.
You have a contradiction, you say you need no novel code in the accesors so you'd like to synthesise them, but the code you need is completely novel. Properties are syntactic sugar for a few common cases, of which this isn't one.
There are several ways to do what you want, eg. as #Justin suggests overriding setvalue:forundefinedkey: - you just need a lookup of forward targets, but the sugar of properties isn't a good fit for this.

iOS layout; I'm not getting it

Well, "not getting it" is too harsh; I've got it working in for what for me is a logical setup, but it does not seem to be what iOS deems logical. So I'm not getting something.
Suppose I've got an app that shows two pieces of information; a date and a table. According to the MVC approach I've got three MVC at work here, one for the date, one for the table and one that takes both these MCVs and makes it into a screen, wiring them up.
The master MVC knows how/where it wants to layout the two sub MVC's. Each detail MVC only takes care of its own childeren within the bounds that were specified by the master MVC. Something like:
- (void)loadView {
MVC* mvc1 = [[MVC1 alloc] initwithFrame:...]
[self.view addSubview:mvc1.view];
MVC* mvc2 = [[MVC2 alloc] initwithFrame:...]
[self.view addSubview:mvc2.view];
}
If the above is logical (which is it for me) then I would expect any MVC class to have a constructor "initWithFrame". But an MVC does not, only view have this.
Why?
How would one correctly layout nested MVCs? (Naturally I do not have just these two, but the detail MVCs have sub MVCs again.)
Thanks all for replying. I will study the links that were provided.
Let me try to explain my issue one more time, hopefully to making it more clear. Do note that I already figured out that my view does not match iOS's, since I do not like where my code is going.
Yes, I'm calling a UIViewController an "MVC", since it for me at the moment implements all aspects of a MVC; it has controller code and an embedded view, plus the controller usually also holds and provides the data (all TableView examples implement it like this).
MVC can be present on many levels; basically a UITextField could (should?) be a MVC; there is a view, but also controller logic involved that you do not want to mix with other code. Encapsulation. For example: Java's Swing JTextField has a MVC. So does a JTable, JList, ... Multiple MVC patterns nested in other MVC's to build a whole screen.
This what I expect when some platform says it uses the MVC pattern. So When I coded the table, I created a MVC and only send the loadData message with a date as the parameter to it. It needs to take care of the rest itself. I have a Detail MVC that can slide in; I then tell it the object it needs to show and it needs to take care of the rest itself. Encapsulation.
So I have a lot of UIViewControllers with embedded UIViews. And that is not the way to do it...
One more potential link is the great talk from WWDC 2010 on MVC.
http://developer.apple.com/videos/wwdc/2010/
It is Session 116 - Model-View-Controllr for iPhone OS
The session is chock full of practical advice on how MVC really works, what makes it tick, why it's good. But it also has a lot of intro stuff to help folks new to the concept to wrap their heads around it.
If I understand your sentence on Java's Swing classes above are you talking about the anonymous classes that respond to events? If so those are not "MVC's", they are what is termed 'Observers', when they observe an event from the view they take some action (usually send a message to a controller). Cocoa Touch uses the Target/Action paradigm (and delegation) to achieve this.
I'd also strongly suggest you take Matthew and Stephen's advice and write a bunch of code. If you don't build that base of intuition, asking the right question (which is most of what is needed to get a good answer) is very difficult.
I really think the WWDC 2010 talk will help.
Good Luck!
If I understand your question -- and I may not, see my comments on it -- I think you're applying the MVC design pattern far too granularly. Most commonly in the setup you describe you'll have a single Model, a single Controller, and multiple Views that are grouped/combined, as in a .xib file.
In Cocoa Touch terms you'd have one UIView that contains a UILabel with the date and a UITableView for your table. These are your Views.
You'll certainly have a Model for the table data, likely an array of data. Your date data might be from its own model if it's a date retrieved from something or calculated or whatever, something entirely separate from the array of data. If it's instead associated with the array data -- they're both pulling from a database, or the date is calculated from the array data, or what have you -- then you have a single Model.
If the data is all coming from a single Model then a single Controller is likely fine. Even if the data is coming from more than one source/Model you likely only need/want one controller in this setup. The UITableView will have a UITableViewController, and that same controller can take care of providing your date as well.
To sum, the Model View Controller design pattern doesn't call for having a bunch of nested sets of models, views, and controllers. They could be, and sufficiently complex projects may call for it. Broadly, though, you'll have a controller that's associated with a model and one or more views, and that set of objects works together to provide a piece of functionality.
Tbee,
I'll post a tiny code example here, since it seems you're not really getting it.
#interface MyView : UIView
#property (retain) IBOutlet UIButton *button1;
#property (retain) IBOutlet UIButton *button2;
#property (assign) bool myData;
-(IBAction) doButton1:(id)sender;
-(IBAction) doButton2:(id)sender;
#end;
#implementation MyView
#synthesize button1 = _button1;
#synthesize button2 = _button2;
#synthesize myData = _myData;
// I'm leaving out the initWithNib, viewDidLoad, etc.
- (IBAction) doButton1:(id)sender
{
// do something as a result of clicking button1
_myData = YES;
}
- (IBAction) doButton2:(id)sender
{
// do something as a result of clicking button2
_myData = NO;
}
#end
Connect those up in InterfaceBuilder, and you've got a working "MVC." You don't need a completely new UIViewController for each button. The one for the View takes care of it.
UITableView and it's associated Views are more complex, and may require an additional UIViewController to help encapsulate. I really don't suggest starting out by using them, but this is a good tutorial here. It's got a lot of images which will show you how to connect things up in IB and the like. It's old, so your XCode may not look like the images, but it helps.
Thanks for the links, I'll look into them.
So far I've rewritten most of my application to using views instead of viewcontrollers (except the toplevel one) and it starts to match up with the API calls that are available like layoutSubviews. What I find disturbing that I need to do this now:
[tableDataSource loadData:date];
[tableView reloadData];
Where in my previous setup all I did was:
[tableViewController loadData:date];
But apparently that is the way to do it. One thing is unclear to me ATM. Since I construct and layout the view in loadView in my AppViewController, how do they get relayouted if the orientation changes. The VC does not have a layoutSubviews, so I should use the didRotateFromInterfaceOrientation and reposition the subviews from there?
BTW, I'm not mixing registering anonymous inner classes as listeners (observers). I'm very experienced with writing Swing components and JavaFX controls. And that probably is the culprit, in Java(FX) every component has a view and a controller (not always a model).

Resources