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.
Related
I have project where user can add some Items to TableView. My data source for this TableView are objects with 2 properties NSString * nameOfItem and NSNumber * numberOfItem. Is any possibility to check my NSMutableArray if it contain string #"someString" as property nameOfItem and if yes than change numberOfItem +1 ?
UPDATE:
I try to do it with for(...in...) but it works just when i have only one object in my NSMutableArray. If i have more objects there it create one new object and than change value to ++ on old one:
Here is some code what i tried:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
NSString*nameToCheck = [NSString stringWithFormat:#"%#", [self.ivc.objects objectAtIndex:indexPath.row]];
for (Items *itemNamed in self.ivc.shoppingList.items) {
if ([itemNamed.nameOfItem isEqualToString:nameToCheck]) {
[itemNamed setNumberOfItem:[NSNumber numberWithInt:[itemNamed.numberOfItem integerValue]+1.0]];
} else {
Items *item = [Items new];
[item setNameOfItem:name];
[item setNumberOfItem:#(1)];
[self.ivc.shoppingList.items insertObject:item atIndex:0];
}
I want to create new one only if is not yet in that list.
Any help appreciated.
I don't know something like this but you can use a NSDictionary instead of NSArray using the name property as a key. And if you don't want to do that, you can sort the array and make a binary search for element.
I have an NSTableView that I am trying to sort. The data source is an NSMutableArray which contains instances of custom classes.
-(void)tableView:(NSTableView *)tableView sortDescriptorsDidChange: (NSArray *)oldDescriptors
I am using the above so that I can track when the user presses a header of the table. I am using the following method to do the sorting:
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"make" ascending:bool_asc_desc] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [ads_printers_array sortedArrayUsingDescriptors:sortDescriptors];
What I would like to know is how can I pass on the table column which was pressed? This would allow me to change the initWithKey:#"make" (using if statements) so that I can sort according to the header clicked.
Thanks.
P.S. I have tried the following:
if ([ads_rdp_driver_table selectedColumn] == tc_make)
with ads_rdp_driver_table being my NSTableView and tc_make being an NSTableColumn which I have defined. However, I am getting this error:
ISO C++ forbids comparison between pointer and integer
I guess this could work if I could figure out the error.
The selectedColumn method of an NSTableView returns the index of the selected column (not a pointer to it), which is why you are getting the compile error. To get the actual column, try something like this instead (code my contain errors, Im typing verbatim):
NSTableColumn * selectedColumn = (NSTableColumn *)[[tableView tableColumns] objectAtIndex: [tableView selectedColumn]];
NSLog(#"Column identifier: %#", [selectedColumn identifier]);
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];
}
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. :-)
I'm making this program that has an NSTableView with four columns, two of which are make of checkboxes. I'm only trying to get one working right now and I've gotten stuck.
First, here's my relevant code:
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView {
NSString *filePathThree = [[NSBundle mainBundle] pathForResource:#"mydictionary" ofType:#"plist"];
NSData *myDataThree = [[NSData alloc]initWithContentsOfFile:filePathThree];
self.flozzCodeAndName = (NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:myDataThree
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:NULL
errorDescription:NULL];
return [[flozzCodeAndName objectForKey:#"name"] count];
}
- (void)tableView:(NSTableView *)tableView
setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)rowIndex
{
NSButtonCell *cell;
cell = [[NSButtonCell alloc] init];
[cell setButtonType:NSSwitchButton];
[cell setTitle:#""];
[cell setTag:rowIndex];
NSLog(#"%d", [cell tag]);
[cell setCellAttribute:NSCellEditable to:3];
[cell setImagePosition:NSImageOnly];
[cell setState:NSOnState];
NSLog(#"%d", [cell state]);
[havzColumn setDataCell:cell];
[myTableVeew reloadData];
[cell release];
}
- (id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex {
NSString *filePathThree = [[NSBundle mainBundle] pathForResource:#"mydictionary" ofType:#"plist"];
NSData *myDataThree = [[NSData alloc]initWithContentsOfFile:filePathThree];
self.flozzCodeAndName = (NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:myDataThree
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:NULL
errorDescription:NULL];
NSArray *myArray = [flozzCodeAndName objectForKey:[aTableColumn identifier]];
NSString *myStringValue = [myArray objectAtIndex:rowIndex];
return myStringValue;
}
As you can see, I'm using the data source method for this table rather than bindings. The book I read for Cocoa made some checkboxes with tags, but I think they were in an array, so that might not be the best thing to do.
Anyway, when I run this, the debugger will show me the tag (which equals the row) along with the state of the button (1 for all of them because of NSOnState). My problem is that I cannot get the boxes to check and uncheck depending on their state. I read this question: Checkbox on table column won't register click
And then the NSTableView datasource reference. According to Mr. Nozzi in the linked question, it seems to me that an array containing the states for the boxes is needed, so I tried that, setting [cell state] to an NSNumber to get it into an NSMutableArray. I FUBAR'd that and don't think that was right. There are 454 rows in this table (tags go to 453 because of arrays starting at 0), for all four columns.
I also wonder if I should put the cell definition stuff that is in tableview:setObjectValue: into an 'awakeFromNib'. I did put a checkbox button cell in the IB, but I was having problems with it earlier, so I decided to do it programmatically too. During all of these, I did, and still do, have a [myTableVeew reloadData] in the setObjectValue method.
The assistance is appreciated, if any other info is needed, I can get it.
You have two problems: Your data source keeps getting blown away and you're not using the ...objectValue... method properly.
Data Source:
You're blowing away your data source in your -numberOfRowsInTableView: method and replacing it every time the table needs to do a refresh. You'll want to cache (a mutable copy of) your dictionary only when you need to (like at application launch) then only refer to it from the table data source methods. Perhaps you should move it to an instance variable and use proper accessors.
Also, the documentation mentions that, because the data source methods are called very frequently, they should be fast, so from a performance viewpoint alone this is not a good idea. You should only do what it takes to answer the question the delegate method is posing to keep the table responsive with large data sets.
Object Value: You should ONLY be returning the object value from this method (usually an NSNumber object containing the state the checkbox is meant to toggle.
You should set your table column's -dataCell when the view is loaded or at application launch. Even easier: drag a check box cell into the table column in Interface Builder to set that as the data cell without code.
Additional Observation: If you plan to persist the changes to this information in any way, note that you must never rely on the application bundle being writable and should never attempt to overwrite resource files like the one you're loading from your bundle. You'll need to save the information elsewhere, using your bundle copy as a template copy only.