Skip to content

Commit

Permalink
NIPagingScrollView additions
Browse files Browse the repository at this point in the history
NIPagingScrollView.pageInset if non zero will inset the page from full screen, thus making neighboring pages visible.  This is useful for showing that there is scrollable content and presents a 'film-strip' like appearance.

Note in this case we take on the page snapping responsibilities as UIScrollView is fundamentally view bounds oriented and doens't support edges of other pages showing.
  • Loading branch information
George Harker committed Apr 1, 2015
1 parent a94c503 commit 4eeb799
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 21 deletions.
4 changes: 4 additions & 0 deletions src/pagingscrollview/src/NIPagingScrollView.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ typedef enum {

#pragma mark Configuring Presentation

// Controls the border between images.
@property (nonatomic) CGFloat pageMargin;
// Used to make the view smaller than full screen, thus showing neighboring images
// in the view.
@property (nonatomic) CGFloat pageInset;
@property (nonatomic) NIPagingScrollViewType type; // Default: NIPagingScrollViewHorizontal

#pragma mark Visible Pages
Expand Down
103 changes: 83 additions & 20 deletions src/pagingscrollview/src/NIPagingScrollView.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

const NSInteger NIPagingScrollViewUnknownNumberOfPages = -1;
const CGFloat NIPagingScrollViewDefaultPageMargin = 10;
const CGFloat NIPagingScrollViewDefaultPageInset = 0;

@implementation NIPagingScrollView {
NIViewRecycler* _viewRecycler;
Expand All @@ -49,6 +50,7 @@ @implementation NIPagingScrollView {
- (void)commonInit {
// Default state.
self.pageMargin = NIPagingScrollViewDefaultPageMargin;
self.pageInset = NIPagingScrollViewDefaultPageInset;
self.type = NIPagingScrollViewHorizontal;

// Internal state
Expand Down Expand Up @@ -119,14 +121,14 @@ - (CGRect)frameForPageAtIndex:(NSInteger)pageIndex {
CGRect pageFrame = bounds;

if (NIPagingScrollViewHorizontal == self.type) {
pageFrame.origin.x = (bounds.size.width * pageIndex);
pageFrame.origin.x = ((bounds.size.width - self.pageInset * 2) * pageIndex);
// We need to counter the extra spacing added to the paging scroll view in
// frameForPagingScrollView.
pageFrame = CGRectInset(pageFrame, self.pageMargin, 0);
pageFrame = CGRectInset(pageFrame, self.pageMargin + self.pageInset, 0);

} else if (NIPagingScrollViewVertical == self.type) {
pageFrame.origin.y = (bounds.size.height * pageIndex);
pageFrame = CGRectInset(pageFrame, 0, self.pageMargin);
pageFrame.origin.y = ((bounds.size.height - self.pageInset * 2) * pageIndex);
pageFrame = CGRectInset(pageFrame, 0, self.pageMargin + self.pageInset);
}

return pageFrame;
Expand All @@ -137,32 +139,32 @@ - (CGSize)contentSizeForPagingScrollView {
// outlined above.
CGRect bounds = _scrollView.bounds;
if (NIPagingScrollViewHorizontal == self.type) {
return CGSizeMake(bounds.size.width * self.numberOfPages, bounds.size.height);
return CGSizeMake((bounds.size.width - self.pageInset * 2) * self.numberOfPages, bounds.size.height);

} else if (NIPagingScrollViewVertical == self.type) {
return CGSizeMake(bounds.size.width, bounds.size.height * self.numberOfPages);
return CGSizeMake(bounds.size.width, (bounds.size.height - self.pageInset * 2) * self.numberOfPages);
}

return CGSizeZero;
}

- (CGPoint)contentOffsetFromPageOffset:(CGPoint)offset {
if (NIPagingScrollViewHorizontal == self.type) {
offset.x -= self.pageMargin;
offset.x -= self.pageMargin + self.pageInset;

} else if (NIPagingScrollViewVertical == self.type) {
offset.y -= self.pageMargin;
offset.y -= self.pageMargin + self.pageInset;
}

return offset;
}

- (CGFloat)pageScrollableDimension {
if (NIPagingScrollViewHorizontal == self.type) {
return _scrollView.bounds.size.width;
return _scrollView.bounds.size.width - self.pageInset * 2;

} else if (NIPagingScrollViewVertical == self.type) {
return _scrollView.bounds.size.height;
return _scrollView.bounds.size.height - self.pageInset * 2;
}

return 0;
Expand Down Expand Up @@ -214,19 +216,42 @@ - (NSInteger)currentVisiblePageIndex {
if (NIPagingScrollViewHorizontal == self.type) {
// Whatever image is currently displayed in the center of the screen is the currently
// visible image.
return NIBoundi((NSInteger)(NICGFloatFloor((contentOffset.x + boundsSize.width / 2) / boundsSize.width)
return NIBoundi((NSInteger)(NICGFloatFloor((contentOffset.x + boundsSize.width / 2) /
(boundsSize.width - self.pageInset * 2))
+ 0.5f),
0, self.numberOfPages - 1);

} else if (NIPagingScrollViewVertical == self.type) {
return NIBoundi((NSInteger)(NICGFloatFloor((contentOffset.y + boundsSize.height / 2) / boundsSize.height)
return NIBoundi((NSInteger)(NICGFloatFloor((contentOffset.y + boundsSize.height / 2) /
(boundsSize.height - self.pageInset * 2))
+ 0.5f),
0, self.numberOfPages - 1);
}

return 0;
}

- (NSInteger)pageIndexForOffset:(CGFloat)offset {
CGSize boundsSize = _scrollView.bounds.size;

if (NIPagingScrollViewHorizontal == self.type) {
// Whatever image is currently displayed in the center of the screen is the currently
// visible image.
return NIBoundi((NSInteger)(NICGFloatFloor((offset + boundsSize.width / 2) /
(boundsSize.width - self.pageInset * 2))
+ 0.5f),
0, self.numberOfPages - 1);

} else if (NIPagingScrollViewVertical == self.type) {
return NIBoundi((NSInteger)(NICGFloatFloor((offset + boundsSize.height / 2) /
(boundsSize.height - self.pageInset * 2))
+ 0.5f),
0, self.numberOfPages - 1);
}

return 0;
}

- (NSRange)rangeOfVisiblePages {
if (0 >= self.numberOfPages) {
return NSMakeRange(0, 0);
Expand Down Expand Up @@ -350,15 +375,20 @@ - (void)updateVisiblePagesShouldNotifyDelegate:(BOOL)shouldNotifyDelegate {

[self didChangeCenterPageIndexFrom:oldCenterPageIndex to:_centerPageIndex];

// Prioritize displaying the currently visible page.
if (![self isDisplayingPageForIndex:_centerPageIndex]) {
[self displayPageAtIndex:_centerPageIndex];
if (_pageInset != 0) {
// When multiple images are visible load them all.
[self preloadOffscreenPages];
} else {
// Prioritize displaying the currently visible page.
if (![self isDisplayingPageForIndex:_centerPageIndex]) {
[self displayPageAtIndex:_centerPageIndex];
}

// Add missing pages after displaying the current page.
[self performSelector:@selector(preloadOffscreenPages)
withObject:nil
afterDelay:0];
}

// Add missing pages after displaying the current page.
[self performSelector:@selector(preloadOffscreenPages)
withObject:nil
afterDelay:0];
} else {
_centerPageIndex = -1;
}
Expand Down Expand Up @@ -393,6 +423,11 @@ - (void)setFrame:(CGRect)frame {
[self layoutVisiblePages];
}

- (void)layoutSubviews {
_scrollView.contentSize = [self contentSizeForPagingScrollView];
[self layoutVisiblePages];
}

#pragma mark - UIScrollViewDelegate

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
Expand Down Expand Up @@ -440,6 +475,21 @@ - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL
}
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
if (_pageInset == 0) {
// We handle the standard case for scrolling via pagingEnabled.
return;
}
CGFloat targetX = _scrollView.contentOffset.x + velocity.x * 60.0f;
NSInteger targetIndex = [self pageIndexForOffset:targetX];

CGPoint offset = [self frameForPageAtIndex:targetIndex].origin;
offset = [self contentOffsetFromPageOffset:offset];

*targetContentOffset = offset;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self updateVisiblePagesShouldNotifyDelegate:YES];
[self resetSurroundingPages];
Expand Down Expand Up @@ -694,6 +744,19 @@ - (void)setPageMargin:(CGFloat)pageMargin {
[self setNeedsLayout];
}

- (void)setPageInset:(CGFloat)pageInset {
_pageInset = pageInset;
// Do not auto-page inset views. The paging is based on view bounds,
// which would not be correct. We compute the snapping manually.
_scrollView.pagingEnabled = (_pageInset == 0);

// Retain the current position.
CGPoint offset = [self frameForPageAtIndex:_centerPageIndex].origin;
_scrollView.contentOffset = [self contentOffsetFromPageOffset:offset];

[self setNeedsLayout];
}

- (void)setType:(NIPagingScrollViewType)type {
if (_type != type) {
_type = type;
Expand Down
3 changes: 2 additions & 1 deletion src/photos/src/NIPhotoAlbumScrollView.m
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ - (void)willDisplayPage:(NIPhotoScrollView *)page {
originalPhotoDimensions: &originalPhotoDimensions];

page.photoDimensions = originalPhotoDimensions;
page.loading = isLoading;
// Only mark the view as loading if the center image is loading.
page.loading = (page.pageIndex == self.centerPageIndex) && isLoading;

if (nil == image) {
page.zoomingIsEnabled = NO;
Expand Down

0 comments on commit 4eeb799

Please sign in to comment.