I have a problem that my custom navigation bar appears for the first time with animation. I think it happens because I use auto-layout and it animates it self into the state of landscape or portrait. But I want to have a functionality, that after first time I enter the screen everything is still, and where it belongs. and after that if I turn the screen or if I do something all the animations appears like now.
Is there a good think to omit the first animations when the view creates itself?
The animations are : labels floating from left to right. and 1 label appears as from 0px width and height it scales into 100% width and height
code:
[self.navigationController.navigationBar addSubview:self.topBar];
self.topbar is UIView. I have added it to block [UIView performWithoutAnimation:^{ }]; but it does not helps, everytime the view appears for the first time I have my labels floating from left to right.
The answer is that I used some methods [self layoutIfNeeded]; After setting the constraint. The solution is to have a method set like this:
- (void) yourAnimation:(BOOL)animated{
if(animated){
[UIView animateWithDuration:0.3 animations:^{
//your animated code
//[self layoutIfNeeded];
}];
}else{
[UIView performWithoutAnimation:^{
//your stuff without animation
}];
}
}
Related
I have this strange new issue: this code (that works perfectly)
[UIView animateWithDuration:0.4 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
CGRect frame = _logoClaim.frame;
frame.origin.y -= 180;
_logoClaim.frame = frame;
} completion:NULL];
moves a view that contains a UIImageView and an UILabel to the top of my self.view.
The view that moves unhides a UITextField.
When I try to write text into the UITextField, obviously appear the keyboard.
And at this moment, the view animated before, returns to the original start position!!!
What is the reason?
Put a completion block in your animation and check the finished value. The keyboard is cancelling the animation and the finished bool will be NO. I would disable input in the UITextField until the animation is completed. Do this in your completion block.
EDIT
Looking at your duration and re-reading the question,I may be mistaken with what I think you mean. If this answer is incorrect I will remove it.
Also, search your code for _logoClaim.frame in case you are adjusting it onKeyboardWillAppear
You would need to create outlet for its bottom/top space constraints and update the constant value of it inside the animation block.
[UIView animateWithDuration:0.4
...
animations:^{
/*Update the value of the constraint outlet supposing it to be a bottom space constraint*/
_layoutConstraintBottom += 180;
[self.logoClaim layoutIfNeeded];
} completion:NULL];
Do not forget to call -layoutIfNeeded for your animating view.
I have a UILabel inside of a UITableViewCell. I am trying to animate the label moving to the right when the user taps the cell. I have this code:
CGRect otherFrame = cellLabel.frame;
otherFrame.origin.x +=50;
[UIView animateWithDuration:1.0f animations:^{
cellLabel.frame = otherFrame;
}];
The odd thing that's happening is that the label is jumping 50 pixels to the left and animating back to its origin (where it was before the action began).
I actually had this working earlier in the week and didn't have any trouble with it, and after scouring through the revision history, I can't figure out where I've gone wrong. I must be missing something stupid.
EDIT:
Based on the answer from Jakub, I found that this works:
[UIView animateWithDuration:0.01f animations:^{}
completion:^(BOOL finished)
{
CGRect otherFrame = cellLabel.frame;
otherFrame.origin.x += 50;
[UIView animateWithDuration:1.0f animations:^{
cellLabel.frame = otherFrame;
}];
}];
Oddly, if I move all of the logic into a completion handler that performs a new animation after the first one completes (without the first actually doing anything), everything animates properly. This is super hacky, and I'm not at all happy with it.
Can anyone think of what would cause the initial animation to move the frame to the negative offset of the intended destination, only to animate it back to its origin, and why triggering a new animation as the completion handler of an empty animation would work?
EDIT 2:
I am going to mark Duncan's answer as the right one because it pointed me in the right direction, but I am still baffled why/how these symptoms can happen:
The animation moves the frame to the negative offset of the
destination, then animates it back to the origin (rather than from
the origin to the destination)
Running an empty animation block but adding another animation to the first block's completion handler animates correctly
Info for the attempted answers:
I have to use auto layout for the project, and as far as I know, I
can't disable it for a single view
I am not putting anything onto a background thread. I didn't try specifically dispatching to the main thread, but I think I was already there. Isn't all visible UI always on the main thread? It was animating, just not as expected.
The reason I didn't go the route of changing constraints to begin with is that I am using prototype cells and IB doesn't let you create IBOutlets from prototype cells. There's a bit of work to walk a constraint list and detect a specific one, so I left the constraints blank and tried to animate the frame (and as I said, it was working earlier in the week -- and still worked when animating from an animation block's completion handler).
So the final solution was to add constraints to the container cell (the important one here being the leading), then to animate, I had to find the constraint:
NSLayoutConstraint *titleLeadingConstraint = nil;
for( NSLayoutConstraint *constraint in cellLabel.superview.constraints )
{
if( constraint.firstItem == cellLabel && constraint.firstAttribute == NSLayoutAttributeLeading )
{
titleLeadingConstraint = constraint;
}
}
Then set the constraint constant:
titleLeadingConstraint.constant = 55.0;
Then set the animation block:
[UIView animateWithDuration:1.0f animations:^{
[self.view layoutIfNeeded];
}];
This solution should be more future proof (and robust, reliable and stable) than moving the frame, but it turned out to be a fair amount of work in discovery.
Do you have auto layout set in your storyboard or XIB by mistake? (It is on by default). If so you either need to turn AutoLayout off or animate a constraint rather than manipulating your view's frame.
Where are you calling this code?
Try calling UIView animateWithDuration:... method on the main thread, in a
dispatch_async(dispatch_get_main_queue(), ^{
// Your animation here
});
If this method is not executed on the main thread, it usually jumps to the final stage of the animation.
You should reset the origin after animation.
Background
I'm working on a quick and dirty notes app purely to try to understand autolayout. As such I am looking for an autolayout-specific solution to this problem.
I am quite sure that my terminology and understanding of this subject may be incorrect in places so if I misphrase or omit information through ignorance that would otherwise be helpful I am very happy to update this question with better specifics.
Short Problem Summary
This app is a simple note app. On the detail view of the note, there are two text input views, a UITextField, and a UITextView.
The goal is to use autolayout to animate a change of height to the UITextView when it is being edited (making room for the keyboard), and then animate the UITextView back to it's original size when editing is finished.
The animation code I have in place works, however when the UITextView is scrolled near to the bottom of the text the animation from "editing" size to "non-editing" size displays incorrectly durring the animation. (The final result of the animation, however is correct.)
I'm open to alternate "correct" ways of doing this if there's a common pattern for the solution. I am, however, looking for an autolayout solution which, I believe, means avoiding modifying the view's frame directly. (Could be wrong on that.)
Details and Code
A short video of the problem is available here:
http://pile.cliffpruitt.com/m/constraint_problem.mp4
This is the code performing the animation:
// self.bodyFieldConstraintBottom refers to an outlet referencing the UITextView's bottom constraint
// This animation occurrs when the text view is tapped
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
[self enterEditingMode];
[UIView animateWithDuration:2.35
animations:^{
NSLayoutConstraint *bottom_constraint = self.bodyFieldConstraintBottom;
bottom_constraint.constant = 216;
[self.view layoutIfNeeded];
}];
return YES;
}
// This animation occurrs when editing ends and the text field size is restored
- (BOOL)textViewShouldEndEditing:(UITextView *)textView
{
[self exitEditingMode];
[UIView animateWithDuration:2.35
animations:^{
NSLayoutConstraint *bottom_constraint = self.bodyFieldConstraintBottom;
bottom_constraint.constant = 20;
[self.view layoutIfNeeded];
}];
return YES;
}
Full project source (in all it's messy glory) can be downloaded here:
http://pile.cliffpruitt.com/dl/LittleNotebooks.zip
Additional Comments
My understanding of cocoa terminology isn't the best so I'm having a hard time making google searches and docs searches effective. My best guess about the problem (based on observing the animation at a slow speed) is that it is related to a scroll offset somehow because unless the text is scrolled past a certain point, the problem does not manifest itself.
I have read quite a few SO question/answers including:
Resizing an UITextView when the keyboard pops up with auto layout
How to resize UITextView on iOS when a keyboard appears?
UIScrollView animation of height and contentOffset "jumps" content from bottom
The problem is that these answers either do not work ([self.bodyField setContentInset:UIEdgeInsetsMake(0, 0, 216, 0)]; seems to have no effect) or appear to rely on setting the frame of the UIText view which I believe is not supposed to be done when using autolayout.
Final Side Note
I've been at this off and on for about 4 days so my understanding and recollection of all I've read and tried is actually a little less clear than when I'd started. I hope I'm explaining this well enough to convey the issue.
EDIT:
I've noticed that this code actually gets somewhat close to the desired result:
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
[self enterEditingMode];
[UIView animateWithDuration:2.35
animations:^{
[self.bodyField setContentInset:UIEdgeInsetsMake(0, 0, 216, 0)];
[self.view layoutIfNeeded];
}];
return YES;
}
- (BOOL)textViewShouldEndEditing:(UITextView *)textView
{
[self exitEditingMode];
[UIView animateWithDuration:2.35
animations:^{
[self.bodyField setContentInset:UIEdgeInsetsMake(0, 0, 0, 0)];
[self.view layoutIfNeeded];
}];
return YES;
}
The problem with this version is that the scroll indicator scrolls down past the visible area of the text content, meaning it gets "lost" behind the keybaord. Also, it does not help me understand the correct way to animate a UITextView (UIScrollView ?) bottom constraint.
The issue looks weird and I am really not sure whats the main issue but I found out that for the best results you should call [self.view setNeedsUpdateConstraints]; before animating view.
My example code to animate view when keyboard appears:
-(void)keyboardWillShow:(NSNotification *)notification {
CGSize kbSize = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
//BH: iOS7 is messed up
CGFloat keyboardHeight = kbSize.width;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
keyboardHeight = kbSize.height;
}
self.centerYConstraint.constant = keyboardHeight;
[self.view setNeedsUpdateConstraints];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:[notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
[UIView setAnimationCurve:[notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]];
[UIView setAnimationBeginsFromCurrentState:YES];
[self.view layoutIfNeeded];
[UIView commitAnimations];
}
I am using commit animations to animate view with same animationCurve as iOS is animating keyboard so view is moving 1 to 1 accordingly to keyboard. Also, please notice if statement for iOS8 vs iOS7 where Apple finally fixed window sizing.
My button rotates upon clicks in an animated way by using UIView's animateWithDuration:animations:, however the button is not animating.
I'm trying to reimplement UITableView's disclosure indicator. I am making my own because UITableView's is not flexible enough. For example, I would like mine to be on the left, but this is not possible.
My disclosure button subclass almost works. It rotates correctly, but when it rotates there is no animation. It should goes from the non rotated to rotated; there is no animation in between.
#define DegreesToRadians(x) ((x) * M_PI / 180.0)
- (void)rotateToOpenPosition {
[UIView animateWithDuration:0.5 animations:^{
[self setTransform: CGAffineTransformMakeRotation((CGFloat)DegreesToRadians(90))];
}];
}
- (void)rotateToClosedPosition {
[UIView animateWithDuration:0.5 animations:^{
[self setTransform: CGAffineTransformMakeRotation((CGFloat)DegreesToRadians(0))];
}];
}
I'm also using Auto Layout in the nib that contain the UITableViewCell the disclosure button is in.
This might be related to the problem. Here are the constraints:
EDIT: This is most likely not the problem, as I tried removing auto layout from the same nib, and the problem still occurs.
Why is it not rotating, and how can I fix it?
I was calling [tableView reloadData] right after I called the methods to start animating the button.
Stupid.
I have a UIView "MainView" that initially appears as follows:
The gradient bar is part of MainView, the whitespace beneath is part of a Container View subview.
When the search button in top-right is tapped, I animate a searchBar from offscreen to be visible in the view:
I manage to do this by the following code:
CGRect currentViewFrame = self.view.bounds;
currentViewFrame.origin.y += searchViewHeight;
[UIView animateWithDuration:0.4
delay:0.0
usingSpringWithDamping:1.0
initialSpringVelocity:4.0
options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.view.frame = currentViewFrame;
}
completion:^(BOOL finished)
{
}];
Visually, the result of this animation is perfect. The entire view shifts, and the searchBar is now on screen. However, the searchBar does not respond to interaction. Correct me if I'm wrong, but I expect this is because the MainView's frame no longer includes the screen area that the searchBar now occupies, so its effectively a gesture deadzone.
So this makes me think that instead of lazily animating the entire MainView down to accomodate the searchBar, I must instead individually translate all subviews of MainView one at a time. In this simple situation, that would not be a big problem, but I can envision a circumstance with tens of subviews making that completely unrealistic.
What is the best method to accomplish what I am trying to do? Is there a secret to animating entire views/subviews without having gesture deadzones? Thanks in advance!!