mirror of
https://github.com/SoPat712/YTLitePlus.git
synced 2025-08-24 11:28:52 -04:00
271 lines
9.0 KiB
Objective-C
271 lines
9.0 KiB
Objective-C
//
|
|
// FHSViewController.m
|
|
// FLEX
|
|
//
|
|
// Created by Tanner Bennett on 1/6/20.
|
|
// Copyright © 2020 FLEX Team. All rights reserved.
|
|
//
|
|
|
|
#import "FHSViewController.h"
|
|
#import "FHSSnapshotView.h"
|
|
#import "FLEXHierarchyViewController.h"
|
|
#import "FLEXColor.h"
|
|
#import "FLEXAlert.h"
|
|
#import "FLEXWindow.h"
|
|
#import "FLEXResources.h"
|
|
#import "NSArray+FLEX.h"
|
|
#import "UIBarButtonItem+FLEX.h"
|
|
|
|
BOOL const kFHSViewControllerExcludeFLEXWindows = YES;
|
|
|
|
@interface FHSViewController () <FHSSnapshotViewDelegate>
|
|
/// An array of only the target views whose hierarchies
|
|
/// we wish to snapshot, not every view in the snapshot.
|
|
@property (nonatomic, readonly) NSArray<UIView *> *targetViews;
|
|
@property (nonatomic, readonly) NSArray<FHSView *> *views;
|
|
@property (nonatomic ) NSArray<FHSViewSnapshot *> *snapshots;
|
|
@property (nonatomic, ) FHSSnapshotView *snapshotView;
|
|
|
|
@property (nonatomic, readonly) UIView *containerView;
|
|
@property (nonatomic, readonly) NSArray<UIView *> *viewsAtTap;
|
|
@property (nonatomic, readonly) NSMutableSet<Class> *forceHideHeaders;
|
|
@end
|
|
|
|
@implementation FHSViewController
|
|
@synthesize views = _views;
|
|
@synthesize snapshotView = _snapshotView;
|
|
|
|
#pragma mark - Initialization
|
|
|
|
+ (instancetype)snapshotWindows:(NSArray<UIWindow *> *)windows {
|
|
return [[self alloc] initWithViews:windows viewsAtTap:nil selectedView:nil];
|
|
}
|
|
|
|
+ (instancetype)snapshotView:(UIView *)view {
|
|
return [[self alloc] initWithViews:@[view] viewsAtTap:nil selectedView:nil];
|
|
}
|
|
|
|
+ (instancetype)snapshotViewsAtTap:(NSArray<UIView *> *)viewsAtTap selectedView:(UIView *)view {
|
|
NSParameterAssert(viewsAtTap.count);
|
|
NSParameterAssert(view.window);
|
|
return [[self alloc] initWithViews:@[view.window] viewsAtTap:viewsAtTap selectedView:view];
|
|
}
|
|
|
|
- (id)initWithViews:(NSArray<UIView *> *)views
|
|
viewsAtTap:(NSArray<UIView *> *)viewsAtTap
|
|
selectedView:(UIView *)view {
|
|
NSParameterAssert(views.count);
|
|
|
|
self = [super init];
|
|
if (self) {
|
|
_forceHideHeaders = [NSMutableSet setWithObject:NSClassFromString(@"_UITableViewCellSeparatorView")];
|
|
_selectedView = view;
|
|
_viewsAtTap = viewsAtTap;
|
|
|
|
if (!viewsAtTap && kFHSViewControllerExcludeFLEXWindows) {
|
|
Class flexwindow = [FLEXWindow class];
|
|
views = [views flex_filtered:^BOOL(UIView *view, NSUInteger idx) {
|
|
return [view class] != flexwindow;
|
|
}];
|
|
}
|
|
|
|
_targetViews = views;
|
|
_views = [views flex_mapped:^id(UIView *view, NSUInteger idx) {
|
|
BOOL isScrollView = [view.superview isKindOfClass:[UIScrollView class]];
|
|
return [FHSView forView:view isInScrollView:isScrollView];
|
|
}];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)refreshSnapshotView {
|
|
// Alert view to block interaction while we load everything
|
|
UIAlertController *loading = [FLEXAlert makeAlert:^(FLEXAlert *make) {
|
|
make.title(@"Please Wait").message(@"Generating snapshot…");
|
|
}];
|
|
[self presentViewController:loading animated:YES completion:^{
|
|
self.snapshots = [self.views flex_mapped:^id(FHSView *view, NSUInteger idx) {
|
|
return [FHSViewSnapshot snapshotWithView:view];
|
|
}];
|
|
FHSSnapshotView *newSnapshotView = [FHSSnapshotView delegate:self];
|
|
|
|
// This work is highly intensive so we do it on a background thread first
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
|
// Setting the snapshots computes lots of SCNNodes, takes several seconds
|
|
newSnapshotView.snapshots = self.snapshots;
|
|
|
|
// After we finish generating all the model objects and scene nodes, display the view
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
// Dismiss alert
|
|
[loading dismissViewControllerAnimated:YES completion:nil];
|
|
|
|
self.snapshotView = newSnapshotView;
|
|
});
|
|
});
|
|
}];
|
|
}
|
|
|
|
|
|
#pragma mark - View Controller Lifecycle
|
|
|
|
- (void)loadView {
|
|
[super loadView];
|
|
self.view.backgroundColor = FLEXColor.primaryBackgroundColor;
|
|
}
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
|
|
// Initialize back bar button item for 3D view to look like a button
|
|
self.navigationItem.hidesBackButton = YES;
|
|
self.navigationItem.leftBarButtonItem = [UIBarButtonItem
|
|
flex_itemWithImage:FLEXResources.toggle2DIcon
|
|
target:self.navigationController
|
|
action:@selector(toggleHierarchyMode)
|
|
];
|
|
}
|
|
|
|
- (void)viewDidAppear:(BOOL)animated {
|
|
[super viewDidAppear:animated];
|
|
|
|
if (!_snapshotView) {
|
|
[self refreshSnapshotView];
|
|
}
|
|
}
|
|
|
|
|
|
#pragma mark - Public
|
|
|
|
- (void)setSelectedView:(UIView *)view {
|
|
_selectedView = view;
|
|
self.snapshotView.selectedView = view ? [self snapshotForView:view] : nil;
|
|
}
|
|
|
|
|
|
#pragma mark - Private
|
|
|
|
#pragma mark Properties
|
|
|
|
- (FHSSnapshotView *)snapshotView {
|
|
return self.isViewLoaded ? _snapshotView : nil;
|
|
}
|
|
|
|
- (void)setSnapshotView:(FHSSnapshotView *)snapshotView {
|
|
NSParameterAssert(snapshotView);
|
|
|
|
_snapshotView = snapshotView;
|
|
|
|
// Initialize our toolbar items
|
|
self.toolbarItems = @[
|
|
[UIBarButtonItem flex_itemWithCustomView:snapshotView.spacingSlider],
|
|
UIBarButtonItem.flex_flexibleSpace,
|
|
[UIBarButtonItem
|
|
flex_itemWithImage:FLEXResources.moreIcon
|
|
target:self action:@selector(didPressOptionsButton:)
|
|
],
|
|
UIBarButtonItem.flex_flexibleSpace,
|
|
[UIBarButtonItem flex_itemWithCustomView:snapshotView.depthSlider]
|
|
];
|
|
[self resizeToolbarItems:self.view.frame.size];
|
|
|
|
// If we have views-at-tap, dim the other views
|
|
[snapshotView emphasizeViews:self.viewsAtTap];
|
|
// Set the selected view, if any
|
|
snapshotView.selectedView = [self snapshotForView:self.selectedView];
|
|
snapshotView.headerExclusions = self.forceHideHeaders.allObjects;
|
|
[snapshotView setNeedsLayout];
|
|
|
|
// Remove old snapshot, if any, and add the new one
|
|
[_snapshotView removeFromSuperview];
|
|
snapshotView.frame = self.containerView.bounds;
|
|
[self.containerView addSubview:snapshotView];
|
|
}
|
|
|
|
- (UIView *)containerView {
|
|
return self.view;
|
|
}
|
|
|
|
#pragma mark Helper
|
|
|
|
- (FHSViewSnapshot *)snapshotForView:(UIView *)view {
|
|
if (!view || !self.snapshots.count) return nil;
|
|
|
|
for (FHSViewSnapshot *snapshot in self.snapshots) {
|
|
FHSViewSnapshot *found = [snapshot snapshotForView:view];
|
|
if (found) {
|
|
return found;
|
|
}
|
|
}
|
|
|
|
// Error: we have snapshots but the view we requested is not in one
|
|
@throw NSInternalInconsistencyException;
|
|
return nil;
|
|
}
|
|
|
|
#pragma mark Events
|
|
|
|
- (void)didPressOptionsButton:(UIBarButtonItem *)sender {
|
|
[FLEXAlert makeSheet:^(FLEXAlert *make) {
|
|
if (self.selectedView) {
|
|
make.button(@"Hide selected view").handler(^(NSArray<NSString *> *strings) {
|
|
[self.snapshotView hideView:[self snapshotForView:self.selectedView]];
|
|
});
|
|
make.button(@"Hide headers for views like this").handler(^(NSArray<NSString *> *strings) {
|
|
Class cls = [self.selectedView class];
|
|
if (![self.forceHideHeaders containsObject:cls]) {
|
|
[self.forceHideHeaders addObject:[self.selectedView class]];
|
|
self.snapshotView.headerExclusions = self.forceHideHeaders.allObjects;
|
|
}
|
|
});
|
|
}
|
|
make.title(@"Options");
|
|
make.button(@"Toggle headers").handler(^(NSArray<NSString *> *strings) {
|
|
[self.snapshotView toggleShowHeaders];
|
|
});
|
|
make.button(@"Toggle outlines").handler(^(NSArray<NSString *> *strings) {
|
|
[self.snapshotView toggleShowBorders];
|
|
});
|
|
make.button(@"Cancel").cancelStyle();
|
|
} showFrom:self source:sender];
|
|
}
|
|
|
|
- (void)resizeToolbarItems:(CGSize)viewSize {
|
|
CGFloat sliderHeights = self.snapshotView.spacingSlider.bounds.size.height;
|
|
CGFloat sliderWidths = viewSize.width / 3.f;
|
|
CGRect frame = CGRectMake(0, 0, sliderWidths, sliderHeights);
|
|
self.snapshotView.spacingSlider.frame = frame;
|
|
self.snapshotView.depthSlider.frame = frame;
|
|
|
|
[self.navigationController.toolbar setNeedsLayout];
|
|
}
|
|
|
|
- (void)viewWillTransitionToSize:(CGSize)size
|
|
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
|
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
|
|
|
|
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
|
[self resizeToolbarItems:self.view.frame.size];
|
|
} completion:nil];
|
|
}
|
|
|
|
#pragma mark FHSSnapshotViewDelegate
|
|
|
|
- (void)didDeselectView:(FHSViewSnapshot *)snapshot {
|
|
// Our setter would also call the setter for the snapshot view,
|
|
// which we don't need to do here since it is already selected
|
|
_selectedView = nil;
|
|
}
|
|
|
|
- (void)didLongPressView:(FHSViewSnapshot *)snapshot {
|
|
|
|
}
|
|
|
|
- (void)didSelectView:(FHSViewSnapshot *)snapshot {
|
|
// Our setter would also call the setter for the snapshot view,
|
|
// which we don't need to do here since it is already selected
|
|
_selectedView = snapshot.view.view;
|
|
}
|
|
|
|
@end
|