Created
February 6, 2014 16:18
-
-
Save christianroman/8847406 to your computer and use it in GitHub Desktop.
Facebook Paper clone using Auto Layout and UIScrollView (First attempt)
This file contains hidden or 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
// | |
// HomeViewController.m | |
// Stories | |
// | |
// Created by Christian Roman on 05/02/14. | |
// Copyright (c) 2014 Christian Roman. All rights reserved. | |
// | |
#import "HomeViewController.h" | |
#import "StoryView.h" | |
@interface HomeViewController () <UIScrollViewDelegate> | |
@property (nonatomic, strong) UIScrollView *scrollView; | |
@property (nonatomic, strong) NSLayoutConstraint *constraintHeight; | |
@property (nonatomic, strong) NSMutableArray *contrainstArray; | |
@property (nonatomic, assign) BOOL pageOpened; | |
@end | |
@implementation HomeViewController | |
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil | |
{ | |
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; | |
if (self) { | |
// Custom initialization | |
} | |
return self; | |
} | |
- (void)viewDidLoad | |
{ | |
[super viewDidLoad]; | |
[self configureScrollView]; | |
[self adjustStories]; | |
} | |
- (void)configureScrollView | |
{ | |
_contrainstArray = [[NSMutableArray alloc] init]; | |
_scrollView = [UIScrollView new]; | |
[_scrollView setDelegate:self]; | |
_scrollView.translatesAutoresizingMaskIntoConstraints = NO; | |
[self.view addSubview:_scrollView]; | |
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_scrollView | |
attribute:NSLayoutAttributeLeft | |
relatedBy:NSLayoutRelationEqual | |
toItem:self.view | |
attribute:NSLayoutAttributeLeft | |
multiplier:1.0 | |
constant:0]]; | |
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_scrollView | |
attribute:NSLayoutAttributeRight | |
relatedBy:NSLayoutRelationEqual | |
toItem:self.view | |
attribute:NSLayoutAttributeRight | |
multiplier:1.0 | |
constant:0]]; | |
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_scrollView | |
attribute:NSLayoutAttributeWidth | |
relatedBy:NSLayoutRelationEqual | |
toItem:self.view | |
attribute:NSLayoutAttributeWidth | |
multiplier:1.0 | |
constant:0]]; | |
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_scrollView | |
attribute:NSLayoutAttributeTrailing | |
relatedBy:NSLayoutRelationEqual | |
toItem:self.view | |
attribute:NSLayoutAttributeTrailing | |
multiplier:1.0 | |
constant:0]]; | |
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_scrollView | |
attribute:NSLayoutAttributeBottom | |
relatedBy:NSLayoutRelationEqual | |
toItem:self.view | |
attribute:NSLayoutAttributeBottom | |
multiplier:1.0 | |
constant:0]]; | |
_constraintHeight = [NSLayoutConstraint constraintWithItem:_scrollView | |
attribute:NSLayoutAttributeHeight | |
relatedBy:NSLayoutRelationEqual | |
toItem:nil | |
attribute:NSLayoutAttributeNotAnAttribute | |
multiplier:1.0 | |
constant:253]; | |
[self.view addConstraint:_constraintHeight]; | |
} | |
- (void)adjustStories | |
{ | |
StoryView *previousStory = nil; | |
for (NSInteger i = 0; i < 10; i++) { | |
StoryView *story = [StoryView new]; | |
[story setTag:i]; | |
CGFloat hue = ( arc4random() % 256 / 256.0 ); | |
CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; | |
CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; | |
UIColor *color = [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1]; | |
[story setBackgroundColor:color]; | |
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapFrom:)]; | |
[story addGestureRecognizer:tapGestureRecognizer]; | |
story.translatesAutoresizingMaskIntoConstraints = NO; | |
[_scrollView addSubview:story]; | |
// Default constraints | |
[_scrollView addConstraint:[NSLayoutConstraint constraintWithItem:story | |
attribute:NSLayoutAttributeTop | |
relatedBy:NSLayoutRelationEqual | |
toItem:_scrollView | |
attribute:NSLayoutAttributeTop | |
multiplier:1.0 | |
constant:0]]; | |
[_scrollView addConstraint:[NSLayoutConstraint constraintWithItem:story | |
attribute:NSLayoutAttributeBottom | |
relatedBy:NSLayoutRelationEqual | |
toItem:_scrollView | |
attribute:NSLayoutAttributeBottom | |
multiplier:1.0 | |
constant:0]]; | |
NSLayoutConstraint *storyWidthConstraint = [NSLayoutConstraint constraintWithItem:story | |
attribute:NSLayoutAttributeWidth | |
relatedBy:NSLayoutRelationEqual | |
toItem:nil | |
attribute:NSLayoutAttributeNotAnAttribute | |
multiplier:1.0 | |
constant:143]; | |
[_scrollView addConstraint:storyWidthConstraint]; | |
[_contrainstArray addObject:storyWidthConstraint]; | |
[_scrollView addConstraint:[NSLayoutConstraint constraintWithItem:story | |
attribute:NSLayoutAttributeHeight | |
relatedBy:NSLayoutRelationEqual | |
toItem:_scrollView | |
attribute:NSLayoutAttributeHeight | |
multiplier:1.0 | |
constant:0]]; | |
if (!previousStory) { | |
[_scrollView addConstraint:[NSLayoutConstraint constraintWithItem:story | |
attribute:NSLayoutAttributeLeft | |
relatedBy:NSLayoutRelationEqual | |
toItem:_scrollView | |
attribute:NSLayoutAttributeLeft | |
multiplier:1.0 | |
constant:0]]; | |
} else { | |
[_scrollView addConstraint:[NSLayoutConstraint constraintWithItem:story | |
attribute:NSLayoutAttributeLeft | |
relatedBy:NSLayoutRelationEqual | |
toItem:previousStory | |
attribute:NSLayoutAttributeRight | |
multiplier:1.0 | |
constant:0]]; | |
} | |
previousStory = story; | |
} | |
[_scrollView addConstraint:[NSLayoutConstraint constraintWithItem:previousStory | |
attribute:NSLayoutAttributeRight | |
relatedBy:NSLayoutRelationEqual | |
toItem:_scrollView | |
attribute:NSLayoutAttributeRight | |
multiplier:1.0 | |
constant:0]]; | |
} | |
- (void)handleTapFrom:(UITapGestureRecognizer *)sender | |
{ | |
NSInteger element = sender.view.tag; | |
CGRect frame = _scrollView.frame; | |
frame.origin.x = _pageOpened ? 0 : 320 * element; | |
[_scrollView scrollRectToVisible:frame animated:YES]; | |
//[_scrollView setContentOffset:CGPointMake(-320 * element, 0) animated:YES]; | |
if (!_pageOpened) { | |
[self.view removeConstraint:_constraintHeight]; | |
_constraintHeight = [NSLayoutConstraint constraintWithItem:_scrollView | |
attribute:NSLayoutAttributeHeight | |
relatedBy:NSLayoutRelationEqual | |
toItem:self.view | |
attribute:NSLayoutAttributeHeight | |
multiplier:1.0 | |
constant:0]; | |
[self.view addConstraint:_constraintHeight]; | |
[self increaseStoriesSize]; | |
} else { | |
[self.view removeConstraint:_constraintHeight]; | |
_constraintHeight = [NSLayoutConstraint constraintWithItem:_scrollView | |
attribute:NSLayoutAttributeHeight | |
relatedBy:NSLayoutRelationEqual | |
toItem:nil | |
attribute:NSLayoutAttributeNotAnAttribute | |
multiplier:1.0 | |
constant:253]; | |
[self.view addConstraint:_constraintHeight]; | |
[self decreaseStoriesSize]; | |
} | |
[_scrollView setPagingEnabled:!_pageOpened]; | |
[self.view setNeedsUpdateConstraints]; | |
[_scrollView setNeedsUpdateConstraints]; | |
[UIView animateWithDuration:0.3 animations:^{ | |
[self.view layoutIfNeeded]; | |
[_scrollView layoutIfNeeded]; | |
} completion:^(BOOL finished) { | |
_pageOpened = !_pageOpened; | |
}]; | |
} | |
- (void)increaseStoriesSize | |
{ | |
for (NSLayoutConstraint *constraint in _contrainstArray) { | |
[constraint setConstant:320]; | |
} | |
} | |
- (void)decreaseStoriesSize | |
{ | |
for (NSLayoutConstraint *constraint in _contrainstArray) { | |
[constraint setConstant:143]; | |
} | |
} | |
@end |
This file contains hidden or 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
// | |
// StoryView.m | |
// Stories | |
// | |
// Created by Christian Roman on 05/02/14. | |
// Copyright (c) 2014 Christian Roman. All rights reserved. | |
// | |
#import "StoryView.h" | |
@import QuartzCore; | |
@implementation StoryView | |
- (id)initWithFrame:(CGRect)frame | |
{ | |
self = [super initWithFrame:frame]; | |
if (self) { | |
[self setBackgroundColor:[UIColor whiteColor]]; | |
[self.layer setCornerRadius:4.0]; | |
[self.layer setBorderColor:[UIColor blackColor].CGColor]; | |
[self.layer setBorderWidth:0.5]; | |
} | |
return self; | |
} |
Author
christianroman
commented
Feb 6, 2014
Amazing!
Tried with something similar ended up with updating frames with animations. You solution seems to be better. Like this!
Very good practice of creating layout constraint programatically. Thumb up !
are you able to expand views that are further away ( elements 4,5...) with the same animation? i've tried using your code at it seems that this only works when the scrollview doesn't need to scroll to a point thats more than 320 points away from the start.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment