This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Pass two integers as one integer
Will this work in Objective-C?
Pass two integers as one integer
If so, how do I do it with NSInteger?
I'm asking because I want to calculate a unique NSInteger tag from the NSUIntegers row & section of a UITableView?
See, I'm dealing with a UITableViewController that has three sections, which each have multiple rows. Every row has a UISwitch, and each UISwitch is linked to the same target-action method, switchAction:.
In switchAction:, my plan is to inspect the sender's tag to figure out the UISwitch's NSIndexPath (section & row) of the UITableView.
So, I want two methods like:
+ (NSInteger)integerFromInteger1:(NSInteger)int1 integer2:(NSInteger)int2;
+ (NSIndexPath *)indexPathFromInteger:(NSInteger)integer;
The first method may work better written in C. That works too if you prefer.
Rather than messing around with bit-shifting, try this:
First, find the UITableViewCell containing the UISwitch. If you have a custom UITableViewCell subclass, just direct the UISwitch's target/action to a method on the cell that contains it. If you are using a stock UITableViewCell, you could find the UITableViewCell containing the UISwitch by calling superview in a loop.
Once you have the UITableViewCell, call a method on your view controller (or whatever has access to the UITableView) and you can call UITableView's indexPathForCell: method to get an NSIndexPath object with the section and row.
According to How to convert An NSInteger to an int?, NSInteger will always be at least 32 bits on every system/architecture, so yes, the answers to Pass two integers as one integer will work.
Based on #benzado's answer, I came up with a beautiful solution for how to get the index path of the UISwitch that sent the switchAction: message.
- (void)switchAction:(id)sender {
UISwitch *onOff = (UISwitch *)sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:
(UITableViewCell *)[onOff superview]];
// carry on...
}
No tags necessary. Just keep your pants on.
Related
I'm seeking to implement a UITableView that has sections representing the recent history and future queue of a media player. It seems to me that a Queue-type structure would be most applicable for this, as this represents the nature of the operation, but the requirement that the future queue part be editable poses some challenges.
I think that a linked list would be the best option here to store the media representations, as opposed to a vector type structure where all of the elements are stored contiguously. It seems to me that, in the case of moving or removing an object within the queue and adding object at the end, a linked list is more efficient than a vector, as simply assigning a few pointers different values seems more lightweight than moving entire chunks of memory. The internal implementation details of NSMutableArray seem quite obscure but I'm assuming it's a vector type.
However, I've never really seen a true linked-list implementation in Objective-C. Furthermore, the structure of UITableViewDataSource, requiring cellForRowAtIndexPath: to be called with a specific row rather than simply iterating through the list, exposes the weakness of linked list implementations, as seeking a specific index can be expensive. This would be irrelevant if cellForRowAtIndexPath: is only called in order, but it seems reckless to ignore the indexPath parameter in favor of just iterating through the list.
How are these structures typically implemented? Is iterating through a link list as proposed a bad idea?
Since Objective-C doesn't have an explicitly defined "linked list" object type, the closest alternative would be NSMutableArray.
You should never assume the order in which
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath;
will be called, and if you implement the UITableView correctly, it should only be called for the index paths of cells right before they appear on screen (thus the order it's called would alter based on the direction the user is scrolling in).
I recommend that you create a 2-dimensional NSMutableArray with the 1st representing the sections in the table and the 2nd representing the rows in each section. You would then initialize the array using something like:
self.sectionsArray = [NSMutableArray array];
[self.sectionsArray addObject:[NSMutableArray array]]; // history rows
[self.sectionsArray addObject:[NSMutableArray array]]; // queued rows
Which would allow you to easily retrieve the stored items using something along the lines of:
- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger section = indexPath.section;
NSUInteger row = indexPath.row;
id fetchedObject = self.sectionsArray[section][row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellReuseID"];
// perform setup here based on the fetchedObject
return cell;
}
I've listed some NSMutableArray methods you may find helpful below:
- (void)addObject:(ObjectType)anObject;
- (void)insertObject:(ObjectType)anObject atIndex:(NSUInteger)index;
- (void)removeObject:(ObjectType)anObject atIndex:(NSUInteger)index;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(ObjectType)anObject;
- (void)removeLastObject;
I want to get the number of rows in an NSIndexPath section. For all my searching here & in Apple's docs I can't find how to get this value. Any clues on it?
My specific purpose is to use that to determine whether or not the selected row is the last row in the section so if anyone has a suggestion for determining that by another means that would be as good.
Everything is in docs
numberOfRowsInSection:
int rows = [table numberOfRowsInSection:indexPath.section];
The only way is to ask the data source of the table:
NSUInteger rowCount = [tableView.dataSource tableView:tableView numberOfRowsInSection:indexPath.section];
Here's how I do it in my case:
The table section holds a list of objects stored in NSArray, hence the amount of rows in section is the amount of objects in the array. In your case - if row is not the object from array - just do the check
if ([indexPath row] + 1 == [_objectsArray count]) {
...
But if your items in table are not held in a collection, you can easily retrieve number of rows in section just by calling appropriate method on tableView object in your didSelectRow.. delegate method:
NSInteger numberOfRows = [tableView numberOfRowsInSection:[indexPath section]];
You can call tableView:numberOfRowsInSection: method of the UITableViewDataSource, passing the section number. However, it is your own code that produces this number based on what's in the model, so it may make sense to look at the implementation of tableView:numberOfRowsInSection: in your data source to see if you could get the same answer through an alternative path.
If you are talking about on a UITableView, you can ask it's dataSource, which is somewhat likely to be yourself :-)
[self.tableView.dataSource tableView: self.tableView numberOfRowsInSection: indexPath.section];
// or, if you are the dataSource...
[self tableView: self.tableView numberOfRowsInSection: indexPath.section];
I have situation where I use view based Table Views and don't want to use bindings between data source and table view. This is mainly due to the fact that my NSTableCellView can have multiple subviews, complex validation and triggered calls to methods in other objects.
We have very clear path of updating NSTableView with data source with:
tableView:viewForTableColumn:row:
However, for backwards, that is updating datasource with updates in NSTableView we have nothing of the sort we have for cell based Table views:
tableView:setObjectValue:forTableColumn:row:
Target Action pattern is suggested instead. So, I have basically 2 questions:
If I set target and action for one specific view, or its subview, how do I get proper row and column info to know what to update in data source?
Should clickedRow and clickedColumn from NSTableView do the trick, although I have edited or changed one subview object?
How can I inform the target (as other object, not NSTableView instance) about row and column if action will pass for example NSTextField as parameter?
I can basically come to clickedColumn and clickedRow (if those 2 properties are proper answer to first question) through subview tree, but I find this as pretty much non-elegant solution and have hunch there is a better way....
THanks in advance....
NSTableCellView has an objectValue. Presumably you're already setting it, so the action can use [(NSTableCellView *)[sender superview] objectValue] to find out which object it needs to manipulate.
I suggest that you also subclass NSTableCellView and implement the action there. If you need access to other parts of the model, you can add an outlet for your view controller.
If you really need the row number, you can call indexOfObject on your content array.
The two NSTableView Methods rowForView and columnForView should do the trick.
You can call them with the sender of an Target/Action Method, like one triggered by an NSButton in your TableView (its ok, to have it somewhere in a subwiew)
Or you can call these Methods from within a delegate method implementation like the textDidChange from NSTextDelegate. So you can easily update your corresponding Array.
If you don't want continous updates textDidEndEditing would also do the job.
- (void)textDidChange:(NSNotification *)notification
{
NSTextView *tv = [notification object];
int r = [tableView rowForView:tv];
int c = [tableView columnForView:tv];
NSLog(#"Row: %d Column: %d", r, c);
// updating code here
}
I've been attempting this for two days, and constantly running into dead ends.
I've been through Aaron Hillegass's Cocoa Programming for MAC OS X, and done all the relevant exercises dealing with NSTableview and mutable arrays, and I have been attempting to modify them to suit my needs.
However none of them seem to be using an array with objects as a data source, it seems to use the tableview as the datasource.
I'm trying to implement Jonas Jongejan's "reworking" of my code here, with a Cocoa front end to display the results.
Any pointers or suggestions I know this should be simple, but I'm lost in the wilderness here.
I can populate the table by setting the array
It's pretty simple really, once you get to understand it (of course!). You can't use an NSArray directly as a table source. You need to either create a custom object that implements NSTableViewDataSource or implement that protocol in some existing class - usually a controller. If you use Xcode to create a standard document based application, the document controller class - (it will be called MyDocument) is a good class to use.
You need to implement at least these two methods:
– numberOfRowsInTableView:
– tableView:objectValueForTableColumn:row:
If you have a mutable array whose values you'd like to use in a table view with one column, something like the following should do as a start:
– numberOfRowsInTableView: (NSTableView*) aTableView
{
return [myMutableArray count];
}
– tableView: (NSTableView*) aTableView objectValueForTableColumn: (NSTableColumn *)aTableColum row: (NSInteger)rowIndex
{
return [myMutableArray objectAtIndex: rowIndex];
}
It has just occurred to me that you could add the above two methods as a category to NSArray replacing myMutableArray with self and then you can use an array as a data source.
Anyway, with a mutable array, it is important that any time you change it, you need to let the table view know it has been changed, so you need to send the table view -reloadData.
If your table view has more than one column and you want to populate it with properties of objects in your array, there's a trick you can do to make it easier for yourself. Let's say the objects in your array are instances of a class called Person with two methods defined:
-(NSString*) givenName;
-(NSString*) familyName;
and you want your table view to have a column for each of those, you can set the identifier property of each column to the name of the property in Person that that column displays and use something like the following:
– tableView: (NSTableView*) aTableView objectValueForTableColumn: (NSTableColumn *)aTableColum row: (NSInteger)rowIndex
{
Person* item = [myMutableArray objectAtIndex: rowIndex];
return [item valueForKey: [tableColumn identifier]];
}
If you replace valueForKey: with valueForKeyPath: and your Person class also has the following methods:
-(Person*) mother;
-(Person*) father;
-(NSString*) fullName; // concatenation of given name and family name
you can add table columns with identifiers like: father.fullName or mother.familyName and the values will be automatically populated.
You could go the datasource route and do all of the heavy lifting yourself, or you could let bindings do all the heavy lifting for you. Add an NSArrayController to the nib file that has the table view in it. Make sure that the File's Owner of the nib is set to the same class that has the mutable array in it. Bind the contentArray of the array controller to File's Owner.myMutableArray. For each column bind Value to the array controller arrangedObjects and add the appropriate key path. This will allow you to get things like user sorting for free if you ever need it.
On the iPhone (I know you're talking about Mac, but maybe this could help) you have to use delegation for loading a tableView. It asks for a cell and you use your array to fill-in the data where needed.
I'm not sure if this works for the Mac, but it'd be worth looking into.
Maybe set dataSource to self and use those delegate methods to access your array based on the row and column #
Apple has a whole guide for Table View Programming so I suggest you start with the Using a Table Data Source section of the that guide.
I have a problem with sorting NSTableColumn contents. In my NSTableView there are three columns: File, Size, Path. The contents are stored in NSMutableArray. Each object in this array is a NSDictionary containing three keys: file, size and path - value for each is a NSString.
In Interface Builder, in each Table Column's attributes I can choose sorting options:
Selector: IB entered "compare:" which I think is ok, because I compare NSStrings.
Sort Key - and that's the problem I think - I don't know what to enter here.
Any clues? If you've got questions about my code, please ask.
So I found the complete solution.
First, go to Interface Builder. Select column that you want to sort. Go to the column's inspector and choose the first button to see the "Table Column Attributes". Set appropriate values (literally, no " or ' or # are needed):
Sort key: file
where 'file' is the key of dictionary that contents is shown in your column.
Selector: compare:
standard sort function.
Now, save all the changes here and jump to Xcode, to the class in which is the model, source of the data shown in NSTableView. You should already know that you need two methods there:
-(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
-(id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn
*)tableColumn row:(NSInteger)row
these two are needed to conform the NSTableDataSource informal protocol. You can read about it at the MacDev Center.
Now all you have to do is to add a new method:
-(void)tableView:(NSTableView *)tableView sortDescriptorsDidChange: (NSArray *)oldDescriptors
it can contain a very simple code that will do the thing:
-(void)tableView:(NSTableView *)tableView sortDescriptorsDidChange: (NSArray *)oldDescriptors
{
NSArray *newDescriptors = [tableView sortDescriptors];
[results sortUsingDescriptors:newDescriptors];
//"results" is my NSMutableArray which is set to be the data source for the NSTableView object.
[tableView reloadData];
}
And that's all. It's working in my app, hope it will work in your case :D Everything should work automatically. I've just saved all files and Built the app. And it worked. :)
Site that helped me:
CocoaDev: SortingNSTableView
Key is the key that you use in dictionary to retrieve the value.
In your case you have three keys: file, size and path. Select the one on which you want to sort. The key is used to retrieve value from each record to be used for sorting.
If your keys are #"file", #"size", #"path" and you want to sort on file then try to put value.file into the Sort Key field in IB.
Use keys that you use when inserting values into your NSDictionary.