CTAssetsPageViewController.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /*
  2. MIT License (MIT)
  3. Copyright (c) 2015 Clement CN Tsang
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. */
  20. #import "CTAssetsPageViewController.h"
  21. #import "CTAssetsPageView.h"
  22. #import "CTAssetItemViewController.h"
  23. #import "CTAssetScrollView.h"
  24. #import "NSNumberFormatter+CTAssetsPickerController.h"
  25. #import "NSBundle+CTAssetsPickerController.h"
  26. #import "UIImage+CTAssetsPickerController.h"
  27. #import "PHAsset+CTAssetsPickerController.h"
  28. @interface CTAssetsPageViewController ()
  29. <UIPageViewControllerDataSource, UIPageViewControllerDelegate>
  30. @property (nonatomic, assign) BOOL allowsSelection;
  31. @property (nonatomic, assign, getter = isStatusBarHidden) BOOL statusBarHidden;
  32. @property (nonatomic, copy) NSArray *assets;
  33. @property (nonatomic, strong, readonly) PHAsset *asset;
  34. @property (nonatomic, strong) CTAssetsPageView *pageView;
  35. @property (nonatomic, strong) UIBarButtonItem *playButton;
  36. @property (nonatomic, strong) UIBarButtonItem *pauseButton;
  37. @end
  38. @implementation CTAssetsPageViewController
  39. - (instancetype)initWithFetchResult:(PHFetchResult *)fetchResult
  40. {
  41. NSMutableArray *assets = [NSMutableArray new];
  42. for (PHAsset *asset in fetchResult)
  43. [assets addObject:asset];
  44. return [self initWithAssets:[NSArray arrayWithArray:assets]];
  45. }
  46. - (instancetype)initWithAssets:(NSArray *)assets
  47. {
  48. self = [super initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll
  49. navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
  50. options:@{UIPageViewControllerOptionInterPageSpacingKey:@30.f}];
  51. if (self)
  52. {
  53. self.assets = assets;
  54. self.dataSource = self;
  55. self.delegate = self;
  56. self.allowsSelection = NO;
  57. self.automaticallyAdjustsScrollViewInsets = NO;
  58. }
  59. return self;
  60. }
  61. - (void)viewDidLoad
  62. {
  63. [super viewDidLoad];
  64. [self setupViews];
  65. [self addNotificationObserver];
  66. }
  67. - (void)dealloc
  68. {
  69. [self removeNotificationObserver];
  70. }
  71. - (BOOL)prefersStatusBarHidden
  72. {
  73. if (self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClassCompact)
  74. return YES;
  75. else
  76. return self.isStatusBarHidden;
  77. }
  78. #pragma mark - Setup
  79. - (void)setupViews
  80. {
  81. self.pageView = [CTAssetsPageView new];
  82. [self.view insertSubview:self.pageView atIndex:0];
  83. [self.view setNeedsUpdateConstraints];
  84. }
  85. - (void)setupButtons
  86. {
  87. if (!self.playButton)
  88. {
  89. UIImage *playImage = [UIImage ctassetsPickerImageNamed:@"PlayButton"];
  90. playImage = [playImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  91. UIBarButtonItem *playButton =
  92. [[UIBarButtonItem alloc] initWithImage:playImage style:UIBarButtonItemStyleDone target:self action:@selector(playAsset:)];
  93. self.playButton = playButton;
  94. }
  95. if (!self.pauseButton)
  96. {
  97. UIImage *pasueImage = [UIImage ctassetsPickerImageNamed:@"PauseButton"];
  98. pasueImage = [pasueImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  99. UIBarButtonItem *pauseButton =
  100. [[UIBarButtonItem alloc] initWithImage:pasueImage style:UIBarButtonItemStylePlain target:self action:@selector(pauseAsset:)];
  101. self.pauseButton = pauseButton;
  102. }
  103. }
  104. #pragma mark - Update title
  105. - (void)updateTitle:(NSInteger)index
  106. {
  107. NSNumberFormatter *nf = [NSNumberFormatter new];
  108. NSInteger count = self.assets.count;
  109. self.title = [NSString stringWithFormat:CTAssetsPickerLocalizedString(@"%@ of %@", nil),
  110. [nf ctassetsPickerStringFromAssetsCount:index],
  111. [nf ctassetsPickerStringFromAssetsCount:count]];
  112. }
  113. #pragma mark - Update toolbar
  114. - (void)updateToolbar
  115. {
  116. [self setupButtons];
  117. if ([self.asset ctassetsPickerIsVideo])
  118. self.toolbarItems = @[[self toolbarSpace], self.playButton, [self toolbarSpace]];
  119. else
  120. self.toolbarItems = nil;
  121. }
  122. - (void)replaceToolbarButton:(UIBarButtonItem *)button
  123. {
  124. if (button)
  125. {
  126. UIBarButtonItem *space = [self toolbarSpace];
  127. self.toolbarItems = @[space, button, space];
  128. }
  129. }
  130. - (UIBarButtonItem *)toolbarSpace
  131. {
  132. return [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
  133. }
  134. #pragma mark - Accessors
  135. - (NSInteger)pageIndex
  136. {
  137. return [self.assets indexOfObject:self.asset];
  138. }
  139. - (void)setPageIndex:(NSInteger)pageIndex
  140. {
  141. NSInteger count = self.assets.count;
  142. if (pageIndex >= 0 && pageIndex < count)
  143. {
  144. PHAsset *asset = self.assets[pageIndex];
  145. CTAssetItemViewController *page = [CTAssetItemViewController assetItemViewControllerForAsset:asset];
  146. page.allowsSelection = self.allowsSelection;
  147. [self setViewControllers:@[page]
  148. direction:UIPageViewControllerNavigationDirectionForward
  149. animated:NO
  150. completion:NULL];
  151. [self updateTitle:pageIndex + 1];
  152. [self updateToolbar];
  153. }
  154. }
  155. - (PHAsset *)asset
  156. {
  157. return ((CTAssetItemViewController *)self.viewControllers[0]).asset;
  158. }
  159. #pragma mark - Page view controller data source
  160. - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
  161. {
  162. PHAsset *asset = ((CTAssetItemViewController *)viewController).asset;
  163. NSInteger index = [self.assets indexOfObject:asset];
  164. if (index > 0)
  165. {
  166. PHAsset *beforeAsset = self.assets[(index - 1)];
  167. CTAssetItemViewController *page = [CTAssetItemViewController assetItemViewControllerForAsset:beforeAsset];
  168. page.allowsSelection = self.allowsSelection;
  169. return page;
  170. }
  171. return nil;
  172. }
  173. - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
  174. {
  175. PHAsset *asset = ((CTAssetItemViewController *)viewController).asset;
  176. NSInteger index = [self.assets indexOfObject:asset];
  177. NSInteger count = self.assets.count;
  178. if (index < count - 1)
  179. {
  180. PHAsset *afterAsset = self.assets[(index + 1)];
  181. CTAssetItemViewController *page = [CTAssetItemViewController assetItemViewControllerForAsset:afterAsset];
  182. page.allowsSelection = self.allowsSelection;
  183. return page;
  184. }
  185. return nil;
  186. }
  187. #pragma mark - Page view controller delegate
  188. - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
  189. {
  190. if (completed)
  191. {
  192. CTAssetItemViewController *vc = (CTAssetItemViewController *)pageViewController.viewControllers[0];
  193. NSInteger index = [self.assets indexOfObject:vc.asset] + 1;
  194. [self updateTitle:index];
  195. [self updateToolbar];
  196. }
  197. }
  198. - (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers
  199. {
  200. [self.navigationController setToolbarHidden:YES animated:YES];
  201. }
  202. #pragma mark - Notification observer
  203. - (void)addNotificationObserver
  204. {
  205. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  206. [center addObserver:self
  207. selector:@selector(assetScrollViewDidTap:)
  208. name:CTAssetScrollViewDidTapNotification
  209. object:nil];
  210. [center addObserver:self
  211. selector:@selector(assetScrollViewPlayerDidPlayToEnd:)
  212. name:AVPlayerItemDidPlayToEndTimeNotification
  213. object:nil];
  214. [center addObserver:self
  215. selector:@selector(assetScrollViewPlayerWillPlay:)
  216. name:CTAssetScrollViewPlayerWillPlayNotification
  217. object:nil];
  218. [center addObserver:self
  219. selector:@selector(assetScrollViewPlayerWillPause:)
  220. name:CTAssetScrollViewPlayerWillPauseNotification
  221. object:nil];
  222. }
  223. - (void)removeNotificationObserver
  224. {
  225. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  226. [center removeObserver:self name:CTAssetScrollViewDidTapNotification object:nil];
  227. [center removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
  228. [center removeObserver:self name:CTAssetScrollViewPlayerWillPlayNotification object:nil];
  229. [center removeObserver:self name:CTAssetScrollViewPlayerWillPauseNotification object:nil];
  230. }
  231. #pragma mark - Notification events
  232. - (void)assetScrollViewDidTap:(NSNotification *)notification
  233. {
  234. UITapGestureRecognizer *gesture = (UITapGestureRecognizer *)notification.object;
  235. if (gesture.numberOfTapsRequired == 1)
  236. [self toggleFullscreen:gesture];
  237. }
  238. - (void)assetScrollViewPlayerDidPlayToEnd:(NSNotification *)notification
  239. {
  240. [self replaceToolbarButton:self.playButton];
  241. [self setFullscreen:NO];
  242. }
  243. - (void)assetScrollViewPlayerWillPlay:(NSNotification *)notification
  244. {
  245. [self replaceToolbarButton:self.pauseButton];
  246. [self setFullscreen:YES];
  247. }
  248. - (void)assetScrollViewPlayerWillPause:(NSNotification *)notification
  249. {
  250. [self replaceToolbarButton:self.playButton];
  251. }
  252. #pragma mark - Toggle fullscreen
  253. - (void)toggleFullscreen:(id)sender
  254. {
  255. [self setFullscreen:!self.isStatusBarHidden];
  256. }
  257. - (void)setFullscreen:(BOOL)fullscreen
  258. {
  259. if (fullscreen)
  260. {
  261. [self.pageView enterFullscreen];
  262. [self fadeAwayControls:self.navigationController];
  263. }
  264. else
  265. {
  266. [self.pageView exitFullscreen];
  267. [self fadeInControls:self.navigationController];
  268. }
  269. }
  270. - (void)fadeInControls:(UINavigationController *)nav
  271. {
  272. self.statusBarHidden = NO;
  273. [nav setNavigationBarHidden:NO];
  274. nav.navigationBar.alpha = 0.0f;
  275. if ([self.asset ctassetsPickerIsVideo])
  276. {
  277. [nav setToolbarHidden:NO];
  278. nav.toolbar.alpha = 0.0f;
  279. }
  280. [UIView animateWithDuration:0.2
  281. animations:^{
  282. [self setNeedsStatusBarAppearanceUpdate];
  283. nav.navigationBar.alpha = 1.0f;
  284. if ([self.asset ctassetsPickerIsVideo])
  285. nav.toolbar.alpha = 1.0f;
  286. }];
  287. }
  288. - (void)fadeAwayControls:(UINavigationController *)nav
  289. {
  290. self.statusBarHidden = YES;
  291. [UIView animateWithDuration:0.2
  292. animations:^{
  293. [self setNeedsStatusBarAppearanceUpdate];
  294. [nav setNavigationBarHidden:YES animated:NO];
  295. [nav setToolbarHidden:YES animated:NO];
  296. nav.navigationBar.alpha = 0.0f;
  297. nav.toolbar.alpha = 0.0f;
  298. }];
  299. }
  300. #pragma mark - Playback
  301. - (void)playAsset:(id)sender
  302. {
  303. [((CTAssetItemViewController *)self.viewControllers[0]) playAsset:sender];
  304. }
  305. - (void)pauseAsset:(id)sender
  306. {
  307. [((CTAssetItemViewController *)self.viewControllers[0]) pauseAsset:sender];
  308. }
  309. @end