IDMZoomingScrollView.m 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. //
  2. // IDMZoomingScrollView.m
  3. // IDMPhotoBrowser
  4. //
  5. // Created by Michael Waterfall on 14/10/2010.
  6. // Copyright 2010 d3i. All rights reserved.
  7. //
  8. #import "IDMZoomingScrollView.h"
  9. #import "IDMPhotoBrowser.h"
  10. #import "IDMPhoto.h"
  11. // Declare private methods of browser
  12. @interface IDMPhotoBrowser ()
  13. - (UIImage *)imageForPhoto:(id<IDMPhoto>)photo;
  14. - (void)cancelControlHiding;
  15. - (void)hideControlsAfterDelay;
  16. //- (void)toggleControls;
  17. - (void)handleSingleTap;
  18. @end
  19. // Private methods and properties
  20. @interface IDMZoomingScrollView ()
  21. @property (nonatomic, weak) IDMPhotoBrowser *photoBrowser;
  22. - (void)handleSingleTap:(CGPoint)touchPoint;
  23. - (void)handleDoubleTap:(CGPoint)touchPoint;
  24. @end
  25. @implementation IDMZoomingScrollView
  26. @synthesize photoImageView = _photoImageView, photoBrowser = _photoBrowser, photo = _photo, captionView = _captionView;
  27. - (id)initWithPhotoBrowser:(IDMPhotoBrowser *)browser {
  28. if ((self = [super init])) {
  29. // Delegate
  30. self.photoBrowser = browser;
  31. // Tap view for background
  32. _tapView = [[IDMTapDetectingView alloc] initWithFrame:self.bounds];
  33. _tapView.tapDelegate = self;
  34. _tapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  35. _tapView.backgroundColor = [UIColor clearColor];
  36. [self addSubview:_tapView];
  37. // Image view
  38. _photoImageView = [[IDMTapDetectingImageView alloc] initWithFrame:CGRectZero];
  39. _photoImageView.tapDelegate = self;
  40. _photoImageView.backgroundColor = [UIColor clearColor];
  41. [self addSubview:_photoImageView];
  42. CGRect screenBound = [[UIScreen mainScreen] bounds];
  43. CGFloat screenWidth = screenBound.size.width;
  44. CGFloat screenHeight = screenBound.size.height;
  45. if ([[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeLeft || [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationLandscapeRight) {
  46. screenWidth = screenBound.size.height;
  47. screenHeight = screenBound.size.width;
  48. }
  49. // Progress view
  50. _progressView = [[DACircularProgressView alloc] initWithFrame:CGRectMake((screenWidth-35.)/2., (screenHeight-35.)/2, 35.0f, 35.0f)];
  51. [_progressView setProgress:0.0f];
  52. _progressView.tag = 101;
  53. _progressView.thicknessRatio = 0.1;
  54. _progressView.roundedCorners = NO;
  55. _progressView.trackTintColor = browser.trackTintColor ? self.photoBrowser.trackTintColor : [UIColor colorWithWhite:0.2 alpha:1];
  56. _progressView.progressTintColor = browser.progressTintColor ? self.photoBrowser.progressTintColor : [UIColor colorWithWhite:1.0 alpha:1];
  57. [self addSubview:_progressView];
  58. // Setup
  59. self.backgroundColor = [UIColor clearColor];
  60. self.delegate = self;
  61. self.showsHorizontalScrollIndicator = NO;
  62. self.showsVerticalScrollIndicator = NO;
  63. self.decelerationRate = UIScrollViewDecelerationRateFast;
  64. self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  65. }
  66. return self;
  67. }
  68. - (void)setPhoto:(id<IDMPhoto>)photo {
  69. _photoImageView.image = nil; // Release image
  70. if (_photo != photo) {
  71. _photo = photo;
  72. }
  73. [self displayImage];
  74. }
  75. - (void)prepareForReuse {
  76. self.photo = nil;
  77. [_captionView removeFromSuperview];
  78. self.captionView = nil;
  79. }
  80. #pragma mark - Image
  81. // Get and display image
  82. - (void)displayImage {
  83. if (_photo) {
  84. // Reset
  85. self.maximumZoomScale = 1;
  86. self.minimumZoomScale = 1;
  87. self.zoomScale = 1;
  88. self.contentSize = CGSizeMake(0, 0);
  89. // Get image from browser as it handles ordering of fetching
  90. UIImage *img = [self.photoBrowser imageForPhoto:_photo];
  91. if (img) {
  92. // Hide ProgressView
  93. //_progressView.alpha = 0.0f;
  94. [_progressView removeFromSuperview];
  95. // Set image
  96. _photoImageView.image = img;
  97. _photoImageView.hidden = NO;
  98. // Setup photo frame
  99. CGRect photoImageViewFrame;
  100. photoImageViewFrame.origin = CGPointZero;
  101. photoImageViewFrame.size = img.size;
  102. _photoImageView.frame = photoImageViewFrame;
  103. self.contentSize = photoImageViewFrame.size;
  104. // Set zoom to minimum zoom
  105. [self setMaxMinZoomScalesForCurrentBounds];
  106. } else {
  107. // Hide image view
  108. _photoImageView.hidden = YES;
  109. _progressView.alpha = 1.0f;
  110. }
  111. [self setNeedsLayout];
  112. }
  113. }
  114. - (void)setProgress:(CGFloat)progress forPhoto:(IDMPhoto*)photo {
  115. IDMPhoto *p = (IDMPhoto*)self.photo;
  116. if ([photo.photoURL.absoluteString isEqualToString:p.photoURL.absoluteString]) {
  117. if (_progressView.progress < progress) {
  118. [_progressView setProgress:progress animated:YES];
  119. }
  120. }
  121. }
  122. // Image failed so just show black!
  123. - (void)displayImageFailure {
  124. [_progressView removeFromSuperview];
  125. }
  126. #pragma mark - Setup
  127. - (void)setMaxMinZoomScalesForCurrentBounds {
  128. // Reset
  129. self.maximumZoomScale = 1;
  130. self.minimumZoomScale = 1;
  131. self.zoomScale = 1;
  132. // Bail
  133. if (_photoImageView.image == nil) return;
  134. // Sizes
  135. CGSize boundsSize = self.bounds.size;
  136. boundsSize.width -= 0.1;
  137. boundsSize.height -= 0.1;
  138. CGSize imageSize = _photoImageView.frame.size;
  139. // Calculate Min
  140. CGFloat xScale = boundsSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
  141. CGFloat yScale = boundsSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
  142. CGFloat minScale = MIN(xScale, yScale); // use minimum of these to allow the image to become fully visible
  143. // If image is smaller than the screen then ensure we show it at
  144. // min scale of 1
  145. if (xScale > 1 && yScale > 1) {
  146. //minScale = 1.0;
  147. }
  148. // Calculate Max
  149. CGFloat maxScale = 4.0; // Allow double scale
  150. // on high resolution screens we have double the pixel density, so we will be seeing every pixel if we limit the
  151. // maximum zoom scale to 0.5.
  152. if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
  153. maxScale = maxScale / [[UIScreen mainScreen] scale];
  154. if (maxScale < minScale) {
  155. maxScale = minScale * 2;
  156. }
  157. }
  158. // Calculate Max Scale Of Double Tap
  159. CGFloat maxDoubleTapZoomScale = 4.0 * minScale; // Allow double scale
  160. // on high resolution screens we have double the pixel density, so we will be seeing every pixel if we limit the
  161. // maximum zoom scale to 0.5.
  162. if ([UIScreen instancesRespondToSelector:@selector(scale)]) {
  163. maxDoubleTapZoomScale = maxDoubleTapZoomScale / [[UIScreen mainScreen] scale];
  164. if (maxDoubleTapZoomScale < minScale) {
  165. maxDoubleTapZoomScale = minScale * 2;
  166. }
  167. }
  168. // Set
  169. self.maximumZoomScale = maxScale;
  170. self.minimumZoomScale = minScale;
  171. self.zoomScale = minScale;
  172. self.maximumDoubleTapZoomScale = maxDoubleTapZoomScale;
  173. // Reset position
  174. _photoImageView.frame = CGRectMake(0, 0, _photoImageView.frame.size.width, _photoImageView.frame.size.height);
  175. [self setNeedsLayout];
  176. }
  177. #pragma mark - Layout
  178. - (void)layoutSubviews {
  179. // Update tap view frame
  180. _tapView.frame = self.bounds;
  181. // Super
  182. [super layoutSubviews];
  183. // Center the image as it becomes smaller than the size of the screen
  184. CGSize boundsSize = self.bounds.size;
  185. CGRect frameToCenter = _photoImageView.frame;
  186. // Horizontally
  187. if (frameToCenter.size.width < boundsSize.width) {
  188. frameToCenter.origin.x = floorf((boundsSize.width - frameToCenter.size.width) / 2.0);
  189. } else {
  190. frameToCenter.origin.x = 0;
  191. }
  192. // Vertically
  193. if (frameToCenter.size.height < boundsSize.height) {
  194. frameToCenter.origin.y = floorf((boundsSize.height - frameToCenter.size.height) / 2.0);
  195. } else {
  196. frameToCenter.origin.y = 0;
  197. }
  198. // Center
  199. if (!CGRectEqualToRect(_photoImageView.frame, frameToCenter))
  200. _photoImageView.frame = frameToCenter;
  201. }
  202. #pragma mark - UIScrollViewDelegate
  203. - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
  204. return _photoImageView;
  205. }
  206. - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
  207. [_photoBrowser cancelControlHiding];
  208. }
  209. - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view {
  210. [_photoBrowser cancelControlHiding];
  211. }
  212. - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
  213. [_photoBrowser hideControlsAfterDelay];
  214. }
  215. - (void)scrollViewDidZoom:(UIScrollView *)scrollView {
  216. [self setNeedsLayout];
  217. [self layoutIfNeeded];
  218. }
  219. #pragma mark - Tap Detection
  220. - (void)handleSingleTap:(CGPoint)touchPoint {
  221. // [_photoBrowser performSelector:@selector(toggleControls) withObject:nil afterDelay:0.2];
  222. [_photoBrowser performSelector:@selector(handleSingleTap) withObject:nil afterDelay:0.2];
  223. }
  224. - (void)handleDoubleTap:(CGPoint)touchPoint {
  225. // Cancel any single tap handling
  226. [NSObject cancelPreviousPerformRequestsWithTarget:_photoBrowser];
  227. // Zoom
  228. if (self.zoomScale == self.maximumDoubleTapZoomScale) {
  229. // Zoom out
  230. [self setZoomScale:self.minimumZoomScale animated:YES];
  231. } else {
  232. // Zoom in
  233. CGSize targetSize = CGSizeMake(self.frame.size.width / self.maximumDoubleTapZoomScale, self.frame.size.height / self.maximumDoubleTapZoomScale);
  234. CGPoint targetPoint = CGPointMake(touchPoint.x - targetSize.width / 2, touchPoint.y - targetSize.height / 2);
  235. [self zoomToRect:CGRectMake(targetPoint.x, targetPoint.y, targetSize.width, targetSize.height) animated:YES];
  236. }
  237. // Delay controls
  238. [_photoBrowser hideControlsAfterDelay];
  239. }
  240. // Image View
  241. - (void)imageView:(UIImageView *)imageView singleTapDetected:(UITouch *)touch {
  242. [self handleSingleTap:[touch locationInView:imageView]];
  243. }
  244. - (void)imageView:(UIImageView *)imageView doubleTapDetected:(UITouch *)touch {
  245. [self handleDoubleTap:[touch locationInView:imageView]];
  246. }
  247. // Background View
  248. - (void)view:(UIView *)view singleTapDetected:(UITouch *)touch {
  249. [self handleSingleTap:[touch locationInView:view]];
  250. }
  251. - (void)view:(UIView *)view doubleTapDetected:(UITouch *)touch {
  252. [self handleDoubleTap:[touch locationInView:view]];
  253. }
  254. @end