setting UITableViewCell to value from custom object instance - objective-c

So basically I am trying to create instances of the class below every time I received a valid response from a web request and then store those instances in an array so I can access their data later. Then, I try to populate a table view with specific fields from the instance(s) that are stored in the array. I've been having some issues since I am very familiar with C++ and do this sort of thing with vectors and then just access based off of the index I need, but this has had me pulling my hair out! Thanks, code is below:
eventDetails.h:
#interface eventDetails : NSObject {
NSString *eventName, *eventID;
}
-(void) setEventID : (NSString *) ID;
-(void) setEventName: (NSString *) name;
-(NSString *) getEventName;
-(NSString *) getEventID;
and also note that
NSMutableArray *events
is declared in my .h file and
events = [[NSMutableArray alloc] init];
has been called in the viewDidLoad
I then dynamically create instances as a response is received from an web request and add them to an array:
if ([elementName isEqualToString:#"id"])
{
NSLog(#"at beginning of event, length is %i", [events count]);
temp = [[eventDetails alloc] init];
[temp setEventID:[NSMutableString stringWithString:soapResults]];
[soapResults setString:#""];
elementFound = FALSE;
}
if ([elementName isEqualToString:#"name"])
{
[temp setEventName:[NSMutableString stringWithString:soapResults]];
[events addObject:temp];
[soapResults setString:#""];
elementFound = FALSE;
//[temp release];
}
After everything is all said and done, I created a little test function to ensure the data was set correctly:
-(void) test{
for (eventDetails *s in events){
NSLog(#"Entry ID: %# with name %#", [s getEventID], [s getEventName]);
}
}
and I get the following (correct) output:
2011-04-09 18:53:24.624 Validator[90982:207] Entry ID: 701 with name iPhone Test Event
2011-04-09 18:53:24.625 Validator[90982:207] Entry ID: 784 with name Another iPhone Test Event
2011-04-09 18:53:24.626 Validator[90982:207] Entry ID: 839 with name third iphone
I then try to refresh the table view, and have it pull in data from the instances in the array:
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
//---try to get a reusable cell---
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//---create new cell if no reusable cell is available---
if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
//---set the text to display for the cell---
eventDetails *cellDetails = [[eventDetails alloc] init];
NSInteger row = [indexPath row];
cellDetails = [[self events] objectAtIndex:row];
NSString *cellValue = [cellDetails getEventName];
NSLog(#"Event is: %#", cellValue);
cell.textLabel.text = cellValue;
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
return cell;
}
But every time the program gets to this part, it crashed which a EXC_BAD_ACCESS where I say:
cell.textLabel.text = cellValue;
Thanks for your help. I think I might be doing something wrong with how I declare the instances of the eventDetails class, but I am not sure since it is working correctly as far as storing that data. If you need any more code, I have the missing sections.

There are too many omitted details in the code you posted to know for sure, but my guess would be the eventName isn't retained, and is deallocated sometime before you attempt to use it.
Check your setEventName: implementation; it would need to send either retain or copy to the name argument to ensure that the string won't be deallocated before you're done using it. However, the situation is more complex than that if you want to avoid memory leaks, so you if you haven't done so already, I'd recommend reading up on memory management, in particular, Apple's excellent Memory Management Programming Guide. (Note: I've given up posting links since Apple keeps changing them).
A side note: don't prefix the names of accessor methods with the word get; that would be fine in Java or C++, but this is Objective-C. Your accessors should look like this:
- (NSString *)eventName;
- (NSString *)eventID;
There's no guarantee that Foundation mechanisms that rely on introspection will work correctly with accessors that don't follow the documented naming conventions, so that's another thing to read up on. :-)

Related

How to keep data in a NSMutableArray

AAA.m:
- (void)keepCurrentArray:(id)object
{
_currentTest=[[NSMutableArray alloc]init];
[_currentTest addObject:#"one"];
[_currentTest addObject:#"two"];
[_currentTest addObject:object];
NSLog(#"My Array is:%#",_currentTest);
}
Class BBB.m is passing objects to class AAA.
Right now if i'm passing X to the above method so the array will be: one,two,X . Then i'll send it Y and the array will be one,two,Y instead of what i want to accomplish: one,two,x,one,two,y.
Is that because I'm alloc and init _currentTest every time? How can I solve it?
Update:
I had a few suggestions on how to solve this and none of them worked for me. I've created a new project with just the code in the answers and i'm still getting the same result when I try to add the second object i get: one, two, test instead of one,two,test,one,two,test
Yes, it's because that you're alloc and init-ing every time you run that method. Instead, put _currentTest = [[NSMutableArray alloc] init]; in AAA.m's init method.
AAA.m
-(id)init
{
if ((self = [super init]))
_currentTest = [[NSMutableArray alloc] init];
return self;
}
- (void)keepCurrentArray:(id)object
{
[_currentTest addObject:#"one"];
[_currentTest addObject:#"two"];
[_currentTest addObject:object];
NSLog(#"My Array is:%#",_currentTest);
}
_currentTest=[[NSMutableArray alloc]init]; in a method is never a good thing!!!
As per naming convention it seems to be a property to the AAA Class. So for property, the alloc+init should be either in init or awakeFromNib. So that if is initialized just once.
However in some situations init is called more than once then your previous values are lost and new set are added.
So what you can do is make another class and put this _currentTest Array there and make it static and use it here. I hope this will work fine. And make sure in the init method of that class it is initialized just once, as :
//**this is not compiled and checked may contains typo and errors**
#implementation Storage
static NSMutableArray *yourStaticArray;
-(id)init{
self = [super init];
if (self) {
if (!yourStaticArray) {
yourStaticArray=[NSMutableArray new];
}
}
return self;
}
-(void)addYourStaticArray:(NSString *)val{
[yourStaticArray addObject:val];
}
-(NSArray *)yourStaticArray {
return yourStaticArray ;
}
#end
Well you need to have a property for that _currentTest if you want to be able to keep it around between method call.
Put this in your .h file
#property (nonatomic, copy) NSMutableArray * currentTest;
And this in hour .m file
- (NSMutableArray *)currentTest
{
if (!_currentTest)
_currentTest = [[NSMutableArray alloc] initWithCapacity:11];
return _currentTest;
}
- (void)keepCurrentArray:(id)object
{
[self.currentTest addObject:#"one"];
[self.currentTest addObject:#"two"];
[self.currentTest addObject:object];
NSLog(#"My Array is:%#", self.currentTest);
}
I Just try the code you've put on drop box and it's working exactly as it is suppose to, the array keeps it's value and everything,
BUT
Exactly as it is suppose to is not what you are trying to achieve
Your problem is not in AAA.m, your problem is in BBB.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ViewController *kios = [ViewController new];
[kios keepCurrentArray:#"Test"];
[kios keepCurrentArray:#"Test2"];
}
I took the liberty of adding the #"test2" to the code you've send. If you run it you will see that your array still exist when the second call is made.
The REAL problem here is that you are creating a NEW ViewController each time. A brand new one, it is normal that it is empty (clean), it's a new one.
If I buy a note pad monday and fill it up, I don't expect when I'm buying an other one on friday to be already fill with the stuff I've wrote on monday in the previous one.
But this is exactly that behaviour that you are expecting from your ViewController.
You need to store your NSMutableArray in an other object that doesn't
get destroy and created over and over again.
This is happening because you are creating a new array every time that your method is called. Basically, you need to see if it has already been created, and only create it if needed. You can change your method to:
- (void)keepCurrentArray:(id)object
{
if (!_currentTest)
{
_currentTest=[[NSMutableArray alloc]init];
}
[_currentTest addObject:#"one"];
[_currentTest addObject:#"two"];
[_currentTest addObject:object];
NSLog(#"My Array is:%#",_currentTest);
}
EDIT:
In addition to the above problem, you also have this code which needs to be corrected (comments removed):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ViewController *kios = [ViewController new];
[kios keepCurrentArray:#"Test"];
}
This code creates a new instance of ViewController every time that you click on a row in the table. Because you are creating a new instance instead of reusing the old one, you start with an empty array each time. In order to keep adding to the same array, you need to keep using the same view controller.
In order to do this, you need to add a declared property to your .h file, similar to your currentTest declared property:
#property (strong,nonatomic) ViewController *kios;
Then, change your action so that you only create a new view controller if needed (the first time) and then reuses it after that:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (!_kios)
{
_kios = [ViewController new];
}
[_kios keepCurrentArray:#"Test"];
}

Dictionary retrieval of value for key IOS

I am sorry i am new to IOS,i couldnt figure out the solution to this problem
This is just a beginner restaurant menu
There is a tableview containing items and price and when i click one item,it displays another view where the user has to input the quantity and click a done button,so when the user clicks done i want to multiply the quantity times the price,how do i retrieve that particular price and multiply it with the quantity user input in the textfield.
Here's my code
I have declared NSDictionary in the Menu header file called
NSDictionary *dict;
My viewdidload method
dict=[[NSDictionaryalloc]initWithObjectsAndKeys:
#"TomatoSoup",#"20.00",#"VegManchowSoup",#"12.00",nil];
NSLog(#"%#",dict);
[super viewDidLoad];
I have displayed this contents in a table view
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section{
return [[dict allKeys]count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
NSArray *sortedkeys=[[dict allKeys]sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
NSString *key=[sortedkeys objectAtIndex:indexPath.row];
NSString *value=[dict objectForKey:key];
cell.textLabel.text=value;
cell.detailTextLabel.text=key;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath{
if(indexPath.row==0){
VegQuantity *vegetarian1 = [[VegQuantity alloc] initWithNibName:#"VegQuantity" bundle:nil];
vegetarian1.m_SelectedIndexPath=indexPath.row;
vegetarian1.pass=dict;
[self presentModalViewController:vegetarian1 animated:YES];
}
if(indexPath.row==1){
VegQuantity *vegetarian1 = [[VegQuantity alloc] initWithNibName:#"VegQuantity" bundle:nil];
vegetarian1.m_SelectedIndexPath=indexPath.row;
[self presentModalViewController:vegetarian1 animated:YES];
}
}
VegQuantity.h
There is a View that has a textfield and a button saying done,
now when I click the done button I need to retrieve the value for that particular soup and multiply it with the number of quantity I input.
My problem is how am I supposed to retrieve the price(value) for that particular key and multiply it with the quantity.
dict=[[NSDictionary alloc]initWithObjectsAndKeys:
#"TomatoSoup",#"20.00",#"VegManchowSoup",#"12.00",nil];
The method is initWithObjectsAndKeys, which means first is the object then key, (key "20.00", object - "TomatoSoup") - in your case it's the opposite.
Second, instead of having an NSString for the price (I suppose it's price or quantity) use NSNumber - [NSNumber numberWithFloat:20.0f].
Then, make your VegQuantity view controller (btw it's good idea to call it VegQuantityViewController, in order to keep the naming conventions) 2 properties:
#property (nonatomic, strong) NSString *itemName; //Use strong if using ARC, otherwise retain
#property (nonatomic, strong) NSNumber *price;
and pass those values to the view controller before you show it. Then inside it you can do whatever you want with them.
P.S. It's a good practice to use properties to manipulate the values of the instance variables.
You retrieve a value from a Dictionary by using.
[dict objectForKey:#"someDummyKey"];
But to be honest. You should use a NSMutableArray as datasource for you UITableView and not a NSDictionary.

Adding data to a tableView using a NSMutableArray

I'm having a problem adding an item to my tableView.
I used to initialize an empty tableView at the start of my App and get it filled with scanned items every time the tableView reappears and there is an item in my variable.
Initialization of the tableView:
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:nil];
self.listArray = array;
TableView Data Source:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.listArray count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
if(section == 0)
return #"Eingescannte Artikel:";
else
return nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"testCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
NSUInteger row = [indexPath row];
cell.textLabel.text = [listArray objectAtIndex:row];//[NSString stringWithFormat:#"Das ist Zeile %i", indexPath.row];
return cell;
}
(Not the whole thing but the ones I changed)
As you may have seen I use an NSMutableArray to add items to my tableView.
So if an item ist scanned I'm adding it to my array like this:
[listArray insertObject:sharedGS.strEAN atIndex:0]; //using a shared Instance where I implemented my variable.
I also tried to use an variable to extend my Index every time a new Item is added, but it won't work both ways.
I'm quite new to programming so an not-too-hard-to-understand-answer would be quite nice ;)
If there's any information missing, feel free to ask.
/edit: Trying to specify my question: The data from the variable is written in a TableViewCell, but if I scan another one the other one is just being replaced. Not sure if it's a problem with my array or my tableView...
/edit No.2: Found out(thanks to fzwo) that my array isn't working correctly. It just doesn't grow by an addObject: or insertObject:atIndex: command. But I just don't get why... :(
All I'm doing: [listArray addObject:sharedGS.strEAN]; not that much space for errors in one simple line. Maybe I'm just too stupid to recognize what I'm doing wrong:D
You state that your problem is "adding an item to my tableView" , since you are adding the object to your array i am guessing the problem is that you are not reloading the table or that it is missing the dataSource binding.
You have not actually asked any question (even if you added info to "specify your question") so a wild guess, after
[listArray insertObject:sharedGS.strEAN atIndex:0];
put
[yourTableView reloadData];
Are you intentionally adding new items to the top of the table ? otherwise you could do
[listArray addObject:sharedGS.strEAN]; to add new items to the bottom
Otherwise it's worth noting that you are misusing dequeueReusableCellWithIdentifier, look at the example below for proper usage:
// Try to retrieve from the table view a now-unused cell with the given identifier
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
// If no cell is available, create a new one using the given identifier
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease];
}

UITableView is not displaying Cell Style 2

I'm trying to display an array of dictionaries variable in the table Cell Style 2 which is what the Contacts App uses to display the information of the contact.
For some reason the cell style does not show up.
The array variable "arrayOfDictionaries_E" which has the following setup.
/// Dictionary 1
NSArray *dataArray = [NSArray arrayWithObjects: #"Host", hostname, nil];
NSArray *keysArray = [NSArray arrayWithObjects: #"Label", #"Data", nil];
NSDictionary *host_dict = [[NSDictionary alloc] initWithObjects: dataArray forKeys: keysArray];
/// Dictionary 2
NSArray *dataArray2 = [NSArray arrayWithObjects: #"IP", hostIP, nil];
NSArray *keysArray2 = [NSArray arrayWithObjects: #"Label", #"Data", nil];
NSDictionary *ip_dict = [[NSDictionary alloc] initWithObjects: dataArray2 forKeys: keysArray2];
// Storage into Array of Dictionaries
NSArray *dictionariesArray = [[NSArray alloc] initWithObjects: host_dict, ip_dict, nil];
I have tested that the array contains the 2 dictionaries and both dictionaries are populated.
So I'm having trouble with:
First, I want the all cells to use CellStyle2.
Second, I want each dictionary to be created in a new section. (like the the Contacts App, selectively bundle groups of items together)
So here comes the code in my implementation file. It is a UIViewController adopting the 2 UITableView protocols.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [arrayOfDictionaries_E count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 2;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier] autorelease];
}
NSDictionary * dict = [arrayOfDictionaries_E objectAtIndex:indexPath.row];
cell.textLabel.text = [dict objectForKey:#"label"];
cell.detailTextLabel.text = [dict objectForKey:#"Data"];
return cell;
}
Find a screenshot of the current tableView here.
On the nib, I have linked the tableView outlet to the file's Owner and done likewise for the delegate and source. I have changed the Style to Grouped.
However as seen on the image above, when I run the program the cell doesn't display in the proper style specified also I don't know how to get each row to display in separate sections. --> I'm think of an Array > Array > Dictionary variable, to get to work, however I'm quite lost at the fact of how this can be implemented.
I'm still relatively new to Objective-c, so you may need to explain with actual code implementations, to demonstrate how I may achieve this.
Thanks in advance to taking the time to answer this question.
There are 2 problems which I think could be in your way:
You are using #"label" as the key when populating the cell, but #"Label" when populating the dictionary. The keys are case sensitive
You are using indexPath.row, this will only be 0 or 1 since you have 2 rows per section. You should be using indexPath.section since there is one section for each entry in your array.
I'm not sure what you do expect to see in rows one and two, though.
OK, I've had another look at your question and you seem to be making things a bit more complicated than they need to be. You appear to have a host name and an IP address that you want to display in two cells within a section, with the same labels in each part of the section.
Why not just have a single array of dictionaries, holding the name and IP address, then in cellForRowAtIndexPath pick the dictionary based on the section number, then based on the row number display the relevant item from the dictionary, and hardcode the label at that point?
By the way, the reason your cell style does not appear is because you are passing nil for the label due to the issue mentioned above, so it appears to only have a single label.

How to passing a NSMutableArray to another ViewController class

I have created NSMutale Array in "HeroListViewController". I want use it in another viewController which is MapTutorialViewController. I tried like this.
in HeroListViewController.h
MapTutorialViewController *maptutorialcontroller;
NSMutableArray *listData;
set properties and synthesize them correctly
in HeroListViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
listData = [[NSMutableArray alloc] init];
}
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *HeroTableViewCell = #"HeroTableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:HeroTableViewCell];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:HeroTableViewCell] autorelease];
}
NSManagedObject *oneHero = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSInteger tab = [tabBar.items indexOfObject:tabBar.selectedItem];
switch (tab) {
case kByName:
cell.textLabel.text = [oneHero valueForKey:#"name"];
cell.detailTextLabel.text = [oneHero valueForKey:#"secretIdentity"];
break;
case kBySecretIdentity:
cell.detailTextLabel.text = [oneHero valueForKey:#"name"];
cell.textLabel.text = [oneHero valueForKey:#"secretIdentity"];
default:
break;
}
[listData addObject: [oneHero valueForKey:#"secretIdentity"]];
count=[listData count];
printf("No of items of listData:%u\n", count);
if(maptutorialcontroller==nil){
maptutorialcontroller= [[MapTutorialViewController alloc]initWithNibName:#"MapTutorialViewController" bundle:nil];
maptutorialcontroller.secondarray=listData;
}
count=[maptutorialcontroller.secondarray count];
printf("No of items of seconarray :%u\n", count);
return cell;
}
OUTPUTS : No of items of listData:3
No of items of seconarray :3 // both are correct
BUT the the problem I have, when I try to use the secondarray in "MapTutorialViewController" like this,
in MapTutorialViewController.h
HeroListViewController *heroviewcontroller;
NSMutableArray *secondarray;
set properties and synthesize them correctly
in MapTutorialViewController.m
- (void)viewDidLoad
{
heroviewcontroller = [[HeroListViewController alloc]initWithNibName:#"HeroListViewController" bundle:nil];
self.secondarray=[heroviewcontroller.listData mutableCopy];
//secondarray= heroviewcontroller.listData;
int count;
count = [secondarray count];
//
printf("No of items of secondarray from MapTutorialViewContriller :%u\n", count);
}
OUTPUT : No of items of secondarray from MapTutorialViewContriller :0
Why it is 0
whats the wrong with my code, please help me
Example
firstviewcontroller .h file
before #interface
use #class secondViewcontroller;
declare this inside of #interface with
secondViewcontroller *sVC;
then in firstViewController.m file
before #implementation
#import "secondViewcontroller.h"
then
-------------------
secondVC.h file
#interface inside declare this
say NSMutableArray *secondarray;
and sythasize them.
-------------------
after this
in firstViewcontroller.h viewdidload create this sVC by alloc and initwithnibname then
sVC.secondArray=urfirstArray;
now while u push this sVC controller to navigation controller u can nslog this array in viewdidload.
This would only work if you create and fill the mutable array in the init method.
You should look into delegation and/or notification.
How is that array being created within HeroListViewController? In this method, you are creating a NEW instance of HeroListViewController and trying to get a property from it. If you already have a HeroListViewController in memory, this is completely wrong.
Make a property on the class for this viewDidLoad method. It should be of type NSMutableArray. When you allocate and initialize this class, call [set myArray:heroListArray] on it from HeroListViewController. That should give you access to it.
I'm assuming that you have a view containing this new view and the hero list view. If that is the case, then you could create a property in the new view like so:
#property (nonatomic,retain)HeroListViewController *heroListViewController;
and then set it equal to the heroList from the outside:
newView.heroListViewController = HeroListViewController;
The main problem with your code at the moment is that you're creating a new instance of HeroListViewController by using alloc init, and you're not accessing the same thing. By setting the new view's heroListViewController property, you can get access to the correct viewController.
Finally, in viewDidLoad of the new view - I'd actually put the code in viewWillAppear:(BOOL)Animated - you can put code to match the arrays.
Note that this whole way of doing it is messy and could be better done with a singleton class if you need access to an array in multiple places. The above will help you get it working quick, but if you want a really clean fix, go here: http://www.iphonedevsdk.com/forum/iphone-sdk-tutorials/24135-singleton-classes.html