cellForRowAtIndex customised method - ios

In my tableViewController there is a cellForRowAtIndex method which has three possible sources.
Search results from a search bar controller.
Collapsed sections from NSFetchedResultsController.
Expanded sections from NSFetchedResultsController.
At the first option there are no issues.
At the second option, the method creates a new Top Row to announce that the section is expandable. No issues at this option.
At the third option, the selected section expands its rows, but with the issue that the first row is not the expected row, but the new Top Row from the previous option.
Here you have both screenshots:
And that is the method code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
ToDoItem *toDoItem = nil;
//SEARCH RESULTS
if (tableView == self.searchDisplayController.searchResultsTableView)
{
if (cell==nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
}
NSLog(#"Configuring cell to show search results");
toDoItem = [self.searchResults objectAtIndex:indexPath.row];
cell.textLabel.text = toDoItem.todoName;
NSDate *fechaToDO = toDoItem.todoDueDate;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"EEEE, dd MMMM YYYY"];
NSString *fechaToDo = [dateFormatter stringFromDate:fechaToDO];
NSString *valorSomeDay = toDoItem.isSomeDay;
if ([valorSomeDay isEqualToString:#"issomeday"]){
cell.detailTextLabel.text = #"Someday";
}
else {
cell.detailTextLabel.text = fechaToDo;
}
}
//COLLAPSABLE/EXPANDABLE
else
{
if ([self tableView:tableView canCollapseSection:indexPath.section])
{
if (!indexPath.row)
{
// first row
cell.textLabel.text = #"Expandable"; // only top row showing
if ([expandedSections containsIndex:indexPath.section])
{
cell.accessoryView = [DTCustomColoredAccessory accessoryWithColor:[UIColor grayColor] type:DTCustomColoredAccessoryTypeUp];
}
else
{
cell.accessoryView = [DTCustomColoredAccessory accessoryWithColor:[UIColor grayColor] type:DTCustomColoredAccessoryTypeDown];
}
}
else
//FETCHED RESULTS
{
cell.accessoryView = nil;
ToDoItem *todoitem = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = todoitem.todoName;
NSDate *fechaToDO = todoitem.todoDueDate;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"EEEE, dd MMMM YYYY"];
NSString *fechaToDo = [dateFormatter stringFromDate:fechaToDO];
NSString *valorSomeDay = todoitem.isSomeDay;
if ([valorSomeDay isEqualToString:#"issomeday"]){
cell.detailTextLabel.text = #"Someday";
}
else {
cell.detailTextLabel.text = fechaToDo;
}
}
}
}
return cell;
}
Any help is welcome to be able to show the first core data object and not the invented top row when expanding the sections.
It is also request to propose me another solution that could avoid the use of this invented top row when sections are collapsed..
Thank you.

You need to keep a mapping of your original index paths and the new index paths after you inserted the rows that resulted from the expansion.
If you implement it properly, you should be able to get the new index path from the old index path. Ex: item 3 is now 5 after row 1 was expanded and added 2 subrows.
EDIT: how to implement the mapping
You don't need to be experienced in iOS to implement it. I am not very experienced and I implemented it.
Create an array whose keys will be the index paths as the table view sees them (ie. 0 to 9 if you have 10 total rows), and the values are the index paths (or anything you want, really), that tell you what row in your datasource you are referencing (ie. 0, 1, 2, 3, 3.0, 3.1, 3.2, 4, 5, 6 if row 3 has 3 subrows currently expanded).
Whenever you expand (add) rows, insert them in that dictionary at the right place: in this example, when you expanded row 3, insert all 3 rows at index 4, 5, 6.
Whenever you collapse (remove) rows, delete them from that array.
Example:
0 => 0
1 => 1
2 => 2
3 => 3
4 => 4
5 => 5
6 => 6
becomes
0 => 0
1 => 1
2 => 2
3 => 3
4 => 3.0
5 => 3.1
6 => 3.2
7 => 4
8 => 5
9 => 6
when you expand row 3

Related

YapDatabase sorting confounds editing update

In a UITableViewController I use YapDatabase with Mantle sorting the following way:
YapDatabaseViewSorting *viewSorting = [YapDatabaseViewSorting withObjectBlock:^NSComparisonResult(NSString *group, NSString *collection1, NSString *key1, XQBuilding *object1, NSString *collection2, NSString *key2, XQBuilding *object2) {
if ([group isEqualToString:XQBuildingsViewGroupName]) {
return [[object1 name] compare:[object2 name] options:NSNumericSearch];
}
if ([group isEqualToString:XQPicturesGroup]) {
return [[object1 updatedAt] compare:[object2 updatedAt]];
}
return NSOrderedSame;
}];
YapDatabaseViewOptions *options = [[YapDatabaseViewOptions alloc] init];
options.isPersistent = NO;
YapDatabaseView *databaseView = [[YapDatabaseView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:#"" options:options];
Although the used option I have sometimes (when an edited name changes the edited item order in the list) incorrect indexPath on reading:
- (UITableViewCell*)editableTableView:(UITableView *)tableView simpleCellForIndexPath:(NSIndexPath *)indexPath
{
__block XQBuilding *building;
__block NSNumber *gla;
[self.readConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
building = [[transaction ext:XQBuildingListYapName] objectAtIndexPath:indexPath withMappings:self.tableViewAnimator.viewMappings];
gla = [building glaWithTransaction:transaction];
}];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"BuildingCell"];
cell.textLabel.text = building.name;
cell.detailTextLabel.text = [NSString stringWithFormat: #"%# sqm", formatDecimal(gla)];
return cell;
}
i. e. building in such case is a different one than was edited. How to get a correct indexPath according to actual sorting?
There are plenty of things that should be correctly coded to make it work properly!
As I can see from your code you use YapDatabaseViewMappings. Do you use LongLivedReadTransactions and do you subscribe to YapDatabaseModifiedNotification to properly modify the table on database changes?
There is an simplified example that shows how to use all these things to update your UITableView in real time right after database is updated.

How to get TimeZone abbreviation

I want to create a list of time zones.
But I got the following error at timeZone.abbreviation.
-[__NSCFString abbreviation]: unrecognized selector sent to instance 0x19cb80b0
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
// Configure the cell...
NSTimeZone *timeZone = [[NSTimeZone knownTimeZoneNames] objectAtIndex:indexPath.row];
cell.textLabel.text = timeZone.abbreviation; // <- Error Here
cell.detailTextLabel.text = timeZone.description;
cell.accessoryType = (timeZone == self.timeZone)? UITableViewCellAccessoryCheckmark :UITableViewCellAccessoryNone;
return cell;
}
I tried to search on the internet but I cannot find the solution so far.
Please give me some advice.
Thanks in advance.
Try something like this:
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:[[NSTimeZone knownTimeZoneNames] objectAtIndex:indexPath.row]];
cell.textLabel.text = timeZone.abbreviation; // <- Error Here
cell.detailTextLabel.text = timeZone.description;
Your current code gets an array of NSStrings, not NSTimeZones. This should give you an NSTimeZone, and then you can get the abbreviation property on it.
as mentioned here: https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/classes/NSTimeZone_Class/Reference/Reference.html#//apple_ref/occ/clm/NSTimeZone/knownTimeZoneNames
[NSTimeZone knownTimeZoneNames]
returns a list of String ids of the timezones - so your timezone object actually contains a NSString not a NSTimeZone object. So you have to do:
NSString *timeZoneID = [[NSTimeZone knownTimeZoneNames] objectAtIndex:indexPath.row];
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:timeZoneID];
to get the real timezone object to call abbreviation on

custom UItableView not displaying correctly on ios8

I made a custom UITableViewCell and when I display it, i have this result (I'm running xcode 6 and iOS 8 beta 1 on an iPhone 5.)
http://imgur.com/2MyT0mF,Nx1o4bl#0
And when I rotate my device, then rotate back to portrait, everything becomes correct.
http://imgur.com/2MyT0mF,Nx1o4bl#1
Note that when I was using xcode 5 to compile my app, I had no problems.
Code used in cellForRowAtIndexPath:
BGTCoursCell1 *cell = (BGTCoursCell1 *)[tableView dequeueReusableCellWithIdentifier:#"Cell"];
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"BGTCoursCell1" owner:self options:nil];
cell = [nib objectAtIndex:0];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm"];
NSLocale *locale = [NSLocale currentLocale];
[dateFormatter setLocale:locale];
if (indexPath.section == 0)
{
BGTCours *cours = [[currentDay coursMatin] objectAtIndex:indexPath.row];
cell.matiereLabel.text = [cours matiere];
cell.salleLabel.text = [cours salle];
cell.heureLabel.text = [NSString stringWithFormat:#"De %# à %#", [dateFormatter stringFromDate:[cours beginDate]], [dateFormatter stringFromDate:[cours endDate]]];
}
else
{
BGTCours *cours = [[currentDay coursAprem] objectAtIndex:indexPath.row];
cell.matiereLabel.text = [cours matiere];
cell.salleLabel.text = [cours salle];
cell.heureLabel.text = [NSString stringWithFormat:#"De %# à %#", [dateFormatter stringFromDate:[cours beginDate]], [dateFormatter stringFromDate:[cours endDate]]];
}
return cell;
Thanks !
You need to return a CGFloat from the tableView:heightForRowAtIndexPath: . The error you are seeing will appear if you return a float from the same method:
//causes the bug
-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
//...
}
//fixes the bug
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return tableView.rowHeight; // whatever
}
This question has an accepted answer, but I have the same problem which was not fixed by the accepted answer which tells us to change float to CGFloat.
I found out that people who has code based on table's rowHeight may have this problem too. In iOS8 it is important to look at estimatedRowHeight not just rowHeight.
As I was using these variables in my programmatically generated UITableView, fixing those variables solved the problem.
I might be a bit late to the party but I've encountered exactly same problem with iOS 8 beta 2 and xCode 6 beta 2 today(26-June-2014), I see they still haven't fixed the bug.
There's also another bug with xCode 6 beta, that is my app can no request the GPS location properly - it simply won't ask for the permission. This is also solved by switching back to xCode 5. I think at the moment is more viable to use xCode 5 to build and test your app.
try reusing the cell, like, change:
BGTCoursCell1 *cell = (BGTCoursCell1 *)[tableView dequeueReusableCellWithIdentifier:#"Cell"];
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"BGTCoursCell1" owner:self options:nil];
cell = [nib objectAtIndex:0];
to
static NSString *CellIdentifier = #"Cell";
static NSString *CellNib = #"BGTCoursCell1";
BGTCoursCell1 *cell = (BGTCoursCell1 *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if( cell == nil ) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellNib owner:self options:nil];
cell = [nib objectAtIndex:0];
}
...
return cell;
i had a slightly similar problem and fixed it by using
cell.clipsToBounds = YES

Multiple methods named 'location' found with mismatched result, parameter type, or attributes

I have read the other questions concerning multiple methods but still do not know how to fix my code. I would be grateful for help with this. I have put a * around the statement where the error occurs.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"eventCell";
EQCalendarCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[EQCalendarCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
cell.titleLabel.text = [[self.eventsList objectAtIndex:indexPath.row] title];
cell.locationLabel.text = [[self.eventsList objectAtIndex:indexPath.row] location];
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat: #"dd-MM-yy HH:mm"];
NSString *startDateString = [df stringFromDate:[[self.eventsList
objectAtIndex:indexPath.row] startDate]];
cell.startDateLabel.text = startDateString;
NSString *endDateString = [df stringFromDate:[[self.eventsList
objectAtIndex:indexPath.row] endDate]];
cell.endDateLabel.text = endDateString;
return cell;
}
Thanking you in advance for your help.
Casting the result of retrieving the object from the self.eventsList collection should solve the problem. E.g.:
cell.locationLabel.text = [((MyClassName *)[self.eventsList objectAtIndex:indexPath.row]) location];
Replacing MyClassName with the name of the class in the collection.
You need to cast [self.eventsList objectAtIndex:indexPath.row] to the relevant type so that the compiler knows what data type your are dealing with.
Without seeing how your self.eventList list is populated it's not possible to tell you the solution exactly, but your line should be replaced with something like this (split into two lines for clarity, but you could use a cast instead of a variable to keep it on one line)
MyEventClass *event = [self.eventsList objectAtIndex:indexPath.row];
cell.locationLabel.text = [event location];
You need to cast EKEvent class. This will solve your problem...
EKEvent *event = [self.eventlist objectAtIndex:indexPath.row];
NSString *placeStr=[event location];
In one scenario, if the factory method that creates the object has return type "id" then the compiler will check the method signature in all the classes. If compiler find the same method signature in more than one class then it will raise the issue. So replace the return type "id" with "specific class name".

UITableView cell subtitles from plist data

I want to add some subtitles to my UITableView cells (where I display continents, countries, other data). Already populated my first table with continents. Now I want to add numbers of countries as subtitle in every continent cell. I added:
int numberEurope;
numberEurope = [europe count]; //this works fine
int numberAfrica;
numberAfrica = [africa count]; //this works fine
NSNumber *myNum1 = [NSNumber numberWithInt:numberEurope];
NSNumber *myNum2 = [NSNumber numberWithInt:numberAfrica];
NSArray *myArray = [NSArray arrayWithObjects: myNum1, myNum2, nil];
cell.textLabel.text = ContinentName;
cell.detailTextLabel.text = [[NSString alloc] initWithFormat:#"%# countries", myArray];
return cell;
But the subtitles are the same in every cell, the full array is shown: ( 2, 2) countries instead of 2 countries in the first cell and 2 countries in the second one. What am I doing wrong? The plist I use is screenshot here: Cannot Feed UITableView with .plist
You have to select proper value based on indexPath:
cell.detailTextLabel.text = [[NSString alloc] initWithFormat:#"%d countries", [[myArray objectAtIndex:[indexPath row]] intValue]];

Resources