Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save bleeckerj/5e80f740eaf89513f053 to your computer and use it in GitHub Desktop.
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
//
// 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