Create stream view hierarchy programmatically

This commit is contained in:
Cameron Gutman 2020-11-01 20:00:39 -06:00
parent b799978cac
commit 0d75dd4efb
13 changed files with 111 additions and 163 deletions

View File

@ -30,8 +30,6 @@
@interface StreamView : UIView <X1KitMouseDelegate, UITextFieldDelegate, UIPointerInteractionDelegate>
#endif
@property (nonatomic, retain) IBOutlet UITextField* keyInputField;
- (void) setupStreamView:(ControllerSupport*)controllerSupport
swipeDelegate:(id<EdgeDetectionDelegate>)swipeDelegate
interactionDelegate:(id<UserInteractionDelegate>)interactionDelegate

View File

@ -19,6 +19,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
@implementation StreamView {
OnScreenControls* onScreenControls;
UITextField* keyInputField;
BOOL isInputingText;
float streamAspectRatio;
@ -51,6 +52,9 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
TemporarySettings* settings = [[[DataManager alloc] init] getSettings];
keyInputField = [[UITextField alloc] initWithFrame:CGRectZero];
[self addSubview:keyInputField];
#if TARGET_OS_TV
// tvOS requires RelativeTouchHandler to manage Apple Remote input
self->touchHandler = [[RelativeTouchHandler alloc] initWithView:self];
@ -164,18 +168,18 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
if ([[event allTouches] count] == 3) {
if (isInputingText) {
Log(LOG_D, @"Closing the keyboard");
[_keyInputField resignFirstResponder];
[keyInputField resignFirstResponder];
isInputingText = false;
} else {
Log(LOG_D, @"Opening the keyboard");
// Prepare the textbox used to capture keyboard events.
_keyInputField.delegate = self;
_keyInputField.text = @"0";
[_keyInputField becomeFirstResponder];
[_keyInputField addTarget:self action:@selector(onKeyboardPressed:) forControlEvents:UIControlEventEditingChanged];
keyInputField.delegate = self;
keyInputField.text = @"0";
[keyInputField becomeFirstResponder];
[keyInputField addTarget:self action:@selector(onKeyboardPressed:) forControlEvents:UIControlEventEditingChanged];
// Undo causes issues for our state management, so turn it off
[_keyInputField.undoManager disableUndoRegistration];
[keyInputField.undoManager disableUndoRegistration];
isInputingText = true;
}

View File

@ -19,19 +19,6 @@ typedef struct {
int networkDroppedFrames;
} video_stats_t;
@protocol ConnectionCallbacks <NSObject>
- (void) connectionStarted;
- (void) connectionTerminated:(int)errorCode;
- (void) stageStarting:(const char*)stageName;
- (void) stageComplete:(const char*)stageName;
- (void) stageFailed:(const char*)stageName withError:(int)errorCode portTestFlags:(int)portTestFlags;
- (void) launchFailed:(NSString*)message;
- (void) rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor;
- (void) connectionStatusUpdate:(int)status;
@end
@interface Connection : NSOperation <NSStreamDelegate>
-(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionCallbacks:(id<ConnectionCallbacks>)callbacks;

View File

@ -0,0 +1,21 @@
//
// ConnectionCallbacks.h
// Moonlight
//
// Created by Cameron Gutman on 11/1/20.
// Copyright © 2020 Moonlight Game Streaming Project. All rights reserved.
//
@protocol ConnectionCallbacks <NSObject>
- (void) connectionStarted;
- (void) connectionTerminated:(int)errorCode;
- (void) stageStarting:(const char*)stageName;
- (void) stageComplete:(const char*)stageName;
- (void) stageFailed:(const char*)stageName withError:(int)errorCode portTestFlags:(int)portTestFlags;
- (void) launchFailed:(NSString*)message;
- (void) rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor;
- (void) connectionStatusUpdate:(int)status;
- (void) videoContentShown;
@end

View File

@ -84,7 +84,7 @@
// Initializing the renderer must be done on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
VideoDecoderRenderer* renderer = [[VideoDecoderRenderer alloc] initWithView:self->_renderView];
VideoDecoderRenderer* renderer = [[VideoDecoderRenderer alloc] initWithView:self->_renderView callbacks:self->_callbacks];
self->_connection = [[Connection alloc] initWithConfig:self->_config renderer:renderer connectionCallbacks:self->_callbacks];
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
[opQueue addOperation:self->_connection];

View File

@ -8,9 +8,11 @@
@import AVFoundation;
#import "ConnectionCallbacks.h"
@interface VideoDecoderRenderer : NSObject
- (id)initWithView:(UIView*)view;
- (id)initWithView:(UIView*)view callbacks:(id<ConnectionCallbacks>)callbacks;
- (void)setupWithVideoFormat:(int)videoFormat refreshRate:(int)refreshRate;

View File

@ -13,6 +13,7 @@
@implementation VideoDecoderRenderer {
StreamView* _view;
id<ConnectionCallbacks> _callbacks;
AVSampleBufferDisplayLayer* displayLayer;
Boolean waitingForSps, waitingForPps, waitingForVps;
@ -61,11 +62,12 @@
}
}
- (id)initWithView:(StreamView*)view
- (id)initWithView:(StreamView*)view callbacks:(id<ConnectionCallbacks>)callbacks
{
self = [super init];
_view = view;
_callbacks = callbacks;
[self reinitializeDisplayLayer];
@ -371,6 +373,9 @@
if ([self isNalReferencePicture:nalType]) {
// Ensure the layer is visible now
self->displayLayer.hidden = NO;
// Tell our parent VC to hide the progress indicator
[self->_callbacks videoContentShown];
}
// Dereference the buffers

View File

@ -19,9 +19,6 @@
#else
@interface StreamFrameViewController : UIViewController <ConnectionCallbacks, EdgeDetectionDelegate, InputPresenceDelegate, UserInteractionDelegate>
#endif
@property (strong, nonatomic) IBOutlet UILabel *stageLabel;
@property (strong, nonatomic) IBOutlet UILabel *tipLabel;
@property (strong, nonatomic) IBOutlet UIActivityIndicatorView *spinner;
@property (nonatomic) StreamConfiguration* streamConfig;
@end

View File

@ -26,6 +26,9 @@
UITapGestureRecognizer *_menuGestureRecognizer;
UITapGestureRecognizer *_menuDoubleTapGestureRecognizer;
UITextView *_overlayView;
UILabel *_stageLabel;
UILabel *_tipLabel;
UIActivityIndicatorView *_spinner;
StreamView *_streamView;
BOOL _userIsInteracting;
}
@ -54,17 +57,31 @@
[self.navigationController setNavigationBarHidden:YES animated:YES];
[self.stageLabel setText:[NSString stringWithFormat:@"Starting %@...", self.streamConfig.appName]];
[self.stageLabel sizeToFit];
self.stageLabel.textAlignment = NSTextAlignmentCenter;
self.stageLabel.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
self.spinner.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2 - self.stageLabel.frame.size.height - self.spinner.frame.size.height);
[UIApplication sharedApplication].idleTimerDisabled = YES;
_stageLabel = [[UILabel alloc] init];
[_stageLabel setUserInteractionEnabled:NO];
[_stageLabel setText:[NSString stringWithFormat:@"Starting %@...", self.streamConfig.appName]];
[_stageLabel sizeToFit];
_stageLabel.textAlignment = NSTextAlignmentCenter;
_stageLabel.textColor = [UIColor whiteColor];
_stageLabel.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
_spinner = [[UIActivityIndicatorView alloc] init];
[_spinner setUserInteractionEnabled:NO];
#if TARGET_OS_TV
[_spinner setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
#else
[_spinner setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhite];
#endif
[_spinner sizeToFit];
[_spinner startAnimating];
_spinner.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2 - _stageLabel.frame.size.height - _spinner.frame.size.height);
_controllerSupport = [[ControllerSupport alloc] initWithConfig:self.streamConfig presenceDelegate:self];
_inactivityTimer = nil;
_streamView = (StreamView*)self.view;
_streamView = [[StreamView alloc] initWithFrame:self.view.frame];
[_streamView setupStreamView:_controllerSupport swipeDelegate:self interactionDelegate:self config:self.streamConfig];
#if TARGET_OS_TV
@ -82,18 +99,23 @@
[self.view addGestureRecognizer:_menuDoubleTapGestureRecognizer];
#endif
_tipLabel = [[UILabel alloc] init];
[_tipLabel setUserInteractionEnabled:NO];
#if TARGET_OS_TV
[self.tipLabel setText:@"Tip: Double tap the Menu button to disconnect from your PC"];
[_tipLabel setText:@"Tip: Double tap the Menu button to disconnect from your PC"];
#else
[self.tipLabel setText:@"Tip: Swipe from the left edge to disconnect from your PC"];
[_tipLabel setText:@"Tip: Swipe from the left edge to disconnect from your PC"];
#endif
[self.tipLabel sizeToFit];
self.tipLabel.textAlignment = NSTextAlignmentCenter;
self.tipLabel.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height * 0.9);
[_tipLabel sizeToFit];
_tipLabel.textColor = [UIColor whiteColor];
_tipLabel.textAlignment = NSTextAlignmentCenter;
_tipLabel.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height * 0.9);
_streamMan = [[StreamManager alloc] initWithConfig:self.streamConfig
renderView:self.view
renderView:_streamView
connectionCallbacks:self];
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
[opQueue addOperation:_streamMan];
@ -112,6 +134,11 @@
selector: @selector(applicationDidEnterBackground:)
name: UIApplicationDidEnterBackgroundNotification
object: nil];
[self.view addSubview:_streamView];
[self.view addSubview:_stageLabel];
[self.view addSubview:_spinner];
[self.view addSubview:_tipLabel];
}
- (void)willMoveToParentViewController:(UIViewController *)parent {
@ -244,8 +271,8 @@
dispatch_async(dispatch_get_main_queue(), ^{
// Leave the spinner spinning until it's obscured by
// the first frame of video.
self.stageLabel.hidden = YES;
self.tipLabel.hidden = YES;
self->_stageLabel.hidden = YES;
self->_tipLabel.hidden = YES;
[self->_streamView showOnScreenControls];
@ -318,9 +345,9 @@
dispatch_async(dispatch_get_main_queue(), ^{
NSString* lowerCase = [NSString stringWithFormat:@"%s in progress...", stageName];
NSString* titleCase = [[[lowerCase substringToIndex:1] uppercaseString] stringByAppendingString:[lowerCase substringFromIndex:1]];
[self.stageLabel setText:titleCase];
[self.stageLabel sizeToFit];
self.stageLabel.center = CGPointMake(self.view.frame.size.width / 2, self.stageLabel.center.y);
[self->_stageLabel setText:titleCase];
[self->_stageLabel sizeToFit];
self->_stageLabel.center = CGPointMake(self.view.frame.size.width / 2, self->_stageLabel.center.y);
});
}
@ -404,6 +431,10 @@
});
}
- (void) videoContentShown {
[_spinner stopAnimating];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="16096" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="pui-7y-JNH">
<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="17156" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="pui-7y-JNH">
<device id="appleTV" appearance="dark"/>
<dependencies>
<deployment identifier="tvOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17126"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -87,8 +87,8 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="3xZ-qb-ZmB"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<connections>
<outlet property="loadingSpinner" destination="WZH-wo-LWE" id="eu5-eD-K5c"/>
@ -106,36 +106,11 @@
<viewControllerLayoutGuide type="top" id="a9G-T3-mGR"/>
<viewControllerLayoutGuide type="bottom" id="6Xc-C7-Or1"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="CAm-nS-IFz" userLabel="RenderView" customClass="StreamView">
<view key="view" multipleTouchEnabled="YES" contentMode="scaleToFill" id="CAm-nS-IFz">
<rect key="frame" x="0.0" y="0.0" width="1920" height="875"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="0eJ-kd-ugl" userLabel="Spinner">
<rect key="frame" x="928" y="508" width="64" height="64"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BUJ-Dh-gl5" userLabel="Stage Label">
<rect key="frame" x="914" y="580" width="93" height="46"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="T9a-QR-DGU" userLabel="Tip Label">
<rect key="frame" x="914" y="821" width="93" height="46"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="Vnr-7v-hDb"/>
</view>
<connections>
<outlet property="spinner" destination="0eJ-kd-ugl" id="KfF-3I-6hN"/>
<outlet property="stageLabel" destination="BUJ-Dh-gl5" id="tEa-k3-sU2"/>
<outlet property="tipLabel" destination="T9a-QR-DGU" id="M1t-iG-6aC"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="IJO-zp-f3d" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>

View File

@ -161,6 +161,7 @@
/* Begin PBXFileReference section */
693B3A9A218638CD00982F7B /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
9803CCAB254F9EAF00EE185E /* ConnectionCallbacks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ConnectionCallbacks.h; sourceTree = "<group>"; };
98132E8C20BC9A62007A053F /* Moonlight v1.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.1.xcdatamodel"; sourceTree = "<group>"; };
9819CC12254F107A008A7C8E /* RelativeTouchHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RelativeTouchHandler.h; sourceTree = "<group>"; };
9819CC13254F107A008A7C8E /* RelativeTouchHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RelativeTouchHandler.m; sourceTree = "<group>"; };
@ -580,6 +581,7 @@
FB89461B19F646E200339C8A /* StreamManager.m */,
FB89461C19F646E200339C8A /* VideoDecoderRenderer.h */,
FB89461D19F646E200339C8A /* VideoDecoderRenderer.m */,
9803CCAB254F9EAF00EE185E /* ConnectionCallbacks.h */,
);
path = Stream;
sourceTree = "<group>";

View File

@ -322,56 +322,19 @@
<viewControllerLayoutGuide type="top" id="l3k-pD-Q9i"/>
<viewControllerLayoutGuide type="bottom" id="vu8-3s-HlY"/>
</layoutGuides>
<view key="view" multipleTouchEnabled="YES" contentMode="scaleToFill" id="VPm-Ae-rc4" userLabel="RenderView" customClass="StreamView">
<view key="view" multipleTouchEnabled="YES" contentMode="scaleToFill" id="SMn-1j-R3I">
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<activityIndicatorView opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" hidesWhenStopped="YES" animating="YES" style="white" translatesAutoresizingMaskIntoConstraints="NO" id="iOs-1X-mSU">
<rect key="frame" x="502" y="374" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dDs-kT-po6">
<rect key="frame" x="491" y="402" width="42" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<textField hidden="YES" opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="JkF-l4-Qyt">
<rect key="frame" x="335" y="503" width="97" height="65"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="tintColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="textColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardAppearance="alert" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no"/>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Tip Text" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vij-AB-rV8" userLabel="Tip Label">
<rect key="frame" x="478" y="718" width="68" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.3333333432674408" green="0.3333333432674408" blue="0.3333333432674408" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="keyInputField" destination="JkF-l4-Qyt" id="oel-UD-aSP"/>
</connections>
<color key="backgroundColor" red="0.3333357871" green="0.33332890269999998" blue="0.33333355190000002" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
</view>
<navigationItem key="navigationItem" id="iUf-9X-GeA"/>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="spinner" destination="iOs-1X-mSU" id="LSl-lr-7fF"/>
<outlet property="stageLabel" destination="dDs-kT-po6" id="ziI-M8-UVf"/>
<outlet property="tipLabel" destination="Vij-AB-rV8" id="kq4-2f-1Kl"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="hON-k2-Efa" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1530.46875" y="172.8515625"/>
<point key="canvasLocation" x="1530.1507537688442" y="172.66187050359713"/>
</scene>
<!--Loading Frame View Controller-->
<scene sceneID="lcG-48-utf">

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="DL0-L5-LOv">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<device id="retina6_1" orientation="landscape" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17126"/>
@ -12,7 +12,7 @@
<objects>
<viewController id="dgh-JZ-Q7z" customClass="MainFrameViewController" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="Rtu-AT-Alw" customClass="AppCollectionView">
<rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="370"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.3333333432674408" green="0.3333333432674408" blue="0.3333333432674408" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="15" minimumInteritemSpacing="15" id="YcZ-cR-3tK">
@ -60,7 +60,7 @@
<viewControllerLayoutGuide type="bottom" id="iBq-4E-Dbr"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Usg-e0-g4a">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
@ -82,7 +82,7 @@
<viewControllerLayoutGuide type="bottom" id="HaZ-6S-ZpN"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="iNk-qF-gIr" customClass="UIScrollView">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Resolution" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8zy-ri-Dqc">
@ -300,7 +300,7 @@
<objects>
<navigationController id="ftZ-kC-fxI" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" translucent="NO" id="0ZA-Ec-QgD">
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<color key="barTintColor" red="0.66666668653488159" green="0.66666668653488159" blue="0.66666668653488159" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes">
@ -320,59 +320,22 @@
<objects>
<viewController id="mI3-9F-XwU" customClass="StreamFrameViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="JIX-qR-Sh3"/>
<viewControllerLayoutGuide type="bottom" id="ody-Fj-H4p"/>
<viewControllerLayoutGuide type="top" id="u8d-aX-gHk"/>
<viewControllerLayoutGuide type="bottom" id="kcN-yf-dtN"/>
</layoutGuides>
<view key="view" multipleTouchEnabled="YES" contentMode="scaleToFill" id="eir-e9-IPE" customClass="StreamView">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<view key="view" multipleTouchEnabled="YES" contentMode="scaleToFill" id="p4u-yn-VXk">
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Stage" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2HK-Z5-4Ch">
<rect key="frame" x="262" y="149" width="45" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" hidesWhenStopped="YES" animating="YES" style="white" translatesAutoresizingMaskIntoConstraints="NO" id="0vm-Iv-K4b">
<rect key="frame" x="197" y="354" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
<textField hidden="YES" opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="r45-6t-nxS">
<rect key="frame" x="406" y="293" width="97" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="tintColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="textColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardAppearance="alert" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no"/>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Tip Text" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QwC-QD-dae" userLabel="Tip Label">
<rect key="frame" x="304" y="334" width="61" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.3333333432674408" green="0.3333333432674408" blue="0.3333333432674408" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections>
<outlet property="keyInputField" destination="r45-6t-nxS" id="7Du-Ju-Qcm"/>
</connections>
<color key="backgroundColor" red="0.3333357871" green="0.33332890269999998" blue="0.33333355190000002" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
</view>
<navigationItem key="navigationItem" id="8uX-4T-BiC"/>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="spinner" destination="0vm-Iv-K4b" id="Lif-zG-6Jh"/>
<outlet property="stageLabel" destination="2HK-Z5-4Ch" id="1bI-Ig-OpD"/>
<outlet property="tipLabel" destination="QwC-QD-dae" id="TZe-nh-xiv"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="pqv-jd-33O" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3031.884057971015" y="533.03571428571422"/>
<point key="canvasLocation" x="3031.4732142857142" y="531.8840579710145"/>
</scene>
<!--Loading Frame View Controller-->
<scene sceneID="kPF-XD-S77">
@ -383,11 +346,11 @@
<viewControllerLayoutGuide type="bottom" id="hby-2y-jAk"/>
</layoutGuides>
<view key="view" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="center" id="nSw-dc-mi4">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="oNu-Gu-QeM">
<rect key="frame" x="266" y="426" width="37" height="37"/>
<rect key="frame" x="266" y="187" width="37" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMinY="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
</subviews>