I just switched from iOS 7.1 to iOS 8.1. I ran into the following problem: UIView animateWithDuration . Pictures were worth a thousand words, I made a video about the behavior under each iOS:
iOS 7.1:
https://vimeo.com/113887812
iOS 8.1:
https://vimeo.com/113887814
This app is for iPhone.
I should note that before posting I was looking for SO and other places. This seems like a recognized issue, but I found some links to “using constraint based animation” (which I didn't understand) and turning off auto-layout, but no real consensus.
I tried a couple of strokes, but did not disappear. If someone can point me (or skip me) an explicit fix, I would really appreciate it.
I should mention that the application is designed to work only in portrait position, so I did not think about automatic layout during the development process.
All considered species are created programmatically, with the exception of the scroller itself.
Here is the code. I have included the whole method, which is quite long, but I really do not understand what has changed in the new iOS, so I am safe. The call to animateWithDuration pretty deep in 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]; } }