Calling layoutIfNeeded in animations block freezes UI on iPhone 7 - ios

Has anyone experienced the same issue, when calling layoutIfNeeded in animations block freezes UI?
I have a method:
- (void)hide {
dispatch_block_t onMain = ^{
CGFloat height = -viewContent.frame.size.height;
[UIView animateKeyframesWithDuration:0.15f delay:0 options:0 animations:^{
[UIView addKeyframeWithRelativeStartTime:0.f relativeDuration:0.7f animations:^{
self.constraintViewContentBottom.constant = height/3;
[self layoutIfNeeded];
}];
[UIView addKeyframeWithRelativeStartTime:0.7f relativeDuration:0.3f animations:^{
self.constraintViewContentBottom.constant = height;
[self layoutIfNeeded];
}];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.2f animations:^{
self.viewBackground.alpha = 0.0f;
}
completion:^(BOOL finished) {
self.hidden = YES;
}];
}];
};
if([NSThread isMainThread]) {
onMain();
}
else {
dispatch_sync(dispatch_get_main_queue(), onMain);
}
}
Which works perfectly on the many devices, but on the one iPhone 7, the line: [self layoutIfNeeded]; freezes UI.
I'm not able to debug that device, but can get some logs from it.
Does anyone know how we can solve the problem?
Appreciate any help.

Set the constraints' constants before the animation block. layoutIfNeeded is the only thing that has to be put into the block...

Related

Infinite loop when layoutIfNeeded called

I have a method that, via some delegate, manage my app base animation (UIAnimateWithDuration).
This method works fine for so long, everywhere in my app. After updating to Xcode 9, the method still works but in one controller it doesn't.
It seems to generate an infinite loop with the following message:
warning: could not execute support code to read Objective-C class data in the process. This may reduce the quality of type information available.
when [self.view layoutIfNeeded] called.
Here the snippet:
//MyTableViewDelegate.m
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.contentOffset.y <= 0.) {
[self.presenter gestureAnimationWithConstant:scrollView.contentOffset.y alpha:__alphaMaxContainer];
return;
}
}
//MyPresenter.m
-(void)gestureAnimationWithConstant:(float)y alpha:(double)alpha{
[self.view animationOnScrollingWith:y];
}
//MyViewController.m
-(void)animationOnScrollingWith:(float)constant{
self.constraint.constant += 1;
[self.view layoutIfNeeded]; //here it fires infinite loop
if (self.constraint.constant > - (self.containerView.frame.size.height-10)){
[self constraintAnimationWithAlpha:constant alpha:__maxAlphaContainer enableGesture:NO];
}
}
-(void)constraintAnimationWithAlpha:(float)constant alpha:(double)alpha enableGesture:(BOOL)enableGesture{
[UIView animateWithDuration:0.5 delay:0.0 usingSpringWithDamping:1.0 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.constraint.constant = constant;
[self.view layoutIfNeeded];
[self changeTopSectionAlphaValue:alpha];
} completion:^(BOOL finished) {
if (enableGesture)
self->__gesture.enabled = YES;
[UIView animateWithDuration:0.3 animations:^{
[self.view layoutIfNeeded];
}];
}];
}
Any idea? I'm going mad!

IOS/Objective-c: code reduction within animations

I have this simple animation that fades in several labels, one at a time. But I wonder if it is possible to reduce the code with this logic?
[UIView animateWithDuration:0.50f animations:^{
[_latLabel setAlpha:0.9f];
[_firstLat setAlpha:0.9f];
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.50f animations:^{
[_lonLabel setAlpha:0.9f];
[_firstLon setAlpha:0.9f];
}completion:^(BOOL finished) {
[UIView animateWithDuration:0.50f animations:^{
[_speedLabel setAlpha:0.9f];
[_firstSpeed setAlpha:0.9f];
}completion:^(BOOL finished) {
[UIView animateWithDuration:0.50f animations:^{
[_realNorthLabel setAlpha:0.9f];
[_firstReal setAlpha:0.9f];
}completion:^(BOOL finished) {
[UIView animateWithDuration:0.50f animations:^{
[_magneticNorthLabel setAlpha:0.9f];
[_firstMagnetic setAlpha:0.9f];
}];
}];
}];
}];
}];
Considering the need to perform operation on 2 label objects while executing any single section of the animation block, create a dictionary structure wherein section indexes define the chronological order of the sections animations to be performed & each section index contains an array of label objects.
A simple iterating loop over this structure christened here as dictionaryOfSectionedLabels can help achieve the desired alternate animation implementation.
NSDictionary<NSNumber *, NSArray *> *dictionaryOfSectionedLabels = #{ #0: #[_latLabel, _firstLat],
#1: #[_lonLabel, _firstLon],
#2: #[_speedLabel, _firstSpeed],
#3: #[_realNorthLabel, _firstReal],
#4: #[_magneticNorthLabel, _firstMagnetic]
};
for (NSUInteger i = 0; i < dictionaryOfSectionedLabels.allKeys.count; i++) {
[UIView animateWithDuration:0.5 delay:0.5*i options:UIViewAnimationOptionLayoutSubviews animations:^{
UILabel *firstLabel = [dictionaryOfSectionedLabels[#(i)] firstObject];
UILabel *secondLabel = [dictionaryOfSectionedLabels[#(i)] lastObject];
firstLabel.alpha = 0.9f;
secondLabel.alpha = 0.9f;
} completion:nil];
}
Creating an NSDictionary of your labels can be tedious and confusing code to read in the future. I'd recommend you just create a method.
-(void)customFadeForLabel:(UILabel *)theLabel withDelay:(float)delayAmount {
[UIView animateWithDuration:0.50f
delay:delayAmount
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
[theLabel setAlpha:0.9f];
}completion:nil];
}
Then just call it as needed with delays
[self customFadeForLabel:_latLabel withDelay:0.00f];
[self customFadeForLabel:_firstLat withDelay:0.00f];
[self customFadeForLabel:_lonLabel withDelay:0.50f];
[self customFadeForLabel:_firstLon withDelay:0.50f];
[self customFadeForLabel:_speedLabel withDelay:1.00f];
[self customFadeForLabel:_firstSpeed withDelay:1.00f];
[self customFadeForLabel:_realNorthLabel withDelay:1.50f];
[self customFadeForLabel:_firstReal withDelay:1.50f];
[self customFadeForLabel:_magneticNorthLabel withDelay:2.00f];
[self customFadeForLabel:_firstMagnetic withDelay:2.00f];

Continous fade in and fade out

Im trying to fade in and out of a light node I have created I just dont know where to call the function accordingly so it will continously fade in and out. Never tried animating anything here is what I have.
- (void)fadeOutIn:(UIView *)view duration:(NSTimeInterval)duration
{
[self enumerateChildNodesWithName:#"//*" usingBlock:^(SKNode *node, BOOL *stop) {
if ([node.name isEqualToString:#"light1"]) {
[UIView animateKeyframesWithDuration:5 delay:2 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse|UIViewAnimationCurveEaseInOut animations:^{
node.alpha = 0;
} completion:^(BOOL finished) {
}];
}
}];
}
where should I call this function?that is fades in and out continously?
Should it be called within an action?
Thank You
Use this for Continue fade in out.
-(void)viewDidAppear:(BOOL)animated{
[UIView animateKeyframesWithDuration:1 delay:0 options:UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse|UIViewAnimationCurveEaseInOut|UIViewAnimationOptionAllowUserInteraction animations:^{
node.alpha = 0;
} completion:^(BOOL finished) {
[UIView animateWithDuration:1 animations:^{
node.alpha = 1;
} completion:nil];
}];
}
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[objAnimate setAlpha:1.0];
[UIView animateKeyframesWithDuration:2.0 delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeLinear | UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse animations:^{
[objAnimate setAlpha:0.0];
} completion:nil];
}
This will create continuous fade in and out for your object when it appears. If you want to create programmatically a custom object and then use this code then do it after your custom object is added in your view as below (example):
objAnimate = [[UILabel alloc] initWithFrame:CGRectMake(20, 50, 200, 40)];
objAnimate.backgroundColor = [UIColor lightGrayColor];
[objAnimate setAlpha:1.0];
[self.view addSubview:objAnimate];
[UIView animateKeyframesWithDuration:2.0 delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeLinear | UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionRepeat|UIViewAnimationOptionAutoreverse animations:^{
[objAnimate setAlpha:0.0];
} completion:nil];
The view starts animating as soon as its added in your view.

UIView animation layoutIfneeded not working

I am using this message to animate a customer view to the lower edge of the device screen. I've connected the layout constraint and set initial constant as -60. When the value is set to 0, I expect the view to animate to top. But unfortunately the expected functionality is not working. Even the method is called, the view is hoped up when interface orientation changes. I am using Xcode 6.4 and iOS 8 as base sdks. Any help will be appreciated.
if (alertVisible)
{
self.alertViewVerticalConstraint.constant = 0;
[UIView animateWithDuration:0.25 animations: ^{
[self.view bringSubviewToFront:self.alertView];
self.alertView.alpha = 0.5;
[self.alertView layoutIfNeeded];
}];
}
else
{
self.alertViewVerticalConstraint.constant = -60;
[UIView animateWithDuration:0.25 animations: ^{
self.alertView.alpha = 0.0;
[self.alertView layoutIfNeeded];
}];
}
Whenever you want to animate the constraint changes, you have to defines the line within the animation block.
So your code will become something like this,
if (alertVisible)
{
[UIView animateWithDuration:0.25 animations: ^{
self.alertViewVerticalConstraint.constant = 0;
[self.view bringSubviewToFront:self.alertView];
self.alertView.alpha = 0.5;
[self.view layoutIfNeeded];
}];
}
else
{
[UIView animateWithDuration:0.25 animations: ^{
self.alertViewVerticalConstraint.constant = -60;
self.alertView.alpha = 0.0;
[self.view layoutIfNeeded];
}];
}

Create UIView Animation that will loop after the completion method is run

I'm having a bit of trouble. I've set my UIView Animation to repeat itself infinitely, however it does this before the animation starts it's completion function. I want it to wait until after the completion function fully runs because I want to run multiple animations. Any idea how to do this? If not is there a different way I can accomplish the same thing?
I'll post my code below:
-(void) createBlueControlAnimation:(int)messageCount{
if(messageCount == 0) {
return;
}
[blueControl setContentOffset:CGPointMake(0, 0)];
[UIView animateWithDuration:0.5 delay:2 options:UIViewAnimationOptionRepeat
animations:^{
[blueControl setContentOffset:CGPointMake(screenWidth, 0)];
} completion:^(BOOL finished){
if(messageCount >= 2){
[UIView animateWithDuration:0.5 delay:2 options:UIViewAnimationOptionAllowAnimatedContent
animations:^{
[blueControl setContentOffset:CGPointMake(screenWidth * 2, 0)];
} completion:^(BOOL finished){
if(messageCount == 3) {
[UIView animateWithDuration:0.5 delay:2 options:UIViewAnimationOptionAllowAnimatedContent
animations:^{
[blueControl setContentOffset:CGPointMake(screenWidth * 3, 0)];
}completion:^(BOOL finished){
[self scrollAnimationToStart];
}];
}else{
[self scrollAnimationToStart];
}
}];
}else{
[self scrollAnimationToStart];
}
}];
}
-(void) scrollAnimationToStart {
[UIView animateWithDuration:0.5 delay:2 options:UIViewAnimationOptionAllowAnimatedContent
animations:^{
[blueControl setContentOffset:CGPointMake(0, 0)];
} completion:^(BOOL finished){}];
}
So based on the number of messages I want the animation to continue running, and then when it gets to the last message, loop back to the first message and then restart the animation. However right now it just loops infinitely between the first and the second message. Any ideas on how to fix this would be greatly appreciated, thanks.
Firstly: You should create separate consts for anything that is in code.
extern NSTimeInterval const animationDuration;
NSTimeInterval const animationDuration = 0.5;
extern NSTimeInterval const animationDelay;
NSTimeInterval const animationDelay = 2;
Secondly: intent your code in reasonable way, it's quite hard to read what you've pasted. Use one method and push every repeatable part of code into another method.
- (void)createBlueControlAnimation:(NSUInteger)messageCount {
if (messageCount == 0) {
return;
}
__weak typeof(self) weakSelf = self;
[self offsetBlueControl:0];
[UIView animateWithDuration:animationDuration delay:animationDelay options:UIViewAnimationOptionAllowAnimatedContent animations:^{
[weakSelf offsetBlueControl:screenWidth];
} completion:^(BOOL finished) {
if (messageCount < 2) {
[self scrollAnimationToStart];
} else {
[UIView animateWithDuration:animationDuration delay:animationDelay options:UIViewAnimationOptionAllowAnimatedContent animations:^{
[weakSelf offsetBlueControl:screenWidth * 2];
} completion:^(BOOL finished) {
if (messageCount != 3) {
[self scrollAnimationToStart];
} else {
[UIView animateWithDuration:animationDuration delay:animationDelay options:UIViewAnimationOptionAllowAnimatedContent animations:^{
[weakSelf offsetBlueControl:screenWidth * 3];
} completion:^(BOOL finished) {
[self scrollAnimationToStart];
}];
}
}];
}
}];
}
- (void)scrollAnimationToStart {
[UIView animateWithDuration:animationDuration delay:animationDelay options:UIViewAnimationOptionAllowAnimatedContent animations:^{
[blueControl setContentOffset:CGPointMake(0, 0)];
} completion:^(BOOL finished) {}];
}
- (void)offsetBlueControl:(CGFloat)xOffset {
[blueControl setContentOffset:CGPointMake(xOffset, 0)];
}
I should mention here that this animation block is in fact similar everywhere. I'd set it as a separate method also.
Thirdly, keep same way of spacing everywhere (eg check out https://github.com/NYTimes/objective-c-style-guide).
That's all about code, the quality, readibility etc. I'd set it as comment, but there's too much code in my response, so it has to be an answer.
I don't fully understand what do you want to do. As far as I understood:
You have a UI element, called BlueControl.
You want to move this element.
Basing on some counter you want to move your view by some width (btw I advise to switch to NSIntegers from ints and use unsigned option, so NSUInteger etc). The move steps:
3.1. Move button right during 0.5s
3.2. Wait 2 seconds if counter is large enough, otherwise end step 3
3.3. Increase counter, repeat step 3.
You want to repeat the process infinitely.
Is that right? I need to understand the problem to edit this answer and paste a full response here.

Resources