This is a very basic question but its my first time doing any animation in an app.
I have the following scenario. View 1 with some text is displayed and at a certain time the other views 2 and 3 should be animated in from the top pushing down on the first view.
At the moment I'm using auto layout in storyboard. Should I stop doing that when I want to animate or continue using it?
Do I have to programmatically add/update constraints or show/hidden views?
I have tried show/hidden but it doesn't seem to work for me. When trying this I added all the views (right picture) and then set view 2 and 3 to be hidden hoping the bottom view to automatically go up, but that didn't work...
I haven't tried to manually set constraints because i have never done it before. I have a basic understanding of how it should be done but would like to know first if its the way to go?
I've set up a very basic project to illustrate my answer. You can find it here. Almost everything is done in IB. The left red view is pinned to the top of the screen and this constraint is connected to an IBOutlet in ViewController.m. Position of other views depend on position of the left red view. We change this constraint's constant value two times - first time after VC has loaded but not yet appeared on the screen, to move red views out of the screen. Second time, when button is pressed, we change constraint to what it was and force layout with animation. That's pretty much it.
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *redViewTopConstraint;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.redViewTopConstraint.constant = - ([UIScreen mainScreen].bounds.size.width * 0.3 + 20);
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)action:(id)sender {
self.redViewTopConstraint.constant = 20;
[UIView animateWithDuration:0.5f delay:0 usingSpringWithDamping:0.65f initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState animations:^{
[self.view layoutIfNeeded];
} completion:nil];
}
#end
Related
Is it possible to set a property application-wide to double the speed of transitions within an iOS app?
Application-wide?
Try setting the speed property on the backing layer for your view controllers' content views. speed=2 would be double-speed. You could probably set this in the viewDidLoad method for all your view controllers.
You might also be able to create a custom subclass of UIWindow, and have that window object set the speed property on it's view layer to 2.0 in a bottleneck method like makeKeyWindow. You'd need to make all your app's UIWindow objects use your custom class. I'd have to do some digging to figure out how to do that.
##Edit:
Or, better idea, set self.window.layer.speed = 2.0 in your app delegate after creating the window, as suggested by #Costique in the comment below.
Note that this approach will speed up ALL animations, not just transitions and segues. If you only want to speed up segues you would have to figure out how to target just those animations. I'd have to think about that.
Apple does not have a simple way to change that since it would make transitions too heterogenous amongst different apps. You could double the layer's speed, but that would mess up the timing on the rest of your animations. The best way is to implement your own transition using a category on UIViewControler.
UIViewController+ShowModalFromView.h
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
#interface UIViewController (ShowModalFromView)
- (void)presentModalViewController:(UIViewController *)modalViewController fromView:(UIView *)view;
#end
UIViewController+ShowModalFromView.m
#import "UIViewController+ShowModalFromView.h"
#implementation UIViewController (ShowModalFromView)
- (void)presentModalViewController:(UIViewController *)modalViewController fromView:(UIView *)view {
modalViewController.modalPresentationStyle = UIModalPresentationFormSheet;
// Add the modal viewController but don't animate it. We will handle the animation manually
[self presentModalViewController:modalViewController animated:NO];
// Remove the shadow. It causes weird artifacts while animating the view.
CGColorRef originalShadowColor = modalViewController.view.superview.layer.shadowColor;
modalViewController.view.superview.layer.shadowColor = [[UIColor clearColor] CGColor];
// Save the original size of the viewController's view
CGRect originalFrame = modalViewController.view.superview.frame;
// Set the frame to the one of the view we want to animate from
modalViewController.view.superview.frame = view.frame;
// Begin animation
[UIView animateWithDuration:1.0f
animations:^{
// Set the original frame back
modalViewController.view.superview.frame = originalFrame;
}
completion:^(BOOL finished) {
// Set the original shadow color back after the animation has finished
modalViewController.view.superview.layer.shadowColor = originalShadowColor;
}];
}
#end
This can easily be changed to use whatever animated transition you want. Hope this helps!
I'm building an app whereas I'm facing a strange issue with constraints.
I'm trying to animate a height constraint attached to a UIView in storyboard, but the simulator displays some weird behaviour. To debug, I created a completely new, iOS 9.2 project and added a UIButton, set up a horizontal pin and superview top constraint to it, linked the top constraint to my header file and the button itself to an IBAction.
When calling this code alone:
- (IBAction)myAction:(id)sender {
self.topConstraint.constant += 150.0f;
}
The button snaps 150 points down, even without calling [self.view layoutIfNeeded];.
However, when I'm using this code:
- (IBAction)myAction:(id)sender {
self.topConstraint.constant += 150.0f;
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:1 animations:^{
[self.view layoutIfNeeded];
}];
}
The button slides down.
How come it snaps there without me calling layoutIfNeeded when there's no animation block? I find this very strange.
Here's my header file:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;
#end
And my top NSLayoutConstraint setup:
And the UIViewController in interface builder:
Calling layoutIfNeeded on a view merely forces an update of the view early, rather than waiting for an update through the drawing cycle, when you animate your constraint you are placing the layoutIfNeeded inside the animation block thus animating the change, in the 2nd instance where you do not call layoutIfNeeded, the drawing cycle comes around and updates the view without animation.
I have created a very simple test environment one Tab Bar Controller and one View Controller with the following structure:
UI TAB BAR Controller ---------> UIScrollView
UIScrollView
View
Scroll View
Label
Label
Label
.h file
#interface rpViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
#end
.m file
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.scrollView.contentSize = CGSizeMake(320, 1000);
}
If the start entry point for the app is the view with the scroller everything works fine!
If the start entry point is theTab Bar Controller the scroller is not working?
If your going to work purely with Autolayout you should not be doing any direct manipulations of frames, bounds or contentSize. Apple has written a technical note about working with UIScrollView and Autolayout Technical Note TN2154 that you should read.
So to answer the question, if you are going to be using auto layout you cannot manipulate contentSize and expect consistent results.
I got stuck with the strange behavior of UIScrollView that I cannot click on the UIButton after I enlarge the contentSize of the UIScrollView.
What I wanted to do:
Show a form with using UIScrollView
After clicking on the submit button, the scroll view will enlarge some amount of height to show further information.
In the further information, I will place and show another UIButton(hidden at the beginning) in order to process to the next step.
The problem is that I placed the new UIButton to the enlarged area and the button is not clickable whereas I placed the UIButton to existing area(the initial frame of scroll view) then the button works normally. For both cases, the scroll bar of the scroll view performs normal behavior.(ie, the scroll end is the new content height)
Here is what I have so far:
A UIView xib (placed all elements inside it including the hidden button)
A UIScrollView (loaded the UIView xib into it)
UIView* view = [[[NSBundle mainBundle] loadNibNamed:#"view" owner:self options:nil] objectAtIndex:0];
[view loaded];
[scrollView addSubview:view];
After the submit button is clicked:
// offset = some amount;
[scrollview setContentSize:CGSizeMake(scrollview.contentSize.width, scrollview.contentSize.height+offset)];
// h = some amount before the end of the scroll view
CGRect r = nextBtn.frame;
r.origin.y = h;
nextBtn.frame = r;
[nextBtn setHidden:NO];
I have tried to change the clipSubviews attribute of scroll view but seems it is not working for my case at all.
Anyone knows how it happens? And is there any better methods to get this job done? (resize the scroll view and then place another button in it)
Any help would be great! Thanks a lot!
I assume that the button is direct or indirect child of the contents view you load from the xib file.
The problem is because you changed the scroll view's contentSize but did not change the frame of the contents view. Superviews participate in touch hit testing. You can place a subview outside of its superview bounds and by default the subview will not be clipped but touch events will not be delivered to it because the superview checks during hit testing that the touch is outside of its bounds and rejects the touch. See UIView hitTest:withEvent: for details.
You can add a button say submit and another button as subview of the scrollview.Keep your another button hidden in start and call setContentsize. now when user clicks on submit button then set another button to visible and again call set content size.In this way you would be able to achieve what you want to.
i have tried to generate scenario for you.I have tried this on view based application with storyboard without auto layout.
so in story board do some thing like this:
#import <UIKit/UIKit.h>
#interface ALViewController : UIViewController
#property (nonatomic, weak) IBOutlet UIScrollView *scroll;
#property (nonatomic, weak) IBOutlet UIButton *btn1;
#property (nonatomic, weak) IBOutlet UIButton *btn2;
===========
implementation:
#import "ALViewController.h"
#interface ALViewController ()
#end
#implementation ALViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.btn2 setHidden:YES];
[self adjustContentSize];
}
-(void)adjustContentSize
{
CGRect contentRect = CGRectZero;
for (UIView *view in self.scroll.subviews)
{
if (![view isHidden]) {
contentRect = CGRectUnion(contentRect, view.frame);
}
}
self.scroll.contentSize = contentRect.size;
}
(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(IBAction)btnClicked:(id)sender
{
UIButton *btn = (UIButton *)sender;
if (btn.tag ==1)
{
[self.btn2 setHidden:FALSE];
[self adjustContentSize];
}
}
#end
and in storyboard some thing like this:
#end
for your information:view frame 0,0,768,1024
scroll frame: 0,0,768,1024
btn1 frame: 361, 753, 46, 30
btn2 frame: 351, 1032, 46, 30
run your code :
at first your scroll view would be unscrollable because we have set btw 2 to be hidden and set content size.As soon as you will be clicking on btn 1, see code we are making btn 2 visible again and calling set content size.
Conclusion:
the scrollview content size looks for its visible contain of which we can take benefit of.
thanks and regards.
2ND UPDATE
Courtesy of this thread
In iOS6, XCode 4.5, UIScrollView is not Scrolling, despite contentSize being set
the answer has been provided! Turning off Auto Layout on your Storyboard (the entire thing) will fix ScrollView and allow things to scroll properly. Spread the word people!
UPDATE
Added code. Seriously cannot figure out why this will not work on a iPad.
.h file
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIImageView *imageView;
#property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
#end
.m file
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.scrollView.contentSize = CGSize(768, 1600);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Again just to clarify I'm using Storyboards, I'm first dragging in a ScrollView, setting the size to standard iPad screen (768x1004 because I have a Nav bar at the top) then adding my image on top of that and setting it's size (768x1600). Then I connect both of these to the header file (as shown above) and still I can't get the image to scroll. I have my view mode set to "Aspect Fit", could this be the issue? I've tried other settings and no luck but not all of them.
Any specific advice would be so appreciated!
ORIGINAL POST
I know there a number of topics out there regarding this one, but I'm having a dog of a time trying to get a ScrollView to work. I am attempting to load an image that is 768x1600 (on an iPad screen only) so that you have to vertically scroll to see more of the image's content. To create the ScrollView, I use a Storyboard and drag a ScrollView into the ViewController frame, align it and ensure interactions are enabled. Then, on top of the ScrollView (also in Storyboard) I add the UIImage I wish to use (ensuring it is a subview) and size it accordingly, then set interactions enabled as well.
No matter how hard I try, I cannot get this to work in Simulator or on my device. I have made sure that my ScrollView is set as a delegate in the IB, that is has an IBOutlet on the header file and is both synthesized and loaded into my implementation file. I have also ensured that I have set contentSize by;
self.scrollView.contentSize = CGSizeMake(768, 1600);
I have also tried to add the UIImage as a Subview via code (as opposed to on the Storyboard) and when I do this the image does not appear when I run Simulator. On top of this, when I click the page no scrolling option is available.
Any help anyone can give me on this one is much appreciated. If code is necessary I will post it.
I think I just figured it out. You say you drug the UIImageView on top of the scroll view.
UIImageView's aren't user-interactable!
Try selecting your image view in Interface Builder and setting 'User interaction enabled' to YES for the image view.
If that doesn't work, I would suggest adding the image view in code (remove it from IB first):
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"someImage.png"]];
self.imageView.frame = CGRectMake(0,0,768,1600);
self.imageView.userInteractionEnable = YES;
[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = CGSizeMake(768, 1600);
self.scrollView.frame = self.view.bounds;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end