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];
}];
}
Related
I want to add a sheet to the KeyWindow( [[[UIApplication sharedApplication ].delegate window ] ), with animation when user taps the avatar.
I want the sheet appears straight downToUp , not from the origin ( the left and bottom endpoint), by changing the constraint's constant.
The issue sometimes happens, sometime it behaves as expected.
I guess the coordinator-layout matters.
Here is my code: Create and show.
#implementation ZBPhotoSheetView
- (void)awakeFromNib
{
[super awakeFromNib ];
self.backgroundColor = [[UIColor blackColor ] colorWithAlphaComponent: 0.3 ];
//self.vesselViewBottomConstraint.constant = -150; // add it or not ,don't like it matters.
}
+ (ZBPhotoSheetView *)createZhePhotoSheetView
{
ZBPhotoSheetView * zheSheetView = [[[NSBundle mainBundle ] loadNibNamed:#"ZBPhotoSheetView" owner: nil options: nil ] firstObject ];
return zheSheetView;
}
- (void)showZhePhotoSheetView{
if (!_sheetIsShowing){
[[[UIApplication sharedApplication ].delegate window ] addSubview: self ];
self.frame = [UIScreen mainScreen ].bounds;
_sheetIsShowing = YES;
[UIView animateWithDuration: 0.3f delay:0.f options: UIViewAnimationOptionCurveEaseIn animations:^{
self.vesselViewBottomConstraint.constant = 0;
// [self setNeedsUpdateConstraints ]; // add it or not ,don't like it matters.
[self layoutIfNeeded];
} completion:^(BOOL finished) {}];
}
}
Here is my Xib: The sheet starts out of the scene's bottom.
You need to call layoutIfNeeded once before changing the constraint constant. You just added the view in window, so the initial auto layout and your constraint changes happing together in animation. Try this.
- (void)showZhePhotoSheetView{
if (!_sheetIsShowing){
[[[UIApplication sharedApplication ].delegate window ] addSubview: self ];
self.frame = [UIScreen mainScreen ].bounds;
_sheetIsShowing = YES;
[self layoutIfNeeded];
[UIView animateWithDuration: 0.3f delay:0.f options: UIViewAnimationOptionCurveEaseIn animations:^{
self.vesselViewBottomConstraint.constant = 0;
// [self setNeedsUpdateConstraints ]; // add it or not ,don't like it matters.
[self layoutIfNeeded];
} completion:^(BOOL finished) {}];
}
}
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...
This is a continuation of this question
CGRectMake : How To Calculate X and Y For UIPickerView Animation?
answered by https://stackoverflow.com/users/2315974/danypata
I have a picker view that I want to animate on and off screen on a button press and using the code in the previous question/answer I have got it to animate on screen the first time the button is pressed.
the second tome the button is pressed it just disappears and then the third time it is pressed it animates to the wrong place...please help
the code
if (self.pickerSort.hidden == NO) {
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.pickerSort.frame = CGRectMake(0,self.view.frame.size.height
+ self.pickerSort.frame.size.height,-self.pickerSort.frame.size.width,-self.pickerSort.frame.size.height);
}
completion:^(BOOL finished) {
}];
self.pickerSort.hidden = YES;
// [self performSelector:#selector(hidePicker) withObject:nil afterDelay:1];
} else if (self.pickerSort.hidden == YES) {
self.pickerSort.hidden = NO;
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
self.pickerSort.frame = CGRectMake(0 ,
self.view.frame.size.height
- self.pickerSort.frame.size.height,
self.pickerSort.frame.size.width,
self.pickerSort.frame.size.height);
}
completion:^(BOOL finished) {
}];
[self.view bringSubviewToFront:self.pickerSort];
Behavior in images - button press animates onto screen beautifully
Second Press it disappears with no animation
Third Press it animates to here
Any help would be great...functionality I am looking for is how to put the picker view back on an animation so the 3rd press is the same as the first
Please try to use the below code.
self.pickerSort.hidden = NO;
NSTimeInterval duration = 0.5;
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
CGRect pickerFrame = self.pickerSort.frame;
// currently picker is off the screen, show it.
if (pickerFrame.origin.y == self.view.frame.size.height) {
// set frame to show picker
pickerFrame.origin.y = self.view.frame.size.height - self.pickerSort.frame.size.height;
} else {
// set frame to hide picker
pickerFrame.origin.y = self.view.frame.size.height;
}
self.pickerSort.frame = pickerFrame;
}
completion:^(BOOL finished) {
self.pickerSort.hidden = YES;
}];
After playing around with this and learning the ins and outs of frames and CGRects, I achieved this by using the following code
if (pickerSort.hidden == YES) {
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
pickerSort.hidden = NO;
visualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
[self.tableStations addSubview:visualEffectView];
pickerSort.frame = CGRectMake(0,0,originalPickerFrame.size.width,originalPickerFrame.size.height);
visualEffectView.frame = CGRectMake(0,0,originalPickerFrame.size.width,originalPickerFrame.size.height - 64);
}
completion:^(BOOL finished) {
self.navigationController.navigationItem.leftBarButtonItem.enabled = NO;
}];
}
else
{
visualEffectView.hidden = YES;
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
pickerSort.frame = CGRectMake(0,-originalPickerFrame.size.height,0,0);
}
completion:^(BOOL finished) {
self.navigationController.navigationItem.leftBarButtonItem.enabled = YES;
pickerSort.hidden = YES;
}];
}
I set the original frame size in viewDidLoad like so
pickerSort = [[UIPickerView alloc]initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, self.view.frame.size.height)];
originalPickerFrame = pickerSort.frame;
pickerSort.frame = CGRectMake(0,-originalPickerFrame.size.height,0, 0);
Hope this can help somebody in the future
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);
}
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.