iPhone 4 iOS 7 Tableview lag when scrolling - ios

I'm having some trouble with lag in my UITableview.
There aren't any problems on an iPhone 5 and after I started caching images in an NSDictionary, the iPhone 4 with iOS 6 became very responsive.
The problem remains on an iPhone 4 with iOS 7 however.
I've read trough some threads here with tips about making views opaque which I did but it didn't help. All my views except the labels are opaque (because if they are opaque they fill and that won't work for my purpose)
I do load a background image from the storyboard, do you guys know if this might be affecting performance? Is the storyboard inefficient when it comes to loading images?
Do you have any other tips for improving performance on a UITableView?
Thanks in advance!
Some code as requested,and these are the elements on the cell: http://imgur.com/Dcif6QE
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
HomeListCell *cell;
if([self.eventList lastObject])
{
static NSString *CellIdentifier = #"HomeListCell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.userInteractionEnabled = YES;
cell.event = [self.eventList objectAtIndex:indexPath.row];
cell.parent = self;
if([cell.event.event.event_type count] != 0)
{
Event_Type *eventType = [cell.event.event.event_type firstObject];
NSString *imageName = #"HomeList_Type";
imageName = [imageName stringByAppendingString:eventType.name];
cell.eventTypeImage.image = [self.imageDict objectForKey:imageName];
}
//Laad de images hier uit de cache om scroll performance te verbeteren
int score = [cell.event.rating intValue];
[cell moveView:cell.ratingNumber duration:0.0 curve:UIViewAnimationCurveLinear x:0.0 y:0.0];
if(score > 0)
{
cell.ratingImage.image = [self.imageDict objectForKey:#"HomeList_plus"];
}
else if(score == 0)
{
cell.ratingImage.image = nil;
[cell moveView:cell.ratingNumber duration:0.0 curve:UIViewAnimationCurveLinear x:0.0 y:-10.0];
}
else
{
cell.ratingImage.image = [self.imageDict objectForKey:#"HomeList_min.png"];
score = -score;
}
cell.ratingNumber.text = [NSString stringWithFormat:#"%d", score];
[cell styleSelf];
}
And styleSelf has this code:
-(void) styleSelf {
LocationManager *locationManager = [LocationManager sharedInstance];
//Tekens die verandert moeten worden
NSCharacterSet *notAllowedY = [NSCharacterSet characterSetWithCharactersInString:#"ÿ"];
NSString *resultString = [[event.event.name componentsSeparatedByCharactersInSet:notAllowedY] componentsJoinedByString:#"y"];
//Afstand berekening
double eventLong = [self.event.location.address.gps_long doubleValue];
double eventLat = [self.event.location.address.gps_lat doubleValue];
CLLocation* locatie = [[CLLocation alloc]initWithLatitude:eventLat longitude:eventLong];
//Date + time
NSString *eventDate = event.opening;
eventDate = [eventDate stringByReplacingOccurrencesOfString:#"T" withString:#" "];
NSDate *theDate;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
if([eventDate hasSuffix:#"Z"])
{
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ssZ"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]];
theDate = [dateFormatter dateFromString:eventDate];
}
else
{
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
theDate = [dateFormatter dateFromString:eventDate];
}
[dateFormatter setDateFormat:#"HH:mm"];
self.timeNumberLabel.text = [dateFormatter stringFromDate:theDate];
self.timeNumberAfscheurLabel.text = [dateFormatter stringFromDate:theDate];
[dateFormatter setDateFormat:#"MM-dd"];
if ([[dateFormatter stringFromDate:theDate] isEqualToString:[dateFormatter stringFromDate:[NSDate date]]])
{
self.timeWhenLabel.text = NSLocalizedString(#"HomeList-Vandaag", nil);
self.timeWhenAfscheurLabel.text = NSLocalizedString(#"HomeList-Vandaag", nil);
}
else
{
[dateFormatter setDateFormat:#"MM"];
NSString *maand = [dateFormatter stringFromDate:theDate];
NSString *monthName = NSLocalizedString([#"Maand-" stringByAppendingString: maand], nil);
[dateFormatter setDateFormat:#"d"];
NSString *dag = [dateFormatter stringFromDate:theDate];
NSString *DatumString = [[dag stringByAppendingString:#" "]stringByAppendingString:monthName];
self.timeWhenLabel.text = [#" " stringByAppendingString:DatumString];
self.timeWhenAfscheurLabel.text = [#" " stringByAppendingString:DatumString];
}
//De cell vormen of de user gaat of niet
if([event.user_attends_event count] == 0)
{
[self moveView:self.nietAfgescheurdKnop duration:0 curve:UIViewAnimationCurveLinear x:0.0 y:0.0];
[self moveView:self.timeNumberAfscheurLabel duration:0 curve:UIViewAnimationCurveLinear x:0.0 y:0.0];
[self moveView:self.timeWhenAfscheurLabel duration:0 curve:UIViewAnimationCurveLinear x:0.0 y:0.0];
}
else
{
[self moveView:self.nietAfgescheurdKnop duration:0 curve:UIViewAnimationCurveLinear x:50.0 y:0.0];
[self moveView:self.timeNumberAfscheurLabel duration:0 curve:UIViewAnimationCurveLinear x:50.0 y:0.0];
[self moveView:self.timeWhenAfscheurLabel duration:0 curve:UIViewAnimationCurveLinear x:50.0 y:0.0];
}
self.event.userDistance = [locationManager getDistanceBetween:locatie];
if([self.event.userDistance isEqualToString:#"GPS error"])
{
self.distanceNumberLabel.text = NSLocalizedString(#"Extras-GPS", nil);
self.distanceTypeLabel.text = NSLocalizedString(#"Extras-UIT", nil);
self.distanceNumberLabel.textColor = [UIColor grayColor];
self.distanceTypeLabel.textColor = [UIColor grayColor];
}
else
{
NSString *placehold = self.event.userDistance;
placehold = [placehold stringByReplacingOccurrencesOfString:#"." withString:#","];
self.distanceNumberLabel.text = placehold;
self.distanceTypeLabel.text = NSLocalizedString(#"Extras-Km", nil);
self.distanceNumberLabel.textColor = [UIColor blackColor];
self.distanceTypeLabel.textColor = [UIColor blackColor];
}
// Configure the cell...
self.titleLabel.text = resultString;
self.tagsLabel.text = [event getMetadataString];
}

Your evil culprit is NSDateFormatter. This is a super-heavy object to create. You should create a single version of it somewhere and reuse it, setting the properties (formats, time zones, etc.) freely.
It's also a good idea to use Instruments -> Time Profiler to see exactly which methods are taking up time on the main thread.

Related

UIDatePicker in tableview can't be changed sometimes

sometimes I have problems with the UIDatePicker. I built a screen with a UITableView where I can set a start time and an end time for each day of the week.
Now, however, I have the strange behavior that the picker sometimes does not allow a change. It is grayed out but not disabled. When I turn the picker it moves, but when I let go, it automatically returns to its previous value.
The behavior is reproducible. I change one day and close the picker. Change another day and close the picker. Then I scroll down a bit, the tableview jerking slightly. If I now open the last changed picker I have the mentioned behavior. I noticed the behavior on iOS 9 to 11 and on different devices.
For a better understanding, I have created a screenshot.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"timeProfilSettingsCell";
TimeProfilSettingsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDate *dateValidFrom = nil;
NSDate *dateValidUntil = nil;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]];
if (cell == nil)
{
[tableView registerNib:[UINib nibWithNibName:#"TimeProfilSettingsCell" bundle:nil] forCellReuseIdentifier:CellIdentifier];
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
TimeInterval *timeInterval = [nma_currentTimeInterval objectAtIndex:indexPath.row];
if (timeInterval.ni_validFrom == TIMEPROFIL_VALUE_ALWAYS_VALID_FROM && timeInterval.ni_validUntil == TIMEPROFIL_VALUE_ALWAYS_VALID_UNTIL && timeInterval.ni_timeIntervalWeekdayActive == 1)
{
[cell.checkBoxImageView setImage:[UIImage imageNamed:#"IconHakenBlau"]];
}
else if (timeInterval.ni_timeIntervalWeekdayActive == 1)
{
[cell.checkBoxImageView setImage:[UIImage imageNamed:#"Clock_blue"]];
}
else
{
[cell.checkBoxImageView setImage:[UIImage imageNamed:#"IconOvalBlau"]];
}
cell.separatorView.backgroundColor = [UIColor colorWithRed:227.0/255.0 green:227.0/255.0 blue:227.0/255.0 alpha:1.0];
cell.weekDayLabel.text = [self getWeekDayName:indexPath.row];
[cell.checkBoxButton addTarget:self action:#selector(checkBoxButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[cell.checkBoxButton setTag:indexPath.row];
cell.validityLabel.textColor = [UIColor colorWithRed:123.0/255.0 green:164.0/255.0 blue:219.0/255.0 alpha:1.0];
[cell.validityButton addTarget:self action:#selector(validityButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[cell.validityButton setTag:indexPath.row];
// Handling so that the picker is closed when it is clicked elsewhere
UITapGestureRecognizer *tapGestureRecognize = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissDatePicker:)];
tapGestureRecognize.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tapGestureRecognize];
BOOL b_open = NO;
if (b_pickerVisible && ni_touchedRow == indexPath.row)
{
b_open = YES;
cell.b_validityPickerVisible = YES;
[cell.validityStartPicker setHidden:NO];
[cell.validityStartPicker setEnabled:YES];
[cell.validityEndPicker setHidden:NO];
[cell.validityEndPicker setEnabled:YES];
cell.validityFromLabel.hidden = NO;
[cell.separatorView setFrame:CGRectMake(cell.separatorView.frame.origin.x, 304, cell.separatorView.frame.size.width, cell.separatorView.frame.size.height)];
[cell.validityStartPicker setTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]];
[cell.validityEndPicker setTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]];
[cell.validityStartPicker addTarget:self action:#selector(updateValidityStartPicker:) forControlEvents:UIControlEventValueChanged];
[cell.validityEndPicker addTarget:self action:#selector(updateValidityEndPicker:) forControlEvents:UIControlEventValueChanged];
if (b_pickerJustOpened)
{
b_pickerJustOpened = NO;
if (tableView.contentOffset.y > 0)
{
tableView.contentOffset = CGPointMake(0, tableView.contentOffset.y + 102);
}
}
if (b_pickerValueChanged)
{
// Picker-value not changed
}
else if (timeInterval.ni_validFrom == TIMEPROFIL_VALUE_INVALID && timeInterval.ni_validUntil == TIMEPROFIL_VALUE_INVALID)
{
// Default value, if the entry was previously "not"
[cell.validityStartPicker setDate:[NSDate dateWithTimeIntervalSince1970:0x20 /* 8:00 */ * 900 /* 15 Minuten x 60 Sekunden */] animated:NO];
[cell.validityEndPicker setDate:[NSDate dateWithTimeIntervalSince1970:0x40 /* 16:00 */ * 900 /* 15 Minuten x 60 Sekunden */] animated:NO];
b_pickerValueChanged = YES;
[cell.validityLabel setTextColor:[UIColor redColor]];
}
else
{
[cell.validityStartPicker setDate:[NSDate dateWithTimeIntervalSince1970:timeInterval.ni_validFrom * 900 /* 15 Minuten x 60 Sekunden */] animated:NO];
[cell.validityEndPicker setDate:[NSDate dateWithTimeIntervalSince1970:(timeInterval.ni_validUntil + 1 /* +1 aufrunden auf die volle viertel Stunde*/) * 900 /* 15 Minuten x 60 Sekunden */] animated:NO];
}
}
else
{
cell.b_validityPickerVisible = NO;
cell.validityStartPicker.hidden = YES;
cell.validityEndPicker.hidden = YES;
cell.validityFromLabel.hidden = YES;
[cell.validityStartPicker setEnabled:YES];
[cell.validityEndPicker setEnabled:YES];
[cell.separatorView setFrame:CGRectMake(cell.separatorView.frame.origin.x, 88, cell.separatorView.frame.size.width, cell.separatorView.frame.size.height)];
}
if (b_pickerValueChanged && ni_touchedRow == indexPath.row)
{
if ([[dateFormatter stringFromDate:[cell.validityEndPicker date]] isEqualToString:#"00:00"])
{
cell.validityLabel.text = [NSString stringWithFormat:#"%# - 23:59:59 %# >", [dateFormatter stringFromDate:[cell.validityStartPicker date]], NSLocalizedString(#"oClock", nil)];
}
else
{
cell.validityLabel.text = [NSString stringWithFormat:#"%# - %# %# >", [dateFormatter stringFromDate:[cell.validityStartPicker date]], [dateFormatter stringFromDate:[cell.validityEndPicker date]], NSLocalizedString(#"oClock", nil)];
}
[cell.validityLabel setTextColor:[UIColor redColor]];
}
else if ((timeInterval.ni_validFrom == TIMEPROFIL_VALUE_ALWAYS_VALID_FROM && timeInterval.ni_validUntil == TIMEPROFIL_VALUE_ALWAYS_VALID_UNTIL && timeInterval.ni_timeIntervalWeekdayActive == 1) || (ni_flagTimeIntervalActive == 0 && timeInterval.ni_timeIntervalWeekdayActive == 1))
{
cell.validityLabel.text = NSLocalizedString(#"Always", nil);
}
else if (timeInterval.ni_validFrom == TIMEPROFIL_VALUE_INVALID || timeInterval.ni_validUntil == TIMEPROFIL_VALUE_INVALID)
{
cell.validityLabel.text = NSLocalizedString(#"Not", nil);
}
else
{
timeInterval.ni_timeIntervalWeekdayActive = 1;
dateValidFrom = [NSDate dateWithTimeIntervalSince1970:timeInterval.ni_validFrom * 900 /* 15 Minuten x 60 Sekunden */];
dateValidUntil = [NSDate dateWithTimeIntervalSince1970:(timeInterval.ni_validUntil + 1 /* +1 aufrunden auf die volle viertel Stunde*/) * 900 /* 15 Minuten x 60 Sekunden */];
if (timeInterval.ni_validUntil == TIMEPROFIL_VALUE_ALWAYS_VALID_UNTIL)
{
cell.validityLabel.text = [NSString stringWithFormat:#"%# - 23:59:59 %#", [dateFormatter stringFromDate:dateValidFrom], NSLocalizedString(#"oClock", nil)];
}
else
{
cell.validityLabel.text = [NSString stringWithFormat:#"%# - %# %#", [dateFormatter stringFromDate:dateValidFrom], [dateFormatter stringFromDate:dateValidUntil], NSLocalizedString(#"oClock", nil)];
}
}
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
return cell;
}
I have extracted the part of the code into a project and made it available for download.
I hope someone has an idea.
Thanks for your help
Finally I found a solution for the problem. After setting the locale in the code the picker didn't block.
Here ist the code I added after setTimeZone: in my code.
[cell.validityStartPicker setLocale:[[NSLocale alloc] initWithLocaleIdentifier:[[NSLocale currentLocale] localeIdentifier]]];
[cell.validityEndPicker setLocale:[[NSLocale alloc] initWithLocaleIdentifier:[[NSLocale currentLocale] localeIdentifier]]];

How to show image in specific time?

I have TableViewController and ViewController. In ViewController I save time and in TableViewController I want to show image in table cell if time more then 1:00. But my code doesn’t work… Help me please.
Code in TableViewController
_timeElapsed.text = [[NSUserDefaults standardUserDefaults] stringForKey:#"time0"];
if (self.timeElapsed.text >= #"1:00") {
UIImageView *imageView2 = [[UIImageView alloc]initWithFrame:CGRectMake(28.5, 23, 25, 25)];
imageView2.backgroundColor = [UIColor clearColor];
[imageView2.layer setMasksToBounds:YES];
imageView2.tag = 3;
[cell.contentView addSubview:imageView2];
UIImageView *imgView2 = (UIImageView *)[cell.contentView viewWithTag:3];
imgView2.image = [UIImage imageNamed:#"accessory.png"];
}
#seto nugroho
Now my _timeElapsed.text = 2:57 but image doesn’t show
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: [NSString stringWithFormat:#"Cell%d", indexPath.row] forIndexPath:indexPath];
_timeElapsed.text = [[NSUserDefaults standardUserDefaults] stringForKey:#"time0"];
NSString *stringFromInput = self.timeElapsed.text;
NSString *stringToCompare = #"1:00";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"m:ss";
NSDate *timeFromInput = [dateFormatter dateFromString:stringFromInput];
NSDate *timeToCompare = [dateFormatter dateFromString:stringToCompare];
if ([timeFromInput compare:timeToCompare] == NSOrderedDescending) {
//actual time is greater
UIImageView *imageView2 = [[UIImageView alloc]initWithFrame:CGRectMake(28.5, 23, 25, 25)];
imageView2.backgroundColor = [UIColor clearColor];
[imageView2.layer setMasksToBounds:YES];
imageView2.tag = 3;
[cell.contentView addSubview:imageView2];
UIImageView *imgView2 = (UIImageView *)[cell.contentView viewWithTag:3];
imgView2.image = [UIImage imageNamed:#"accessory.png"];
} else {
//not greater than 1 minutes
}
[tableView setSeparatorInset:UIEdgeInsetsMake(0, 70, 0, 1)];
return cell;
}
#iOS Geek
If timeString = #"0:05" and if (timeInSeconds > 1) image doesn’t show but in this case I see image.
NSString *timeString = #"0:05";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"mm:ss";
NSDate *timeDate = [formatter dateFromString:timeString];
formatter.dateFormat = #"mm";
int minutes = [[formatter stringFromDate:timeDate] intValue];
formatter.dateFormat = #"ss";
int seconds = [[formatter stringFromDate:timeDate] intValue];
int timeInSeconds = seconds + minutes * 60;
if (timeInSeconds > 1) {
UIImageView *imageView2 = [[UIImageView alloc]initWithFrame:CGRectMake(28.5, 23, 25, 25)];
imageView2.backgroundColor = [UIColor clearColor];
[imageView2.layer setMasksToBounds:YES];
imageView2.tag = 3;
[cell.contentView addSubview:imageView2];
UIImageView *imgView2 = (UIImageView *)[cell.contentView viewWithTag:3];
imgView2.image = [UIImage imageNamed:#"accessory.png"];
}
You can not compare string values, unless you want to see if they are equal character by character(in which case you use "isEqualToString:").
Convert you time to integer values and then compare.
Update
You can use NSDateFormatter to get seconds and minutes from the time string:
NSString *timeString = #"3:31";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"mm:ss";
NSDate *timeDate = [formatter dateFromString:timeString];
formatter.dateFormat = #"mm";
int minutes = [[formatter stringFromDate:timeDate] intValue];
formatter.dateFormat = #"ss";
int seconds = [[formatter stringFromDate:timeDate] intValue];
int timeInSeconds = seconds + minutes * 60;
Your code doesn't work because you are comparing String, which obviously is not what you want.
You should convert your string to date and then compare it.
NSString *stringFromInput = self.timeElapsed.text;
NSString *stringToCompare = #"1:00";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"m:ss";
NSDate *timeFromInput = [dateFormatter dateFromString:stringFromInput];
NSDate *timeToCompare = [dateFormatter dateFromString:stringToCompare];
if ([timeFromInput compare:timeToCompare] == NSOrderedDescending) {
//actual time is greater
} else {
//not greater than 1 minutes
}
Turn warnings on. The compiler will warn you that you are comparing pointers with the >= operator, which is often undefined behaviour, and often nonsense, as in your case.
self.timeElapsed.text >= #"1:00"
compares two pointers. It compares a pointer to the location where the object self.timeElapsed.text is stored, and a pointer to the location where the object #"1:00" is stored. Which is obviously nonsense and completely unrelated to the actual values of the objects.
It's bad to compare time like this:
self.timeElapsed.text >= #"1:00"
Please convert it to NSDate or Int(not so good but easy). Like this:
[[self.timeElapsed.text componentsSeparatedByString:#":"] firstObject].intValue > 1

iOS app's archive data doesn't load properly

I'm pretty new to this, so bear with me.
I built an app using iOS Programming: The Big Nerd Ranch Guide 4th ed called Homepwner which utilizes archiving to store information when the app goes into the background and load information when it starts up again, and it works totally fine. I decided to implement the same strategy for my own app called SBL, but when the app is loaded after having been quit, the data appears to load but doesn't display properly.
The app displays an array of "CCIEvent"s in a table and they appear like the example:
7:00 AM: Wet and Dirty Diaper
8:48 AM: Fed - Both - 20 min
9:48 AM: Napped - 1 hr 0 min
But when I quit the app and start it again, the list pulls up like this (I had to identify it as code to make this post, but the info below displays on screen in my table as such):
<CCIEvent: 0x154e44e20>
<CCIEvent: 0x154e2c7b0>
<CCIEvent: 0x154e77b90>
I really don't know where to look, but here's all the code relevant to saving/loading (I think):
In CCIEvent.m
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.startTime forKey:#"startTime"];
[aCoder encodeObject:self.bedTime forKey:#"bedTime"];
[aCoder encodeObject:self.wakeTime forKey:#"wakeTime"];
[aCoder encodeBool:self.isNap forKey:#"isNap"];
[aCoder encodeInt:self.feedDuration forKey:#"feedDuration"];
[aCoder encodeInt:self.sourceIndex forKey:#"sourceIndex"];
[aCoder encodeInt:self.diaperIndex forKey:#"diaperIndex"];
[aCoder encodeObject:self.note forKey:#"note"];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
_startTime = [aDecoder decodeObjectForKey:#"startTime"];
_bedTime = [aDecoder decodeObjectForKey:#"bedTime"];
_wakeTime = [aDecoder decodeObjectForKey:#"wakeTime"];
_isNap = [aDecoder decodeBoolForKey:#"isNap"];
_feedDuration = [aDecoder decodeIntForKey:#"feedDuration"];
_sourceIndex = [aDecoder decodeIntForKey:#"sourceIndex"];
_diaperIndex = [aDecoder decodeIntForKey:#"diaperIndex"];
_note = [aDecoder decodeObjectForKey:#"note"];
}
return self;
}
In CCIEventStore.m
- (instancetype)initPrivate
{
self = [super init];
if (self) {
NSString *path = [self eventArchivePath];
_privateEvents = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
// If the array hadn't been saved previously, create an empty one
if (!_privateEvents) {
_privateEvents = [[NSMutableArray alloc] init];
NSLog(#"Did NOT load events");
} else {
NSLog(#"Loaded events");
}
}
return self;
}
- (CCIEvent *)createEventWithEventType:(NSString *)eventType
startTime:(NSDate *)startTime
bedTime:(NSDate *)bedTime
wakeTime:(NSDate *)wakeTime
isNap:(BOOL)isNap
feedDuration:(int)feedDuration
sourceIndex:(int)sourceIndex
diaperIndex:(int)diaperIndex
note:(NSString *)note
{
CCIEvent *event = [[CCIEvent alloc] initWithEventType:eventType
startTime:startTime
bedTime:bedTime
wakeTime:wakeTime
isNap:isNap
feedDuration:feedDuration
sourceIndex:sourceIndex
diaperIndex:diaperIndex
note:note];
[self.privateEvents addObject:event];
return event;
}
- (NSString *)eventArchivePath
{
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [documentDirectories firstObject];
return [documentDirectory stringByAppendingPathComponent:#"events.archive"];
}
- (BOOL)saveChanges
{
NSString *path = [self eventArchivePath];
return [NSKeyedArchiver archiveRootObject:self.privateEvents
toFile:path];
}
In AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application {
BOOL success = [[CCIEventStore sharedStore] saveChanges];
if (success) {
NSLog(#"Saved all of the CCIEvents");
} else {
NSLog(#"Could not save any of the CCIEvents");
}
}
This is the some code from the view controller where the table is which I have called CCILogViewController.m:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"
forIndexPath:indexPath];
CCIEvent *event = self.sortedAndFilteredArray[indexPath.row];
if ([event.eventType isEqualToString:#"sleepEvent"] && event.wakeTime) {
NSDateComponents *dateComponentsToday = [[NSCalendar currentCalendar] components:NSCalendarUnitYear fromDate:[NSDate date]];
NSInteger yearToday = [dateComponentsToday year];
NSInteger dayOfYearToday = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay
inUnit:NSCalendarUnitYear
forDate:[NSDate date]];
NSDateComponents *dateComponentsEvent = [[NSCalendar currentCalendar] components:NSCalendarUnitYear
fromDate:event.startTime];
NSInteger yearEvent = [dateComponentsEvent year];
NSInteger dayOfYearEvent = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay
inUnit:NSCalendarUnitYear
forDate:event.startTime];
NSInteger dayOfYearWakeEvent;
dayOfYearWakeEvent = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay
inUnit:NSCalendarUnitYear
forDate:event.wakeTime];
if (event.wakeTime && yearEvent == yearToday && dayOfYearEvent == dayOfYearToday - 1 && dayOfYearWakeEvent == dayOfYearToday) {
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", [dateFormatter stringFromDate:event.startTime], [self.sortedAndFilteredArray[indexPath.row] description]];
} else if (event.wakeTime && yearEvent == yearToday - 1 && dayOfYearEvent == dayOfYearToday - 1 && dayOfYearEvent == 1 && dayOfYearWakeEvent == dayOfYearToday) {
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", [dateFormatter stringFromDate:event.startTime], [self.sortedAndFilteredArray[indexPath.row] description]];
} else {
cell.textLabel.text = [self.sortedAndFilteredArray[indexPath.row] description];
}
} else if (self.dateOptionIndex == 2 || self.dateOptionIndex == 3) {
cell.textLabel.text = [NSString stringWithFormat:#"%# %#", [dateFormatter stringFromDate:event.startTime], [self.sortedAndFilteredArray[indexPath.row] description]];
} else {
cell.textLabel.text = [self.sortedAndFilteredArray[indexPath.row] description];
}
if (event.note) {
cell.accessoryType = UITableViewCellAccessoryDetailButton;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
Here's how I get the sortedAndFilteredArray which is called in few different places including viewWillAppear:
- (void)sortAndFilterArray
{
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"startTime" ascending:self.arrayIsAscending];
NSArray *sortedArray = [[[CCIEventStore sharedStore] allEvents] sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
NSMutableArray *sortedAndFilteredArray = [[NSMutableArray alloc] init];
NSMutableArray *finalArray = [[NSMutableArray alloc] init];
for (CCIEvent *event in sortedArray) {
switch (self.eventOptionIndex) {
case 1:
if ([event.eventType isEqualToString:#"sleepEvent"]) {
[sortedAndFilteredArray addObject:event];
}
break;
case 2:
if ([event.eventType isEqualToString:#"feedEvent"]) {
[sortedAndFilteredArray addObject:event];
}
break;
case 3:
if ([event.eventType isEqualToString:#"diaperEvent"]) {
[sortedAndFilteredArray addObject:event];
}
break;
default:
[sortedAndFilteredArray addObject:event];
break;
}
}
NSDateComponents *dateComponentsToday = [[NSCalendar currentCalendar] components:NSCalendarUnitYear fromDate:[NSDate date]];
NSInteger yearToday = [dateComponentsToday year];
NSInteger dayOfYearToday = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay
inUnit:NSCalendarUnitYear
forDate:[NSDate date]];
for (CCIEvent *event in sortedAndFilteredArray) {
NSDateComponents *dateComponentsEvent = [[NSCalendar currentCalendar] components:NSCalendarUnitYear
fromDate:event.startTime];
NSInteger yearEvent = [dateComponentsEvent year];
NSInteger dayOfYearEvent = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay
inUnit:NSCalendarUnitYear
forDate:event.startTime];
NSInteger dayOfYearWakeEvent;
switch (self.dateOptionIndex) {
case 0:
dayOfYearWakeEvent = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay
inUnit:NSCalendarUnitYear
forDate:event.wakeTime];
// Filter here for dateIndex 0 (Today)
if ([event.eventType isEqualToString:#"sleepEvent"] && event.wakeTime && yearEvent == yearToday && dayOfYearEvent == dayOfYearToday - 1 && dayOfYearWakeEvent == dayOfYearToday) {
[finalArray addObject:event];
} else if ([event.eventType isEqualToString:#"sleepEvent"] && event.wakeTime && yearEvent == yearToday - 1 && dayOfYearEvent == dayOfYearToday - 1 && dayOfYearEvent == 1 && dayOfYearWakeEvent == dayOfYearToday) {
[finalArray addObject:event];
} else if (yearEvent == yearToday && dayOfYearEvent == dayOfYearToday) {
[finalArray addObject:event];
}
break;
case 1:
// Filter here for dateIndex 1 (Yesterday)
if (yearEvent == yearToday && dayOfYearEvent == dayOfYearToday - 1) {
[finalArray addObject:event];
} else if (yearEvent == yearToday - 1 && dayOfYearEvent == dayOfYearToday - 1 && dayOfYearEvent == 1) {
[finalArray addObject:event];
}
break;
case 2:
// Filter here for dateIndex 2 (Past Week)
if (yearEvent == yearToday && dayOfYearEvent >= dayOfYearEvent - 6) {
[finalArray addObject:event];
} else if (yearEvent == yearToday - 1 && dayOfYearEvent >= dayOfYearToday - 6 && dayOfYearEvent < 7) {
[finalArray addObject:event];
}
break;
default:
// No filter here for dateIndex 3 (All Time)
[finalArray addObject:event];
break;
}
}
self.sortedAndFilteredArray = [finalArray copy];
}
This is what is being called when getting description of a CCIEvent:
- (NSString *)description
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
[dateFormatter setDateStyle:NSDateFormatterNoStyle];
if ([self.eventType isEqualToString:#"sleepEvent"]) {
NSString *bedTimeString = [dateFormatter stringFromDate:self.bedTime];
NSString *wakeTimeString = [dateFormatter stringFromDate:self.wakeTime];
long min = self.sleepDuration/60;
long hrs = self.sleepDuration/60/60;
long rMinutes = min - hrs * 60;
if (!self.wakeTime && self.bedTime) {
if (self.isNap) {
return [[NSString alloc] initWithFormat:#"%#: Went Down for Nap", bedTimeString];
} else {
return [[NSString alloc] initWithFormat:#"%#: Went Down", bedTimeString];
}
} else if (self.wakeTime && !self.bedTime) {
if (self.isNap) {
return [[NSString alloc] initWithFormat:#"%#: Woke from Nap", wakeTimeString];
} else {
return [[NSString alloc] initWithFormat:#"%#: Woke", wakeTimeString];
}
} else if (self.sleepDuration <= 60) {
if (self.isNap) {
return [[NSString alloc] initWithFormat:#"%#: Napped - 1 min", bedTimeString];
} else {
return [[NSString alloc] initWithFormat:#"%#: Slept - 1 min", bedTimeString];
}
} else if (self.sleepDuration > 60 && self.sleepDuration < 60 * 60){
if (self.isNap) {
return [[NSString alloc] initWithFormat:#"%#: Napped - %ld min", bedTimeString, min];
} else {
return [[NSString alloc] initWithFormat:#"%#: Slept - %ld min", bedTimeString, min];
}
} else if (hrs == 1) {
if (self.isNap) {
return [[NSString alloc] initWithFormat:#"%#: Napped - 1 hr %ld min", bedTimeString, rMinutes];
} else {
return [[NSString alloc] initWithFormat:#"%#: Slept - 1 hr %ld min", bedTimeString, rMinutes];
}
} else {
if (self.isNap) {
return [[NSString alloc] initWithFormat:#"%#: Napped - %ld hrs %ld min", bedTimeString, hrs, rMinutes];
} else {
return [[NSString alloc] initWithFormat:#"%#: Slept - %ld hrs %ld min", bedTimeString, hrs, rMinutes];
}
}
} else if ([self.eventType isEqualToString:#"feedEvent"]) {
NSString *startTimeString = [dateFormatter stringFromDate:self.startTime];
NSArray *sourceArray = #[#"Both", #"Left", #"Right", #"Bottle"];
NSString *sourceString = sourceArray[self.sourceIndex];
return [[NSString alloc] initWithFormat:#"%#: Fed - %# - %d min", startTimeString, sourceString, self.feedDuration / 60];
} else if ([self.eventType isEqualToString:#"diaperEvent"]) {
NSString *startTimeString = [dateFormatter stringFromDate:self.startTime];
NSArray *diaperArray = #[#"Wet and Dirty", #"Wet", #"Dirty"];
NSString *diaperString = diaperArray[self.diaperIndex];
return [[NSString alloc] initWithFormat:#"%#: %# Diaper", startTimeString, diaperString];
}
return [super description];
}
Do you have a custom description method in CCIEvent? By default, NSObject description will display the memory location of an object which is exactly what you're seeing. You can do a basic test by creating something very simple like:
- (NSString *)description
{
return [[NSString alloc] initWithFormat:#"%#", self.note];
}
It still seems like something isn't loading somewhere, or I'd expect you to see that problem during general usage. If you do have a CCIEvent description method, can you post it? I'm also curious about sortedAndFilteredArray.
You might try sprinkling your code with NSLog() calls to see if you're getting what you expect at various points. Keep an eye on the XCode console when the app runs to see what data you're getting. Ie, in CCIEvent.m initWithCoder you might add something like the following at the end of the if (self) { block:
NSLog(#"Got %#", _note);
Or even check your description method right there:
NSLog([self description]);
Also be sure to do some NSLog's in your encodeWithCoder method. My suspicion is the problem is occurring on the save side - there's something keeping event details from saving properly, so when they are restored they are empty so in your cellForRowAtIndexPath method it's hitting the default condition and just calling description on CCIEvent, which I suspect may not exist and is doing the default behavior of displaying the memory location. If I'm right, this points to a disconnect between CCIEventStore's privateEvents and the event being edited in your detail view. Check the object properties in your header files and see if somewhere CCIEvent is being passed around using copy rather than strong.
Unrelated to the error, but still worth noting - the item in cellForRowAtIndexPath:
cell.textLabel.text = [self.sortedAndFilteredArray[indexPath.row] description];
Those lines probably aren't related to the issue, but they contain a redundant accessor. Since you already have:
CCIEvent *event = self.sortedAndFilteredArray[indexPath.row];
any times you want to access data from the event, you should use that variable, so the above textLabel would be:
cell.textLabel.text = [event description];
There are a few other places in cellForRowAtIndexPath that could use a similar modification.
Hope this helps some!
I sprinkled my code with NSLogs and determined that my CCIEvents were loading just fine. I found it odd that my special description worked before loading but not after loading. Before loading it would give me the string I wanted, but after loading it would point to memory. After inspecting my description method, I noticed that I defaulted to returning the super's description method unless the CCIEvent met certain criteria, which forced me to realize that my CCIEvents have an attribute of eventType which I did not include in the encodeWithCoder: or initWithCoder: blocks. All I had to do was add eventType in there, and my description works like it should. Thanks so much, Chris! I literally left this project alone for months because I was so stumped.

Autoresizing KILabel in UITableViewCell

I have a custom UILabel that I download from Github (called KILabel). The reason I got it because I wanted to duplicate Instagram's activity feed(The segmented control and the UITableViews with 2 different sets of information). In instagram's app I know there is the ability to tap on people usernames within the label that is contained in each and ever UITableViewCell(This is where KILabel comes into play). KILabel recognizes "#usernames", "#hashtags", and URLs and makes them tappable almost like a UIButton.
I had previously went without the KILabel and used the regular UILabel and it was able to expand in iOS 8 and iOS 7. Now, when I use the KILabel it auto expands(Dynamically changes its height to fit the text) ONLY on iOS 7 and it does NOT start off expanded. When I scroll in my UITableViewController, that is when the label expands. It starts off normal height.
As of right now this what the Custom Cell looks like
#import <UIKit/UIKit.h>
#import "KILabel.h"
#interface NewsCell : UITableViewCell
#property (strong, nonatomic) IBOutlet KILabel *cellLabel;
#property (strong, nonatomic) IBOutlet UIImageView *cellImageView;
#end
I have the label in the interface(storyboard) hooked up to the custom KILabel class as well as the ImageView.
Next I have the cell being implemented in the UITableViewController. I tested it with a large piece of text to see if the cell and label would expand to the correct size
cell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier2 forIndexPath:indexPath];
cell.cellImageView.layer.cornerRadius = 6.0f;
cell.cellImageView.clipsToBounds = YES;
cell.cellImageView.layer.borderWidth = 1.0f;
cell.cellImageView.layer.borderColor = [[UIColor blackColor] CGColor];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] init];
longPress.numberOfTapsRequired = 1;
[cell.cellLabel addGestureRecognizer:longPress];
//Create the cell label going into the cell
cell.cellLabel.linkTapHandler = ^(KILinkType linkType, NSString *string, NSRange range) {
NSString *mString = [string stringByReplacingOccurrencesOfString:#"#" withString:#""];
if (linkType == KILinkTypeURL) {
// Open URLs
//[self attemptOpenURL:[NSURL URLWithString:string]];
} else if (linkType == KILinkTypeUserHandle) {
if (longPress.state == UIGestureRecognizerStatePossible) {
[self tapOnUsername:longPress username:mString];
}
} else {
// Put up an alert with a message if it's not an URL
NSString *linkTypeString = #"Username";
if (linkType == KILinkTypeHashtag)
{
linkTypeString = #"Hashtag";
}
NSString *message = [NSString stringWithFormat:#"You tapped %# which is a %#", string, linkTypeString];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Hello"
message:message
delegate:nil
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alert show];
}
};
PFObject *eachNews = [self.followingNews objectAtIndex:indexPath.row];
PFUser *notifier = [eachNews objectForKey:#"Notifier"];
PFUser *notified = [eachNews objectForKey:#"Notified"];
[notifier fetchIfNeeded];
[notified fetchIfNeeded];
NSString *notifierString = [[NSString alloc] init];
NSString *notifiedString = [[NSString alloc] init];
NSString *grammer = [[NSString alloc] init];
NSDate *timeStamp = eachNews.createdAt;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MMM d, yyyy h:mm a"];
NSString *timeString = [timeStamp formattedAsTimeAgo];
if ([notifier.username isEqualToString:_loggedInUser.username]) {
notifierString = #"You";
grammer = #"are";
} else {
notifierString = [NSString stringWithFormat:#"#%#", notifier.username];
grammer = #"is";
}
if ([notified.username isEqualToString: _loggedInUser.username]) {
notifiedString = #"you";
} else {
notifiedString = [NSString stringWithFormat:#"#%#", notified.username];
}
if (notifier[#"profileImage"] == nil) {
UIImage *hermet = [UIImage imageNamed:#"user"];
[cell.cellImageView setImage:hermet];
} else {
PFFile *imageFile = notifier[#"profileImage"];
[cell.cellImageView setImage:[UIImage imageWithData:[imageFile getData]]];
}
NSMutableString *newsText = [[NSMutableString alloc] init];
if ([eachNews[#"Type"] isEqualToString:#"Follow"]) {
[newsText appendString:[NSString stringWithFormat:#"%# aaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUVaaaaaaaaaaaaaaasdasafsfsdgsdgsdfgsdfsodjnfsaioefgnarpuoigbweuifbsdpugbdsfiougbsdiugosbdgiusobfasdioFPBSADUBVSIUDVBSAIUDBSDIUVBSDUVIBDSFIUVBSDIUVSBAVIPUBDSIUV%# %#.\n", notifierString, eachNews[#"Messages"], notifiedString]];
} else if ([eachNews[#"Type"] isEqualToString:#"New Founder"]) {
[newsText appendString:[NSString stringWithFormat:#"%# %# %#.\n", notifierString, grammer, eachNews[#"Messages"]]];
} else if ([eachNews[#"Type"] isEqualToString:#"Club Create"]) {
if([notified.username isEqualToString:_loggedInUser.username]) {
notifiedString = #"You";
} else {
notifiedString = notified.username;
}
if (notified[#"profileImage"] == nil) {
UIImage *hermet = [UIImage imageNamed:#"user"];
[cell.cellImageView setImage:hermet];
} else {
PFFile *imageFile = notified[#"profileImage"];
[cell.cellImageView setImage:[UIImage imageWithData:[imageFile getData]]];
}
[newsText appendString:[NSString stringWithFormat:#"%# %#\n", notifiedString, eachNews[#"Messages"]]];
}
[newsText appendString:timeString];
NSArray *appendedString = [newsText componentsSeparatedByString:#"\n"];
NSRange dateRange = [newsText rangeOfString:appendedString[1]];
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:newsText];
[attrString beginEditing];
[attrString addAttribute: NSForegroundColorAttributeName
value:[UIColor lightGrayColor]
range:dateRange];
[attrString endEditing];
cell.cellLabel.attributedText = attrString;
CGSize sizeForLabel = CGSizeMake(cell.cellLabel.frame.size.width, 0);
CGRect labelRect = CGRectMake(cell.cellLabel.frame.origin.x, cell.cellLabel.frame.origin.y, sizeForLabel.width, CGFLOAT_MAX);
// Use a dummy label and its textRectForBounds method to calculate the height
// of a real label.
KILabel *measureLabel = [[KILabel alloc] initWithFrame:CGRectMake(cell.cellLabel.frame.origin.x, cell.cellLabel.frame.origin.y, labelRect.size.width, 0)];
measureLabel.numberOfLines = 0;
measureLabel.attributedText = attrString;
labelRect = [measureLabel textRectForBounds:labelRect limitedToNumberOfLines:0];
cell.cellLabel.frame = labelRect;
The height of UITableViewCell is completely fine and expands perfectly. The label is the only issues. It does not expand at all on iOS 8 and expands ONLY AFTER I have scrolled in the UITableViewController on iOS 7.
I would tagged KILabel but I can't and if anyone has used another UILabels like KILabel and been able to get them auto-expand please refer me to them.
KILabel on GitHub: https://github.com/Krelborn/KILabel
Also, I have already tried the Gist the creator posted on his GitHub and that will get my cell to expand perfectly.

Date Picker - show time between two dates (UIDatePickerModeDateAndTime)

I'm currently showing a Date Picker of the kind UIDatePickerModeCountDownTimer that uses:
- (UIDatePicker *)datePicker {
if (!_datePicker) {
_datePicker = [[UIDatePicker alloc] init];
_datePicker.datePickerMode = UIDatePickerModeCountDownTimer;
_datePicker.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.8];
}
return _datePicker;
}
- (void)setDuration:(NSTimeInterval)duration {
_duration = duration;
self.datePicker.countDownDuration = _duration;
}
... date picker, and shows (in a label) the time from current date to date chosen in future with:
- (void)update {
if (self.time) {
[self setImage:nil forState:UIControlStateNormal];
NSString *title;
NSTimeInterval timeInterval = [self.time doubleValue];
if (self.ticking) {
NSMutableString *dateFormat = [[NSMutableString alloc] init];
if (timeInterval < 0) {
[dateFormat appendString:#"-"];
}
if (fabsf(timeInterval) > 60 * 60) {
[dateFormat appendString:#"hh:"];
}
[dateFormat appendString:#"mm:ss"];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = dateFormat;
formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
title = [formatter stringFromDate:date];
if ([self.time integerValue] > 0) {
self.circleView.backgroundColor = [UIColor colorWithRed:0.373 green:0.702 blue:0.522 alpha:1];
} else {
self.circleView.backgroundColor = [UIColor colorWithRed:0.820 green:0.373 blue:0.424 alpha:1];
}
} else {
NSMutableString *text = [[NSMutableString alloc] init];
if (fabsf(timeInterval) < 60) {
// Show seconds
[text appendFormat:#"%.0fs", timeInterval];
} else if (fabsf(timeInterval) < 60 * 60) {
// Show minutes
[text appendFormat:#"%.0fm", floorf(timeInterval / 60)];
} else {
// Show hours
[text appendFormat:#"%.0fh", floorf(timeInterval / 60 / 60)];
}
title = text;
self.circleView.backgroundColor = [UIColor colorWithWhite:1.0f alpha:0.2];
}
[self setTitle:title forState:UIControlStateNormal];
return;
}
[self setTitle:nil forState:UIControlStateNormal];
[self setImage:[UIImage imageNamed:#"plus"] forState:UIControlStateNormal];
self.circleView.backgroundColor = [UIColor colorWithWhite:1.0f alpha:0.05];
}
... but I switched the DatePicker to UIDatePickerModeDateAndTime and need to figure out how to update my update method with it.
I need to show month/day in addition to hour/minute/second in the label.
If you want a method like delegate, then this can help you..
Add target to your date picker....
[myDatePicker addTarget:self action:#selector(onPickerValueChanged:) forControlEvents:UIControlEventValueChanged];
Remove target in dealloc. Otherwise if your picker is scrolling and viewController is popped, app will crash.
- (void dealloc
{
[myPicker removeTarget:self action:#selector(onPickerValueChanged:) forControlEvents:UIControlEventValueChanged];
[myPicker release];//FOR NON ARC
[super dealloc];//FOR NON ARC
}
Implement value Change like
- (IBAction)onPickerValueChanged:(id)sender
{
[self update];
}

Resources