I am probably doing something extremely stupid but I cannot figure out why this does not work.
I am trying to perform a simple UIView block animation but have run into trouble. I have recreated in a test project.
I have a view on a View controller, when I press the button, I create a new view and set its frame to be out of the current view (above it). I want to animate the transition so that the view currently on the screen moves downwards out of the view as the new one above it comes down to take its place.
Here is the code which is hooked up to the button,
the original view is hooked up as self.view1
- (IBAction)buttonPressed:(id)sender {
UIView *view2 = [[UIView alloc] init];
view2.backgroundColor = [UIColor blueColor];
float offScreenY = 0 - self.view1.frame.size.height;
CGRect offScreenRect = CGRectMake(0, offScreenY, self.view1.frame.size.width, self.view1.frame.size.height);
view2.frame = offScreenRect;
[self.view addSubview:view2];
float oldY = self.view1.frame.origin.y + self.view1.frame.size.height;
CGRect oldRect = CGRectMake(0, oldY, self.view1.frame.size.width, self.view1.frame.size.height);
[UIView animateWithDuration:0.5 animations:^{
self.view1.frame = oldRect;
view2.frame = CGRectMake(0, 0, self.view1.frame.size.width, self.view1.frame.size.height);
}];
}
This just animates view2 down and does not animate view 1.
If I do not add view 2 as a subview and only put view1's frame change in the animation block then view1 animates correctly.
BUT they will not work together!
Why is this?
This is a classic symptom of having autolayout turned on. If you animate frame, it works, but as soon autolayout reapplies the constraints on the view, view1 will return to its original location. By adding view2, iOS automatically reapplies autolayout constraints immediately and your view1 therefore won't move.
Bottom line, don't use autolayout and try to animate frame properties directly. Two solutions:
The easy solution is to turn off autolayout. Go to IB, select the "File inspector" and uncheck the "Use Autolayout" button:
If you want to keep autolayout on, you shouldn't be animating by changing the frame properties directly. You would animate by change the layout constraint constants. This has been answered elsewhere on S.O., but if you need guidance on that approach, let me know.
The basic idea, though, is to create an IBOutlet for your top constraint for view1 called, say, view1TopConstraint, and then in your animation block you can say
self.view1TopConstraint.constant += self.view1.frame.size.height;
[self.view layoutIfNeeded];
For this to work, though, you'd have to be careful about your other constraints on view1 (e.g., have a height constraint, have no bottom constraint or if you have one, lower its priority, etc.). This can be a hassle the first time you do it, but you'll quickly get the hang of animating by changing constraints.
But, then again, if you're using constraints, you probably shouldn't be defining view2 by its frame, but probably defining constraints for that, too.
In this case it is better for you to have a container view.
Add view1 and view2 inside this container accordingly. Container's some part will be in the screen and container's frame size will be double of the size of a view. Animate the container, so the other two views will be animated accordingly..
Related
I'm attempting to animate a view into position in a view that's using UIDynamics. I'm not clear on the interaction between the Core Animation animations and UIDynamics but something's going on that I don't understand.
I have a test project with a view controller with a single view, a label, that starts at the bottom middle of the view and I'm trying to animate motion to the top middle.
// Create the label
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(160, 400, 100, 50)];
label.text = #"hello";
[self.view addSubview:label];
// Set up the animator and collision
self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
UICollisionBehavior *collisionBehaviour = [[UICollisionBehavior alloc] initWithItems:#[ label ]];
[collisionBehaviour addBoundaryWithIdentifier:#"wall"
fromPoint:CGPointMake(0, 50)
toPoint:CGPointMake(320, 50)];
[self.animator addBehavior:collisionBehaviour];
// Animate the label
[UIView animateWithDuration:1.0
animations:^{
label.frame = CGRectMake(160, 100, 320, 100);
}];
This is simply a line boundary at the top of the view, nowhere near either the start or end position of my animation. I don't see why it should affect the animation of the button into its initial position, but it does.
If I comment out the addBehaviour: call then the animation works fine. But when I add the behaviour, the animation goes to the wrong location. I don't understand why.
The reason why in your example the label moves left is because you are changing the width of the label from 100px to 320px and the text is right justified.
Now as for why it doesn't move up, when you add the UICollisionBehavior to the label, and then to the UIDynamicAnimator, the UIDynamicAnimator is now in charge of moving the label. When you try and animate it up, the animation goes off, then the UIDynamicAnimator sees the change and sets the center of the label back to what it originally was. You can see this for yourself if you subclass UILabel and NSLog on when the frame and center changes. If you override setCenter to just return your label moves upward.
If you want to move the label either;
Don't attach a UICollisionBehavior to the label and animate it
Also attach a UIDynamicItemBehavior to the label and then addLinearVelocity
You should not set any view's frame manually if that view is managed by a dynamic animator.
The reason why it works without calling -addBehavior: is that the dynamic animator doesn't care about the label until any of its behaviors references the label.
You can move the label manually and start the dynamic animator when the animation is finished, but the better approach is to use a UISnapBehavior to move your label to the desired position.
Also, if you change a view's frame that is managed by a dynamic animator you have to remove all behaviors associated with that view and add them again. You should set your label's initial size and only change it's position from there on, but not its size.
when you set frame of your view below that line write
[self.animator updateItemUsingCurrentState:label];
My UIScrollView is a ~4500px horizontal view that the user needs to scroll horizontally through to view the content.
I have set it up as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
sview.frame = CGRectMake(0, 0, 568, 320);
sview.contentSize = CGSizeMake(4500, 320);
[sview setScrollEnabled:YES];
}
Yet the scroll view does nothing. Is there something obvious I missed? i've tried literally every tutorial on the web.
I got similar issue. I did following modifications and the scrollView started scrolling for me:
Select to check the 'Bounce Horizontally' property for UIScrollView
in xib.
Move the code following code to viewDidAppear instead of
viewDidLoad:
-(void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
sview.frame = CGRectMake(0, 0, 568, 320);
sview.contentSize = CGSizeMake(4500, 320);
[sview setScrollEnabled:YES];
}
I think this should help you.
I've explained it here, but there are so many answers to this problem that suggests turning off Auto Layout. That fixes the problem but that's not really the correct solution. Here's my answer:
Turning Auto Layout works, but that's not the solution. If you really need Auto Layout, then use it, if you don't need it, turn it off. But that is not the correct fix for this solution.
UIScrollView works differently with other views in Auto Layout. Here is Apple's release note on Auto Layout, I've copied the interesting bit:
Here are some notes regarding Auto Layout support for UIScrollView:
In general, Auto Layout considers the top, left, bottom, and right edges of a view to be the visible edges. That is, if you pin a view to
the left edge of its superview, you’re really pinning it to the
minimum x-value of the superview’s bounds. Changing the bounds origin
of the superview does not change the position of the view.
The UIScrollView class scrolls its content by changing the origin of its bounds. To make this work with Auto Layout, the top, left, bottom,
and right edges within a scroll view now mean the edges of its content
view.
The constraints on the subviews of the scroll view must result in a size to fill, which is then interpreted as the content size of the
scroll view. (This should not be confused with the
intrinsicContentSize method used for Auto Layout.) To size the scroll
view’s frame with Auto Layout, constraints must either be explicit
regarding the width and height of the scroll view, or the edges of the
scroll view must be tied to views outside of its subtree.
Note that you can make a subview of the scroll view appear to float (not scroll) over the other scrolling content by creating constraints
between the view and a view outside the scroll view’s subtree, such as
the scroll view’s superview.
Apple then goes on to show example of how to correctly use UIScrollView with Auto Layout.
As a general rule, one of the easiest fix is to create a constraint between the element to the bottom of the UIScrollView. So in the element that you want to be at the bottom of the UIScrollView, create this bottom space constraint:
Once again, if you do not want to use Auto Layout, then turn it off. You can then set the contentSize the usual way. But what you should understand is that this is an intended behaviour of Auto Layout.
First of all you have to add some content to UIScrollSiew as subview for scrolling,without content on UIScrollView how can you scroll?. Here is what i did,just add UIImageView to UIScrollView as subview of size same as size of UIScrollView...
In viewDidLoad method try the following code..
-(void)viewDidLoad
{
UIScrollView *scroll=[[UIScrollView alloc] init];
scroll.frame=CGRectMake(0, 0, 320, 460);
UIImageView *imageView=[[UIImageView alloc] init];
imageView.frame=CGRectMake(0, 0, 320,460);
imageView.image=[UIImage imageNamed:#"chiranjeevi.jpeg"];
scroll.contentSize = CGSizeMake(4500, 460);
[scroll setScrollEnabled:YES];
[scroll addSubview:imageView];
[self.view addSubview:scroll];
}
I tested this code it works well.I hope this code will be helpful to you..
I assume you are adding UISrollingView in your Xib file. This will work for you.
sview.delegate = self;
sview.backgroundColor=[UIColor clearColor];
[sview setCanCancelContentTouches:NO];
sview.indicatorStyle = UIScrollViewIndicatorStyleWhite;
sview.clipsToBounds = YES;
sview.scrollEnabled = YES;
sview.contentSize = CGSizeMake(320,570);
CGPoint topOffset = CGPointMake(0,0);
[sview setContentOffset:topOffset animated:YES];
Also, make sure to give IBOutlet connection in your Xib file.
I also faced the same issue.I added the scroll view in xib.I also added some subviews to this scroll view. The scroll view would stop scrolling after I added the subviews. The solution for this problem was in the xib for the view in file inspector Use Autolayout was checked. I unchecked it and the scroll view scrolled after adding the subviews.
The solution was uncheking the Use Autolayout in file inspector in xib.
I am developing an application in which i am creating small sub views in the same view.
What i want is when i change the value of alpha of the view, the value of alpha for sub view is also getting changed which i do not want.
How to implement the code for changing the value of the view not the small sub views created.
Thanks,
You cannot do that, the alpha is inheritate, so what you have to do is to change the view structure from
YourMainView -> Your SubView
To
ContainerView ->YourMainView
-> Your SubView
So now YourMain View the one that you want to apply the animation, is not the paret view of your current subView, the both views are sibilings, also ContainerView will have a clear background, so it wont affect it will just contain both the views
As suggested already, you should not add YourSubVew as subview of YourMainView.
Make both of your views a subview of an empty and transparent common subview (ContainerView) which has the same size and position that YourMainView currently has.
ContainerView -> YourMainView
-> YourSubView
Make sure to add YourMainView first as subview of ContainerView before you add YourSubView as subview of ContainterView. Otherwise YourMainView may overlap/hide YourSubView.
Doing so you can set both views' alpha independend from each other.
(Omar's answer is quite correct, but his "graphical visualisation" is misleading and it missed the point of the sequence of the subviews.)
Edit:
What you have today is like this:
yourMainView.alpha = 1.0;
...
[yourMainView addSubView:yourSubView];
yourMainView.alpha = 0.1; //probably with animations etc.
What happens is, that yourSubView will "inherit" the alphaValue of its superview.
ANd there is even more to that. Assuming that your subView has an alpha of 0.5, that would effectively become 0.05 when when its superview's alpha is set to 0.1.
Change that code to:
containerView = [[UIView alloc] init]; //you may choose a different init method
//If you use initWithFrame then use the frame of yourMainView.
//However, make sure that ContainterView is of the same size as yourMainView and has the same position.
[containerView addSubView:yourMainView];
yourMainView.alpha = 1.0;
...
[containerView addSubView:yourSubView]; //add it to the container too!
yourMainView.alpha = 0.1; //this will now effect the MainView only.
That is basically all the trick. Views can perfectly well overlap each other without being subviews. Make them subviews only then when they really are subveiws, when they are moved togehter, appear and disappear together, etc.
I have added a subview over my UITableView using:
TransparentViewController *tvc =
[[TransparentViewController alloc]
initWithNibName:#"TransparentViewController" bundle:nil];
[self.view addSubview:tvc.view];
My Nib has a UIImageView in it that has some text and a transparent background.
When I load the detailView for the table for the first time I show the subview that gives a brief explanation of the information that you can see below the text. Works really well.
What i would like to do is alter the alpha of the underlying table so that it is dimmer but not affect the alpha of the overlay subview. If i use:
[self.view setAlpha:(CGFloat)];
It dims the overlay as well. I seem to be having a mental block.
Changing the alpha affects the subviews as well. Your tvc.view is a subview of self.view, so it is naturally going to be affected.
Why don't you try this: put another view in tvc.view and send this view to this view to the back.
(UIView*) back = [[UIView alloc] initWithFrame:CGFrameMake(...)];
back.backgroundColor = [UIColor grayColor]; // choose a color that you like;
back.alpha = 0.5; // whatever works for you
[tvc.view addSubview:back];
[tvc.view sendSubviewToBack:back];
Set the size and alpha of this new view to something you like. The table view will show through it to a limited extent, which may accomplish what you are trying to do.
Since this is part of our tvc view, it will appear when you show that view and go away when you hide that view.
I have a view hierarchy like this:
UIView
- ADBannerView
- UIImageView
- UILabel
- UIButton
- UINavigationController
- UIView
I'm loading the image view, label and button from a nib file and the UINavigationController from another nib. All have autoresizing masks set. I'm creating the ADBannerView programmatically.
Now my problem is that I would like the image, label, button to move down and the navigation controller to shrink when I insert an ADBannerView. However this is not happening, instead the ADBannerView is placed on top of the image and the label.
Can anybody explain to me what am I doing wrong here?
In other to get those things to "automatically" shift down when you put in the ADBannerView, you'll need to enclose them in their own view and then change the size and position of that view. Assuming the ADBannerView is 50 pixels tall, you'll want to move that UIView down 50 pixels and reduce its height by 50 pixels.
Assuming that self.enclosingView is the new view that you will use to enclose the image, label and button... and assuming you want to make this animated (you probably do, it usually looks a lot better):
// Start the AdBannerView off of the top of the screen
CGRect adFrame = self.bannerView.frame;
adFrame.origin.x = 0.0;
adFrame.origin.y = 0.0 - adFrame.size.height;
self.bannerView.frame = adFrame;
[UIView beginAnimations:#"Show Ads" context:nil];
// Animate the shrinking of the enclosing view
CGRect enclosingFrame = self.enclosingView.frame;
enclosingFrame.size.height -= self.bannerView.frame.size.height;
enclosingFrame.origin.y += self.bannerView.frame.size.height;
self.enclosingView.frame = enclosingFrame;
// Animate the motion of the bannerView into view
adFrame.origin.y = 0.0;
self.bannerView.frame = adFrame;
[UIView commitAnimations];
Autoresizing mask defines size changes on parent's frame changes, not on sibling insertion/removal. You have to adjust frame for corresponding views programmatically. Kenny Wyland already gave you idea how this can be achieved with less pain. Take a look at CGRectDivide method - with it it's easy to split available space between two views.