NSComboBox - Obtaining selected information and NSComboBoxDataSource - objective-c

For the life of me, I am being continually stumped with NSComboBox.
I created an object that conforms the NSComboBoxDataSource protocol, and implemented:
- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox;
- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index;
I set the instance of my NSComboBox to use a Data Source, and set this object as the source. That works great, my implementation returns the number of items, and returns an NSString value for an item at specific indices.
Then I decide that I want to do something when something is selected, this is where my problems begin. There is no obvious method to override in the NSComboBoxDataSource protocol to handle the selection of items in the combo box.
So, I also have my object conform to NSComboBoxDelegate and implement:
- (void)comboBoxSelectionDidChange:(NSNotification *)notification;
Unfortunately, unlike NSTableView on selection, the notification's object is the NSComboBox not the object of the item selected. "Fine" I tell myself, I will call the NSComboBox method:
- (id)objectValueOfSelectedItem;
This should return the item that is selected and I can go from there. However, that method is to be called ONLY when usesDataSource is set to NO, which is not my case. Warnings start flying when I use this.
So, my question is, what is the proper way to handle NSComboBox selections when you are using a data source?

I think you want indexOfSelectedItem instead of objectValueOfSelectedItem. Then since you're the data source you should be able to call your own comboBox:objectValueForItemAtIndex: method.

Related

How to set the number of displayed items of NSMenu?

As the title mentioned. I implemented an combo-box-like NSMenu object. But I wonder how to set the number of displayed items. Like the method of NSComboBox: -setNumberOfVisibleItems:
Could any one tell me?
There is no method built in.
You could subclass NSMenu easily and override the addItem... and insertItem... methods to first check numberOfItems and remove or cancel as needed.
Of course, if you are sure you will control that menu you can just do this checking before any coded that adds items.
Of course you could also create a new delegate protocol that inherits from the NSMenuDelegate Protocol while you are at it and then easily have a delegate manage via methods like a shouldAddMenuItem or willAddMenuItem

How do I access the properties of UITextField's selectedTextRange UITextPosition objects?

I'm trying to establish the start and end positions of a text selection of a UITextField instance, using its selectedTextRange property (as gained from the UITextInput protocol). However, I have no idea how to access the properties of the UITextPosition objects that make up the start and end properties of selectedTextRange.
Apple's docs on UITextPosition are woeful at this time, providing no methods or properties, though I know there are such properties in the object, because NSLogging one gives this:
<UITextPositionImpl: 0x6aaeb60>
<<WebVisiblePosition: 0x6aa40e0>(offset=5, context=([s|a], [u+0073|u+0061])>
In this example, the 'offset' is correct, and the context shows the characters either side of the selection point ('s' and 'a'), but I don't know how to access this nebulous WebVisiblePosition class. So, in short, is there a way of retrieving the details I want using UITextPosition objects from UITextField?
Of course, just after asking my question I found the answer, in this SO question: UITextPosition in UITextField.
It seems that when used as part of UITextField, the UITextPosition objects are not meant to be tinkered with directly, but used to feed other methods. In this case, the method offsetFromPosition:toPosition:, along with the text field property beginningOfDocument, can be used to return an NSInteger of a selection index.

How to refresh an NSTableVIew when you populate the NSMutableArray (bind) associated with it

I tried to bind my NSArraycontroller to an NSMutableArray; the array holds objects of type "iData" (it's a custom class). The class further contains some NSMutableString variables which are set as keys for KVC. Then I bound the my NSTableColumn to the NSArrayController and set key model paths of every column respectively.
When I try to populate the array, the GUI does not show anything. What did I forget?
So it's likely that you solved this long ago, but in case someone else stumbles across this question...
I am populating the array via NSMutableArray, but I am not sure how can I populate the array via NSArrayController, since I don't have the instance in my Controller.m class. Please tell me how can I resolve this issue.
It is possible that you were doing something like
[myData addObject:someObject];
However, your NSArrayController will not learn of this change to the NSMutableArray instance because addObject is not KVC compliant. You need to inform any object that is observing that your NSMutableArray instance has changed. There are at least two ways to do this. Assuming that your NSMutableArray instance property is named "myData", then you can do something like the following:
[self.willChangeValueForKey:#"myData"];
[myData addObject:someObject];
[self.didChangeValueForKey:#"myData"];
or
NSMutableArray *bindingsCompliantArray = [self mutableArrayValueForKey:#"myData"];
[bindingsCompliantArray addObject:someObject];
Another SO answer (linked) has a good explanation on what mutableArrayValueForKey actually does, but I recommend reading the Apple developer docs on key-value coding and key-value observation to help understand it.
I have dragged the NSController instance in my mainmenu.nib tray. Do i need to declare an IBOutLet NSArrayController in my Controller.h file and then connect it with the NSArrayController instance in the tray ?
You need a NSArrayController instance in your nib file, but you do not need an IBOutlet in your interface for the situation that you've described here. The NSArrayController should be bound to the key of your NSMutableArray (myData in my example) and it sounds like you already have your table columns bound correctly.
Although Stephen's answer is probably "the way to go", I think the OP's original question "How do I insert/delete/manage my NSMutableArray using the NSArrayController, deserves a simpler and more direct answer:
NSArrayController provides a full and rich set of methods and even IBActions to fulfill almost anything you want on the managed NSMutableArray, with all the niceties of handling things "through filters", through selection, and "keeping sorting rules"
e.g. insert a new item so that it is inserted according to the current sort-descriptions.
Here's an excerpt from these methods (open NSArrayController.h for the full set) and remember that the 'content' is your NSMutableArray, while 'arrangedObjects' is an array provided by the NSArrayController that applies filtering and sorting to the content, "on its way" to the display in the NSTableView.
- (BOOL)addSelectedObjects:(NSArray *)objects;
- (BOOL)removeSelectedObjects:(NSArray *)objects;
- (IBAction)add:(nullable id)sender; // overridden to add a new object to the content objects and to the arranged objects
- (IBAction)remove:(nullable id)sender; // overridden to remove the selected objects
- (IBAction)insert:(nullable id)sender;
- (void)addObject:(id)object; // overridden to add to the content objects and to the arranged objects if all filters currently applied are matched
- (void)addObjects:(NSArray *)objects;
- (void)insertObject:(id)object atArrangedObjectIndex:(NSUInteger)index; // inserts into the content objects and the arranged objects (as specified by index in the arranged objects) - will raise an exception if the object does not match all filters currently applied
- (void)insertObjects:(NSArray *)objects atArrangedObjectIndexes:(NSIndexSet *)indexes;
- (void)removeObjectAtArrangedObjectIndex:(NSUInteger)index; // removes from the content objects and the arranged objects (as specified by index in the arranged objects)
- (void)removeObjectsAtArrangedObjectIndexes:(NSIndexSet *)indexes;
- (void)removeObject:(id)object; // removes from the content objects and the arranged objects (if currently contained)
- (void)removeObjects:(NSArray *)objects;
All this, of course, is for direct programmatic control over the content, and not "automagically" via Cocoa-Bindings.

Cocoa: getting a Table View cell to send action messages

I'm really having trouble getting a Cocoa Table View cell to send action messages.
At the most basic level, in IB there is an action assigned for the NSTextViewCell object, and after editing and pressing Return nothing happens.
So I have an IBOutlet hooked up to the NSTextViewCell, and have been experimenting with NSActionCell messages to it. But the Table View seems to pretty much just ignore them.
I've also tried subclassing NSTextViewCell, but the methods I'm seeing all look like they want to pass values to the object from somewhere, not return a value from inside the object to configure its behavior.
I'm pretty new to programming and Cocoa -- can someone explain each thing that needs to be overridden and how and where to do it?
AFAIK, the cells in an NSTableView won't send action messages out to your application, they're sent to the NSTableView so it can update its data. NSTableView itself tries to be pretty clever and update your data directly, rather than just telling you something changed, so depending on what you're trying to do and what the data source for the table is, you have a few options.
If you're using an NSTableViewDataSource object to populate the table, it's simple; just implement tableView:setObjectValue:forTableColumn:row: and the NSTableView will call that every time something is edited.
If you're using Cocoa data binding (for example, using an NSArrayController to bind an array of objects to the table,) then as long as everything is wired up correctly, the data should just automagically get updated in the source objects when the table is edited. If you need to take special action, then you can do whatever you need to in the property setter of your data class.
I haven't tried it yet, but could work...
NSCell *cellYouWant = [tableView preparedCellAtColumn:tableView.clickedColumn row:tableView.clickedRow];

When does selectedCell change?

I have an NSTableView and I want to do something whenever the selectedCell element changes.
So, my table view is called tableView, and this is what I want to observe:
[tableView selectedCell]
I tried using key-value observing, but that didn't seem to work, or maybe I was doing it wrong. Any ideas?
Most properties of Cocoa's own classes are not observable. If a property is observable, the documentation for it will explicitly say so; if the documentation doesn't say a property is observable, assume it isn't.
Furthermore, properties that don't exist are doubly not observable. The documentation for NSTableView and NSOutlineView both mention no method named “selectedCell”. You should assume there isn't one.
If you want to know when the user selects a different row, be the table view's delegate; it sends delegate messages for that, if you'll respond to them.
NSTableView will use one and only one dataCell object for each column. selectedCell is the wrong way. You can use selectedColumn to get the selected column and then ask for its dataCell.
And: I guess you are searching for NSTableView delegate methods
tableViewSelectionDidChange: and tableViewSelectionIsChanging: