PageRenderTime 26ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/SPHChatBubble/MHFacebookImageViewer.m

https://gitlab.com/lisit1003/SPHChatBubble
Objective C | 434 lines | 334 code | 70 blank | 30 comment | 30 complexity | cbb7d50f5f2c39bdc4184c6564597fd9 MD5 | raw file
  1. //
  2. // MHFacebookImageViewer.m
  3. //
  4. // Copyright (c) 2013 Michael Henry Pantaleon (http://www.iamkel.net). All rights reserved.
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. #import "MHFacebookImageViewer.h"
  24. static const CGFloat kMinBlackMaskAlpha = 0.3f;
  25. static const CGFloat kMaxImageScale = 2.5f;
  26. static const CGFloat kMinImageScale = 1.0f;
  27. @interface MHFacebookImageViewer()<UIGestureRecognizerDelegate,UIScrollViewDelegate>{
  28. NSMutableArray *_gestures;
  29. UIView *_blackMask;
  30. UIImageView * _imageView;
  31. UIScrollView * _scrollView;
  32. UIButton * _doneButton;
  33. UIView * _superView;
  34. CGPoint _panOrigin;
  35. CGRect _originalFrameRelativeToScreen;
  36. BOOL _isAnimating;
  37. BOOL _isDoneAnimating;
  38. }
  39. - (void) addPanGestureToView:(UIView*)view;
  40. @end
  41. @implementation MHFacebookImageViewer
  42. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  43. {
  44. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  45. if (self) {
  46. // Custom initialization
  47. }
  48. return self;
  49. }
  50. - (void)loadView
  51. {
  52. [super loadView];
  53. [UIApplication sharedApplication].statusBarHidden = YES;
  54. UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
  55. CGRect windowBounds = rootViewController.view.bounds;
  56. self.view = [[UIView alloc] initWithFrame:windowBounds];
  57. self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  58. _blackMask = [[UIView alloc] initWithFrame:windowBounds];
  59. _blackMask.backgroundColor = [UIColor blackColor];
  60. _blackMask.alpha = 0.0f;
  61. _blackMask.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  62. [self.view insertSubview:_blackMask atIndex:0];
  63. _scrollView = [[UIScrollView alloc]initWithFrame:windowBounds];
  64. _scrollView.delegate = self;
  65. [self.view addSubview:_scrollView];
  66. _doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
  67. [_doneButton addTarget:self
  68. action:@selector(close:)
  69. forControlEvents:UIControlEventTouchUpInside];
  70. [_doneButton setImage:[UIImage imageNamed:@"Done"] forState:UIControlStateNormal];
  71. _doneButton.frame = CGRectMake(windowBounds.size.width - (51.0f + 9.0f),15.0f, 51.0f, 26.0f);
  72. _superView = self.senderView.superview;
  73. // Compute Original Frame Relative To Screen
  74. CGRect newFrame = [self.senderView convertRect:[[UIScreen mainScreen] applicationFrame] toView:nil];
  75. newFrame.origin = CGPointMake(newFrame.origin.x, newFrame.origin.y);
  76. newFrame.size = self.senderView.frame.size;
  77. _originalFrameRelativeToScreen = newFrame;
  78. }
  79. - (void)viewDidLoad
  80. {
  81. [super viewDidLoad];
  82. _isAnimating = YES;
  83. dispatch_async(dispatch_get_main_queue(), ^{
  84. [self.senderView removeFromSuperview];
  85. _imageView = [[UIImageView alloc]initWithFrame:_originalFrameRelativeToScreen];
  86. _imageView.contentMode = UIViewContentModeScaleAspectFill;
  87. [_scrollView addSubview:_imageView];
  88. [_imageView setImage:self.senderView.image];
  89. UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
  90. // Start animation on view did load
  91. [UIView animateWithDuration:0.4f delay:0.0f options:0 animations:^{
  92. _imageView.frame = [self centerFrameFromImage:_imageView.image];
  93. CGAffineTransform transf = CGAffineTransformIdentity;
  94. // Root View Controller - move backward
  95. rootViewController.view.transform = CGAffineTransformScale(transf, 0.95f, 0.95f);
  96. // Root View Controller - move forward
  97. self.view.transform = CGAffineTransformScale(transf, 1.05f, 1.05f);
  98. _blackMask.alpha = 1;
  99. } completion:^(BOOL finished) {
  100. if (finished) {
  101. _imageView.userInteractionEnabled = YES;
  102. [self addPanGestureToView:_imageView];
  103. [self addMultipleGesture];
  104. _isAnimating = NO;
  105. }
  106. }];
  107. });
  108. }
  109. #pragma mark - Compute the new size of image relative to width(window)
  110. - (CGRect) centerFrameFromImage:(UIImage*) image {
  111. if(!image) return CGRectZero;
  112. CGRect windowBounds = self.view.bounds;
  113. CGSize newImageSize = [self imageResizeBaseOnWidth:windowBounds
  114. .size.width oldWidth:image
  115. .size.width oldHeight:image.size.height];
  116. return CGRectMake(0.0f, windowBounds.size.height/2 - newImageSize.height/2, newImageSize.width, newImageSize.height);
  117. }
  118. - (CGSize)imageResizeBaseOnWidth:(CGFloat) newWidth oldWidth:(CGFloat) oldWidth oldHeight:(CGFloat)oldHeight {
  119. CGFloat scaleFactor = newWidth / oldWidth;
  120. CGFloat newHeight = oldHeight * scaleFactor;
  121. return CGSizeMake(newWidth, newHeight);
  122. }
  123. - (void)didReceiveMemoryWarning
  124. {
  125. [super didReceiveMemoryWarning];
  126. // Dispose of any resources that can be recreated.
  127. }
  128. #pragma mark - Add Pan Gesture
  129. - (void) addPanGestureToView:(UIView*)view
  130. {
  131. UIPanGestureRecognizer* panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self
  132. action:@selector(gestureRecognizerDidPan:)];
  133. panGesture.cancelsTouchesInView = YES;
  134. panGesture.delegate = self;
  135. [view addGestureRecognizer:panGesture];
  136. [_gestures addObject:panGesture];
  137. panGesture = nil;
  138. }
  139. # pragma mark - Avoid Unwanted Horizontal Gesture
  140. - (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)panGestureRecognizer {
  141. CGPoint translation = [panGestureRecognizer translationInView:self.view];
  142. return fabs(translation.y) > fabs(translation.x) ;
  143. }
  144. #pragma mark - Gesture recognizer
  145. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
  146. _panOrigin = _imageView.frame.origin;
  147. gestureRecognizer.enabled = YES;
  148. return !_isAnimating;
  149. }
  150. - (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
  151. return YES;
  152. }
  153. #pragma mark - Handle Panning Activity
  154. - (void) gestureRecognizerDidPan:(UIPanGestureRecognizer*)panGesture {
  155. if(_scrollView.zoomScale != 1.0f || _isAnimating)return;
  156. // Hide the Done Button
  157. [self hideDoneButton];
  158. _scrollView.bounces = NO;
  159. CGSize windowSize = _blackMask.bounds.size;
  160. CGPoint currentPoint = [panGesture translationInView:self.view];
  161. CGFloat y = currentPoint.y + _panOrigin.y;
  162. CGRect frame = _imageView.frame;
  163. frame.origin = CGPointMake(0, y);
  164. _imageView.frame = frame;
  165. CGFloat yDiff = abs((y + _imageView.frame.size.height/2) - windowSize.height/2);
  166. _blackMask.alpha = MAX(1 - yDiff/(windowSize.height/2),kMinBlackMaskAlpha);
  167. if ((panGesture.state == UIGestureRecognizerStateEnded || panGesture.state == UIGestureRecognizerStateCancelled) && _scrollView.zoomScale == 1.0f) {
  168. if(_blackMask.alpha < 0.7) {
  169. [self dismissViewController];
  170. }else {
  171. [self rollbackViewController];
  172. }
  173. }
  174. }
  175. #pragma mark - Show
  176. - (void)presentFromRootViewController
  177. {
  178. UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
  179. [self presentFromViewController:rootViewController];
  180. }
  181. - (void)presentFromViewController:(UIViewController *)controller
  182. {
  183. _rootViewController = controller;
  184. [controller addChildViewController:self];
  185. [controller.view addSubview:self.view];
  186. [self didMoveToParentViewController:controller];
  187. }
  188. #pragma mark - Just Rollback
  189. - (void)rollbackViewController
  190. {
  191. _isAnimating = YES;
  192. [UIView animateWithDuration:0.2f delay:0.0f options:0 animations:^{
  193. _imageView.frame = [self centerFrameFromImage:_imageView.image];
  194. _blackMask.alpha = 1;
  195. } completion:^(BOOL finished) {
  196. if (finished) {
  197. _isAnimating = NO;
  198. }
  199. }];
  200. }
  201. #pragma mark - Dismiss
  202. - (void)dismissViewController
  203. {
  204. _isAnimating = YES;
  205. dispatch_async(dispatch_get_main_queue(), ^{
  206. [self hideDoneButton];
  207. UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
  208. _imageView.clipsToBounds = YES;
  209. [UIView animateWithDuration:0.2f delay:0.0f options:0 animations:^{
  210. _imageView.frame = _originalFrameRelativeToScreen;
  211. CGAffineTransform transf = CGAffineTransformIdentity;
  212. rootViewController.view.transform = CGAffineTransformScale(transf, 1.0f, 1.0f);
  213. self.view.transform = CGAffineTransformScale(transf, 1.0f, 1.0f);
  214. _blackMask.alpha = 0.0f;
  215. } completion:^(BOOL finished) {
  216. if (finished) {
  217. [self.view removeFromSuperview];
  218. [self removeFromParentViewController];
  219. [_superView addSubview:self.senderView ];
  220. [UIApplication sharedApplication].statusBarHidden = NO;
  221. _isAnimating =NO;
  222. }
  223. }];
  224. });
  225. }
  226. - (void)close:(UIButton *)sender {
  227. [sender removeFromSuperview];
  228. [self dismissViewController];
  229. }
  230. # pragma mark - UIScrollView Delegate
  231. - (void)centerScrollViewContents {
  232. CGSize boundsSize = self.view.bounds.size;
  233. CGRect contentsFrame = _imageView.frame;
  234. if (contentsFrame.size.width < boundsSize.width) {
  235. contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
  236. } else {
  237. contentsFrame.origin.x = 0.0f;
  238. }
  239. if (contentsFrame.size.height < boundsSize.height) {
  240. contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
  241. } else {
  242. contentsFrame.origin.y = 0.0f;
  243. }
  244. _imageView.frame = contentsFrame;
  245. }
  246. - (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
  247. return _imageView;
  248. }
  249. - (void)scrollViewDidZoom:(UIScrollView *)scrollView {
  250. _isAnimating = YES;
  251. [self hideDoneButton];
  252. [self centerScrollViewContents];
  253. }
  254. - (void) scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale {
  255. _isAnimating = NO;
  256. }
  257. - (void)addMultipleGesture {
  258. UITapGestureRecognizer *twoFingerTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTwoFingerTap:)];
  259. twoFingerTapGesture.numberOfTapsRequired = 1;
  260. twoFingerTapGesture.numberOfTouchesRequired = 2;
  261. [_scrollView addGestureRecognizer:twoFingerTapGesture];
  262. UITapGestureRecognizer *singleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didSingleTap:)];
  263. singleTapRecognizer.numberOfTapsRequired = 1;
  264. singleTapRecognizer.numberOfTouchesRequired = 1;
  265. [_scrollView addGestureRecognizer:singleTapRecognizer];
  266. UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didDobleTap:)];
  267. doubleTapRecognizer.numberOfTapsRequired = 2;
  268. doubleTapRecognizer.numberOfTouchesRequired = 1;
  269. [_scrollView addGestureRecognizer:doubleTapRecognizer];
  270. [singleTapRecognizer requireGestureRecognizerToFail:doubleTapRecognizer];
  271. _scrollView.minimumZoomScale = kMinImageScale;
  272. _scrollView.maximumZoomScale = kMaxImageScale;
  273. _scrollView.zoomScale = 1;
  274. [self centerScrollViewContents];
  275. }
  276. #pragma mark - For Zooming
  277. - (void)didTwoFingerTap:(UITapGestureRecognizer*)recognizer {
  278. CGFloat newZoomScale = _scrollView.zoomScale / 1.5f;
  279. newZoomScale = MAX(newZoomScale, _scrollView.minimumZoomScale);
  280. [_scrollView setZoomScale:newZoomScale animated:YES];
  281. }
  282. #pragma mark - Showing of Done Button if ever Zoom Scale is equal to 1
  283. - (void)didSingleTap:(UITapGestureRecognizer*)recognizer {
  284. if(_doneButton.superview){
  285. [self hideDoneButton];
  286. }else {
  287. if(_scrollView.zoomScale == _scrollView.minimumZoomScale){
  288. if(!_isDoneAnimating){
  289. _isDoneAnimating = YES;
  290. [self.view addSubview:_doneButton];
  291. _doneButton.alpha = 0.0f;
  292. [UIView animateWithDuration:0.2f animations:^{
  293. _doneButton.alpha = 1.0f;
  294. } completion:^(BOOL finished) {
  295. [self.view bringSubviewToFront:_doneButton];
  296. _isDoneAnimating = NO;
  297. }];
  298. }
  299. }else if(_scrollView.zoomScale == _scrollView.maximumZoomScale) {
  300. CGPoint pointInView = [recognizer locationInView:_imageView];
  301. [self zoomInZoomOut:pointInView];
  302. }
  303. }
  304. }
  305. #pragma mark - Zoom in or Zoom out
  306. - (void)didDobleTap:(UITapGestureRecognizer*)recognizer {
  307. CGPoint pointInView = [recognizer locationInView:_imageView];
  308. [self zoomInZoomOut:pointInView];
  309. }
  310. - (void) zoomInZoomOut:(CGPoint)point {
  311. // Check if current Zoom Scale is greater than half of max scale then reduce zoom and vice versa
  312. CGFloat newZoomScale = _scrollView.zoomScale > (_scrollView.maximumZoomScale/2)?_scrollView.minimumZoomScale:_scrollView.maximumZoomScale;
  313. CGSize scrollViewSize = _scrollView.bounds.size;
  314. CGFloat w = scrollViewSize.width / newZoomScale;
  315. CGFloat h = scrollViewSize.height / newZoomScale;
  316. CGFloat x = point.x - (w / 2.0f);
  317. CGFloat y = point.y - (h / 2.0f);
  318. CGRect rectToZoomTo = CGRectMake(x, y, w, h);
  319. [_scrollView zoomToRect:rectToZoomTo animated:YES];
  320. }
  321. #pragma mark - Hide the Done Button
  322. - (void) hideDoneButton {
  323. if(!_isDoneAnimating){
  324. if(_doneButton.superview) {
  325. _isDoneAnimating = YES;
  326. _doneButton.alpha = 1.0f;
  327. [UIView animateWithDuration:0.2f animations:^{
  328. _doneButton.alpha = 0.0f;
  329. } completion:^(BOOL finished) {
  330. _isDoneAnimating = NO;
  331. [_doneButton removeFromSuperview];
  332. }];
  333. }
  334. }
  335. }
  336. - (void) dealloc {
  337. _gestures = nil;
  338. _blackMask = nil;
  339. _imageView = nil;
  340. _scrollView = nil;
  341. _doneButton = nil;
  342. _superView = nil;
  343. }
  344. @end
  345. #pragma mark - UIImageView Category
  346. @implementation UIImageView (MHFacebookImageViewer)
  347. #pragma mark - Initializer for UIImageView
  348. - (void) setupImageViewer {
  349. self.userInteractionEnabled = YES;
  350. UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap:)];
  351. [self addGestureRecognizer:tapGesture];
  352. tapGesture = nil;
  353. }
  354. - (void) removeImageViewer {
  355. self.userInteractionEnabled = NO;
  356. }
  357. #pragma mark - Handle Tap
  358. - (void) didTap:(UITapGestureRecognizer*)gestureRecognizer {
  359. MHFacebookImageViewer * imageBrowser = [[MHFacebookImageViewer alloc]init];
  360. imageBrowser.senderView = self;
  361. if(self.image)
  362. [imageBrowser presentFromRootViewController];
  363. }
  364. @end