iOS 6 table scrolling causing issues - uitableview

There are two sections in my table view, one for test status and one for results.
The third row in the first section gets changed upon the test completing. During the test it documents the percentage complete and after it changes to showing if any problems have been detected.
The color of the text also changes to indicate bad/ok/good. For this particular test there are about 20 rows of results.
The problem being that within those 20 results two cells are being treated like the third cell in section one and turning red.
Now I assume as I scroll the table reloads which must mean my code is incorrect and has only be shown up by a test with lots of results. Any help would be great. I suspect it is the code below:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
UITableViewCell *serverLoc = [tableView dequeueReusableCellWithIdentifier:#"speedCell"];
// UITableViewCell *switchCell = [tableView dequeueReusableCellWithIdentifier:#"switchCell"];
switch (indexPath.section) {
case 0:
switch (indexPath.row) {
case 0:
serverLoc.textLabel.text = #"Test location:";
serverLoc.detailTextLabel.text = testLocation;
serverLoc.selectionStyle = UITableViewCellSelectionStyleNone;
serverLoc.userInteractionEnabled = NO;
break;
case 1:
serverLoc.textLabel.text = #"Status:";
serverLoc.detailTextLabel.text = statusText;
serverLoc.selectionStyle = UITableViewCellSelectionStyleNone;
serverLoc.userInteractionEnabled = NO;
break;
case 2:
if ([TestEnded isEqualToString:#"no"]) {
serverLoc.textLabel.text = #"Progress";
serverLoc.selectionStyle = UITableViewCellSelectionStyleNone;
serverLoc.userInteractionEnabled = NO;
serverLoc.detailTextLabel.text = [NSString stringWithFormat:#"%ld%%", (long)progressInt];
break;
}
else {
serverLoc.selectionStyle = UITableViewCellSelectionStyleBlue;
serverLoc.userInteractionEnabled = YES;
serverLoc.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
serverLoc.textLabel.text = #"Problems Detected:";
serverLoc.detailTextLabel.text = [NSString stringWithFormat:#"%ld", (long)problemsDetected];
if (problemsDetected == 0) {
serverLoc.textLabel.textColor = [UIColor colorWithRed:0.0 / 255 green:102.0 / 255 blue:51.0 / 255 alpha:1.0];
serverLoc.detailTextLabel.textColor = [UIColor colorWithRed:0.0 / 255 green:102.0 / 255 blue:51.0 / 255 alpha:1.0];
}
else if (problemsDetected == 1) {
serverLoc.textLabel.textColor = [UIColor colorWithRed:226.0 / 255 green:232.0 / 255 blue:52.0 / 255 alpha:1.0];
serverLoc.detailTextLabel.textColor = [UIColor colorWithRed:226.0 / 255 green:232.0 / 255 blue:52.0 / 255 alpha:1.0];
}
else {
serverLoc.textLabel.textColor = [UIColor redColor];
serverLoc.detailTextLabel.textColor = [UIColor redColor];
}
}
break;
}
break;
I wasn't sure if it would work when I first implemented it. It "did" but clearly I wasn't needing to scroll enough to unveil the bug.
Thanks in advance for any pointers

The code here looks fine... It sounds like your table is reusing the cell set up for this particular path but not returning the values to default before reusing the cell. (e.g. in the section == 1 case: severLoc.textLabel.textColor = [UIColor blackColor];) This will also show up if case 0,0 and 0,1 end up with a 0,2 cell when grabbing a reused cell.

Related

UICollectionViewCell strange behaviour. Cells not updating layer correctly

I have a UICollectionView with multiple cells on which I add a CAGradient layer representing the color of each cell. The problem is that when I push another view controller on top of the present view controller and then pop the second view controller, my collection view cells shift colors in a random order. To give you an idea I have attached screenshots.
This is the original order of the cells. This is correct
This happens when I push another view controller and then return
You can see that the cells shifted their colors even though I changed nothing.
This is the code I use to initialize the cells.
[collectionview reloadData] is called in -viewWillAppear so the cells load every time the view appears
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
[self filterRecords];
MyCell *cell = (MyCell*)[collectionView dequeueReusableCellWithReuseIdentifier:#"ProspectCVCell" forIndexPath:indexPath];
for (int i = 0; i < [eventsSortedForAddedDate count]; i++)
{
Events* event = (Events*)[eventsSortedForAddedDate objectAtIndex:i];
if ([event.activityLevel.activityName isEqualToString:#"None"])
continue;
color = [[event activityLevel] color];
if (![color isEqualToString:#"#FFCCCCCC"])
break;
else
color = nil;
}
if (!([color length] > 0) || color == nil)
{
color = #"#FFCCCCCC";
}
UIColor* myColor = [self getUIColorObjectFromHexString:color alpha:.9];
//cell.backgroundColor = myColor;
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = cell.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[myColor CGColor], (id)[[UIColor whiteColor] CGColor], nil];
gradient.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0f], [NSNumber numberWithFloat:0.85f], nil];
//[cell.layer insertSublayer:gradient atIndex:0];
[cell.layer insertSublayer:gradient atIndex:0];
cell.prospectImageView.layer.shadowColor = [UIColor blackColor].CGColor;
cell.prospectImageView.layer.shadowOffset = CGSizeMake(0, 3.0);
cell.prospectImageView.layer.shadowRadius = 3.0;
cell.prospectImageView.layer.shadowOpacity = 0.8;
cell.cellLabel.text = p.displayName;
cell.layer.borderWidth = 0.5f;
cell.layer.borderColor = [UIColor whiteColor].CGColor;
return cell;
}
There is nothing wrong in the way I get the color, I have debugged multiple times and checked that the colors I get are the correct ones.
If I do
cell.backgroundColor = myColor;
The cells do not change their colors and function as expected. So I am pretty sure that the problem lies with the CAGradientLayer.
I have tried everything that I could think of but nothing seems to work!
Try this once,
[cell.layer insertSublayer:gradient atIndex:[[cell.layer sublayers] indexOfObject:[[cell.layer sublayers] lastObject]]];
instead of
[cell.layer insertSublayer:gradient atIndex:0];
Update :
As asked in comment, Apple doc states about dequeueReusableCellWithReuseIdentifier,
Call this method from your data source object when asked to provide a new cell for the collection view. This method dequeues an existing cell if one is available or creates a new one based on the class or nib file you previously registered.
And second thing you can also remove previous layers and then can add new one at index 0. it is better because it not increase number of layer which is not necessary.

How do you efficiently detect collision for UIView and UICollectionViewCells?

I have a markerview (uiview) that forever and repeatedly goes through a bunch of uicollectionviewcells I want to detect and possibly change the colour of those cells detected and reset it back when the marker goes past the cell.
Heres what i have so far :
View did appear :
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(displayLinkHandler)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
- (void) displayLinkHandler {
id presentationLayer1 = marker.markerLine.layer.presentationLayer;
for (NSIndexPath* cellPath in [seqcollectionview indexPathsForVisibleItems]) {
UICollectionViewCell *cell = [seqcollectionview cellForItemAtIndexPath:cellPath];
id presentationLayer2 = cell.layer.presentationLayer;
BOOL nowIntersecting = CGRectIntersectsRect([presentationLayer1 frame], [presentationLayer2 frame]);
// I'll keep track of whether the two views were intersected in a class property,
// and therefore only display a message if the intersected state changes.
if (nowIntersecting != wasIntersected) {
if (nowIntersecting && cell.selected) {
NSLog(#"row is: %d section is: %d", [cellPath row], [cellPath section]);
cell.backgroundColor = [UIColor whiteColor];
}else{
cell.backgroundColor = [UIColor colorWithRed:1 green:0.855 blue:0.741 alpha:1];
}
wasIntersected = nowIntersecting;
}
}
}
So far this works but the delay for when the color is meant to change is just too much !
Cheers

ios - Making a Calendar. Year view is really slow. Lots of subviews

I'm making a calendar and the year view in particular, and understandably, is quite slow when coming into view.
The year view is simply a UICollectionView, and each month is a custom UICollectionViewCell XIB. The month has a title UILabel, and 7 dayName UILabels, and 42 dayNumber UILabels.
That's a total of 50 UILabels per month X 12. Aieesh...
How do other iOS calendar apps handle showing so many subviews without degradation in performance? The performance hit is mainly when swiping this view to the next and previous years. As the view is sliding in, the collection view is propagating the cells, causing the slow-down. I could hold off on populating the cells until the scrolling has stopped, but I'd really like to not show a white view as it's scrolling into the window.
Edit* As requested - my cellForRow method:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
NSIndexPath *indexPathPlusOne = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section];
EICalendarYearViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"EICalendarYearViewCell" forIndexPath:indexPath];
if (_datesLoaded == YES)
{
//Get First Day in Month
NSMutableArray *arrayForDatesInMonth = [_dictHoldingArrayForEachMonth objectForKey:[NSNumber numberWithLong:indexPathPlusOne.row]];
NSDate *firstDayInMonth = arrayForDatesInMonth[0];
NSDateComponents* comp = [[NSCalendar currentCalendar] components:(NSCalendarUnitWeekday) fromDate:firstDayInMonth];
//setting month label
switch (indexPathPlusOne.row) {
case 1:
cell.labelTitle.text = #"January";
break;
case 2:
cell.labelTitle.text = #"February";
break;
case 3:
cell.labelTitle.text = #"March";
break;
case 4:
cell.labelTitle.text = #"April";
break;
case 5:
cell.labelTitle.text = #"May";
break;
case 6:
cell.labelTitle.text = #"June";
break;
case 7:
cell.labelTitle.text = #"July";
break;
case 8:
cell.labelTitle.text = #"August";
break;
case 9:
cell.labelTitle.text = #"September";
break;
case 10:
cell.labelTitle.text = #"October";
break;
case 11:
cell.labelTitle.text = #"November";
break;
case 12:
cell.labelTitle.text = #"December";
break;
default:
break;
}
//setting number of days in previous month to show.
long previousMonthDays = 0;
if (comp.weekday == 7)
{
previousMonthDays = 0;
}
else
{
previousMonthDays = comp.weekday - 1;
}
for (int i = 0; i < cell.arrayOfDayLabels.count; i++)
{
UILabel *labelDay = cell.arrayOfDayLabels[i];
//previous Month days
if (i <= previousMonthDays)
{
labelDay.text = #" ";
labelDay.textColor = [UIColor colorWithWhite:0.8 alpha:1.000];
}
//Actual days in current month
if (i >= previousMonthDays && i < previousMonthDays + arrayForDatesInMonth.count)
{
NSDate *dateForDay = arrayForDatesInMonth[i - previousMonthDays];
NSDateComponents *compsForDay = [[NSCalendar currentCalendar] components:NSCalendarUnitDay fromDate:dateForDay];
labelDay.text = [NSString stringWithFormat:#"%ld", (long)compsForDay.day];
labelDay.textColor = [UIColor darkGrayColor];
}
else
{
labelDay.text = #" ";
labelDay.textColor = [UIColor colorWithWhite:0.8 alpha:1.000];
}
}
}
else
{
for (int i = 0; i < cell.arrayOfDayLabels.count; i++)
{
UILabel *labelDay = cell.arrayOfDayLabels[i];
labelDay.text = #" ";
}
}
cell.layer.shouldRasterize = YES;
[cell.layer setRasterizationScale:3.0f];
return cell;
}
For rmaddy, a screenshot of the methods causing majority of slow-down.

UICollectionView cell borders in a flow layout

I'm using UICollectionView to lay out a bunch of cells that are sectioned by first letter of their title. Each cell should have a very thin border around it, and the section headers should have borders above and below. Here's my current prototype:
I achieve the current appearance with the following rules:
Stroke the right and bottom edge of each cell.
Stroke the bottom edge of each section heading.
This is very close to what I want, but there are two defects:
If the line before a section heading isn't full, then the border along the top of the heading stops short of the right edge of the screen.
It's not visible in this screenshot, but if a line is full, the right border of the last cell in the line is still drawn, which looks a little odd against the edge of the screen.
My best idea to fix this is to somehow tell each cell if it's in the last row of a section or the last cell in a row; then the cell would turn off the offending borders, section headings would draw a top border as well as a bottom, and everything would be hunky-dory. I don't know how to achieve that, though.
Any thoughts on how to manage that, or another way to get the look I'm going for? I'm currently using a UICollectionViewFlowLayout.
I ended up subclassing UICollectionViewFlowLayout and applying several heuristics after the flow layout had calculated the attributes for each cell:
If center.y is equal to center.y of the last item in the section, the cell is in the last row of the section.
If CGRectGetMaxY(frame) is equal to CGRectGetMaxY(self.collectionView.bounds), then the cell is agains the right edge of the collection view.
I then stored the results of these calculations in a subclass of UICollectionViewLayoutAttributes, and wrote a UICollectionViewCell subclass whose -applyLayoutAttributes: method would adjust the borders its background view draws based on the additional properties.
I've put the whole mess into a fairly enormous gist so you can see exactly what I did. Happy hacking.
My best idea to fix this is to somehow tell each cell if it's in the last row of a section or the last cell in a row; then the cell would turn off the offending borders, section headings would draw a top border as well as a bottom, and everything would be hunky-dory. I don't know how to achieve that, though.
What you describe is more or less what I did in a similar scenario. I added a border property to my cell:
typedef NS_OPTIONS(NSInteger, TLGridBorder) {
TLGridBorderNone = 0,
TLGridBorderTop = 1 << 0,
TLGridBorderRight = 1 << 1,
TLGridBorderBottom = 1 << 2,
TLGridBorderLeft = 1 << 3,
TLGridBorderAll = TLGridBorderTop | TLGridBorderRight | TLGridBorderBottom | TLGridBorderLeft,
};
#interface TLGridCellView : UIView
#property (nonatomic) TLGridBorder border;
#end
Then I set the border in my view controller's cell configuration:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
TLGridCellView *cell = ...;
if (indexPath.item == self collectionView:collectionView numberOfItemsInSection:indexPath.section - 1) {
cell.border = TLGridBorderLeft;
} else {
cell.border = TLGridBorderLeft | TLGridBorderRight;
}
return cell;
}
I solve this problem in a simple way. I didn't add boarder to cell, instead I add a label with boarder into the cell. For the first column, the frame of the label is the same with the cell. For the other label, I set the x coordinate -0.5 to make their boarder overlap. Hope it helps.
Here is the code:
- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"cell" forIndexPath:indexPath];
// Then use it
UILabel *label = nil;
if (cell.contentView.subviews.count > 0) {
label = cell.contentView.subviews[0];
} else {
label = [[UILabel alloc] init];
}
label.text = #"北京";
[label setTextAlignment:NSTextAlignmentCenter];
[label setFont:[UIFont systemFontOfSize:14]];
[label setCenter:cell.contentView.center];
CGRect frame = label.frame;
if (indexPath.row%4 == 0) {
frame.origin.x = 0;
} else {
frame.origin.x = -0.5;
}
frame.origin.y = 0;
frame.size.width = self.collectionView.frame.size.width / 4;
frame.size.height = self.collectionView.frame.size.height / 9;
[label setFrame:frame];
if (cell.contentView.subviews.count == 0) {
[[cell contentView] addSubview:label];
}
label.layer.borderWidth = 0.5;
label.layer.borderColor = [[UIColor lightGrayColor] CGColor];
cell.backgroundColor = [UIColor whiteColor];
return cell;
}

Trying to render a large columnar grid within a UITableView

I have created a sort of GRID using uitableView. For that I have taken various labels for showing grid type line I am using line image. So by considering Grid my tableview is having around 88 columns.
My issue is when I scroll it down, I am getting jerky effect. Its performance s very poor. I am creating around 108 label and each row is having 88 labels and 86 image views.
What step do I need to follow to improve scrolling performance???
I was using clearColor for label background. But later on I have removed those background colors.
Having read your problem again, I am thinking you are scrolling horizontally and vertically with an incredibly wide tableview. If this is the case then you need to switch to UIScrollView and attach each item to this view. UIScrollView will only load the views that are visible and provide the kind of scroll performance you desire.
It will be important to avoid this:
// Using ARC, therefore no release on the UILabel
- (void) viewDidLoad {
self.scrollView.backgroundColor = [UIColor whiteColor];
UIFont *font = [UIFont systemFontOfSize:24];
CGSize size = [#"10000:10000" sizeWithFont:font];
_scrollView.contentSize = CGSizeMake(10000 * size.width, 10000 * size.height);
for (int y = 0; y < 10000; y++) {
for (int x = 0; x < 10000; x++) {
NSLog(#"Loading: %d, %d", x, y);
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(x * size.width, y * size.height, size.width, size.height)];
label.font = font;
label.textAlignment = UITextAlignmentRight;
label.text = [NSString stringWithFormat:#"%d:%d", x, y];
[_scrollView addSubview:label];
}
}
}
While this will eventually load it will take ages while it loads up all of these labels and it will consume a ton of memory. You want to lazy load this view just like a TableView. I will write up an example this evening.
You need to give the table cell a reuse identifier. Otherwise, each time you create a completely new cell and consume more and more memory.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *const kReuseIdentifer = #"ReuseIdentifer";
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kReuseIdentifier] autorelease];
}
return cell;
}

Resources