123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- //
- // IDMZoomingScrollView.m
- // IDMPhotoBrowser
- //
- // Created by Michael Waterfall on 14/10/2010.
- // Copyright 2010 d3i. All rights reserved.
- //
- #import "IDMZoomingScrollView.h"
- #import "IDMPhotoBrowser.h"
- #import "IDMPhoto.h"
- // Declare private methods of browser
- @interface IDMPhotoBrowser ()
- - (UIImage *)imageForPhoto:(id<IDMPhoto>)photo;
- - (void)cancelControlHiding;
- - (void)hideControlsAfterDelay;
- //- (void)toggleControls;
- - (void)handleSingleTap;
- @end
- // Private methods and properties
- @interface IDMZoomingScrollView ()
- @property (nonatomic, weak) IDMPhotoBrowser *photoBrowser;
- - (void)handleSingleTap:(CGPoint)touchPoint;
- - (void)handleDoubleTap:(CGPoint)touchPoint;
- @end
- @implementation IDMZoomingScrollView
- @synthesize photoImageView = _photoImageView, photoBrowser = _photoBrowser, photo = _photo, captionView = _captionView;
- - (id)initWithPhotoBrowser:(IDMPhotoBrowser *)browser {
- if ((self = [super init])) {
- // Delegate
- self.photoBrowser = browser;
-
- // Tap view for background
- _tapView = [[IDMTapDetectingView alloc] initWithFrame:self.bounds];
- _tapView.tapDelegate = self;
- _tapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- _tapView.backgroundColor = [UIColor clearColor];
- [self addSubview:_tapView];
-
- // Image view
- _photoImageView = [[IDMTapDetectingImageView alloc] initWithFrame:CGRectZero];
- _photoImageView.tapDelegate = self;
- _photoImageView.backgroundColor = [UIColor clearColor];
- [self addSubview:_photoImageView];
-
- CGRect screenBound = [[UIScreen mainScreen] bounds];
- CGFloat screenWidth = screenBound.size.width;
- CGFloat screenHeight = screenBound.size.height;
-
- if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft || [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight) {
- screenWidth = screenBound.size.height;
- screenHeight = screenBound.size.width;
- }
-
- // Progress view
- _progressView = [[DACircularProgressView alloc] initWithFrame:CGRectMake((screenWidth-35.)/2., (screenHeight-35.)/2, 35.0f, 35.0f)];
- [_progressView setProgress:0.0f];
- _progressView.tag = 101;
- _progressView.thicknessRatio = 0.1;
- _progressView.roundedCorners = NO;
- _progressView.trackTintColor = browser.trackTintColor ? self.photoBrowser.trackTintColor : [UIColor colorWithWhite:0.2 alpha:1];
- _progressView.progressTintColor = browser.progressTintColor ? self.photoBrowser.progressTintColor : [UIColor colorWithWhite:1.0 alpha:1];
- [self addSubview:_progressView];
-
- // Setup
- self.backgroundColor = [UIColor clearColor];
- self.delegate = self;
- self.showsHorizontalScrollIndicator = NO;
- self.showsVerticalScrollIndicator = NO;
- self.decelerationRate = UIScrollViewDecelerationRateFast;
- self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
- }
-
- return self;
- }
- - (void)setPhoto:(id<IDMPhoto>)photo {
- _photoImageView.image = nil; // Release image
- if (_photo != photo) {
- _photo = photo;
- }
- [self displayImage];
- }
- - (void)prepareForReuse {
- self.photo = nil;
- [_captionView removeFromSuperview];
- self.captionView = nil;
- }
- #pragma mark - Image
- // Get and display image
- - (void)displayImage {
- if (_photo) {
- // Reset
- self.maximumZoomScale = 1;
- self.minimumZoomScale = 1;
- self.zoomScale = 1;
-
- self.contentSize = CGSizeMake(0, 0);
-
- // Get image from browser as it handles ordering of fetching
- UIImage *img = [self.photoBrowser imageForPhoto:_photo];
- if (img) {
- // Hide ProgressView
- //_progressView.alpha = 0.0f;
- [_progressView removeFromSuperview];
-
- // Set image
- _photoImageView.image = img;
- _photoImageView.hidden = NO;
-
- // Setup photo frame
- CGRect photoImageViewFrame;
- photoImageViewFrame.origin = CGPointZero;
- photoImageViewFrame.size = img.size;
-
- _photoImageView.frame = photoImageViewFrame;
- self.contentSize = photoImageViewFrame.size;
- // Set zoom to minimum zoom
- [self setMaxMinZoomScalesForCurrentBounds];
- } else {
- // Hide image view
- _photoImageView.hidden = YES;
-
- _progressView.alpha = 1.0f;
- }
-
- [self setNeedsLayout];
- }
- }
- - (void)setProgress:(CGFloat)progress forPhoto:(IDMPhoto*)photo {
- IDMPhoto *p = (IDMPhoto*)self.photo;
- if ([photo.photoURL.absoluteString isEqualToString:p.photoURL.absoluteString]) {
- if (_progressView.progress < progress) {
- [_progressView setProgress:progress animated:YES];
- }
- }
- }
- // Image failed so just show black!
- - (void)displayImageFailure {
- [_progressView removeFromSuperview];
- }
- #pragma mark - Setup
- - (void)setMaxMinZoomScalesForCurrentBounds {
- // Reset
- self.maximumZoomScale = 1;
- self.minimumZoomScale = 1;
- self.zoomScale = 1;
-
- // Bail
- if (_photoImageView.image == nil) return;
-
- // Sizes
- CGSize boundsSize = self.bounds.size;
- boundsSize.width -= 0.1;
- boundsSize.height -= 0.1;
-
- CGSize imageSize = _photoImageView.frame.size;
-
- // Calculate Min
- CGFloat xScale = boundsSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
- CGFloat yScale = boundsSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
- CGFloat minScale = MIN(xScale, yScale); // use minimum of these to allow the image to become fully visible
-
- // If image is smaller than the screen then ensure we show it at
- // min scale of 1
- if (xScale > 1 && yScale > 1) {
- //minScale = 1.0;
- }
-
- // Calculate Max
- CGFloat maxScale = 4.0; // Allow double scale
- // on high resolution screens we have double the pixel density, so we will be seeing every pixel if we limit the
- // maximum zoom scale to 0.5.
- if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
- maxScale = maxScale / [[UIScreen mainScreen] scale];
-
- if (maxScale < minScale) {
- maxScale = minScale * 2;
- }
- }
- // Calculate Max Scale Of Double Tap
- CGFloat maxDoubleTapZoomScale = 4.0 * minScale; // Allow double scale
- // on high resolution screens we have double the pixel density, so we will be seeing every pixel if we limit the
- // maximum zoom scale to 0.5.
- if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
- maxDoubleTapZoomScale = maxDoubleTapZoomScale / [[UIScreen mainScreen] scale];
- if (maxDoubleTapZoomScale < minScale) {
- maxDoubleTapZoomScale = minScale * 2;
- }
- }
-
- // Set
- self.maximumZoomScale = maxScale;
- self.minimumZoomScale = minScale;
- self.zoomScale = minScale;
- self.maximumDoubleTapZoomScale = maxDoubleTapZoomScale;
-
- // Reset position
- _photoImageView.frame = CGRectMake(0, 0, _photoImageView.frame.size.width, _photoImageView.frame.size.height);
- [self setNeedsLayout];
- }
- #pragma mark - Layout
- - (void)layoutSubviews {
- // Update tap view frame
- _tapView.frame = self.bounds;
-
- // Super
- [super layoutSubviews];
-
- // Center the image as it becomes smaller than the size of the screen
- CGSize boundsSize = self.bounds.size;
- CGRect frameToCenter = _photoImageView.frame;
-
- // Horizontally
- if (frameToCenter.size.width < boundsSize.width) {
- frameToCenter.origin.x = floorf((boundsSize.width - frameToCenter.size.width) / 2.0);
- } else {
- frameToCenter.origin.x = 0;
- }
-
- // Vertically
- if (frameToCenter.size.height < boundsSize.height) {
- frameToCenter.origin.y = floorf((boundsSize.height - frameToCenter.size.height) / 2.0);
- } else {
- frameToCenter.origin.y = 0;
- }
-
- // Center
- if (!CGRectEqualToRect(_photoImageView.frame, frameToCenter))
- _photoImageView.frame = frameToCenter;
- }
- #pragma mark - UIScrollViewDelegate
- - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
- return _photoImageView;
- }
- - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
- [_photoBrowser cancelControlHiding];
- }
- - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
- [_photoBrowser cancelControlHiding];
- }
- - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
- [_photoBrowser hideControlsAfterDelay];
- }
- - (void)scrollViewDidZoom:(UIScrollView *)scrollView {
- [self setNeedsLayout];
- [self layoutIfNeeded];
- }
- #pragma mark - Tap Detection
- - (void)handleSingleTap:(CGPoint)touchPoint {
- // [_photoBrowser performSelector:@selector(toggleControls) withObject:nil afterDelay:0.2];
- [_photoBrowser performSelector:@selector(handleSingleTap) withObject:nil afterDelay:0.2];
- }
- - (void)handleDoubleTap:(CGPoint)touchPoint {
-
- // Cancel any single tap handling
- [NSObject cancelPreviousPerformRequestsWithTarget:_photoBrowser];
-
- // Zoom
- if (self.zoomScale == self.maximumDoubleTapZoomScale) {
-
- // Zoom out
- [self setZoomScale:self.minimumZoomScale animated:YES];
-
- } else {
-
- // Zoom in
- CGSize targetSize = CGSizeMake(self.frame.size.width / self.maximumDoubleTapZoomScale, self.frame.size.height / self.maximumDoubleTapZoomScale);
- CGPoint targetPoint = CGPointMake(touchPoint.x - targetSize.width / 2, touchPoint.y - targetSize.height / 2);
-
- [self zoomToRect:CGRectMake(targetPoint.x, targetPoint.y, targetSize.width, targetSize.height) animated:YES];
-
- }
-
- // Delay controls
- [_photoBrowser hideControlsAfterDelay];
- }
- // Image View
- - (void)imageView:(UIImageView *)imageView singleTapDetected:(UITouch *)touch {
- [self handleSingleTap:[touch locationInView:imageView]];
- }
- - (void)imageView:(UIImageView *)imageView doubleTapDetected:(UITouch *)touch {
- [self handleDoubleTap:[touch locationInView:imageView]];
- }
- // Background View
- - (void)view:(UIView *)view singleTapDetected:(UITouch *)touch {
- [self handleSingleTap:[touch locationInView:view]];
- }
- - (void)view:(UIView *)view doubleTapDetected:(UITouch *)touch {
- [self handleDoubleTap:[touch locationInView:view]];
- }
- @end
|