I just switched from iOS 7.1 to iOS 8.1. am encountering the following problem with UIView animateWithDuration
. Pictures being worth a thousand words, I made videos of the behavior under each iOS:
iOS 7.1:
iOS 8.1:
It's an iPhone app.
I should note that I've searched SO and other places before posting. It seems to be a recognized problem, but what I've found are a few references to "using constraint-based animation" (which I didn't understand) and turning off Auto layout, but no real consensus.
I've tried a couple of stabs, but have gotten nowhere. If someone can point me to (or walk me through) an explicit fix, I'd really appreciate it.
I should mention that the app is intended to work only in the portrait position, so I gave no thought to auto layout during the development process.
All the views involved are created programmatically, except the scroller itself.
Here is the code. I included the whole method, which is pretty long, but I really don't understand what has changed in the new iOS, so am being on the safe side. The animateWithDuration
call is pretty deep into the chunk:
-(void) displayDayChart
{
[self.scroller setBackgroundColor:[UIColor clearColor]];
UIImage *image = [UIImage imageNamed:@"GrayPaper.png"];
self.chartView.backgroundColor = [UIColor colorWithPatternImage:image];
[self.view addSubview:self.scroller];
[self.scroller setScrollEnabled:YES];
[self.scroller setContentSize:CGSizeMake(320, 1800)];
[self.scroller setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
dayBarArray = [[NSMutableArray alloc]init];
infoLabelArray = [[NSMutableArray alloc]init];
timeLabelArray = [[NSMutableArray alloc]init];
timeFrameDC = - 86400; // 24 hours for this test, selectable in real life
[self.scroller.subviews makeObjectsPerformSelector: @selector(removeFromSuperview)];
timeframeDCStart = [NSDate dateWithTimeIntervalSinceNow:timeFrameDC];
timeframeDCEnd = [timeframeDCStart dateByAddingTimeInterval:abs(timeFrameDC)];
actPredicate = [NSPredicate predicateWithFormat:@"(startTime >= %@ AND stopTime <= %@) OR ((startTime <= %@ AND stopTime >= %@) OR (startTime <= %@ AND stopTime == NULL))",timeframeDCStart,timeframeDCEnd,timeframeDCStart,timeframeDCStart,timeframeDCEnd];
NSFetchedResultsController *dayActivityFRC = [TimedActivity MR_fetchAllSortedBy:@"startTime" ascending:YES withPredicate:actPredicate groupBy:nil delegate:nil];
double taDuration;
double activityPercent;
int barHeight;
int lastHeight = 0;
int tickerCount = 1;
int barY;
int lastBarY = 0;
CGRect lastBarFrame;
#pragma mark DisplayDayChart setup
for (TimedActivity *activity in dayActivityFRC.fetchedObjects)
{
// Create bar and associated labels
thisBar = [[DayChartBar alloc] initWithFrame:CGRectZero];
thisInfoLabel = [[UILabel alloc]initWithFrame:CGRectZero];
thisTimeLabel = [[UILabel alloc]initWithFrame:CGRectZero];
arrowView = [[UIView alloc]initWithFrame:CGRectZero];
thisBar.tag = tickerCount;
thisInfoLabel.tag = thisBar.tag + 100;
thisTimeLabel.tag = thisBar.tag + 200;
arrowView.tag = thisBar.tag + 300;
UIImage *image = [UIImage imageNamed:@"LeftArrowRed.png"];
// Add them to the scroller
[self.scroller addSubview:thisBar];
[self.scroller addSubview:thisInfoLabel];
[self.scroller addSubview:thisTimeLabel];
[self.scroller addSubview:arrowView];
NSCalendar *calendar = [NSCalendar currentCalendar];
if (!activity.stopTime) // Currently running
{
if ([activity.startTime timeIntervalSinceDate:timeframeDCStart] < 0)
{
dayBarDurationString = @"24:00:00";
taDuration = (fabs([timeframeDCStart timeIntervalSinceDate:timeframeDCEnd]));
}
else
{
NSDateComponents *components= [calendar components:NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit fromDate:activity.startTime toDate:timeframeDCEnd options:0];
NSInteger hours = [components hour];
NSInteger minutes = [components minute];
NSInteger seconds =[components second];
dayBarDurationString = [NSString stringWithFormat:@"%02li:%02li:%02li",(long)hours,(long)minutes,(long)seconds];
taDuration = (fabs([activity.startTime timeIntervalSinceDate:timeframeDCEnd]));
}
}
else // Either early edge case or fully encapsulated
{
if ([activity.startTime timeIntervalSinceDate:timeframeDCStart] < 0) // Early edge case
{
NSDateComponents *components= [calendar components:NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit fromDate:timeframeDCStart toDate:activity.stopTime options:0];
NSInteger hours = [components hour];
NSInteger minutes = [components minute];
NSInteger seconds =[components second];
dayBarDurationString = [NSString stringWithFormat:@"%02li:%02li:%02li",(long)hours,(long)minutes,(long)seconds];
taDuration = (fabs([activity.stopTime timeIntervalSinceDate:timeframeDCStart]));
}
else if ([activity.startTime timeIntervalSinceDate:timeframeDCStart] > 0) // Encapsulated
{
NSDateComponents *components= [calendar components:NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit fromDate:activity.startTime toDate:activity.stopTime options:0];
NSInteger hours = [components hour];
NSInteger minutes = [components minute];
NSInteger seconds =[components second];
dayBarDurationString = [NSString stringWithFormat:@"%02li:%02li:%02li",(long)hours,(long)minutes,(long)seconds];
taDuration = [activity.duration doubleValue];
}
}
NSDateFormatter *localDateFormatter = [[NSDateFormatter alloc] init];
[localDateFormatter setDateFormat:@"MMM dd"];
NSDateFormatter *localTimeFormatter = [[NSDateFormatter alloc] init];
[localTimeFormatter setDateFormat:@"hh:mm a"];
NSString * timeLabelDateString = [[NSString alloc]init];
NSString * timeLabelTimeString = [[NSString alloc]init];
NSString * timeLabelString = [[NSString alloc]init];
if (([activity.startTime timeIntervalSinceDate:timeframeDCStart] < 0))
{
timeLabelDateString = [localDateFormatter stringFromDate:timeframeDCStart];
timeLabelTimeString = [localTimeFormatter stringFromDate:timeframeDCStart];
}
else
{
timeLabelDateString = [localDateFormatter stringFromDate:activity.startTime];
timeLabelTimeString = [localTimeFormatter stringFromDate:activity.startTime];
}
timeLabelString = [NSString stringWithFormat:@"%@ - %@",timeLabelDateString,timeLabelTimeString];
thisBar.endColor = activity.color;
activityPercent = fabs((taDuration / timeFrameDC) * 100);
if (activityPercent > 100)
{
activityPercent = 100;
}
if (activityPercent < 1.9)
{
activityPercent = 1.9;
}
barHeight = abs((self.scroller.contentSize.height - 100) * (activityPercent / 100));
if (tickerCount == 1)
{
barY = self.scroller.contentSize.height -10;
}
else
{
barY = (lastBarY - lastHeight);
}
#pragma mark DisplayDayChart animation
[UIView animateWithDuration:.8
delay:0.0
options: UIViewAnimationCurveEaseInOut // Deprecated, but still works
animations:^
{
// Starting state *************************************
thisBar.frame = CGRectMake(20, self.scroller.contentSize.height, 130, 0);
thisBar.backgroundColor = [UIColor blackColor];
thisInfoLabel.frame = CGRectMake(20, self.scroller.contentSize.height, thisBar.frame.size.width, 30);
thisTimeLabel.frame = CGRectMake((thisBar.frame.origin.x) + (thisBar.frame.size.width + 35), self.scroller.contentSize.height, 150, 15);
thisTimeLabel.textColor = [UIColor blueColor];
arrowView.frame = CGRectMake(thisTimeLabel.frame.origin.x - 20, thisTimeLabel.frame.origin.y, 16, 16);
thisInfoLabel.textColor = [UIColor clearColor];
// End state *************************************
thisBar.frame = CGRectMake(20, barY, 130, - barHeight);
thisBar.backgroundColor = thisBar.endColor;
thisBar.layer.shadowColor = [[UIColor blackColor] CGColor];
thisBar.layer.frame = CGRectInset(thisBar.layer.frame, 0.0, 3.0);
thisBar.layer.shadowOpacity = 0.7;
thisBar.layer.shadowRadius = 4.0;
thisBar.layer.shadowOffset = CGSizeMake(5.0f, 5.0f);
// Position infoLabel
thisInfoLabel.frame = CGRectMake(20, self.scroller.contentSize.height + ((thisBar.frame.size.height / 2) - 30), thisBar.frame.size.width, 30);
thisTimeLabel.frame = CGRectMake((thisBar.frame.origin.x) + (thisBar.frame.size.width + 35), (thisBar.frame.origin.y) + (thisBar.frame.size.height) - 5, 150, 15);
thisInfoLabel.textColor = [UIColor blackColor];
dayBarNameString = activity.name;
[thisInfoLabel setFont:[UIFont fontWithName:@"Noteworthy-Bold" size:16.0]];
thisInfoLabel.numberOfLines = 0;
thisInfoLabel.textAlignment = NSTextAlignmentCenter;
// thisInfoLabel.layer.borderWidth = 1;
thisInfoLabel.text = [NSString stringWithFormat:@"%@\n%@",dayBarNameString,dayBarDurationString];
[thisInfoLabel sizeToFit];
if ( thisInfoLabel.frame.size.height >= thisBar.frame.size.height)
{
thisInfoLabel.text = [NSString stringWithFormat:@"%@",dayBarNameString];
}
else
{
thisInfoLabel.text = [NSString stringWithFormat:@"%@\n%@",dayBarNameString,dayBarDurationString];
}
[thisInfoLabel sizeToFit];
[thisInfoLabel setHidden:NO];
thisInfoLabel.center = thisBar.center;
arrowView.frame = CGRectMake(thisTimeLabel.frame.origin.x - 20, thisTimeLabel.frame.origin.y, 16, 16);
arrowView.backgroundColor = [UIColor colorWithPatternImage:image];
thisTimeLabel.textColor = [UIColor blueColor];
[thisTimeLabel setFont:[UIFont boldSystemFontOfSize:13]];
thisTimeLabel.numberOfLines = 0;
thisTimeLabel.textAlignment = NSTextAlignmentLeft;
thisTimeLabel.text = timeLabelString;
[thisTimeLabel sizeToFit];
}
completion:^(BOOL finished)
{
[self repositionLabels];
}];
[dayBarArray addObject:thisBar];
[infoLabelArray addObject:thisInfoLabel];
[timeLabelArray addObject:thisTimeLabel];
taDuration = 0;
tickerCount ++;
lastBarY = barY;
lastHeight = barHeight;
lastBarFrame = thisBar.frame;
[self positionInitialDayBarView];
}
}