MWGridCell.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  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. // MWGridCell.m
  6. // MWPhotoBrowser
  7. //
  8. // Created by Michael Waterfall on 08/10/2013.
  9. //
  10. //
  11. /***** BEGIN THREEMA MODIFICATION: Use stylekit check image *********/
  12. #import "ThreemaFramework/ThreemaFramework-swift.h"
  13. /***** END THREEMA MODIFICATION: Use stylekit check image *********/
  14. #import <DACircularProgress/DACircularProgressView.h>
  15. #import "MWGridCell.h"
  16. #import "MWCommon.h"
  17. #import "MWPhotoBrowserPrivate.h"
  18. #import "UIImage+MWPhotoBrowser.h"
  19. #define VIDEO_INDICATOR_PADDING 10
  20. @interface MWGridCell () {
  21. UIImageView *_imageView;
  22. UIImageView *_videoIndicator;
  23. UIImageView *_loadingError;
  24. DACircularProgressView *_loadingIndicator;
  25. UIButton *_selectedButton;
  26. }
  27. @end
  28. @implementation MWGridCell
  29. - (id)initWithFrame:(CGRect)frame {
  30. if ((self = [super initWithFrame:frame])) {
  31. // Grey background
  32. self.backgroundColor = [UIColor colorWithWhite:0.12 alpha:1];
  33. self.isAccessibilityElement = YES;
  34. // Image
  35. _imageView = [UIImageView new];
  36. _imageView.frame = self.bounds;
  37. _imageView.contentMode = UIViewContentModeScaleAspectFill;
  38. _imageView.clipsToBounds = YES;
  39. _imageView.autoresizesSubviews = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  40. _imageView.accessibilityTraits = UIAccessibilityTraitImage;
  41. /***** BEGIN THREEMA MODIFICATION: accessibilityIgnoresInvertColors *********/
  42. if (@available(iOS 11.0, *)) {
  43. _imageView.accessibilityIgnoresInvertColors = true;
  44. }
  45. /***** END THREEMA MODIFICATION: accessibilityIgnoresInvertColors *********/
  46. [self addSubview:_imageView];
  47. // Video Image
  48. _videoIndicator = [UIImageView new];
  49. _videoIndicator.hidden = NO;
  50. UIImage *videoIndicatorImage = [UIImage imageForResourcePath:@"MWPhotoBrowser.bundle/VideoOverlay" ofType:@"png" inBundle:[NSBundle bundleForClass:[self class]]];
  51. _videoIndicator.frame = CGRectMake(self.bounds.size.width - videoIndicatorImage.size.width - VIDEO_INDICATOR_PADDING, self.bounds.size.height - videoIndicatorImage.size.height - VIDEO_INDICATOR_PADDING, videoIndicatorImage.size.width, videoIndicatorImage.size.height);
  52. _videoIndicator.image = videoIndicatorImage;
  53. _videoIndicator.autoresizesSubviews = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
  54. [self addSubview:_videoIndicator];
  55. // Selection button
  56. _selectedButton = [UIButton buttonWithType:UIButtonTypeCustom];
  57. _selectedButton.contentMode = UIViewContentModeTopRight;
  58. _selectedButton.adjustsImageWhenHighlighted = NO;
  59. /***** BEGIN THREEMA MODIFICATION: Use stylekit check image *********/
  60. [_selectedButton setImage:[[StyleKit uncheck] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateNormal];
  61. [_selectedButton setImage:[[StyleKit check] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateSelected];
  62. /***** END THREEMA MODIFICATION: Use stylekit check image *********/
  63. [_selectedButton addTarget:self action:@selector(selectionButtonPressed) forControlEvents:UIControlEventTouchDown];
  64. _selectedButton.hidden = YES;
  65. _selectedButton.frame = CGRectMake(0, 0, 26, 26);
  66. [self addSubview:_selectedButton];
  67. // Loading indicator
  68. _loadingIndicator = [[DACircularProgressView alloc] initWithFrame:CGRectMake(0, 0, 40.0f, 40.0f)];
  69. _loadingIndicator.userInteractionEnabled = NO;
  70. _loadingIndicator.thicknessRatio = 0.1;
  71. _loadingIndicator.roundedCorners = NO;
  72. [self addSubview:_loadingIndicator];
  73. // Listen for photo loading notifications
  74. [[NSNotificationCenter defaultCenter] addObserver:self
  75. selector:@selector(setProgressFromNotification:)
  76. name:MWPHOTO_PROGRESS_NOTIFICATION
  77. object:nil];
  78. [[NSNotificationCenter defaultCenter] addObserver:self
  79. selector:@selector(handleMWPhotoLoadingDidEndNotification:)
  80. name:MWPHOTO_LOADING_DID_END_NOTIFICATION
  81. object:nil];
  82. }
  83. return self;
  84. }
  85. - (void)dealloc {
  86. [[NSNotificationCenter defaultCenter] removeObserver:self];
  87. }
  88. - (void)setGridController:(MWGridViewController *)gridController {
  89. _gridController = gridController;
  90. // Set custom selection image if required
  91. if (_gridController.browser.customImageSelectedSmallIconName) {
  92. [_selectedButton setImage:[UIImage imageNamed:_gridController.browser.customImageSelectedSmallIconName] forState:UIControlStateSelected];
  93. }
  94. }
  95. #pragma mark - View
  96. - (void)layoutSubviews {
  97. [super layoutSubviews];
  98. _imageView.frame = self.bounds;
  99. if ([_photo respondsToSelector:@selector(isUtiPreview)]) {
  100. BOOL isUtiThumb = [_photo performSelector:@selector(isUtiPreview)];
  101. if (isUtiThumb == true) {
  102. _imageView.frame = CGRectMake((self.bounds.size.width/2) - (36.0/2), (self.bounds.size.height/2) - (36.0/2), 36.0, 36.0);
  103. _imageView.contentMode = UIViewContentModeScaleAspectFit;
  104. } else {
  105. _imageView.frame = self.bounds;
  106. _imageView.contentMode = UIViewContentModeScaleAspectFill;
  107. }
  108. }
  109. _loadingIndicator.frame = CGRectMake(floorf((self.bounds.size.width - _loadingIndicator.frame.size.width) / 2.),
  110. floorf((self.bounds.size.height - _loadingIndicator.frame.size.height) / 2),
  111. _loadingIndicator.frame.size.width,
  112. _loadingIndicator.frame.size.height);
  113. _selectedButton.frame = CGRectMake(self.bounds.size.width - (_selectedButton.frame.size.width * 1.4),
  114. self.bounds.size.height - (_selectedButton.frame.size.height * 1.4), _selectedButton.frame.size.width, _selectedButton.frame.size.height);
  115. }
  116. #pragma mark - Cell
  117. - (void)prepareForReuse {
  118. _photo = nil;
  119. _gridController = nil;
  120. _imageView.image = nil;
  121. _loadingIndicator.progress = 0;
  122. _selectedButton.hidden = YES;
  123. [self hideImageFailure];
  124. [super prepareForReuse];
  125. }
  126. #pragma mark - Image Handling
  127. - (void)setPhoto:(id <MWPhoto>)photo {
  128. _photo = photo;
  129. if ([photo respondsToSelector:@selector(isVideo)]) {
  130. _videoIndicator.hidden = !photo.isVideo;
  131. } else {
  132. _videoIndicator.hidden = YES;
  133. }
  134. if (_photo) {
  135. if (![_photo underlyingImage]) {
  136. [self showLoadingIndicator];
  137. } else {
  138. [self hideLoadingIndicator];
  139. }
  140. } else {
  141. [self showImageFailure];
  142. }
  143. }
  144. - (void)displayImage {
  145. _imageView.image = [_photo underlyingImage];
  146. _selectedButton.hidden = !_selectionMode;
  147. [self setNeedsLayout];
  148. [self hideImageFailure];
  149. }
  150. #pragma mark - Selection
  151. - (void)setSelectionMode:(BOOL)selectionMode {
  152. _selectionMode = selectionMode;
  153. }
  154. - (void)setIsSelected:(BOOL)isSelected {
  155. _isSelected = isSelected;
  156. _selectedButton.selected = isSelected;
  157. }
  158. - (void)selectionButtonPressed {
  159. _selectedButton.selected = !_selectedButton.selected;
  160. [_gridController.browser setPhotoSelected:_selectedButton.selected atIndex:_index];
  161. }
  162. #pragma mark - Touches
  163. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  164. _imageView.alpha = 0.6;
  165. [super touchesBegan:touches withEvent:event];
  166. }
  167. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  168. _imageView.alpha = 1;
  169. [super touchesEnded:touches withEvent:event];
  170. }
  171. - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
  172. _imageView.alpha = 1;
  173. [super touchesCancelled:touches withEvent:event];
  174. }
  175. #pragma mark Indicators
  176. - (void)hideLoadingIndicator {
  177. _loadingIndicator.hidden = YES;
  178. }
  179. - (void)showLoadingIndicator {
  180. _loadingIndicator.progress = 0;
  181. _loadingIndicator.hidden = NO;
  182. [self hideImageFailure];
  183. }
  184. - (void)showImageFailure {
  185. // Only show if image is not empty
  186. if (![_photo respondsToSelector:@selector(emptyImage)] || !_photo.emptyImage) {
  187. if (!_loadingError) {
  188. _loadingError = [UIImageView new];
  189. _loadingError.image = [UIImage imageForResourcePath:@"MWPhotoBrowser.bundle/ImageError" ofType:@"png" inBundle:[NSBundle bundleForClass:[self class]]];
  190. _loadingError.userInteractionEnabled = NO;
  191. [_loadingError sizeToFit];
  192. [self addSubview:_loadingError];
  193. }
  194. _loadingError.frame = CGRectMake(floorf((self.bounds.size.width - _loadingError.frame.size.width) / 2.),
  195. floorf((self.bounds.size.height - _loadingError.frame.size.height) / 2),
  196. _loadingError.frame.size.width,
  197. _loadingError.frame.size.height);
  198. }
  199. [self hideLoadingIndicator];
  200. _imageView.image = nil;
  201. }
  202. - (void)hideImageFailure {
  203. if (_loadingError) {
  204. [_loadingError removeFromSuperview];
  205. _loadingError = nil;
  206. }
  207. }
  208. #pragma mark - Notifications
  209. - (void)setProgressFromNotification:(NSNotification *)notification {
  210. dispatch_async(dispatch_get_main_queue(), ^{
  211. NSDictionary *dict = [notification object];
  212. id <MWPhoto> photoWithProgress = [dict objectForKey:@"photo"];
  213. if (photoWithProgress == _photo) {
  214. // NSLog(@"%f", [[dict valueForKey:@"progress"] floatValue]);
  215. float progress = [[dict valueForKey:@"progress"] floatValue];
  216. _loadingIndicator.progress = MAX(MIN(1, progress), 0);
  217. }
  218. });
  219. }
  220. - (void)handleMWPhotoLoadingDidEndNotification:(NSNotification *)notification {
  221. id <MWPhoto> photo = [notification object];
  222. if (photo == _photo) {
  223. if ([photo underlyingImage]) {
  224. // Successful load
  225. [self displayImage];
  226. } else {
  227. // Failed to load
  228. [self showImageFailure];
  229. }
  230. [self hideLoadingIndicator];
  231. }
  232. }
  233. /***** BEGIN THREEMA MODIFICATION: add function *********/
  234. - (NSString *)accessibilityLabel {
  235. return [_photo accessibilityLabelForContent];
  236. }
  237. /***** END THREEMA MODIFICATION: add function *********/
  238. @end