I have a NSMutableArray *objectsArray; containing dictionaries, each NSDictionary have a "Name" string with the objects name as value (no surprise). UITableView is already sorted using NSSortDescriptor by name in ascending order.
Now I need to section the TableView using the name's first letter, but I cannot find the needed code for:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
/*Return: How many letters from a-z are represented
as the first letter of the objects name?
I.e. If there is no object in the dictionary with name starting with letter B,
the number of sections are now 25 if all the other letters are included.*/
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//Return: How many objects are starting with each letter
So I need to know how to return the correct number of section and rows?
Ps. Is there anything else that I have to do to make this it work, like in cell creation process cellForRowAtIndexPath or when passing the objectsArray through the segue to the DetailViewController?
Additional info about app structure:
objectsArray is from a plist array with dictionaries. using a custom prototype cell to populate the Table View. Xcode 4.2 with storyboard. App for iPhone 5.1
Create a NSMutableSet before you sort. Have your comparator store, in your set, the first letter of each Name it encounters as it sorts. Then the number of sections is the size of your set. This way, you pay very little cost since your comparators are already having to iterate through the array anyway.
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 am new to Xcode and i have created a NSmutableArray that retrieves its values from a function. i would like to able to re organise the contents of my Array
When i output my array, i get the following results
(value A,value B,value C,value D)
I would like to re organize my array to appear a customised way so i would like it to appear in the following way
(value B,value D,value A,value C)
Any ideas on how to acheve this ?
I am using xcode 4.6.2
User can see the custom order and sorted order. right?
Have a NSMutableArray. Organise the content and Iterate to show it in UITableview
When you want to show sorted content in UITableView use the below method to get the sorted array.
-(NSMutableArray *)mySortedArray
{
return [myArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
}
As my first app I am trying to master simple Navigation Based numismatic collection app.
There's some trouble with indexes in my array filled from .plist. Plist has 191 records; the ordinary list of countries -> item 0 - String - Albania ... item 190 - String - Vanuatu.
I read these records into my temporary array to populate UITableView.
NSString *countriesFile=[[NSBundle mainBundle]pathForResource:#"countries" ofType:#"plist"];
countries=[[NSArray alloc]initWithContentsOfFile:countriesFile];
This table view has sections -> continents.
Well, my problem is:
When pushing to another nib, it should read the name of country and show it in header and label. So it does, but only for european countries. If I tap some other continent's country, I see some european country. Please see screenshots
Tapping on european country -> Belgium, shows right.
Moving to african section:
Taping on Angola and getting Austria.
So, Angola's index - 92. Austria - 1. But Austria and Angola are both second cell in their native section.
Where's the problem and what would it be?
Thanks in advance.
I am assuming you are using -(void)tableView: (UITableView*)tableView didSelectRowAtIndexPath: (NSIndexPath*)indexPath to tell when you have clicked on an item in the list?
Also, are you using [indexPath row] (or indexPath.row) to figure out the index of the row you tapped on?
What the row property returns is the row index within the current section. It does NOT return the absolute row index for the entire table. Because of this, it does not matter if you click on the index two item in the first section or the index two item in the second section, row will return 2 in both of those cases.
An idea would be to change the .plist file a bit. Instead of it only having one long array of countries, have a parent array that represents the continents. Then the object for each index in this continent array, have another array that will hold the list of countries in there.
-(void)tableView: (UITableView*)tableView didSelectRowAtIndexPath: (NSIndexPath*)indexPath
{
NSUInteger section=[indexPath section];
NSUInteger row=[indexPath row];
NSString* countryName=[[plistArray objectAtIndex: section] objectAtIndex: row];
//rest of code...
}
It sounds like you are not taking into account the section value of indexPath. An indexPath has two parts, a section and a row. In tables that have a single section you usually just read the row value. In your case it looks like you have a grouped table so you need to read both indexPath.section and indexPath.row for the data location.
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.