One section per row in a UITableView - objective-c

I am trying to create a UITableView that has one row per section to give the look of a message chat table. How would one achieve this? Using
[array count]
In different combinations in the two delegate methods below either return separate sections of the same cell or multiple rows in each section.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [array count];}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;}

That looks like it should do what you want. Then you need to use the section property from the index path instead of the row when picking out an array element in cellForRowAtIndexPath:.

To fix it:
[Array objectAtIndex:indexPath.section];
Use "section" instead of "row".

Related

Reordering UITableViewCells with Core Data TableView

I have a UITableView that is working with Core data for the user o add their own Cells... but i wanted them to be able to reorder the TableViewCells the way they wanted... i use the code now BUT whenever the reorder it, say i went to add another cell, it would return the the regular state... images below if you're lost...
Below is the code:
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) // Don't move the first row
return NO;
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
id ob = [_devices objectAtIndex:destinationIndexPath.row];
[_devices replaceObjectAtIndex:destinationIndexPath.row withObject:[_devices objectAtIndex:sourceIndexPath.row]];
[_devices replaceObjectAtIndex:sourceIndexPath.row withObject:ob];
}
You should add an order property on your entity so that your sort order is persistent.
Then your data source for the table view needs to be sorted by that order key.
It is likely that your _devices array is getting reinitialized with the results of your Core Data fetch request and therefore your modification to the order is lost.
Updated for comment
Assuming you have an entity named Device that is a sub-class of NSManagedObject with a property named order you could do the following.
NSSortDescriptor *orderSortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"order" ascending:YES];
[devicesFetchRequest setSortDescriptors:#[orderSortDescriptor]];
Then when you execute your fetch request and capture the results in your _devices array they will be sorted by the order property.
Then in - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath you will need update the order property on the affected entities.
If you move the device from position 5 to position 2 then you need to update the entities from position 2-4 by +1 and the moved entity to 2. This could get inefficient for large data sets quickly but for small data sets it should perform fine.

Retrieving Data in A NSTableView

I have a three column Table View populated by two NSMutableDictionaries which share the same keys (ie key | value1 | value2 ) with dict1(key,value1) and dict2(key,value2).
I want to manually enter data in the third column and create the key/value objects in dict2. But when I do that, my code picks the wrong key :S
Here's the code for the delegate :
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
if ([[aTableColumn identifier] isEqualToString:#"value2"])
{
[dict2 setValue:anObject forKey:[[[aTableView tableColumnWithIdentifier:#"key"] dataCellForRow:rowIndex] stringValue ]];
}
}
Any idea ?
EDIT : I want to complete my question : how can you retrieve the data displayed in a cell ?
I see no other way to use NSMutableDictionnaries with TableViews, because [dict allKeys] does not give the keys in the same order that the one that is used to display it ! And stocking a version of [dict allKeys] just seems redundant and stupid.
I think this delegate function you're using is used for showing the data in the tableview with the format data source. It's used for setting, not retrieving.
You may use - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath to set the data source for tableview.

TableView doesn't allow for 'section' in dictionary

I want to loop through a list of json items to use in my sectioned tableView. For this I would like to restructure the data to have a section->array setup, where array contains an array of sessions.
First of all, I don't know if this is the preferred way to go, there may be easier ways. I keep getting the error that I am not allowed to use 'section' as an identifier in the dictionary. Moreover, when I use something else than a 'section' the dictionary keeps getting overridden.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString *day = _json[#"days"][3];
NSString *key;
NSUInteger count = 0;
NSMutableArray *sessionList = [[NSMutableArray alloc] init];
NSArray *timeslotsSorted = [[_json[#"schedule"][day] allKeys]
sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSArray *locationsSorted = [[_json[#"schedule"][day][timeslotsSorted[section]] allKeys]
sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
for (key in locationsSorted) {
NSDictionary *temp = _json[#"schedule"][day][timeslotsSorted[section]][key];
if ([temp isKindOfClass:[NSDictionary class]]) {
[sessionList addObject:temp[#"title"]]; //test array
count++;
}
}
_sessionDict = #{
section: sessionList
};
return count;
}
You are doing all the work to build your data structure in the wrong place. Lets say there are 10 sections in your data. This will call the tableView: numberOfRowsInSection method 10 times which makes this a pretty inefficient place to do much work. You will also have to implement the method that returns the number of sections to show, and the method to display each individual row.
I would build my data structures in the viewWillLoad method and then store it locallaly and reuse it in all the tableView methods.
First, this is what NSInteger is:
typedef int NSInteger;
You must wrap it into an object. Something like:
[NSNumber numberWithInteger:section]
And than add it to your dictionary.
i don't really know that your timeslotsSorted and locationsSorted contain right items, but lets say they do. I would recommend you to have this sorting before - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section and - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; are called.
Lets say, you received JSON, so you have to parse it as you do, and then call [tableView reloadData] or reload visible cells with animations.
and then your tableView data source methods will be called and you will do something like:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [sessionList count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSString* key = [self.sessionList objectAtIndex:section];
return [[self.secions objectForKey:key] count];
}
and don't forget to make strong properties for self.sections and self.sessionList

Obtain a stringValue from NSTableView

I have a simple NSTableView which I have loaded with data from a NSMutableArray. When I select a row (entry) in the tableView and modify it, I can find the row index, what I cannot get is the edited entry out as a string so I can modify the array. I can find lots of information on selecting rows, etc., but not on how to get the actual modified string. I keep thinking this should be real simple. Help please.
Part of my code:
- (IBAction)toDoEdit:(id)sender // Accept the edited data
{
NSString *toDoItem = [[toDoTableCell:toDoTableView dataCellFoTableColumn:0 row:rowToBeEdited] stringValue];
// I get the error "dataCellForTableColumn' method cannot be found.
[toDoArray replaceObjectAtIndex:rowToBeDeleted withObject:toDoItem];
[toDoTableView reloadData];
[toDoTableView deselectRow:rowToBeDeleted];
}
~~~~~~~~~~~
// This method should return the cell value of the selected row
- toDoTableCell:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
return toDoTableCell; // No errors here but toDoTableCell is nil.
}
The 'Add' data to tableView works, 'Delete' data from tableView works, I just cannot get the edited data out of tableView so I can reload the data with the corrections.
What you are looking for is an NSTableView Delegate method:
- (NSCell *)tableView:(NSTableView *)tableView dataCellForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
This will return the NSCell that is in the row and column that you specify. From the NSCell you should be able to extract the value that you need. Depending on how you are using your NSCell you would either call [cell stringValue] or [cell objectValue].
Try this:
– tableView:setObjectValue:forTableColumn:row:
in - NSTableViewDataSource Protocol Reference
--- Edited based on comment ---
Above method is called whenever user tries to edit a table row, it also provides user with changed value as parameter. If you are trying to edit the row in table itself then it should serve your purpose. You can simply check the objectValue obtained as parameter and verify if it is correct or not. In case it is incorrect you can modify the obtained value and set it in todoArray.
Briefly:
- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
{
// below case is an example, you can add your own
if([anObject isEqualToString:#"Incorrect"])
{
anObject = #"Correct";
}
// Considering todoArray is array of dictionary items containing keys as table-column identifiers
NSMutableDictionary *originalData = [todoArray objectAtIndex:rowIndex];
[originalData setValue:anObject forKey:[aTableColumn identifier]];
[toDoTableView reloadData];
}
To get the value being edited you can simply use this code in above method, before setting the new value:
NSString *editedValue = [[todoArray objectAtIndex:rowIndex] valueForKey:[aTableColumn identifier]];
Hope this helps :)
It is simple. Read up on Cocoa Bindings and NSArrayController.
Take a look at the NSTableView methods selectedColumn, selectedColumnIndexes, selectedRow and selectedRowIndexes. I guess they should provide you with the needed information.
Now you can query the model, i.e. the array, for the data you need.

Obj-C, have a number of tableviews, need to quickly add code to say not data found?

I have a number of views with UITableViews and I'd like to quickly add code to each so that I can add a row with a simple label saying that there was no data to be shown.
Whats making things a little bit more complicated is that in some cases I'm using custom cells.
I don't want to have to change my populate data array functions, but I can't think of a way to add this feature to a lot of tables quickly.
Can anyone show me what to do ?
In the tableView:numberOfRowsInSection: data source method, get the number of rows as normal. Then, if the number is 0, return 1 instead. In the tableView:cellForRowAtIndexPath: method, perform the same test. If there is no data, use a different cell and set it up for your message.
The following example assumes that you have 1 section and that your data is an NSArray instance variable named theArray which contains strings.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger num = theArray.count;
return (num ? num : 1);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
if(theArray.count == 0) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
cell.textLabel.text = #"No Data Found";
return cell;
}
// Normal processing here
}