276 lines
11 KiB
Objective-C
Executable File
276 lines
11 KiB
Objective-C
Executable File
//
|
|
// GKRotationManager.m
|
|
// Example
|
|
//
|
|
// Created by QuintGao on 2023/3/31.
|
|
// Copyright © 2023 QuintGao. All rights reserved.
|
|
//
|
|
|
|
#import "GKRotationManager.h"
|
|
#import "GKLandscapeWindow.h"
|
|
#import "GKLandscapeViewController.h"
|
|
#import "GKRotationManager_iOS_9_15.h"
|
|
#import "GKRotationManager_iOS_16_Later.h"
|
|
#import <objc/message.h>
|
|
|
|
@interface GKRotationManager()<GKLandscapeViewControllerDelegate>
|
|
|
|
@property (nonatomic, assign) BOOL isGeneratingDeviceOrientation;
|
|
|
|
@end
|
|
|
|
@implementation GKRotationManager
|
|
|
|
+ (GKInterfaceOrientationMask)supportedInterfaceOrientationsForWindow:(UIWindow *)window {
|
|
if ([window isKindOfClass:GKLandscapeWindow.class]) {
|
|
GKRotationManager *manager = [(GKLandscapeWindow *)window rotationManager];
|
|
if (manager != nil) {
|
|
return (GKInterfaceOrientationMask)[manager supportedInterfaceOrientationsForWindow:window];
|
|
}
|
|
}
|
|
return GKInterfaceOrientationMaskUnknow;
|
|
}
|
|
|
|
+ (instancetype)rotationManager {
|
|
if (@available(iOS 16.0, *)) {
|
|
return [[GKRotationManager_iOS_16_Later alloc] _init];
|
|
}else {
|
|
return [[GKRotationManager_iOS_9_15 alloc] _init];
|
|
}
|
|
}
|
|
|
|
- (instancetype)_init {
|
|
if (self = [super init]) {
|
|
self.currentOrientation = UIInterfaceOrientationPortrait;
|
|
self.supportInterfaceOrientation = GKInterfaceOrientationMaskAll;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)addDeviceOrientationObserver {
|
|
self.isGeneratingDeviceOrientation = UIDevice.currentDevice.isGeneratingDeviceOrientationNotifications;
|
|
if (!self.isGeneratingDeviceOrientation) {
|
|
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
|
|
}
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeviceOrientationChange) name:UIDeviceOrientationDidChangeNotification object:nil];
|
|
}
|
|
|
|
- (void)removeDeviceOrientationObserver {
|
|
if (!self.isGeneratingDeviceOrientation) {
|
|
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
|
|
}
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
|
|
}
|
|
|
|
- (void)dealloc {
|
|
#ifdef DEBUG
|
|
NSLog(@"%d -- [%@ %s]", (int)__LINE__, NSStringFromClass(self.class), sel_getName(_cmd));
|
|
#endif
|
|
[_window setHidden:YES];
|
|
[self removeDeviceOrientationObserver];
|
|
}
|
|
|
|
- (void)setAllowOrientationRotation:(BOOL)allowOrientationRotation {
|
|
_allowOrientationRotation = allowOrientationRotation;
|
|
|
|
if (allowOrientationRotation) {
|
|
[self addDeviceOrientationObserver];
|
|
}else {
|
|
[self removeDeviceOrientationObserver];
|
|
}
|
|
}
|
|
|
|
- (UIInterfaceOrientation)currentDeviceOrientation {
|
|
return (UIInterfaceOrientation)UIDevice.currentDevice.orientation;
|
|
}
|
|
|
|
- (void)rotate {
|
|
UIInterfaceOrientation orientation = self.currentOrientation == UIInterfaceOrientationPortrait ? UIInterfaceOrientationLandscapeRight : UIInterfaceOrientationPortrait;
|
|
[self rotateToOrientation:orientation animated:YES];
|
|
}
|
|
|
|
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated {
|
|
[self rotateToOrientation:orientation animated:animated completion:nil];
|
|
}
|
|
|
|
- (void)rotateToOrientation:(UIInterfaceOrientation)orientation animated:(BOOL)animated completion:(void (^)(void))completion {
|
|
self.currentOrientation = orientation;
|
|
if (UIInterfaceOrientationIsLandscape(orientation)) {
|
|
if (!self.window) {
|
|
self.window = [[GKLandscapeWindow alloc] init];
|
|
self.window.rootViewController = self.landscapeViewController;
|
|
self.window.rotationManager = self;
|
|
}
|
|
}
|
|
self.disableAnimations = !animated;
|
|
if (UIDevice.currentDevice.systemVersion.doubleValue < 16.0) {
|
|
[self interfaceOrientation:UIInterfaceOrientationUnknown completion:nil];
|
|
}
|
|
[self interfaceOrientation:orientation completion:completion];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation GKRotationManager (Internal)
|
|
|
|
@dynamic disableAnimations;
|
|
|
|
- (void)setDisableAnimations:(BOOL)disableAnimations {
|
|
objc_setAssociatedObject(self, @selector(disableAnimations), @(disableAnimations), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
}
|
|
|
|
- (BOOL)disableAnimations {
|
|
return [objc_getAssociatedObject(self, _cmd) boolValue];
|
|
}
|
|
|
|
- (UIInterfaceOrientationMask)supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window {
|
|
@throw [NSException exceptionWithName:NSInternalInconsistencyException
|
|
reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)]
|
|
userInfo:nil];
|
|
}
|
|
|
|
- (UIInterfaceOrientation)getCurrentOrientation {
|
|
if (@available(iOS 16.0, *)) {
|
|
NSArray *array = UIApplication.sharedApplication.connectedScenes.allObjects;
|
|
UIWindowScene *scene = array.firstObject;
|
|
return scene.interfaceOrientation;
|
|
}else {
|
|
return self.currentDeviceOrientation;
|
|
}
|
|
}
|
|
|
|
- (GKLandscapeViewController *)landscapeViewController {
|
|
@throw [NSException exceptionWithName:NSInternalInconsistencyException
|
|
reason:[NSString stringWithFormat:@"You must override %@ in a subclass.", NSStringFromSelector(_cmd)]
|
|
userInfo:nil];
|
|
}
|
|
|
|
- (void)interfaceOrientation:(UIInterfaceOrientation)orientation completion:(void (^)(void))completion {
|
|
// subclass implementation
|
|
}
|
|
|
|
- (void)handleDeviceOrientationChange {
|
|
if (!self.allowOrientationRotation) return;
|
|
if (!UIDeviceOrientationIsValidInterfaceOrientation([UIDevice currentDevice].orientation)) return;
|
|
UIInterfaceOrientation currentOrientation = self.currentDeviceOrientation;
|
|
if (currentOrientation == self.currentOrientation) return;
|
|
self.currentOrientation = currentOrientation;
|
|
if (currentOrientation == UIInterfaceOrientationPortraitUpsideDown) return;
|
|
|
|
switch (currentOrientation) {
|
|
case UIInterfaceOrientationPortrait: {
|
|
if ([self _isSupportedPortrait]) {
|
|
[self rotateToOrientation:UIInterfaceOrientationPortrait animated:YES];
|
|
}
|
|
}
|
|
break;
|
|
case UIInterfaceOrientationLandscapeLeft: {
|
|
if ([self _isSupportedLandscapeLeft]) {
|
|
[self rotateToOrientation:UIInterfaceOrientationLandscapeLeft animated:YES];
|
|
}
|
|
}
|
|
break;
|
|
case UIInterfaceOrientationLandscapeRight: {
|
|
if ([self _isSupportedLandscapeRight]) {
|
|
[self rotateToOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
|
|
}
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
- (BOOL)isSupportInterfaceOrientation:(UIInterfaceOrientation)orientation {
|
|
if (orientation == UIInterfaceOrientationPortrait) {
|
|
return [self _isSupportedPortrait];
|
|
}else if (orientation == UIInterfaceOrientationLandscapeLeft) {
|
|
return [self _isSupportedLandscapeLeft];
|
|
}else if (orientation == UIInterfaceOrientationLandscapeRight) {
|
|
return [self _isSupportedLandscapeRight];
|
|
}else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
|
|
return [self _isSupportedPortraitUpsideDown];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (void)willChangeOrientation:(UIInterfaceOrientation)orientation {
|
|
_isFullscreen = UIInterfaceOrientationIsLandscape(orientation);
|
|
!self.orientationWillChange ?: self.orientationWillChange(_isFullscreen);
|
|
}
|
|
|
|
- (void)didChangeOrientation:(UIInterfaceOrientation)orientation {
|
|
_isFullscreen = UIInterfaceOrientationIsLandscape(orientation);
|
|
!self.orientationDidChanged ?: self.orientationDidChanged(_isFullscreen);
|
|
}
|
|
|
|
- (BOOL)_isSupportedPortrait {
|
|
return self.supportInterfaceOrientation & GKInterfaceOrientationMaskPortrait;
|
|
}
|
|
|
|
- (BOOL)_isSupportedPortraitUpsideDown {
|
|
return self.supportInterfaceOrientation & GKInterfaceOrientationMaskPortraitUpsideDown;
|
|
}
|
|
|
|
- (BOOL)_isSupportedLandscapeLeft {
|
|
return self.supportInterfaceOrientation & GKInterfaceOrientationMaskLandscapeLeft;
|
|
}
|
|
|
|
- (BOOL)_isSupportedLandscapeRight {
|
|
return self.supportInterfaceOrientation & GKInterfaceOrientationMaskLandscapeRight;
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark - fix safe area
|
|
|
|
API_DEPRECATED("deprecated!", ios(13.0, 16.0)) @implementation GKRotationManager (GKRotationSafeAreaFixing)
|
|
+ (void)initialize {
|
|
if (@available(iOS 16.0, *)) return;
|
|
if (@available(iOS 13.0, *)) {
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
Class cls = UIViewController.class;
|
|
NSData *data = [NSData.alloc initWithBase64EncodedString:@"X3NldENvbnRlbnRPdmVybGF5SW5zZXRzOmFuZExlZnRNYXJnaW46cmlnaHRNYXJnaW46" options:kNilOptions];
|
|
NSString *method = [NSString.alloc initWithData:data encoding:NSUTF8StringEncoding];
|
|
SEL originalSelector = NSSelectorFromString(method);
|
|
SEL swizzledSelector = NSSelectorFromString([@"gk" stringByAppendingString:method]);
|
|
|
|
Method originalMethod = class_getInstanceMethod(cls, originalSelector);
|
|
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector);
|
|
if ( originalMethod != NULL ) method_exchangeImplementations(originalMethod, swizzledMethod);
|
|
});
|
|
}
|
|
}
|
|
@end
|
|
|
|
API_DEPRECATED("deprecated!", ios(13.0, 16.0)) @implementation UIViewController (GKRotationSafeAreaFixing)
|
|
- (void)gk_setContentOverlayInsets:(UIEdgeInsets)insets andLeftMargin:(CGFloat)leftMargin rightMargin:(CGFloat)rightMargin {
|
|
GKSafeAreaInsetsMask mask = self.disabledAdjustSafeAreaInsetsMask;
|
|
if (mask & GKSafeAreaInsetsMaskTop) insets.top = 0;
|
|
if (mask & GKSafeAreaInsetsMaskLeft) insets.left = 0;
|
|
if (mask & GKSafeAreaInsetsMaskBottom) insets.bottom = 0;
|
|
if (mask & GKSafeAreaInsetsMaskRight) insets.right = 0;
|
|
|
|
UIWindow *keyWindow = UIApplication.sharedApplication.keyWindow;
|
|
UIWindow *otherWindow = self.view.window;
|
|
if ([keyWindow isKindOfClass:GKLandscapeWindow.class] && otherWindow != nil) {
|
|
GKRotationManager *manager = ((GKLandscapeWindow *)keyWindow).rotationManager;
|
|
UIWindow *superviewWindow = manager.containerView.window;
|
|
if (superviewWindow != otherWindow) {
|
|
[self gk_setContentOverlayInsets:insets andLeftMargin:leftMargin rightMargin:rightMargin];
|
|
}
|
|
}else {
|
|
[self gk_setContentOverlayInsets:insets andLeftMargin:leftMargin rightMargin:rightMargin];
|
|
}
|
|
}
|
|
|
|
- (void)setDisabledAdjustSafeAreaInsetsMask:(GKSafeAreaInsetsMask)disabledAdjustSafeAreaInsetsMask {
|
|
objc_setAssociatedObject(self, @selector(disabledAdjustSafeAreaInsetsMask), @(disabledAdjustSafeAreaInsetsMask), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
}
|
|
|
|
- (GKSafeAreaInsetsMask)disabledAdjustSafeAreaInsetsMask {
|
|
return [objc_getAssociatedObject(self, _cmd) integerValue];
|
|
}
|
|
|
|
@end
|