How to use NSIndexSet - cocoa

In Objective-C, my program opens a window and displays a table. I want to have a specified row of the table highlighted.
How do I do this?
I seem to need the code
[myTableView selectRowIndexes:(NSIndexSet *) byExtendingSelection:(BOOL)];
I looked at the developer documentation, and figured out that the BOOL should be NO.
By looking at the NSIndexSet docs, I can't figure out what the right syntax should be.

it would be the proper way:
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 3)];
or you can use the NSMutableIndexSet for the random indexes:
NSMutableIndexSet *mutableIndexSet = [[NSMutableIndexSet alloc] init];
[mutableIndexSet addIndex:0];
[mutableIndexSet addIndex:2];
[mutableIndexSet addIndex:9];
etc.

Printing out an NSIndexSet in the debugger will show you that they are internally NSRanges. To create one, you can either specify the range or a single explicit index (from which it will create the range); something like
NSIndexSet *indexes = [[NSIndexSet alloc] initWithIndex:rowToHighlight];
[myTableView selectRowIndexes:indexes byExtendingSelection:NO];
[indexes release];
Note that the index(es) must all be unsigned integers (NSUIntegers, specifically).

I'd use a factory method to avoid having to manage memory:
[myTableView selectRowIndexes:[NSIndexSet indexSetWithIndex:indexes]
byExtendingSelection:NO];

I seem to need the code
[myTableView selectRowIndexes:(NSIndexSet *) byExtendingSelection:(BOOL)];
No; those are casts without anything to cast, which is invalid.
Remove the casts and put values there instead.
I looked at the developer documentation, and figured out that the BOOL should be NO.
Yes, because you don't want to extend the selection, you want to replace it.
By looking at the NSIndexSet docs, I can't figure out what the right syntax should be.
The same as for passing any other variable or message expression.
You need to create an index set and then either stash it in a variable and pass that or pass the result of the creation message directly.

Related

NSTreeController - Retrieving selected node

I added Book object in bookController (NSCreeController). Now i want to get stored Book object when i select the row.
- (IBAction)addClicked:(id)sender {
NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
// NSTimeInterval is defined as double
NSUInteger indexArr[] = {0,0};
Book *obj = [[Book alloc] init];
NSString *dateString = [NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterNoStyle timeStyle:NSDateFormatterLongStyle];
obj.title = [NSString stringWithFormat:#"New %#",dateString];
obj.filename = [NSString stringWithFormat:#"%d",arc4random()%100000];
[self.booksController insertObject:obj atArrangedObjectIndexPath:[NSIndexPath indexPathWithIndexes:indexArr length:2]];
}
I concede there perhaps could be a better solution--
I am unfamiliar with how NSTreeController works, but I looked a the class reference and noticed that it has a content property, similar to an NSArrayController (Which I am familiar with grabbing specific objects from).
I believe that if the content property is actually of type of some kind of tree data structure, my answer here probably won't work. The class reference says this about content:
The value of this property can be an array of objects, or a
single root object. The default value is nil. This property is
observable using key-value observing.
So this is what I historically have done with the expected results:
NSString *predicateString = [NSString stringWithFormat:NEVER_TRANSLATE(#"(filename == %#) AND (title == %#)"), #"FILENAME_ARGUMENT_HERE", #"TITLE_ARGUMENT_HERE"];
NSArray *matchingObjects = [[self content] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:predicateString]];
Then simply calling -objectAtIndex: will grab you your object. Note that the NSArray will be empty if the object doesn't exist, and if you have duplicate objects, there will be multiple objects in the array.
I also searched for an answer to your question, and found this SO thread:
Given model object, how to find index path in NSTreeController?
It looks pretty promising if my solution doesn't work, the author just steps through the tree and does an isEqual comparison.
If you could (if it's not too much trouble), leave a comment here to let me know what works for you, I'm actually curious :)

Inverting currently seleted items in NSTableView

At the moment I can invert the currently selected items in an NSTableView with:
- (IBAction) doSelectInvert:(id) sender
{
NSIndexSet *set1=[myTable selectedRowIndexes];
NSMutableIndexSet *set2=[[NSMutableIndexSet alloc] init];
NSUInteger nCount=[myTable numberOfRows];
for(NSUInteger n=0;n<nCount;n++)
if(![set1 containsIndex:n]) [set2 addIndex:n];
[myTable selectRowIndexes:set2 byExtendingSelection:NO];
[set2 release];
}
Is this correct, or is there a more elegant way?
The most important improvement you can make is to adopt ARC. This will make your code more elegant by removing extra retain/release calls, and prevent a whole lot of simple errors. It's not fun to track down an extra release call that only sometimes has detectable symptoms.
Looking at all the NSMutableIndexSet methods, there are some more powerful operators, like -removeIndexes:, that can save you a bit of work, compared to building up your set one-at-a-time. Finding a way to use them sometimes means "inverting" your thinking a bit — for example, removing items from the set of everything, instead of adding items to an empty set.
- (IBAction) doSelectInvert:(id) sender
{
// start with all rows
NSRange allRows = NSMakeRange(0, [myTable numberOfRows]);
NSMutableIndexSet *invertedSelection = [NSMutableIndexSet indexSetWithIndexesInRange:allRows];
//then remove the currently selected rows
[invertedSelection removeIndexes:[myTable selectedRowIndexes]];
//set the new selection
[myTable selectRowIndexes:invertedSelection byExtendingSelection:NO];
}
Swift 5 variant, based on Vincent's answer and with feedback from #vadian:
var selection = IndexSet(integersIn: 0...tableView.numberOfRows)
selection.subtract(tableView.selectedRowIndexes)
tableView.selectRowIndexes(selection, byExtendingSelection: false)
Or if you prefer hard to read one liners:
tableView.selectRowIndexes(IndexSet(integersIn: 0...tableView.numberOfRows).subtracting(tableView.selectedRowIndexes), byExtendingSelection: false)

Sorting NSTableView

I have two coloumn in NSTableView as Name and Salary with 5-10 values. I want to sort these coloumn after click on header of both the column. There is lots of data present in Internet but I am not able to use these. Please help me to do this in cocoa.
Thanks in advance and appreciate any help.
Each table column has a method setSortDescriptorPrototype
Sort descriptors are ways of telling the array how to sort itself (ascending, descending, ignoring case etc.)
Iterate over each of the columns you want as sortable and call this method on each of those columns, and pass the required sort descriptor (In my case I'll be using the column identifier)
for (NSTableColumn *tableColumn in tableView.tableColumns ) {
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:tableColumn.identifier ascending:YES selector:#selector(compare:)];
[tableColumn setSortDescriptorPrototype:sortDescriptor];
}
After writing this piece of initialization code, NSTableViewDataSource has a method - (void)tableView:(NSTableView *)aTableView sortDescriptorsDidChange:(NSArray *)oldDescriptors that notifies you whenever a sort descriptor is changed, implement this method in the data source and send a message to the data array to sort itself
- (void)tableView:(NSTableView *)aTableView sortDescriptorsDidChange:(NSArray *)oldDescriptors
{
self.data = [self.data sortedArrayUsingDescriptors:sortDescriptors];
[aTableView reloadData];
}
This method will get fired each time a column header is clicked, and NSTableColumn shows a nice little triangle showing the sorting order.
I stumbled upon this question while looking for the easiest way of implementing something similar. Although the original question is old, I hope someone finds my answer useful! Please note that I am using Xcode 5.1.1
Ok so to do this you need to:
select the actual column you want to sort in your table.
In your Attributes Inspector you need to fill in two fields: Sort Key, and Selector.
In the Sort Key field, you need to enter the value of your Identifier. The value of your Identifier is located in your Identity Inspector.
In the Selector field you need to enter a suitable selector method based on the object type in the column. The default method is; compare:
Based on the Table View Programming Guide for Mac. The compare: method works with NSString, NSDate, and NSNumber objects. If your table column contains only strings, you may want to consider using the caseInsensitiveCompare: method if case sensitivity is unimportant. However, consider replacing these method signatures with the localizedCompare: or localizedCaseInsensitiveCompare: methods to take into the account the user’s language requirements.
Finally, you need to declare the tableView:sortDescriptorsDidChange: method in your Table View Controller in the format shown below:
-(void)tableView:(NSTableView *)mtableView sortDescriptorsDidChange:(NSArray *)oldDescriptors
{
[listArray sortUsingDescriptors: [mtableView sortDescriptors]];
[tableView reloadData];
}
Just had lately the same issue to get tableView sorted.
My approach :
bind your sortDescriptors to tableview's arrayController
bind tableview's sortDescriptors to Arraycontroller's sort descriptor
perform the settings in attribute inspector (see Tosin's answer above)
Worked perfect for me. No need to set prototypes for columns or something else.
Thanks very much ,It is usefullly for my question.
my code like this
First, set unique values in the XIB interface,like name...
- (void)viewDidLoad {
[super viewDidLoad];
self.itemTableView.dataSource = self;
self.itemTableView.delegate = self;
self.itemTableView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleRegular;
self.itemTableView.usesAlternatingRowBackgroundColors = YES;
for (NSTableColumn *tableColumn in self.itemTableView.tableColumns ) {
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:tableColumn.identifier ascending:NO selector:#selector(compare:)];
[tableColumn setSortDescriptorPrototype:sortDescriptor];
}
}
-(void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray<NSSortDescriptor *> *)oldDescriptors{
NSLog(#"sortDescriptorsDidChange:%#",oldDescriptors);
[self.itemArr sortUsingDescriptors:[tableView sortDescriptors]];
[self.itemTableView reloadData];
}

NSInvocation and ARC (Automatic Reference Counting)

When trying to migrate my current code to ARC, I'm getting errors whenever I pass an NSString as an NSInvocation argument.
Example:
NSInvocation inv = ...;
NSString *one = #"Hello World!";
[inv setArgument:&one atIndex:2];
The error happens when I use the Refactor -> Convert to Objective-C ARC option from the Edit menu. The text is "NSInvocation's setArgument is not safe to be used with an object with ownership other than __unsafe_retained."
How would I get around this?
This might work;
__unsafe_unretained NSString *one = #"Hello World";
As Joshua Weinberg commented, using NSInvocation is not recommended anymore.
If you have up to two parameters you can use performSelector.
For three parameters or more, you can use NSObject's -methodForSelector: as explained here.

NSArray multiply each argument

Is the there a way to multiply each NSNumber contained in the array by 10?
Here is what I have so far:
NSMutableArray *vertex = [NSMutableArray arrayWithCapacity:3];
[vertex addObject:[NSNumber numberWithFloat:1.0]];
[vertex addObject:[NSNumber numberWithFloat:2.0]];
[vertex addObject:[NSNumber numberWithFloat:3.0]];
[vertex makeObjectsPerformSelector:#selector(doSomethingToObject:)];
I am not sure what selector to use to do this, please help!
Not easily, no. You would have to loop through the entire thing and replace all those instances of NSNumber with new instances of NSNumber (NSNumber itself is immutable). So, for example:
for( int i = 0; i < [vertex count]; i++ )
[vertex replaceObjectAtIndex:i withObject:[NSNumber numberWithFloat:[[vertex objectAtIndex:i] floatValue] * 10.0f]];
Obviously this is rather hard to read. You are probably better off just using a regular, primitive array of floats if you are going to be manipulating them often (e.g., applying transformations to them).
Short answer - no.
NSNumber of merely a container for a primitive value. It does not do any mathematical work. The makeObjectsPerformSelector method can be used to tell each object in the array to do something. But the class of each of those objects has to have the method the selector is for. NSNumber also does not provide any method for changing the stored value. So even if you added a category to NSNumber to do the math, you would still have to replace the old value in the array with the newly computed one.
I think a better solution would be to add a category method to NSMutableArray to do the work. It would look through the contents, calculate each new value and then replace each array member with the new one.

Resources