Store UITableView Information in Single NSMutableArray - iphone

I am trying to store all of the items scrolled past in my UITableView in an NSMutableArray. In my cellForRowAtIndexPath method I include the code below. All of the objects (Id, currentTime, et...) are NSStrings.
I am trying to compile all of them into a single array, but I don't think I am doing this correctly. I think I might be overwriting the dictionary in every single cellForRowAtIndexPath.
Any help would be great. At the end, I just want a single array that allows me to look up various aspects of the "rows/cells" that were scrolled past...
In my .m file:
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:Id forKey:#"key1"];
[dict setObject:currentTime forKey:#"key2"];
[dict setObject:textForMyLabel forKey:#"key3"];
[dict setObject:placeLatitude forKey:#"key4"];
[dict setObject:placeLongitude forKey:#"key5"];
[scrolledPast addObject:dict];
NSLog(#"array: %#", dict);
In my .h file:
#interface viewController {
NSMutableArray *scrolledPast;
}
#property (nonatomic, retain) NSMutableArray *scrolledPast;

You need to initialize your scrolledPast.
Do this in your viewDidLoad:
- (void) viewDidLoad {
// .....
scrolledPast = [NSMutableArray array];
}

Related

ios - How to initialize an array with names? [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I'm new to iPhone development,I want to initialize strings in an array like the given format
-(IBAction)btnSubmit:(UIButton *)sender
{
NSString *phone = phoneNumber.text;
NSLog(#"Phone :%#",phone);
NSString *TxtUserName = userName.text;
NSString *TxtEmailId = emailId.text;
NSArray *details = [NSArray arrayWithObjects:TxtUserName,TxtEmailId,phone,nil];
}
I'm getting details :(
"xxxxxxx",
"xxxxx#gmail.com",
7675
)
I want to get details :("Name=xxxx","Email=xxxx#gmail.com","Phone=8786")
Any ideas? Thanks in advance.
initialize array like
NSMutableArray *dummyArr = [ [ NSMutableArray alloc] init];
[dummyArr addObject #"Adam"];
[dummyArr addObject #"John"];
Either NSDictionary is what you need, however if you want in the format you have specified in the question:
NSString *phone = NSString stringWithFormat:#"Phone=%#",phoneNumber.text];
NSLog(#"Phone :%#",phone);
NSString *TxtUserName = [NSString stringWithFormat:#"Name=%#",userName.text];
NSString *TxtEmailId = [NSString stringWithFormat:#"Email=%#",emailId.text];
NSArray *details = [NSArray arrayWithObjects:TxtUserName,TxtEmailId,phone,nil];
The other answers mentioned here deal with the part of using NSDictionary/NSMutableDictionary.
Use NSDictionary or NSMutableDictionary: can handle key value pair
Array can store only objects,what you need is key-value pair mechanism
you can do like below...
dictMut=[[NSMutableDictionary alloc]init];
[dictMut setObject:#"xxxx" forKey:#"Name"];
[dictMut setObject:#"xxxx#gmail.com" forKey:#"email"];
[dictMut setObject:#"8786" forKey:#"phone"];
NSMutableArray *temp=[NSMutableArray alloc]init];
[temp addObject:dictMut];
Happy Coding!!!
What you could do is:
NSDictionary *dictionary = #{#"name": userName.text, #"email":emailId.text};
Or use NSMutableDictionary if you want to change the objects.
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:#"John" forKey:#"Firstname"];
[dict setObject:#"Doe" forKey:#"Lastname"];
[dict setObject:#"info at objectgraph.com" forKey:#"Email"];
NSLog(#"%#", dict);
NSArray *keys = [dict allKeys];
// values in foreach loop
for (NSString *key in keys) {
NSLog(#"%# is %#",key, [dict objectForKey:key]);
}
NSDictionary *dict = [[NSDictionary alloc] init];
[dict setValue:TxtUserName forKey:#"Name"];
[dict setValue:TxtEmailId forKey:#"Email"];
to get it back:
[dict valueForKey:#"Name"];
You can use NSDictionary for this type of work. You can do it using this two method.
NSDictionary * = [NSDictionary dictionaryWithObjectsAndKeys:userName.text,#"Name",emailId.text,#"Email",phoneNumber.text,#"phone" nil];
Or you can do it like this.
NSMutableDictionary *eventData = [NSDictionary dictionaryWithObjectsAndKeys:userName.text,#"Name", nil];
[eventData setObject:emailId.text forKey:#"Email"];
[eventData setObject:phoneNumber.text forKey:#"phone"];
Try:
NSMutableDictionary* dict = [NSMutableDictionary new];
[dict setObject:#"Bob Jones" forKey:#"name"];
[dict setObject:#31337 forKey:#"number"];
[dict setObject:#"Turkey" forKey:#"location"];
OR, shorthand:
NSDictionary dict = #{#"name": #"Bob Jones", #"number": #31337, #"location": #"Turkey"};
The #31337 is objective-c shorthand for [NSNumber numberWithInt:31337] and is needed because NSDictionary / NSMutableDictionary can only store NSObject-derived classes.
In the first example, you're defining a blank NSMutableDictionary and then adding stuff to it. In the second, you're creating a constant, immutable, unchangeable dictionary with some keys and values in it. Example 1 and example 2 have the same data in it, but you can add / delete stuff from the first example.
Also, NSMutableDictionary is a subclass of NSDictionary, which means you can pass it to functions that accept NSDictionary. You cannot cast the other way.
You can use an NSDictionary which will allow you to store the individual bits of data you require, but you might also want to follow the MVC-design pattern which is very important in the Cocoa and Cocoa Touch frameworks.
In the MVC design pattern the data you are attempting to represent is the Model, so if you are doing anything non-trivial with the data you will want to create one or more objects representing the data, which can then be passed around your app and used appropriately.
It would be declared something like:
MyModel.h:
#interface MyModel : NSObject
#property (strong, readwrite) NSString *username;
#property (strong, readwrite) NSString *phoneNumber;
#property (strong, readwrite) NSString *emailAddress;
#end
In the implementation file (MyModel.m) you don't even have to use #synthesize these days as Apple considers that to be too much typing. Instead the modern clang compiler will auto-generate the backing instance variables on your behalf. Just remember to reference these properties using myobject.username or self.username rather than simply _username.
You can then consider adding methods that allow the data to be manipulated. In this way your allow your model to change over time without having to re-code significant parts of your app, which would otherwise rely on an NSDictionary with a fixed key name, which will simplify the maintenance of your app.
While using dictionary is a very good option creating a model class for storing user info would be more useful. You can create methods which would work on the data for more reusability and keeping things simple.
#interface User : NSObject
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *emailID;
#property (nonatomic, copy) NSString *phoneNumber;
- (id)initWithName:(NSString *)name
emailID:(NSString *)emailID
andPhoneNumber:(NSString *)phoneNumber;
Create user instance and set the values
-(IBAction)btnSubmit:(UIButton *)sender
{
User *user = [User new];
user.name = userName.text;
user.emailID = emailId.text;
user.phoneNumber = phoneNumber.text;
//Use this instance for your work
}
First set your data to NSMutableDictionary.........
Dic=[[NSMutableDictionary alloc]init];
[Dic setObject:#"xxxx" forKey:#"Name"];
[Dic setObject:#"xxxx#gmail.com" forKey:#"email"];
[Dic setObject:#"8786" forKey:#"phone"];
And then get data from NSMutableDictionary....

Error Reading Copied NSMutableArray on iPhone SDK

In one of my methods, I fetched and parsed a JSON and placed it inside an NSArray called jsonArray in -(void)method1. I then copied the contents of that jsonArray to an NSMutableArray called copiedJsonArray to be used on other methods. Problem is, copiedJsonArray crashes whenever I log its contents in the console from the other methods -(void)method2 but it logs fine in -(void)method1.
How can I fix this?
In my header file:
#interface MainViewController : UIViewController
#property (nonatomic, retain) NSMutableArray *copiedJsonArray;
In my implementation file:
#synthesize copiedJsonArray;
- (void)viewDidLoad
{
[self method1];
}
- (void)method1
{
NSString *urlString = [NSString stringWithFormat:THE_URL];
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
NSString *jsonString = [[[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding] autorelease];
NSDictionary *jsonDictonary = [jsonString JSONValue];
NSArray *jsonArray = [jsonDictonary valueForKeyPath:#"QUERY.DATA"];
self.copiedJsonArray = [[NSMutableArray alloc] initWithArray:jsonArray copyItems:YES];
NSLog(#"Copied JSON Array in Method 1: %#", self.copiedJsonArray);
[self method2];
}
- (void)method2
{
NSLog(#"Copied JSON Array in Method 2: %#", self.copiedJsonArray);
}
I also tried doing this too but it does the same error:
copiedJsonArray = [jsonArray mutableCopy];
I also tried implementing NSCopy but fails too:
#interface MainViewController : UIViewController <NSCopying>
{
NSMutableArray *copiedJsonArray;
}
I'm doing this so that I can do a loop in my copiedJsonArray without fetching its contents from JSON again and again when the user taps on my UISegmentedControl.
If you call method2 before method1 it will crash as copiedJasonArray has not been created. You should not create instance variables inside methods (as you cannot know if they have been called). You should do it when you create your viewController, in viewDidLoad for example.
And use properties:
#interface
#property (retain) NSMutableArray* copiedJsonArray;
#end
then either
#synthesize copiedJsonArray = _copiedJsonArray
or leave that line it out (the compiler will put it in automatically in 4.5)
access as self.copiedJsonArray or _copiedJSONArray.
Outside of getters,setters,inits and deallocs, use the self. form, it's safer.
You could also create _copiedJsonArray lazily in the setter:
- (NSMutableArray*) copiedJsonArray
{
if (!_copiedJasonArray)
_copiedJsonArray = [NSMutableArray alloc] init;
return _copiedJasonArray;
}

Best way to store arrays with keys, and have them sorted by order of entry

Having a little issue with NSDictionary and sorting. I've read a few posts on here about using certain methods such as sortedArrayUsingSelector and keysSortedByValueUsingComparator but cannot quite grasp it. At the moment I have a Table View Controller displaying some data correctly. The only issue I have is the order it is displaying it (or rather, the order of the sections).
In the declaration file:
#interface SettingsTableViewController : UITableViewController {
NSDictionary *tableContents;
NSArray *keys;
}
#property (nonatomic,retain) NSDictionary *data;
#property (nonatomic,retain) NSArray *keys;
#end
And the implementation file, for viewDidLoad:
NSArray *arrayTemporary1 = [[NSArray alloc]initWithObjects:#"Settings 1",#"Setting 2",#"Setting 3",#"Setting 4",nil];
NSArray *arrayTemporary2 = [[NSArray alloc]initWithObjects:#"Enable Gestures",nil];
NSArray *arrayTemporary3 = [[NSArray alloc]initWithObjects:#"Instructions",#"Frequently Asked Questions",#"About",nil];
NSDictionary *temporary =[[NSDictionary alloc]initWithObjectsAndKeys:arrayTemporary1,#"Settings",arrayTemporary2,#"Application",arrayTemporary3,#"Information",nil];
self.tableContents = temporary;
self.keys = [self.tableContents allKeys];
[temporary release];
[arrayTemporary1 release];
[arrayTemporary2 release];
[arrayTemporary3 release];
Individual cell data is accessed from an array NSArray *listData =[self.tableContents objectForKey:[self.keys objectAtIndex:[indexPath section]]]; inside the cellForRowAtIndexPath method.
Whilst this works fine, the issue I'm having is that rather than displaying the sections in the order that it is written in temporary, it does so randomly. Would appreciate some guidance.
Dictionaries do not guarantee ordering, so tableContents has no memory of the order in which you added the arrays to it. You should keep ordered data in NSArrays.
Put the titles of the sections in a separate NSArray. Then use the dictionary you have to look up the data for the cells when you need to. This lets you keep the titles in the order you want, and still have a way to find the values in the dictionary.

plist to array "Collection was mutated while being enumerated" exception [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Getting exception as “Collection was mutated while being enumerated”
This question is a continuation of another question I posted earlier How to read in plist data into a data model?
#devdavid's helped me to get this far...
I have a plist file called "HotelList.plist" and it looks like this:
<array>
<dict>
<key>hotelID</key>
<integer>0</integer>
<key>name</key>
<string>Solmar</string>
// ... more keys and strings ...
</dict>
// ... more hotel entries ...
</array>
I have a "hotel" class describing the keys.
I have a data model class where I would like to read in this plist into an array.
#import "DataModel.h"
#import "Hotel.h"
// Private methods
#interface DataModel ()
#property (nonatomic, retain) NSMutableArray *hotels;
-(void)loadHotels;
#end
#implementation DataModel
#synthesize hotels;
- (id)init {
if ((self = [super init])) {
[self loadHotels];
}
return self;
}
- (void)dealloc {
[hotels release];
[super dealloc];
}
- (void)loadHotels {
NSBundle* bundle = [NSBundle mainBundle];
NSString* plistpath = [bundle pathForResource:#"HotelList" ofType:#"plist"];
hotels = [[NSMutableArray arrayWithContentsOfFile:plistpath]retain];
for (NSDictionary *hotelDict in hotels) {
Hotel *hotel = [[Hotel alloc] init];
hotel.hotelID = [[hotelDict objectForKey:#"hotelID"] intValue];
hotel.name = [hotelDict objectForKey:#"name"];
[hotels addObject:hotel];
[hotel release];
}
}
#end
When I run this, the debugger shows me that each hotel dict was read in but when it reaches the end of the plist (I have about 30 hotels), it tries to go back to the first one and crashes, giving an exception "Collection was mutated while being enumerated".
The green SIGABRT indicator stops on the
for (NSDictionary *hotelDict in hotels) {
line. Is there something wrong with my for loop? The way I set up the arrays/dictionaries? Or maybe the formatting of the plist is wrong (although I don't think so because the debugger shows me it is reading it correctly)?
For completeness, I should mention that, yes, the plist file is present and is in the mainBundle, and spelled correctly. Also, the data in the plist is static -- I won't have to save anything new to a file.
Please help!
Use accessors, not ivars directly. Your problem would have been more obvious that way, and you'd avoid the possible leak you have in the assignment of hotel (if loadHotels is every called a second time). Your code reads dictionaries into an array and then tries to append Hotel objects onto that same array. Here's what you really meant to say:
NSArray *hotelDicts = [[NSArray alloc] initWithContentsOfFile:plistpath];
self.hotels = [NSMutableArray array];
for (NSDictionary *hotelDict in hotelDicts) {
Hotel *hotel = [[Hotel alloc] init];
hotel.hotelID = [[hotelDict objectForKey:#"hotelID"] intValue];
hotel.name = [hotelDict objectForKey:#"name"];
[self.hotels addObject:hotel];
[hotel release];
}
[hotelDicts release];
You're attempting to modify the collection you're enumerating through -- you can't do this. Instead of using an enumerator, try iterating through the collection instead. When you iterate through an array, you can still add/remove members of the array because you aren't bound to the enumeration that was defined prior to begining the for block.
One way to iterate through the collection is with a simple for loop:
for( int i = 0; i < [array count]; i++ )
{
id object = [array objectAtIndex:i];
// do something with object
}

Using plist in a DetailView

I have this tableview in my app which has a particular item name such as "Apple". When the user taps on that selected item they are directed to a detailView that views an image and description of that selected item. I was thinking to use plist´s to do this, but I don't know how to. Please help me.
I looked at some sources such as iCodeBlog but they don´t really explain retrieving and saving so well. Maybe you people can also give reference to some links that describe this better.
Heres a plist that I have tried. Each of the items (Orange, Apple) have the data that I want to display in my detailView. I basically want to display the data shown.
Here is an example view controller which will do what you want. I named the class DetailViewController and loaded the plist from Details.plist in the app's resources. Images are also located in the app's resources.
DetailViewController.h
#import <UIKit/UIKit.h>
#interface DetailViewController : UIViewController {
UILabel *description;
UIImageView *imageView;
NSString *selectedItem;
}
#property (nonatomic, retain) IBOutlet UILabel *description;
#property (nonatomic, retain) IBOutlet UIImageView *imageView;
#property (nonatomic, copy) NSString *selectedItem;
#end
DetailViewController.m
#import "DetailViewController.h"
#implementation DetailViewController
#synthesize description, imageView, selectedItem;
- (void)viewDidLoad {
NSDictionary *details = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Details" ofType:#"plist"]];
details = [details objectForKey:self.selectedItem];
self.description.text = [details objectForKey:#"description"];
self.imageView.image = [UIImage imageNamed:[details objectForKey:#"image"]];
}
#end
Then, all you have to do is update the selectedItem property before loading the view and create a NIB file with the description label and image view attached to their outlets.
Plists are really useful for many cases:
+ (NSMutableDictionary*) containerDictionary {
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"ContainerGraphics.plist"];
NSMutableDictionary *plistData = [NSMutableDictionary dictionaryWithContentsOfFile:finalPath];
return plistData;
}
This loads a plist file (Property List) called "ContainerGraphics.plist" from my apps bundle.
The plist file is actually just an XML file. You can build one in Xcode by selecting "new file" -> "resource" -> "Property list".
You don't really say what it is you don't understand, so if there is something specific that you find hard to comprehend, please elaborate:)
EDIT:
With the plist you post, use my above approach to "load in" the plist file.
Then it is an NSDictionary of NSDictionaries..
So to get to the "Apple" dictionary you go:
NSDictionary *appleDictionary = [plistData objectForKey:#"Apple"];
Which means something along - go into the list and retrieve the "Apple" dictionary.
Now to get the image and description values you would go:
NSString *imagePath = [appleDictionary objectForKey:#"image"];
NSString *descriptionString = [appleDictionary objectForKey:#"description"];
That is pretty much what there is to it.
If you have a a tableView that need to be populated with this data there is a catch!
The data inside an NSDictionary has no order. So you can't just reference [dictionary objectAtIndex:indexPath.row] as you would do with an array of data for a tableView.
What you do then is to first get the plistData, as in my first example.
(do this in viewDidLoad).
Then you extract all the keys from the plistData dictionary - notice that the "top dictionary is specific, "Apple, "Orange" etc. not values you want to hardcode into your app. But, the dictionaries inside these dictionaries are general, image, description…
So:
NSArray *allKeys = [plistData allKeys];
the allKey array now contains keys (Apple, Orange, etc.)to get to the NSDictionaries in the plistData.
In the cellForRowAtIndexPath method you can now go:
NSDictionary *dictionaryForCurrentCell = [plistData objectForKey:[allKeys objectAtIndex:indexPath.row]];
[cell.textLabel setText:[dictionaryForCurrentCell objectForKey:#"image"]];
[cell.detailLabel setText:[dictionaryForCurrentCell objectForKey:#"description"]];
Hope it helps:)
Pass an NSString to the detail view and load the item based on the NSString, This could be a particular dictionary name, or item in an array.
newDetailViewController.currentItem = #"Array1";
and in my detail view, define:
.h
NSString *currentItem;
#property(nonatomic,retain) NSString *currentItem;
.m
#synthesize currentItem;
Now currentItem in your detailView controller will be "Array1", since that is what we passed to it.
It all depends on really how your plist is setup, are you using an NSArray, an NSDictionary?
There are plenty of ways you can put this to use:
NSString *current = [NSString stringWithFormat:#"%#/%#",appDocsPath,currentItem];
NSMutableDictionary* dict = [[[NSMutableDictionary alloc] initWithContentsOfFile:current] autorelease];
EDIT FROM QUESTION:
Using the example above:
aLabel.text = [dict objectForKey#"description"];
Create an array in a plist with a number of items which is equal to the number of rows. Create a string called description and a string called image in each item. Get the number of the selected row in didSelectRowAtIndex: method. Store the dictionary in an NSDictionary
[myDictionary initWithDictionary:[myArray objectAtIndex:indexPath.row]]
Then get the imagestring and the description string by calling
[myDictionary objectForKey:#"image"]
and pass these strings to the detail view.