NTESMediaPreviewViewController.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. //
  2. // NTESMediaPreviewViewController.m
  3. // NIM
  4. //
  5. // Created by chris on 2017/9/21.
  6. // Copyright © 2017年 Netease. All rights reserved.
  7. //
  8. #import "NTESMediaPreviewViewController.h"
  9. #import "UIView+NTES.h"
  10. #import "UIImageView+WebCache.h"
  11. #import "UIImage+ForceDecode.h"
  12. #import "UIImage+NTESColor.h"
  13. #import "NTESGalleryViewController.h"
  14. #import "MessageViewController.h"
  15. #import "NTESVideoViewController.h"
  16. @interface NTESMediaPreviewViewHeader : UICollectionReusableView
  17. @property (nonatomic,strong) UILabel *titleLabel;
  18. - (void)refresh:(NSString *)title;
  19. @end
  20. @interface NTESMediaPriviewViewCell : UICollectionViewCell
  21. @property (nonatomic,strong) UIImageView *imageView;
  22. @property (nonatomic,strong) UILabel *durationLabel;
  23. - (void)refresh:(NTESMediaPreviewObject *)object;
  24. @end
  25. @interface NTESMediaPreviewViewController ()<UICollectionViewDelegate,UICollectionViewDataSource>
  26. {
  27. BOOL _scollToFocus;
  28. NSCalendar *_calendar;
  29. NSMutableArray *_titles;
  30. NSMutableDictionary<NSString *, NSMutableArray *> *_contents;
  31. }
  32. @property (nonatomic,copy) NSArray *objects;
  33. @property (nonatomic,strong) NTESMediaPreviewObject *focusObject;
  34. @property (nonatomic,strong) UICollectionView *collectionView;
  35. @property (nonatomic,assign) NSInteger itemCountPerLine;
  36. @property (nonatomic,assign) CGFloat minimumInteritemSpacing;
  37. @property (nonatomic,assign) CGFloat minimumLineSpacing;
  38. @end
  39. @implementation NTESMediaPreviewViewController
  40. - (instancetype)initWithPriviewObjects:(NSArray<NTESMediaPreviewObject *> *) objects
  41. focusObject:(NTESMediaPreviewObject *)focusObject
  42. {
  43. self = [super init];
  44. if (self)
  45. {
  46. _objects = objects;
  47. _focusObject = focusObject;
  48. _itemCountPerLine = 3;
  49. _minimumInteritemSpacing = 1.0f;
  50. _minimumLineSpacing = 1.0f;
  51. _calendar = [NSCalendar currentCalendar];
  52. _contents = [[NSMutableDictionary alloc] init];
  53. _titles = [[NSMutableArray alloc] init];
  54. }
  55. return self;
  56. }
  57. - (void)viewDidLoad {
  58. [super viewDidLoad];
  59. self.navigationItem.title = @"图片和视频";
  60. [self sort];
  61. UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
  62. self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
  63. self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  64. self.collectionView.backgroundColor = UIColorFromRGB(0x1d1d1d);
  65. self.collectionView.delegate = self;
  66. self.collectionView.dataSource = self;
  67. [self.collectionView registerClass:[NTESMediaPriviewViewCell class] forCellWithReuseIdentifier:@"cell"];
  68. [self.collectionView registerClass:[NTESMediaPreviewViewHeader class]forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"header"];
  69. UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
  70. btn.frame = CGRectMake(0, 0, 40, 40);
  71. [btn setImage:[UIImage imageNamed:@"fanhui2"] forState:UIControlStateNormal];
  72. btn.imageEdgeInsets = UIEdgeInsetsMake(0, -20, 0, 0);
  73. [btn addTarget:self action:@selector(backClick) forControlEvents:UIControlEventTouchUpInside];
  74. UIBarButtonItem *leftItem = [[UIBarButtonItem alloc] initWithCustomView:btn];
  75. UIBarButtonItem *nagetiveSpacer = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
  76. nagetiveSpacer.width = -12;//这个值可以根据自己需要自己调整
  77. self.navigationItem.leftBarButtonItems = @[nagetiveSpacer, leftItem];
  78. [self.view addSubview:self.collectionView];
  79. }
  80. -(void)backClick{
  81. [self.navigationController popViewControllerAnimated:YES];
  82. }
  83. - (void)viewWillAppear:(BOOL)animated{
  84. self.tabBarController.tabBar.hidden = YES;
  85. }
  86. - (void)viewDidDisappear:(BOOL)animated{
  87. self.tabBarController.tabBar.hidden = NO;
  88. }
  89. - (void)viewDidLayoutSubviews
  90. {
  91. [super viewDidLayoutSubviews];
  92. if (!_scollToFocus && self.objects.count)
  93. {
  94. NSIndexPath *indexpath = [self indexPath:self.focusObject];
  95. [self.collectionView scrollToItemAtIndexPath:indexpath atScrollPosition:UICollectionViewScrollPositionTop animated:NO];
  96. _scollToFocus = YES;
  97. }
  98. }
  99. - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
  100. {
  101. CGFloat width = (collectionView.width - self.minimumInteritemSpacing * (self.itemCountPerLine - 1)) / self.itemCountPerLine;
  102. CGFloat height = width;
  103. return CGSizeMake(width, height);
  104. }
  105. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
  106. {
  107. return self.minimumInteritemSpacing;
  108. }
  109. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
  110. {
  111. return self.minimumLineSpacing;
  112. }
  113. - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
  114. {
  115. return CGSizeMake(collectionView.width, 45);
  116. }
  117. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
  118. {
  119. NSString *title = [_titles objectAtIndex:section];
  120. NSArray *array = [_contents objectForKey:title];
  121. return array.count;
  122. }
  123. - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
  124. {
  125. return _titles.count;
  126. }
  127. - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
  128. {
  129. NTESMediaPriviewViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
  130. NTESMediaPreviewObject *object = [self objectAtIndex:indexPath];
  131. [cell refresh:object];
  132. return cell;
  133. }
  134. -(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
  135. {
  136. NTESMediaPreviewViewHeader *reusableView;
  137. if (kind==UICollectionElementKindSectionHeader)
  138. {
  139. reusableView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"header" forIndexPath:indexPath];
  140. NSString *title = [_titles objectAtIndex:indexPath.section];
  141. [reusableView refresh:title];
  142. }
  143. return reusableView;
  144. }
  145. - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
  146. {
  147. NSArray *viewControllers = self.navigationController.viewControllers;
  148. NSMutableArray *vcs = [[NSMutableArray alloc] init];
  149. for (UIViewController *vc in viewControllers)
  150. {
  151. [vcs addObject:vc];
  152. if ([vc isKindOfClass:[MessageViewController class]])
  153. {
  154. break;
  155. }
  156. }
  157. [vcs addObject:self];
  158. self.navigationController.viewControllers = [NSArray arrayWithArray:vcs];
  159. NTESMediaPreviewObject *object = [self objectAtIndex:indexPath];
  160. if (object.type == NTESMediaPreviewTypeImage)
  161. {
  162. NTESGalleryItem *item = [[NTESGalleryItem alloc] init];
  163. item.thumbPath = [object thumbPath];
  164. item.imageURL = [object url];
  165. item.name = [object displayName];
  166. item.itemId = [object objectId];
  167. item.size = [object imageSize];
  168. NTESGalleryViewController *vc = [[NTESGalleryViewController alloc] initWithItem:item session:nil];
  169. vc.hidesBottomBarWhenPushed = YES;
  170. [self.navigationController pushViewController:vc animated:YES];
  171. }
  172. else if(object.type == NTESMediaPreviewTypeVideo)
  173. {
  174. NTESVideoViewItem *item = [[NTESVideoViewItem alloc] init];
  175. item.path = [object path];
  176. item.url = [object url];
  177. item.itemId = [object objectId];
  178. NTESVideoViewController *vc = [[NTESVideoViewController alloc] initWithVideoViewItem:item];
  179. vc.hidesBottomBarWhenPushed = YES;
  180. [self.navigationController pushViewController:vc animated:YES];
  181. }
  182. }
  183. - (NTESMediaPreviewObject *)objectAtIndex:(NSIndexPath *)indexPath
  184. {
  185. NSString *key = [_titles objectAtIndex:indexPath.section];
  186. NSArray *array = [_contents objectForKey:key];
  187. return [array objectAtIndex:indexPath.row];
  188. }
  189. - (NSIndexPath *)indexPath:(NTESMediaPreviewObject *)object
  190. {
  191. NSString *key = [self keyForPreviewObject:object];
  192. NSArray *array = [_contents objectForKey:key];
  193. NSInteger section = [_titles indexOfObject:key];
  194. section = (section != NSNotFound? section : 0);
  195. NSInteger row = [array indexOfObject:object];
  196. row = (row != NSNotFound? row : 0);
  197. return [NSIndexPath indexPathForRow:row inSection:section];
  198. }
  199. - (void)sort
  200. {
  201. [_contents removeAllObjects];
  202. [_titles removeAllObjects];
  203. [self.objects enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  204. NTESMediaPreviewObject *object = obj;
  205. NSString *key = [self keyForPreviewObject:object];
  206. NSMutableArray *array = [_contents objectForKey:key];
  207. if (!array)
  208. {
  209. array = [[NSMutableArray alloc] init];
  210. [_contents setObject:array forKey:key];
  211. //因为objects是有序的,这里可以保证 titles 也是有序的,只有第一次出现这个 key 时才添加到 title
  212. [_titles addObject:key];
  213. }
  214. [array addObject:object];
  215. }];
  216. }
  217. - (NSString *)keyForPreviewObject:(NTESMediaPreviewObject *)object
  218. {
  219. NSTimeInterval time = object.timestamp;
  220. NSDate * date = [NSDate dateWithTimeIntervalSince1970:time];
  221. NSDate * now = [NSDate date];
  222. NSCalendarUnit components = (NSCalendarUnit)(NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitWeekOfMonth);
  223. NSDateComponents *dateComponents = [_calendar components:components fromDate:date];
  224. NSDateComponents *nowComponents = [_calendar components:components fromDate:now];
  225. NSString *key;
  226. if (dateComponents.year == nowComponents.year && dateComponents.month == nowComponents.month && dateComponents.weekOfMonth == nowComponents.weekOfMonth)
  227. {
  228. key = @"本周";
  229. }
  230. else
  231. {
  232. key = [NSString stringWithFormat:@"%zd年%zd月",dateComponents.year,dateComponents.month];
  233. }
  234. return key;
  235. }
  236. #pragma mark - 旋转处理 (iOS8 or above)
  237. - (void)viewWillTransitionToSize:(CGSize)size
  238. withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
  239. {
  240. [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
  241. if (![self isViewLoaded]) {
  242. return;
  243. }
  244. [self.collectionView.collectionViewLayout invalidateLayout];
  245. }
  246. @end
  247. FOUNDATION_STATIC_INLINE NSUInteger NTESCacheCostForImage(UIImage *image) {
  248. return image.size.height * image.size.width * image.scale * image.scale;
  249. }
  250. @implementation NTESMediaPreviewObject
  251. - (BOOL)isEqual:(id)object
  252. {
  253. NTESMediaPreviewObject *obj = (NTESMediaPreviewObject *)object;
  254. return [self.objectId isEqualToString:obj.objectId];
  255. }
  256. @end
  257. @implementation NTESMediaPriviewViewCell
  258. - (instancetype)initWithFrame:(CGRect)frame
  259. {
  260. self = [super initWithFrame:frame];
  261. if (self)
  262. {
  263. _imageView = [[UIImageView alloc] initWithFrame:frame];
  264. _imageView.contentMode = UIViewContentModeScaleAspectFill;
  265. _imageView.clipsToBounds = YES;
  266. [self.contentView addSubview:_imageView];
  267. _durationLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  268. _durationLabel.font = [UIFont systemFontOfSize:13.f];
  269. _durationLabel.textColor = UIColorFromRGB(0xffffff);
  270. _durationLabel.shadowColor = UIColorFromRGB(0x0);
  271. _durationLabel.shadowOffset = CGSizeMake(0.5, 0.5);
  272. [self.contentView addSubview:_durationLabel];
  273. }
  274. return self;
  275. }
  276. - (void)refresh:(NTESMediaPreviewObject *)object
  277. {
  278. static NSCache *previewImageCache;
  279. static UIImage *placeHolderImage;
  280. static NSCache *durationCache;
  281. static dispatch_once_t onceToken;
  282. dispatch_once(&onceToken, ^{
  283. previewImageCache = [[NSCache alloc] init];
  284. durationCache = [[NSCache alloc] init];
  285. placeHolderImage = [UIImage imageWithColor:[UIColor grayColor]];
  286. });
  287. self.imageView.image = nil;
  288. UIImage *image = [previewImageCache objectForKey:object.thumbPath];
  289. if (!image && [[NSFileManager defaultManager] fileExistsAtPath:object.thumbPath])
  290. {
  291. //存磁盘读出
  292. image = [UIImage imageWithContentsOfFile:object.thumbPath];
  293. //预解码
  294. image = [UIImage decodedImageWithImage:image];
  295. //缓存
  296. NSUInteger cost = NTESCacheCostForImage(image);
  297. [previewImageCache setObject:image forKey:object.thumbPath cost:cost];
  298. }
  299. if (!image && object.thumbUrl)
  300. {
  301. [self.imageView sd_setImageWithURL:[NSURL URLWithString:object.thumbUrl] placeholderImage:placeHolderImage];
  302. }
  303. else
  304. {
  305. self.imageView.image = image;
  306. }
  307. //刷新时长框
  308. CGRect originFrame = self.durationLabel.frame;
  309. if (object.duration > 0)
  310. {
  311. NSString *duration = [durationCache objectForKey:object.thumbPath];
  312. if (!duration)
  313. {
  314. NSInteger seconds = (object.duration+500)/1000; //四舍五入
  315. duration = [NSString stringWithFormat:@"%02zd:%02zd",seconds / 60,seconds % 60];
  316. [durationCache setObject:duration forKey:object.thumbPath];
  317. }
  318. self.durationLabel.text = duration;
  319. }
  320. else
  321. {
  322. self.durationLabel.text = nil;
  323. }
  324. [self.durationLabel sizeToFit];
  325. if (!CGRectEqualToRect(originFrame, self.durationLabel.frame))
  326. {
  327. [self setNeedsLayout];
  328. }
  329. }
  330. - (void)layoutSubviews
  331. {
  332. [super layoutSubviews];
  333. self.imageView.frame = self.contentView.bounds;
  334. CGFloat right = 5.f;
  335. CGFloat botttom = 5.f;
  336. self.durationLabel.right = self.width - right;
  337. self.durationLabel.bottom = self.height - botttom;
  338. }
  339. @end
  340. @implementation NTESMediaPreviewViewHeader
  341. - (instancetype)initWithFrame:(CGRect)frame
  342. {
  343. self = [super initWithFrame:frame];
  344. if (self)
  345. {
  346. _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  347. _titleLabel.font = [UIFont systemFontOfSize:15.f];
  348. _titleLabel.textColor = UIColorFromRGB(0xffffff);
  349. [self addSubview:_titleLabel];
  350. }
  351. return self;
  352. }
  353. - (void)refresh:(NSString *)title
  354. {
  355. self.titleLabel.text = title;
  356. [self.titleLabel sizeToFit];
  357. }
  358. - (void)layoutSubviews
  359. {
  360. [super layoutSubviews];
  361. self.titleLabel.left = 10.f;
  362. self.titleLabel.centerY = self.height * .5f;
  363. }
  364. @end