Sort items in a UITableView by date - iphone

So in my app, I have a UITableView, and in the navigation controller there is a button to add a new item. This than takes you to an add an item screen and when you finish, i use a custom delegate to pass the information from the detail viewController to the main tableViewController. Now that I have the info from delegate, I take the 3 pieces of info and put them in an NSArray in my main Array. Now my question is, how can I sort the detail items by date. I have tried (unsuccessfully) 3 times now and I can't seem to figure it out. The arrays and dictionarys get confusing because they are nested. Any help is greatly appreciated.
The Delegate Method in the tableViewController class:
-(void)finishedAddingFoodItemFromDetail:(NSDate *)date whatWasEaten:(NSString *)whatFood whichMeal:(NSString *)meal{
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMMM d, yyyy"];
NSString *dateString = [[NSMutableString alloc] init];
dateString = [dateFormatter stringFromDate:date];
if (!self.theArray) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"theArray"]) {
self.theArray = [defaults objectForKey:#"theArray"];
}
else{
self.theArray = [[NSMutableArray alloc] init];
}
}
[self.theArray addObject:[NSArray arrayWithObjects:dateString, meal, whatFood, nil]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:self.theArray forKey:#"theArray"];
[self.tableView reloadData];
[self dismissModalViewControllerAnimated:YES];
}
This is the file higharchy I was thinking of, but I can't seem to implement it.
/* file higharchy
* - theBigDict //large dictionary holding everything
* - MutableArray by day stored as objects of theBigDict
* - Array of the meals info:
* - Meal type
* - Food
*/

There are several methods on NSMutableArray you can call to sort its contents:
– sortUsingDescriptors:
– sortUsingComparator:
– sortWithOptions:usingComparator:
– sortUsingFunction:context:
– sortUsingSelector:
Since you have an array of complex objects, you will need to write your own function or comparator and call the appropriate sortUsingXxx: method. Do this before you store the array back into NSUserDefaults and before calling reloadData: on your table.
Does this help?
EDIT: Your custom function or comparator will have to pull out the date from the two array elements being compared (passed into your function) and return the appropriate comparison results, e.g. NSOrderedAscending, etc.

Related

How can I pass a "MutableArray with full of Objects" to another class by using NSUserDefaults?

How can I pass a "MutableArray with full of Objects" to another class by using NSUserDefaults? I know how to pass "MutableArray"s but this does not work!
So;
I have a MutableArray; 'myCityObjects', and I populate it with objects; 'cities'
In each 'cities' object there are properties like cityName, cityLocation etc...
[myCityObjects addObject:cities];
Now, what I want to do is to pass this MutableArray (filled with objects) to another class by using 'NSUserDefaults';
[[NSUserDefaults standardUserDefaults] setObject: myCityObjects forKey:#"MCO"];
And in the other class,
NSMutableArray *getMyCityObjects = [[NSArray alloc] init];
getMyCityObjects = [[NSUserDefaults standardUserDefaults] mutableArrayValueForKey:#"MCO"];
But it doesn't work! I cannot get myCityObjects in the other class, "getMyCityObjects" is empty. How can I fix that?
Thanks,
E.
NSUserDefaults always returns immutable objects, even if the original object was mutable.
In your first View, You can save value in NSUserDefaults like this:
NSMutableArray *arr= [NSMutableArray arrayWithObjects:#"asd",#"dsa",nil];
[[NSUserDefaults standardUserDefaults] setObject:arr forKey:#"MCO"];
After this in another view, you can retrieve value from NSUserDefaults in this way.
NSMutableArray *abc = [NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"MCO"]];
Your array is nil because the objects in it (your custom objects) can't be serialised.
Please take a look at the NSCoding protocol. Objects you want to serialise (eg for writing to NSUserDefaults) must implement the methods -encodeWithCoder: and -initWithCoder.
I'm sure you'll find how this is rather easily done searching for the terms I gave you...
I have run into this problem before. The problem with the NSUserDefaults is that it can only contain strings, numbers, and booleans, and arrays or dictionaries of those types of values. My solution is to get around that by storing all the properties in NSDictionaries.
Create two class functions on your "cities" class (I'm calling it CityClass):
+(NSDictionary *)dictionaryFromCity:(CityClass *)myCity {
NSDictionary *returnDict = #{#"keyForIntProperty" : myCity.intProperty, #"keyForFloatProperty" : myCity.floatProperty, #"keyForNSStringProperty", myCity.NSStringProperty"};
return returnDict;
}
+(CityClass *)cityFromDictionary:(NSDictionary *)myDict {
CityClass *returnCity = [[CityClass alloc] init];
returnCity.intProperty = [[myDict objectForKey:#"keyForIntProperty"] intValue];
returnCity.floatProperty = [[myDict objectForKey:#"keyForFloatProperty"] floatValue];
returnCity.NSStringProperty = [myDict objectForKey:#"keyForNSStringProperty"];
//any other setup for the CityClass
return returnCity;
}
Now you can store and retrieve your objects without a problem using the new functions:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
//store a CityClass object
NSDictionary *storageDict = [CityClass dictionaryFromCity:cityToStore];
[defaults setObject:storageDict forKey:#"keyForCity"];
//retrieve a CityClass object
NSDictionary *retrieveDict = [defaults objectForKey:#"keyForCity"];
CityClass *retrievedCity = [CityClass cityFromDictionary:retrieveDict];
What you can do here is create a Constructor in your other class for e.g.
-(void)initWithArrayOfObject:(NSMutableArray *)arr_OfObjects;
{
arr_SecondClassArrayOfObjects = [[NSMutableArray alloc]initWithArray:arr_OfObjects];
}
From your first class send this array as :
[objOfSecondClass initWithArrayOfObject: myCityObjects];

NSUserDefaults display date and data in the tableView

In the FirstViewController of my survey app I have NSMutableArray that contains data (strings). I've also created date varibale. Here's code
NSDateFormatter *dateformater=[[NSDateFormatter alloc] init];
[dateformater setDateFormat:#"EEEE,dd MMMM yyyy HH:mm a"];
NSDate *today=[NSDate date];
NSString *currentDate=[dateformater stringFromDate:today];
Now I want with the help of NSUserDefaults save data from the array and also save date variable when the survey has been taken and display it in the SecondViewController's tableview (first user can see date of the survey and then by tapping the date - data from array).
I know how NSUSerDefaults work but I don't know how to put array by date variable to be shown in the SecondViewController. Can anyone help me on that?
In AppDelegate.h file just declare variable...
NSUserDefaults *userDefaults;
NSMutableArray *arrDate;
after...
In AppDelegate.m Fille in applicationDidFinishLonching: Method
userDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingtblArrayForSearch = [userDefaults objectForKey:#"arrDate"];
if (dataRepresentingtblArrayForSearch != nil) {
NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingtblArrayForSearch];
if (oldSavedArray != nil)
arrDate = [[NSMutableArray alloc] initWithArray:oldSavedArray];
else
arrDate = [[NSMutableArray alloc] init];
} else {
arrDate = [[NSMutableArray alloc] init];
}
[arrDate retain];
after that when you want to insert,update or delete Data from this UserDefaults Use Bellow Code...
for Delete record from array
[appDelegate.arrDate removeObjectAtIndex:Index];/// give the integer value instead of Index
and this is for add record in array..
[appDelegate.arrDate addObject:currentDate];///add value or add date here
after in final save this modification in array just archive like bellow..
NSData *data=[NSKeyedArchiver archivedDataWithRootObject:appDelegate.arrDate];
[appDelegate.userDefaults setObject:data forKey:#"arrDate"];
[appDelegate.userDefaults synchronize];
Create a NSDictionary with the date and data object:
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:[NSString stringWithFormat:#"%#",currentDate], #"date", #"yourData", #"data", nil];
Then put it in the NSUserDefault using:
[NSUserDefaults standardUserDefaults] setObject:yourDictionary forKey:#"item1"];
This will store a single data to NSUserDefault.
You can retrieve the object by using:
NSDictionary * dictionary = [[NSUserDefaults standardUserDefaults] dictionaryForKey:#"item1"];
Now you will get the date using [dictionary objectForKey:#"date"]; and data using : [dictionary objectForKey:#"data"];

Adding NSString to NSMutableArray substitutes the previous NSString

I'm basically trying to add a new string to my NSMutableArray using this code in ViewWillAppear
NSUserDefaults *favBooksDefaults = [NSUserDefaults standardUserDefaults];
self.favBook = [favBooksDefaults stringForKey:#"favBook"];
self.favBookList = [[NSMutableArray alloc]init];
[self.favBookList addObject:favBook];
Of course, while I do this, I want to preserve ALL the previous strings that were present in the Array. Because when I add a new string for the NSUserDefaults and then to the Array, it simply substitutes the old one.
Why is that and how can I save all the objects?
Thanks in advance.
Mr Br.'s answer is correct:
In your header file declare favBookList as a property and include a book adding method:
#interface yourViewController : UIViewController {
NSMutableArray *favBookList;
}
-(void)addBook;
#property (nonatomic, retain) NSMutableArray *favBookList;
In your viewDidLoad method, initialize favBookList. Don't forget #synthesize!
-(void)viewDidLoad {
self.favBookList = [[NSMutableArray alloc] init];
[super viewDidLoad];
}
Now you are free to add a book from your user defaults at any time.
-(void)addBook{
self.favBook = [[NSUserDefaults standardUserDefaults] stringForKey:#"favBook"];
[favBookList addObject:favBook];
}
When you alloc and init a new NSMutableArray object you will have an empty array with no values in it. It doesn't substitute it as there is nothing to substitute in there. It just adds the NSSString as the first value. This of course happens EVERY time you alloc/init a new instance of NSMutableArray.
SOLUTION: Make the NSMutableArray a instance variable of your view controller. alloc/init it once (e.g. in the viewDidLoad). Every time viewWillAppear gets called you can add values without reinitializing a new NSMutableArray.
NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:oldArray];
[newArray replaceObjectAtIndex:oldObjectIndex withObject:newObject] // if you want to replace oldObject with newObject;
[newArray addObject:newObject] // if you want to add object to the array;
If i get it right you want to replace the old object with new one?
NSUserDefaults *favBooksDefaults = [NSUserDefaults standardUserDefaults];
self.favBook = [favBooksDefaults stringForKey:#"favBook"];
if ([self.favBookList indexOfObject:self.favBook] != NSNotFound) {
[self.favBookList addObject:favBook];
}
You should also init your favBookList in viewDidLoad method:
self.favBookList = [NSMutableArray array];

Pre-populating a NSUserDefaults array with Zero Value values

I need to pre-populate and save an array in NSUserDefaults so that downstream methods can read and write to ten values stored there. I've constructed this workable solution, but is there a better way of doing this?
Any insight is appreciated!
lq
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *myArray = [[NSMutableArray alloc] init];
// Set the array with ten Zero Value placeholders
for (NSUInteger i = 0; i < 10; ++i) {
[myArray addObject:[NSNumber numberWithInt:0]];
}
[userDefaults setObject:myArray forKey:#"someKeyName"];
[myArray release];
Later methods call this array like this:
- (void)doSomethingUseful {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *someUsefulArray = [[NSMutableArray alloc] initWithArray:[userDefaults objectForKey:#"someKeyName"]];
// read some values, write some values: int someInt = [someUsefulArray objectAtIndex:3]; // etc.
// store array values back to NSUserDefaults . . .
// IS THERE A WAY TO READ AND WRITE DIRECTLY TO INDEX 3 of the NSUserDefaults array instead???
[someUsefulArray release]
}
I've actually done the same thing in a shipping application. Sure, it doesn't feel elegant, but it does the job.
The only more elegant, and more convoluted, solution would be to use a data-driven approach:
Have a .plist file containing what you consider to be your default settings.
If the program detects that the user defaults is empty, it will load this default plist, and commit it to NSUserDefaults.
Using this method your code is not responsible for building the objects. However, if you are trying to accomplish a schema-upgrade, you're going to need to go back to the code.

NSUserDefaults won't save NSDictionary

I'm writing an application which uses NSUserDefaults as the data storage mechanism, and am hitting a problem when trying to save data (that conforms to the Property List protocols):
+ (BOOL)storeAlbum:(Album *)album
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *albums = (NSMutableDictionary *)[prefs objectForKey:#"my_adventure_book_albums"];
NSLog(#"Existing albums: %#",albums);
if (!albums)
albums = [NSMutableDictionary dictionaryWithObject:album forKey:#"album"];
else
[albums setObject:album forKey:#"album"];
NSLog(#"%#",album);
[prefs setObject:albums forKey:#"my_adventure_book_albums"];
return [prefs synchronize];
}
I get this output:
2010-06-29 17:17:09.929 MyAdventureBook[39892:207] Existing albums: (null)
2010-06-29 17:17:09.930 MyAdventureBook[39892:207] test
2010-06-29 17:17:09.931 MyAdventureBook[39892:207] *** -[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '{
album = test;
}' of class 'NSCFDictionary'.
The description method of Album looks like:
- (NSString *)description
{
// Convert to a NSDictionary for serializing
if (!title) title = #"";
if (!date) date = [NSDate dateWithTimeIntervalSinceNow:0];
if (!coverImage) coverImage = #"";
if (!images) images = [[NSArray alloc] initWithObjects:#"",nil];
//NSDictionary *dict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:title,date,coverImage,images,nil] forKeys:[NSArray arrayWithObjects:#"title",#"date",#"coverImage",#"images",nil]];
//NSDictionary *dict = [NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:title,nil] forKeys:[NSArray arrayWithObjects:#"title",nil]];
//return [dict description];
return #"test";
}
All of the commented-out lines have the same result, so I just decided to see if the NSString "test" would work, which it (of course) doesn't.
But the object you put inside the dictionary, an Album* is most likely not a property list object, is it? Every object, all the way down, needs to be a property list object for this to work. A description method isn't good enough to make this happen.
As a workaround, you can use NSCoding and an NSKeyedArchiver to write out your dictionary to an NSData, which you can store among the preferences.
You can only put basic foundation types into a property list. NSUserDefaults writes preferences out as a property list. See here for property list allowed types. In a nutshell, it is numbers, strings, data, dates, and arrays and dictionaries of those. Dictionaries must have string keys.
NSUserDefaults always returns immutable objects, so you can't just cast them to mutable. Do [prefs objectForKey:#"my_adventure_book_albums"] mutableCopy] (and remember to release it when finished).