XG_MediaBrowseView.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. //
  2. // XG_MediaBrowseView.m
  3. // MyApp
  4. //
  5. // Created by huxinguang on 2018/10/30.
  6. // Copyright © 2018年 huxinguang. All rights reserved.
  7. //
  8. #import "XG_MediaBrowseView.h"
  9. #import "XG_MediaCell.h"
  10. #import "UIView+XGAdd.h"
  11. @interface XG_MediaBrowseView()<UICollectionViewDelegate,UICollectionViewDataSource,UIGestureRecognizerDelegate>
  12. @property (nonatomic, weak) UIView *fromView;
  13. @property (nonatomic, weak) UIView *toContainerView;
  14. @property (nonatomic, strong) UIView *blackBackground;
  15. @property (nonatomic, strong) UICollectionView *collectionView;
  16. @property (nonatomic, assign) NSInteger fromItemIndex;
  17. @property (nonatomic, strong) UICollectionView *fromCollectionView;
  18. @property (nonatomic, assign) BOOL isPresented;
  19. @end
  20. @implementation XG_MediaBrowseView
  21. - (instancetype)initWithItems:(NSArray<XG_AssetModel *> *)items{
  22. self = [super init];
  23. if (items.count == 0) return nil;
  24. self.backgroundColor = [UIColor clearColor];
  25. self.frame = [UIScreen mainScreen].bounds;
  26. self.clipsToBounds = YES;
  27. _items = items;
  28. [self setupSubViews];
  29. [self addGesture];
  30. return self;
  31. }
  32. - (void)setupSubViews{
  33. [self addSubview:self.blackBackground];
  34. [self addSubview:self.collectionView];
  35. }
  36. #pragma mark - Getter & Setter
  37. - (UIView *)blackBackground{
  38. if (!_blackBackground) {
  39. _blackBackground = [UIView new];
  40. _blackBackground.frame = self.bounds;
  41. _blackBackground.backgroundColor = [UIColor blackColor];
  42. _blackBackground.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
  43. }
  44. return _blackBackground;
  45. }
  46. -(UICollectionView *)collectionView{
  47. if (!_collectionView) {
  48. UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init];
  49. layout.minimumLineSpacing = 0;
  50. layout.minimumInteritemSpacing = 0;
  51. layout.itemSize = [UIScreen mainScreen].bounds.size;
  52. layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
  53. _collectionView = [[UICollectionView alloc]initWithFrame:self.bounds collectionViewLayout:layout];
  54. _collectionView.backgroundColor = [UIColor clearColor];
  55. _collectionView.delegate = self;
  56. _collectionView.dataSource = self;
  57. _collectionView.pagingEnabled = YES;
  58. _collectionView.showsHorizontalScrollIndicator = NO;
  59. [_collectionView registerClass:[XG_MediaCell class] forCellWithReuseIdentifier:NSStringFromClass([XG_MediaCell class])];
  60. }
  61. return _collectionView;
  62. }
  63. - (NSInteger)currentPage{
  64. NSInteger page = self.collectionView.contentOffset.x / self.collectionView.width + 0.5;
  65. if (page >= _items.count) page = (NSInteger)_items.count - 1;
  66. if (page < 0) page = 0;
  67. return page;
  68. }
  69. #pragma mark - Gesture
  70. - (void)addGesture{
  71. UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onSingleTap:)];
  72. singleTap.delegate = self;
  73. [self addGestureRecognizer:singleTap];
  74. UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onDoubleTap:)];
  75. doubleTap.delegate = self;
  76. doubleTap.numberOfTapsRequired = 2;
  77. [singleTap requireGestureRecognizerToFail:doubleTap];
  78. [self addGestureRecognizer:doubleTap];
  79. }
  80. - (void)onSingleTap:(UITapGestureRecognizer *)gesture{
  81. XG_MediaCell *cell = [self currentCell];
  82. if (cell.item.asset.mediaType == PHAssetMediaTypeVideo && cell.playBtn.hidden) {
  83. [cell pauseAndResetPlayer];
  84. return;
  85. }
  86. [self dismissAnimated:YES completion:nil];
  87. }
  88. - (void)onDoubleTap:(UITapGestureRecognizer *)gesture{
  89. if (!_isPresented) return;
  90. XG_MediaCell *cell = [self currentCell];
  91. if (cell.item.asset.mediaType == PHAssetMediaTypeVideo) {
  92. [cell pausePlayer];
  93. return;
  94. }
  95. if (cell.scrollView.zoomScale > 1) {
  96. [cell.scrollView setZoomScale:1 animated:YES];
  97. } else {
  98. CGPoint touchPoint = [gesture locationInView:cell.imageView];
  99. CGFloat newZoomScale = cell.scrollView.maximumZoomScale;
  100. CGFloat xsize = self.width / newZoomScale;
  101. CGFloat ysize = self.height / newZoomScale;
  102. [cell.scrollView zoomToRect:CGRectMake(touchPoint.x - xsize/2, touchPoint.y - ysize/2, xsize, ysize) animated:YES];
  103. }
  104. }
  105. - (void)presentCellImageAtIndexPath:(NSIndexPath *)indexpath
  106. FromCollectionView:(UICollectionView *)collectV
  107. toContainer:(UIView *)toContainer
  108. animated:(BOOL)animated
  109. completion:(void (^)(void))completion{
  110. if (!toContainer) return;
  111. _fromCollectionView = collectV;
  112. XG_AssetCell *assetCell = (XG_AssetCell *)[_fromCollectionView cellForItemAtIndexPath:indexpath];
  113. _fromView = assetCell.imageView;
  114. _toContainerView = toContainer;
  115. _fromItemIndex = indexpath.item;
  116. self.size = _toContainerView.size;
  117. [_toContainerView addSubview:self];
  118. [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:_fromItemIndex-1 inSection:0] atScrollPosition:UICollectionViewScrollPositionRight animated:NO];
  119. [self.collectionView layoutIfNeeded];//关键,否则下面获取的cell是nil
  120. [UIView setAnimationsEnabled:YES];
  121. XG_MediaCell *cell = [self currentCell];
  122. CGRect fromFrame = [_fromView convertRect:_fromView.bounds toView:cell.mediaContainerView];
  123. cell.mediaContainerView.clipsToBounds = NO;
  124. cell.imageView.frame = fromFrame;
  125. float oneTime = animated ? 0.3 : 0;
  126. self.collectionView.userInteractionEnabled = NO;
  127. [UIView animateWithDuration:oneTime delay:0 options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseInOut animations:^{
  128. cell.imageView.frame = cell.mediaContainerView.bounds;
  129. }completion:^(BOOL finished) {
  130. cell.mediaContainerView.clipsToBounds = YES;
  131. cell.imageView.clipsToBounds = YES;
  132. self.isPresented = YES;
  133. self.collectionView.userInteractionEnabled = YES;
  134. if (completion) completion();
  135. }];
  136. }
  137. - (void)dismissAnimated:(BOOL)animated completion:(void (^)(void))completion {
  138. [[NSNotificationCenter defaultCenter]postNotificationName:kShowStatusBarNotification object:nil];
  139. [UIView setAnimationsEnabled:YES];
  140. self.blackBackground.alpha = 0;
  141. NSInteger currentPage = self.currentPage;
  142. XG_MediaCell *cell = [self currentCell];
  143. cell.imageView.clipsToBounds = YES;
  144. UIView *fromView = nil;
  145. if (self.fromItemIndex-1 == currentPage) {
  146. fromView = self.fromView;
  147. } else {
  148. XG_AssetCell *assetCell = (XG_AssetCell *)[_fromCollectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:currentPage+1 inSection:0]];
  149. fromView = assetCell.imageView;
  150. }
  151. self.isPresented = NO;
  152. BOOL isFromImageClipped = fromView.layer.contentsRect.size.height < 1;
  153. [CATransaction begin];
  154. [CATransaction setDisableActions:YES];
  155. if (isFromImageClipped) {
  156. CGRect frame = cell.mediaContainerView.frame;
  157. cell.mediaContainerView.layer.anchorPoint = CGPointMake(0.5, 0);
  158. cell.mediaContainerView.frame = frame;
  159. }
  160. [CATransaction commit];
  161. if (fromView == nil) {
  162. [UIView animateWithDuration:animated ? 0.25 : 0 delay:0 options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseOut animations:^{
  163. self.alpha = 0.0;
  164. [self.collectionView.layer setValue:@0.95 forKeyPath:@"transform.scale"];
  165. self.collectionView.alpha = 0;
  166. }completion:^(BOOL finished) {
  167. [self.collectionView.layer setValue:@1 forKeyPath:@"transform.scale"];
  168. [self removeFromSuperview];
  169. if (completion) completion();
  170. }];
  171. return;
  172. }
  173. if (isFromImageClipped) {
  174. CGPoint off = cell.scrollView.contentOffset;
  175. off.y = 0 - cell.scrollView.contentInset.top;
  176. [cell.scrollView setContentOffset:off animated:NO];
  177. }
  178. [UIView animateWithDuration:animated ? 0.3 : 0 delay:0 options:UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionCurveEaseOut animations:^{
  179. if (isFromImageClipped) {
  180. CGRect fromFrame = [fromView convertRect:fromView.bounds toView:cell];
  181. CGFloat scale = fromFrame.size.width / cell.mediaContainerView.width * cell.scrollView.zoomScale;
  182. CGFloat height = fromFrame.size.height / fromFrame.size.width * cell.mediaContainerView.width;
  183. if (isnan(height)) height = cell.mediaContainerView.height;
  184. cell.mediaContainerView.height = height;
  185. cell.mediaContainerView.center = CGPointMake(CGRectGetMidX(fromFrame), CGRectGetMinY(fromFrame));
  186. [cell.mediaContainerView.layer setValue:@(scale) forKeyPath:@"transform.scale"];
  187. } else {
  188. CGRect fromFrame = [fromView convertRect:fromView.bounds toView:cell.mediaContainerView];
  189. cell.mediaContainerView.clipsToBounds = NO;
  190. cell.imageView.contentMode = fromView.contentMode;
  191. cell.imageView.frame = fromFrame;
  192. }
  193. }completion:^(BOOL finished) {
  194. self.alpha = 0;
  195. cell.mediaContainerView.layer.anchorPoint = CGPointMake(0.5, 0.5);
  196. [self removeFromSuperview];
  197. if (completion) completion();
  198. }];
  199. }
  200. - (XG_MediaCell *)currentCell{
  201. return (XG_MediaCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:self.currentPage inSection:0]];
  202. }
  203. #pragma mark - UICollectionViewDataSource
  204. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
  205. return self.items.count;
  206. }
  207. - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
  208. XG_MediaCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([XG_MediaCell class]) forIndexPath:indexPath];
  209. cell.item = self.items[indexPath.row];
  210. return cell;
  211. }
  212. #pragma mark - UICollectionViewDelegate
  213. - (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath{
  214. NSArray <NSIndexPath *> *indexPaths = [_fromCollectionView indexPathsForVisibleItems];
  215. NSIndexPath *indexP = [NSIndexPath indexPathForItem:indexPath.item + 1 inSection:0];
  216. if (![indexPaths containsObject:indexP]) {
  217. [_fromCollectionView scrollToItemAtIndexPath:indexP atScrollPosition:UICollectionViewScrollPositionBottom animated:NO];
  218. }
  219. }
  220. #pragma mark - UIScrollViewDelegate
  221. - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
  222. XG_MediaCell *cell = [self currentCell];
  223. if (cell.item.asset.mediaType == PHAssetMediaTypeVideo) {
  224. NSInteger currentPage = self.currentPage;
  225. if(targetContentOffset->x != scrollView.frame.size.width*currentPage){
  226. [cell pauseAndResetPlayer];
  227. }
  228. }
  229. }
  230. #pragma mark - UIGestureRecognizerDelegate
  231. #pragma mark - resolve collectionView & slider conflict
  232. // 方案一
  233. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
  234. if (CGRectContainsPoint([self currentCell].bottomBar.frame, [touch locationInView:self])) {
  235. self.collectionView.scrollEnabled = NO;
  236. return NO;
  237. }
  238. self.collectionView.scrollEnabled = YES;
  239. return YES;
  240. }
  241. // 方案二
  242. //-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
  243. // if (CGRectContainsPoint([self currentCell].bottomBar.frame, point)) {
  244. // self.collectionView.scrollEnabled = NO;
  245. // return YES;
  246. // }
  247. // self.collectionView.scrollEnabled = YES;
  248. // return YES;
  249. //}
  250. // 方案三
  251. //-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
  252. // if (CGRectContainsPoint([self currentCell].bottomBar.frame, point)) {
  253. // self.collectionView.scrollEnabled = NO;
  254. // return [super hitTest:point withEvent:event];
  255. // }
  256. // self.collectionView.scrollEnabled = YES;
  257. // return [super hitTest:point withEvent:event];
  258. //}
  259. /*
  260. // Only override drawRect: if you perform custom drawing.
  261. // An empty implementation adversely affects performance during animation.
  262. - (void)drawRect:(CGRect)rect {
  263. // Drawing code
  264. }
  265. */
  266. @end