Alright, I'm almost positive I'm just doing something stupid, but I'm new to this, so please excuse my incompetence. I have a nested animation block, and it changes the view controller once it's finished - one problem. It fires prematurely. I don't know if it's me incorrectly changing the view controller, or the block is incorrect. Any help would be great. Thanks in advance.
SplashViewController.m
#import "SplashViewController.h"
#import "MenuViewController.h"
#interface SplashViewController ()
#end
#implementation SplashViewController
-(void) drawColoredBars
{
UIView *greenBar = [[UIView alloc] initWithFrame:CGRectMake(75.0, -self.view.bounds.size.height, 3.0, self.view.bounds.size.height)];
UIView *orangeBar = [[UIView alloc] initWithFrame:CGRectMake(87.5, -self.view.bounds.size.height - 25, 3.0, self.view.bounds.size.height)];
UIView *yellowBar = [[UIView alloc] initWithFrame:CGRectMake(100.0, -self.view.bounds.size.height - 50, 3.0, self.view.bounds.size.height)];
UIColor *green = [UIColor colorWithRed:142.0/255.0f green:149.0/255.0f blue:31.0/255.0f alpha:0.6f];
UIColor *orange = [UIColor colorWithRed:204.0/255.0f green:82.0/255.0f blue:29.0/255.0f alpha:0.6f];
UIColor *yellow = [UIColor colorWithRed:254.0/255.0f green:190.0/255.0f blue:16.0/255.0f alpha:0.6f];
greenBar.backgroundColor = green;
orangeBar.backgroundColor = orange;
yellowBar.backgroundColor = yellow;
greenBar.tag = 1;
orangeBar.tag = 2;
yellowBar.tag = 3;
[self.view addSubview:greenBar];
[self.view addSubview:orangeBar];
[self.view addSubview:yellowBar];
}
-(void) animate
{
MenuViewController *menu = [[MenuViewController alloc] init];
double delay = 0.0;
double x = 75.0;
for (int i = 1; i < 4; i++) {
CGRect frame = CGRectMake(x, 0.0, 3.0, self.view.bounds.size.height);
[UIView animateWithDuration: 2.5
delay: delay
options: UIViewAnimationOptionCurveEaseIn
animations:^{
[self.view viewWithTag:i].frame = frame;
}
completion:^(BOOL finished){
[UIView animateWithDuration: 2.5
delay: 2.5
options: UIViewAnimationOptionCurveEaseIn
animations:^{
self.view.alpha = 0.0;
}
completion:^(BOOL finished){
[self presentViewController:menu animated:NO completion:nil];
}];
}];
delay = delay + 0.5;
x = x + 12.5;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
CAGradientLayer *layer = [GradientBackground drawGradient];
layer.frame = self.view.bounds;
[self.view.layer insertSublayer:layer atIndex:0];
[self drawColoredBars];
[self animate];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
Once again, thanks!
The viewDidLoad method is too early to set up animations. Try putting [self animate] in viewDidAppear: instead.
Also, make sure [self.view viewWithTag:i] doesn't return nil. If it returns nil, the outer animation block doesn't have anything to animate and will fire its completion block immediately.
Related
Hi i am beginner in ios and in my project i am adding UIImage on UIScrollview and i have added tap gesture on UIImage
When we double click on UIImage then image should be zooming full screen size on view controller
After the full screen size image we can zoom it like any way what we want(i mean using like pinch zoom effect)here my requirement is when we double click on image then image need to set it's original position i have tried my level best but i did not get result please help me
my code is below:
#import "ViewController2.h"
#interface ViewController2 ()
{
UIScrollView * myScroll;
UITapGestureRecognizer *tap;
BOOL isFullScreen;
CGRect prevFrame;
UIImageView * _imageView;
}
#end
#implementation ViewController2
- (void)viewDidLoad
{
[super viewDidLoad];
isFullScreen = FALSE;
myScroll = [[UIScrollView alloc] init];
myScroll.frame = self.view.bounds;
myScroll.contentSize = CGSizeMake(_imageView.frame.size.width, _imageView.frame.size.height);
myScroll.maximumZoomScale = 4.0;
myScroll.minimumZoomScale = 1.0;
myScroll.clipsToBounds = YES;
myScroll.delegate = self;
myScroll.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:myScroll];
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 300, 200)];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
[_imageView setClipsToBounds:YES];
_imageView.userInteractionEnabled = YES;
_imageView.image = [UIImage imageNamed:#"ram.jpeg"];
UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imgToFullScreen:)];
tapper.numberOfTapsRequired = 1;
[_imageView addGestureRecognizer:tapper];
[myScroll addSubview:_imageView];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _imageView;
}
-(void)imgToFullScreen:(UITapGestureRecognizer*)sender {
if (!isFullScreen) {
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
prevFrame = _imageView.frame;
[_imageView setFrame:[[UIScreen mainScreen] bounds]];
}completion:^(BOOL finished){
isFullScreen = TRUE;
}];
return;
}
else{
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
[_imageView setFrame:prevFrame];
}completion:^(BOOL finished){
isFullScreen = FALSE;
}];
return;
}
}
#end
I did some modifications in your code as like below try it .Check it give your wanted output or not.
#interface ScrollViewController ()<UIScrollViewDelegate>{
UIScrollView * myScroll;
UITapGestureRecognizer *tap;
BOOL isFullScreen;
CGRect prevFrame;
UIImageView * _imageView;
CGAffineTransform trans;
}
#end
#implementation ScrollViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
isFullScreen = FALSE;
myScroll = [[UIScrollView alloc] init];
myScroll.frame = [UIScreen mainScreen].bounds;
myScroll.contentSize = CGSizeMake(prevFrame.size.width, prevFrame.size.height);
myScroll.maximumZoomScale = 4.0;
myScroll.minimumZoomScale = 1.0;
myScroll.clipsToBounds = YES;
myScroll.delegate = self;
myScroll.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:myScroll];
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 10, 300, 200)];
_imageView.contentMode = UIViewContentModeScaleAspectFill;
[_imageView setClipsToBounds:YES];
_imageView.userInteractionEnabled = YES;
_imageView.image = [UIImage imageNamed:#"img.png"];
UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(imgToFullScreen:)];
tapper.numberOfTapsRequired = 2;
[_imageView addGestureRecognizer:tapper];
[myScroll addSubview:_imageView];
prevFrame = _imageView.frame;
trans = _imageView.transform;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _imageView;
}
-(void)imgToFullScreen:(UITapGestureRecognizer*)sender {
if (!isFullScreen) {
myScroll.contentSize = prevFrame.size;
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
_imageView.frame = [UIScreen mainScreen].bounds;
}completion:^(BOOL finished){
isFullScreen = TRUE;
}];
return;
}
else{
[UIView animateWithDuration:0.5 delay:0 options:0 animations:^{
myScroll.contentSize = CGSizeMake(prevFrame.size.width, prevFrame.size.height);
_imageView.transform = trans;
_imageView.frame = prevFrame;
NSLog(#"%# %#",NSStringFromCGSize(myScroll.contentSize),NSStringFromCGRect(prevFrame));
}completion:^(BOOL finished){
isFullScreen = FALSE;
}];
return;
}
}
I've made a subview to record sound which looks like this:
http://i.stack.imgur.com/EBdEV.png
After the bars faded, it looks like this:
http://i.imgur.com/sDAp4nk.png
I couldn't figure out why. This is the controller. Is there something in the storyboard I could turn on/off?
Thanks for help :)
#import "SubviewController.h"
#implementation SubviewController
#synthesize delegate;
- (void)viewWillAppear:(BOOL)inAnimated {
[super viewWillAppear:inAnimated];
if([self.delegate respondsToSelector:#selector(subviewControllerWillAppear:)]) {
[self.delegate subviewControllerWillAppear:self];
}
}
- (void)viewWillDisappear:(BOOL)inAnimated {
if([self.delegate respondsToSelector:#selector(subviewControllerWillDisappear:)]) {
[self.delegate subviewControllerWillDisappear:self];
}
[super viewWillDisappear:inAnimated];
}
- (void)presentFromViewController:(UIViewController *)inViewController animated:(BOOL)inAnimated {
CGRect theBounds = inViewController.view.bounds;
UIView *theView = self.view;
UIView *theBackgroundView = [[UIView alloc] initWithFrame:theBounds];
CGRect theFrame = theView.frame;
theFrame.origin.x = (CGRectGetWidth(theBounds) - CGRectGetWidth(theFrame)) / 2.0;
theFrame.origin.y = (CGRectGetHeight(theBounds) - CGRectGetHeight(theFrame)) / 2.0;
theView.frame = theFrame;
[inViewController addChildViewController:self];
theBackgroundView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.8];
theBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
theBackgroundView.alpha = 0.0;
[theBackgroundView addSubview:theView];
[inViewController.view addSubview:theBackgroundView];
[UIView animateWithDuration:inAnimated ? 0.75 : 0.0
animations:^{
theBackgroundView.alpha = 1.0;
}
completion:^(BOOL inFinished) {
[self didMoveToParentViewController:inViewController];
}];
}
- (void)dismissAnimated:(BOOL)inAnimated {
UIView *theView = self.view;
UIView *theBackgroundView = theView.superview;
[self willMoveToParentViewController:nil];
[UIView animateWithDuration:inAnimated ? 0.75 : 0.0
animations:^{
theBackgroundView.alpha = 0.0;
}
completion:^(BOOL inFinished) {
[theBackgroundView removeFromSuperview];
[theView removeFromSuperview];
[self removeFromParentViewController];
}];
}
#end
#implementation SubviewSegue
- (void)perform {
[self.destinationViewController presentFromViewController:self.sourceViewController animated:YES];
}
#end
Check your constraints. You probably have one that has a minimum height between 2 subviews and its pushing that toolbar down or something.
Also, a lot of weird things happen when you animate frames while autolayout is on so be careful about that.
I'm trying to make a animation that is done in Skype iOS application. The animation can be seen in this link Animation Section 3:Add a Contact
I tried with UIBezierPath but I can't do it. Any ideas?
Thanks!
My code
#implementation CustomView
#pragma mark - Lifecycle and initialize components
- (instancetype) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if ( self ) {
self.alpha = 0;
[self configureCancelButton];
[self configureCollectionView];
[self addSubview:self.cancelButton];
[self addSubview:self.collectionView];
}
return self;
}
- (void) configureCancelButton {
self.cancelButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.cancelButton setTitle:#"Cancel" forState:UIControlStateNormal];
self.cancelButton.titleLabel.font = [UIFont systemFontOfSize:20];
[self.cancelButton addTarget:self action:#selector (cancelView) forControlEvents:UIControlEventTouchUpInside];
self.cancelButton.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.8];
self.cancelButton.layer.cornerRadius = 7.f;
self.frameForCancelButton = CGRectMake (self.bounds.origin.x+5, self.bounds.size.height - kCancelButtonHeight - 5, self.bounds.size.width-10, kCancelButtonHeight);
self.cancelButton.frame = CGRectMake (self.frameForCancelButton.origin.x, self.frameForCancelButton.origin.y + kAnimationOffset, self.frameForCancelButton.size.width, self.frameForCancelButton.size.height);
}
- (void) configureCollectionView {
UICollectionViewFlowLayout *layout= [[UICollectionViewFlowLayout alloc] init];
layout.itemSize = CGSizeMake(kItemSize, kItemSize);
layout.minimumInteritemSpacing = 3.0;
layout.sectionInset = UIEdgeInsetsMake(0, 7, 7, 10);
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.frameForCollectionView = CGRectMake(self.cancelButton.frame.origin.x, self.frame.size.height - (self.cancelButton.frame.size.height + 10 + kCollectionHeight), self.cancelButton.bounds.size.width, kCollectionHeight);
self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake (self.frameForCollectionView.origin.x, self.frameForCollectionView.origin.y + kAnimationOffset, self.frameForCollectionView.size.width, self.frameForCollectionView.size.height)
collectionViewLayout:layout];
self.collectionView.backgroundColor = [UIColor colorWithWhite:1 alpha:.8];
[self.collectionView registerClass:[RAShareCell class] forCellWithReuseIdentifier:#"Cell"];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
self.collectionView.layer.cornerRadius = 7.f;
}
#pragma mark - Public Methods
- (void) present {
[UIView animateWithDuration:0.35 animations:^{
self.alpha = 1;
self.darkView.alpha = .5;
}];
[UIView animateWithDuration:1.0 delay:0.45 options:(UIViewAnimationOptions) UIViewAnimationCurveEaseInOut animations:^{
[self setupAnimation];
} completion:nil];
}
- (void) cancelView {
[UIView animateWithDuration:0.35 delay:0 options:UIViewAnimationCurveEaseInOut animations:^{
self.cancelButton.frame = CGRectMake (self.frameForCancelButton.origin.x, self.frameForCancelButton.origin.y + kAnimationOffset, self.frameForCancelButton.size.width, self.frameForCancelButton.size.height);
self.collectionView.frame= CGRectMake(self.frameForCollectionView.origin.x, self.frameForCollectionView.origin.y + kAnimationOffset, self.frameForCollectionView.size.width, self.frameForCollectionView.size.height);
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
;
}
- (void) setupAnimation {
self.cancelButton.alpha = 1;
self.collectionView.alpha = 1;
self.cancelButton.frame = self.frameForCancelButton;
self.collectionView.frame = self.frameForCollectionView;
[self addShapeLayer];
}
- (void)addShapeLayer
{
self.shapeLayer = [CAShapeLayer layer];
self.shapeLayer.path = [[self pathAtInterval:0.0] CGPath];
self.shapeLayer.fillColor = [[UIColor redColor] CGColor];
self.shapeLayer.lineWidth = 3.0;
self.shapeLayer.strokeColor = [[UIColor redColor] CGColor];
[self.collectionView.layer insertSublayer:self.shapeLayer atIndex:0];
}
- (UIBezierPath *) pathAtInterval:(NSTimeInterval) interval {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(self.collectionView.frame.origin.x,self.collectionView.frame.origin.y)];
[path addQuadCurveToPoint:CGPointMake(self.collectionView.frame.size.width, self.collectionView.frame.origin.y) controlPoint:CGPointMake(self.collectionView.frame.size.width/2, self.collectionView.frame.origin.y-30)];
return path;
}
Your animation block does not contain any animations. You should move your setup outside of the animation block and then animate within it.
[self setupAnimation];
[UIView animateWithDuration:1.0 delay:0.45 options:(UIViewAnimationOptions) UIViewAnimationCurveEaseInOut animations:^{
// animate something
} completion:nil];
In addition to this, your intended animation does not have a linear progression, meaning that you'l want to use animateKeyframesWithDuration instead.
[UIView animateKeyframesWithDuration:1.0 delay:0.45 options:UIViewAnimationCurveEaseInOut animations:^{
for (NSInteger i = 0; i < 10; i++) {
[UIView addKeyframeWithRelativeStartTime:i/10.0
relativeDuration:1.0/10.0
animations:^{
self.shapeLayer = [self pathAtInterval:i/10.0];
}];
}
} completion:nil];
1, add the path you created to CAShaperLayer object;
2, see the "strokeStart" and "strokeEnd" properties in CAShapeLayer class;
3, add a animation which called "CABasicAnimation" to your Layer obj.
I have a simple program that creates a subview and animates it across the screen.
As part of this program, I would like to add functionality when the subview is tapped. I am using the following method to create the subview, add the UITapGestureRecognizer and then animate the subview:
int randomName = arc4random() % ([pieceNames count] - 1);
int animationDuration = arc4random() % 5 + 5 ;
NSString *randomPiece = [pieceNames objectAtIndex:randomName];
float yStart = arc4random() % 650;
float yEnd = arc4random() % 650;
UIView *piece = [[PieceView alloc]initWithFrame:CGRectMake(100.0, yStart, 75.0, 75.0)];
[piece setValue:randomPiece forKey:#"name"];
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc]initWithTarget:self
action:#selector(handleTouch:)];
[piece addGestureRecognizer:recognizer];
[[self view] addSubview:piece];
[UIView animateWithDuration:animationDuration
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction
animations:^(void){
piece.center = CGPointMake(950.0, yEnd);
} completion:^(BOOL done){
[piece removeFromSuperview];
}];
Here is the code that handles the tap:
PieceView *pv = (PieceView *) recognizer.view;
NSLog(#"%# was tapped", pv.name);
What happens is when a PieceView is touched the program does not respond. However, if I remove the animation block then the program responds to the tap.
Why does the UITapGestureRecognizer fail to respond to PieceView when it is animated?
I struggled with this same problem, and it boils down to this: the animated view only ever is in two places: the starting position, and the ending position. Core Animation simply renders the view's layer in interpolated positions between the start and end points over a period of time.
It's almost like when you look to the stars and realize that what you see is not actually what is happening right now. :)
Luckily, the solution is pretty simple. You can put a tap recognizer on the superview and then inspect your animated view's presentationLayer (which does give you an accurate frame at any point in time) to determine if your tap is a hit or not.
I've built a simple UIViewController that demonstrates both the problem and solution:
#import <UIKit/UIKit.h>
#interface MSMViewController : UIViewController
#end
And the implementation:
#import "MSMViewController.h"
#interface MSMViewController ()
#property (nonatomic, strong) UIView *animatedView;
#end
#implementation MSMViewController
- (void)viewDidLoad {
[super viewDidLoad];
CGRect startFrame = CGRectMake(125, 0, 70, 70);
CGRect endFrame = CGRectMake(125, 400, 70, 70);
// draw a box to show where the animated view begins
UIView *startOutlineView = [[UIView alloc] initWithFrame:startFrame];
startOutlineView.layer.borderColor = [UIColor blueColor].CGColor;
startOutlineView.layer.borderWidth = 1;
[self.view addSubview:startOutlineView];
// draw a box to show where the animated view ends
UIView *endOutlineView = [[UIView alloc] initWithFrame:endFrame];
endOutlineView.layer.borderColor = [UIColor blueColor].CGColor;
endOutlineView.layer.borderWidth = 1;
[self.view addSubview:endOutlineView];
self.animatedView = [[UIView alloc] initWithFrame:startFrame];
self.animatedView.backgroundColor = [UIColor yellowColor];
[self.view addSubview:self.animatedView];
[UIView animateWithDuration:10 delay:2 options:UIViewAnimationOptionAllowUserInteraction animations:^{
self.animatedView.frame = endFrame;
} completion:nil];
// this gesture recognizer will only work in the space where endOutlintView is
UITapGestureRecognizer *boxTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(boxTap:)];
[self.animatedView addGestureRecognizer:boxTap];
// this one will work
UITapGestureRecognizer *superviewTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(superviewTap:)];
[self.view addGestureRecognizer:superviewTap];
}
- (void)boxTap:(UITapGestureRecognizer *)tap {
NSLog(#"tap. view is at %#", NSStringFromCGPoint(self.animatedView.frame.origin));
}
- (void)superviewTap:(UITapGestureRecognizer *)tap {
CGRect boxFrame = [self.animatedView.layer.presentationLayer frame];
if (CGRectContainsPoint(boxFrame, [tap locationInView:self.view])) {
NSLog(#"we tapped the box!");
}
}
#end
The solution is much simpler, you just need to set the animation's option UIViewAnimationOptions.allowUserInteraction.
UIView.animate(withDuration: duration, delay: 0.1, options: [.allowUserInteraction], animations: {
...
}, completion: { (completed) in
...
}
I'm having some problems with UIView subview animations. What I'm trying to do is that when you press the main view, a subview will slide down from the top and on the next tap it will slide up and be removed. But in the current state, it just does the first tap commands and on the second tap, it displays a nslog, but the removal of the view and the animation doesn't work.
Here is the code in the event handling function:
- (void)tapGestureHandler: (UITapGestureRecognizer *)recognizer
{
NSLog(#"tap");
CGRect frame = CGRectMake(0.0f, -41.0f, self.view.frame.size.width, 41.0f);
UIView *topBar = [[UIView alloc] initWithFrame:frame];
UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"topbar.png"]];
topBar.backgroundColor = background;
if (topBarState == 0) {
[self.view addSubview:topBar];
[UIView animateWithDuration:0.5 animations:^{topBar.frame = CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 41.0f);}];
topBarState = 1;
} else if (topBarState == 1){
[UIView animateWithDuration:0.5 animations:^{topBar.frame = CGRectMake(0.0f, -41.0f, self.view.frame.size.width, 41.0f);} completion:^(BOOL finished){[topBar removeFromSuperview];}];
NSLog(#"removed");
topBarState = 0;
}
}
How do i make it so that the subview animate and is removed properly?
Best Regards
FreeSirenety
you are always set the topBar frame with y = -41, so for topBarState = 1, animation works for y=-41 to y=-41 and seems to be as not working
CGRect frame = CGRectMake(0.0f, -41.0f, self.view.frame.size.width, 41.0f);
UIView *topBar = [[UIView alloc] initWithFrame:frame];
every time you are creating the view topBar.
Declare topBar in .h and alloc init in viewDidLoad.
- (void)viewDidLoad {
CGRect frame = CGRectMake(0.0f, -41.0f, self.view.frame.size.width, 41.0f);
topBar = [[UIView alloc] initWithFrame:frame];
UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"topbar.png"]];
topBar.backgroundColor = background;
[self.view addSubview:topBar];
topBarState = 0;
}
- (void)tapGestureHandler: (UITapGestureRecognizer *)recognizer
{
if (topBarState == 0) {
[UIView animateWithDuration:0.5 animations:^{topBar.frame = CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 41.0f);}];
topBarState = 1;
} else if (topBarState == 1){
[UIView animateWithDuration:0.5 animations:^{topBar.frame = CGRectMake(0.0f, -41.0f, self.view.frame.size.width, 41.0f);} completion:^(BOOL finished){[topBar removeFromSuperview];}];
topBarState = 0;
}
}