UIView animateWithDuration在iOS 8中performance有所不同

我刚刚从iOS 7.1切换到了iOS 8.1。 我遇到以下UIView animateWithDuration问题。 图片胜过千言万语,我制作了每个iOS下的行为video:

iOS 7.1:

iOS 8.1:

这是一个iPhone应用程序。

我应该注意到,在发布之前,我已经search了SO和其他地方。 这似乎是一个公认的问题,但是我发现有一些“使用基于约束的animation”(我不明白),closures了自动布局,但没有真正的共识。

我尝试了几次刺,但没有得到。 如果有人能指出我(或通过一个明确的解决办法),我真的很感激。

我应该提到的是,应用程序的目的只是在肖像位置,所以我没有想到在开发过程中的自动布局。

所有涉及的视图都是以编程方式创build的,除了滚动条本身。

这是代码。 我包括了很长的整个方法,但是我真的不明白在新的iOS中已经发生了什么变化,所以在安全方面。 animateWithDuration调用非常深入到块中:


 -(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]; } } 

好,首先。 拒绝使用Interface Builder会导致很多便利。 这就像说:“我只使用Xcode 3,因为它曾经为我工作,所以我只保留它”。 界面生成器是一个非常好的工具,使创build用户界面非常容易。

实际的问题

你说你没有使用AutoLayout。 这是不正确的。

默认行为(在iOS8中,我认为也是iOS7)是,当你创build一个UIView (或任何UI元素)并将其添加到视图,然后系统将采取视图的auto resizing mask ,并应用自动布局约束匹配resize的面具。

这意味着当你做…

 UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 21)]; [self.view addSubView:label]; 

然后,标签将自动布局约束添加到它将其固定到位置(10,10)和大小(100,21)。

你不能通过做…改变框架…

 label.frame = CGRectMake(10, 50, 100, 21); 

因为自动布局的限制只是把它放在开始的地方。

解决scheme

有两个解决scheme。

正确的解决scheme是学习如何使用AutoLayout。 它已经出现了3年,对于iOS开发来说变得越来越重要(即使在纵向iPhone应用程序中,您现在也有4种不同的设备尺寸可供处理)。 如果你没有开始学习AutoLayout,你将被抛在后面。 现在对于所有的iOS开发来说,这都是非常需要的。

不正确的,非常糟糕的解决scheme是阻止系统添加这些限制。 你可以通过添加行来做到这一点…

 label.translatesAutoresizingMaskIntoConstraints = NO; 

在创build标签(和所有其他界面元素)之后。

这将停止添加这些自动约束,并阻止它们干扰您的布局的东西。

这是错误的解决scheme。 如果你这样做,你只是延迟了你必须学习自动布局的点。

我仍然不明白为什么切换到iOS 8带来了如此大的变化

我期望有一个不同的原因是,在iOS 8中设置标签文本(在animation块中正在执行的操作)会立即触发布局,而iOS 7则不然。布局意味着现在所有的约束条件都是立即服从,从而使一切恢复到原来的位置(因为约束没有改变)。

例如,请参阅我的答案: https : //stackoverflow.com/a/26964376/341994

在这里: https : //stackoverflow.com/a/27085648/341994

在这里: https : //stackoverflow.com/a/26953986/341994