UIView animateWithDuration behaves differently in iOS 8

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]; } } 
0
ios iphone animation uiview
Dec 09 '14 at 18:01
source share
2 answers

OK, first. You lose a lot of convenience by refusing to use Interface Builder. I like to say, "I only use Xcode 3 because it worked for me, so I just saved it." Interface Builder is a great tool that makes creating user interfaces incredibly easy.

Actual problem

You said you are not using AutoLayout. This is not true.

The default behavior (in iOS8 specifically, as well as iOS7, I think) is that when you create a UIView (or any user interface element) and then add it to the view, then the system will take an auto resizing mask for the view and apply the Auto Layout restrictions for matching mask resizing.

This means that when you ...

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

Then, restrictions on the automatic layout were added to the label to fix it in position (10, 10) and size (100, 21).

You cannot change the frame by doing ...

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

Because the limitations of the automatic layout will simply return it to where it started.

Solutions

There are two solutions.

The correct solution is to learn how to use AutoLayout. It has existed for three years and is becoming increasingly important for the development of iOS (even on iPhone with a portrait for iPhone, you now have 4 different sizes of devices). If you do not start learning AutoLayout, you will stay. This is pretty much a requirement for all iOS developments.

The solution is wrong and very bad - to prevent the system from adding these restrictions. You can do this by adding a line ...

 label.translatesAutoresizingMaskIntoConstraints = NO; 

after creating the label (and all other interface elements).

This will stop adding these automatic constraints and stop them from interfering with your layout material.

This is the wrong decision . If you do this, you simply delay the point at which you should examine the Auto Market.

+5
Dec 09 '14 at 18:14
source share

I still don’t understand why the transition to iOS 8 has led to such a big change.

I expect that one of the reasons for the difference is that setting the label text (which you do in the animation block) launches the layout right away in iOS 8, which was not in iOS 7. Layout means that now all restrictions are immediately fulfilled, therefore all returns to its original position (because the restrictions have not changed).

See, for example, my answer is here: https://stackoverflow.com/a/166778/

And here: https://stackoverflow.com>

And here: https://stackoverflow.com/a/464829/

0
Dec 12 '14 at 23:38
source share



All Articles