animation completion called immediately completion - ios

I am now confused a lot .
This condition is normal when the value yes animation action .
But state when no value completion called immediately.
I do not know if there is any difference between the two .
- (void)setDimView : (UIView*)targetView state:(BOOL)state
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
UIView *dim = [[UIView alloc]initWithFrame:screenRect];
dim.tag = TAG;
if (state)
{
dim.alpha = 0.0;
}
[UIView transitionWithView:dim duration:0.5 options:UIViewAnimationOptionCurveEaseOut animations:^ {
if (state)
{
[targetView setHidden:NO];
[targetView addSubview:dim];
dim.backgroundColor = [UIColor blackColor];
dim.alpha = 0.6;
}
else
{
dim.alpha = 0.0;
}
}completion:^(BOOL finished) {
if (!state)
{
for (UIView *subview in [targetView subviews])
{
if (subview.tag == TAG)
{
[subview removeFromSuperview];
[targetView setHidden:YES];
}
}
}
}];
}

This block working sequence is first it called animation block and then completion block, but if you are set animation NO then its immediately call to completion.
I am not getting why you are so confused about it?

Difference between animation block and completion block
The code inside the animation block called as a loop. if a view's frame change is set in animation block it will execute as animation it take a duration to execute your code which you can configure. but if you are setting this view's frame change in completion block this will execute in single stretch after the completion of animation.

Related

keyboard not show at first time(iOS)

My code like this:
- (void)setupSubViews {
[self addSubview:self.textView];
[self addSubview:self.sendButton];
[self.textView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(5);
make.bottom.equalTo(-5);
make.leading.equalTo(9);
}];
[self.sendButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(self);
make.trailing.equalTo(-9);
make.leading.equalTo(self.textView.mas_trailing).offset(7);
make.width.equalTo(60);
make.height.equalTo(40);
}];
}
The show function to make the textView becomeFirstResponder
- (void)show {
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
[keyWindow addSubview:self];
[self mas_makeConstraints:^(MASConstraintMaker *make) {
make.leading.trailing.equalTo(0);
make.bottom.equalTo(InputHeight);
}];
[self.textView becomeFirstResponder];
}
This view add to the keyWindow at the bottom of mainScreen, it's translate will changing follow the keyboard when the keyboard's frame changed.
- (void)keyboardWillChangeFrame:(NSNotification *)noti {
if (!self.textView.isFirstResponder) {
return;
}
NSValue *endFrame = noti.userInfo[#"UIKeyboardFrameEndUserInfoKey"];
CGFloat endY = [endFrame CGRectValue].origin.y;
BOOL isBackToBottom = endY == UI_SCREEN_HEIGHT;
CGFloat needOffset = endY - (UI_SCREEN_HEIGHT + InputHeight);
if (isBackToBottom) {
[UIView animateWithDuration:0.25 animations:^{
self.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
} else {
[UIView animateWithDuration:0.25 animations:^{
self.transform = CGAffineTransformMakeTranslation(0, needOffset);
}];
}
}
Use Case:
viewController has a button, button click action like this:
- (void)beginChat {
[self.inputView show];
}
self.inputView is the above customView,
- (LiveChatInputView *)inputView {
if (!_inputView) {
_inputView = [LiveChatInputView new];
_inputView.sendBlock = ^(NSString *string) {
....
};
}
return _inputView;
}
Now my question is that when we call show function at the first time after application launched, the keyboard will not show, but call show function in second time, every thing is fine.
How to deal with this question, THX.
Calling this method is not a guarantee that the object will become the first responder.
As per Apple,
A responder object only becomes the first responder if the current
responder can resign first-responder status (canResignFirstResponder)
and the new responder can become first responder.
Try calling becomeFirstResponder like below,
[self.textView performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0];
I made a mistake, call [self removeFromSuperview] in the function of keyboardWillChangeFrame:.
I thought UIKeyboardWillChangeFrameNotification could only post it once at the keyboard show or hide, so i put the implementation of back action in keyboardWillChangeFrame:, and controlled by endFrame.origin.y == UI_SCREEN_HEIGHT
In practice, the fact is that:
when the keyboard show, NSNotificationCenter will post UIKeyboardWillChangeFrameNotification three times:
first
second
third
Tips:
only first time show the keyboard after application launched, the first UIKeyboardWillChangeFrameNotification of above three, the endFrame.origin.y is equal to UI_SCREEN_HEIGHT
My solution:
Observer the UIKeyboardWillHideNotification, and do back action in this callback.
Thanks for all answers of this question.

How to animate deletion of a label?

I have a view controller that have a scroll view that bounce horizontally , and in this scroll view I have a label.
I can now hold the label and scroll it down, and if I release it will bounce up back.
What I want is that: When I scroll the view y coordinate (using myScrollView.contentOffset.y) to some value, lets say -33 and under I can release my fine and the label will animate to the bottom of the screen and disappear, and now I can set the label to be a new value, and It will animate from top to the label original position.
Here a photo of how the view controller looks like:
And this is the relevant method I already implemented (powered by #rebello95):
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (self.myScrollView.contentOffset.y <= -73) {
[UIView animateWithDuration:0.3 animations:^{
self.homeLabel.alpha = 0.0;
} completion:^(BOOL finished) {
[self.homeLabel removeFromSuperview];
self.homeLabel = nil;
}];
}
NSLog(#"%f", self.myScrollView.contentOffset.y);
}
Now I want it to slide to the bottom of the page and fade.
thanks!
EDIT: The animation now moves the label to the bottom of the view controller then fades it out.
You can use an animation block to move the label, then put another block inside the completion block to fade out the label, then remove it after the animation completes.
Example:
[UIView animateWithDuration:0.3 animations:^{
[self.myLabel setFrame:CGRectMake(self.myLabel.frame.origin.x, self.view.frame.size.height - self.myLabel.frame.size.height, self.myLabel.frame.size.width, self.myLabel.frame.size.height)];
self.labelRemoving = YES;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 animations:^{
self.myLabel.alpha = 0.0;
} completion:^(BOOL finished) {
[self.myLabel removeFromSuperview];
self.myLabel = nil;
self.labelRemoving = NO;
}];
}];
Sidenote: You should probably be using <= instead of == in your if statement to achieve your desired results. In addition, you may want to set a flag to indicate that your label is being removed (since that method will inevitably be called multiple times). Something like this:
//.h
#property (nonatomic, assign) BOOL labelRemoving;
//.m
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (self.myScrollView.contentOffset.y <= -33 && !self.labelRemoving) {
[UIView animateWithDuration:0.3 animations:^{
[self.myLabel setFrame:CGRectMake(self.myLabel.frame.origin.x, self.view.frame.size.height - self.myLabel.frame.size.height, self.myLabel.frame.size.width, self.myLabel.frame.size.height)];
self.labelRemoving = YES;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 animations:^{
self.myLabel.alpha = 0.0;
} completion:^(BOOL finished) {
[self.myLabel removeFromSuperview];
self.myLabel = nil;
self.labelRemoving = NO;
}];
}];
}
NSLog(#"%f", self.myScrollView.contentOffset.y);
}

Stop UIView animation or detect if view is animating

There's an array with uiviews. Their frames are set due to current interfaceOrientation
Then we add these views to self.view programmatically using
-(void)placeView
{
if ([self.arrayWithViews count] > 0)
{
[UIView animateWithDuration:1.f animations:^{
UIView *currentView = [self.arrayWithViews lastObject];
[currentView setAlpha:0.f];
[self.view addSubview:currentView];
[currentView setAlpha:1.f];
[self.arrayWithViews removeLastObject];
} completion:^(BOOL finished) {
[self placeView];
}];
}
}
Now, if the orientation change happens the views keep adding (with coordinates corresponding to previous orientation).
The question is - how to stop the animation process or how to prevent interface orientation change while the animation takes place.
P.S. [self.view.layer removeAllAnimations] doesn't work (I didn't figure out why)
Could you please advise what to do?
removeAllAnimations will still call completion block, so you repeat to call placeView, so you need add a flag:
-(void)placeView
{
if(!self.runAni){ //Add a flag to stop repeat to call [self placeView]
return;
}
if ([self.arrayWithViews count] > 0)
{
[UIView animateWithDuration:1.f animations:^{
UIView *currentView = [self.arrayWithViews lastObject];
[currentView setAlpha:0.f];
[self.view addSubview:currentView];
[currentView setAlpha:1.f];
[self.arrayWithViews removeLastObject];
} completion:^(BOOL finished) {
[self placeView];
}];
}
}
to stop animation, you should do :
self.runAni = NO;
[self.view.layer removeAllAnimations];
begin to do:
self.runAni = YES;
[self placeView];

difficulty removing HudView after animation completes

I have made a game of Tic-Tac-Toe. If the game ends in a tie, an animated hudView appears for a couple of seconds. Then the game starts over, and that's when my problems occur. It doesn't respond to me tapping on screen to draw 'X' or 'O''s anymore. My suspicion is that the hudView is still there. I have tried different things to remove it, with no luck.
+ (instancetype)hudInView:(UIView *)view animated:(BOOL)animated
{
HudView *hudView = [[HudView alloc] initWithFrame:view.bounds];
hudView.opaque = NO;
[view addSubview:hudView];
view.userInteractionEnabled = NO;
[hudView showAnimated:YES];
return hudView;
}
And the animation:
- (void)showAnimated:(BOOL)animated
{
if (animated) {
self.alpha = 0.0f;
self.transform = CGAffineTransformMakeScale(1.3f, 1.3f);
[UIView animateWithDuration:4.5 animations:^{
self.alpha = 1.0f;
self.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
self.alpha = 0.0f;
}];
}
}
In the completion block, I have tried the following:
[self.superview removeFromSuperview];
In my ViewController where all of this gets called I've tried:
HudView *hudView = [HudView hudInView:self.view animated:YES];
hudView.text = #"\tIt's a Tie\n";
[hudView removeFromSuperview];
[self.view removeFromSuperview]
[hudView.superview removeFromSuperview];
Nothing I've tried so far is working. Any help would be much appreciated.

How to check if NSLayoutConstraint is animating

I am creating a custom NSLayoutConstraint subclass and I need to know if the layout constraint's constant property is currently animating for internal state handling. In other words, I need to distinguish between:
{ //no animation
myLayoutConstraint.constant = 100;
}
and
{ //animated
myLayoutConstraint.constant = 100;
[UIView animateWithDuration:0.2 animations:^{
[self.myViewThatHasTheConstraintAttached layoutIfNeeded];
} completion:^(BOOL finished) {
[...]
}];
}
So that I can handle corner cases for receiving a message on the middle of an animation. Is this possible?
The only way to do this would be to have a boolean wherever you want to access this and do something like...
{ //no animation
theView.animatingChange = NO;
myLayoutConstraint.constant = 100;
}
{ //animated
theView.animatingChange = YES;
myLayoutConstraint.constant = 100;
[UIView animateWithDuration:0.2 animations:^{
[self.myViewThatHasTheConstraintAttached layoutIfNeeded];
} completion:^(BOOL finished) {
[...]
theView.animatingChange = NO;
}];
}
The property on the view changes immediately to the "end" value of the animation. It doesn't get changed to all the intermediate values while it is animating. Just the drawing on the screen is animated.

Resources