Created
June 24, 2014 22:28
-
-
Save bleeckerj/5e80f740eaf89513f053 to your computer and use it in GitHub Desktop.
Animate the size and layout of UIViews within a UIScrollview. This shows how to use Masonry/AutoLayout to have a UIScrollView with subviews in its content view that can dynamically change their size/layout
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Created by Julian Bleecker on June 22, 2014. | |
// Copyright (c) 2014 Julian Bleecker. All rights reserved. | |
// | |
#import "MASExampleScrollViewDynamicContentViewSize.h" | |
/** | |
* | |
* This shows how to use Masonry/AutoLayout to have a UIScrollView with | |
* subviews in its content view that can dynamically change their size/layout | |
* | |
* | |
*/ | |
@interface MASExampleScrollViewDynamicContentViewSize () <UIViewControllerAnimatedTransitioning, UIGestureRecognizerDelegate> | |
@property (strong, nonatomic) UIScrollView* scrollView; | |
@property (strong, nonatomic) UIView *view_1, *view_2, *view_3, *view_4; | |
@property CGFloat growingViewHeight; | |
@property int lowGrowHeight; | |
@property (nonatomic, strong) UIPercentDrivenInteractiveTransition* inter; | |
@property BOOL interacting; | |
@end | |
@implementation MASExampleScrollViewDynamicContentViewSize | |
UIView *contentView, *sizingView; | |
BOOL growUp, lowGrow; | |
int lowGrowHeight; | |
CGFloat moveLeft; | |
UIScreenEdgePanGestureRecognizer *sep; | |
- (id)init { | |
self = [super init]; | |
if (!self) return nil; | |
growUp = YES; | |
lowGrow = NO; | |
self.growingViewHeight = 50; | |
lowGrowHeight = 50; | |
UIScrollView *scrollView = UIScrollView.new; | |
[scrollView setAutoresizesSubviews:YES]; | |
self.scrollView = scrollView; | |
scrollView.backgroundColor = [UIColor lightGrayColor]; | |
[self addSubview:scrollView]; | |
[self.scrollView mas_makeConstraints:^(MASConstraintMaker *make) { | |
make.edges.equalTo(self); | |
}]; | |
// We create a dummy contentView that will hold everything (necessary to use scrollRectToVisible later) | |
contentView = UIView.new; | |
[self.scrollView addSubview:contentView]; | |
[contentView mas_makeConstraints:^(MASConstraintMaker *make) { | |
make.edges.equalTo(self.scrollView).with.insets(UIEdgeInsetsMake(0, 0, 0, 0)); | |
make.width.equalTo(self.scrollView.mas_width); | |
}]; | |
UIView *lastView; | |
self.view_1 = UIView.new; | |
self.view_1.backgroundColor = [self randomColor]; | |
self.view_2 = UIView.new; | |
self.view_2.backgroundColor = [self randomColor]; | |
self.view_3 = UIView.new; | |
self.view_3.backgroundColor = [self randomColor]; | |
self.view_4 = UIView.new; | |
self.view_4.backgroundColor = [UIColor redColor]; | |
[contentView addSubview:self.view_1]; | |
[contentView addSubview:self.view_2]; | |
[contentView addSubview:self.view_3]; | |
[contentView addSubview:self.view_4]; | |
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)]; | |
[self.view_1 addGestureRecognizer:singleTap]; | |
[self.view_1 mas_updateConstraints:^(MASConstraintMaker *make) { | |
make.top.equalTo(@0); | |
make.left.equalTo(@0); | |
make.width.equalTo(contentView.mas_width); | |
make.height.equalTo(@(200)); | |
}]; | |
UITapGestureRecognizer *singleTap_2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)]; | |
[self.view_2 addGestureRecognizer:singleTap_2]; | |
[self.view_2 mas_updateConstraints:^(MASConstraintMaker *make) { | |
make.top.equalTo(self.view_1.mas_bottom); | |
make.left.equalTo(@0); | |
make.width.equalTo(contentView.mas_width); | |
make.height.equalTo(@(100)).key(@"height view_2"); | |
}]; | |
UIPanGestureRecognizer *flick = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)]; | |
flick.delegate = self; | |
[self.view_3 addGestureRecognizer:flick]; | |
[self.view_3 mas_updateConstraints:^(MASConstraintMaker *make) { | |
make.top.equalTo(self.view_2.mas_bottom); | |
make.left.equalTo(@0); | |
make.width.equalTo(contentView.mas_width); | |
make.height.equalTo(@(200)).key(@"height view_3"); | |
}]; | |
sep = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(panEdge:)]; | |
sep.edges = UIRectEdgeRight; | |
sep.delegate = self; | |
[self.view_4 addGestureRecognizer:sep]; | |
[self.view_4 mas_updateConstraints:^(MASConstraintMaker *make) { | |
make.top.equalTo(self.view_3.mas_bottom); | |
make.left.equalTo(@0); | |
make.width.equalTo(contentView.mas_width); | |
make.height.equalTo(@(200)).key(@"height view_4"); | |
make.bottom.equalTo(contentView.mas_bottom); | |
}]; | |
lastView = self.view_4; | |
// dummy view, which determines the size of the contentView size and therefore the scrollView contentSize | |
sizingView = UIView.new; | |
[sizingView setBackgroundColor:[UIColor purpleColor]]; | |
[scrollView addSubview:sizingView]; | |
[sizingView mas_makeConstraints:^(MASConstraintMaker *make) { | |
make.top.equalTo(lastView.mas_bottom); | |
make.bottom.equalTo(contentView.mas_bottom); | |
}]; | |
[self setNeedsUpdateConstraints]; | |
return self; | |
} | |
- (void)updateConstraints { | |
[self.view_1 mas_updateConstraints:^(MASConstraintMaker *make) { | |
make.height.equalTo(@(self.growingViewHeight)); | |
}]; | |
[self.view_2 mas_updateConstraints:^(MASConstraintMaker *make) { | |
make.top.equalTo(self.view_1.mas_bottom); | |
make.height.equalTo(@(self.growingViewHeight*1.5)); | |
}]; | |
[self.view_3 mas_updateConstraints:^(MASConstraintMaker *make) { | |
make.top.equalTo(self.view_2.mas_bottom); | |
}]; | |
[self.view_4 mas_updateConstraints:^(MASConstraintMaker *make) { | |
make.top.equalTo(self.view_3.mas_bottom); | |
}]; | |
[self.view_3 mas_updateConstraints:^(MASConstraintMaker *make) { | |
make.height.equalTo(@(lowGrowHeight)); | |
//make.height.equalTo(self.view_3).with.offset(lowGrowHeight); | |
}]; | |
[self.view_4 mas_updateConstraints:^(MASConstraintMaker *make) { | |
make.left.equalTo(@(-1*moveLeft)); | |
}]; | |
//according to apple super should be called at end of method | |
[super updateConstraints]; | |
} | |
- (void)panEdge:(UIScreenEdgePanGestureRecognizer *)sender | |
{ | |
UIView *v = sender.view; | |
if (sender.state == UIGestureRecognizerStateBegan) { | |
NSLog(@"%@", @"begin"); | |
self.inter = [UIPercentDrivenInteractiveTransition new]; | |
self.interacting = YES; | |
} | |
else if (sender.state == UIGestureRecognizerStateChanged) { | |
CGPoint delta = [sender translationInView: v]; | |
CGFloat percent = fabs(delta.x/v.bounds.size.width); | |
[self.inter updateInteractiveTransition:percent]; | |
NSLog(@"%f", percent); | |
moveLeft = 300*percent; | |
} | |
else if (sender.state == UIGestureRecognizerStateEnded) { | |
CGPoint delta = [sender translationInView: v]; | |
CGFloat percent = fabs(delta.x/v.bounds.size.width); | |
self.inter.completionSpeed = 0.5; | |
// (try completionSpeed = 2 to see "ghosting" problem after a partial) | |
// (can occur with 1 as well) | |
// (setting to 0.5 seems to fix it) | |
if (percent > 0.5) { | |
NSLog(@"%@", @"calling finish"); | |
[self.inter finishInteractiveTransition]; | |
moveLeft = 0; | |
} | |
else { | |
NSLog(@"%@", @"calling cancel"); | |
[self.inter cancelInteractiveTransition]; | |
moveLeft = 0; | |
} | |
} | |
else if (sender.state == UIGestureRecognizerStateCancelled) { | |
[self.inter cancelInteractiveTransition]; | |
} | |
[self setNeedsUpdateConstraints]; | |
// update constraints now so we can animate the change | |
[self updateConstraintsIfNeeded]; | |
[UIView animateWithDuration:0.1 animations:^{ | |
[self layoutIfNeeded]; | |
}]; | |
} | |
- (void)pan:(UIPanGestureRecognizer *)sender | |
{ | |
if(sender.view == self.view_3) { | |
CGPoint velocity = [sender velocityInView:sender.view]; | |
if(sender.state == UIGestureRecognizerStateEnded) { | |
if (velocity.y >0) // panning down | |
{ | |
lowGrow = YES; | |
lowGrowHeight += 50; | |
[sender.view setBackgroundColor:[self randomColor]]; | |
//self.brightness = self.brightness -.02; | |
} | |
if (velocity.y <0) // panning up | |
{ | |
lowGrow = NO; | |
lowGrowHeight -= 50; | |
//self.brightness = self.brightness +.02; | |
NSLog (@"Increasing brigntness in pan"); | |
} | |
if(lowGrowHeight < 50) lowGrowHeight = 50; | |
} | |
// if(sender.state == UIGestureRecognizerStateBegan) { | |
// lowGrow ^= YES; | |
// } | |
[self setNeedsUpdateConstraints]; | |
// update constraints now so we can animate the change | |
[self updateConstraintsIfNeeded]; | |
[UIView animateWithDuration:0.2 animations:^{ | |
[self layoutIfNeeded]; | |
}]; | |
} | |
} | |
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer | |
{ | |
if(gestureRecognizer == sep) { | |
return YES; | |
} | |
if(gestureRecognizer.view == self.view_3) { | |
CGPoint translation = [gestureRecognizer locationInView:self.view_3]; | |
if(translation.x <50) { | |
return YES; | |
} | |
} | |
return NO; | |
} | |
- (void)singleTap:(UITapGestureRecognizer*)sender { | |
//[sender.view setAlpha:sender.view.alpha / 1.20]; // To see something happen on screen when you tap :O | |
UIView *view = sender.view; | |
if(view == self.view_1) { | |
if (growUp == YES) { | |
self.growingViewHeight += 250; | |
growUp = NO; | |
} else { | |
self.growingViewHeight -= 250; | |
growUp = YES; | |
} | |
} | |
//self.view_2.frame = CGRectMake(0, 0, 300, 300); | |
//view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, self.growingViewHeight); | |
[self setNeedsUpdateConstraints]; | |
// update constraints now so we can animate the change | |
[self updateConstraintsIfNeeded]; | |
[UIView animateWithDuration:0.4 animations:^{ | |
[self layoutIfNeeded]; | |
}]; | |
[self.scrollView scrollRectToVisible:sender.view.frame animated:YES]; | |
} | |
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext { | |
return 0.4; | |
} | |
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { | |
NSLog(@"%@", transitionContext); | |
} | |
- (UIColor *)randomColor { | |
CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0 | |
CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white | |
CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black | |
return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment