I have a vertical navigation bar that lays out the items within it using AutoLayout. When I animate the navigation bar's position on and off screen, the layout of the items within the navigation bar is incorrect. Items are resized as if the bar is shrinking in width, when that is, in fact, not occurring. What am I doing wrong?
Video of issue (note that I colored the background of each button red so you can see it more easily):
"Link".
Code animating the navigation bar (in the view controller):
self.verticalNavBarLeadingSpace.constant = hidden ? -self.verticalNavBarWidth.constant : 0;
[UIView animateWithDuration:2 delay:0 options:UIViewAnimationOptionCurveEaseOut
animations:^{
[self.view layoutIfNeeded];
}
completion:nil];
The -updateConstraints method in my view (using FLK+AutoLayout to create constraints):
- (void) updateConstraints
{
// Wipe out existing constraints
[self removeConstraints:self.addedConstraints];
[self.addedConstraints removeAllObjects];
UIButton *lastItem;
NSString *height = [NSString stringWithFormat:#"%f", self.itemHeight];
for (UIButton *btn in _items) {
[self.addedConstraints addObjectsFromArray:[btn constrainHeight:height]];
[self.addedConstraints addObjectsFromArray:[btn alignLeading:#"0" trailing:#"0" toView:self]];
if (!lastItem) {
[self.addedConstraints addObjectsFromArray:[btn alignTopEdgeWithView:self predicate:#"0"]];
} else {
[self.addedConstraints addObjectsFromArray:[btn constrainTopSpaceToView:lastItem predicate:#"0"]];
}
lastItem = btn;
}
[super updateConstraints];
}
Related
I use this code in objective c to make a fade in fadeout animation when the user scrolls up or down the uitableview. Is there any way to make a nice animation like shrinking the navigationbar slowly while the user scrolls to top slowly? The more he scrolls to the top the more the navigationbar should shrink.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat scrollPos = self.tableView.contentOffset.y ;
if(scrollPos >= _currentOffset ){
//hide navbar and fadeout
[UIView animateWithDuration:0.50 animations:^{
[self.navigationController setNavigationBarHidden:YES animated:YES];
self.navigationController.navigationBar.alpha = 0.0f;
}];
} else {
//Slide it up incrementally and fadein, etc.
[UIView animateWithDuration:0.50 animations:^{
[self.navigationController setNavigationBarHidden:NO animated:YES];
self.navigationController.navigationBar.alpha = 1.0f;
}];
}
}
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
scrollView = self.tableView;
_currentOffset = self.tableView.contentOffset.y;
}
You cannot customize the size of UINavigationBar on your own. You can only hide/appear it. Apple does not allow you to do that.
You can use UIView instead! Just hide your UINavigationBar and put the same frame of UIView. Since UIView is highly customizable, you can do whatever you want.
I have a UIImageView which is centred horizontally and vertically in the launch screen. I want to display the same logo on the main storyboard (main view controller) when the launch screen goes, centred horizontally but at the top of the screen.
I have added the same image view to my main storyboard in the same position and added the following code to animate the image from centre to top of screen.
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self animateLogo];
}
- (void)animateLogo {
[UIView animateWithDuration:1 animations:^(void){
CGRect currentFrame = self.logoView.frame;
self.logoView.frame = CGRectMake(currentFrame.origin.x, self.view.frame.size.height, currentFrame.size.width, currentFrame.size.height);
}];
}
#end
However instead of seeing the animation move up, I see it move down from the top to centre.
Can someone point me in the right direction to fix this?
You should animate constraint instead frame:
- (void) viewDidAppear:(BOOL)animated {
self.topConstraint.constant = (self.view.frame.size.height - self.viewToAnimate.frame.size.height) / 2;
[self.viewToAnimate setNeedsUpdateConstraints];
[UIView animateWithDuration:.5 animations:^{
[self.viewToAnimate layoutIfNeeded];
}];
}
So i have a really weird problem at my hands and hours of search has provided nothing.
I have a uiview containing a uitextfield. Now initially this view is outside of the visible screen with coordinates like x=-500,y=-500.
However in response to a button press this view animates and moves into the center of the screen.
Now whenever i tap on a uitextfield that is the subview of this view.This view moves back to its original coordinates outside the screen.
I have frantically checked my code and there is nothing that is moving the view outside again once its in. Any help in explaining this very unfamiliar behaviour would be really appreciated.
This code moves the view onto the screen
- (IBAction)Register:(id)sender {
//(self.view.frame.size.width/2)-(self.SignUp_Screen.frame.size.width/2);
//self.login_Screen.hidden = YES;
self.blurView.hidden = NO;
//self.SignUp_Screen.layer.zPosition = 5;
NSLog(#"Register");
self.SignUp_Screen.hidden = NO;
[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.SignUp_Screen.frame = CGRectMake(35, 50,self.SignUp_Screen.frame.size.width , self.SignUp_Screen.frame.size.height);
} completion:^(BOOL finished) {
}];
}
and these are the delegate methods for the textfield
-(void)textFieldDidEndEditing:(UITextField *)textField
{
NSLog(#"TextFieldEndEditing");
[textField resignFirstResponder];
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
NSLog(#"textFieldShouldReturn");
[textField resignFirstResponder];
return YES;
}
As Wezly hints at, if you are using autolayout, you don't modify the frame directly anymore. That's the old world. You want to have an Outlet / property for the constraint and animate it.
[UIView animateWithDuration:0.25
animations:^{
SignUp_Screen.centerXConstraint.constant = ...;
SignUp_Screen.centerYConstraint.constant = ...;
[SignUp_Screen layoutIfNeeded];
}];
See here and here for more details.
You should not modify frame if you are using auto layout. You should animate view by animating constraint's constant. For example:
NSLayoutConstraint *viewY; //constraint from superview top to view top
viewY.constant = 100;
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:0.3f animations:^{
[self.view layoutIfNeeded];
}];
The way i solved this problem was by linking an IBOutlet to the constraint I wanted to change and then animating it's constant value.
.h
#interface ViewController : UIViewController {
IBOutlet NSLayoutConstraint *constraintHandle;
}
.m
[UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
constraintHandle.constant = self.view.center.x;
[SignUp_Screen layoutIfNeeded];
} completion:^(BOOL finished) {
}];
Don't forget to link the IBOutlet to your constraint in your storyboard or xib.
I have a scroll view with a label in it, and I wants after someones scroll the label X px's to the right and release his finger to delete this label with animation.
So I created a delegate connection and added the scroll view delegate method:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(#"scroll view did scroll");
}
In this method I want to say something like:
if myScrollView.someProperty moved X px's to the right and the user pulled his finger
delete this label sliding with animation to the right
Can someone please help out here :/
tnx ahead!!
Check UIScrollView's contentOffset property:
contentOffset - The point at which the origin of the content view is offset
from the origin of the scroll view.
you can use UISwipeGestureRecognizer to do this and define how many pixel you want to drag the label to the right. You can try the following code
- (void)viewDidLoad {
[super viewDidLoad];
mylabel.userInteractionEnabled=YES;
[mylabel sizeToFit];
[mylabel addGestureRecognizer:[[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(didSwipeLabel:)]];
}
- (void)didSwipeLabel:(UISwipeGestureRecognizer*)swipe
{
NSLog(#"swipe");
swipe.direction = UISwipeGestureRecognizerDirectionRight;
if (swipe.direction == UISwipeGestureRecognizerDirectionRight) {
[UIView animateWithDuration:0.5 animations:^{
// swipe the label 50px right
mylabel.transform = CGAffineTransformMakeTranslation(50.0, 0.0);
} completion:^(BOOL finished) {
// when animation
[UIView animateWithDuration:0.5 animations:^{
NSLog(#"label should be removed");
[mylabel removeFromSuperview];
}];
}];
}
}
I have a view controller with multiple child view controllers in it (set up using Storyboards), and I move the one on top to the right (with the status bar as well) to display the underlying sidebar view controller.
This works perfectly with frames, as shown below:
- (void)displaySidebar {
self.fullScreenSnapshotOverlay = [self takeFullScreenSnapshot];
[self.postsView addSubview:self.fullScreenSnapshotOverlay];
[self hideStatusBar];
[UIView animateWithDuration:0.4 animations:^{
CGRect newFrame = self.postsView.frame;
newFrame.origin.x += 200.0;
self.postsView.frame = newFrame;
}];
}
(hideStatusBar simply called the UIApplication method and layoutIfNeeded.)
Giving me this (perfect) result:
However, if in the Storyboard I go to the container view controller and make a constraint from its leading space to the left of the view controller it's embedded in, and then adjust that constant, it really messes up the navigation bar, I assume due to hiding the status bar and taking a screenshot. I'm using this code:
- (void)displaySidebar {
self.fullScreenSnapshotOverlay = [self takeFullScreenSnapshot];
[self.postsViewController.view addSubview:self.fullScreenSnapshotOverlay];
[self hideStatusBar];
[UIView animateWithDuration:0.4 animations:^{
self.postsViewControllerDistanceFromLeftSideConstraint.constant = 270.0;
[self.view layoutIfNeeded];
}];
}
Giving me this messed up result:
Now I know the simple thing to do would be to just continue with frames, but I'd like to learn how do it properly with Auto Layout. What am I doing wrong here?
I'm not sure how what I did is any different from what you're doing. I modified the code I posted to your other question (Why does hiding my status bar completely break my simple animation?) to what's below, and it worked fine.
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self performSelector:#selector(displaySidebar) withObject:nil afterDelay:1];
}
-(void)displaySidebar {
self.snapshotView = [self takeSnapshot];
[self.PostsView addSubview:self.snapshotView];
[self hideStatusBar];
self.leftCon.constant = 270;
[UIView animateWithDuration:1.0 animations:^{
[self.view layoutIfNeeded];
}];
}
-(void)moveOutMenu { // called from a button in the menu controller
self.leftCon.constant = 0;
[UIView animateWithDuration:1.0 animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
[[UIApplication sharedApplication] setStatusBarHidden:NO];
[self.snapshotView performSelector:#selector(removeFromSuperview) withObject:nil afterDelay:0.01];
}];
}
-(UIView *)takeSnapshot {
UIView *v = [[UIScreen mainScreen] snapshotViewAfterScreenUpdates:NO];
return v;
}
-(void)hideStatusBar {
[[UIApplication sharedApplication] setStatusBarHidden:YES];
[self.view layoutIfNeeded];
}
What are all the constraints you have on the container view that you're moving? Maybe there's something different there (I have top, leading, bottom and width constraints on mine -- leftCon is the outlet to the leading constraint).