// // DTSmartPagingScrollView.m // DTSmartPhotoView // // Created by Stefan Gugarel on 5/11/12. // Copyright (c) 2012 Cocoanetics. All rights reserved. // #import "DTSmartPagingScrollView.h" #import @interface DTSmartPagingScrollView () - (void)_setupVisiblePageViews; - (CGRect)frameForPageViewAtIndex:(NSUInteger)index; - (void)_updateCurrentPage; @end @implementation DTSmartPagingScrollView { DT_WEAK_VARIABLE id _pageDatasource; NSUInteger _numberOfPages; NSMutableDictionary *_viewsByPage; NSMutableSet *_visiblePageViews; NSUInteger _currentPageIndex; BOOL _firstLayoutDone; } - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.pagingEnabled = YES; // no indicators because zooming subview has there self.showsVerticalScrollIndicator = NO; self.showsHorizontalScrollIndicator = NO; _viewsByPage = [[NSMutableDictionary alloc] init]; _visiblePageViews = [[NSMutableSet alloc] init]; } return self; } - (void)layoutSubviews { [super layoutSubviews]; if (!_numberOfPages) { return; } if (self.dragging) { // perform update right away [self _updateCurrentPage]; } // Note: first content size plus page so that the visible view is properly shown CGSize neededContentSize = CGSizeMake(self.bounds.size.width * _numberOfPages, self.bounds.size.height); if (!CGSizeEqualToSize(neededContentSize, self.contentSize)) { // frame changed self.contentSize = neededContentSize; [self scrollToPage:_currentPageIndex animated:NO]; } [self _setupVisiblePageViews]; } - (void)_updateCurrentPage { NSUInteger newPageIndex = roundf(self.contentOffset.x / self.frame.size.width); if (_currentPageIndex != newPageIndex) { [self willChangeValueForKey:@"currentPageIndex"]; _currentPageIndex = newPageIndex; if ([_pageDatasource respondsToSelector:@selector(smartPagingScrollView:didScrollToPageAtIndex:)]) { [_pageDatasource smartPagingScrollView:self didScrollToPageAtIndex:_currentPageIndex]; } [self didChangeValueForKey:@"currentPageIndex"]; } } - (NSRange)rangeOfVisiblePages { CGFloat position = self.contentOffset.x / self.bounds.size.width; NSInteger firstVisibleIndex = MAX(0, floorf(position)); NSInteger lastVisibleIndex = MIN(ceilf(position), _numberOfPages-1); // check if right page is really visible CGRect rightFrame = [self frameForPageViewAtIndex:lastVisibleIndex]; if (!CGRectIntersectsRect(rightFrame, self.bounds)) { lastVisibleIndex--; } // check if left page is really visible CGRect leftFrame = [self frameForPageViewAtIndex:firstVisibleIndex]; if (!CGRectIntersectsRect(leftFrame, self.bounds)) { firstVisibleIndex++; } return NSMakeRange(firstVisibleIndex, lastVisibleIndex - firstVisibleIndex + 1); } - (UIView *)viewForIndex:(NSUInteger)index { NSNumber *cacheKey = [NSNumber numberWithUnsignedInteger:index]; UIView *view = [_viewsByPage objectForKey:cacheKey]; if (view) { // got cached view return view; } // get view from datasource view = [_pageDatasource smartPagingScrollView:self viewForPageAtIndex:index]; // cache it [_viewsByPage setObject:view forKey:cacheKey]; return view; } - (void)_setupVisiblePageViews { NSRange visibleRange = [self rangeOfVisiblePages]; [CATransaction begin]; [CATransaction setDisableActions:NO]; NSMutableSet *newVisiblePageViews = [NSMutableSet set]; for (NSInteger idx = visibleRange.location; idx)pageDatasource { if (_pageDatasource != pageDatasource) { _pageDatasource = pageDatasource; // refresh parameters [self reloadData]; } } - (void)setCurrentPageIndex:(NSUInteger)currentPageIndex { if (_currentPageIndex != currentPageIndex) { _currentPageIndex = currentPageIndex; [self scrollToPage:_currentPageIndex animated:NO]; } } @synthesize pageDatasource = _pageDatasource; @synthesize currentPageIndex = _currentPageIndex; @end