ZFOrientationObserver.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. // ZFOrentationObserver.m
  2. // ZFPlayer
  3. //
  4. // Copyright (c) 2016年 任子丰 ( http://github.com/renzifeng )
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. #import "ZFOrientationObserver.h"
  24. #import "ZFPlayer.h"
  25. #define SysVersion [[UIDevice currentDevice] systemVersion].floatValue
  26. @interface ZFFullViewController : UIViewController
  27. @property (nonatomic, assign) UIInterfaceOrientationMask interfaceOrientationMask;
  28. @end
  29. @implementation ZFFullViewController
  30. - (void)viewDidLoad {
  31. [super viewDidLoad];
  32. self.view.backgroundColor = [UIColor whiteColor];
  33. }
  34. - (BOOL)shouldAutorotate {
  35. return YES;
  36. }
  37. - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
  38. if (self.interfaceOrientationMask) {
  39. return self.interfaceOrientationMask;
  40. }
  41. return UIInterfaceOrientationMaskLandscape;
  42. }
  43. @end
  44. @interface UIWindow (CurrentViewController)
  45. /*!
  46. @method currentViewController
  47. @return Returns the topViewController in stack of topMostController.
  48. */
  49. + (UIViewController*)zf_currentViewController;
  50. @end
  51. @implementation UIWindow (CurrentViewController)
  52. + (UIViewController*)zf_currentViewController; {
  53. UIWindow *window = [[UIApplication sharedApplication].delegate window];
  54. UIViewController *topViewController = [window rootViewController];
  55. while (true) {
  56. if (topViewController.presentedViewController) {
  57. topViewController = topViewController.presentedViewController;
  58. } else if ([topViewController isKindOfClass:[UINavigationController class]] && [(UINavigationController*)topViewController topViewController]) {
  59. topViewController = [(UINavigationController *)topViewController topViewController];
  60. } else if ([topViewController isKindOfClass:[UITabBarController class]]) {
  61. UITabBarController *tab = (UITabBarController *)topViewController;
  62. topViewController = tab.selectedViewController;
  63. } else {
  64. break;
  65. }
  66. }
  67. return topViewController;
  68. }
  69. @end
  70. @interface ZFOrientationObserver ()
  71. @property (nonatomic, weak) UIView *view;
  72. @property (nonatomic, assign, getter=isFullScreen) BOOL fullScreen;
  73. @property (nonatomic, strong) UIView *cell;
  74. @property (nonatomic, assign) NSInteger playerViewTag;
  75. @property (nonatomic, assign) ZFRotateType roateType;
  76. @property (nonatomic, strong) UIView *blackView;
  77. @property (nonatomic, strong) UIWindow *customWindow;
  78. @end
  79. @implementation ZFOrientationObserver
  80. - (instancetype)init {
  81. self = [super init];
  82. if (self) {
  83. _duration = 0.30;
  84. _fullScreenMode = ZFFullScreenModeLandscape;
  85. _supportInterfaceOrientation = ZFInterfaceOrientationMaskAllButUpsideDown;
  86. _allowOrentitaionRotation = YES;
  87. _roateType = ZFRotateTypeNormal;
  88. _currentOrientation = UIInterfaceOrientationPortrait;
  89. }
  90. return self;
  91. }
  92. - (void)updateRotateView:(UIView *)rotateView
  93. containerView:(UIView *)containerView {
  94. self.view = rotateView;
  95. self.containerView = containerView;
  96. }
  97. - (void)cellModelRotateView:(UIView *)rotateView rotateViewAtCell:(UIView *)cell playerViewTag:(NSInteger)playerViewTag {
  98. self.roateType = ZFRotateTypeCell;
  99. self.view = rotateView;
  100. self.cell = cell;
  101. self.playerViewTag = playerViewTag;
  102. }
  103. - (void)cellOtherModelRotateView:(UIView *)rotateView containerView:(UIView *)containerView {
  104. self.roateType = ZFRotateTypeCellOther;
  105. self.view = rotateView;
  106. self.containerView = containerView;
  107. }
  108. - (void)dealloc {
  109. [self removeDeviceOrientationObserver];
  110. [self.blackView removeFromSuperview];
  111. }
  112. - (void)addDeviceOrientationObserver {
  113. if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
  114. [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
  115. }
  116. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeviceOrientationChange) name:UIDeviceOrientationDidChangeNotification object:nil];
  117. }
  118. - (void)removeDeviceOrientationObserver {
  119. if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
  120. [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
  121. }
  122. [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
  123. }
  124. - (void)handleDeviceOrientationChange {
  125. if (self.fullScreenMode == ZFFullScreenModePortrait || !self.allowOrentitaionRotation) return;
  126. UIInterfaceOrientation currentOrientation = UIInterfaceOrientationUnknown;
  127. if (UIDeviceOrientationIsValidInterfaceOrientation([UIDevice currentDevice].orientation)) {
  128. currentOrientation = (UIInterfaceOrientation)[UIDevice currentDevice].orientation;
  129. } else {
  130. return;
  131. }
  132. // Determine that if the current direction is the same as the direction you want to rotate, do nothing
  133. if (currentOrientation == _currentOrientation && !self.forceDeviceOrientation) return;
  134. switch (currentOrientation) {
  135. case UIInterfaceOrientationPortrait: {
  136. if ([self isSupportedPortrait]) {
  137. [self enterLandscapeFullScreen:UIInterfaceOrientationPortrait animated:YES];
  138. }
  139. }
  140. break;
  141. case UIInterfaceOrientationLandscapeLeft: {
  142. if ([self isSupportedLandscapeLeft]) {
  143. [self enterLandscapeFullScreen:UIInterfaceOrientationLandscapeLeft animated:YES];
  144. }
  145. }
  146. break;
  147. case UIInterfaceOrientationLandscapeRight: {
  148. if ([self isSupportedLandscapeRight]) {
  149. [self enterLandscapeFullScreen:UIInterfaceOrientationLandscapeRight animated:YES];
  150. }
  151. }
  152. break;
  153. default: break;
  154. }
  155. }
  156. - (void)forceDeviceOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated {
  157. UIView *superview = nil;
  158. if (UIInterfaceOrientationIsLandscape(orientation)) {
  159. /// It's not set from the other side of the screen to this side
  160. if (!self.isFullScreen) {
  161. self.view.frame = [self.view convertRect:self.view.frame toView:superview];
  162. }
  163. self.fullScreen = YES;
  164. superview = self.fullScreenContainerView;
  165. } else {
  166. if (!self.fullScreen) return;
  167. self.fullScreen = NO;
  168. if (self.roateType == ZFRotateTypeCell) superview = [self.cell viewWithTag:self.playerViewTag];
  169. else superview = self.containerView;
  170. if (self.blackView.superview != nil) [self.blackView removeFromSuperview];
  171. }
  172. if (self.orientationWillChange) self.orientationWillChange(self, self.isFullScreen);
  173. [UIViewController attemptRotationToDeviceOrientation];
  174. [superview addSubview:self.view];
  175. if (animated) {
  176. [UIView animateWithDuration:self.duration animations:^{
  177. self.view.frame = superview.bounds;
  178. [self.view layoutIfNeeded];
  179. [self interfaceOrientation:orientation];
  180. } completion:^(BOOL finished) {
  181. if (self.fullScreen) {
  182. [superview insertSubview:self.blackView belowSubview:self.view];
  183. self.blackView.frame = superview.bounds;
  184. }
  185. if (self.orientationDidChanged) self.orientationDidChanged(self, self.isFullScreen);
  186. }];
  187. } else {
  188. self.view.frame = superview.bounds;
  189. [self.view layoutIfNeeded];
  190. [UIView animateWithDuration:0 animations:^{
  191. [self interfaceOrientation:orientation];
  192. }];
  193. if (self.fullScreen) {
  194. [superview insertSubview:self.blackView belowSubview:self.view];
  195. self.blackView.frame = superview.bounds;
  196. }
  197. if (self.orientationDidChanged) self.orientationDidChanged(self, self.isFullScreen);
  198. }
  199. }
  200. - (void)normalOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated {
  201. UIView *superview = nil;
  202. CGRect frame;
  203. if (UIInterfaceOrientationIsLandscape(orientation)) {
  204. superview = self.fullScreenContainerView;
  205. /// It's not set from the other side of the screen to this side
  206. if (!self.isFullScreen) {
  207. self.view.frame = [self.view convertRect:self.view.frame toView:superview];
  208. }
  209. [superview addSubview:self.view];
  210. self.fullScreen = YES;
  211. if (self.orientationWillChange) self.orientationWillChange(self, self.isFullScreen);
  212. ZFFullViewController *fullVC = [[ZFFullViewController alloc] init];
  213. if (orientation == UIInterfaceOrientationLandscapeLeft) {
  214. fullVC.interfaceOrientationMask = UIInterfaceOrientationMaskLandscapeLeft;
  215. } else {
  216. fullVC.interfaceOrientationMask = UIInterfaceOrientationMaskLandscapeRight;
  217. }
  218. self.customWindow.rootViewController = fullVC;
  219. } else {
  220. self.fullScreen = NO;
  221. if (self.orientationWillChange) self.orientationWillChange(self, self.isFullScreen);
  222. ZFFullViewController *fullVC = [[ZFFullViewController alloc] init];
  223. fullVC.interfaceOrientationMask = UIInterfaceOrientationMaskPortrait;
  224. self.customWindow.rootViewController = fullVC;
  225. if (self.roateType == ZFRotateTypeCell) superview = [self.cell viewWithTag:self.playerViewTag];
  226. else superview = self.containerView;
  227. if (self.blackView.superview != nil) [self.blackView removeFromSuperview];
  228. }
  229. frame = [superview convertRect:superview.bounds toView:self.fullScreenContainerView];
  230. if (animated) {
  231. [UIView animateWithDuration:self.duration animations:^{
  232. self.view.transform = [self getTransformRotationAngle:orientation];
  233. [UIView animateWithDuration:self.duration animations:^{
  234. self.view.frame = frame;
  235. [self.view layoutIfNeeded];
  236. }];
  237. } completion:^(BOOL finished) {
  238. [superview addSubview:self.view];
  239. self.view.frame = superview.bounds;
  240. if (self.fullScreen) {
  241. [superview insertSubview:self.blackView belowSubview:self.view];
  242. self.blackView.frame = superview.bounds;
  243. }
  244. if (self.orientationDidChanged) self.orientationDidChanged(self, self.isFullScreen);
  245. }];
  246. } else {
  247. self.view.transform = [self getTransformRotationAngle:orientation];
  248. [superview addSubview:self.view];
  249. self.view.frame = superview.bounds;
  250. [self.view layoutIfNeeded];
  251. if (self.fullScreen) {
  252. [superview insertSubview:self.blackView belowSubview:self.view];
  253. self.blackView.frame = superview.bounds;
  254. }
  255. if (self.orientationDidChanged) self.orientationDidChanged(self, self.isFullScreen);
  256. }
  257. }
  258. - (void)interfaceOrientation:(UIInterfaceOrientation)orientation {
  259. if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
  260. SEL selector = NSSelectorFromString(@"setOrientation:");
  261. NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
  262. [invocation setSelector:selector];
  263. [invocation setTarget:[UIDevice currentDevice]];
  264. UIInterfaceOrientation val = orientation;
  265. [invocation setArgument:&val atIndex:2];
  266. [invocation invoke];
  267. }
  268. }
  269. /// Gets the rotation Angle of the transformation.
  270. - (CGAffineTransform)getTransformRotationAngle:(UIInterfaceOrientation)orientation {
  271. if (orientation == UIInterfaceOrientationPortrait) {
  272. return CGAffineTransformIdentity;
  273. } else if (orientation == UIInterfaceOrientationLandscapeLeft) {
  274. return CGAffineTransformMakeRotation(-M_PI_2);
  275. } else if(orientation == UIInterfaceOrientationLandscapeRight) {
  276. return CGAffineTransformMakeRotation(M_PI_2);
  277. }
  278. return CGAffineTransformIdentity;
  279. }
  280. #pragma mark - public
  281. - (void)enterLandscapeFullScreen:(UIInterfaceOrientation)orientation animated:(BOOL)animated {
  282. if (self.fullScreenMode == ZFFullScreenModePortrait) return;
  283. _currentOrientation = orientation;
  284. if (self.forceDeviceOrientation) {
  285. [self forceDeviceOrientation:orientation animated:animated];
  286. } else {
  287. [self normalOrientation:orientation animated:animated];
  288. }
  289. }
  290. - (void)enterPortraitFullScreen:(BOOL)fullScreen animated:(BOOL)animated {
  291. if (self.fullScreenMode == ZFFullScreenModeLandscape) return;
  292. UIView *superview = nil;
  293. if (fullScreen) {
  294. superview = self.fullScreenContainerView;
  295. self.view.frame = [self.view convertRect:self.view.frame toView:superview];
  296. [superview addSubview:self.view];
  297. self.fullScreen = YES;
  298. } else {
  299. if (self.roateType == ZFRotateTypeCell) {
  300. superview = [self.cell viewWithTag:self.playerViewTag];
  301. } else {
  302. superview = self.containerView;
  303. }
  304. self.fullScreen = NO;
  305. }
  306. if (self.orientationWillChange) self.orientationWillChange(self, self.isFullScreen);
  307. CGRect frame = [superview convertRect:superview.bounds toView:self.fullScreenContainerView];
  308. if (animated) {
  309. [UIView animateWithDuration:self.duration animations:^{
  310. self.view.frame = frame;
  311. [self.view layoutIfNeeded];
  312. } completion:^(BOOL finished) {
  313. [superview addSubview:self.view];
  314. self.view.frame = superview.bounds;
  315. if (self.orientationDidChanged) self.orientationDidChanged(self, self.isFullScreen);
  316. }];
  317. } else {
  318. [superview addSubview:self.view];
  319. self.view.frame = superview.bounds;
  320. [self.view layoutIfNeeded];
  321. if (self.orientationDidChanged) self.orientationDidChanged(self, self.isFullScreen);
  322. }
  323. }
  324. - (void)exitFullScreenWithAnimated:(BOOL)animated {
  325. if (self.fullScreenMode == ZFFullScreenModeLandscape) {
  326. [self enterLandscapeFullScreen:UIInterfaceOrientationPortrait animated:animated];
  327. } else if (self.fullScreenMode == ZFFullScreenModePortrait) {
  328. [self enterPortraitFullScreen:NO animated:animated];
  329. }
  330. }
  331. #pragma mark - private
  332. /// is support portrait
  333. - (BOOL)isSupportedPortrait {
  334. return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskPortrait;
  335. }
  336. /// is support landscapeLeft
  337. - (BOOL)isSupportedLandscapeLeft {
  338. return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeLeft;
  339. }
  340. /// is support landscapeRight
  341. - (BOOL)isSupportedLandscapeRight {
  342. return self.supportInterfaceOrientation & ZFInterfaceOrientationMaskLandscapeRight;
  343. }
  344. #pragma mark - getter
  345. - (UIView *)blackView {
  346. if (!_blackView) {
  347. _blackView = [UIView new];
  348. _blackView.backgroundColor = [UIColor blackColor];
  349. }
  350. return _blackView;
  351. }
  352. - (UIWindow *)customWindow {
  353. if (!_customWindow) {
  354. if (@available(iOS 13.0, *)) {
  355. UIWindowScene *windowScene = nil;
  356. for (UIScene *scene in [UIApplication sharedApplication].connectedScenes) {
  357. if (scene.activationState == UISceneActivationStateForegroundActive) {
  358. windowScene = (UIWindowScene *)scene;
  359. }
  360. if (!windowScene && [UIApplication sharedApplication].connectedScenes.count == 1) {
  361. windowScene = (UIWindowScene *)scene;
  362. }
  363. }
  364. if (windowScene) {
  365. _customWindow = [[UIWindow alloc] initWithWindowScene:windowScene];
  366. } else {
  367. _customWindow = [[UIWindow alloc] initWithFrame:CGRectZero];
  368. }
  369. } else {
  370. _customWindow = [[UIWindow alloc] initWithFrame:CGRectZero];
  371. }
  372. }
  373. return _customWindow;
  374. }
  375. #pragma mark - setter
  376. - (void)setLockedScreen:(BOOL)lockedScreen {
  377. _lockedScreen = lockedScreen;
  378. if (lockedScreen) {
  379. [self removeDeviceOrientationObserver];
  380. } else {
  381. [self addDeviceOrientationObserver];
  382. }
  383. }
  384. - (UIView *)fullScreenContainerView {
  385. if (!_fullScreenContainerView) {
  386. _fullScreenContainerView = [UIApplication sharedApplication].keyWindow;
  387. }
  388. return _fullScreenContainerView;
  389. }
  390. - (void)setFullScreen:(BOOL)fullScreen {
  391. _fullScreen = fullScreen;
  392. [[UIWindow zf_currentViewController] setNeedsStatusBarAppearanceUpdate];
  393. [UIViewController attemptRotationToDeviceOrientation];
  394. }
  395. - (void)setStatusBarHidden:(BOOL)statusBarHidden {
  396. _statusBarHidden = statusBarHidden;
  397. [[UIWindow zf_currentViewController] setNeedsStatusBarAppearanceUpdate];
  398. }
  399. @end