123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- //
- // FLEXWindowManagerController.m
- // FLEX
- //
- // Created by Tanner on 2/6/20.
- // Copyright © 2020 FLEX Team. All rights reserved.
- //
- #import "FLEXWindowManagerController.h"
- #import "FLEXManager+Private.h"
- #import "FLEXUtility.h"
- #import "FLEXObjectExplorerFactory.h"
- #import <TargetConditionals.h>
- @interface FLEXWindowManagerController ()
- @property (nonatomic) UIWindow *keyWindow;
- @property (nonatomic, copy) NSString *keyWindowSubtitle;
- @property (nonatomic, copy) NSArray<UIWindow *> *windows;
- @property (nonatomic, copy) NSArray<NSString *> *windowSubtitles;
- @property (nonatomic, copy) NSArray<UIScene *> *scenes API_AVAILABLE(ios(13));
- @property (nonatomic, copy) NSArray<NSString *> *sceneSubtitles;
- @property (nonatomic, copy) NSArray<NSArray *> *sections;
- @end
- @implementation FLEXWindowManagerController
- #pragma mark - Initialization
- - (id)init {
- return [self initWithStyle:UITableViewStylePlain];
- }
- - (void)viewDidLoad {
- [super viewDidLoad];
-
- self.title = @"Windows";
- if (@available(iOS 13, *)) {
- self.title = @"Windows and Scenes";
- }
-
- [self disableToolbar];
- [self reloadData];
- }
- #pragma mark - Private
- - (void)reloadData {
- self.keyWindow = UIApplication.sharedApplication.keyWindow;
- self.windows = UIApplication.sharedApplication.windows;
- self.keyWindowSubtitle = self.windowSubtitles[[self.windows indexOfObject:self.keyWindow]];
- self.windowSubtitles = [self.windows flex_mapped:^id(UIWindow *window, NSUInteger idx) {
- return [NSString stringWithFormat:@"Level: %@ — Root: %@",
- @(window.windowLevel), window.rootViewController
- ];
- }];
-
- if (@available(iOS 13, *)) {
- self.scenes = UIApplication.sharedApplication.connectedScenes.allObjects;
- self.sceneSubtitles = [self.scenes flex_mapped:^id(UIScene *scene, NSUInteger idx) {
- return [self sceneDescription:scene];
- }];
-
- self.sections = @[@[self.keyWindow], self.windows, self.scenes];
- } else {
- self.sections = @[@[self.keyWindow], self.windows];
- }
-
- [self.tableView reloadData];
- }
- - (void)dismissAnimated {
- [self dismissViewControllerAnimated:YES completion:nil];
- }
- - (void)showRevertOrDismissAlert:(void(^)())revertBlock {
- [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
- [self reloadData];
- [self.tableView reloadData];
-
- UIWindow *highestWindow = UIApplication.sharedApplication.keyWindow;
- UIWindowLevel maxLevel = 0;
- for (UIWindow *window in UIApplication.sharedApplication.windows) {
- if (window.windowLevel > maxLevel) {
- maxLevel = window.windowLevel;
- highestWindow = window;
- }
- }
-
- [FLEXAlert makeAlert:^(FLEXAlert *make) {
- make.title(@"Keep Changes?");
- make.message(@"If you do not wish to keep these settings, choose 'Revert Changes' below.");
-
- make.button(@"Keep Changes").destructiveStyle();
- make.button(@"Keep Changes and Dismiss").destructiveStyle().handler(^(NSArray<NSString *> *strings) {
- [self dismissAnimated];
- });
- make.button(@"Revert Changes").cancelStyle().handler(^(NSArray<NSString *> *strings) {
- revertBlock();
- [self reloadData];
- [self.tableView reloadData];
- });
- } showFrom:[FLEXUtility topViewControllerInWindow:highestWindow]];
- }
- - (NSString *)sceneDescription:(UIScene *)scene API_AVAILABLE(ios(13)) {
- NSString *state = [self stringFromSceneState:scene.activationState];
- NSString *title = scene.title.length ? scene.title : nil;
- NSString *suffix = nil;
-
- if ([scene isKindOfClass:[UIWindowScene class]]) {
- UIWindowScene *windowScene = (id)scene;
- suffix = FLEXPluralString(windowScene.windows.count, @"windows", @"window");
- }
-
- NSMutableString *description = state.mutableCopy;
- if (title) {
- [description appendFormat:@" — %@", title];
- }
- if (suffix) {
- [description appendFormat:@" — %@", suffix];
- }
-
- return description.copy;
- }
- - (NSString *)stringFromSceneState:(UISceneActivationState)state API_AVAILABLE(ios(13)) {
- switch (state) {
- case UISceneActivationStateUnattached:
- return @"Unattached";
- case UISceneActivationStateForegroundActive:
- return @"Active";
- case UISceneActivationStateForegroundInactive:
- return @"Inactive";
- case UISceneActivationStateBackground:
- return @"Backgrounded";
- }
-
- return [NSString stringWithFormat:@"Unknown state: %@", @(state)];
- }
- #pragma mark - Table View Data Source
- - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
- return self.sections.count;
- }
- - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- return self.sections[section].count;
- }
- - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
- switch (section) {
- case 0: return @"Key Window";
- case 1: return @"Windows";
- case 2: return @"Connected Scenes";
- }
-
- return nil;
- }
- - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
- UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath];
- #if !TARGET_OS_TV
- cell.accessoryType = UITableViewCellAccessoryDetailButton;
- #endif
- cell.textLabel.lineBreakMode = NSLineBreakByTruncatingTail;
-
- UIWindow *window = nil;
- NSString *subtitle = nil;
-
- switch (indexPath.section) {
- case 0:
- window = self.keyWindow;
- subtitle = self.keyWindowSubtitle;
- break;
- case 1:
- window = self.windows[indexPath.row];
- subtitle = self.windowSubtitles[indexPath.row];
- break;
- case 2:
- if (@available(iOS 13, *)) {
- UIScene *scene = self.scenes[indexPath.row];
- cell.textLabel.text = scene.description;
- cell.detailTextLabel.text = self.sceneSubtitles[indexPath.row];
- return cell;
- }
- }
-
- cell.textLabel.text = window.description;
- cell.detailTextLabel.text = [NSString
- stringWithFormat:@"Level: %@ — Root: %@",
- @((NSInteger)window.windowLevel), window.rootViewController.class
- ];
-
- return cell;
- }
- #pragma mark - Table View Delegate
- - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
- UIWindow *window = nil;
- NSString *subtitle = nil;
- FLEXWindow *flex = FLEXManager.sharedManager.explorerWindow;
-
- id cancelHandler = ^{
- [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
- };
-
- switch (indexPath.section) {
- case 0:
- window = self.keyWindow;
- subtitle = self.keyWindowSubtitle;
- break;
- case 1:
- window = self.windows[indexPath.row];
- subtitle = self.windowSubtitles[indexPath.row];
- break;
- case 2:
- if (@available(iOS 13, *)) {
- UIScene *scene = self.scenes[indexPath.row];
- UIWindowScene *oldScene = flex.windowScene;
- BOOL isWindowScene = [scene isKindOfClass:[UIWindowScene class]];
- BOOL isFLEXScene = isWindowScene ? flex.windowScene == scene : NO;
-
- [FLEXAlert makeAlert:^(FLEXAlert *make) {
- make.title(NSStringFromClass(scene.class));
-
- if (isWindowScene) {
- if (isFLEXScene) {
- make.message(@"Already the FLEX window scene");
- }
-
- make.button(@"Set as FLEX Window Scene")
- .handler(^(NSArray<NSString *> *strings) {
- flex.windowScene = (id)scene;
- [self showRevertOrDismissAlert:^{
- flex.windowScene = oldScene;
- }];
- }).enabled(!isFLEXScene);
- make.button(@"Cancel").cancelStyle();
- } else {
- make.message(@"Not a UIWindowScene");
- make.button(@"Dismiss").cancelStyle().handler(cancelHandler);
- }
- } showFrom:self];
- }
- }
- __block UIWindow *targetWindow = nil, *oldKeyWindow = nil;
- __block UIWindowLevel oldLevel;
- __block BOOL wasVisible;
-
- subtitle = [subtitle stringByAppendingString:
- @"\n\n1) Adjust the FLEX window level relative to this window,\n"
- "2) adjust this window's level relative to the FLEX window,\n"
- "3) set this window's level to a specific value, or\n"
- "4) make this window the key window if it isn't already."
- ];
-
- [FLEXAlert makeAlert:^(FLEXAlert *make) {
- make.title(NSStringFromClass(window.class)).message(subtitle);
- make.button(@"Adjust FLEX Window Level").handler(^(NSArray<NSString *> *strings) {
- targetWindow = flex; oldLevel = flex.windowLevel;
- flex.windowLevel = window.windowLevel + strings.firstObject.integerValue;
-
- [self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }];
- });
- make.button(@"Adjust This Window's Level").handler(^(NSArray<NSString *> *strings) {
- targetWindow = window; oldLevel = window.windowLevel;
- window.windowLevel = flex.windowLevel + strings.firstObject.integerValue;
-
- [self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }];
- });
- make.button(@"Set This Window's Level").handler(^(NSArray<NSString *> *strings) {
- targetWindow = window; oldLevel = window.windowLevel;
- window.windowLevel = strings.firstObject.integerValue;
-
- [self showRevertOrDismissAlert:^{ targetWindow.windowLevel = oldLevel; }];
- });
- make.button(@"Make Key And Visible").handler(^(NSArray<NSString *> *strings) {
- oldKeyWindow = UIApplication.sharedApplication.keyWindow;
- wasVisible = window.hidden;
- [window makeKeyAndVisible];
-
- [self showRevertOrDismissAlert:^{
- window.hidden = wasVisible;
- [oldKeyWindow makeKeyWindow];
- }];
- }).enabled(!window.isKeyWindow && !window.hidden);
- make.button(@"Cancel").cancelStyle().handler(cancelHandler);
-
- make.textField(@"+/- window level, i.e. 5 or -10");
- } showFrom:self];
- }
- - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)ip {
- [self.navigationController pushViewController:
- [FLEXObjectExplorerFactory explorerViewControllerForObject:self.sections[ip.section][ip.row]]
- animated:YES];
- }
- @end
|