├── .gitignore ├── ALMoviePlayerController.podspec ├── ALMoviePlayerController ├── ALAirplayView.h ├── ALAirplayView.m ├── ALButton.h ├── ALButton.m ├── ALMoviePlayerController.h ├── ALMoviePlayerController.m ├── ALMoviePlayerControls.h ├── ALMoviePlayerControls.m └── Images │ ├── movieBackward.png │ ├── movieBackward@2x.png │ ├── movieBackwardSelected.png │ ├── movieBackwardSelected@2x.png │ ├── movieEndFullscreen.png │ ├── movieEndFullscreen@2x.png │ ├── movieForward.png │ ├── movieForward@2x.png │ ├── movieForwardSelected.png │ ├── movieForwardSelected@2x.png │ ├── movieFullscreen.png │ ├── movieFullscreen@2x.png │ ├── moviePause.png │ ├── moviePause@2x.png │ ├── moviePlay.png │ └── moviePlay@2x.png ├── ALMoviePlayerControllerDemo ├── ALMoviePlayerController.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── ALMoviePlayerController │ ├── ALMoviePlayerController-Info.plist │ ├── ALMoviePlayerController-Prefix.pch │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── ViewController.h │ ├── ViewController.m │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m ├── ALMoviePlayerControllerTests │ ├── ALMoviePlayerControllerTests-Info.plist │ ├── ALMoviePlayerControllerTests.m │ └── en.lproj │ │ └── InfoPlist.strings └── popeye.mp4 ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .Trashes 3 | *.swp 4 | *.lock 5 | DerivedData/ 6 | build/ 7 | *.pbxuser 8 | *.mode1v3 9 | *.mode2v3 10 | *.perspectivev3 11 | !default.pbxuser 12 | !default.mode1v3 13 | !default.mode2v3 14 | !default.perspectivev3 15 | xcuserdata 16 | *.xcuserstate 17 | *.xccheckout 18 | *.moved-aside -------------------------------------------------------------------------------- /ALMoviePlayerController.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'ALMoviePlayerController' 3 | s.version = '0.3.0' 4 | s.summary = 'A drop-in replacement for MPMoviePlayerController that exposes the UI elements and allows for maximum customization.' 5 | s.homepage = 'https://github.com/alobi/ALMoviePlayerController' 6 | s.author = { 'Anthony Lobianco' => 'anthony@lobian.co' } 7 | s.license = 'MIT' 8 | s.platform = :ios, '5.0' 9 | s.requires_arc = true 10 | s.source = { :git => 'https://github.com/alobi/ALMoviePlayerController.git', :tag => s.version.to_s } 11 | s.source_files = 'ALMoviePlayerController/*.{h,m}' 12 | s.resources = 'ALMoviePlayerController/Images/*.{png}' 13 | s.frameworks = 'QuartzCore', 'MediaPlayer' 14 | end 15 | -------------------------------------------------------------------------------- /ALMoviePlayerController/ALAirplayView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ALAirplayView.h 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol ALAirplayViewDelegate 12 | @optional 13 | - (void)airplayButtonTouchedDown; 14 | - (void)airplayButtonTouchedUpInside; 15 | - (void)airplayButtonTouchedUpOutside; 16 | - (void)airplayButtonTouchFailed; 17 | @end 18 | 19 | @interface ALAirplayView : MPVolumeView 20 | 21 | @property (nonatomic, weak) id delegate; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ALMoviePlayerController/ALAirplayView.m: -------------------------------------------------------------------------------- 1 | // 2 | // ALAirplayView.m 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import "ALAirplayView.h" 10 | 11 | @interface ALAirplayView () 12 | 13 | @property (nonatomic, strong) UILongPressGestureRecognizer *pressGesture; 14 | 15 | @end 16 | 17 | @implementation ALAirplayView 18 | 19 | - (id)init { 20 | if ( self = [super init] ) { 21 | [self setShowsRouteButton:YES]; 22 | [self setShowsVolumeSlider:NO]; 23 | 24 | _pressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(pressed:)]; 25 | _pressGesture.minimumPressDuration = 0.01; 26 | _pressGesture.cancelsTouchesInView = NO; 27 | [self addGestureRecognizer:_pressGesture]; 28 | } 29 | return self; 30 | } 31 | 32 | - (void)pressed:(UILongPressGestureRecognizer *)gesture { 33 | if (gesture.state == UIGestureRecognizerStateBegan) { 34 | if ([self.delegate respondsToSelector:@selector(airplayButtonTouchedDown)]) { 35 | [self.delegate airplayButtonTouchedDown]; 36 | } 37 | } 38 | else if (gesture.state == UIGestureRecognizerStateEnded) { 39 | CGPoint point = [gesture locationInView:self]; 40 | if (CGRectContainsPoint((CGRect){.origin=CGPointMake(0, 0), .size=self.frame.size}, point)) { 41 | if ([self.delegate respondsToSelector:@selector(airplayButtonTouchedUpInside)]) { 42 | [self.delegate airplayButtonTouchedUpInside]; 43 | } 44 | } else { 45 | if ([self.delegate respondsToSelector:@selector(airplayButtonTouchedUpOutside)]) { 46 | [self.delegate airplayButtonTouchedUpOutside]; 47 | } 48 | } 49 | } 50 | else if (gesture.state == UIGestureRecognizerStateCancelled || gesture.state == UIGestureRecognizerStateFailed) { 51 | if ([self.delegate respondsToSelector:@selector(airplayButtonTouchFailed)]) { 52 | [self.delegate airplayButtonTouchFailed]; 53 | } 54 | } 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /ALMoviePlayerController/ALButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // ALButton.h 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol ALButtonDelegate 12 | @optional 13 | - (void)buttonTouchedDown:(UIButton *)button; 14 | - (void)buttonTouchedUpOutside:(UIButton *)button; 15 | - (void)buttonTouchCancelled:(UIButton *)button; 16 | @end 17 | 18 | @interface ALButton : UIButton 19 | 20 | @property (nonatomic, weak) id delegate; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ALMoviePlayerController/ALButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // ALButton.m 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import "ALButton.h" 10 | 11 | static const CGFloat expandedMargin = 10.f; 12 | 13 | @implementation ALButton 14 | 15 | - (id)init { 16 | if ( self = [super init] ) { 17 | self.showsTouchWhenHighlighted = YES; 18 | [self addTarget:self action:@selector(touchedDown:) forControlEvents:UIControlEventTouchDown]; 19 | [self addTarget:self action:@selector(touchedUpOutside:) forControlEvents:UIControlEventTouchUpOutside]; 20 | [self addTarget:self action:@selector(touchCancelled:) forControlEvents:UIControlEventTouchCancel]; 21 | } 22 | return self; 23 | } 24 | 25 | - (void)touchedDown:(UIButton *)button { 26 | if ([self.delegate respondsToSelector:@selector(buttonTouchedDown:)]) { 27 | [self.delegate buttonTouchedDown:self]; 28 | } 29 | } 30 | 31 | - (void)touchedUpOutside:(UIButton *)button { 32 | if ([self.delegate respondsToSelector:@selector(buttonTouchedUpOutside:)]) { 33 | [self.delegate buttonTouchedUpOutside:self]; 34 | } 35 | } 36 | 37 | - (void)touchCancelled:(UIButton *)button { 38 | if ([self.delegate respondsToSelector:@selector(buttonTouchCancelled:)]) { 39 | [self.delegate buttonTouchCancelled:self]; 40 | } 41 | } 42 | 43 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { 44 | CGRect expandedFrame = CGRectMake(0 - expandedMargin , 0 - expandedMargin , self.frame.size.width + (expandedMargin * 2) , self.frame.size.height + (expandedMargin * 2)); 45 | return (CGRectContainsPoint(expandedFrame, point) == 1) ? self : nil; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /ALMoviePlayerController/ALMoviePlayerController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ALMoviePlayerController.h 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "ALMoviePlayerControls.h" 11 | 12 | static NSString * const ALMoviePlayerContentURLDidChangeNotification = @"ALMoviePlayerContentURLDidChangeNotification"; 13 | 14 | @protocol ALMoviePlayerControllerDelegate 15 | @optional 16 | - (void)movieTimedOut; 17 | @required 18 | - (void)moviePlayerWillMoveFromWindow; 19 | @end 20 | 21 | @interface ALMoviePlayerController : MPMoviePlayerController 22 | 23 | - (void)setFrame:(CGRect)frame; 24 | - (id)initWithFrame:(CGRect)frame; 25 | 26 | @property (nonatomic, weak) id delegate; 27 | @property (nonatomic, strong) ALMoviePlayerControls *controls; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /ALMoviePlayerController/ALMoviePlayerController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ALMoviePlayerController.m 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import "ALMoviePlayerController.h" 10 | 11 | @implementation UIDevice (ALSystemVersion) 12 | 13 | + (float)iOSVersion { 14 | static float version = 0.f; 15 | static dispatch_once_t onceToken; 16 | dispatch_once(&onceToken, ^{ 17 | version = [[[UIDevice currentDevice] systemVersion] floatValue]; 18 | }); 19 | return version; 20 | } 21 | 22 | @end 23 | 24 | @implementation UIApplication (ALAppDimensions) 25 | 26 | + (CGSize)sizeInOrientation:(UIInterfaceOrientation)orientation { 27 | CGSize size = [UIScreen mainScreen].bounds.size; 28 | UIApplication *application = [UIApplication sharedApplication]; 29 | if (UIInterfaceOrientationIsLandscape(orientation)) { 30 | size = CGSizeMake(size.height, size.width); 31 | } 32 | if (!application.statusBarHidden && [UIDevice iOSVersion] < 7.0) { 33 | size.height -= MIN(application.statusBarFrame.size.width, application.statusBarFrame.size.height); 34 | } 35 | return size; 36 | } 37 | 38 | @end 39 | 40 | static const CGFloat movieBackgroundPadding = 20.f; //if we don't pad the movie's background view, the edges will appear jagged when rotating 41 | static const NSTimeInterval fullscreenAnimationDuration = 0.3; 42 | 43 | @interface ALMoviePlayerController () 44 | 45 | @property (nonatomic, strong) UIView *movieBackgroundView; 46 | @property (nonatomic, readwrite) BOOL movieFullscreen; //used to manipulate default fullscreen property 47 | 48 | @end 49 | 50 | @implementation ALMoviePlayerController 51 | 52 | # pragma mark - Construct/Destruct 53 | 54 | - (id)init { 55 | return [self initWithFrame:CGRectZero]; 56 | } 57 | 58 | - (id)initWithContentURL:(NSURL *)url { 59 | [[NSException exceptionWithName:@"ALMoviePlayerController Exception" reason:@"Set contentURL after initialization." userInfo:nil] raise]; 60 | return nil; 61 | } 62 | 63 | - (id)initWithFrame:(CGRect)frame { 64 | if ( (self = [super init]) ) { 65 | 66 | self.view.frame = frame; 67 | self.view.backgroundColor = [UIColor blackColor]; 68 | [self setControlStyle:MPMovieControlStyleNone]; 69 | 70 | _movieFullscreen = NO; 71 | 72 | if (!_movieBackgroundView) { 73 | _movieBackgroundView = [[UIView alloc] init]; 74 | _movieBackgroundView.alpha = 0.f; 75 | [_movieBackgroundView setBackgroundColor:[UIColor blackColor]]; 76 | } 77 | } 78 | return self; 79 | } 80 | 81 | - (void)dealloc { 82 | _delegate = nil; 83 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 84 | } 85 | 86 | # pragma mark - Getters 87 | 88 | - (BOOL)isFullscreen { 89 | return _movieFullscreen; 90 | } 91 | 92 | - (CGFloat)statusBarHeightInOrientation:(UIInterfaceOrientation)orientation { 93 | if ([UIDevice iOSVersion] >= 7.0) 94 | return 0.f; 95 | else if ([UIApplication sharedApplication].statusBarHidden) 96 | return 0.f; 97 | return 20.f; 98 | } 99 | 100 | # pragma mark - Setters 101 | 102 | - (void)setContentURL:(NSURL *)contentURL { 103 | if (!_controls) { 104 | [[NSException exceptionWithName:@"ALMoviePlayerController Exception" reason:@"Set contentURL after setting controls." userInfo:nil] raise]; 105 | } 106 | [super setContentURL:contentURL]; 107 | [[NSNotificationCenter defaultCenter] postNotificationName:ALMoviePlayerContentURLDidChangeNotification object:nil]; 108 | [self play]; 109 | } 110 | 111 | - (void)setControls:(ALMoviePlayerControls *)controls { 112 | if (_controls != controls) { 113 | _controls = controls; 114 | _controls.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); 115 | [self.view addSubview:_controls]; 116 | } 117 | } 118 | 119 | - (void)setFrame:(CGRect)frame { 120 | [self.view setFrame:frame]; 121 | [self.controls setFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)]; 122 | } 123 | 124 | - (void)setFullscreen:(BOOL)fullscreen { 125 | [self setFullscreen:fullscreen animated:NO]; 126 | } 127 | 128 | - (void)setFullscreen:(BOOL)fullscreen animated:(BOOL)animated { 129 | _movieFullscreen = fullscreen; 130 | if (fullscreen) { 131 | [[NSNotificationCenter defaultCenter] postNotificationName:MPMoviePlayerWillEnterFullscreenNotification object:nil]; 132 | UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; 133 | if (!keyWindow) { 134 | keyWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:0]; 135 | } 136 | if (CGRectEqualToRect(self.movieBackgroundView.frame, CGRectZero)) { 137 | [self.movieBackgroundView setFrame:keyWindow.bounds]; 138 | } 139 | [keyWindow addSubview:self.movieBackgroundView]; 140 | [UIView animateWithDuration:animated ? fullscreenAnimationDuration : 0.0 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{ 141 | self.movieBackgroundView.alpha = 1.f; 142 | } completion:^(BOOL finished) { 143 | self.view.alpha = 0.f; 144 | [self.movieBackgroundView addSubview:self.view]; 145 | UIInterfaceOrientation currentOrientation = [[UIApplication sharedApplication] statusBarOrientation]; 146 | [self rotateMoviePlayerForOrientation:currentOrientation animated:NO completion:^{ 147 | [UIView animateWithDuration:animated ? fullscreenAnimationDuration : 0.0 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{ 148 | self.view.alpha = 1.f; 149 | } completion:^(BOOL finished) { 150 | [[NSNotificationCenter defaultCenter] postNotificationName:MPMoviePlayerDidEnterFullscreenNotification object:nil]; 151 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarOrientationWillChange:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; 152 | }]; 153 | }]; 154 | }]; 155 | 156 | } else { 157 | [[NSNotificationCenter defaultCenter] postNotificationName:MPMoviePlayerWillExitFullscreenNotification object:nil]; 158 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; 159 | [UIView animateWithDuration:animated ? fullscreenAnimationDuration : 0.0 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{ 160 | self.view.alpha = 0.f; 161 | } completion:^(BOOL finished) { 162 | if ([self.delegate respondsToSelector:@selector(moviePlayerWillMoveFromWindow)]) { 163 | [self.delegate moviePlayerWillMoveFromWindow]; 164 | } 165 | self.view.alpha = 1.f; 166 | [UIView animateWithDuration:animated ? fullscreenAnimationDuration : 0.0 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{ 167 | self.movieBackgroundView.alpha = 0.f; 168 | } completion:^(BOOL finished) { 169 | [self.movieBackgroundView removeFromSuperview]; 170 | [[NSNotificationCenter defaultCenter] postNotificationName:MPMoviePlayerDidExitFullscreenNotification object:nil]; 171 | }]; 172 | }]; 173 | } 174 | } 175 | 176 | #pragma mark - Notifications 177 | 178 | - (void)videoLoadStateChanged:(NSNotification *)note { 179 | switch (self.loadState) { 180 | case MPMovieLoadStatePlayable: 181 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(movieTimedOut) object:nil]; 182 | [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; 183 | default: 184 | break; 185 | } 186 | } 187 | 188 | - (void)statusBarOrientationWillChange:(NSNotification *)note { 189 | UIInterfaceOrientation orientation = (UIInterfaceOrientation)[[[note userInfo] objectForKey:UIApplicationStatusBarOrientationUserInfoKey] integerValue]; 190 | [self rotateMoviePlayerForOrientation:orientation animated:YES completion:nil]; 191 | } 192 | 193 | - (void)rotateMoviePlayerForOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void (^)(void))completion { 194 | CGFloat angle; 195 | CGSize windowSize = [UIApplication sizeInOrientation:orientation]; 196 | CGRect backgroundFrame; 197 | CGRect movieFrame; 198 | switch (orientation) { 199 | case UIInterfaceOrientationPortraitUpsideDown: 200 | angle = M_PI; 201 | backgroundFrame = CGRectMake(-movieBackgroundPadding, -movieBackgroundPadding, windowSize.width + movieBackgroundPadding*2, windowSize.height + movieBackgroundPadding*2); 202 | movieFrame = CGRectMake(movieBackgroundPadding, movieBackgroundPadding, backgroundFrame.size.width - movieBackgroundPadding*2, backgroundFrame.size.height - movieBackgroundPadding*2); 203 | break; 204 | case UIInterfaceOrientationLandscapeLeft: 205 | angle = - M_PI_2; 206 | backgroundFrame = CGRectMake([self statusBarHeightInOrientation:orientation] - movieBackgroundPadding, -movieBackgroundPadding, windowSize.height + movieBackgroundPadding*2, windowSize.width + movieBackgroundPadding*2); 207 | movieFrame = CGRectMake(movieBackgroundPadding, movieBackgroundPadding, backgroundFrame.size.height - movieBackgroundPadding*2, backgroundFrame.size.width - movieBackgroundPadding*2); 208 | break; 209 | case UIInterfaceOrientationLandscapeRight: 210 | angle = M_PI_2; 211 | backgroundFrame = CGRectMake(-movieBackgroundPadding, -movieBackgroundPadding, windowSize.height + movieBackgroundPadding*2, windowSize.width + movieBackgroundPadding*2); 212 | movieFrame = CGRectMake(movieBackgroundPadding, movieBackgroundPadding, backgroundFrame.size.height - movieBackgroundPadding*2, backgroundFrame.size.width - movieBackgroundPadding*2); 213 | break; 214 | case UIInterfaceOrientationPortrait: 215 | default: 216 | angle = 0.f; 217 | backgroundFrame = CGRectMake(-movieBackgroundPadding, [self statusBarHeightInOrientation:orientation] - movieBackgroundPadding, windowSize.width + movieBackgroundPadding*2, windowSize.height + movieBackgroundPadding*2); 218 | movieFrame = CGRectMake(movieBackgroundPadding, movieBackgroundPadding, backgroundFrame.size.width - movieBackgroundPadding*2, backgroundFrame.size.height - movieBackgroundPadding*2); 219 | break; 220 | } 221 | 222 | if (animated) { 223 | [UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 224 | self.movieBackgroundView.transform = CGAffineTransformMakeRotation(angle); 225 | self.movieBackgroundView.frame = backgroundFrame; 226 | [self setFrame:movieFrame]; 227 | } completion:^(BOOL finished) { 228 | if (completion) 229 | completion(); 230 | }]; 231 | } else { 232 | self.movieBackgroundView.transform = CGAffineTransformMakeRotation(angle); 233 | self.movieBackgroundView.frame = backgroundFrame; 234 | [self setFrame:movieFrame]; 235 | if (completion) 236 | completion(); 237 | } 238 | } 239 | 240 | # pragma mark - Internal Methods 241 | 242 | - (void)play { 243 | [super play]; 244 | 245 | //remote file 246 | if (![self.contentURL.scheme isEqualToString:@"file"] && self.loadState == MPMovieLoadStateUnknown) { 247 | [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; 248 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(movieTimedOut) object:nil]; 249 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(videoLoadStateChanged:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; 250 | [self performSelector:@selector(movieTimedOut) withObject:nil afterDelay:20.f]; 251 | } 252 | } 253 | 254 | -(void)movieTimedOut { 255 | if (!(self.loadState & MPMovieLoadStatePlayable) || !(self.loadState & MPMovieLoadStatePlaythroughOK)) { 256 | if ([self.delegate respondsToSelector:@selector(movieTimedOut)]) { 257 | [self.delegate performSelector:@selector(movieTimedOut)]; 258 | } 259 | } 260 | } 261 | 262 | @end 263 | -------------------------------------------------------------------------------- /ALMoviePlayerController/ALMoviePlayerControls.h: -------------------------------------------------------------------------------- 1 | // 2 | // ALMoviePlayerControls.h 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @class ALMoviePlayerController; 13 | 14 | typedef enum { 15 | /** Controls will appear in a bottom bar */ 16 | ALMoviePlayerControlsStyleEmbedded, 17 | 18 | /** Controls will appear in a top bar and bottom bar */ 19 | ALMoviePlayerControlsStyleFullscreen, 20 | 21 | /** Controls will appear as ALMoviePlayerControlsStyleFullscreen when in fullscreen and ALMoviePlayerControlsStyleEmbedded at all other times */ 22 | ALMoviePlayerControlsStyleDefault, 23 | 24 | /** Controls will not appear */ 25 | ALMoviePlayerControlsStyleNone, 26 | 27 | } ALMoviePlayerControlsStyle; 28 | 29 | typedef enum { 30 | /** Controls are not doing anything */ 31 | ALMoviePlayerControlsStateIdle, 32 | 33 | /** Controls are waiting for movie to finish loading */ 34 | ALMoviePlayerControlsStateLoading, 35 | 36 | /** Controls are ready to play and/or playing */ 37 | ALMoviePlayerControlsStateReady, 38 | 39 | } ALMoviePlayerControlsState; 40 | 41 | @interface ALMoviePlayerControls : UIView 42 | 43 | /** 44 | The style of the controls. Can be changed on the fly. 45 | 46 | Default value is ALMoviePlayerControlsStyleDefault 47 | */ 48 | @property (nonatomic, assign) ALMoviePlayerControlsStyle style; 49 | 50 | /** 51 | The state of the controls. 52 | */ 53 | @property (nonatomic, readonly) ALMoviePlayerControlsState state; 54 | 55 | /** 56 | The color of the control bars. 57 | 58 | Default value is black with a hint of transparency. 59 | */ 60 | @property (nonatomic, strong) UIColor *barColor; 61 | 62 | /** 63 | The height of the control bars. 64 | 65 | Default value is 70.f for iOS7+ and 50.f for previous versions. 66 | */ 67 | @property (nonatomic, assign) CGFloat barHeight; 68 | 69 | /** 70 | The amount of time that the controls should stay on screen before automatically hiding. 71 | 72 | Default value is 5 seconds. 73 | */ 74 | @property (nonatomic, assign) NSTimeInterval fadeDelay; 75 | 76 | /** 77 | The rate at which the movie should fastforward or rewind. 78 | 79 | Default value is 3x. 80 | */ 81 | @property (nonatomic, assign) float seekRate; 82 | 83 | /** 84 | Should the time-remaining number decrement as the video plays? 85 | 86 | Default value is NO. 87 | */ 88 | @property (nonatomic) BOOL timeRemainingDecrements; 89 | 90 | /** 91 | Are the controls currently showing on screen? 92 | */ 93 | @property (nonatomic, readonly, getter = isShowing) BOOL showing; 94 | 95 | 96 | /** 97 | The default initializer method. The parameter may not be nil. 98 | */ 99 | - (id)initWithMoviePlayer:(ALMoviePlayerController *)moviePlayer style:(ALMoviePlayerControlsStyle)style; 100 | 101 | @end -------------------------------------------------------------------------------- /ALMoviePlayerController/ALMoviePlayerControls.m: -------------------------------------------------------------------------------- 1 | // 2 | // ALMoviePlayerControls.m 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import "ALMoviePlayerControls.h" 10 | #import "ALMoviePlayerController.h" 11 | #import "ALAirplayView.h" 12 | #import "ALButton.h" 13 | #import 14 | #import 15 | 16 | @implementation UIDevice (ALSystemVersion) 17 | 18 | + (float)iOSVersion { 19 | static float version = 0.f; 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | version = [[[UIDevice currentDevice] systemVersion] floatValue]; 23 | }); 24 | return version; 25 | } 26 | 27 | @end 28 | 29 | @interface ALMoviePlayerControlsBar : UIView 30 | 31 | @property (nonatomic, strong) UIColor *color; 32 | 33 | @end 34 | 35 | static const inline BOOL isIpad() { 36 | return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad; 37 | } 38 | static const CGFloat activityIndicatorSize = 40.f; 39 | static const CGFloat iPhoneScreenPortraitWidth = 320.f; 40 | 41 | @interface ALMoviePlayerControls () { 42 | @private 43 | int windowSubviews; 44 | } 45 | 46 | @property (nonatomic, weak) ALMoviePlayerController *moviePlayer; 47 | @property (nonatomic, assign) ALMoviePlayerControlsState state; 48 | @property (nonatomic, getter = isShowing) BOOL showing; 49 | 50 | @property (nonatomic, strong) NSTimer *durationTimer; 51 | 52 | @property (nonatomic, strong) UIView *activityBackgroundView; 53 | @property (nonatomic, strong) UIActivityIndicatorView *activityIndicator; 54 | 55 | @property (nonatomic, strong) ALMoviePlayerControlsBar *topBar; 56 | @property (nonatomic, strong) ALMoviePlayerControlsBar *bottomBar; 57 | @property (nonatomic, strong) UISlider *durationSlider; 58 | @property (nonatomic, strong) ALButton *playPauseButton; 59 | @property (nonatomic, strong) MPVolumeView *volumeView; 60 | @property (nonatomic, strong) ALAirplayView *airplayView; 61 | @property (nonatomic, strong) ALButton *fullscreenButton; 62 | @property (nonatomic, strong) UILabel *timeElapsedLabel; 63 | @property (nonatomic, strong) UILabel *timeRemainingLabel; 64 | @property (nonatomic, strong) ALButton *seekForwardButton; 65 | @property (nonatomic, strong) ALButton *seekBackwardButton; 66 | @property (nonatomic, strong) ALButton *scaleButton; 67 | 68 | @end 69 | 70 | @implementation ALMoviePlayerControls 71 | 72 | # pragma mark - Construct/Destruct 73 | 74 | - (id)initWithMoviePlayer:(ALMoviePlayerController *)moviePlayer style:(ALMoviePlayerControlsStyle)style { 75 | self = [super init]; 76 | if (self) { 77 | self.backgroundColor = [UIColor clearColor]; 78 | 79 | _moviePlayer = moviePlayer; 80 | _style = style; 81 | _showing = NO; 82 | _fadeDelay = 5.0; 83 | _timeRemainingDecrements = NO; 84 | _barColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; 85 | 86 | //in fullscreen mode, move controls away from top status bar and bottom screen bezel. I think the iOS7 control center gestures interfere with the uibutton touch events. this will alleviate that a little (correct me if I'm wrong and/or adjust if necessary). 87 | _barHeight = [UIDevice iOSVersion] >= 7.0 ? 70.f : 50.f; 88 | 89 | _seekRate = 3.f; 90 | _state = ALMoviePlayerControlsStateIdle; 91 | 92 | [self setup]; 93 | [self addNotifications]; 94 | } 95 | return self; 96 | } 97 | 98 | - (void)dealloc { 99 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 100 | [_durationTimer invalidate]; 101 | [self nilDelegates]; 102 | } 103 | 104 | # pragma mark - Construct/Destruct Helpers 105 | 106 | - (void)setup { 107 | if (self.style == ALMoviePlayerControlsStyleNone) 108 | return; 109 | 110 | //top bar 111 | _topBar = [[ALMoviePlayerControlsBar alloc] init]; 112 | _topBar.color = _barColor; 113 | _topBar.alpha = 0.f; 114 | [self addSubview:_topBar]; 115 | 116 | //bottom bar 117 | _bottomBar = [[ALMoviePlayerControlsBar alloc] init]; 118 | _bottomBar.color = _barColor; 119 | _bottomBar.alpha = 0.f; 120 | [self addSubview:_bottomBar]; 121 | 122 | _durationSlider = [[UISlider alloc] init]; 123 | _durationSlider.value = 0.f; 124 | _durationSlider.continuous = YES; 125 | [_durationSlider addTarget:self action:@selector(durationSliderValueChanged:) forControlEvents:UIControlEventValueChanged]; 126 | [_durationSlider addTarget:self action:@selector(durationSliderTouchBegan:) forControlEvents:UIControlEventTouchDown]; 127 | [_durationSlider addTarget:self action:@selector(durationSliderTouchEnded:) forControlEvents:UIControlEventTouchUpInside]; 128 | [_durationSlider addTarget:self action:@selector(durationSliderTouchEnded:) forControlEvents:UIControlEventTouchUpOutside]; 129 | 130 | _timeElapsedLabel = [[UILabel alloc] init]; 131 | _timeElapsedLabel.backgroundColor = [UIColor clearColor]; 132 | _timeElapsedLabel.font = [UIFont systemFontOfSize:12.f]; 133 | _timeElapsedLabel.textColor = [UIColor lightTextColor]; 134 | _timeElapsedLabel.textAlignment = NSTextAlignmentRight; 135 | _timeElapsedLabel.text = @"0:00"; 136 | _timeElapsedLabel.layer.shadowColor = [UIColor blackColor].CGColor; 137 | _timeElapsedLabel.layer.shadowRadius = 1.f; 138 | _timeElapsedLabel.layer.shadowOffset = CGSizeMake(1.f, 1.f); 139 | _timeElapsedLabel.layer.shadowOpacity = 0.8f; 140 | 141 | _timeRemainingLabel = [[UILabel alloc] init]; 142 | _timeRemainingLabel.backgroundColor = [UIColor clearColor]; 143 | _timeRemainingLabel.font = [UIFont systemFontOfSize:12.f]; 144 | _timeRemainingLabel.textColor = [UIColor lightTextColor]; 145 | _timeRemainingLabel.textAlignment = NSTextAlignmentLeft; 146 | _timeRemainingLabel.text = @"0:00"; 147 | _timeRemainingLabel.layer.shadowColor = [UIColor blackColor].CGColor; 148 | _timeRemainingLabel.layer.shadowRadius = 1.f; 149 | _timeRemainingLabel.layer.shadowOffset = CGSizeMake(1.f, 1.f); 150 | _timeRemainingLabel.layer.shadowOpacity = 0.8f; 151 | 152 | if (_style == ALMoviePlayerControlsStyleFullscreen || (_style == ALMoviePlayerControlsStyleDefault && _moviePlayer.isFullscreen)) { 153 | [_topBar addSubview:_durationSlider]; 154 | [_topBar addSubview:_timeElapsedLabel]; 155 | [_topBar addSubview:_timeRemainingLabel]; 156 | 157 | _fullscreenButton = [[ALButton alloc] init]; 158 | [_fullscreenButton setTitle:@"Done" forState:UIControlStateNormal]; 159 | [_fullscreenButton setTitleShadowColor:[UIColor blackColor] forState:UIControlStateNormal]; 160 | _fullscreenButton.titleLabel.shadowOffset = CGSizeMake(1.f, 1.f); 161 | [_fullscreenButton.titleLabel setFont:[UIFont systemFontOfSize:14.f]]; 162 | _fullscreenButton.delegate = self; 163 | [_fullscreenButton addTarget:self action:@selector(fullscreenPressed:) forControlEvents:UIControlEventTouchUpInside]; 164 | [_topBar addSubview:_fullscreenButton]; 165 | 166 | _scaleButton = [[ALButton alloc] init]; 167 | _scaleButton.delegate = self; 168 | [_scaleButton setImage:[UIImage imageNamed:@"movieFullscreen.png"] forState:UIControlStateNormal]; 169 | [_scaleButton setImage:[UIImage imageNamed:@"movieEndFullscreen.png"] forState:UIControlStateSelected]; 170 | [_scaleButton addTarget:self action:@selector(scalePressed:) forControlEvents:UIControlEventTouchUpInside]; 171 | [_topBar addSubview:_scaleButton]; 172 | 173 | _volumeView = [[MPVolumeView alloc] init]; 174 | [_volumeView setShowsRouteButton:NO]; 175 | [_volumeView setShowsVolumeSlider:YES]; 176 | [_bottomBar addSubview:_volumeView]; 177 | 178 | _seekForwardButton = [[ALButton alloc] init]; 179 | [_seekForwardButton setImage:[UIImage imageNamed:@"movieForward.png"] forState:UIControlStateNormal]; 180 | [_seekForwardButton setImage:[UIImage imageNamed:@"movieForwardSelected.png"] forState:UIControlStateSelected]; 181 | _seekForwardButton.delegate = self; 182 | [_seekForwardButton addTarget:self action:@selector(seekForwardPressed:) forControlEvents:UIControlEventTouchUpInside]; 183 | [_bottomBar addSubview:_seekForwardButton]; 184 | 185 | _seekBackwardButton = [[ALButton alloc] init]; 186 | [_seekBackwardButton setImage:[UIImage imageNamed:@"movieBackward.png"] forState:UIControlStateNormal]; 187 | [_seekBackwardButton setImage:[UIImage imageNamed:@"movieBackwardSelected.png"] forState:UIControlStateSelected]; 188 | _seekBackwardButton.delegate = self; 189 | [_seekBackwardButton addTarget:self action:@selector(seekBackwardPressed:) forControlEvents:UIControlEventTouchUpInside]; 190 | [_bottomBar addSubview:_seekBackwardButton]; 191 | } 192 | 193 | else if (_style == ALMoviePlayerControlsStyleEmbedded || (_style == ALMoviePlayerControlsStyleDefault && !_moviePlayer.isFullscreen)) { 194 | [_bottomBar addSubview:_durationSlider]; 195 | [_bottomBar addSubview:_timeElapsedLabel]; 196 | [_bottomBar addSubview:_timeRemainingLabel]; 197 | 198 | _fullscreenButton = [[ALButton alloc] init]; 199 | [_fullscreenButton setImage:[UIImage imageNamed:@"movieFullscreen.png"] forState:UIControlStateNormal]; 200 | [_fullscreenButton addTarget:self action:@selector(fullscreenPressed:) forControlEvents:UIControlEventTouchUpInside]; 201 | _fullscreenButton.delegate = self; 202 | [_bottomBar addSubview:_fullscreenButton]; 203 | } 204 | 205 | //static stuff 206 | _playPauseButton = [[ALButton alloc] init]; 207 | [_playPauseButton setImage:[UIImage imageNamed:@"moviePause.png"] forState:UIControlStateNormal]; 208 | [_playPauseButton setImage:[UIImage imageNamed:@"moviePlay.png"] forState:UIControlStateSelected]; 209 | [_playPauseButton setSelected:_moviePlayer.playbackState == MPMoviePlaybackStatePlaying ? NO : YES]; 210 | [_playPauseButton addTarget:self action:@selector(playPausePressed:) forControlEvents:UIControlEventTouchUpInside]; 211 | _playPauseButton.delegate = self; 212 | [_bottomBar addSubview:_playPauseButton]; 213 | 214 | _airplayView = [[ALAirplayView alloc] init]; 215 | _airplayView.delegate = self; 216 | [_bottomBar addSubview:_airplayView]; 217 | 218 | _activityBackgroundView = [[UIView alloc] init]; 219 | [_activityBackgroundView setBackgroundColor:[UIColor blackColor]]; 220 | _activityBackgroundView.alpha = 0.f; 221 | 222 | _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; 223 | _activityIndicator.alpha = 0.f; 224 | _activityIndicator.hidesWhenStopped = YES; 225 | } 226 | 227 | - (void)resetViews { 228 | [self stopDurationTimer]; 229 | [self nilDelegates]; 230 | [_activityBackgroundView removeFromSuperview]; 231 | [_activityIndicator removeFromSuperview]; 232 | [_topBar removeFromSuperview]; 233 | [_bottomBar removeFromSuperview]; 234 | } 235 | 236 | - (void)nilDelegates { 237 | _airplayView.delegate = nil; 238 | _playPauseButton.delegate = nil; 239 | _fullscreenButton.delegate = nil; 240 | _seekForwardButton.delegate = nil; 241 | _seekBackwardButton.delegate = nil; 242 | _scaleButton.delegate = nil; 243 | } 244 | 245 | # pragma mark - Setters 246 | 247 | - (void)setStyle:(ALMoviePlayerControlsStyle)style { 248 | if (_style != style) { 249 | BOOL flag = _style == ALMoviePlayerControlsStyleDefault; 250 | [self hideControls:^{ 251 | [self resetViews]; 252 | _style = style; 253 | [self setup]; 254 | if (_style != ALMoviePlayerControlsStyleNone) { 255 | double delayInSeconds = 0.2; 256 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 257 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 258 | [self setDurationSliderMaxMinValues]; 259 | [self monitorMoviePlayback]; //resume values 260 | [self startDurationTimer]; 261 | [self showControls:^{ 262 | if (flag) { 263 | //put style back to default 264 | _style = ALMoviePlayerControlsStyleDefault; 265 | } 266 | }]; 267 | 268 | }); 269 | } else { 270 | if (flag) { 271 | //put style back to default 272 | _style = ALMoviePlayerControlsStyleDefault; 273 | } 274 | } 275 | }]; 276 | } 277 | } 278 | 279 | - (void)setState:(ALMoviePlayerControlsState)state { 280 | if (_state != state) { 281 | _state = state; 282 | 283 | switch (state) { 284 | case ALMoviePlayerControlsStateLoading: 285 | [self showLoadingIndicators]; 286 | break; 287 | case ALMoviePlayerControlsStateReady: 288 | [self hideLoadingIndicators]; 289 | break; 290 | case ALMoviePlayerControlsStateIdle: 291 | default: 292 | break; 293 | } 294 | } 295 | } 296 | 297 | - (void)setBarColor:(UIColor *)barColor { 298 | if (_barColor != barColor) { 299 | _barColor = barColor; 300 | [self.topBar setColor:barColor]; 301 | [self.bottomBar setColor:barColor]; 302 | } 303 | } 304 | 305 | # pragma mark - UIControl/Touch Events 306 | 307 | - (void)durationSliderTouchBegan:(UISlider *)slider { 308 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideControls:) object:nil]; 309 | [self.moviePlayer pause]; 310 | } 311 | 312 | - (void)durationSliderTouchEnded:(UISlider *)slider { 313 | [self.moviePlayer setCurrentPlaybackTime:floor(slider.value)]; 314 | [self.moviePlayer play]; 315 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 316 | } 317 | 318 | - (void)durationSliderValueChanged:(UISlider *)slider { 319 | double currentTime = floor(slider.value); 320 | double totalTime = floor(self.moviePlayer.duration); 321 | [self setTimeLabelValues:currentTime totalTime:totalTime]; 322 | } 323 | 324 | - (void)buttonTouchedDown:(UIButton *)button { 325 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideControls:) object:nil]; 326 | } 327 | 328 | - (void)buttonTouchedUpOutside:(UIButton *)button { 329 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 330 | } 331 | 332 | - (void)buttonTouchCancelled:(UIButton *)button { 333 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 334 | } 335 | 336 | - (void)airplayButtonTouchedDown { 337 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideControls:) object:nil]; 338 | } 339 | 340 | - (void)airplayButtonTouchedUpOutside { 341 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 342 | } 343 | 344 | - (void)airplayButtonTouchFailed { 345 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 346 | } 347 | 348 | - (void)airplayButtonTouchedUpInside { 349 | //TODO iphone 350 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideControls:) object:nil]; 351 | UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; 352 | if (!keyWindow) { 353 | keyWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:0]; 354 | } 355 | if (isIpad()) { 356 | windowSubviews = keyWindow.layer.sublayers.count; 357 | [keyWindow addObserver:self forKeyPath:@"layer.sublayers" options:NSKeyValueObservingOptionNew context:NULL]; 358 | } else { 359 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeKey:) name:UIWindowDidBecomeKeyNotification object:nil]; 360 | } 361 | } 362 | 363 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 364 | if (![keyPath isEqualToString:@"layer.sublayers"]) { 365 | return; 366 | } 367 | UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; 368 | if (!keyWindow) { 369 | keyWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:0]; 370 | } 371 | if (keyWindow.layer.sublayers.count == windowSubviews) { 372 | [keyWindow removeObserver:self forKeyPath:@"layer.sublayers"]; 373 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 374 | } 375 | } 376 | 377 | - (void)windowDidResignKey:(NSNotification *)note { 378 | UIWindow *resignedWindow = (UIWindow *)[note object]; 379 | if ([self isAirplayShowingInView:resignedWindow]) { 380 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIWindowDidResignKeyNotification object:nil]; 381 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 382 | } 383 | } 384 | 385 | - (void)windowDidBecomeKey:(NSNotification *)note { 386 | UIWindow *keyWindow = (UIWindow *)[note object]; 387 | if ([self isAirplayShowingInView:keyWindow]) { 388 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIWindowDidBecomeKeyNotification object:nil]; 389 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidResignKey:) name:UIWindowDidResignKeyNotification object:nil]; 390 | } 391 | } 392 | 393 | - (BOOL)isAirplayShowingInView:(UIView *)view { 394 | BOOL actionSheet = NO; 395 | for (UIView *subview in view.subviews) { 396 | if ([subview isKindOfClass:[UIActionSheet class]]) { 397 | actionSheet = YES; 398 | } else { 399 | actionSheet = [self isAirplayShowingInView:subview]; 400 | } 401 | } 402 | return actionSheet; 403 | } 404 | 405 | - (void)playPausePressed:(UIButton *)button { 406 | self.moviePlayer.playbackState == MPMoviePlaybackStatePlaying ? [self.moviePlayer pause] : [self.moviePlayer play]; 407 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 408 | } 409 | 410 | - (void)fullscreenPressed:(UIButton *)button { 411 | if (self.style == ALMoviePlayerControlsStyleDefault) { 412 | self.style = self.moviePlayer.isFullscreen ? ALMoviePlayerControlsStyleEmbedded : ALMoviePlayerControlsStyleFullscreen; 413 | } 414 | if (self.moviePlayer.currentPlaybackRate != 1.f) { 415 | self.moviePlayer.currentPlaybackRate = 1.f; 416 | } 417 | [self.moviePlayer setFullscreen:!self.moviePlayer.isFullscreen animated:YES]; 418 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 419 | } 420 | 421 | - (void)scalePressed:(UIButton *)button { 422 | button.selected = !button.selected; 423 | [self.moviePlayer setScalingMode:button.selected ? MPMovieScalingModeAspectFill : MPMovieScalingModeAspectFit]; 424 | } 425 | 426 | - (void)seekForwardPressed:(UIButton *)button { 427 | self.moviePlayer.currentPlaybackRate = !button.selected ? self.seekRate : 1.f; 428 | button.selected = !button.selected; 429 | self.seekBackwardButton.selected = NO; 430 | if (!button.selected) { 431 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 432 | } 433 | } 434 | 435 | - (void)seekBackwardPressed:(UIButton *)button { 436 | self.moviePlayer.currentPlaybackRate = !button.selected ? -self.seekRate : 1.f; 437 | button.selected = !button.selected; 438 | self.seekForwardButton.selected = NO; 439 | if (!button.selected) { 440 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 441 | } 442 | } 443 | 444 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 445 | if (self.style == ALMoviePlayerControlsStyleNone) 446 | return; 447 | } 448 | 449 | - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 450 | if (self.style == ALMoviePlayerControlsStyleNone) 451 | return; 452 | self.isShowing ? [self hideControls:nil] : [self showControls:nil]; 453 | } 454 | 455 | # pragma mark - Notifications 456 | 457 | - (void)addNotifications { 458 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlaybackStateDidChange:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil]; 459 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:nil]; 460 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieContentURLDidChange:) name:ALMoviePlayerContentURLDidChangeNotification object:nil]; 461 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieDurationAvailable:) name:MPMovieDurationAvailableNotification object:nil]; 462 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieLoadStateDidChange:) name:MPMoviePlayerLoadStateDidChangeNotification object:nil]; 463 | } 464 | 465 | - (void)movieFinished:(NSNotification *)note { 466 | self.playPauseButton.selected = YES; 467 | [self.durationTimer invalidate]; 468 | [self.moviePlayer setCurrentPlaybackTime:0.0]; 469 | [self monitorMoviePlayback]; //reset values 470 | [self hideControls:nil]; 471 | self.state = ALMoviePlayerControlsStateIdle; 472 | } 473 | 474 | - (void)movieLoadStateDidChange:(NSNotification *)note { 475 | switch (self.moviePlayer.loadState) { 476 | case MPMovieLoadStatePlayable: 477 | case MPMovieLoadStatePlaythroughOK: 478 | [self showControls:nil]; 479 | self.state = ALMoviePlayerControlsStateReady; 480 | break; 481 | case MPMovieLoadStateStalled: 482 | case MPMovieLoadStateUnknown: 483 | break; 484 | default: 485 | break; 486 | } 487 | } 488 | 489 | - (void)moviePlaybackStateDidChange:(NSNotification *)note { 490 | switch (self.moviePlayer.playbackState) { 491 | case MPMoviePlaybackStatePlaying: 492 | self.playPauseButton.selected = NO; 493 | [self startDurationTimer]; 494 | 495 | //local file 496 | if ([self.moviePlayer.contentURL.scheme isEqualToString:@"file"]) { 497 | [self setDurationSliderMaxMinValues]; 498 | [self showControls:nil]; 499 | } 500 | case MPMoviePlaybackStateSeekingBackward: 501 | case MPMoviePlaybackStateSeekingForward: 502 | self.state = ALMoviePlayerControlsStateReady; 503 | break; 504 | case MPMoviePlaybackStateInterrupted: 505 | self.state = ALMoviePlayerControlsStateLoading; 506 | break; 507 | case MPMoviePlaybackStatePaused: 508 | case MPMoviePlaybackStateStopped: 509 | self.state = ALMoviePlayerControlsStateIdle; 510 | self.playPauseButton.selected = YES; 511 | [self stopDurationTimer]; 512 | break; 513 | default: 514 | break; 515 | } 516 | } 517 | 518 | - (void)movieDurationAvailable:(NSNotification *)note { 519 | [self setDurationSliderMaxMinValues]; 520 | } 521 | 522 | - (void)movieContentURLDidChange:(NSNotification *)note { 523 | [self hideControls:^{ 524 | //don't show loading indicator for local files 525 | self.state = [self.moviePlayer.contentURL.scheme isEqualToString:@"file"] ? ALMoviePlayerControlsStateReady : ALMoviePlayerControlsStateLoading; 526 | }]; 527 | } 528 | 529 | # pragma mark - Internal Methods 530 | 531 | - (void)startDurationTimer { 532 | self.durationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(monitorMoviePlayback) userInfo:nil repeats:YES]; 533 | [[NSRunLoop currentRunLoop] addTimer:self.durationTimer forMode:NSDefaultRunLoopMode]; 534 | } 535 | 536 | - (void)stopDurationTimer { 537 | [self.durationTimer invalidate]; 538 | } 539 | 540 | - (void)showControls:(void(^)(void))completion { 541 | if (!self.isShowing) { 542 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideControls:) object:nil]; 543 | if (self.style == ALMoviePlayerControlsStyleFullscreen || (self.style == ALMoviePlayerControlsStyleDefault && self.moviePlayer.isFullscreen)) { 544 | [self.topBar setNeedsDisplay]; 545 | } 546 | [self.bottomBar setNeedsDisplay]; 547 | [UIView animateWithDuration:0.3 delay:0.0 options:0 animations:^{ 548 | if (self.style == ALMoviePlayerControlsStyleFullscreen || (self.style == ALMoviePlayerControlsStyleDefault && self.moviePlayer.isFullscreen)) { 549 | self.topBar.alpha = 1.f; 550 | } 551 | self.bottomBar.alpha = 1.f; 552 | } completion:^(BOOL finished) { 553 | _showing = YES; 554 | if (completion) 555 | completion(); 556 | [self performSelector:@selector(hideControls:) withObject:nil afterDelay:self.fadeDelay]; 557 | }]; 558 | } else { 559 | if (completion) 560 | completion(); 561 | } 562 | } 563 | 564 | - (void)hideControls:(void(^)(void))completion { 565 | if (self.isShowing) { 566 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hideControls:) object:nil]; 567 | [UIView animateWithDuration:0.3 delay:0.0 options:0 animations:^{ 568 | if (self.style == ALMoviePlayerControlsStyleFullscreen || (self.style == ALMoviePlayerControlsStyleDefault && self.moviePlayer.isFullscreen)) { 569 | self.topBar.alpha = 0.f; 570 | } 571 | self.bottomBar.alpha = 0.f; 572 | } completion:^(BOOL finished) { 573 | _showing = NO; 574 | if (completion) 575 | completion(); 576 | }]; 577 | } else { 578 | if (completion) 579 | completion(); 580 | } 581 | } 582 | 583 | - (void)showLoadingIndicators { 584 | [self addSubview:_activityBackgroundView]; 585 | [self addSubview:_activityIndicator]; 586 | [_activityIndicator startAnimating]; 587 | 588 | [UIView animateWithDuration:0.2f animations:^{ 589 | _activityBackgroundView.alpha = 1.f; 590 | _activityIndicator.alpha = 1.f; 591 | }]; 592 | } 593 | 594 | - (void)hideLoadingIndicators { 595 | [UIView animateWithDuration:0.2f delay:0.0 options:0 animations:^{ 596 | self.activityBackgroundView.alpha = 0.0f; 597 | self.activityIndicator.alpha = 0.f; 598 | } completion:^(BOOL finished) { 599 | [self.activityBackgroundView removeFromSuperview]; 600 | [self.activityIndicator removeFromSuperview]; 601 | }]; 602 | } 603 | 604 | - (void)setDurationSliderMaxMinValues { 605 | CGFloat duration = self.moviePlayer.duration; 606 | self.durationSlider.minimumValue = 0.f; 607 | self.durationSlider.maximumValue = duration; 608 | } 609 | 610 | - (void)setTimeLabelValues:(double)currentTime totalTime:(double)totalTime { 611 | double minutesElapsed = floor(currentTime / 60.0); 612 | double secondsElapsed = fmod(currentTime, 60.0); 613 | self.timeElapsedLabel.text = [NSString stringWithFormat:@"%.0f:%02.0f", minutesElapsed, secondsElapsed]; 614 | 615 | double minutesRemaining; 616 | double secondsRemaining; 617 | if (self.timeRemainingDecrements) { 618 | minutesRemaining = floor((totalTime - currentTime) / 60.0); 619 | secondsRemaining = fmod((totalTime - currentTime), 60.0); 620 | } else { 621 | minutesRemaining = floor(totalTime / 60.0); 622 | secondsRemaining = floor(fmod(totalTime, 60.0)); 623 | } 624 | self.timeRemainingLabel.text = self.timeRemainingDecrements ? [NSString stringWithFormat:@"-%.0f:%02.0f", minutesRemaining, secondsRemaining] : [NSString stringWithFormat:@"%.0f:%02.0f", minutesRemaining, secondsRemaining]; 625 | } 626 | 627 | - (void)monitorMoviePlayback { 628 | double currentTime = floor(self.moviePlayer.currentPlaybackTime); 629 | double totalTime = floor(self.moviePlayer.duration); 630 | [self setTimeLabelValues:currentTime totalTime:totalTime]; 631 | self.durationSlider.value = ceil(currentTime); 632 | } 633 | 634 | - (void)layoutSubviews { 635 | [super layoutSubviews]; 636 | 637 | if (self.style == ALMoviePlayerControlsStyleNone) 638 | return; 639 | 640 | //common sizes 641 | CGFloat paddingFromBezel = self.frame.size.width <= iPhoneScreenPortraitWidth ? 10.f : 20.f; 642 | CGFloat paddingBetweenButtons = self.frame.size.width <= iPhoneScreenPortraitWidth ? 10.f : 30.f; 643 | CGFloat paddingBetweenPlaybackButtons = self.frame.size.width <= iPhoneScreenPortraitWidth ? 20.f : 30.f; 644 | CGFloat paddingBetweenLabelsAndSlider = 10.f; 645 | CGFloat sliderHeight = 34.f; //default height 646 | CGFloat volumeHeight = 20.f; 647 | CGFloat volumeWidth = isIpad() ? 210.f : 120.f; 648 | CGFloat seekWidth = 36.f; 649 | CGFloat seekHeight = 20.f; 650 | CGFloat airplayWidth = 30.f; 651 | CGFloat airplayHeight = 22.f; 652 | CGFloat playWidth = 18.f; 653 | CGFloat playHeight = 22.f; 654 | CGFloat labelWidth = 30.f; 655 | 656 | if (self.style == ALMoviePlayerControlsStyleFullscreen || (self.style == ALMoviePlayerControlsStyleDefault && self.moviePlayer.isFullscreen)) { 657 | //top bar 658 | CGFloat fullscreenWidth = 34.f; 659 | CGFloat fullscreenHeight = self.barHeight; 660 | CGFloat scaleWidth = 28.f; 661 | CGFloat scaleHeight = 28.f; 662 | self.topBar.frame = CGRectMake(0, 0, self.frame.size.width, self.barHeight); 663 | self.fullscreenButton.frame = CGRectMake(paddingFromBezel, self.barHeight/2 - fullscreenHeight/2, fullscreenWidth, fullscreenHeight); 664 | self.timeElapsedLabel.frame = CGRectMake(self.fullscreenButton.frame.origin.x + self.fullscreenButton.frame.size.width + paddingBetweenButtons, 0, labelWidth, self.barHeight); 665 | self.scaleButton.frame = CGRectMake(self.topBar.frame.size.width - paddingFromBezel - scaleWidth, self.barHeight/2 - scaleHeight/2, scaleWidth, scaleHeight); 666 | self.timeRemainingLabel.frame = CGRectMake(self.scaleButton.frame.origin.x - paddingBetweenButtons - labelWidth, 0, labelWidth, self.barHeight); 667 | 668 | //bottom bar 669 | self.bottomBar.frame = CGRectMake(0, self.frame.size.height - self.barHeight, self.frame.size.width, self.barHeight); 670 | self.playPauseButton.frame = CGRectMake(self.bottomBar.frame.size.width/2 - playWidth/2, self.barHeight/2 - playHeight/2, playWidth, playHeight); 671 | self.seekForwardButton.frame = CGRectMake(self.playPauseButton.frame.origin.x + self.playPauseButton.frame.size.width + paddingBetweenPlaybackButtons, self.barHeight/2 - seekHeight/2 + 1.f, seekWidth, seekHeight); 672 | self.seekBackwardButton.frame = CGRectMake(self.playPauseButton.frame.origin.x - paddingBetweenPlaybackButtons - seekWidth, self.barHeight/2 - seekHeight/2 + 1.f, seekWidth, seekHeight); 673 | 674 | //hide volume view in iPhone's portrait orientation 675 | if (self.frame.size.width <= iPhoneScreenPortraitWidth) { 676 | self.volumeView.alpha = 0.f; 677 | } else { 678 | self.volumeView.alpha = 1.f; 679 | self.volumeView.frame = CGRectMake(paddingFromBezel, self.barHeight/2 - volumeHeight/2, volumeWidth, volumeHeight); 680 | } 681 | 682 | self.airplayView.frame = CGRectMake(self.bottomBar.frame.size.width - paddingFromBezel - airplayWidth, self.barHeight/2 - airplayHeight/2, airplayWidth, airplayHeight); 683 | } 684 | 685 | else if (self.style == ALMoviePlayerControlsStyleEmbedded || (self.style == ALMoviePlayerControlsStyleDefault && !self.moviePlayer.isFullscreen)) { 686 | self.bottomBar.frame = CGRectMake(0, self.frame.size.height - self.barHeight, self.frame.size.width, self.barHeight); 687 | 688 | //left side of bottom bar 689 | self.playPauseButton.frame = CGRectMake(paddingFromBezel, self.barHeight/2 - playHeight/2, playWidth, playHeight); 690 | self.timeElapsedLabel.frame = CGRectMake(self.playPauseButton.frame.origin.x + self.playPauseButton.frame.size.width + paddingBetweenButtons, 0, labelWidth, self.barHeight); 691 | 692 | //right side of bottom bar 693 | CGFloat fullscreenWidth = 28.f; 694 | CGFloat fullscreenHeight = fullscreenWidth; 695 | self.fullscreenButton.frame = CGRectMake(self.bottomBar.frame.size.width - paddingFromBezel - fullscreenWidth, self.barHeight/2 - fullscreenHeight/2, fullscreenWidth, fullscreenHeight); 696 | self.airplayView.frame = CGRectMake(self.fullscreenButton.frame.origin.x - paddingBetweenButtons - airplayWidth, self.barHeight/2 - airplayHeight/2, airplayWidth, airplayHeight); 697 | self.timeRemainingLabel.frame = CGRectMake(self.airplayView.frame.origin.x - paddingBetweenButtons - labelWidth, 0, labelWidth, self.barHeight); 698 | } 699 | 700 | //duration slider 701 | CGFloat timeRemainingX = self.timeRemainingLabel.frame.origin.x; 702 | CGFloat timeElapsedX = self.timeElapsedLabel.frame.origin.x; 703 | CGFloat sliderWidth = ((timeRemainingX - paddingBetweenLabelsAndSlider) - (timeElapsedX + self.timeElapsedLabel.frame.size.width + paddingBetweenLabelsAndSlider)); 704 | self.durationSlider.frame = CGRectMake(timeElapsedX + self.timeElapsedLabel.frame.size.width + paddingBetweenLabelsAndSlider, self.barHeight/2 - sliderHeight/2, sliderWidth, sliderHeight); 705 | 706 | if (self.state == ALMoviePlayerControlsStateLoading) { 707 | [_activityBackgroundView setFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)]; 708 | [_activityIndicator setFrame:CGRectMake((self.frame.size.width / 2) - (activityIndicatorSize / 2), (self.frame.size.height / 2) - (activityIndicatorSize / 2), activityIndicatorSize, activityIndicatorSize)]; 709 | } 710 | } 711 | 712 | @end 713 | 714 | # pragma mark - ALMoviePlayerControlsBar 715 | 716 | @implementation ALMoviePlayerControlsBar 717 | 718 | - (id)init { 719 | if ( self = [super init] ) { 720 | self.opaque = NO; 721 | } 722 | return self; 723 | } 724 | 725 | - (void)setColor:(UIColor *)color { 726 | if (_color != color) { 727 | _color = color; 728 | [self setNeedsDisplay]; 729 | } 730 | } 731 | 732 | - (void)drawRect:(CGRect)rect { 733 | CGContextRef context = UIGraphicsGetCurrentContext(); 734 | CGContextSetFillColorWithColor(context, [_color CGColor]); 735 | CGContextFillRect(context, rect); 736 | } 737 | 738 | @end 739 | -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieBackward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieBackward.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieBackward@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieBackward@2x.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieBackwardSelected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieBackwardSelected.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieBackwardSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieBackwardSelected@2x.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieEndFullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieEndFullscreen.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieEndFullscreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieEndFullscreen@2x.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieForward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieForward.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieForward@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieForward@2x.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieForwardSelected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieForwardSelected.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieForwardSelected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieForwardSelected@2x.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieFullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieFullscreen.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/movieFullscreen@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/movieFullscreen@2x.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/moviePause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/moviePause.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/moviePause@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/moviePause@2x.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/moviePlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/moviePlay.png -------------------------------------------------------------------------------- /ALMoviePlayerController/Images/moviePlay@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerController/Images/moviePlay@2x.png -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 20F694C818050C8800DEDB45 /* movieBackward.png in Resources */ = {isa = PBXBuildFile; fileRef = 20F694C418050C8800DEDB45 /* movieBackward.png */; }; 11 | 20F694C918050C8800DEDB45 /* movieBackward@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 20F694C518050C8800DEDB45 /* movieBackward@2x.png */; }; 12 | 20F694CA18050C8800DEDB45 /* movieForward.png in Resources */ = {isa = PBXBuildFile; fileRef = 20F694C618050C8800DEDB45 /* movieForward.png */; }; 13 | 20F694CB18050C8800DEDB45 /* movieForward@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 20F694C718050C8800DEDB45 /* movieForward@2x.png */; }; 14 | 20F694D01805176800DEDB45 /* movieBackwardSelected.png in Resources */ = {isa = PBXBuildFile; fileRef = 20F694CC1805176800DEDB45 /* movieBackwardSelected.png */; }; 15 | 20F694D11805176800DEDB45 /* movieBackwardSelected@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 20F694CD1805176800DEDB45 /* movieBackwardSelected@2x.png */; }; 16 | 20F694D21805176800DEDB45 /* movieForwardSelected.png in Resources */ = {isa = PBXBuildFile; fileRef = 20F694CE1805176800DEDB45 /* movieForwardSelected.png */; }; 17 | 20F694D31805176800DEDB45 /* movieForwardSelected@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 20F694CF1805176800DEDB45 /* movieForwardSelected@2x.png */; }; 18 | 4F1FABCB18072DD800849D06 /* movieEndFullscreen.png in Resources */ = {isa = PBXBuildFile; fileRef = 4F1FABC918072DD800849D06 /* movieEndFullscreen.png */; }; 19 | 4F1FABCC18072DD800849D06 /* movieEndFullscreen@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4F1FABCA18072DD800849D06 /* movieEndFullscreen@2x.png */; }; 20 | 4FE7646618045F5A00D91AF3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FE7646518045F5A00D91AF3 /* Foundation.framework */; }; 21 | 4FE7646818045F5A00D91AF3 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FE7646718045F5A00D91AF3 /* CoreGraphics.framework */; }; 22 | 4FE7646A18045F5A00D91AF3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FE7646918045F5A00D91AF3 /* UIKit.framework */; }; 23 | 4FE7647018045F5A00D91AF3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4FE7646E18045F5A00D91AF3 /* InfoPlist.strings */; }; 24 | 4FE7647218045F5A00D91AF3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE7647118045F5A00D91AF3 /* main.m */; }; 25 | 4FE7647618045F5A00D91AF3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE7647518045F5A00D91AF3 /* AppDelegate.m */; }; 26 | 4FE7647818045F5A00D91AF3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4FE7647718045F5A00D91AF3 /* Images.xcassets */; }; 27 | 4FE7647F18045F5A00D91AF3 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FE7647E18045F5A00D91AF3 /* XCTest.framework */; }; 28 | 4FE7648018045F5A00D91AF3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FE7646518045F5A00D91AF3 /* Foundation.framework */; }; 29 | 4FE7648118045F5A00D91AF3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FE7646918045F5A00D91AF3 /* UIKit.framework */; }; 30 | 4FE7648918045F5A00D91AF3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4FE7648718045F5A00D91AF3 /* InfoPlist.strings */; }; 31 | 4FE7648B18045F5A00D91AF3 /* ALMoviePlayerControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE7648A18045F5A00D91AF3 /* ALMoviePlayerControllerTests.m */; }; 32 | 4FE7649518045F9F00D91AF3 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FE7649418045F9F00D91AF3 /* QuartzCore.framework */; }; 33 | 4FE7649718045FA600D91AF3 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4FE7649618045FA600D91AF3 /* MediaPlayer.framework */; }; 34 | 4FE764A11804605200D91AF3 /* ALAirplayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE7649A1804605200D91AF3 /* ALAirplayView.m */; }; 35 | 4FE764A21804605200D91AF3 /* ALButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE7649C1804605200D91AF3 /* ALButton.m */; }; 36 | 4FE764A31804605200D91AF3 /* ALMoviePlayerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE7649E1804605200D91AF3 /* ALMoviePlayerController.m */; }; 37 | 4FE764A41804605200D91AF3 /* ALMoviePlayerControls.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE764A01804605200D91AF3 /* ALMoviePlayerControls.m */; }; 38 | 4FE764A71804606B00D91AF3 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE764A61804606B00D91AF3 /* ViewController.m */; }; 39 | 4FE764A9180463AE00D91AF3 /* popeye.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 4FE764A8180463AE00D91AF3 /* popeye.mp4 */; }; 40 | 4FE764B218046B1800D91AF3 /* movieFullscreen.png in Resources */ = {isa = PBXBuildFile; fileRef = 4FE764AC18046B1800D91AF3 /* movieFullscreen.png */; }; 41 | 4FE764B318046B1800D91AF3 /* movieFullscreen@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4FE764AD18046B1800D91AF3 /* movieFullscreen@2x.png */; }; 42 | 4FE764B418046B1800D91AF3 /* moviePause.png in Resources */ = {isa = PBXBuildFile; fileRef = 4FE764AE18046B1800D91AF3 /* moviePause.png */; }; 43 | 4FE764B518046B1800D91AF3 /* moviePause@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4FE764AF18046B1800D91AF3 /* moviePause@2x.png */; }; 44 | 4FE764B618046B1800D91AF3 /* moviePlay.png in Resources */ = {isa = PBXBuildFile; fileRef = 4FE764B018046B1800D91AF3 /* moviePlay.png */; }; 45 | 4FE764B718046B1800D91AF3 /* moviePlay@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4FE764B118046B1800D91AF3 /* moviePlay@2x.png */; }; 46 | /* End PBXBuildFile section */ 47 | 48 | /* Begin PBXContainerItemProxy section */ 49 | 4FE7648218045F5A00D91AF3 /* PBXContainerItemProxy */ = { 50 | isa = PBXContainerItemProxy; 51 | containerPortal = 4FE7645A18045F5A00D91AF3 /* Project object */; 52 | proxyType = 1; 53 | remoteGlobalIDString = 4FE7646118045F5A00D91AF3; 54 | remoteInfo = ALMoviePlayerController; 55 | }; 56 | /* End PBXContainerItemProxy section */ 57 | 58 | /* Begin PBXFileReference section */ 59 | 20F694C418050C8800DEDB45 /* movieBackward.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = movieBackward.png; sourceTree = ""; }; 60 | 20F694C518050C8800DEDB45 /* movieBackward@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "movieBackward@2x.png"; sourceTree = ""; }; 61 | 20F694C618050C8800DEDB45 /* movieForward.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = movieForward.png; sourceTree = ""; }; 62 | 20F694C718050C8800DEDB45 /* movieForward@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "movieForward@2x.png"; sourceTree = ""; }; 63 | 20F694CC1805176800DEDB45 /* movieBackwardSelected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = movieBackwardSelected.png; sourceTree = ""; }; 64 | 20F694CD1805176800DEDB45 /* movieBackwardSelected@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "movieBackwardSelected@2x.png"; sourceTree = ""; }; 65 | 20F694CE1805176800DEDB45 /* movieForwardSelected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = movieForwardSelected.png; sourceTree = ""; }; 66 | 20F694CF1805176800DEDB45 /* movieForwardSelected@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "movieForwardSelected@2x.png"; sourceTree = ""; }; 67 | 4F1FABC918072DD800849D06 /* movieEndFullscreen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = movieEndFullscreen.png; sourceTree = ""; }; 68 | 4F1FABCA18072DD800849D06 /* movieEndFullscreen@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "movieEndFullscreen@2x.png"; sourceTree = ""; }; 69 | 4FE7646218045F5A00D91AF3 /* ALMoviePlayerController.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ALMoviePlayerController.app; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | 4FE7646518045F5A00D91AF3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 71 | 4FE7646718045F5A00D91AF3 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 72 | 4FE7646918045F5A00D91AF3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 73 | 4FE7646D18045F5A00D91AF3 /* ALMoviePlayerController-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ALMoviePlayerController-Info.plist"; sourceTree = ""; }; 74 | 4FE7646F18045F5A00D91AF3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 75 | 4FE7647118045F5A00D91AF3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 76 | 4FE7647318045F5A00D91AF3 /* ALMoviePlayerController-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ALMoviePlayerController-Prefix.pch"; sourceTree = ""; }; 77 | 4FE7647418045F5A00D91AF3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 78 | 4FE7647518045F5A00D91AF3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 79 | 4FE7647718045F5A00D91AF3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 80 | 4FE7647D18045F5A00D91AF3 /* ALMoviePlayerControllerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ALMoviePlayerControllerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | 4FE7647E18045F5A00D91AF3 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 82 | 4FE7648618045F5A00D91AF3 /* ALMoviePlayerControllerTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ALMoviePlayerControllerTests-Info.plist"; sourceTree = ""; }; 83 | 4FE7648818045F5A00D91AF3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 84 | 4FE7648A18045F5A00D91AF3 /* ALMoviePlayerControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALMoviePlayerControllerTests.m; sourceTree = ""; }; 85 | 4FE7649418045F9F00D91AF3 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 86 | 4FE7649618045FA600D91AF3 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; 87 | 4FE764991804605200D91AF3 /* ALAirplayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALAirplayView.h; sourceTree = ""; }; 88 | 4FE7649A1804605200D91AF3 /* ALAirplayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALAirplayView.m; sourceTree = ""; }; 89 | 4FE7649B1804605200D91AF3 /* ALButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALButton.h; sourceTree = ""; }; 90 | 4FE7649C1804605200D91AF3 /* ALButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALButton.m; sourceTree = ""; }; 91 | 4FE7649D1804605200D91AF3 /* ALMoviePlayerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALMoviePlayerController.h; sourceTree = ""; }; 92 | 4FE7649E1804605200D91AF3 /* ALMoviePlayerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALMoviePlayerController.m; sourceTree = ""; }; 93 | 4FE7649F1804605200D91AF3 /* ALMoviePlayerControls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ALMoviePlayerControls.h; sourceTree = ""; }; 94 | 4FE764A01804605200D91AF3 /* ALMoviePlayerControls.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ALMoviePlayerControls.m; sourceTree = ""; }; 95 | 4FE764A51804606B00D91AF3 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 96 | 4FE764A61804606B00D91AF3 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 97 | 4FE764A8180463AE00D91AF3 /* popeye.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = popeye.mp4; sourceTree = SOURCE_ROOT; }; 98 | 4FE764AC18046B1800D91AF3 /* movieFullscreen.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = movieFullscreen.png; sourceTree = ""; }; 99 | 4FE764AD18046B1800D91AF3 /* movieFullscreen@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "movieFullscreen@2x.png"; sourceTree = ""; }; 100 | 4FE764AE18046B1800D91AF3 /* moviePause.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = moviePause.png; sourceTree = ""; }; 101 | 4FE764AF18046B1800D91AF3 /* moviePause@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "moviePause@2x.png"; sourceTree = ""; }; 102 | 4FE764B018046B1800D91AF3 /* moviePlay.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = moviePlay.png; sourceTree = ""; }; 103 | 4FE764B118046B1800D91AF3 /* moviePlay@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "moviePlay@2x.png"; sourceTree = ""; }; 104 | /* End PBXFileReference section */ 105 | 106 | /* Begin PBXFrameworksBuildPhase section */ 107 | 4FE7645F18045F5A00D91AF3 /* Frameworks */ = { 108 | isa = PBXFrameworksBuildPhase; 109 | buildActionMask = 2147483647; 110 | files = ( 111 | 4FE7649718045FA600D91AF3 /* MediaPlayer.framework in Frameworks */, 112 | 4FE7649518045F9F00D91AF3 /* QuartzCore.framework in Frameworks */, 113 | 4FE7646818045F5A00D91AF3 /* CoreGraphics.framework in Frameworks */, 114 | 4FE7646A18045F5A00D91AF3 /* UIKit.framework in Frameworks */, 115 | 4FE7646618045F5A00D91AF3 /* Foundation.framework in Frameworks */, 116 | ); 117 | runOnlyForDeploymentPostprocessing = 0; 118 | }; 119 | 4FE7647A18045F5A00D91AF3 /* Frameworks */ = { 120 | isa = PBXFrameworksBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | 4FE7647F18045F5A00D91AF3 /* XCTest.framework in Frameworks */, 124 | 4FE7648118045F5A00D91AF3 /* UIKit.framework in Frameworks */, 125 | 4FE7648018045F5A00D91AF3 /* Foundation.framework in Frameworks */, 126 | ); 127 | runOnlyForDeploymentPostprocessing = 0; 128 | }; 129 | /* End PBXFrameworksBuildPhase section */ 130 | 131 | /* Begin PBXGroup section */ 132 | 4FE7645918045F5A00D91AF3 = { 133 | isa = PBXGroup; 134 | children = ( 135 | 4FE764981804605200D91AF3 /* ALMoviePlayerController */, 136 | 4FE7646B18045F5A00D91AF3 /* ALMoviePlayerControllerDemo */, 137 | 4FE7648418045F5A00D91AF3 /* ALMoviePlayerControllerTests */, 138 | 4FE7646418045F5A00D91AF3 /* Frameworks */, 139 | 4FE7646318045F5A00D91AF3 /* Products */, 140 | ); 141 | sourceTree = ""; 142 | }; 143 | 4FE7646318045F5A00D91AF3 /* Products */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 4FE7646218045F5A00D91AF3 /* ALMoviePlayerController.app */, 147 | 4FE7647D18045F5A00D91AF3 /* ALMoviePlayerControllerTests.xctest */, 148 | ); 149 | name = Products; 150 | sourceTree = ""; 151 | }; 152 | 4FE7646418045F5A00D91AF3 /* Frameworks */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 4FE7649618045FA600D91AF3 /* MediaPlayer.framework */, 156 | 4FE7649418045F9F00D91AF3 /* QuartzCore.framework */, 157 | 4FE7646518045F5A00D91AF3 /* Foundation.framework */, 158 | 4FE7646718045F5A00D91AF3 /* CoreGraphics.framework */, 159 | 4FE7646918045F5A00D91AF3 /* UIKit.framework */, 160 | 4FE7647E18045F5A00D91AF3 /* XCTest.framework */, 161 | ); 162 | name = Frameworks; 163 | sourceTree = ""; 164 | }; 165 | 4FE7646B18045F5A00D91AF3 /* ALMoviePlayerControllerDemo */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 4FE7647418045F5A00D91AF3 /* AppDelegate.h */, 169 | 4FE7647518045F5A00D91AF3 /* AppDelegate.m */, 170 | 4FE764A51804606B00D91AF3 /* ViewController.h */, 171 | 4FE764A61804606B00D91AF3 /* ViewController.m */, 172 | 4FE764AA180463B100D91AF3 /* Video */, 173 | 4FE7647718045F5A00D91AF3 /* Images.xcassets */, 174 | 4FE7646C18045F5A00D91AF3 /* Supporting Files */, 175 | ); 176 | name = ALMoviePlayerControllerDemo; 177 | path = ALMoviePlayerController; 178 | sourceTree = ""; 179 | }; 180 | 4FE7646C18045F5A00D91AF3 /* Supporting Files */ = { 181 | isa = PBXGroup; 182 | children = ( 183 | 4FE7646D18045F5A00D91AF3 /* ALMoviePlayerController-Info.plist */, 184 | 4FE7646E18045F5A00D91AF3 /* InfoPlist.strings */, 185 | 4FE7647118045F5A00D91AF3 /* main.m */, 186 | 4FE7647318045F5A00D91AF3 /* ALMoviePlayerController-Prefix.pch */, 187 | ); 188 | name = "Supporting Files"; 189 | sourceTree = ""; 190 | }; 191 | 4FE7648418045F5A00D91AF3 /* ALMoviePlayerControllerTests */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | 4FE7648A18045F5A00D91AF3 /* ALMoviePlayerControllerTests.m */, 195 | 4FE7648518045F5A00D91AF3 /* Supporting Files */, 196 | ); 197 | path = ALMoviePlayerControllerTests; 198 | sourceTree = ""; 199 | }; 200 | 4FE7648518045F5A00D91AF3 /* Supporting Files */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | 4FE7648618045F5A00D91AF3 /* ALMoviePlayerControllerTests-Info.plist */, 204 | 4FE7648718045F5A00D91AF3 /* InfoPlist.strings */, 205 | ); 206 | name = "Supporting Files"; 207 | sourceTree = ""; 208 | }; 209 | 4FE764981804605200D91AF3 /* ALMoviePlayerController */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | 4FE7649D1804605200D91AF3 /* ALMoviePlayerController.h */, 213 | 4FE7649E1804605200D91AF3 /* ALMoviePlayerController.m */, 214 | 4FE7649F1804605200D91AF3 /* ALMoviePlayerControls.h */, 215 | 4FE764A01804605200D91AF3 /* ALMoviePlayerControls.m */, 216 | 4FE764991804605200D91AF3 /* ALAirplayView.h */, 217 | 4FE7649A1804605200D91AF3 /* ALAirplayView.m */, 218 | 4FE7649B1804605200D91AF3 /* ALButton.h */, 219 | 4FE7649C1804605200D91AF3 /* ALButton.m */, 220 | 4FE764AB18046B1800D91AF3 /* Images */, 221 | ); 222 | name = ALMoviePlayerController; 223 | path = ../ALMoviePlayerController; 224 | sourceTree = ""; 225 | }; 226 | 4FE764AA180463B100D91AF3 /* Video */ = { 227 | isa = PBXGroup; 228 | children = ( 229 | 4FE764A8180463AE00D91AF3 /* popeye.mp4 */, 230 | ); 231 | name = Video; 232 | sourceTree = ""; 233 | }; 234 | 4FE764AB18046B1800D91AF3 /* Images */ = { 235 | isa = PBXGroup; 236 | children = ( 237 | 4FE764AC18046B1800D91AF3 /* movieFullscreen.png */, 238 | 4FE764AD18046B1800D91AF3 /* movieFullscreen@2x.png */, 239 | 4F1FABC918072DD800849D06 /* movieEndFullscreen.png */, 240 | 4F1FABCA18072DD800849D06 /* movieEndFullscreen@2x.png */, 241 | 4FE764AE18046B1800D91AF3 /* moviePause.png */, 242 | 4FE764AF18046B1800D91AF3 /* moviePause@2x.png */, 243 | 4FE764B018046B1800D91AF3 /* moviePlay.png */, 244 | 4FE764B118046B1800D91AF3 /* moviePlay@2x.png */, 245 | 20F694C418050C8800DEDB45 /* movieBackward.png */, 246 | 20F694C518050C8800DEDB45 /* movieBackward@2x.png */, 247 | 20F694C618050C8800DEDB45 /* movieForward.png */, 248 | 20F694C718050C8800DEDB45 /* movieForward@2x.png */, 249 | 20F694CC1805176800DEDB45 /* movieBackwardSelected.png */, 250 | 20F694CD1805176800DEDB45 /* movieBackwardSelected@2x.png */, 251 | 20F694CE1805176800DEDB45 /* movieForwardSelected.png */, 252 | 20F694CF1805176800DEDB45 /* movieForwardSelected@2x.png */, 253 | ); 254 | path = Images; 255 | sourceTree = ""; 256 | }; 257 | /* End PBXGroup section */ 258 | 259 | /* Begin PBXNativeTarget section */ 260 | 4FE7646118045F5A00D91AF3 /* ALMoviePlayerController */ = { 261 | isa = PBXNativeTarget; 262 | buildConfigurationList = 4FE7648E18045F5A00D91AF3 /* Build configuration list for PBXNativeTarget "ALMoviePlayerController" */; 263 | buildPhases = ( 264 | 4FE7645E18045F5A00D91AF3 /* Sources */, 265 | 4FE7645F18045F5A00D91AF3 /* Frameworks */, 266 | 4FE7646018045F5A00D91AF3 /* Resources */, 267 | ); 268 | buildRules = ( 269 | ); 270 | dependencies = ( 271 | ); 272 | name = ALMoviePlayerController; 273 | productName = ALMoviePlayerController; 274 | productReference = 4FE7646218045F5A00D91AF3 /* ALMoviePlayerController.app */; 275 | productType = "com.apple.product-type.application"; 276 | }; 277 | 4FE7647C18045F5A00D91AF3 /* ALMoviePlayerControllerTests */ = { 278 | isa = PBXNativeTarget; 279 | buildConfigurationList = 4FE7649118045F5A00D91AF3 /* Build configuration list for PBXNativeTarget "ALMoviePlayerControllerTests" */; 280 | buildPhases = ( 281 | 4FE7647918045F5A00D91AF3 /* Sources */, 282 | 4FE7647A18045F5A00D91AF3 /* Frameworks */, 283 | 4FE7647B18045F5A00D91AF3 /* Resources */, 284 | ); 285 | buildRules = ( 286 | ); 287 | dependencies = ( 288 | 4FE7648318045F5A00D91AF3 /* PBXTargetDependency */, 289 | ); 290 | name = ALMoviePlayerControllerTests; 291 | productName = ALMoviePlayerControllerTests; 292 | productReference = 4FE7647D18045F5A00D91AF3 /* ALMoviePlayerControllerTests.xctest */; 293 | productType = "com.apple.product-type.bundle.unit-test"; 294 | }; 295 | /* End PBXNativeTarget section */ 296 | 297 | /* Begin PBXProject section */ 298 | 4FE7645A18045F5A00D91AF3 /* Project object */ = { 299 | isa = PBXProject; 300 | attributes = { 301 | LastUpgradeCheck = 0500; 302 | ORGANIZATIONNAME = "Anthony Lobianco"; 303 | TargetAttributes = { 304 | 4FE7647C18045F5A00D91AF3 = { 305 | TestTargetID = 4FE7646118045F5A00D91AF3; 306 | }; 307 | }; 308 | }; 309 | buildConfigurationList = 4FE7645D18045F5A00D91AF3 /* Build configuration list for PBXProject "ALMoviePlayerController" */; 310 | compatibilityVersion = "Xcode 3.2"; 311 | developmentRegion = English; 312 | hasScannedForEncodings = 0; 313 | knownRegions = ( 314 | en, 315 | ); 316 | mainGroup = 4FE7645918045F5A00D91AF3; 317 | productRefGroup = 4FE7646318045F5A00D91AF3 /* Products */; 318 | projectDirPath = ""; 319 | projectRoot = ""; 320 | targets = ( 321 | 4FE7646118045F5A00D91AF3 /* ALMoviePlayerController */, 322 | 4FE7647C18045F5A00D91AF3 /* ALMoviePlayerControllerTests */, 323 | ); 324 | }; 325 | /* End PBXProject section */ 326 | 327 | /* Begin PBXResourcesBuildPhase section */ 328 | 4FE7646018045F5A00D91AF3 /* Resources */ = { 329 | isa = PBXResourcesBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | 20F694D11805176800DEDB45 /* movieBackwardSelected@2x.png in Resources */, 333 | 20F694CA18050C8800DEDB45 /* movieForward.png in Resources */, 334 | 20F694C918050C8800DEDB45 /* movieBackward@2x.png in Resources */, 335 | 4FE7647018045F5A00D91AF3 /* InfoPlist.strings in Resources */, 336 | 20F694D31805176800DEDB45 /* movieForwardSelected@2x.png in Resources */, 337 | 4F1FABCC18072DD800849D06 /* movieEndFullscreen@2x.png in Resources */, 338 | 4FE764B318046B1800D91AF3 /* movieFullscreen@2x.png in Resources */, 339 | 4FE764B518046B1800D91AF3 /* moviePause@2x.png in Resources */, 340 | 4FE764B218046B1800D91AF3 /* movieFullscreen.png in Resources */, 341 | 20F694CB18050C8800DEDB45 /* movieForward@2x.png in Resources */, 342 | 4FE764B418046B1800D91AF3 /* moviePause.png in Resources */, 343 | 20F694D21805176800DEDB45 /* movieForwardSelected.png in Resources */, 344 | 4FE7647818045F5A00D91AF3 /* Images.xcassets in Resources */, 345 | 4FE764B618046B1800D91AF3 /* moviePlay.png in Resources */, 346 | 20F694D01805176800DEDB45 /* movieBackwardSelected.png in Resources */, 347 | 4FE764B718046B1800D91AF3 /* moviePlay@2x.png in Resources */, 348 | 4F1FABCB18072DD800849D06 /* movieEndFullscreen.png in Resources */, 349 | 4FE764A9180463AE00D91AF3 /* popeye.mp4 in Resources */, 350 | 20F694C818050C8800DEDB45 /* movieBackward.png in Resources */, 351 | ); 352 | runOnlyForDeploymentPostprocessing = 0; 353 | }; 354 | 4FE7647B18045F5A00D91AF3 /* Resources */ = { 355 | isa = PBXResourcesBuildPhase; 356 | buildActionMask = 2147483647; 357 | files = ( 358 | 4FE7648918045F5A00D91AF3 /* InfoPlist.strings in Resources */, 359 | ); 360 | runOnlyForDeploymentPostprocessing = 0; 361 | }; 362 | /* End PBXResourcesBuildPhase section */ 363 | 364 | /* Begin PBXSourcesBuildPhase section */ 365 | 4FE7645E18045F5A00D91AF3 /* Sources */ = { 366 | isa = PBXSourcesBuildPhase; 367 | buildActionMask = 2147483647; 368 | files = ( 369 | 4FE764A71804606B00D91AF3 /* ViewController.m in Sources */, 370 | 4FE7647618045F5A00D91AF3 /* AppDelegate.m in Sources */, 371 | 4FE7647218045F5A00D91AF3 /* main.m in Sources */, 372 | 4FE764A21804605200D91AF3 /* ALButton.m in Sources */, 373 | 4FE764A11804605200D91AF3 /* ALAirplayView.m in Sources */, 374 | 4FE764A41804605200D91AF3 /* ALMoviePlayerControls.m in Sources */, 375 | 4FE764A31804605200D91AF3 /* ALMoviePlayerController.m in Sources */, 376 | ); 377 | runOnlyForDeploymentPostprocessing = 0; 378 | }; 379 | 4FE7647918045F5A00D91AF3 /* Sources */ = { 380 | isa = PBXSourcesBuildPhase; 381 | buildActionMask = 2147483647; 382 | files = ( 383 | 4FE7648B18045F5A00D91AF3 /* ALMoviePlayerControllerTests.m in Sources */, 384 | ); 385 | runOnlyForDeploymentPostprocessing = 0; 386 | }; 387 | /* End PBXSourcesBuildPhase section */ 388 | 389 | /* Begin PBXTargetDependency section */ 390 | 4FE7648318045F5A00D91AF3 /* PBXTargetDependency */ = { 391 | isa = PBXTargetDependency; 392 | target = 4FE7646118045F5A00D91AF3 /* ALMoviePlayerController */; 393 | targetProxy = 4FE7648218045F5A00D91AF3 /* PBXContainerItemProxy */; 394 | }; 395 | /* End PBXTargetDependency section */ 396 | 397 | /* Begin PBXVariantGroup section */ 398 | 4FE7646E18045F5A00D91AF3 /* InfoPlist.strings */ = { 399 | isa = PBXVariantGroup; 400 | children = ( 401 | 4FE7646F18045F5A00D91AF3 /* en */, 402 | ); 403 | name = InfoPlist.strings; 404 | sourceTree = ""; 405 | }; 406 | 4FE7648718045F5A00D91AF3 /* InfoPlist.strings */ = { 407 | isa = PBXVariantGroup; 408 | children = ( 409 | 4FE7648818045F5A00D91AF3 /* en */, 410 | ); 411 | name = InfoPlist.strings; 412 | sourceTree = ""; 413 | }; 414 | /* End PBXVariantGroup section */ 415 | 416 | /* Begin XCBuildConfiguration section */ 417 | 4FE7648C18045F5A00D91AF3 /* Debug */ = { 418 | isa = XCBuildConfiguration; 419 | buildSettings = { 420 | ALWAYS_SEARCH_USER_PATHS = NO; 421 | ARCHS = "$(ARCHS_STANDARD)"; 422 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 423 | CLANG_CXX_LIBRARY = "libc++"; 424 | CLANG_ENABLE_MODULES = YES; 425 | CLANG_ENABLE_OBJC_ARC = YES; 426 | CLANG_WARN_BOOL_CONVERSION = YES; 427 | CLANG_WARN_CONSTANT_CONVERSION = YES; 428 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 429 | CLANG_WARN_EMPTY_BODY = YES; 430 | CLANG_WARN_ENUM_CONVERSION = YES; 431 | CLANG_WARN_INT_CONVERSION = YES; 432 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 433 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 434 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 435 | COPY_PHASE_STRIP = NO; 436 | GCC_C_LANGUAGE_STANDARD = gnu99; 437 | GCC_DYNAMIC_NO_PIC = NO; 438 | GCC_OPTIMIZATION_LEVEL = 0; 439 | GCC_PREPROCESSOR_DEFINITIONS = ( 440 | "DEBUG=1", 441 | "$(inherited)", 442 | ); 443 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 444 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 445 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 446 | GCC_WARN_UNDECLARED_SELECTOR = YES; 447 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 448 | GCC_WARN_UNUSED_FUNCTION = YES; 449 | GCC_WARN_UNUSED_VARIABLE = YES; 450 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 451 | ONLY_ACTIVE_ARCH = YES; 452 | SDKROOT = iphoneos; 453 | TARGETED_DEVICE_FAMILY = "1,2"; 454 | VALID_ARCHS = "armv7 armv7s arm64"; 455 | }; 456 | name = Debug; 457 | }; 458 | 4FE7648D18045F5A00D91AF3 /* Release */ = { 459 | isa = XCBuildConfiguration; 460 | buildSettings = { 461 | ALWAYS_SEARCH_USER_PATHS = NO; 462 | ARCHS = "$(ARCHS_STANDARD)"; 463 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 464 | CLANG_CXX_LIBRARY = "libc++"; 465 | CLANG_ENABLE_MODULES = YES; 466 | CLANG_ENABLE_OBJC_ARC = YES; 467 | CLANG_WARN_BOOL_CONVERSION = YES; 468 | CLANG_WARN_CONSTANT_CONVERSION = YES; 469 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 470 | CLANG_WARN_EMPTY_BODY = YES; 471 | CLANG_WARN_ENUM_CONVERSION = YES; 472 | CLANG_WARN_INT_CONVERSION = YES; 473 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 474 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 475 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 476 | COPY_PHASE_STRIP = YES; 477 | ENABLE_NS_ASSERTIONS = NO; 478 | GCC_C_LANGUAGE_STANDARD = gnu99; 479 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 480 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 481 | GCC_WARN_UNDECLARED_SELECTOR = YES; 482 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 483 | GCC_WARN_UNUSED_FUNCTION = YES; 484 | GCC_WARN_UNUSED_VARIABLE = YES; 485 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 486 | SDKROOT = iphoneos; 487 | TARGETED_DEVICE_FAMILY = "1,2"; 488 | VALIDATE_PRODUCT = YES; 489 | VALID_ARCHS = "armv7 armv7s arm64"; 490 | }; 491 | name = Release; 492 | }; 493 | 4FE7648F18045F5A00D91AF3 /* Debug */ = { 494 | isa = XCBuildConfiguration; 495 | buildSettings = { 496 | ARCHS = "$(ARCHS_STANDARD)"; 497 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 498 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 499 | CODE_SIGN_IDENTITY = "iPhone Developer"; 500 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 501 | GCC_PREFIX_HEADER = "ALMoviePlayerController/ALMoviePlayerController-Prefix.pch"; 502 | INFOPLIST_FILE = "ALMoviePlayerController/ALMoviePlayerController-Info.plist"; 503 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 504 | PRODUCT_NAME = "$(TARGET_NAME)"; 505 | PROVISIONING_PROFILE = ""; 506 | WRAPPER_EXTENSION = app; 507 | }; 508 | name = Debug; 509 | }; 510 | 4FE7649018045F5A00D91AF3 /* Release */ = { 511 | isa = XCBuildConfiguration; 512 | buildSettings = { 513 | ARCHS = "$(ARCHS_STANDARD)"; 514 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 515 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 516 | CODE_SIGN_IDENTITY = "iPhone Developer"; 517 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 518 | GCC_PREFIX_HEADER = "ALMoviePlayerController/ALMoviePlayerController-Prefix.pch"; 519 | INFOPLIST_FILE = "ALMoviePlayerController/ALMoviePlayerController-Info.plist"; 520 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 521 | PRODUCT_NAME = "$(TARGET_NAME)"; 522 | PROVISIONING_PROFILE = ""; 523 | WRAPPER_EXTENSION = app; 524 | }; 525 | name = Release; 526 | }; 527 | 4FE7649218045F5A00D91AF3 /* Debug */ = { 528 | isa = XCBuildConfiguration; 529 | buildSettings = { 530 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 531 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ALMoviePlayerController.app/ALMoviePlayerController"; 532 | FRAMEWORK_SEARCH_PATHS = ( 533 | "$(SDKROOT)/Developer/Library/Frameworks", 534 | "$(inherited)", 535 | "$(DEVELOPER_FRAMEWORKS_DIR)", 536 | ); 537 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 538 | GCC_PREFIX_HEADER = "ALMoviePlayerController/ALMoviePlayerController-Prefix.pch"; 539 | GCC_PREPROCESSOR_DEFINITIONS = ( 540 | "DEBUG=1", 541 | "$(inherited)", 542 | ); 543 | INFOPLIST_FILE = "ALMoviePlayerControllerTests/ALMoviePlayerControllerTests-Info.plist"; 544 | PRODUCT_NAME = "$(TARGET_NAME)"; 545 | TEST_HOST = "$(BUNDLE_LOADER)"; 546 | WRAPPER_EXTENSION = xctest; 547 | }; 548 | name = Debug; 549 | }; 550 | 4FE7649318045F5A00D91AF3 /* Release */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 554 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ALMoviePlayerController.app/ALMoviePlayerController"; 555 | FRAMEWORK_SEARCH_PATHS = ( 556 | "$(SDKROOT)/Developer/Library/Frameworks", 557 | "$(inherited)", 558 | "$(DEVELOPER_FRAMEWORKS_DIR)", 559 | ); 560 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 561 | GCC_PREFIX_HEADER = "ALMoviePlayerController/ALMoviePlayerController-Prefix.pch"; 562 | INFOPLIST_FILE = "ALMoviePlayerControllerTests/ALMoviePlayerControllerTests-Info.plist"; 563 | PRODUCT_NAME = "$(TARGET_NAME)"; 564 | TEST_HOST = "$(BUNDLE_LOADER)"; 565 | WRAPPER_EXTENSION = xctest; 566 | }; 567 | name = Release; 568 | }; 569 | /* End XCBuildConfiguration section */ 570 | 571 | /* Begin XCConfigurationList section */ 572 | 4FE7645D18045F5A00D91AF3 /* Build configuration list for PBXProject "ALMoviePlayerController" */ = { 573 | isa = XCConfigurationList; 574 | buildConfigurations = ( 575 | 4FE7648C18045F5A00D91AF3 /* Debug */, 576 | 4FE7648D18045F5A00D91AF3 /* Release */, 577 | ); 578 | defaultConfigurationIsVisible = 0; 579 | defaultConfigurationName = Release; 580 | }; 581 | 4FE7648E18045F5A00D91AF3 /* Build configuration list for PBXNativeTarget "ALMoviePlayerController" */ = { 582 | isa = XCConfigurationList; 583 | buildConfigurations = ( 584 | 4FE7648F18045F5A00D91AF3 /* Debug */, 585 | 4FE7649018045F5A00D91AF3 /* Release */, 586 | ); 587 | defaultConfigurationIsVisible = 0; 588 | defaultConfigurationName = Release; 589 | }; 590 | 4FE7649118045F5A00D91AF3 /* Build configuration list for PBXNativeTarget "ALMoviePlayerControllerTests" */ = { 591 | isa = XCConfigurationList; 592 | buildConfigurations = ( 593 | 4FE7649218045F5A00D91AF3 /* Debug */, 594 | 4FE7649318045F5A00D91AF3 /* Release */, 595 | ); 596 | defaultConfigurationIsVisible = 0; 597 | defaultConfigurationName = Release; 598 | }; 599 | /* End XCConfigurationList section */ 600 | }; 601 | rootObject = 4FE7645A18045F5A00D91AF3 /* Project object */; 602 | } 603 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController/ALMoviePlayerController-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.lobianco.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController/ALMoviePlayerController-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @implementation AppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 17 | // Override point for customization after application launch. 18 | 19 | UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]]; 20 | self.window.rootViewController = navigationController; 21 | 22 | self.window.backgroundColor = [UIColor whiteColor]; 23 | [self.window makeKeyAndVisible]; 24 | return YES; 25 | } 26 | 27 | - (void)applicationWillResignActive:(UIApplication *)application 28 | { 29 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 30 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 31 | } 32 | 33 | - (void)applicationDidEnterBackground:(UIApplication *)application 34 | { 35 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 36 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 37 | } 38 | 39 | - (void)applicationWillEnterForeground:(UIApplication *)application 40 | { 41 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 42 | } 43 | 44 | - (void)applicationDidBecomeActive:(UIApplication *)application 45 | { 46 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 47 | } 48 | 49 | - (void)applicationWillTerminate:(UIApplication *)application 50 | { 51 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "ipad", 20 | "size" : "29x29", 21 | "scale" : "1x" 22 | }, 23 | { 24 | "idiom" : "ipad", 25 | "size" : "29x29", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "ipad", 30 | "size" : "40x40", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "40x40", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "76x76", 41 | "scale" : "1x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "76x76", 46 | "scale" : "2x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "orientation" : "portrait", 20 | "idiom" : "ipad", 21 | "extent" : "full-screen", 22 | "minimum-system-version" : "7.0", 23 | "scale" : "1x" 24 | }, 25 | { 26 | "orientation" : "landscape", 27 | "idiom" : "ipad", 28 | "extent" : "full-screen", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "1x" 31 | }, 32 | { 33 | "orientation" : "portrait", 34 | "idiom" : "ipad", 35 | "extent" : "full-screen", 36 | "minimum-system-version" : "7.0", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "orientation" : "landscape", 41 | "idiom" : "ipad", 42 | "extent" : "full-screen", 43 | "minimum-system-version" : "7.0", 44 | "scale" : "2x" 45 | } 46 | ], 47 | "info" : { 48 | "version" : 1, 49 | "author" : "xcode" 50 | } 51 | } -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "ALMoviePlayerController.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (nonatomic, strong) ALMoviePlayerController *moviePlayer; 15 | @property (nonatomic) CGRect defaultFrame; 16 | 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | - (id)init { 22 | self = [super init]; 23 | if (self) { 24 | self.title = NSLocalizedString(@"ALMoviePlayerController", @"ALMoviePlayerController"); 25 | } 26 | return self; 27 | } 28 | 29 | - (void)viewDidLoad 30 | { 31 | [super viewDidLoad]; 32 | // Do any additional setup after loading the view. 33 | 34 | self.view.backgroundColor = [UIColor darkGrayColor]; 35 | 36 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Local File" style:UIBarButtonItemStyleBordered target:self action:@selector(localFile)]; 37 | self.navigationItem.leftBarButtonItem.enabled = NO; 38 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Remote File" style:UIBarButtonItemStyleBordered target:self action:@selector(remoteFile)]; 39 | self.navigationItem.rightBarButtonItem.enabled = NO; 40 | 41 | //create a player 42 | self.moviePlayer = [[ALMoviePlayerController alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; 43 | self.moviePlayer.view.alpha = 0.f; 44 | self.moviePlayer.delegate = self; //IMPORTANT! 45 | 46 | //create the controls 47 | ALMoviePlayerControls *movieControls = [[ALMoviePlayerControls alloc] initWithMoviePlayer:self.moviePlayer style:ALMoviePlayerControlsStyleDefault]; 48 | //[movieControls setAdjustsFullscreenImage:NO]; 49 | [movieControls setBarColor:[UIColor colorWithRed:195/255.0 green:29/255.0 blue:29/255.0 alpha:0.5]]; 50 | [movieControls setTimeRemainingDecrements:YES]; 51 | //[movieControls setFadeDelay:2.0]; 52 | //[movieControls setBarHeight:100.f]; 53 | //[movieControls setSeekRate:2.f]; 54 | 55 | //assign controls 56 | [self.moviePlayer setControls:movieControls]; 57 | [self.view addSubview:self.moviePlayer.view]; 58 | 59 | //THEN set contentURL 60 | [self.moviePlayer setContentURL:[NSURL URLWithString:@"http://archive.org/download/WaltDisneyCartoons-MickeyMouseMinnieMouseDonaldDuckGoofyAndPluto/WaltDisneyCartoons-MickeyMouseMinnieMouseDonaldDuckGoofyAndPluto-HawaiianHoliday1937-Video.mp4"]]; 61 | 62 | //delay initial load so statusBarOrientation returns correct value 63 | double delayInSeconds = 0.3; 64 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 65 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 66 | [self configureViewForOrientation:[UIApplication sharedApplication].statusBarOrientation]; 67 | [UIView animateWithDuration:0.3 delay:0.0 options:0 animations:^{ 68 | self.moviePlayer.view.alpha = 1.f; 69 | } completion:^(BOOL finished) { 70 | self.navigationItem.leftBarButtonItem.enabled = YES; 71 | self.navigationItem.rightBarButtonItem.enabled = YES; 72 | }]; 73 | }); 74 | } 75 | 76 | - (void)configureViewForOrientation:(UIInterfaceOrientation)orientation { 77 | CGFloat videoWidth = 0; 78 | CGFloat videoHeight = 0; 79 | if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { 80 | videoWidth = 700.f; 81 | videoHeight = 535.f; 82 | } else { 83 | videoWidth = self.view.frame.size.width; 84 | videoHeight = 220.f; 85 | } 86 | 87 | //calulate the frame on every rotation, so when we're returning from fullscreen mode we'll know where to position the movie plauyer 88 | self.defaultFrame = CGRectMake(self.view.frame.size.width/2 - videoWidth/2, self.view.frame.size.height/2 - videoHeight/2, videoWidth, videoHeight); 89 | 90 | //only manage the movie player frame when it's not in fullscreen. when in fullscreen, the frame is automatically managed 91 | if (self.moviePlayer.isFullscreen) 92 | return; 93 | 94 | //you MUST use [ALMoviePlayerController setFrame:] to adjust frame, NOT [ALMoviePlayerController.view setFrame:] 95 | [self.moviePlayer setFrame:self.defaultFrame]; 96 | } 97 | 98 | //these files are in the public domain and no longer have property rights 99 | - (void)localFile { 100 | [self.moviePlayer stop]; 101 | [self.moviePlayer setContentURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"popeye" ofType:@"mp4"]]]; 102 | [self.moviePlayer play]; 103 | } 104 | 105 | - (void)remoteFile { 106 | [self.moviePlayer stop]; 107 | [self.moviePlayer setContentURL:[NSURL URLWithString:@"http://archive.org/download/WaltDisneyCartoons-MickeyMouseMinnieMouseDonaldDuckGoofyAndPluto/WaltDisneyCartoons-MickeyMouseMinnieMouseDonaldDuckGoofyAndPluto-HawaiianHoliday1937-Video.mp4"]]; 108 | [self.moviePlayer play]; 109 | } 110 | 111 | //IMPORTANT! 112 | - (void)moviePlayerWillMoveFromWindow { 113 | //movie player must be readded to this view upon exiting fullscreen mode. 114 | if (![self.view.subviews containsObject:self.moviePlayer.view]) 115 | [self.view addSubview:self.moviePlayer.view]; 116 | 117 | //you MUST use [ALMoviePlayerController setFrame:] to adjust frame, NOT [ALMoviePlayerController.view setFrame:] 118 | [self.moviePlayer setFrame:self.defaultFrame]; 119 | } 120 | 121 | - (void)movieTimedOut { 122 | NSLog(@"MOVIE TIMED OUT"); 123 | } 124 | 125 | - (BOOL)shouldAutorotate { 126 | return YES; 127 | } 128 | 129 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { 130 | return YES; 131 | } 132 | 133 | -(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { 134 | [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; 135 | [self configureViewForOrientation:toInterfaceOrientation]; 136 | } 137 | 138 | - (void)didReceiveMemoryWarning 139 | { 140 | [super didReceiveMemoryWarning]; 141 | // Dispose of any resources that can be recreated. 142 | } 143 | 144 | @end 145 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerController/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ALMoviePlayerController 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerControllerTests/ALMoviePlayerControllerTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.lobianco.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerControllerTests/ALMoviePlayerControllerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ALMoviePlayerControllerTests.m 3 | // ALMoviePlayerControllerTests 4 | // 5 | // Created by Anthony Lobianco on 10/8/13. 6 | // Copyright (c) 2013 Anthony Lobianco. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ALMoviePlayerControllerTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation ALMoviePlayerControllerTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/ALMoviePlayerControllerTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /ALMoviePlayerControllerDemo/popeye.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lobianco/ALMoviePlayerController/f6c08fccfcf09d060882954bf58975c0d54d912e/ALMoviePlayerControllerDemo/popeye.mp4 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Anthony Lobianco 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Edit (January, 2016):** I've stopped maintaining this repo, but please feel free to fork it! 2 | 3 | # ALMoviePlayerController 4 | 5 | ALMoviePlayerController is a drop-in replacement for MPMoviePlayerController that exposes the UI elements and allows for maximum customization. 6 | 7 | ### Preview 8 | 9 | **ALMoviePlayerController on iPad, iOS 7.0** 10 | 11 | ![Preview1](http://lobianco.github.io/ALMoviePlayerController/screenshots/screenshot2.png) 12 | 13 | **ALMoviePlayerController on iPhone, iOS 6.1** 14 | 15 | ![Preview2](http://lobianco.github.io/ALMoviePlayerController/screenshots/screenshot1.png) 16 | 17 | ### Features 18 | 19 | * Drop-in replacement for ```MPMoviePlayerController``` 20 | * Many different customization options, or you can go with the stock Apple look 21 | * Portrait and landscape support 22 | * Universal (iPhone and iPad) support 23 | * iOS 5.0 - iOS 7 support 24 | * Lightweight, stable component with small memory footprint 25 | 26 | ## Installation 27 | 28 | Installation is easy. 29 | 30 | ### Cocoapods 31 | 32 | 1. Add ```pod 'ALMoviePlayerController', '~>0.3.0'``` to your Podfile 33 | 2. ```#import ``` in your view of choice 34 | 35 | ### Manually 36 | 37 | 1. [Download the ZIP](https://github.com/lobianco/ALMoviePlayerController/archive/master.zip) from Github and copy the ALMoviePlayerController directory to your project 38 | 2. Link the ```QuartzCore.framework``` and ```MediaPlayer.framework``` library in your project's Build Phases 39 | 3. ```#import "ALMoviePlayerController.h"``` in your view of choice 40 | 41 | ### Tested Environments 42 | 43 | ALMoviePlayerController has been tested to work on iOS 5.0, 5.1 and 6.0 (simulator), and iOS 6.1 and 7.0 (device). ALMoviePlayerController requires that ARC be enabled. 44 | 45 | ## Example Usage 46 | 47 | The process is as follows: 48 | 49 | 1. Create an ```ALMoviePlayerController``` movie player and assign yourself as its delegate 50 | 2. Create the ```ALMoviePlayerControls``` controls (and optionally customize) 51 | 3. Assign the controls to the movie player 52 | 4. Set the movie player's ```contentURL```, which will start playing the movie 53 | 5. On device rotation, adjust movie player frame if it's not in fullscreen (when in fullscreen, rotation is handled automatically) 54 | 6. Implement ```ALMoviePlayerController``` delegate methods 55 | 56 | **In code:** 57 | 58 | ```objc 59 | @property (nonatomic, strong) ALMoviePlayerController *moviePlayer; 60 | 61 | //... 62 | 63 | // create a movie player 64 | self.moviePlayer = [[ALMoviePlayerController alloc] initWithFrame:self.view.frame]; 65 | self.moviePlayer.delegate = self; //IMPORTANT! 66 | 67 | // create the controls 68 | ALMoviePlayerControls *movieControls = [[ALMoviePlayerControls alloc] initWithMoviePlayer:self.moviePlayer style:ALMoviePlayerControlsStyleDefault]; 69 | 70 | // optionally customize the controls here... 71 | /* 72 | [movieControls setBarColor:[UIColor colorWithRed:195/255.0 green:29/255.0 blue:29/255.0 alpha:0.5]]; 73 | [movieControls setTimeRemainingDecrements:YES]; 74 | [movieControls setFadeDelay:2.0]; 75 | [movieControls setBarHeight:100.f]; 76 | [movieControls setSeekRate:2.f]; 77 | */ 78 | 79 | // assign the controls to the movie player 80 | [self.moviePlayer setControls:movieControls]; 81 | 82 | // add movie player to your view 83 | [self.view addSubview:self.moviePlayer.view]; 84 | 85 | //set contentURL (this will automatically start playing the movie) 86 | [self.moviePlayer setContentURL:[NSURL URLWithString:@"http://archive.org/download/WaltDisneyCartoons-MickeyMouseMinnieMouseDonaldDuckGoofyAndPluto/WaltDisneyCartoons-MickeyMouseMinnieMouseDonaldDuckGoofyAndPluto-HawaiianHoliday1937-Video.mp4"]]; 87 | ``` 88 | 89 | **On rotation:** 90 | 91 | ```objc 92 | if (!self.moviePlayer.isFullscreen) { 93 | [self.moviePlayer setFrame:frame]; 94 | //"frame" is whatever the movie player's frame should be at that given moment 95 | } 96 | ``` 97 | 98 | **Note:** you MUST use ```[ALMoviePlayerController setFrame:]``` to adjust frame, NOT ```[ALMoviePlayerController.view setFrame:]``` 99 | 100 | ### Delegate methods 101 | 102 | ```objc 103 | @required 104 | - (void)moviePlayerWillMoveFromWindow; 105 | ``` 106 | 107 | ```objc 108 | @optional 109 | - (void)movieTimedOut; 110 | ``` 111 | 112 | **Note:** ```moviePlayerWillMoveFromWindow``` is required for fullscreen mode to work properly. It should be used to re-add the movie player to your view controller's view (because during the transition to fullscreen, it was moved to ```[[UIApplication sharedApplication] keyWindow]```. 113 | 114 | Your code might look something like this: 115 | 116 | ```objc 117 | - (void)moviePlayerWillMoveFromWindow { 118 | if (![self.view.subviews containsObject:self.moviePlayer.view]) 119 | [self.view addSubview:self.moviePlayer.view]; 120 | 121 | [self.moviePlayer setFrame:frame]; 122 | } 123 | ``` 124 | 125 | ### Controls Properties 126 | 127 | ALMoviePlayerControls has the following editable properties: 128 | 129 | ```objc 130 | /** 131 | The style of the controls. Can be changed on the fly. 132 | 133 | Default value is ALMoviePlayerControlsStyleDefault 134 | */ 135 | @property (nonatomic, assign) ALMoviePlayerControlsStyle style; 136 | 137 | /** 138 | The state of the controls. 139 | */ 140 | @property (nonatomic, readonly) ALMoviePlayerControlsState state; 141 | 142 | /** 143 | The color of the control bars. 144 | 145 | Default value is black with a hint of transparency. 146 | */ 147 | @property (nonatomic, strong) UIColor *barColor; 148 | 149 | /** 150 | The height of the control bars. 151 | 152 | Default value is 70.f for iOS7+ and 50.f for previous versions. 153 | */ 154 | @property (nonatomic, assign) CGFloat barHeight; 155 | 156 | /** 157 | The amount of time that the controls should stay on screen before automatically hiding. 158 | 159 | Default value is 5 seconds. 160 | */ 161 | @property (nonatomic, assign) NSTimeInterval fadeDelay; 162 | 163 | /** 164 | The rate at which the movie should fastforward or rewind. 165 | 166 | Default value is 3x. 167 | */ 168 | @property (nonatomic, assign) float seekRate; 169 | 170 | /** 171 | Should the time-remaining number decrement as the video plays? 172 | 173 | Default value is NO. 174 | */ 175 | @property (nonatomic) BOOL timeRemainingDecrements; 176 | 177 | /** 178 | Are the controls currently showing on screen? 179 | */ 180 | @property (nonatomic, readonly, getter = isShowing) BOOL showing; 181 | ``` 182 | 183 | ### Controls Styles 184 | 185 | ```objc 186 | typedef enum { 187 | /** Controls will appear in a bottom bar */ 188 | ALMoviePlayerControlsStyleEmbedded, 189 | 190 | /** Controls will appear in a top bar and bottom bar */ 191 | ALMoviePlayerControlsStyleFullscreen, 192 | 193 | /** Controls will appear as ALMoviePlayerControlsStyleFullscreen when in fullscreen and ALMoviePlayerControlsStyleEmbedded at all other times */ 194 | ALMoviePlayerControlsStyleDefault, 195 | 196 | /** Controls will not appear */ 197 | ALMoviePlayerControlsStyleNone, 198 | 199 | } ALMoviePlayerControlsStyle; 200 | ``` 201 | 202 | ## Suggestions? 203 | 204 | If you have any suggestions, let me know! If you find any bugs, please open a new issue. 205 | 206 | ## Contact Me 207 | 208 | You can reach me anytime at the addresses below. If you use the library, feel free to give me a shoutout on Twitter to let me know how you like it. I'd love to hear your thoughts. 209 | 210 | Github: [lobianco](https://github.com/lobianco)
211 | Twitter: [@lobnco](https://twitter.com/lobnco)
212 | Email: [anthony@lobian.co](mailto:anthony@lobian.co) 213 | 214 | ## Credits & License 215 | 216 | ALMoviePlayerController is developed and maintained by Anthony Lobianco ([@lobnco](https://twitter.com/lobnco)). Licensed under the MIT License. Basically, I would appreciate attribution if you use it. 217 | 218 | Enjoy! 219 | 220 | (⌐■_■) 221 | --------------------------------------------------------------------------------