Populating Two UITableView in the same View - iphone

I have 2 UITableView in the same View and my question is how to populate them with different data in the viewDidLoad method? thanks.

To answer the plist part of your question:
NSString *filePath=[[[NSBundle mainBundle] pathForResource:#"someName" ofType:#"plist"] retain];
NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:filePath] retain];
So just use two of these, one for each table, then just have two tableView, as it appears you already understand.

You populate them in the
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
and in that function you can compare the tableView to see what table it is, and load the appropriate data for that table.
//Assume the following property
#property (nonatomic, retain) NSDictionary *myData;
add this to the Code file
- (NSDictionary) myData
{
if (myData == nil) //Notice I am accessing the variable, not the property accessor.
{
NSString *filePath=[[NSBundle mainBundle] pathForResource:#"someName" ofType:#"plist"];
myData = [[NSDictionary dictionaryWithContentsOfFile:filePath] retain];
}
return myData;
}
then when you access the self.myData property it will open up if it is not opened already.

All delegate and datasource protocol methods always pass a pointer to the tablesView they are called for. Just do an if to check which is meant in each method.

Related

NSDictionary to NSArray, will not display properly?

I have a signature page, were the user can sign their name.
It needs to then be saved to NSDictionary, but i want to call a List of the keys to be text in a TableView for each row or cell.
so:
"viewImage = saved as object to key:Random Number"
That parts somewhat easy, the hard part is when i call it on the other Page to the TableView.
It Exits the App with Error"SIGABRT". Now all my Delegates are in place and working...i believe.
now heres some example code:
FirstPage.m
UIImagePNGRepresentation(viewImage);
NSMutableArray *innerArray = [[NSMutableArray array]init];
[innerArray addObject:viewImage];
[SignatureSave setObject:innerArray forKey:#"5599"];
simple Enough, but doesnt give me an error.
SecondPage.m
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FirstPage *appShare = (FirstPage *)[[UIApplication sharedApplication] delegate];
NSArray *dataDuplicate = [[NSArray alloc]init ];
dataDuplicate = [appShare.SignatureSave allKeysForObject:#"innerArray"];
static NSString *CellIdentifier = #"Cell";
NSLog(#"%#",dataDuplicate);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero]autorelease];
}
if (dataDuplicate != nil) {
cell.textLabel.text = [dataDuplicate objectAtIndex:indexPath.row];
}
else
{
UIAlertView *CellAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Error Loading content, Try Again Later." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[CellAlert show];
[CellAlert release];
}
return cell;
}
#end
Now, How do i get the viewImage to save to the NSDictionary, to be able to call it on the SecondPage and display the name of the objects in the TableVIew?
I really don't understand whats your problem exactly.
first of all, is your dictionary a retained object?
//FirstPage.h
#property (nonatomic, retain) NSDictionary *SignatureSave;
//FirstPage.m
#synthesize SignatureSave;
…
UIImagePNGRepresentation(viewImage);
NSMutableArray *innerArray = [NSMutableArray array]; // using "array" is equivalent to alloc-init-autorelease
[innerArray addObject:viewImage];
[self.SignatureSave setObject:innerArray forKey:#"5599"];
// OR setting the array directly:
UIImagePNGRepresentation(viewImage);
NSArray *innerArray = [NSArray arrayWithObject:viewImage];
[self.SignatureSave setObject:innerArray forKey:#"5599"];
// OR even setting the image directly to the dictionary:
UIImagePNGRepresentation(viewImage);
[self.SignatureSave setObject:viewImage forKey:#"5599"];
now if you access the object by writing self. in front it will call the retain and your object will stay alive. Otherwise it would be autoreleased at the end of the method. This will fix the problem that your dictionary is maybe not present/available at table view creation and you don't have to use a singleton.
what are you trying to access with this code?
FirstPage *appShare = (FirstPage *)[[UIApplication sharedApplication] delegate];
with [[UIApplication sharedApplication] delegate] you get your application delegate (obviously). These are the MyAppNameAppDelegate files but you treat it as a FirstPage class.
Just NSLog() to check you get the right class, the one you expect.
NSLog(#"%#", [[[UIApplication sharedApplication] delegate] class]);
here you have a potential leak, you alloc-init but never release it:
NSArray *dataDuplicate = [[NSArray alloc]init ];
dataDuplicate = [appShare.SignatureSave allKeysForObject:#"innerArray"];
furthermore you can simplify it (will be autoreleased):
NSArray *dataDuplicate = [appShare.SignatureSave allKeysForObject:#"innerArray"];
and here you have another issue.
Why do you call all keys for the object #"innerArray"?
you don't have such an object and it's in many more cases wrong. innerArray was your previously named array in FirstPage.m but it is only for you as a developer to remember the variable better. After compilation it will have a cryptic name anyway. You could access your key #"5599" if you like but I don't think you want this. In your case you want to access all keys of the dictionary so simply call:
NSArray *dataDuplicate = [appShare.SignatureSave allKeys];
now you will have an array with all keys of your dictionary and you can access them like you do with objectAtIndex:.
NSString *keyName = [dataDuplicate objectAtIndex:indexPath.row];
cell.textLabel.text = keyName;
id theObject = [appShare.SignatureSave objectForKey:keyName]; // for example the image
Tell me if this solves your problems or tell me how I misunderstood your question.
I found the answer to this to be quit simple actually,
I ended up going with the Singleton Method instead of the Global Variable Method.
Now the Singleton Method looks terrifying but its quit simple, See here.
The main difference i noticed from the singleton method to the global method is,
Global method takes a lot of converting and re-converting.
Though the Singleton Method is working with a single object over many pages or classes.
Now i hope this will better assist people in the future also!

Instance variable not returning a value in iOS application

This problem might be related to Instance variable not retaining value in iOS application.
My application has a table view that's supposed to be populated with data from a plist. The plist is question is determined based on a defaults setting.
After setting the contents of the instance variable, a count on it shows that it contains multiple records. However, if I then later try to count this instance variable when drawing the table view cells, it shows no records.
--
I declared a "providerData" NSDictionary instance variable in my class header:
#import <UIKit/UIKit.h>
#interface ViewController : UITableViewController {
NSDictionary *providerData;
}
#property (nonatomic, retain) NSDictionary *providerData;
- (void)loadProviderDataSource:(NSString *)providerName;
#end
In my implementation I use viewDidLoad to read a dictionary from a plist file, check the value of "provider_preference", and then call [self loadProviderDataSource:providerPreference]:
- (void)viewDidLoad
{
[super viewDidLoad];
// Retrieve the user's provider preference from defaults:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// Set a default value for provider preference if the user didn't explicitly set one:
NSString *providerPreference = [[NSString alloc] initWithString:#"foo"];
if ([defaults stringForKey:#"provider_preference"]) {
providerPreference = [defaults stringForKey:#"provider_preference"];
}
// Load provider data from the relevant plist:
[self loadProviderDataSource:providerPreference];
}
This all works fine (using NSLog I can see that the plist file is read correctly and that a correct providerPreference value is passed to loadProviderDataSource.
The implementation for loadProviderDataSource is as follows:
- (void)loadProviderDataSource:(NSString *)providerName
{
// Set the filename and path for the data source:
NSString *dataSourceFileName = [[NSString alloc] initWithFormat:#"data-%#.plist",providerName];
NSString *dataSourcePath = [[NSBundle mainBundle] bundlePath];
dataSourcePath = [dataSourcePath stringByAppendingPathComponent:dataSourceFileName];
// Read the provider data:
self.providerData = [[NSDictionary alloc] initWithContentsOfFile:dataSourcePath];
NSLog(#"provider data entry count 1: %d", [self.providerData count]);
}
At this point the NSLog shows me that there are 2 entries in providerData.
However, when I have to set the cells for my table view, the NSLog in the following code shows me that providerData has 0 entries:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSLog(#"provider data entry count 2: %d", [self.providerData count]);
return cell;
}
In loadProviderDataSource I've tried using:
self.providerData = [[NSDictionary alloc] initWithContentsOfFile:dataSourcePath];
and:
[self setProviderData:[[NSDictionary alloc] initWithContentsOfFile:dataSourcePath]];
... neither work and I still end up with this in my log:
provider data entry count 1: 2
provider data entry count 2: 0
provider data entry count 2: 0
Any ideas?
Based on NSLog(#"%#", self); in both the method which shows up as follows
loadProviderDataSource
self: <ViewController: 0x83739b0>
tableView:cellForRowAtIndexPath
self: <ViewController: 0x8373170>
It looks like you are creating a second instance of ViewController somehow.
I understand one of them is created using XIB. Verify whether you are creating another one programmatically.

UITableView crashes when scrolling

I've been attempting to create my own app for a band by my friends, and I've been experimenting with using a custom TableViewCell for news articles that appear on the website. My main objective is to get all of the data from the website, store it in a NSMutableArray, and then display that in my custom cells.
The app runs fine when it loads the first 5-6 cells. However, when I begin to scroll, the app crashes. I've pinpointed the in the cellForRowAtIndexPath: method. Using GDB, I've also come to find out that after I create my NSMutableArray data and once the program runs, after scrolling, my array seems to be autoreleased. I'm not sure why this happens. Here's what I have for my code thus far:
In HomeViewController.h:
#interface HomeViewController : UIViewController {
NSArray *results;
NSMutableArray *titles;
NSMutableArray *dates;
NSMutableArray *entries;
}
#property (nonatomic, retain) NSMutableArray *titles;
#property (nonatomic, retain) NSMutableArray *dates;
#property (nonatomic, retain) NSMutableArray *entries;
#end
In HomeViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
titles = [[NSMutableArray alloc] init];
dates = [[NSMutableArray alloc] init];
entries = [[NSMutableArray alloc] init];
while((i+1) != endIndex){
NSString *curr_title = [[NSString alloc] init];
NSString *curr_date = [[NSString alloc] init];
NSString *curr_entry = [[NSString alloc] init];
//do some character iterations across a string
[titles addObject:curr_title];
[dates addObject:curr_date];
[entries addObject:curr_entry];
[curr_title release];
[curr_date release];
[curr_entry release];
}
}
//more code here, removed
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"NewsCell";
NewsCell *cell = (NewsCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle]
loadNibNamed:#"NewsCell"
owner:self options:nil];
// cell = [topLevelObjects objectAtIndex:0];
for(id currentObject in topLevelObjects){
if([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (NewsCell *)currentObject;
break;
}
}
}
NSLog(#"%d", indexPath.row);
NSLog(#"%d", titles.count);
cell.cellTitle.text = [titles objectAtIndex:indexPath.row];
cell.datePosted.text = [dates objectAtIndex:indexPath.row];
cell.preview.text = [entries objectAtIndex:indexPath.row];
return cell;
}
Again, the first 5-6 cells show up. Once I scroll, I tried doing po titles and got this error:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000009
0x011b309b in objc_msgSend ()
I've tried allocating the arrays in initWithNibName: but that didn't seem to do much. I've tried moving all the code in viewDidLoad: and then calling [super viewDidLoad] and it just produced the same results as well. Sorry this is so long, but I figured people needed to see my code.
I don't see anything obviously wrong with the code you posted. Try enabling the NSZombieEnabled environment variable. This prevents objects from being released so that when your application "crashes" you can determine which object caused the problem.
Also, instead of looping through array of objects returned by loadNibNamed:owner:options, you should assign the desired object to an IBOutlet property of your class. See Loading Custom Table-View Cells From Nib Files for an example.
The following is extracted from the new code you posted:
NSString *curr_title = [[NSString alloc] init];
//do some character iterations across a string
[titles addObject:curr_title];
[curr_title release];
NSString is not mutable (as is NSMutableString). Are you intentionally adding empty strings to the titles array?
In response to your comment: stringByAppendingString creates a new autoreleased NSString object.
NSString *curr_title = [[NSString alloc] init];
// this leaks the original NSString object (curr_title no longer points to it);
// curr_title now points to a new, autoreleased NSString
curr_title = [curr_title stringByAppendingString:#"..."];
[titles addObject:curr_title];
// releasing the autoreleased NSString will cause your application to crash!
[curr_title release];
*EXC_BAD_ACCESS* is a sure sign that one of your objects is getting over released (or wasn't retained properly). NSZombieEnabled is your friend here, just as titaniumdecoy suggests - figuring out what object is being over released is half the battle. Just be sure to turn it off before releasing the app, because (as titaniumdecoy pointed out) it prevents objects from getting released.
I usually use a combination of NSZombieEnabled and well placed breakpoints (so I can walk through the code till it crashes) to figure out where the problem is cropping up in the code. Then it's usually a simple matter of backtracking to figure out where the object was over released.
The problem might be with the implementation of NewsCell, all your properties there being retained?
Also, any reason HomeViewController is subclassing UIViewController and not UITableViewController?
And this should work just fine:
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"NewsCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];

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.

Iphone: threading issue?

I am initialing a dictionary in viewDidLoad and using it to create table view cells.
The first load works fine. But as soon as I scroll the table view to see the item (not displayed at the bottom) the app crashes. Through the Debugger I noticed the address of the dictionary item "rootItemsDict" changes when I did the scroll. Not able to figure out why that is. From my understanding the address of an object initialized once should remain same, at least within the given class instance. Any thoughts?
- (void)viewDidLoad {
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"menu" ofType:#"plist"];
rootItemsDict = [NSDictionary dictionaryWithContentsOfFile:path];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *title = (NSString *)[[[rootItemsDict objectForKey:#"rootMenuItems"] objectAtIndex:row] objectForKey:#"heading"];
+dictionaryWithContentsOfFile: returns an autoreleased instance. To take ownership you need to explicitly retain it:
rootItemsDict = [[NSDictionary dictionaryWithContentsOfFile:path] retain];
... or use the alloc/init form:
rootItemsDict = [[NSDictionary alloc] initWithContentsOfFile:path];
... or if you have a suitable property declaration (retain) use the setter:
self.rootItemsDict = [NSDictionary dictionaryWithContentsOfFile:path];
I recommend to read the Memory Management Programming Guide, especially the section on object ownership.