I'm having trouble making the sections in a UITableView. I've looked at the documentation for UILocalizedIndexedCollation as well as this sample code project:
https://developer.apple.com/library/ios/#samplecode/TableViewSuite/Listings/3_SimpleIndexedTableView_Classes_RootViewController_m.html#//apple_ref/doc/uid/DTS40007318-3_SimpleIndexedTableView_Classes_RootViewController_m-DontLinkElementID_18
What I have below is basically a straight copy/paste from the sample project. However, the sample project uses a custom object (TimeZoneWrapper.h) and then places the object in the correct section based on the object's instance variable (TimeZoneWrapper.localeName). However, I'm not using custom objects. I'm using just a bunch of regular NSStrings. So my question is what method on NSString should I pass to the #selector() to compare and place the string in the correct section array?
Currently, I'm calling NSString's copy method as a temporary hack to get things working (which it does), but I'm not sure if this is correct. A little explanation would be much appreciated!
- (void)configureSections {
// Get the current collation and keep a reference to it.
self.collation = [UILocalizedIndexedCollation currentCollation];
NSInteger index, sectionTitlesCount = [[collation sectionTitles] count]; // sectionTitles are A, B, C, etc.
NSMutableArray *newSectionsArray = [[NSMutableArray alloc] initWithCapacity:sectionTitlesCount];
// Set up the sections array: elements are mutable arrays that will contain the locations for that section.
for (index = 0; index < sectionTitlesCount; index++) {
NSMutableArray *array = [[NSMutableArray alloc] init];
[newSectionsArray addObject:array];
}
// Segregate the loctions into the appropriate arrays.
for (NSString *location in locationList) {
// Ask the collation which section number the location belongs in, based on its locale name.
NSInteger sectionNumber = [collation sectionForObject:location collationStringSelector:#selector(/* what do I put here? */)];
// Get the array for the section.
NSMutableArray *sectionLocations = [newSectionsArray objectAtIndex:sectionNumber];
// Add the location to the section.
[sectionLocations addObject:location];
}
// Now that all the data's in place, each section array needs to be sorted.
for (index = 0; index < sectionTitlesCount; index++) {
NSMutableArray *locationsArrayForSection = [newSectionsArray objectAtIndex:index];
// If the table view or its contents were editable, you would make a mutable copy here.
NSArray *sortedLocationsArrayForSection = [collation sortedArrayFromArray:locationsArrayForSection collationStringSelector:#selector(/* what do I put here */)];
// Replace the existing array with the sorted array.
[newSectionsArray replaceObjectAtIndex:index withObject:sortedLocationsArrayForSection];
}
self.sectionsArray = newSectionsArray;
}
Thanks in advance!
You should use #selector(self).
Using #selector(copy) will cause memory leaks in your project
Related
I have custom object and I have stored my values in Array. but I am bit stuck to show objects values form array. My code definition is here.
for (int i = 0; i < 20; i++)
{
Person *myPerson = [[Person alloc] init];
myPerson.name = #"Brian";
myPerson.age = [NSNumber numberWithInteger:23];
[myArray addObject:myPerson];
[myPerson release];
}
Now I want to show all 20 values which is stored in Array (name and age of person).
How will I show that values?
There are many different ways of showing the customers depending on what you want.
1. Print to the console
If you just want to print them out to the console, you can use:
for (int i = 0; i < 20; i++)
{
Person *thisPerson = [myArray objectAtIndex:i];
NSLog(#"%# has an age of %d", thisPerson.name, thisPerson.age);
}
additionally, you can use Fast Enumerators to neaten things up:
for (Person *thisPerson in myArray)
{
NSLog(#"%# has an age of %d", thisPerson.name, thisPerson.age);
}
2. Showing in a table view
You'll need a UITableView with an instance of UITableViewController that conforms to the UITableViewDataSource protocol.
This tutorial gives you an excellent walkthrough:
http://www.icodeblog.com/2008/08/08/iphone-programming-tutorial-populating-uitableview-with-an-nsarray/
If neither of these solutions suits, please provide more information about what you're trying to achieve.
I know I can concatenate a variable name using stringwithformat, but is it possible to concatenate an object name? I'm not having any luck working around this one.
image+1.hidden = YES; for example.
If I wanted to loop through that, say 10 times, how would I create the 'image+1' part?
Thanks for any help.
I don't think that it is possible to concatenate object names in objective c, but you could create an array of images, and then reference each image like
image[0].hidden = YES;
That would fit the for loop. You could also add the images (I assume that they are UIImages) to an NSArray, then loop through like so:
NSArray* arrayOfImages;
for(UIImage* image in arrayOfImages)
{
image.hidden = YES;
}
Add the objects to an NSArray or NSMutableArray. Then loop through the array to set each object's properties.
For the purposes of discussion mainly, you can use key-value coding to set a property by its name. So, supposing you had instance, an instance of a class that provides the properties image1, image2 and image3 then you could perform:
for(int x = 1; x < 4; x++)
{
// produce the name of the property as an NSString
NSString *propertyName = [NSString stringWithFormat:#"image%d", x];
// use key-value coding to set the property
[instance setValue:someValue forKey:propertyName];
}
For the full list of accessor methods that compliant classes export, see the NSKeyValueCoding Protocol Reference. NSObject implements NSKeyValueCoding, and all properties declared as #property and implemented as #synthesize are compliant, as are any other properties with suitable accessors.
As already noted in the other answers, when what you want is an ordered list of objects so that you can do something with each in turn, either a C-style array or an NSArray is the correct way to proceed, with an NSArray being preferred for style reasons.
NSArray *array = [[NSArray alloc] initWithObjects:image1, image2, image3, image4, image5, image6, image7, image8, image9, image10, nil]; // or use NSMutableArray
for (int x = 0; x < 10; x++) {
((UIImage*)[array objectAtIndex:x]).hidden = YES;
}
I'd prefer not to change the way anArray is designed because it is also used elsewhere as a dataSource for a UITableView. The odd number values are "user names", and the even number values are their matching "dates". (The "pairs" must remain together.)
How would I sort "by user name"?
How would I sort "by date"?
Should I be using sortUsingFunction or sortUsingSelector?
-(void) test
{
NSMutableArray *anArray = [NSMutableArray arrayWithObjects:#"Zeke", #"01-Jan-2010", #"Bob", #"02-Jan-2010", #"Fred", #"03-Jan-2010", #"Susan", #"04-Jan-2010", #"Kim", #"05-Jan-2010", #"Debbie", #"06-Jan-2010", nil];
[anArray sortUsingFunction:SortByDate context:(void *)context];
}
NSComparisonResult SortByDate(NSMutableArray *a1, NSMutableArray *a2, void *context)
{
// What goes here to keep the pair-values "together"?
}
Why aren'e you using a NSDictionary? You can have user names as keys and the dates as corresponding values. Then if you want to sort on user names, just use [dictObj allkeys] to get an array containing just the keys. Sort them as you prefer and then when displaying display the sorted array of keys and fetch the corresponding value for the key and display along side. Similarly, you can get all the values using [dictObj allValues] sort them and display them along with keys.
I'd prefer not to change the way anArray is designed because it is also used elsewhere as a dataSource for a UITableView
Does that mean cells alternate between displaying usernames and dates, or are you multiplying/dividing by 2?
Either way, you need to fix your model. The bit where you go "oops, sorting doesn't work" should be a big hint that you haven't chosen a good representation of your data. Adding a workaround will only lead to more tears later.
I'd suggest something like this:
Create a class to represent your (username,date) tuple.
Add -compareByDate: and -compareByUsername: methods.
[array sortUsingSelector:#selector(compareByDate:)]
If you want to display usernames and dates in alternating cells, then it's still easy to do, but I'd simply use a taller cell and save myself some pain.
Note: The overhead of Objective-C method calls means that you can get a significant performance increase by using sortUsingComparator: and defining appropriate functions (you can stick the function between #implmentation and #end to let them access protected/private ivars) but it's not really worth doing this unless you've determined that sorting is a bottleneck.
Something like this could work:
#class SortWrapper: NSObject
#property (copy,readwrite,atomic) NSString* name;
#property (copy,readwrite,atomic) NSDate* date;
#end
...
- (void)test
{
NSArray* names = #[#"Alice", #"Bob", #"Cameron"];
NSArray* dates = #[aliceDOB, bobDOB, cameronDOB]; // NSDate objects
// turn into wrapped objects for sorting
NSMutableArray* wrappedObjects = [NSMutableArray array];
for ( int i=0; i<names.count; i++ ) {
SortWrapper* wrapped = [[SortWrapper alloc] init];
wrapped.name = names[i];
wrapped.date = dates[i];
[wrappedObjects addObject:wrapped];
}
// to sort by date:
NSArray* sortedByDate = [wrappedObjects sortedArrayUsingComparator:^NSComparisonResult(SortWrapper* obj1, SortWrapper* obj2) {
return [obj1.date compare:obj2.date];
}];
// to sort by name:
NSArray* sortedByName = [wrappedObjects sortedArrayUsingComparator:^NSComparisonResult(SortWrapper* obj1, SortWrapper* obj2) {
return [obj1.name compare:obj2.name];
}];
}
I am relatively new to Objective C and need some array help.
I have a plist which contains a Dictionary and an NSNumber Array, with more arrays to
be added later on.
NSMutableDictionary *mainArray = [[NSMutableDictionary alloc]initWithContentsOfFile:filePath];
NSArray *scoresArray = [mainArray objectForKey:#"scores"];
I need to retrieve all the values from the array and connect them to 10 UILabels which
I've set up in interface builder. I've done the following to cast the NSNumber to a String.
NSNumber *numberOne = [scoresArray objectAtIndex:0];
NSUInteger intOne = [numberOne intValue];
NSString *stringOne = [NSString stringWithFormat:#"%d",intOne];
scoreLabel1.text = stringOne;
This seems a very long winded approach, I'd have to repeat the 4 lines above ten times to retrieve all the array values. Could I use a for loop to iterate through the array with all of the values converted to Strings at the output?
Any info would be greatly appreciated.
// create NSMutableArray* of score UILabel items, called "scoreLabels"
NSMutableArray *scoreLabels = [NSMutableArray arrayWithCapacity:10];
[scoreLabels addObject:scoreLabel1];
[scoreLabels addObject:scoreLabel2];
// ...
NSUInteger _index = 0;
for (NSNumber *_number in scoresArray) {
UILabel *_label = [scoreLabels objectAtIndex:_index];
_label.text = [NSString stringWithFormat:#"%d", [_number intValue]];
_index++;
}
EDIT
I'm not sure why you'd want to comment out _index++. I haven't tested this code, so maybe I'm missing something somewhere. But I don't see anything wrong with _index++ — that's a pretty standard way to increment a counter.
As an alternative to creating the scoreLabels array, you could indeed retrieve the tag property of the subviews of the view controller (in this case, UILabel instances that you add a tag value to in Interface Builder).
Assuming that the tag value is predictable — e.g., each UILabel from scoreLabel1 through scoreLabel10 is labeled with a tag equal to the values of _index that we use in the for loop (0 through 9) — then you could reference the UILabel directly:
// no need to create the NSMutableArray* scoreLabels here
NSUInteger _index = 0;
for (NSNumber *_number in scoresArray) {
UILabel *_label = (UILabel *)[self.view viewWithTag:_index];
_label.text = [NSString stringWithFormat:#"%d", [_number intValue]];
_index++;
}
The key to making that work is that the tag value has to be unique for the UILabel and must be something you can reference with -viewWithTag:.
The code above very simply assumes that the tag values are the same as the _index values, but that isn't required. (It also assumes the UILabel instances are subviews of the view controller's view property, which will depend on how you set up your interface in Interface Builder.)
Some people write functions that add 1000 or some other integer that allows you group types of subviews together — UILabel instances get 1000, 1001, and so on, and UIButton instances would get 2000, 2001, etc.
try using stringValue...
scoreLabel1.text = [(NSNumber *)[scoresArray objectAtIndex:0] stringValue];
I have a table view in which I'm using sectionIndexTitlesForTableView to display an index. However, when I scroll the table, the index scrolls with it. This also results in very slow refreshing of the table. Is there something obvious I could be doing wrong? I want the index to remain in place on the right while the table scrolls. This is the code I'm using for the index titles:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
[tempArray addObject:#"A"];
[tempArray addObject:#"B"];
[tempArray addObject:#"C"];
[tempArray addObject:#"D"];
...
return tempArray;
}
You really should be creating the index list somewhere else (say, in your table controller's init or loadView methods) and retaining it as an instance variable for later use. Then in sectionIndexTitlesForTableView you only have to return that ivar. If it isn't a property with a retain attribute then make sure you retain it when created so it sticks around (and release it in dealloc).
An easy way to create it is:
self.alphabetIndex = [NSArray arrayWithArray:
[#"A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|#"
componentsSeparatedByString:#"|"]];
The actual letters would have to change depending on the language locale setting but this way it's a bit easier to localize.
You definitely don't want to be creating that temp array each time because it's going to get called a lot.
As far as the index scrolling away it may be related to your returning a new array each time. Try the above first and if it doesn't solve the problem then you may want to tweak the value for the table's sectionIndexMinimumDisplayRowCount property and see if it makes any difference.
I would avoid creating a new NSMutableArray and releasing it every time. Try creating those on viewDidLoad or the class constructor and just reference the pre-built array on sectionIndexTitesForTableView.
If you are not manipulating the array at all, you probably don't need the overhead of an NSMutableArray at all. Try switching it to a plain old NSArray by using the arrayWithObjects static autorelease constructor.
That should speed things up for you.
Make a static variable, it will be released on app exit.
static NSMutableArray* alphabet = nil;
+ (void)initialize {
if(self == [MyViewController class]){
NSUInteger const length = 'Z' - 'A' + 1;
alphabet = [[NSMutableArray alloc] initWithCapacity:length];
for(NSUInteger i=0; i<length; ++i){
unichar chr = 'A' + i;
[alphabet addObject:[NSString stringWithCharacters:&chr length:1]];
}
}
}