|
@@ -1,1433 +0,0 @@
|
|
|
-//
|
|
|
-// MWPhotoBrowser.m
|
|
|
-// MWPhotoBrowser
|
|
|
-//
|
|
|
-// Created by Michael Waterfall on 14/10/2010.
|
|
|
-// Copyright 2010 d3i. All rights reserved.
|
|
|
-//
|
|
|
-
|
|
|
-#import <QuartzCore/QuartzCore.h>
|
|
|
-#import "MWCommon.h"
|
|
|
-#import "MWPhotoBrowser.h"
|
|
|
-#import "MWPhotoBrowserPrivate.h"
|
|
|
-#import "UIImage+MWPhotoBrowser.h"
|
|
|
-#import "NCBridgeSwift.h"
|
|
|
-
|
|
|
-#define PADDING 10
|
|
|
-
|
|
|
-static void * MWVideoPlayerObservation = &MWVideoPlayerObservation;
|
|
|
-
|
|
|
-@implementation MWPhotoBrowser
|
|
|
-
|
|
|
-#pragma mark - Init
|
|
|
-
|
|
|
-- (id)init {
|
|
|
- if ((self = [super init])) {
|
|
|
- [self _initialisation];
|
|
|
- }
|
|
|
- return self;
|
|
|
-}
|
|
|
-
|
|
|
-- (id)initWithDelegate:(id <MWPhotoBrowserDelegate>)delegate {
|
|
|
- if ((self = [self init])) {
|
|
|
- _delegate = delegate;
|
|
|
- }
|
|
|
- return self;
|
|
|
-}
|
|
|
-
|
|
|
-- (id)initWithPhotos:(NSArray *)photosArray {
|
|
|
- if ((self = [self init])) {
|
|
|
- _fixedPhotosArray = photosArray;
|
|
|
- }
|
|
|
- return self;
|
|
|
-}
|
|
|
-
|
|
|
-- (id)initWithCoder:(NSCoder *)decoder {
|
|
|
- if ((self = [super initWithCoder:decoder])) {
|
|
|
- [self _initialisation];
|
|
|
- }
|
|
|
- return self;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)_initialisation {
|
|
|
-
|
|
|
- // Defaults
|
|
|
- NSNumber *isVCBasedStatusBarAppearanceNum = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"];
|
|
|
- if (isVCBasedStatusBarAppearanceNum) {
|
|
|
- _isVCBasedStatusBarAppearance = isVCBasedStatusBarAppearanceNum.boolValue;
|
|
|
- } else {
|
|
|
- _isVCBasedStatusBarAppearance = YES; // default
|
|
|
- }
|
|
|
- self.hidesBottomBarWhenPushed = YES;
|
|
|
- _hasBelongedToViewController = NO;
|
|
|
- _photoCount = NSNotFound;
|
|
|
- _previousLayoutBounds = CGRectZero;
|
|
|
- _currentPageIndex = 0;
|
|
|
- _previousPageIndex = NSUIntegerMax;
|
|
|
- _displayActionButton = YES;
|
|
|
- _displayShareButton = YES;
|
|
|
- _displayDeleteButton = YES;
|
|
|
- _displayNavArrows = NO;
|
|
|
- _zoomPhotosToFill = YES;
|
|
|
- _performingLayout = NO; // Reset on view did appear
|
|
|
- _rotating = NO;
|
|
|
- _viewIsActive = NO;
|
|
|
- _enableSwipeToDismiss = YES;
|
|
|
- _delayToHideElements = 5;
|
|
|
- _visiblePages = [[NSMutableSet alloc] init];
|
|
|
- _recycledPages = [[NSMutableSet alloc] init];
|
|
|
- _photos = [[NSMutableArray alloc] init];
|
|
|
- _thumbPhotos = [[NSMutableArray alloc] init];
|
|
|
- _didSavePreviousStateOfNavBar = NO;
|
|
|
- self.automaticallyAdjustsScrollViewInsets = NO;
|
|
|
-
|
|
|
- // Listen for MWPhoto notifications
|
|
|
- [[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
- selector:@selector(handleMWPhotoLoadingDidEndNotification:)
|
|
|
- name:MWPHOTO_LOADING_DID_END_NOTIFICATION
|
|
|
- object:nil];
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-- (void)dealloc {
|
|
|
- [self clearCurrentVideo];
|
|
|
- _pagingScrollView.delegate = nil;
|
|
|
- [[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
|
- [self releaseAllUnderlyingPhotos:NO];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)releaseAllUnderlyingPhotos:(BOOL)preserveCurrent {
|
|
|
- // Create a copy in case this array is modified while we are looping through
|
|
|
- // Release photos
|
|
|
- NSArray *copy = [_photos copy];
|
|
|
- for (id p in copy) {
|
|
|
- if (p != [NSNull null]) {
|
|
|
- if (preserveCurrent && p == [self photoAtIndex:self.currentIndex]) {
|
|
|
- continue; // skip current
|
|
|
- }
|
|
|
- [p unloadUnderlyingImage];
|
|
|
- }
|
|
|
- }
|
|
|
- // Release thumbs
|
|
|
- copy = [_thumbPhotos copy];
|
|
|
- for (id p in copy) {
|
|
|
- if (p != [NSNull null]) {
|
|
|
- [p unloadUnderlyingImage];
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (void)didReceiveMemoryWarning {
|
|
|
-
|
|
|
- // Release any cached data, images, etc that aren't in use.
|
|
|
- [self releaseAllUnderlyingPhotos:YES];
|
|
|
- [_recycledPages removeAllObjects];
|
|
|
-
|
|
|
- // Releases the view if it doesn't have a superview.
|
|
|
- [super didReceiveMemoryWarning];
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - View Loading
|
|
|
-
|
|
|
-// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
|
|
|
-- (void)viewDidLoad {
|
|
|
-
|
|
|
- // View
|
|
|
- self.view.backgroundColor = NCBrandColor.sharedInstance.backgroundView;
|
|
|
- self.view.clipsToBounds = YES;
|
|
|
-
|
|
|
- // Setup paging scrolling view
|
|
|
- CGRect pagingScrollViewFrame = [self frameForPagingScrollView];
|
|
|
- _pagingScrollView = [[UIScrollView alloc] initWithFrame:pagingScrollViewFrame];
|
|
|
- _pagingScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
|
- _pagingScrollView.pagingEnabled = YES;
|
|
|
- _pagingScrollView.delegate = self;
|
|
|
- _pagingScrollView.showsHorizontalScrollIndicator = NO;
|
|
|
- _pagingScrollView.showsVerticalScrollIndicator = NO;
|
|
|
- _pagingScrollView.backgroundColor = NCBrandColor.sharedInstance.backgroundView;
|
|
|
- _pagingScrollView.contentSize = [self contentSizeForPagingScrollView];
|
|
|
- [self.view addSubview:_pagingScrollView];
|
|
|
-
|
|
|
- // Toolbar
|
|
|
- _toolbar = [[UIToolbar alloc] initWithFrame:[self frameForToolbarAtOrientation:[[UIApplication sharedApplication] statusBarOrientation]]];
|
|
|
- _toolbar.tintColor = NCBrandColor.sharedInstance.brandElement;
|
|
|
- _toolbar.barTintColor = NCBrandColor.sharedInstance.tabBar;
|
|
|
- [_toolbar setBackgroundImage:nil forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsDefault];
|
|
|
- [_toolbar setBackgroundImage:nil forToolbarPosition:UIToolbarPositionAny barMetrics:UIBarMetricsCompact];
|
|
|
- _toolbar.barStyle = UIBarStyleDefault; //TWS
|
|
|
- _toolbar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
|
|
|
- [_toolbar setTranslucent:NO];
|
|
|
-
|
|
|
- // Toolbar Items
|
|
|
- if (self.displayNavArrows) {
|
|
|
- NSString *arrowPathFormat = @"UIBarButtonItemArrow%@";
|
|
|
- UIImage *previousButtonImage = [UIImage imageForResourcePath:[NSString stringWithFormat:arrowPathFormat, @"Left"] ofType:@"png" inBundle:[NSBundle bundleForClass:[self class]]];
|
|
|
- UIImage *nextButtonImage = [UIImage imageForResourcePath:[NSString stringWithFormat:arrowPathFormat, @"Right"] ofType:@"png" inBundle:[NSBundle bundleForClass:[self class]]];
|
|
|
- _previousButton = [[UIBarButtonItem alloc] initWithImage:previousButtonImage style:UIBarButtonItemStylePlain target:self action:@selector(gotoPreviousPage)];
|
|
|
- _nextButton = [[UIBarButtonItem alloc] initWithImage:nextButtonImage style:UIBarButtonItemStylePlain target:self action:@selector(gotoNextPage)];
|
|
|
- }
|
|
|
-
|
|
|
- //TWS
|
|
|
- if (self.displayDeleteButton) {
|
|
|
- _deleteButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"deletePhotoBrowser"] style:UIBarButtonItemStylePlain target:self action:@selector(deleteButtonPressed:)];
|
|
|
- }
|
|
|
-
|
|
|
- if (self.displayActionButton) {
|
|
|
- _actionButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"openFile"] style:UIBarButtonItemStylePlain target:self action:@selector(actionButtonPressed:)];
|
|
|
- }
|
|
|
-
|
|
|
- if (self.displayShareButton) {
|
|
|
- _shareButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"sharePhotoBrowser"] style:UIBarButtonItemStylePlain target:self action:@selector(shareButtonPressed:)];
|
|
|
- }
|
|
|
-
|
|
|
- // Update
|
|
|
- [self reloadData];
|
|
|
-
|
|
|
- // Swipe to dismiss
|
|
|
- if (_enableSwipeToDismiss) {
|
|
|
- UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(doneButtonPressed:)];
|
|
|
- swipeGesture.direction = UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionUp;
|
|
|
- [self.view addGestureRecognizer:swipeGesture];
|
|
|
- }
|
|
|
-
|
|
|
- // Super
|
|
|
- [super viewDidLoad];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)performLayout {
|
|
|
-
|
|
|
- // Setup
|
|
|
- _performingLayout = YES;
|
|
|
- NSUInteger numberOfPhotos = [self numberOfPhotos];
|
|
|
-
|
|
|
- // Setup pages
|
|
|
- [_visiblePages removeAllObjects];
|
|
|
- [_recycledPages removeAllObjects];
|
|
|
-
|
|
|
- // Navigation buttons
|
|
|
- if ([self.navigationController.viewControllers objectAtIndex:0] == self) {
|
|
|
- // We're first on stack so show done button
|
|
|
- _doneButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Done", nil) style:UIBarButtonItemStylePlain target:self action:@selector(doneButtonPressed:)];
|
|
|
- // Set appearance
|
|
|
- [_doneButton setBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
|
|
|
- [_doneButton setBackgroundImage:nil forState:UIControlStateNormal barMetrics:UIBarMetricsCompact];
|
|
|
- [_doneButton setBackgroundImage:nil forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
|
|
|
- [_doneButton setBackgroundImage:nil forState:UIControlStateHighlighted barMetrics:UIBarMetricsCompact];
|
|
|
- [_doneButton setTitleTextAttributes:[NSDictionary dictionary] forState:UIControlStateNormal];
|
|
|
- [_doneButton setTitleTextAttributes:[NSDictionary dictionary] forState:UIControlStateHighlighted];
|
|
|
- self.navigationItem.rightBarButtonItem = _doneButton;
|
|
|
- }
|
|
|
-
|
|
|
- // color
|
|
|
- self.navigationController.navigationBar.barTintColor = NCBrandColor.sharedInstance.brand;
|
|
|
- self.navigationController.navigationBar.tintColor = NCBrandColor.sharedInstance.brandText;
|
|
|
- [self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName : NCBrandColor.sharedInstance.brandText}];
|
|
|
- self.navigationController.navigationBar.translucent = false;
|
|
|
- [self setExtendedLayoutIncludesOpaqueBars:YES];
|
|
|
-
|
|
|
- // Toolbar items
|
|
|
- BOOL hasItems = NO;
|
|
|
- UIBarButtonItem *fixedSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:self action:nil];
|
|
|
- fixedSpace.width = 32; // To balance action button
|
|
|
- UIBarButtonItem *fixedSpaceMini = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:self action:nil];
|
|
|
- fixedSpaceMini.width = 25; // To balance action button
|
|
|
- UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
|
|
|
- NSMutableArray *items = [[NSMutableArray alloc] init];
|
|
|
-
|
|
|
-
|
|
|
- // Middle - Nav
|
|
|
- if (_previousButton && _nextButton && numberOfPhotos > 1) {
|
|
|
- hasItems = YES;
|
|
|
- [items addObject:_previousButton];
|
|
|
- [items addObject:fixedSpace];
|
|
|
- [items addObject:fixedSpace];
|
|
|
- [items addObject:_nextButton];
|
|
|
- [items addObject:flexSpace];
|
|
|
- } else {
|
|
|
- [items addObject:flexSpace];
|
|
|
- }
|
|
|
-
|
|
|
- if (_deleteButton) {
|
|
|
- [items addObject:_deleteButton];
|
|
|
- [items addObject:fixedSpaceMini];
|
|
|
- }
|
|
|
- if (_shareButton) {
|
|
|
- [items addObject:_shareButton];
|
|
|
- [items addObject:fixedSpaceMini];
|
|
|
- }
|
|
|
- if (_actionButton) {
|
|
|
- [items addObject:_actionButton];
|
|
|
- }
|
|
|
-
|
|
|
- // Toolbar visibility
|
|
|
- [_toolbar setItems:items];
|
|
|
- BOOL hideToolbar = YES;
|
|
|
- for (UIBarButtonItem* item in _toolbar.items) {
|
|
|
- if (item != fixedSpace && item != flexSpace) {
|
|
|
- hideToolbar = NO;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (hideToolbar) {
|
|
|
- [_toolbar removeFromSuperview];
|
|
|
- } else {
|
|
|
- [self.view addSubview:_toolbar];
|
|
|
- }
|
|
|
-
|
|
|
- // Update nav
|
|
|
- [self updateNavigation];
|
|
|
-
|
|
|
- // Content offset
|
|
|
- _pagingScrollView.contentOffset = [self contentOffsetForPageAtIndex:_currentPageIndex];
|
|
|
- [self tilePages];
|
|
|
- _performingLayout = NO;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-// Release any retained subviews of the main view.
|
|
|
-- (void)viewDidUnload {
|
|
|
- _currentPageIndex = 0;
|
|
|
- _pagingScrollView = nil;
|
|
|
- _visiblePages = nil;
|
|
|
- _recycledPages = nil;
|
|
|
- _toolbar = nil;
|
|
|
- _previousButton = nil;
|
|
|
- _nextButton = nil;
|
|
|
- _progressHUD = nil;
|
|
|
- [super viewDidUnload];
|
|
|
-}
|
|
|
-
|
|
|
-- (BOOL)presentingViewControllerPrefersStatusBarHidden {
|
|
|
- UIViewController *presenting = self.presentingViewController;
|
|
|
- if (presenting) {
|
|
|
- if ([presenting isKindOfClass:[UINavigationController class]]) {
|
|
|
- presenting = [(UINavigationController *)presenting topViewController];
|
|
|
- }
|
|
|
- } else {
|
|
|
- // We're in a navigation controller so get previous one!
|
|
|
- if (self.navigationController && self.navigationController.viewControllers.count > 1) {
|
|
|
- presenting = [self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2];
|
|
|
- }
|
|
|
- }
|
|
|
- if (presenting) {
|
|
|
- return [presenting prefersStatusBarHidden];
|
|
|
- } else {
|
|
|
- return NO;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Appearance
|
|
|
-
|
|
|
-- (void)viewWillAppear:(BOOL)animated {
|
|
|
-
|
|
|
- // Super
|
|
|
- [super viewWillAppear:animated];
|
|
|
-
|
|
|
- // Nav Bar Appearance iPAD
|
|
|
- if (self.traitCollection.horizontalSizeClass != UIUserInterfaceSizeClassCompact) {
|
|
|
-
|
|
|
- // ----- TWS ----- //
|
|
|
- self.navigationItem.hidesBackButton = YES;
|
|
|
- self.navigationController.topViewController.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
|
|
|
- }
|
|
|
-
|
|
|
- // Update UI
|
|
|
- [self hideControlsAfterDelay];
|
|
|
-
|
|
|
- // If rotation occured while we're presenting a modal
|
|
|
- // and the index changed, make sure we show the right one now
|
|
|
- if (_currentPageIndex != _pageIndexBeforeRotation) {
|
|
|
- [self jumpToPageAtIndex:_pageIndexBeforeRotation animated:NO];
|
|
|
- }
|
|
|
-
|
|
|
- // Layaout
|
|
|
- [self.view setNeedsLayout];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)viewDidAppear:(BOOL)animated {
|
|
|
- [super viewDidAppear:animated];
|
|
|
- _viewIsActive = YES;
|
|
|
-
|
|
|
- // Autoplay if first is video
|
|
|
- if (!_viewHasAppearedInitially) {
|
|
|
- if (_autoPlayOnAppear) {
|
|
|
- MWPhoto *photo = [self photoAtIndex:_currentPageIndex];
|
|
|
- if ([photo respondsToSelector:@selector(isVideo)] && photo.isVideo) {
|
|
|
- [self playVideoAtIndex:_currentPageIndex];
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- _viewHasAppearedInitially = YES;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-- (void)viewWillDisappear:(BOOL)animated {
|
|
|
-
|
|
|
- // Detect if rotation occurs while we're presenting a modal
|
|
|
- _pageIndexBeforeRotation = _currentPageIndex;
|
|
|
-
|
|
|
- // Check that we're being popped for good
|
|
|
- if ([self.navigationController.viewControllers objectAtIndex:0] != self &&
|
|
|
- ![self.navigationController.viewControllers containsObject:self]) {
|
|
|
-
|
|
|
- // State
|
|
|
- _viewIsActive = NO;
|
|
|
- [self clearCurrentVideo]; // Clear current playing video
|
|
|
- }
|
|
|
-
|
|
|
- // Controls
|
|
|
- [self.navigationController.navigationBar.layer removeAllAnimations]; // Stop all animations on nav bar
|
|
|
- [NSObject cancelPreviousPerformRequestsWithTarget:self]; // Cancel any pending toggles from taps
|
|
|
- [self setControlsHidden:NO animated:NO permanent:YES];
|
|
|
-
|
|
|
- // Super
|
|
|
- [super viewWillDisappear:animated];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)willMoveToParentViewController:(UIViewController *)parent {
|
|
|
- if (parent && _hasBelongedToViewController) {
|
|
|
- [NSException raise:@"MWPhotoBrowser Instance Reuse" format:@"MWPhotoBrowser instances cannot be reused."];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (void)didMoveToParentViewController:(UIViewController *)parent {
|
|
|
- if (!parent) _hasBelongedToViewController = YES;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-#pragma mark - Layout
|
|
|
-
|
|
|
-- (void)viewWillLayoutSubviews {
|
|
|
- [super viewWillLayoutSubviews];
|
|
|
- [self layoutVisiblePages];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)layoutVisiblePages {
|
|
|
-
|
|
|
- // Flag
|
|
|
- _performingLayout = YES;
|
|
|
-
|
|
|
- // Toolbar
|
|
|
- _toolbar.frame = [self frameForToolbarAtOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
|
|
|
-
|
|
|
- // Remember index
|
|
|
- NSUInteger indexPriorToLayout = _currentPageIndex;
|
|
|
-
|
|
|
- // Get paging scroll view frame to determine if anything needs changing
|
|
|
- CGRect pagingScrollViewFrame = [self frameForPagingScrollView];
|
|
|
-
|
|
|
- // Frame needs changing
|
|
|
- if (!_skipNextPagingScrollViewPositioning) {
|
|
|
- _pagingScrollView.frame = pagingScrollViewFrame;
|
|
|
- }
|
|
|
- _skipNextPagingScrollViewPositioning = NO;
|
|
|
-
|
|
|
- // Recalculate contentSize based on current orientation
|
|
|
- _pagingScrollView.contentSize = [self contentSizeForPagingScrollView];
|
|
|
-
|
|
|
- // Adjust frames and configuration of each visible page
|
|
|
- for (MWZoomingScrollView *page in _visiblePages) {
|
|
|
- NSUInteger index = page.index;
|
|
|
- page.frame = [self frameForPageAtIndex:index];
|
|
|
- if (page.captionView) {
|
|
|
- page.captionView.frame = [self frameForCaptionView:page.captionView atIndex:index];
|
|
|
- }
|
|
|
- if (page.selectedButton) {
|
|
|
- page.selectedButton.frame = [self frameForSelectedButton:page.selectedButton atIndex:index];
|
|
|
- }
|
|
|
- if (page.playButton) {
|
|
|
- page.playButton.frame = [self frameForPlayButton:page.playButton atIndex:index];
|
|
|
- }
|
|
|
-
|
|
|
- // Adjust scales if bounds has changed since last time
|
|
|
- if (!CGRectEqualToRect(_previousLayoutBounds, self.view.bounds)) {
|
|
|
- // Update zooms for new bounds
|
|
|
- [page setMaxMinZoomScalesForCurrentBounds];
|
|
|
- _previousLayoutBounds = self.view.bounds;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // Adjust video loading indicator if it's visible
|
|
|
- [self positionVideoLoadingIndicator];
|
|
|
-
|
|
|
- // Adjust contentOffset to preserve page location based on values collected prior to location
|
|
|
- _pagingScrollView.contentOffset = [self contentOffsetForPageAtIndex:indexPriorToLayout];
|
|
|
- [self didStartViewingPageAtIndex:_currentPageIndex]; // initial
|
|
|
-
|
|
|
- // Reset
|
|
|
- _currentPageIndex = indexPriorToLayout;
|
|
|
- _performingLayout = NO;
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Rotation
|
|
|
-
|
|
|
-- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
|
|
|
- return YES;
|
|
|
-}
|
|
|
-
|
|
|
-#if __IPHONE_OS_VERSION_MAX_ALLOWED < 90000
|
|
|
-- (NSUInteger)supportedInterfaceOrientations
|
|
|
-#else
|
|
|
-- (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
|
|
-#endif
|
|
|
-{
|
|
|
- return UIInterfaceOrientationMaskPortrait;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
|
|
|
-
|
|
|
- // Remember page index before rotation
|
|
|
- _pageIndexBeforeRotation = _currentPageIndex;
|
|
|
- _rotating = YES;
|
|
|
-
|
|
|
- // In iOS 7 the nav bar gets shown after rotation, but might as well do this for everything!
|
|
|
- if ([self areControlsHidden]) {
|
|
|
- // Force hidden
|
|
|
- self.navigationController.navigationBarHidden = YES;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
|
|
|
-
|
|
|
- // Perform layout
|
|
|
- _currentPageIndex = _pageIndexBeforeRotation;
|
|
|
-
|
|
|
- // Delay control holding
|
|
|
- [self hideControlsAfterDelay];
|
|
|
-
|
|
|
- // Layout
|
|
|
- [self layoutVisiblePages];
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
|
|
|
- _rotating = NO;
|
|
|
- // Ensure nav bar isn't re-displayed
|
|
|
- if ([self areControlsHidden]) {
|
|
|
- self.navigationController.navigationBarHidden = NO;
|
|
|
- self.navigationController.navigationBar.alpha = 0;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Data
|
|
|
-
|
|
|
-- (NSUInteger)currentIndex {
|
|
|
- return _currentPageIndex;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)reloadData {
|
|
|
-
|
|
|
- // Reset
|
|
|
- _photoCount = NSNotFound;
|
|
|
-
|
|
|
- // Get data
|
|
|
- NSUInteger numberOfPhotos = [self numberOfPhotos];
|
|
|
- [self releaseAllUnderlyingPhotos:YES];
|
|
|
- [_photos removeAllObjects];
|
|
|
- [_thumbPhotos removeAllObjects];
|
|
|
- for (int i = 0; i < numberOfPhotos; i++) {
|
|
|
- [_photos addObject:[NSNull null]];
|
|
|
- [_thumbPhotos addObject:[NSNull null]];
|
|
|
- }
|
|
|
-
|
|
|
- // Update current page index
|
|
|
- if (numberOfPhotos > 0) {
|
|
|
- _currentPageIndex = MAX(0, MIN(_currentPageIndex, numberOfPhotos - 1));
|
|
|
- } else {
|
|
|
- _currentPageIndex = 0;
|
|
|
- }
|
|
|
-
|
|
|
- // Update layout
|
|
|
- if ([self isViewLoaded]) {
|
|
|
- while (_pagingScrollView.subviews.count) {
|
|
|
- [[_pagingScrollView.subviews lastObject] removeFromSuperview];
|
|
|
- }
|
|
|
- [self performLayout];
|
|
|
- [self.view setNeedsLayout];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (NSUInteger)numberOfPhotos {
|
|
|
- if (_photoCount == NSNotFound) {
|
|
|
- if ([_delegate respondsToSelector:@selector(numberOfPhotosInPhotoBrowser:)]) {
|
|
|
- _photoCount = [_delegate numberOfPhotosInPhotoBrowser:self];
|
|
|
- } else if (_fixedPhotosArray) {
|
|
|
- _photoCount = _fixedPhotosArray.count;
|
|
|
- }
|
|
|
- }
|
|
|
- if (_photoCount == NSNotFound) _photoCount = 0;
|
|
|
- return _photoCount;
|
|
|
-}
|
|
|
-
|
|
|
-- (id<MWPhoto>)photoAtIndex:(NSUInteger)index {
|
|
|
- id <MWPhoto> photo = nil;
|
|
|
- if (index < _photos.count) {
|
|
|
- if ([_photos objectAtIndex:index] == [NSNull null]) {
|
|
|
- if ([_delegate respondsToSelector:@selector(photoBrowser:photoAtIndex:)]) {
|
|
|
- photo = [_delegate photoBrowser:self photoAtIndex:index];
|
|
|
- } else if (_fixedPhotosArray && index < _fixedPhotosArray.count) {
|
|
|
- photo = [_fixedPhotosArray objectAtIndex:index];
|
|
|
- }
|
|
|
- if (photo) [_photos replaceObjectAtIndex:index withObject:photo];
|
|
|
- } else {
|
|
|
- photo = [_photos objectAtIndex:index];
|
|
|
- }
|
|
|
- }
|
|
|
- return photo;
|
|
|
-}
|
|
|
-
|
|
|
-- (MWCaptionView *)captionViewForPhotoAtIndex:(NSUInteger)index {
|
|
|
- MWCaptionView *captionView = nil;
|
|
|
- if ([_delegate respondsToSelector:@selector(photoBrowser:captionViewForPhotoAtIndex:)]) {
|
|
|
- captionView = [_delegate photoBrowser:self captionViewForPhotoAtIndex:index];
|
|
|
- } else {
|
|
|
- id <MWPhoto> photo = [self photoAtIndex:index];
|
|
|
- if ([photo respondsToSelector:@selector(caption)]) {
|
|
|
- if ([photo caption]) captionView = [[MWCaptionView alloc] initWithPhoto:photo];
|
|
|
- }
|
|
|
- }
|
|
|
- captionView.alpha = [self areControlsHidden] ? 0 : 1; // Initial alpha
|
|
|
- return captionView;
|
|
|
-}
|
|
|
-
|
|
|
-- (BOOL)photoIsSelectedAtIndex:(NSUInteger)index {
|
|
|
- BOOL value = NO;
|
|
|
- if (_displaySelectionButtons) {
|
|
|
- if ([self.delegate respondsToSelector:@selector(photoBrowser:isPhotoSelectedAtIndex:)]) {
|
|
|
- value = [self.delegate photoBrowser:self isPhotoSelectedAtIndex:index];
|
|
|
- }
|
|
|
- }
|
|
|
- return value;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)setPhotoSelected:(BOOL)selected atIndex:(NSUInteger)index {
|
|
|
- if (_displaySelectionButtons) {
|
|
|
- if ([self.delegate respondsToSelector:@selector(photoBrowser:photoAtIndex:selectedChanged:)]) {
|
|
|
- [self.delegate photoBrowser:self photoAtIndex:index selectedChanged:selected];
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (UIImage *)imageForPhoto:(id<MWPhoto>)photo {
|
|
|
- if (photo) {
|
|
|
- // Get image or obtain in background
|
|
|
- if ([photo underlyingImage]) {
|
|
|
- return [photo underlyingImage];
|
|
|
- } else {
|
|
|
- [photo loadUnderlyingImageAndNotify];
|
|
|
- }
|
|
|
- }
|
|
|
- return nil;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)loadAdjacentPhotosIfNecessary:(id<MWPhoto>)photo
|
|
|
-{
|
|
|
- MWZoomingScrollView *page = [self pageDisplayingPhoto:photo];
|
|
|
-
|
|
|
- if (page) {
|
|
|
- // If page is current page then initiate loading of previous and next pages
|
|
|
- NSUInteger pageIndex = page.index;
|
|
|
- if (_currentPageIndex == pageIndex) {
|
|
|
-
|
|
|
- if (pageIndex > 0) {
|
|
|
-
|
|
|
- // Preload index - 1
|
|
|
- id <MWPhoto> photo = [self photoAtIndex:pageIndex-1];
|
|
|
-
|
|
|
- if (![photo underlyingImage]) {
|
|
|
- [photo loadUnderlyingImageAndNotify];
|
|
|
- MWLog(@"Pre-loading image at index %lu", (unsigned long)pageIndex-1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (pageIndex < [self numberOfPhotos] - 1) {
|
|
|
-
|
|
|
- // Preload index + 1
|
|
|
- id <MWPhoto> photo = [self photoAtIndex:pageIndex+1];
|
|
|
-
|
|
|
- if (![photo underlyingImage]) {
|
|
|
- [photo loadUnderlyingImageAndNotify];
|
|
|
- MWLog(@"Pre-loading image at index %lu", (unsigned long)pageIndex+1);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - MWPhoto Loading Notification
|
|
|
-
|
|
|
-- (void)handleMWPhotoLoadingDidEndNotification:(NSNotification *)notification {
|
|
|
- id <MWPhoto> photo = [notification object];
|
|
|
- MWZoomingScrollView *page = [self pageDisplayingPhoto:photo];
|
|
|
- if (page) {
|
|
|
- if ([photo underlyingImage]) {
|
|
|
- // Successful load
|
|
|
- [page displayImage];
|
|
|
- [self loadAdjacentPhotosIfNecessary:photo];
|
|
|
- } else {
|
|
|
-
|
|
|
- // Failed to load
|
|
|
- [page displayImageFailure];
|
|
|
- }
|
|
|
- // Update nav
|
|
|
- [self updateNavigation];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Paging
|
|
|
-
|
|
|
-- (void)tilePages {
|
|
|
-
|
|
|
- // Calculate which pages should be visible
|
|
|
- // Ignore padding as paging bounces encroach on that
|
|
|
- // and lead to false page loads
|
|
|
- CGRect visibleBounds = _pagingScrollView.bounds;
|
|
|
- NSInteger iFirstIndex = (NSInteger)floorf((CGRectGetMinX(visibleBounds)+PADDING*2) / CGRectGetWidth(visibleBounds));
|
|
|
- NSInteger iLastIndex = (NSInteger)floorf((CGRectGetMaxX(visibleBounds)-PADDING*2-1) / CGRectGetWidth(visibleBounds));
|
|
|
- if (iFirstIndex < 0) iFirstIndex = 0;
|
|
|
- if (iFirstIndex > [self numberOfPhotos] - 1) iFirstIndex = [self numberOfPhotos] - 1;
|
|
|
- if (iLastIndex < 0) iLastIndex = 0;
|
|
|
- if (iLastIndex > [self numberOfPhotos] - 1) iLastIndex = [self numberOfPhotos] - 1;
|
|
|
-
|
|
|
- // Recycle no longer needed pages
|
|
|
- NSInteger pageIndex;
|
|
|
- for (MWZoomingScrollView *page in _visiblePages) {
|
|
|
- pageIndex = page.index;
|
|
|
- if (pageIndex < (NSUInteger)iFirstIndex || pageIndex > (NSUInteger)iLastIndex) {
|
|
|
- [_recycledPages addObject:page];
|
|
|
- [page.captionView removeFromSuperview];
|
|
|
- [page.selectedButton removeFromSuperview];
|
|
|
- [page.playButton removeFromSuperview];
|
|
|
- [page prepareForReuse];
|
|
|
- [page removeFromSuperview];
|
|
|
- MWLog(@"Removed page at index %lu", (unsigned long)pageIndex);
|
|
|
- }
|
|
|
- }
|
|
|
- [_visiblePages minusSet:_recycledPages];
|
|
|
- while (_recycledPages.count > 2) // Only keep 2 recycled pages
|
|
|
- [_recycledPages removeObject:[_recycledPages anyObject]];
|
|
|
-
|
|
|
- // Add missing pages
|
|
|
- for (NSUInteger index = (NSUInteger)iFirstIndex; index <= (NSUInteger)iLastIndex; index++) {
|
|
|
- if (![self isDisplayingPageForIndex:index]) {
|
|
|
-
|
|
|
- // Add new page
|
|
|
- MWZoomingScrollView *page = [self dequeueRecycledPage];
|
|
|
- if (!page) {
|
|
|
- page = [[MWZoomingScrollView alloc] initWithPhotoBrowser:self];
|
|
|
- }
|
|
|
- [_visiblePages addObject:page];
|
|
|
- [self configurePage:page forIndex:index];
|
|
|
-
|
|
|
- [_pagingScrollView addSubview:page];
|
|
|
- MWLog(@"Added page at index %lu", (unsigned long)index);
|
|
|
-
|
|
|
- // Add caption
|
|
|
- MWCaptionView *captionView = [self captionViewForPhotoAtIndex:index];
|
|
|
- if (captionView) {
|
|
|
- captionView.frame = [self frameForCaptionView:captionView atIndex:index];
|
|
|
- [_pagingScrollView addSubview:captionView];
|
|
|
- page.captionView = captionView;
|
|
|
- }
|
|
|
-
|
|
|
- // Add play button if needed
|
|
|
- if (page.displayingVideo) {
|
|
|
- UIButton *playButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
- [playButton setImage:[UIImage imageForResourcePath:@"PlayButtonOverlayLarge" ofType:@"png" inBundle:[NSBundle bundleForClass:[self class]]] forState:UIControlStateNormal];
|
|
|
- [playButton setImage:[UIImage imageForResourcePath:@"PlayButtonOverlayLargeTap" ofType:@"png" inBundle:[NSBundle bundleForClass:[self class]]] forState:UIControlStateHighlighted];
|
|
|
- [playButton addTarget:self action:@selector(playButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
- [playButton sizeToFit];
|
|
|
- playButton.frame = [self frameForPlayButton:playButton atIndex:index];
|
|
|
- [_pagingScrollView addSubview:playButton];
|
|
|
- page.playButton = playButton;
|
|
|
- }
|
|
|
-
|
|
|
- // Add selected button
|
|
|
- if (self.displaySelectionButtons) {
|
|
|
- UIButton *selectedButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
- [selectedButton setImage:[UIImage imageForResourcePath:@"ImageSelectedOff" ofType:@"png" inBundle:[NSBundle bundleForClass:[self class]]] forState:UIControlStateNormal];
|
|
|
- UIImage *selectedOnImage;
|
|
|
- if (self.customImageSelectedIconName) {
|
|
|
- selectedOnImage = [UIImage imageNamed:self.customImageSelectedIconName];
|
|
|
- } else {
|
|
|
- selectedOnImage = [UIImage imageForResourcePath:@"ImageSelectedOn" ofType:@"png" inBundle:[NSBundle bundleForClass:[self class]]];
|
|
|
- }
|
|
|
- [selectedButton setImage:selectedOnImage forState:UIControlStateSelected];
|
|
|
- [selectedButton sizeToFit];
|
|
|
- selectedButton.adjustsImageWhenHighlighted = NO;
|
|
|
- [selectedButton addTarget:self action:@selector(selectedButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
|
|
- selectedButton.frame = [self frameForSelectedButton:selectedButton atIndex:index];
|
|
|
- [_pagingScrollView addSubview:selectedButton];
|
|
|
- page.selectedButton = selectedButton;
|
|
|
- selectedButton.selected = [self photoIsSelectedAtIndex:index];
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-- (void)updateVisiblePageStates {
|
|
|
- NSSet *copy = [_visiblePages copy];
|
|
|
- for (MWZoomingScrollView *page in copy) {
|
|
|
-
|
|
|
- // Update selection
|
|
|
- page.selectedButton.selected = [self photoIsSelectedAtIndex:page.index];
|
|
|
-
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (BOOL)isDisplayingPageForIndex:(NSUInteger)index {
|
|
|
- for (MWZoomingScrollView *page in _visiblePages)
|
|
|
- if (page.index == index) return YES;
|
|
|
- return NO;
|
|
|
-}
|
|
|
-
|
|
|
-- (MWZoomingScrollView *)pageDisplayedAtIndex:(NSUInteger)index {
|
|
|
- MWZoomingScrollView *thePage = nil;
|
|
|
- for (MWZoomingScrollView *page in _visiblePages) {
|
|
|
- if (page.index == index) {
|
|
|
- thePage = page; break;
|
|
|
- }
|
|
|
- }
|
|
|
- return thePage;
|
|
|
-}
|
|
|
-
|
|
|
-- (MWZoomingScrollView *)pageDisplayingPhoto:(id<MWPhoto>)photo {
|
|
|
- MWZoomingScrollView *thePage = nil;
|
|
|
- for (MWZoomingScrollView *page in _visiblePages) {
|
|
|
- if (page.photo == photo) {
|
|
|
- thePage = page; break;
|
|
|
- }
|
|
|
- }
|
|
|
- return thePage;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)configurePage:(MWZoomingScrollView *)page forIndex:(NSUInteger)index {
|
|
|
- page.frame = [self frameForPageAtIndex:index];
|
|
|
- page.index = index;
|
|
|
- page.photo = [self photoAtIndex:index];
|
|
|
-}
|
|
|
-
|
|
|
-- (MWZoomingScrollView *)dequeueRecycledPage {
|
|
|
- MWZoomingScrollView *page = [_recycledPages anyObject];
|
|
|
- if (page) {
|
|
|
- [_recycledPages removeObject:page];
|
|
|
- }
|
|
|
- return page;
|
|
|
-}
|
|
|
-
|
|
|
-// Handle page changes
|
|
|
-- (void)didStartViewingPageAtIndex:(NSUInteger)index {
|
|
|
-
|
|
|
- // Handle 0 photos
|
|
|
- if (![self numberOfPhotos]) {
|
|
|
- // Show controls
|
|
|
- [self setControlsHidden:NO animated:YES permanent:YES];
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Handle video on page change
|
|
|
- if (!_rotating || index != _currentVideoIndex) {
|
|
|
- [self clearCurrentVideo];
|
|
|
- }
|
|
|
-
|
|
|
- // Release images further away than +/-1
|
|
|
- NSUInteger i;
|
|
|
- if (index > 0) {
|
|
|
- // Release anything < index - 1
|
|
|
- for (i = 0; i < index-1; i++) {
|
|
|
- id photo = [_photos objectAtIndex:i];
|
|
|
- if (photo != [NSNull null]) {
|
|
|
- [photo unloadUnderlyingImage];
|
|
|
- [_photos replaceObjectAtIndex:i withObject:[NSNull null]];
|
|
|
- MWLog(@"Released underlying image at index %lu", (unsigned long)i);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (index < [self numberOfPhotos] - 1) {
|
|
|
- // Release anything > index + 1
|
|
|
- for (i = index + 2; i < _photos.count; i++) {
|
|
|
- id photo = [_photos objectAtIndex:i];
|
|
|
- if (photo != [NSNull null]) {
|
|
|
- [photo unloadUnderlyingImage];
|
|
|
- [_photos replaceObjectAtIndex:i withObject:[NSNull null]];
|
|
|
- MWLog(@"Released underlying image at index %lu", (unsigned long)i);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Load adjacent images if needed and the photo is already
|
|
|
- // loaded. Also called after photo has been loaded in background
|
|
|
- id <MWPhoto> currentPhoto = [self photoAtIndex:index];
|
|
|
- if ([currentPhoto underlyingImage]) {
|
|
|
- // photo loaded so load ajacent now
|
|
|
- [self loadAdjacentPhotosIfNecessary:currentPhoto];
|
|
|
- }
|
|
|
-
|
|
|
- // Notify delegate
|
|
|
- if (index != _previousPageIndex) {
|
|
|
- if ([_delegate respondsToSelector:@selector(photoBrowser:didDisplayPhotoAtIndex:)])
|
|
|
- [_delegate photoBrowser:self didDisplayPhotoAtIndex:index];
|
|
|
- _previousPageIndex = index;
|
|
|
- } else {
|
|
|
- if ([_delegate respondsToSelector:@selector(photoBrowser:didDisplayPhotoAtIndex:)])
|
|
|
- [_delegate photoBrowser:self didDisplayPhotoAtIndex:index];
|
|
|
- }
|
|
|
-
|
|
|
- // Update nav
|
|
|
- [self updateNavigation];
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Frame Calculations
|
|
|
-
|
|
|
-- (CGRect)frameForPagingScrollView {
|
|
|
- CGRect frame = self.view.bounds;// [[UIScreen mainScreen] bounds];
|
|
|
- frame.origin.x -= PADDING;
|
|
|
- frame.size.width += (2 * PADDING);
|
|
|
- return CGRectIntegral(frame);
|
|
|
-}
|
|
|
-
|
|
|
-- (CGRect)frameForPageAtIndex:(NSUInteger)index {
|
|
|
- // We have to use our paging scroll view's bounds, not frame, to calculate the page placement. When the device is in
|
|
|
- // landscape orientation, the frame will still be in portrait because the pagingScrollView is the root view controller's
|
|
|
- // view, so its frame is in window coordinate space, which is never rotated. Its bounds, however, will be in landscape
|
|
|
- // because it has a rotation transform applied.
|
|
|
- CGRect bounds = _pagingScrollView.bounds;
|
|
|
- CGRect pageFrame = bounds;
|
|
|
- pageFrame.size.width -= (2 * PADDING);
|
|
|
- pageFrame.origin.x = (bounds.size.width * index) + PADDING;
|
|
|
- return CGRectIntegral(pageFrame);
|
|
|
-}
|
|
|
-
|
|
|
-- (CGSize)contentSizeForPagingScrollView {
|
|
|
- // We have to use the paging scroll view's bounds to calculate the contentSize, for the same reason outlined above.
|
|
|
- CGRect bounds = _pagingScrollView.bounds;
|
|
|
- return CGSizeMake(bounds.size.width * [self numberOfPhotos], bounds.size.height);
|
|
|
-}
|
|
|
-
|
|
|
-- (CGPoint)contentOffsetForPageAtIndex:(NSUInteger)index {
|
|
|
- CGFloat pageWidth = _pagingScrollView.bounds.size.width;
|
|
|
- CGFloat newOffset = index * pageWidth;
|
|
|
- return CGPointMake(newOffset, 0);
|
|
|
-}
|
|
|
-
|
|
|
-- (CGRect)frameForToolbarAtOrientation:(UIInterfaceOrientation)orientation {
|
|
|
-
|
|
|
- CGFloat safeAreaBottom = 0;
|
|
|
- CGFloat height = 49;
|
|
|
-
|
|
|
- // iOS 11 safeArea
|
|
|
- if (@available(iOS 11, *)) {
|
|
|
- safeAreaBottom = [UIApplication sharedApplication].delegate.window.safeAreaInsets.bottom;
|
|
|
- }
|
|
|
-
|
|
|
- if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && UIInterfaceOrientationIsLandscape(orientation))
|
|
|
- height = 39;
|
|
|
-
|
|
|
- return CGRectIntegral(CGRectMake(0, self.view.bounds.size.height - height - safeAreaBottom, self.view.bounds.size.width, height ));
|
|
|
-}
|
|
|
-
|
|
|
-- (CGRect)frameForCaptionView:(MWCaptionView *)captionView atIndex:(NSUInteger)index {
|
|
|
-
|
|
|
- // iOS 11 safeArea
|
|
|
- CGFloat safeAreaBottom = 0;
|
|
|
- if (@available(iOS 11, *)) {
|
|
|
- safeAreaBottom = [UIApplication sharedApplication].delegate.window.safeAreaInsets.bottom;
|
|
|
- }
|
|
|
-
|
|
|
- CGRect pageFrame = [self frameForPageAtIndex:index];
|
|
|
- CGSize captionSize = [captionView sizeThatFits:CGSizeMake(pageFrame.size.width, 0)];
|
|
|
- CGRect captionFrame = CGRectMake(pageFrame.origin.x,
|
|
|
- pageFrame.size.height - captionSize.height - (_toolbar.superview?_toolbar.frame.size.height:0) - safeAreaBottom,
|
|
|
- pageFrame.size.width,
|
|
|
- captionSize.height);
|
|
|
- return CGRectIntegral(captionFrame);
|
|
|
-}
|
|
|
-
|
|
|
-- (CGRect)frameForSelectedButton:(UIButton *)selectedButton atIndex:(NSUInteger)index {
|
|
|
- CGRect pageFrame = [self frameForPageAtIndex:index];
|
|
|
- CGFloat padding = 20;
|
|
|
- CGFloat yOffset = 0;
|
|
|
- if (![self areControlsHidden]) {
|
|
|
- UINavigationBar *navBar = self.navigationController.navigationBar;
|
|
|
- yOffset = navBar.frame.origin.y + navBar.frame.size.height;
|
|
|
- }
|
|
|
- CGRect selectedButtonFrame = CGRectMake(pageFrame.origin.x + pageFrame.size.width - selectedButton.frame.size.width - padding,
|
|
|
- padding + yOffset,
|
|
|
- selectedButton.frame.size.width,
|
|
|
- selectedButton.frame.size.height);
|
|
|
- return CGRectIntegral(selectedButtonFrame);
|
|
|
-}
|
|
|
-
|
|
|
-- (CGRect)frameForPlayButton:(UIButton *)playButton atIndex:(NSUInteger)index {
|
|
|
- CGRect pageFrame = [self frameForPageAtIndex:index];
|
|
|
- return CGRectMake(floorf(CGRectGetMidX(pageFrame) - playButton.frame.size.width / 2),
|
|
|
- floorf(CGRectGetMidY(pageFrame) - playButton.frame.size.height / 2),
|
|
|
- playButton.frame.size.width,
|
|
|
- playButton.frame.size.height);
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - UIScrollView Delegate
|
|
|
-
|
|
|
-- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
|
|
-
|
|
|
- // Checks
|
|
|
- if (!_viewIsActive || _performingLayout || _rotating) return;
|
|
|
-
|
|
|
- // Tile pages
|
|
|
- [self tilePages];
|
|
|
-
|
|
|
- // Calculate current page
|
|
|
- CGRect visibleBounds = _pagingScrollView.bounds;
|
|
|
- NSInteger index = (NSInteger)(floorf(CGRectGetMidX(visibleBounds) / CGRectGetWidth(visibleBounds)));
|
|
|
- if (index < 0) index = 0;
|
|
|
- if (index > [self numberOfPhotos] - 1) index = [self numberOfPhotos] - 1;
|
|
|
- NSUInteger previousCurrentPage = _currentPageIndex;
|
|
|
- _currentPageIndex = index;
|
|
|
- if (_currentPageIndex != previousCurrentPage) {
|
|
|
- [self didStartViewingPageAtIndex:index];
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
|
|
|
- // Hide controls when dragging begins
|
|
|
- [self setControlsHidden:YES animated:YES permanent:NO];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
|
|
|
- // Update nav when page changes
|
|
|
- [self updateNavigation];
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Navigation
|
|
|
-
|
|
|
-- (void)updateNavigation {
|
|
|
-
|
|
|
- // Title
|
|
|
- NSUInteger numberOfPhotos = [self numberOfPhotos];
|
|
|
- if (numberOfPhotos > 1) {
|
|
|
- if ([_delegate respondsToSelector:@selector(photoBrowser:titleForPhotoAtIndex:)]) {
|
|
|
- self.title = [_delegate photoBrowser:self titleForPhotoAtIndex:_currentPageIndex];
|
|
|
- } else {
|
|
|
- self.title = [NSString stringWithFormat:@"%lu %@ %lu", (unsigned long)(_currentPageIndex+1), NSLocalizedString(@"of", @"Used in the context: 'Showing 1 of 3 items'"), (unsigned long)numberOfPhotos];
|
|
|
- }
|
|
|
- } else {
|
|
|
- self.title = nil;
|
|
|
- }
|
|
|
-
|
|
|
- // Buttons
|
|
|
- _previousButton.enabled = (_currentPageIndex > 0);
|
|
|
- _nextButton.enabled = (_currentPageIndex < numberOfPhotos - 1);
|
|
|
-
|
|
|
- //TWS Disable action button if there is no image or it's a video
|
|
|
- /*
|
|
|
- MWPhoto *photo = [self photoAtIndex:_currentPageIndex];
|
|
|
- if ([photo underlyingImage] == nil || ([photo respondsToSelector:@selector(isVideo)] && photo.isVideo)) {
|
|
|
- _actionButton.enabled = NO;
|
|
|
- _actionButton.tintColor = [UIColor clearColor]; // Tint to hide button
|
|
|
- } else {
|
|
|
- _actionButton.enabled = YES;
|
|
|
- _actionButton.tintColor = nil;
|
|
|
- }
|
|
|
- */
|
|
|
-}
|
|
|
-
|
|
|
-- (void)jumpToPageAtIndex:(NSUInteger)index animated:(BOOL)animated {
|
|
|
-
|
|
|
- // Change page
|
|
|
- if (index < [self numberOfPhotos]) {
|
|
|
- CGRect pageFrame = [self frameForPageAtIndex:index];
|
|
|
- [_pagingScrollView setContentOffset:CGPointMake(pageFrame.origin.x - PADDING, 0) animated:animated];
|
|
|
- [self updateNavigation];
|
|
|
- }
|
|
|
-
|
|
|
- // Update timer to give more time
|
|
|
- [self hideControlsAfterDelay];
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-- (void)gotoPreviousPage {
|
|
|
- [self showPreviousPhotoAnimated:NO];
|
|
|
-}
|
|
|
-- (void)gotoNextPage {
|
|
|
- [self showNextPhotoAnimated:NO];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)showPreviousPhotoAnimated:(BOOL)animated {
|
|
|
- [self jumpToPageAtIndex:_currentPageIndex-1 animated:animated];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)showNextPhotoAnimated:(BOOL)animated {
|
|
|
- [self jumpToPageAtIndex:_currentPageIndex+1 animated:animated];
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Interactions
|
|
|
-
|
|
|
-- (void)selectedButtonTapped:(id)sender {
|
|
|
- UIButton *selectedButton = (UIButton *)sender;
|
|
|
- selectedButton.selected = !selectedButton.selected;
|
|
|
- NSUInteger index = NSUIntegerMax;
|
|
|
- for (MWZoomingScrollView *page in _visiblePages) {
|
|
|
- if (page.selectedButton == selectedButton) {
|
|
|
- index = page.index;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (index != NSUIntegerMax) {
|
|
|
- [self setPhotoSelected:selectedButton.selected atIndex:index];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (void)playButtonTapped:(id)sender {
|
|
|
- // Ignore if we're already playing a video
|
|
|
- if (_currentVideoIndex != NSUIntegerMax) {
|
|
|
- return;
|
|
|
- }
|
|
|
- NSUInteger index = [self indexForPlayButton:sender];
|
|
|
- if (index != NSUIntegerMax) {
|
|
|
- if (!_currentVideoPlayerViewController) {
|
|
|
- [self playVideoAtIndex:index];
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (NSUInteger)indexForPlayButton:(UIView *)playButton {
|
|
|
- NSUInteger index = NSUIntegerMax;
|
|
|
- for (MWZoomingScrollView *page in _visiblePages) {
|
|
|
- if (page.playButton == playButton) {
|
|
|
- index = page.index;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- return index;
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Video
|
|
|
-
|
|
|
-- (void)playVideoAtIndex:(NSUInteger)index {
|
|
|
- id photo = [self photoAtIndex:index];
|
|
|
- if ([photo respondsToSelector:@selector(getVideoURL:)]) {
|
|
|
-
|
|
|
- // Valid for playing
|
|
|
- [self clearCurrentVideo];
|
|
|
- _currentVideoIndex = index;
|
|
|
- [self setVideoLoadingIndicatorVisible:YES atPageIndex:index];
|
|
|
-
|
|
|
- // Get video and play
|
|
|
- __typeof(self) __weak weakSelf = self;
|
|
|
- [photo getVideoURL:^(NSURL *url) {
|
|
|
- dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
- // If the video is not playing anymore then bail
|
|
|
- __typeof(self) strongSelf = weakSelf;
|
|
|
- if (!strongSelf) return;
|
|
|
- if (strongSelf->_currentVideoIndex != index || !strongSelf->_viewIsActive) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (url) {
|
|
|
- [weakSelf _playVideo:url atPhotoIndex:index];
|
|
|
- } else {
|
|
|
- [weakSelf setVideoLoadingIndicatorVisible:NO atPageIndex:index];
|
|
|
- }
|
|
|
- });
|
|
|
- }];
|
|
|
-
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (void)_playVideo:(NSURL *)videoURL atPhotoIndex:(NSUInteger)index {
|
|
|
-
|
|
|
- // Setup player
|
|
|
- _currentVideoPlayerViewController = [[AVPlayerViewController alloc] init];
|
|
|
- _currentVideoPlayerViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
|
|
|
-
|
|
|
- _currentVideoPlayerItem = [[AVPlayerItem alloc] initWithURL:videoURL];
|
|
|
- _currentVideoPlayerViewController.player = [[AVPlayer alloc] initWithPlayerItem:_currentVideoPlayerItem];
|
|
|
- _currentVideoPlayer = _currentVideoPlayerViewController.player;
|
|
|
-
|
|
|
- // Remove the movie player view controller from the "playback did finish" notification observers
|
|
|
- // Observe ourselves so we can get it to use the crossfade transition
|
|
|
- [[NSNotificationCenter defaultCenter] removeObserver:self
|
|
|
- name:AVPlayerItemDidPlayToEndTimeNotification
|
|
|
- object:_currentVideoPlayerItem];
|
|
|
- [[NSNotificationCenter defaultCenter] addObserver:self
|
|
|
- selector:@selector(videoFinishedCallback:)
|
|
|
- name:AVPlayerItemDidPlayToEndTimeNotification
|
|
|
- object:_currentVideoPlayerItem];
|
|
|
-
|
|
|
- // Show
|
|
|
- [self presentViewController:_currentVideoPlayerViewController animated:YES completion:^{
|
|
|
- [_currentVideoPlayer play];
|
|
|
- }];
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-- (void)videoFinishedCallback:(NSNotification*)notification {
|
|
|
-
|
|
|
- // Remove observer
|
|
|
- [[NSNotificationCenter defaultCenter] removeObserver:self
|
|
|
- name:AVPlayerItemDidPlayToEndTimeNotification
|
|
|
- object:_currentVideoPlayerItem];
|
|
|
-
|
|
|
- // Clear up
|
|
|
- [self clearCurrentVideo];
|
|
|
- [self dismissViewControllerAnimated:YES completion:nil];
|
|
|
-}
|
|
|
-
|
|
|
-- (void)clearCurrentVideo {
|
|
|
- [_currentVideoPlayerViewController.player pause];
|
|
|
- [_currentVideoLoadingIndicator removeFromSuperview];
|
|
|
- _currentVideoPlayerViewController = nil;
|
|
|
- _currentVideoLoadingIndicator = nil;
|
|
|
- _currentVideoPlayerItem = nil;
|
|
|
- [[self pageDisplayedAtIndex:_currentVideoIndex] playButton].hidden = NO;
|
|
|
- _currentVideoIndex = NSUIntegerMax;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)setVideoLoadingIndicatorVisible:(BOOL)visible atPageIndex:(NSUInteger)pageIndex {
|
|
|
- if (_currentVideoLoadingIndicator && !visible) {
|
|
|
- [_currentVideoLoadingIndicator removeFromSuperview];
|
|
|
- _currentVideoLoadingIndicator = nil;
|
|
|
- [[self pageDisplayedAtIndex:pageIndex] playButton].hidden = NO;
|
|
|
- } else if (!_currentVideoLoadingIndicator && visible) {
|
|
|
- _currentVideoLoadingIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectZero];
|
|
|
- [_currentVideoLoadingIndicator sizeToFit];
|
|
|
- [_currentVideoLoadingIndicator startAnimating];
|
|
|
- [_pagingScrollView addSubview:_currentVideoLoadingIndicator];
|
|
|
- [self positionVideoLoadingIndicator];
|
|
|
- [[self pageDisplayedAtIndex:pageIndex] playButton].hidden = YES;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (void)positionVideoLoadingIndicator {
|
|
|
- if (_currentVideoLoadingIndicator && _currentVideoIndex != NSUIntegerMax) {
|
|
|
- CGRect frame = [self frameForPageAtIndex:_currentVideoIndex];
|
|
|
- _currentVideoLoadingIndicator.center = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame));
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Control Hiding / Showing
|
|
|
-
|
|
|
-// If permanent then we don't set timers to hide again
|
|
|
-// Fades all controls on iOS 5 & 6, and iOS 7 controls slide and fade
|
|
|
-- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated permanent:(BOOL)permanent {
|
|
|
-
|
|
|
- // Force visible
|
|
|
- if (![self numberOfPhotos] || _alwaysShowControls)
|
|
|
- hidden = NO;
|
|
|
-
|
|
|
- // Cancel any timers
|
|
|
- [self cancelControlHiding];
|
|
|
-
|
|
|
- // Animations & positions
|
|
|
- CGFloat animatonOffset = 20;
|
|
|
- CGFloat animationDuration = (animated ? 0.35 : 0);
|
|
|
-
|
|
|
- // Toolbar, nav bar and captions
|
|
|
- // Pre-appear animation positions for sliding
|
|
|
- if ([self areControlsHidden] && !hidden && animated) {
|
|
|
-
|
|
|
- // Toolbar
|
|
|
- _toolbar.frame = CGRectOffset([self frameForToolbarAtOrientation:[[UIApplication sharedApplication] statusBarOrientation]], 0, animatonOffset);
|
|
|
-
|
|
|
- // Captions
|
|
|
- for (MWZoomingScrollView *page in _visiblePages) {
|
|
|
-
|
|
|
- if (page.captionView) {
|
|
|
-
|
|
|
- MWCaptionView *v = page.captionView;
|
|
|
-
|
|
|
- //TWS
|
|
|
- id <MWPhoto> photo = [self photoAtIndex:self.currentIndex];
|
|
|
- if (photo.caption) {
|
|
|
-
|
|
|
- if ([photo caption]) v.label.text = photo.caption;
|
|
|
- }
|
|
|
-
|
|
|
- // Pass any index, all we're interested in is the Y
|
|
|
- CGRect captionFrame = [self frameForCaptionView:v atIndex:0];
|
|
|
- captionFrame.origin.x = v.frame.origin.x; // Reset X
|
|
|
- v.frame = CGRectOffset(captionFrame, 0, animatonOffset);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ([_delegate respondsToSelector:@selector(setControlsHidden:animated:permanent:)]) {
|
|
|
- [_delegate setControlsHidden:hidden animated:animated permanent:permanent];
|
|
|
- }
|
|
|
-
|
|
|
- [UIView animateWithDuration:animationDuration animations:^(void) {
|
|
|
-
|
|
|
- CGFloat alpha = hidden ? 0 : 1;
|
|
|
-
|
|
|
- // Nav bar slides up on it's own on iOS 7+
|
|
|
- [self.navigationController.navigationBar setAlpha:alpha];
|
|
|
-
|
|
|
- // Toolbar
|
|
|
- _toolbar.frame = [self frameForToolbarAtOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
|
|
|
- if (hidden) _toolbar.frame = CGRectOffset(_toolbar.frame, 0, animatonOffset);
|
|
|
- _toolbar.alpha = alpha;
|
|
|
-
|
|
|
- // Captions
|
|
|
- for (MWZoomingScrollView *page in _visiblePages) {
|
|
|
- if (page.captionView) {
|
|
|
- MWCaptionView *v = page.captionView;
|
|
|
- // Pass any index, all we're interested in is the Y
|
|
|
- CGRect captionFrame = [self frameForCaptionView:v atIndex:0];
|
|
|
- captionFrame.origin.x = v.frame.origin.x; // Reset X
|
|
|
- if (hidden) captionFrame = CGRectOffset(captionFrame, 0, animatonOffset);
|
|
|
- v.frame = captionFrame;
|
|
|
- v.alpha = alpha;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Selected buttons
|
|
|
- for (MWZoomingScrollView *page in _visiblePages) {
|
|
|
- if (page.selectedButton) {
|
|
|
- UIButton *v = page.selectedButton;
|
|
|
- CGRect newFrame = [self frameForSelectedButton:v atIndex:0];
|
|
|
- newFrame.origin.x = v.frame.origin.x;
|
|
|
- v.frame = newFrame;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- } completion:^(BOOL finished) {}];
|
|
|
-
|
|
|
- // Control hiding timer
|
|
|
- // Will cancel existing timer but only begin hiding if
|
|
|
- // they are visible
|
|
|
- if (!permanent) [self hideControlsAfterDelay];
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-- (UIStatusBarStyle)preferredStatusBarStyle {
|
|
|
- return UIStatusBarStyleLightContent;
|
|
|
-}
|
|
|
-
|
|
|
-- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {
|
|
|
- return UIStatusBarAnimationSlide;
|
|
|
-}
|
|
|
-
|
|
|
-- (void)cancelControlHiding {
|
|
|
- // If a timer exists then cancel and release
|
|
|
- if (_controlVisibilityTimer) {
|
|
|
- [_controlVisibilityTimer invalidate];
|
|
|
- _controlVisibilityTimer = nil;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// Enable/disable control visiblity timer
|
|
|
-- (void)hideControlsAfterDelay {
|
|
|
- if (![self areControlsHidden]) {
|
|
|
- [self cancelControlHiding];
|
|
|
- _controlVisibilityTimer = [NSTimer scheduledTimerWithTimeInterval:self.delayToHideElements target:self selector:@selector(hideControls) userInfo:nil repeats:NO];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-- (BOOL)areControlsHidden {
|
|
|
- return (_toolbar.alpha == 0);
|
|
|
-}
|
|
|
-
|
|
|
-- (void)hideControls { [self setControlsHidden:YES animated:YES permanent:NO]; }
|
|
|
-- (void)showControls { [self setControlsHidden:NO animated:YES permanent:NO]; }
|
|
|
-- (void)toggleControls { [self setControlsHidden:![self areControlsHidden] animated:YES permanent:NO]; }
|
|
|
-
|
|
|
-#pragma mark - Properties
|
|
|
-
|
|
|
-- (void)setCurrentPhotoIndex:(NSUInteger)index {
|
|
|
- // Validate
|
|
|
- NSUInteger photoCount = [self numberOfPhotos];
|
|
|
- if (photoCount == 0) {
|
|
|
- index = 0;
|
|
|
- } else {
|
|
|
- if (index >= photoCount)
|
|
|
- index = [self numberOfPhotos]-1;
|
|
|
- }
|
|
|
- _currentPageIndex = index;
|
|
|
- if ([self isViewLoaded]) {
|
|
|
- [self jumpToPageAtIndex:index animated:NO];
|
|
|
- if (!_viewIsActive)
|
|
|
- [self tilePages]; // Force tiling if view is not visible
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Misc
|
|
|
-
|
|
|
-- (void)doneButtonPressed:(id)sender {
|
|
|
-
|
|
|
- // Dismiss view controller
|
|
|
- if ([_delegate respondsToSelector:@selector(photoBrowserDidFinishPresentation:)]) {
|
|
|
- // Call delegate method and let them dismiss us
|
|
|
- [_delegate photoBrowserDidFinishPresentation:self];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Delete
|
|
|
-
|
|
|
-- (void)deleteButtonPressed:(id)sender {
|
|
|
-
|
|
|
- if ([self.delegate respondsToSelector:@selector(photoBrowser:deleteButtonPressedForPhotoAtIndex:deleteButton:)])
|
|
|
- [self.delegate photoBrowser:self deleteButtonPressedForPhotoAtIndex:_currentPageIndex deleteButton:self.deleteButton];
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Share
|
|
|
-
|
|
|
-- (void)shareButtonPressed:(id)sender {
|
|
|
-
|
|
|
- if ([self.delegate respondsToSelector:@selector(photoBrowser:shareButtonPressedForPhotoAtIndex:)])
|
|
|
- [self.delegate photoBrowser:self shareButtonPressedForPhotoAtIndex:_currentPageIndex];
|
|
|
-}
|
|
|
-
|
|
|
-#pragma mark - Actions
|
|
|
-
|
|
|
-- (void)actionButtonPressed:(id)sender {
|
|
|
-
|
|
|
- // Only react when image has loaded
|
|
|
- id <MWPhoto> photo = [self photoAtIndex:_currentPageIndex];
|
|
|
- if ([self numberOfPhotos] > 0 && [photo underlyingImage]) {
|
|
|
-
|
|
|
- // If they have defined a delegate method then just message them
|
|
|
- if ([self.delegate respondsToSelector:@selector(photoBrowser:actionButtonPressedForPhotoAtIndex:)]) {
|
|
|
-
|
|
|
- // Let delegate handle things
|
|
|
- [self.delegate photoBrowser:self actionButtonPressedForPhotoAtIndex:_currentPageIndex];
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- // Show activity view controller
|
|
|
- NSMutableArray *items = [NSMutableArray arrayWithObject:[photo underlyingImage]];
|
|
|
- if (photo.caption) {
|
|
|
- [items addObject:photo.caption];
|
|
|
- }
|
|
|
- self.activityViewController = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
|
|
|
-
|
|
|
- // Show
|
|
|
- typeof(self) __weak weakSelf = self;
|
|
|
- [self.activityViewController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
|
|
|
- weakSelf.activityViewController = nil;
|
|
|
- [weakSelf hideControlsAfterDelay];
|
|
|
- }];
|
|
|
- // iOS 8 - Set the Anchor Point for the popover
|
|
|
- if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8")) {
|
|
|
- self.activityViewController.popoverPresentationController.barButtonItem = _actionButton;
|
|
|
- }
|
|
|
- [self presentViewController:self.activityViewController animated:YES completion:nil];
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // Keep controls hidden
|
|
|
- [self setControlsHidden:NO animated:YES permanent:YES];
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-@end
|