DKPhotoPickerBrowserPhotoScrollView.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. //
  2. // ZLPhotoPickerBrowserPhotoScrollView.m
  3. // ZLAssetsPickerDemo
  4. //
  5. //
  6. #import "DKPhotoPickerBrowserPhotoScrollView.h"
  7. #import "DKPhotoPickerDatas.h"
  8. #import "UIImageView+WebCache.h"
  9. #import "DACircularProgressView.h"
  10. #import "DKPhotoPickerCommon.h"
  11. // Private methods and properties
  12. @interface DKPhotoPickerBrowserPhotoScrollView ()<UIActionSheetDelegate> {
  13. DKPhotoPickerBrowserPhotoView *_tapView; // for background taps
  14. DKPhotoPickerBrowserPhotoImageView *_photoImageView;
  15. }
  16. @property (assign,nonatomic) CGFloat progress;
  17. @property (strong,nonatomic) DACircularProgressView *progressView;
  18. @end
  19. @implementation DKPhotoPickerBrowserPhotoScrollView
  20. - (DACircularProgressView *)progressView{
  21. if (!_progressView) {
  22. DACircularProgressView *progressView = [[DACircularProgressView alloc] init];
  23. progressView.frame = CGRectMake(0, 0, ZLPickerProgressViewW, ZLPickerProgressViewH);
  24. progressView.center = CGPointMake([UIScreen mainScreen].bounds.size.width * 0.5, [UIScreen mainScreen].bounds.size.height * 0.5);
  25. progressView.roundedCorners = YES;
  26. if (iOS7gt) {
  27. progressView.thicknessRatio = 0.1;
  28. progressView.roundedCorners = NO;
  29. } else {
  30. progressView.thicknessRatio = 0.2;
  31. progressView.roundedCorners = YES;
  32. }
  33. [self addSubview:progressView];
  34. self.progressView = progressView;
  35. }
  36. return _progressView;
  37. }
  38. - (id)init{
  39. if ((self = [super init])) {
  40. // Setup
  41. // Tap view for background
  42. _tapView = [[DKPhotoPickerBrowserPhotoView alloc] initWithFrame:self.bounds];
  43. _tapView.tapDelegate = self;
  44. _tapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  45. _tapView.backgroundColor = [UIColor blackColor];
  46. [self addSubview:_tapView];
  47. // Image view
  48. _photoImageView = [[DKPhotoPickerBrowserPhotoImageView alloc] initWithFrame:CGRectZero];
  49. _photoImageView.tapDelegate = self;
  50. _photoImageView.contentMode = UIViewContentModeCenter;
  51. _photoImageView.backgroundColor = [UIColor blackColor];
  52. [self addSubview:_photoImageView];
  53. // Setup
  54. self.backgroundColor = [UIColor blackColor];
  55. self.delegate = self;
  56. self.showsHorizontalScrollIndicator = NO;
  57. self.showsVerticalScrollIndicator = NO;
  58. self.decelerationRate = UIScrollViewDecelerationRateFast;
  59. self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  60. UILongPressGestureRecognizer *longGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longGesture:)];
  61. [self addGestureRecognizer:longGesture];
  62. }
  63. return self;
  64. }
  65. - (void)longGesture:(UILongPressGestureRecognizer *)gesture{
  66. if (gesture.state == UIGestureRecognizerStateBegan) {
  67. if (!self.sheet) {
  68. self.sheet = [[UIActionSheet alloc] initWithTitle:@"提示" delegate:self cancelButtonTitle:@"取消" destructiveButtonTitle:@"保存到相册" otherButtonTitles:nil, nil];
  69. }
  70. [self.sheet showInView:self];
  71. }
  72. }
  73. - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex{
  74. if (buttonIndex == 0){
  75. UIImageWriteToSavedPhotosAlbum(_photoImageView.image, nil, nil, nil);
  76. }
  77. }
  78. - (void)dealloc {
  79. [[NSNotificationCenter defaultCenter] removeObserver:self];
  80. }
  81. - (void)setPhoto:(DKPhotoPickerBrowserPhoto *)photo{
  82. _photo = photo;
  83. __weak typeof(self) weakSelf = self;
  84. if (photo.photoURL.absoluteString.length) {
  85. // 本地相册
  86. NSRange photoRange = [photo.photoURL.absoluteString rangeOfString:@"assets-library"];
  87. if (photoRange.location != NSNotFound){
  88. [[DKPhotoPickerDatas defaultPicker] getAssetsPhotoWithURLs:photo.photoURL callBack:^(UIImage *obj) {
  89. _photoImageView.image = obj;
  90. [weakSelf displayImage];
  91. }];
  92. }else{
  93. UIImage *thumbImage = photo.thumbImage;
  94. if (thumbImage == nil) {
  95. thumbImage = _photoImageView.image;
  96. }else{
  97. _photoImageView.image = thumbImage;
  98. }
  99. _photoImageView.contentMode = UIViewContentModeScaleAspectFit;
  100. _photoImageView.frame = [self setMaxMinZoomScalesForCurrentBounds:_photoImageView];
  101. // 网络URL
  102. [_photoImageView sd_setImageWithURL:photo.photoURL placeholderImage:thumbImage options:SDWebImageRetryFailed progress:^(NSInteger receivedSize, NSInteger expectedSize) {
  103. _photoImageView.progress = (double)receivedSize / expectedSize;
  104. } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
  105. _photoImageView.image = image;
  106. [weakSelf displayImage];
  107. }];
  108. }
  109. } else if (photo.photoImage){
  110. _photoImageView.image = photo.photoImage;
  111. [self displayImage];
  112. }
  113. }
  114. - (CGRect )setMaxMinZoomScalesForCurrentBounds:(UIImageView *)imageView {
  115. // Sizes
  116. CGSize boundsSize = [UIScreen mainScreen].bounds.size;
  117. CGSize imageSize = imageView.image.size;
  118. if (imageSize.width == 0 && imageSize.height == 0) {
  119. return imageView.frame;
  120. }
  121. CGFloat xScale = boundsSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
  122. CGFloat yScale = boundsSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
  123. CGFloat minScale = MIN(xScale, yScale); // use minimum of these to allow the image to become fully visible
  124. // Image is smaller than screen so no zooming!
  125. if (xScale >= 1 && yScale >= 1) {
  126. minScale = MIN(xScale, yScale);
  127. }
  128. CGRect frameToCenter = CGRectMake(0, 0, imageSize.width * minScale, imageSize.height * minScale);
  129. // Horizontally
  130. if (frameToCenter.size.width < boundsSize.width) {
  131. frameToCenter.origin.x = floorf((boundsSize.width - frameToCenter.size.width) / 2.0);
  132. } else {
  133. frameToCenter.origin.x = 0;
  134. }
  135. // Vertically
  136. if (frameToCenter.size.height < boundsSize.height) {
  137. frameToCenter.origin.y = floorf((boundsSize.height - frameToCenter.size.height) / 2.0);
  138. } else {
  139. frameToCenter.origin.y = 0;
  140. }
  141. return frameToCenter;
  142. }
  143. #pragma mark - setProgress
  144. - (void)setProgress:(CGFloat)progress{
  145. _progress = progress;
  146. if (progress == 0) return ;
  147. if (progress / 1.0 != 1.0) {
  148. [self.progressView setProgress:progress animated:YES];
  149. }else{
  150. [self.progressView removeFromSuperview];
  151. self.progressView = nil;
  152. }
  153. }
  154. #pragma mark - Image
  155. // Get and display image
  156. - (void)displayImage {
  157. // Reset
  158. self.maximumZoomScale = 1;
  159. self.minimumZoomScale = 1;
  160. self.zoomScale = 1;
  161. self.contentSize = CGSizeMake(0, 0);
  162. // Get image from browser as it handles ordering of fetching
  163. UIImage *img = _photoImageView.image;
  164. if (img) {
  165. // Set image
  166. _photoImageView.image = img;
  167. _photoImageView.hidden = NO;
  168. // Setup photo frame
  169. CGRect photoImageViewFrame;
  170. photoImageViewFrame.origin = CGPointZero;
  171. photoImageViewFrame.size = img.size;
  172. _photoImageView.frame = photoImageViewFrame;
  173. self.contentSize = photoImageViewFrame.size;
  174. // Set zoom to minimum zoom
  175. [self setMaxMinZoomScalesForCurrentBounds];
  176. }
  177. [self setNeedsLayout];
  178. }
  179. #pragma mark - Loading Progress
  180. #pragma mark - Setup
  181. - (CGFloat)initialZoomScaleWithMinScale {
  182. CGFloat zoomScale = self.minimumZoomScale;
  183. if (_photoImageView) {
  184. // Zoom image to fill if the aspect ratios are fairly similar
  185. CGSize boundsSize = self.bounds.size;
  186. CGSize imageSize = _photoImageView.image.size;
  187. CGFloat boundsAR = boundsSize.width / boundsSize.height;
  188. CGFloat imageAR = imageSize.width / imageSize.height;
  189. CGFloat xScale = boundsSize.width / imageSize.width;
  190. if (ABS(boundsAR - imageAR) < 0.17) {
  191. zoomScale = xScale;
  192. }
  193. }
  194. return zoomScale;
  195. }
  196. - (void)setMaxMinZoomScalesForCurrentBounds {
  197. // Reset
  198. self.maximumZoomScale = 1;
  199. self.minimumZoomScale = 1;
  200. self.zoomScale = 1;
  201. // Bail if no image
  202. if (_photoImageView.image == nil) return;
  203. // Reset position
  204. _photoImageView.frame = CGRectMake(0, 0, _photoImageView.frame.size.width, _photoImageView.frame.size.height);
  205. // Sizes
  206. CGSize boundsSize = self.bounds.size;
  207. CGSize imageSize = _photoImageView.image.size;
  208. // Calculate Min
  209. CGFloat xScale = boundsSize.width / imageSize.width; // the scale needed to perfectly fit the image width-wise
  210. CGFloat yScale = boundsSize.height / imageSize.height; // the scale needed to perfectly fit the image height-wise
  211. CGFloat minScale = MIN(xScale, yScale); // use minimum of these to allow the image to become fully visible
  212. // Calculate Max
  213. CGFloat maxScale = 3;
  214. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
  215. // Let them go a bit bigger on a bigger screen!
  216. maxScale = 4;
  217. }
  218. // Image is smaller than screen so no zooming!
  219. if (xScale >= 1 && yScale >= 1) {
  220. minScale = MIN(xScale, yScale);
  221. }
  222. // Set min/max zoom
  223. self.maximumZoomScale = maxScale;
  224. self.minimumZoomScale = minScale;
  225. // Initial zoom
  226. self.zoomScale = [self initialZoomScaleWithMinScale];
  227. // If we're zooming to fill then centralise
  228. if (self.zoomScale != minScale) {
  229. // Centralise
  230. self.contentOffset = CGPointMake((imageSize.width * self.zoomScale - boundsSize.width) / 2.0,
  231. (imageSize.height * self.zoomScale - boundsSize.height) / 2.0);
  232. // Disable scrolling initially until the first pinch to fix issues with swiping on an initally zoomed in photo
  233. self.scrollEnabled = NO;
  234. }
  235. // Layout
  236. [self setNeedsLayout];
  237. }
  238. #pragma mark - Layout
  239. - (void)layoutSubviews {
  240. // Super
  241. [super layoutSubviews];
  242. // Center the image as it becomes smaller than the size of the screen
  243. CGSize boundsSize = self.bounds.size;
  244. CGRect frameToCenter = _photoImageView.frame;
  245. // Horizontally
  246. if (frameToCenter.size.width < boundsSize.width) {
  247. frameToCenter.origin.x = floorf((boundsSize.width - frameToCenter.size.width) / 2.0);
  248. } else {
  249. frameToCenter.origin.x = 0;
  250. }
  251. // Vertically
  252. if (frameToCenter.size.height < boundsSize.height) {
  253. frameToCenter.origin.y = floorf((boundsSize.height - frameToCenter.size.height) / 2.0);
  254. } else {
  255. frameToCenter.origin.y = 0;
  256. }
  257. // Center
  258. if (!CGRectEqualToRect(_photoImageView.frame, frameToCenter))
  259. _photoImageView.frame = frameToCenter;
  260. }
  261. #pragma mark - UIScrollViewDelegate
  262. - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
  263. return _photoImageView;
  264. }
  265. - (void)scrollViewDidZoom:(UIScrollView *)scrollView {
  266. [self setNeedsLayout];
  267. [self layoutIfNeeded];
  268. }
  269. #pragma mark - Tap Detection
  270. - (void)handleDoubleTap:(CGPoint)touchPoint {
  271. // Zoom
  272. if (self.zoomScale != self.minimumZoomScale && self.zoomScale != [self initialZoomScaleWithMinScale]) {
  273. // Zoom out
  274. [self setZoomScale:self.minimumZoomScale animated:YES];
  275. self.contentSize = CGSizeMake(self.frame.size.width, 0);
  276. } else {
  277. // Zoom in to twice the size
  278. CGFloat newZoomScale = ((self.maximumZoomScale + self.minimumZoomScale) / 2);
  279. CGFloat xsize = self.bounds.size.width / newZoomScale;
  280. CGFloat ysize = self.bounds.size.height / newZoomScale;
  281. [self zoomToRect:CGRectMake(touchPoint.x - xsize/2, touchPoint.y - ysize/2, xsize, ysize) animated:YES];
  282. }
  283. }
  284. - (void)imageView:(UIImageView *)imageView singleTapDetected:(UITouch *)touch{
  285. [self disMissTap:nil];
  286. }
  287. #pragma mark - disMissTap
  288. - (void) disMissTap:(UITapGestureRecognizer *)tap{
  289. if (self.callback){
  290. self.callback(nil);
  291. }else if ([self.photoScrollViewDelegate respondsToSelector:@selector(pickerPhotoScrollViewDidSingleClick:)]) {
  292. [self.photoScrollViewDelegate pickerPhotoScrollViewDidSingleClick:self];
  293. }
  294. }
  295. // Image View
  296. - (void)imageView:(UIImageView *)imageView doubleTapDetected:(UITouch *)touch {
  297. [self handleDoubleTap:[touch locationInView:imageView]];
  298. }
  299. - (void)view:(UIView *)view singleTapDetected:(UITouch *)touch{
  300. [self disMissTap:nil];
  301. }
  302. - (void)view:(UIView *)view doubleTapDetected:(UITouch *)touch {
  303. // Translate touch location to image view location
  304. CGFloat touchX = [touch locationInView:view].x;
  305. CGFloat touchY = [touch locationInView:view].y;
  306. touchX *= 1/self.zoomScale;
  307. touchY *= 1/self.zoomScale;
  308. touchX += self.contentOffset.x;
  309. touchY += self.contentOffset.y;
  310. [self handleDoubleTap:CGPointMake(touchX, touchY)];
  311. }
  312. @end