MWGridViewController.m 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // This file is based on third party code, see below for the original author
  2. // and original license.
  3. // Modifications are (c) by Threema GmbH and licensed under the AGPLv3.
  4. //
  5. // MWGridViewController.m
  6. // MWPhotoBrowser
  7. //
  8. // Created by Michael Waterfall on 08/10/2013.
  9. //
  10. //
  11. #import "MWGridViewController.h"
  12. #import "MWGridCell.h"
  13. #import "MWPhotoBrowserPrivate.h"
  14. #import "MWCommon.h"
  15. @interface MWGridViewController () {
  16. // Store margins for current setup
  17. CGFloat _margin, _gutter, _marginL, _gutterL, _columns, _columnsL;
  18. }
  19. @end
  20. @implementation MWGridViewController
  21. - (id)init {
  22. if ((self = [super initWithCollectionViewLayout:[UICollectionViewFlowLayout new]])) {
  23. // Defaults
  24. (void)(_columns = 3), _columnsL = 4;
  25. (void)(_margin = 0), _gutter = 1;
  26. (void)(_marginL = 0), _gutterL = 1;
  27. // For pixel perfection...
  28. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
  29. // iPad
  30. (void)(_columns = 6), _columnsL = 8;
  31. (void)(_margin = 1), _gutter = 2;
  32. (void)(_marginL = 1), _gutterL = 2;
  33. } else if ([UIScreen mainScreen].bounds.size.height == 480) {
  34. // iPhone 3.5 inch
  35. (void)(_columns = 3), _columnsL = 4;
  36. (void)(_margin = 0), _gutter = 1;
  37. (void)(_marginL = 1), _gutterL = 2;
  38. } else {
  39. // iPhone 4 inch
  40. (void)(_columns = 3), _columnsL = 5;
  41. (void)(_margin = 0), _gutter = 1;
  42. (void)(_marginL = 0), _gutterL = 2;
  43. }
  44. ///***** BEGIN THREEMA MODIFICATION: iOS 11 *********/
  45. // _initialContentOffset = CGPointMake(0, CGFLOAT_MAX);
  46. if (@available(iOS 11.0, *)) {
  47. } else {
  48. _initialContentOffset = CGPointMake(0, CGFLOAT_MAX);
  49. }
  50. ///***** END THREEMA MODIFICATION: iOS 11 *********/
  51. }
  52. return self;
  53. }
  54. #pragma mark - View
  55. - (void)viewDidLoad {
  56. [super viewDidLoad];
  57. [self.collectionView registerClass:[MWGridCell class] forCellWithReuseIdentifier:@"GridCell"];
  58. self.collectionView.alwaysBounceVertical = YES;
  59. self.collectionView.backgroundColor = [UIColor blackColor];
  60. }
  61. - (void)viewWillAppear:(BOOL)animated {
  62. [super viewWillAppear:animated];
  63. UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.view);
  64. }
  65. - (void)viewWillDisappear:(BOOL)animated {
  66. // Cancel outstanding loading
  67. NSArray *visibleCells = [self.collectionView visibleCells];
  68. if (visibleCells) {
  69. for (MWGridCell *cell in visibleCells) {
  70. [cell.photo cancelAnyLoading];
  71. }
  72. }
  73. [super viewWillDisappear:animated];
  74. }
  75. - (void)viewWillLayoutSubviews {
  76. [super viewWillLayoutSubviews];
  77. [self performLayout];
  78. }
  79. - (void)viewDidLayoutSubviews {
  80. [super viewDidLayoutSubviews];
  81. }
  82. - (void)adjustOffsetsAsRequired {
  83. ///***** BEGIN THREEMA MODIFICATION: iOS 11 *********/
  84. // Move to previous content offset
  85. // if (_initialContentOffset.y != CGFLOAT_MAX) {
  86. // self.collectionView.contentOffset = _initialContentOffset;
  87. // [self.collectionView layoutIfNeeded]; // Layout after content offset change
  88. // }
  89. if (@available(iOS 11.0, *)) {
  90. } else {
  91. if (_initialContentOffset.y != CGFLOAT_MAX) {
  92. self.collectionView.contentOffset = _initialContentOffset;
  93. [self.collectionView layoutIfNeeded]; // Layout after content offset change
  94. }
  95. }
  96. ///***** END THREEMA MODIFICATION: iOS 11 *********/
  97. // Check if current item is visible and if not, make it so!
  98. if (_browser.numberOfPhotos > 0) {
  99. NSIndexPath *currentPhotoIndexPath = [NSIndexPath indexPathForItem:_browser.currentIndex inSection:0];
  100. NSArray *visibleIndexPaths = [self.collectionView indexPathsForVisibleItems];
  101. BOOL currentVisible = NO;
  102. for (NSIndexPath *indexPath in visibleIndexPaths) {
  103. if ([indexPath isEqual:currentPhotoIndexPath]) {
  104. currentVisible = YES;
  105. break;
  106. }
  107. }
  108. if (!currentVisible) {
  109. [self.collectionView scrollToItemAtIndexPath:currentPhotoIndexPath atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
  110. }
  111. }
  112. }
  113. - (void)performLayout {
  114. ///***** BEGIN THREEMA MODIFICATION: iOS 11 *********/
  115. UINavigationBar *navBar = self.navigationController.navigationBar;
  116. // self.collectionView.contentInset = UIEdgeInsetsMake(navBar.frame.origin.y + navBar.frame.size.height + [self getGutter], 0, 0, 0);
  117. CGFloat navBarBottom = 0;
  118. if (@available(iOS 11.0, *)) {
  119. } else {
  120. navBarBottom = navBar.frame.origin.y + navBar.frame.size.height;
  121. }
  122. if (_browser.displaySelectionButtons == true) {
  123. self.collectionView.contentInset = UIEdgeInsetsMake(navBarBottom + [self getGutter], 0, _browser.gridToolbar.frame.size.height, 0);
  124. } else {
  125. self.collectionView.contentInset = UIEdgeInsetsMake(navBarBottom + [self getGutter], 0, 0, 0);
  126. }
  127. ///***** END THREEMA MODIFICATION: iOS 11 *********/
  128. }
  129. - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
  130. [self.collectionView reloadData];
  131. [self performLayout]; // needed for iOS 5 & 6
  132. }
  133. #pragma mark - Layout
  134. - (CGFloat)getColumns {
  135. if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) {
  136. // if ((UIInterfaceOrientationIsPortrait(self.interfaceOrientation))) {
  137. return _columns;
  138. } else {
  139. return _columnsL;
  140. }
  141. }
  142. - (CGFloat)getMargin {
  143. if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) {
  144. // if ((UIInterfaceOrientationIsPortrait(self.interfaceOrientation))) {
  145. return _margin;
  146. } else {
  147. return _marginL;
  148. }
  149. }
  150. - (CGFloat)getGutter {
  151. if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) {
  152. // if ((UIInterfaceOrientationIsPortrait(self.interfaceOrientation))) {
  153. return _gutter;
  154. } else {
  155. return _gutterL;
  156. }
  157. }
  158. #pragma mark - Collection View
  159. - (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {
  160. return [_browser numberOfPhotos];
  161. }
  162. - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
  163. MWGridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"GridCell" forIndexPath:indexPath];
  164. if (!cell) {
  165. cell = [[MWGridCell alloc] init];
  166. }
  167. id <MWPhoto> photo = [_browser thumbPhotoAtIndex:indexPath.row];
  168. cell.photo = photo;
  169. cell.gridController = self;
  170. cell.selectionMode = _selectionMode;
  171. cell.isSelected = [_browser photoIsSelectedAtIndex:indexPath.row];
  172. cell.index = indexPath.row;
  173. UIImage *img = [_browser imageForPhoto:photo];
  174. if (img) {
  175. [cell displayImage];
  176. } else {
  177. [photo loadUnderlyingImageAndNotify];
  178. }
  179. return cell;
  180. }
  181. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
  182. ///***** BEGIN THREEMA MODIFICATION: disable if selection mode is on *********/
  183. if (!_browser.displaySelectionButtons) {
  184. [_browser setCurrentPhotoIndex:indexPath.row];
  185. [_browser hideGrid];
  186. } else {
  187. MWGridCell *cell = (MWGridCell *)[collectionView cellForItemAtIndexPath:indexPath];
  188. [cell selectionButtonPressed];
  189. }
  190. ///***** END THREEMA MODIFICATION: disable if selection mode is on *********/
  191. }
  192. - (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
  193. [((MWGridCell *)cell).photo cancelAnyLoading];
  194. }
  195. - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
  196. CGFloat margin = [self getMargin];
  197. CGFloat gutter = [self getGutter];
  198. CGFloat columns = [self getColumns];
  199. CGFloat value = floorf(((self.view.bounds.size.width - (columns - 1) * gutter - 2 * margin) / columns));
  200. return CGSizeMake(value, value);
  201. }
  202. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
  203. return [self getGutter];
  204. }
  205. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
  206. return [self getGutter];
  207. }
  208. - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
  209. CGFloat margin = [self getMargin];
  210. return UIEdgeInsetsMake(margin, margin, margin, margin);
  211. }
  212. @end