From 77b6d304f7bf9f71885de2b321d1ec206ebd1345 Mon Sep 17 00:00:00 2001 From: Diego Waxemberg Date: Wed, 29 Oct 2014 00:47:51 -0400 Subject: [PATCH] added settings menu and persistent storage for settings --- Limelight.xcodeproj/project.pbxproj | 40 + Limelight/AppDelegate.h | 1 + Limelight/AppDelegate.m | 6 +- Limelight/Database/DataManager.h | 22 + Limelight/Database/DataManager.m | 65 + Limelight/Database/Hosts.h | 18 + Limelight/Database/Hosts.m | 17 + Limelight/Database/Settings.h | 20 + Limelight/Database/Settings.m | 19 + .../Left4Dead2.imageset/Left4Dead2-1.jpg | Bin 31418 -> 0 bytes .../Left4Dead2.imageset/Left4Dead2-2.jpg | Bin 31418 -> 0 bytes .../Left4Dead2.imageset/Left4Dead2.jpg | Bin 31418 -> 0 bytes .../Contents.json | 8 +- .../Settings.imageset/settings_2x.png | Bin 0 -> 2764 bytes .../Limelight.xcdatamodel/contents | 17 +- .../ViewControllers/MainFrameViewController.h | 4 +- .../ViewControllers/MainFrameViewController.m | 76 +- .../ViewControllers/SWRevealViewController.h | 418 ++++ .../ViewControllers/SWRevealViewController.m | 1897 +++++++++++++++++ .../ViewControllers/SettingsViewController.h | 20 + .../ViewControllers/SettingsViewController.m | 64 + .../StreamFrameViewController.m | 14 +- iPad.storyboard | 13 + iPhone.storyboard | 120 +- 24 files changed, 2803 insertions(+), 56 deletions(-) create mode 100644 Limelight/Database/DataManager.h create mode 100644 Limelight/Database/DataManager.m create mode 100644 Limelight/Database/Hosts.h create mode 100644 Limelight/Database/Hosts.m create mode 100644 Limelight/Database/Settings.h create mode 100644 Limelight/Database/Settings.m delete mode 100644 Limelight/Images.xcassets/Left4Dead2.imageset/Left4Dead2-1.jpg delete mode 100644 Limelight/Images.xcassets/Left4Dead2.imageset/Left4Dead2-2.jpg delete mode 100644 Limelight/Images.xcassets/Left4Dead2.imageset/Left4Dead2.jpg rename Limelight/Images.xcassets/{Left4Dead2.imageset => Settings.imageset}/Contents.json (59%) create mode 100644 Limelight/Images.xcassets/Settings.imageset/settings_2x.png create mode 100755 Limelight/ViewControllers/SWRevealViewController.h create mode 100755 Limelight/ViewControllers/SWRevealViewController.m create mode 100644 Limelight/ViewControllers/SettingsViewController.h create mode 100644 Limelight/ViewControllers/SettingsViewController.m diff --git a/Limelight.xcodeproj/project.pbxproj b/Limelight.xcodeproj/project.pbxproj index 0c12d335..2d7ee76c 100644 --- a/Limelight.xcodeproj/project.pbxproj +++ b/Limelight.xcodeproj/project.pbxproj @@ -48,6 +48,11 @@ FB8946EC19F6AFE400339C8A /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB8946E119F6AFB800339C8A /* libssl.a */; }; FB8946ED19F6AFE800339C8A /* libopus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB8946EA19F6AFB800339C8A /* libopus.a */; }; FBD3494319FC9C04002D2A60 /* AppManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3494219FC9C04002D2A60 /* AppManager.m */; }; + FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3494F19FF2174002D2A60 /* SettingsViewController.m */; }; + FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3495219FF36FB002D2A60 /* SWRevealViewController.m */; }; + FBD3495B1A004411002D2A60 /* Hosts.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3495A1A004411002D2A60 /* Hosts.m */; }; + FBD3495E1A004412002D2A60 /* Settings.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3495D1A004412002D2A60 /* Settings.m */; }; + FBD349621A0089F6002D2A60 /* DataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD349611A0089F6002D2A60 /* DataManager.m */; }; FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86DF19F7A837001C18A8 /* UIComputerView.m */; }; FBDE86E619F82297001C18A8 /* UIAppView.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86E519F82297001C18A8 /* UIAppView.m */; }; FBDE86E919F82315001C18A8 /* App.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86E819F82315001C18A8 /* App.m */; }; @@ -217,6 +222,17 @@ FB8946EA19F6AFB800339C8A /* libopus.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libopus.a; sourceTree = ""; }; FBD3494119FC9C04002D2A60 /* AppManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppManager.h; sourceTree = ""; }; FBD3494219FC9C04002D2A60 /* AppManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppManager.m; sourceTree = ""; }; + FBD3494E19FF2174002D2A60 /* SettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = ""; }; + FBD3494F19FF2174002D2A60 /* SettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = ""; }; + FBD3495119FF36FB002D2A60 /* SWRevealViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWRevealViewController.h; sourceTree = ""; }; + FBD3495219FF36FB002D2A60 /* SWRevealViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWRevealViewController.m; sourceTree = ""; }; + FBD349571A003F05002D2A60 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; + FBD349591A004411002D2A60 /* Hosts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hosts.h; path = Database/Hosts.h; sourceTree = ""; }; + FBD3495A1A004411002D2A60 /* Hosts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Hosts.m; path = Database/Hosts.m; sourceTree = ""; }; + FBD3495C1A004412002D2A60 /* Settings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Settings.h; path = Database/Settings.h; sourceTree = ""; }; + FBD3495D1A004412002D2A60 /* Settings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Settings.m; path = Database/Settings.m; sourceTree = ""; }; + FBD349601A0089F6002D2A60 /* DataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataManager.h; path = Database/DataManager.h; sourceTree = ""; }; + FBD349611A0089F6002D2A60 /* DataManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DataManager.m; path = Database/DataManager.m; sourceTree = ""; }; FBDE86DE19F7A837001C18A8 /* UIComputerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIComputerView.h; sourceTree = ""; }; FBDE86DF19F7A837001C18A8 /* UIComputerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIComputerView.m; sourceTree = ""; }; FBDE86E419F82297001C18A8 /* UIAppView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIAppView.h; sourceTree = ""; }; @@ -289,6 +305,7 @@ FB290CF019B2C406004C83CF /* Frameworks */ = { isa = PBXGroup; children = ( + FBD349571A003F05002D2A60 /* libsqlite3.dylib */, FB89468F19F6AFB800339C8A /* libs */, FB7E794319C8B71B00A15F68 /* libiconv.dylib */, FB290DC319B2E98F004C83CF /* libxml2.dylib */, @@ -311,6 +328,7 @@ FB89461519F646E200339C8A /* Stream */, FB89461E19F646E200339C8A /* Utility */, FB89462319F646E200339C8A /* ViewControllers */, + FBD3495F1A004453002D2A60 /* Database */, FB290CFA19B2C406004C83CF /* Supporting Files */, FB290D0219B2C406004C83CF /* AppDelegate.h */, FB290D0319B2C406004C83CF /* AppDelegate.m */, @@ -424,10 +442,14 @@ FB89462319F646E200339C8A /* ViewControllers */ = { isa = PBXGroup; children = ( + FBD3495119FF36FB002D2A60 /* SWRevealViewController.h */, + FBD3495219FF36FB002D2A60 /* SWRevealViewController.m */, FB89462419F646E200339C8A /* MainFrameViewController.h */, FB89462519F646E200339C8A /* MainFrameViewController.m */, FB89462619F646E200339C8A /* StreamFrameViewController.h */, FB89462719F646E200339C8A /* StreamFrameViewController.m */, + FBD3494E19FF2174002D2A60 /* SettingsViewController.h */, + FBD3494F19FF2174002D2A60 /* SettingsViewController.m */, ); path = ViewControllers; sourceTree = ""; @@ -593,6 +615,19 @@ path = lib; sourceTree = ""; }; + FBD3495F1A004453002D2A60 /* Database */ = { + isa = PBXGroup; + children = ( + FBD3495C1A004412002D2A60 /* Settings.h */, + FBD3495D1A004412002D2A60 /* Settings.m */, + FBD349591A004411002D2A60 /* Hosts.h */, + FBD3495A1A004411002D2A60 /* Hosts.m */, + FBD349601A0089F6002D2A60 /* DataManager.h */, + FBD349611A0089F6002D2A60 /* DataManager.m */, + ); + name = Database; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -713,6 +748,7 @@ files = ( FB290D0719B2C406004C83CF /* Limelight.xcdatamodeld in Sources */, FB89463219F646E200339C8A /* VideoDecoderRenderer.m in Sources */, + FBD3495E1A004412002D2A60 /* Settings.m in Sources */, FB290D0419B2C406004C83CF /* AppDelegate.m in Sources */, FB89463419F646E200339C8A /* Utils.m in Sources */, FBDE86E619F82297001C18A8 /* UIAppView.m in Sources */, @@ -722,9 +758,12 @@ FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */, FBDE86E919F82315001C18A8 /* App.m in Sources */, FB89463019F646E200339C8A /* StreamConfiguration.m in Sources */, + FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */, + FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */, FB89462C19F646E200339C8A /* HttpManager.m in Sources */, FB89462D19F646E200339C8A /* MDNSManager.m in Sources */, FB89462B19F646E200339C8A /* StreamView.m in Sources */, + FBD3495B1A004411002D2A60 /* Hosts.m in Sources */, FB89463519F646E200339C8A /* MainFrameViewController.m in Sources */, FB89463619F646E200339C8A /* StreamFrameViewController.m in Sources */, FB89462819F646E200339C8A /* CryptoManager.m in Sources */, @@ -732,6 +771,7 @@ FB290D0019B2C406004C83CF /* main.m in Sources */, FBD3494319FC9C04002D2A60 /* AppManager.m in Sources */, FB89462A19F646E200339C8A /* ControllerSupport.m in Sources */, + FBD349621A0089F6002D2A60 /* DataManager.m in Sources */, FB89463119F646E200339C8A /* StreamManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Limelight/AppDelegate.h b/Limelight/AppDelegate.h index 3e323b47..8d06c523 100644 --- a/Limelight/AppDelegate.h +++ b/Limelight/AppDelegate.h @@ -18,5 +18,6 @@ - (void)saveContext; - (NSURL *)applicationDocumentsDirectory; +- (NSURL*) getStoreURL; @end diff --git a/Limelight/AppDelegate.m b/Limelight/AppDelegate.m index 680aa5f9..18fabba9 100644 --- a/Limelight/AppDelegate.m +++ b/Limelight/AppDelegate.m @@ -100,7 +100,7 @@ static NSOperationQueue* mainQueue; return _persistentStoreCoordinator; } - NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Limelight_iOS.sqlite"]; + NSURL *storeURL = [self getStoreURL]; NSError *error = nil; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; @@ -143,4 +143,8 @@ static NSOperationQueue* mainQueue; return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; } +- (NSURL*) getStoreURL { + return [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Limelight_iOS.sqlite"]; +} + @end diff --git a/Limelight/Database/DataManager.h b/Limelight/Database/DataManager.h new file mode 100644 index 00000000..3a193be5 --- /dev/null +++ b/Limelight/Database/DataManager.h @@ -0,0 +1,22 @@ +// +// DataManager.h +// Limelight +// +// Created by Diego Waxemberg on 10/28/14. +// Copyright (c) 2014 Limelight Stream. All rights reserved. +// + +#import +#import "Settings.h" +#import "AppDelegate.h" +#import "Hosts.h" + +@interface DataManager : NSObject + +@property (strong, nonatomic) AppDelegate* appDelegate; + +- (void) saveSettingsWithBitrate:(NSInteger)bitrate framerate:(NSInteger)framerate height:(NSInteger)height width:(NSInteger)width; +- (Settings*) retrieveSettings; + + +@end diff --git a/Limelight/Database/DataManager.m b/Limelight/Database/DataManager.m new file mode 100644 index 00000000..16130224 --- /dev/null +++ b/Limelight/Database/DataManager.m @@ -0,0 +1,65 @@ +// +// DataManager.m +// Limelight +// +// Created by Diego Waxemberg on 10/28/14. +// Copyright (c) 2014 Limelight Stream. All rights reserved. +// + +#import "DataManager.h" + +@implementation DataManager +static NSInteger DEFAULT_BITRATE = 10000; +static NSInteger DEFAULT_FRAMERATE = 60; +static NSInteger DEFAULT_HEIGHT = 720; +static NSInteger DEFAULT_WIDTH = 1280; + +- (id) init { + self = [super init]; + self.appDelegate = [[UIApplication sharedApplication] delegate]; + return self; +} + +- (void) saveSettingsWithBitrate:(NSInteger)bitrate framerate:(NSInteger)framerate height:(NSInteger)height width:(NSInteger)width { + Settings* settingsToSave = [self retrieveSettings]; + settingsToSave.framerate = [NSNumber numberWithInteger:framerate]; + settingsToSave.bitrate = [NSNumber numberWithInteger:bitrate]; + settingsToSave.height = [NSNumber numberWithInteger:height]; + settingsToSave.width = [NSNumber numberWithInteger:width]; + [self saveSettings:settingsToSave]; +} + +- (Settings*) retrieveSettings { + NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init]; + NSEntityDescription* entity = [NSEntityDescription entityForName:@"Settings" inManagedObjectContext:[self.appDelegate managedObjectContext]]; + [fetchRequest setEntity:entity]; + [fetchRequest setAffectedStores:[NSArray arrayWithObjects:[[self.appDelegate persistentStoreCoordinator] persistentStoreForURL:[self.appDelegate getStoreURL]], nil]]; + + NSError* error; + NSArray* fetchedRecords = [[self.appDelegate managedObjectContext] executeFetchRequest:fetchRequest error:&error]; + + if (fetchedRecords.count == 0) { + // create a new settings object with the default values + Settings* settings = [[Settings alloc] initWithEntity:entity insertIntoManagedObjectContext:[self.appDelegate managedObjectContext]]; + settings.framerate = [NSNumber numberWithInteger:DEFAULT_FRAMERATE]; + settings.bitrate = [NSNumber numberWithInteger:DEFAULT_BITRATE]; + settings.height = [NSNumber numberWithInteger:DEFAULT_HEIGHT]; + settings.width = [NSNumber numberWithInteger:DEFAULT_WIDTH]; + return settings; + } else { + // we should only ever have 1 settings object stored + return [fetchedRecords objectAtIndex:0]; + } +} + +- (void) saveSettings:(Settings*)settings { + NSError* error; + if (![[self.appDelegate managedObjectContext] save:&error]) { + NSLog(@"ERROR: Unable to save settings to database"); + } + + [self.appDelegate saveContext]; +} + + +@end diff --git a/Limelight/Database/Hosts.h b/Limelight/Database/Hosts.h new file mode 100644 index 00000000..2b4d1843 --- /dev/null +++ b/Limelight/Database/Hosts.h @@ -0,0 +1,18 @@ +// +// Hosts.h +// Limelight +// +// Created by Diego Waxemberg on 10/28/14. +// Copyright (c) 2014 Limelight Stream. All rights reserved. +// + +#import +#import + + +@interface Hosts : NSManagedObject + +@property (nonatomic, retain) NSString * address; +@property (nonatomic, retain) NSString * name; + +@end diff --git a/Limelight/Database/Hosts.m b/Limelight/Database/Hosts.m new file mode 100644 index 00000000..71eeaa60 --- /dev/null +++ b/Limelight/Database/Hosts.m @@ -0,0 +1,17 @@ +// +// Hosts.m +// Limelight +// +// Created by Diego Waxemberg on 10/28/14. +// Copyright (c) 2014 Limelight Stream. All rights reserved. +// + +#import "Hosts.h" + + +@implementation Hosts + +@dynamic address; +@dynamic name; + +@end diff --git a/Limelight/Database/Settings.h b/Limelight/Database/Settings.h new file mode 100644 index 00000000..1198a2c0 --- /dev/null +++ b/Limelight/Database/Settings.h @@ -0,0 +1,20 @@ +// +// Settings.h +// Limelight +// +// Created by Diego Waxemberg on 10/28/14. +// Copyright (c) 2014 Limelight Stream. All rights reserved. +// + +#import +#import + + +@interface Settings : NSManagedObject + +@property (nonatomic, retain) NSNumber * bitrate; +@property (nonatomic, retain) NSNumber * framerate; +@property (nonatomic, retain) NSNumber * height; +@property (nonatomic, retain) NSNumber * width; + +@end diff --git a/Limelight/Database/Settings.m b/Limelight/Database/Settings.m new file mode 100644 index 00000000..001f8ca2 --- /dev/null +++ b/Limelight/Database/Settings.m @@ -0,0 +1,19 @@ +// +// Settings.m +// Limelight +// +// Created by Diego Waxemberg on 10/28/14. +// Copyright (c) 2014 Limelight Stream. All rights reserved. +// + +#import "Settings.h" + + +@implementation Settings + +@dynamic bitrate; +@dynamic framerate; +@dynamic height; +@dynamic width; + +@end diff --git a/Limelight/Images.xcassets/Left4Dead2.imageset/Left4Dead2-1.jpg b/Limelight/Images.xcassets/Left4Dead2.imageset/Left4Dead2-1.jpg deleted file mode 100644 index ffd4f4afcca29e0ecb248750e0ddee16980a5a0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31418 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 MPgg&ebxsLQ0Kg;OIRF3v literal 0 HcmV?d00001 diff --git a/Limelight/Limelight.xcdatamodeld/Limelight.xcdatamodel/contents b/Limelight/Limelight.xcdatamodeld/Limelight.xcdatamodel/contents index 193f33c9..4012f03c 100644 --- a/Limelight/Limelight.xcdatamodeld/Limelight.xcdatamodel/contents +++ b/Limelight/Limelight.xcdatamodeld/Limelight.xcdatamodel/contents @@ -1,4 +1,17 @@ - - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Limelight/ViewControllers/MainFrameViewController.h b/Limelight/ViewControllers/MainFrameViewController.h index 70e106eb..cc8a15b6 100644 --- a/Limelight/ViewControllers/MainFrameViewController.h +++ b/Limelight/ViewControllers/MainFrameViewController.h @@ -13,8 +13,10 @@ #import "UIComputerView.h" #import "UIAppView.h" #import "AppManager.h" +#import "SWRevealViewController.h" -@interface MainFrameViewController : UIViewController +@interface MainFrameViewController : UIViewController +@property (strong, nonatomic) IBOutlet UIBarButtonItem *settingsSidebarButton; + (StreamConfiguration*) getStreamConfiguration; diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m index 328b0f18..fe6d06c6 100644 --- a/Limelight/ViewControllers/MainFrameViewController.m +++ b/Limelight/ViewControllers/MainFrameViewController.m @@ -16,6 +16,9 @@ #import "UIComputerView.h" #import "UIAppView.h" #import "App.h" +#import "SettingsViewController.h" +#import "DataManager.h" +#import "Settings.h" @implementation MainFrameViewController { NSOperationQueue* _opQueue; @@ -134,60 +137,58 @@ static StreamConfiguration* streamConfig; return; } - // TODO: actually allow the user to choose the config - unsigned long selectedConf = 1; - NSLog(@"selectedConf: %ld", selectedConf); - switch (selectedConf) { - case 0: - streamConfig.width = 1280; - streamConfig.height = 720; - streamConfig.frameRate = 30; - streamConfig.bitRate = 5000; - break; - default: - case 1: - streamConfig.width = 1280; - streamConfig.height = 720; - streamConfig.frameRate = 60; - streamConfig.bitRate = 10000; - break; - case 2: - streamConfig.width = 1920; - streamConfig.height = 1080; - streamConfig.frameRate = 30; - streamConfig.bitRate = 10000; - break; - case 3: - streamConfig.width = 1920; - streamConfig.height = 1080; - streamConfig.frameRate = 60; - streamConfig.bitRate = 20000; - break; + DataManager* dataMan = [[DataManager alloc] init]; + Settings* streamSettings = [dataMan retrieveSettings]; + + streamConfig.frameRate = [streamSettings.framerate intValue]; + streamConfig.bitRate = [streamSettings.bitrate intValue]; + streamConfig.height = [streamSettings.height intValue]; + streamConfig.width = [streamSettings.width intValue]; + + [self performSegueWithIdentifier:@"createStreamFrame" sender:nil]; +} + +- (void)revealController:(SWRevealViewController *)revealController didMoveToPosition:(FrontViewPosition)position { + // If we moved back to the center position, we should save the settings + if (position == FrontViewPositionLeft) { + [(SettingsViewController*)[revealController rearViewController] saveSettings]; } - NSLog(@"StreamConfig: %@, %d, %dx%dx%d at %d Mbps", streamConfig.host, streamConfig.hostAddr, streamConfig.width, streamConfig.height, streamConfig.frameRate, streamConfig.bitRate); - [self performSegueWithIdentifier:@"createStreamFrame" sender:self]; } - (void)viewDidLoad { [super viewDidLoad]; - - NSArray* streamConfigVals = [[NSArray alloc] initWithObjects:@"1280x720 (30Hz)", @"1280x720 (60Hz)", @"1920x1080 (30Hz)", @"1920x1080 (60Hz)",nil]; + + // Change button color + _settingsSidebarButton.tintColor = [UIColor colorWithRed:.2 green:.9 blue:0.f alpha:1.f]; + + // Set the side bar button action. When it's tapped, it'll show up the sidebar. + _settingsSidebarButton.target = self.revealViewController; + _settingsSidebarButton.action = @selector(revealToggle:); + + // Set the gesture + [self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer]; + + [self.revealViewController setDelegate:self]; + + //NSArray* streamConfigVals = [[NSArray alloc] initWithObjects:@"1280x720 (30Hz)", @"1280x720 (60Hz)", @"1920x1080 (30Hz)", @"1920x1080 (60Hz)",nil]; _opQueue = [[NSOperationQueue alloc] init]; [CryptoManager generateKeyPairUsingSSl]; _uniqueId = [CryptoManager getUniqueID]; _cert = [CryptoManager readCertFromFile]; - // Initialize the host picker list + // Only initialize the host picker list once if (hostList == nil) { hostList = [[NSMutableSet alloc] init]; } + [self setAutomaticallyAdjustsScrollViewInsets:NO]; + hostScrollView = [[UIScrollView alloc] init]; - hostScrollView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height / 2); + hostScrollView.frame = CGRectMake(0, self.navigationController.navigationBar.frame.origin.y + self.navigationController.navigationBar.frame.size.height, self.view.frame.size.width, self.view.frame.size.height / 2); [hostScrollView setShowsHorizontalScrollIndicator:NO]; - + appScrollView = [[UIScrollView alloc] init]; appScrollView.frame = CGRectMake(0, hostScrollView.frame.size.height, self.view.frame.size.width, self.view.frame.size.height / 2); [appScrollView setShowsHorizontalScrollIndicator:NO]; @@ -199,7 +200,8 @@ static StreamConfiguration* streamConfig; - (void)viewDidAppear:(BOOL)animated { - [super viewDidDisappear:animated]; + [super viewDidAppear:animated]; + [self.navigationController setNavigationBarHidden:NO animated:YES]; _mDNSManager = [[MDNSManager alloc] initWithCallback:self]; [_mDNSManager searchForHosts]; } diff --git a/Limelight/ViewControllers/SWRevealViewController.h b/Limelight/ViewControllers/SWRevealViewController.h new file mode 100755 index 00000000..279106e3 --- /dev/null +++ b/Limelight/ViewControllers/SWRevealViewController.h @@ -0,0 +1,418 @@ +/* + + Copyright (c) 2013 Joan Lluch + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is furnished + to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + Early code inspired on a similar class by Philip Kluz (Philip.Kluz@zuui.org) + +*/ + +/* + + RELEASE NOTES + + + Version 2.3.0 (Current Version) + + - StoryBoard initializing bug fix + - Minor Code refactoring + + Version 2.2.0 + + - State Restoration support. + - Reverted panGestureRecognizer implementation to before v2.1.0 (works better). + - New properties 'toggleAnimationType', 'springDampingRatio'. Default reveal animation is 'Spring' + - New property 'frontViewShadowColor' + - New properties 'clipsViewsToBounds' and '_extendedPointInsideHit' + - New delegate methods for finer control of front view location in the overdraw area, as long as deprecation note on former delegate methods + - Other minor changes that should not affect current implementations + + Version 2.1.0 + + - Removed SWDirectionPanGestureRecognizer. Horizontal panning is filtered on the shouldBegin delegate. This is cleaner, I hope it does not break previous funcionality + - Took a cleaner approach to storyboard support. SWRevealViewControllerSegue is now deprecated and you should use SWRevealViewControllerSegueSetController and SWRevealViewControllerSeguePushController instead. + - A minor change on the autoresizingMask of the internal views to fix a glitch on iOS8. This should not affect iOS7 + + Version 2.0.2 + + - Added new delegates for better control of gesture recognizers + + Version 2.0.1 + + - Fix: draggableBorderWidth now correctly handles the cases where one of the rear controllers is not provided + - Fix: the shadow related properties are now granted at any time after view load, not just after initialization. + + Version 2.0.0 + +- Dropped support for iOS6 and earlier. This version will only work on iOS7 + +- The method setFrontViewController:animated: does not longer perform a full reveal animation. Instead it just replaces the frontViewController in + its current position. Use the new pushFrontViewController:animated: method to perform a replacement of the front controlles with reveal animation + as in the previous version + + IMPORTANT: You must replace all calls to setFrontViewController:animated by calls to pushFrontViewController:animated to prevent breaking + functionality on existing projects. + +- Added support for animated replacement of child controllers: setRearViewController, setFrontViewController, setRightViewController now have animated versions. + +- The new 'replaceViewAnimationDuration' property sets the default duration of child viewController replacement. + +- Added the following new delegate methods + revealController:willAddViewController:forOperation:animated: + revealController:didAddViewController:forOperation:animated: + +- The class also supports custom UIViewControllerAnimatedTransitioning related with the replacement of child viewControllers. + You can implement the following new delegate method: revealController:animationControllerForOperation:fromViewController:toViewController: + and provide an object conforming to UIViewControllerAnimatedTransitioning to implement custom animations. + + Version 1.1.3 + +- Reverted the supportedInterfaceOrientations to the default behavior. This is consistent with Apple provided controllers + +- The presentFrontViewHierarchically now dynamically takes into account the smaller header height of bars on iPhone landscape orientation + + Version 1.1.2 + + - The status bar style and appearance are now handled in sync with the class animations. + You can implement the methods preferredStatusBarStyle and prefersStatusBarHidden on your child controllers to define the desired appearance + + - The loadView method now calls a method, loadStoryboardControllers, just for the purpose of loading child controllers from a storyboard. + You can override this method and remove the @try @catch statements if you want the debugger not to stop at them in case you have set an exception breakpoint. + + Version 1.1.1 + + - You can now get a tapGestureRecognizer from the class. See the tapGestureRecognizer method for more information. + + - Both the panGestureRecognizer and the tapGestureRecognizer are now attached to the revealViewController's front content view + by default, so they will start working just by calling their access methods even if you do not attach them to any of your views. + This enables you to dissable interactions on your views -for example based on position- without breaking normal gesture behavior. + + - Corrected a bug that caused a crash on iOS6 and earlier. + + Version 1.1.0 + + - The method setFrontViewController:animated now performs the correct animations both for left and right controllers. + + - The class now automatically handles the status bar appearance depending on the currently shown child controller. + + Version 1.0.8 + + - Support for constant width frontView by setting a negative value to reveal widths. See properties rearViewRevealWidth and rightViewRevealWidth + + - Support for draggableBorderWidth. See property of the same name. + + - The Pan gesture recongnizer can be disabled by implementing the following delegate method and returning NO + revealControllerPanGestureShouldBegin: + + - Added the ability to track pan gesture reveal progress through the following new delegate methods + revealController:panGestureBeganFromLocation:progress: + revealController:panGestureMovedToLocation:progress: + revealController:panGestureEndedToLocation:progress: + + Previous Versions + + - No release notes were updated for previous versions. + +*/ + + +#import + +@class SWRevealViewController; +@protocol SWRevealViewControllerDelegate; + +#pragma mark - SWRevealViewController Class + +// Enum values for setFrontViewPosition:animated: +typedef NS_ENUM( NSInteger, FrontViewPosition) +{ + // Front controller is removed from view. Animated transitioning from this state will cause the same + // effect than animating from FrontViewPositionLeftSideMost. Use this instead of FrontViewPositionLeftSideMost when + // you want to remove the front view controller view from the view hierarchy. + FrontViewPositionLeftSideMostRemoved, + + // Left most position, front view is presented left-offseted by rightViewRevealWidth+rigthViewRevealOverdraw + FrontViewPositionLeftSideMost, + + // Left position, front view is presented left-offseted by rightViewRevealWidth + FrontViewPositionLeftSide, + + // Center position, rear view is hidden behind front controller + FrontViewPositionLeft, + + // Right possition, front view is presented right-offseted by rearViewRevealWidth + FrontViewPositionRight, + + // Right most possition, front view is presented right-offseted by rearViewRevealWidth+rearViewRevealOverdraw + FrontViewPositionRightMost, + + // Front controller is removed from view. Animated transitioning from this state will cause the same + // effect than animating from FrontViewPositionRightMost. Use this instead of FrontViewPositionRightMost when + // you intent to remove the front controller view from the view hierarchy. + FrontViewPositionRightMostRemoved, + +}; + +// Enum values for toggleAnimationType +typedef NS_ENUM(NSInteger, SWRevealToggleAnimationType) +{ + SWRevealToggleAnimationTypeSpring, // <- produces a spring based animation + SWRevealToggleAnimationTypeEaseOut, // <- produces an ease out curve animation +}; + + +@interface SWRevealViewController : UIViewController + +/* Basic API */ + +// Object instance init and rear view setting +- (id)initWithRearViewController:(UIViewController *)rearViewController frontViewController:(UIViewController *)frontViewController; + +// Rear view controller, can be nil if not used +@property (nonatomic) UIViewController *rearViewController; +- (void)setRearViewController:(UIViewController *)rearViewController animated:(BOOL)animated; + +// Optional right view controller, can be nil if not used +@property (nonatomic) UIViewController *rightViewController; +- (void)setRightViewController:(UIViewController *)rightViewController animated:(BOOL)animated; + +// Front view controller, can be nil on initialization but must be supplied by the time the view is loaded +@property (nonatomic) UIViewController *frontViewController; +- (void)setFrontViewController:(UIViewController *)frontViewController animated:(BOOL)animated; + +// Sets the frontViewController using a default set of chained animations consisting on moving the +// presented frontViewController to the right most possition, replacing it, and moving it back to the left position +- (void)pushFrontViewController:(UIViewController *)frontViewController animated:(BOOL)animated; + +// Sets the frontViewController position. You can call the animated version several times with different +// positions to obtain a set of animations that will be performed in order one after the other. +@property (nonatomic) FrontViewPosition frontViewPosition; +- (void)setFrontViewPosition:(FrontViewPosition)frontViewPosition animated:(BOOL)animated; + +// The following methods are meant to be directly connected to the action method of a button +// to perform user triggered postion change of the controller views. This is ussually added to a +// button on top left or right of the frontViewController +- (IBAction)revealToggle:(id)sender; +- (IBAction)rightRevealToggle:(id)sender; // <-- simetric implementation of the above for the rightViewController + +// Toogles the current state of the front controller between Left or Right and fully visible +// Use setFrontViewPosition to set a particular position +- (void)revealToggleAnimated:(BOOL)animated; +- (void)rightRevealToggleAnimated:(BOOL)animated; // <-- simetric implementation of the above for the rightViewController + +// The following method will provide a panGestureRecognizer suitable to be added to any view +// in order to perform usual drag and swipe gestures to reveal the rear views. This is usually added to the top bar +// of a front controller, but it can be added to your frontViewController view or to the reveal controller view to provide full screen panning. +// By default, the panGestureRecognizer is added to the view containing the front controller view. To keep this default behavior +// you still need to call this method, just don't add it to any of your views. The default setup allows you to dissable +// user interactions on your controller views without affecting the recognizer. +- (UIPanGestureRecognizer*)panGestureRecognizer; + +// The following method will provide a tapGestureRecognizer suitable to be added to any view on the frontController +// for concealing the rear views. By default no tap recognizer is created or added to any view, however if you call this method after +// the controller's view has been loaded the recognizer is added to the reveal controller's front container view. +// Thus, you can disable user interactions on your frontViewController view without affecting the tap recognizer. +- (UITapGestureRecognizer*)tapGestureRecognizer; + +/* The following properties are provided for further customization, they are set to default values on initialization, + you do not generally have to set them */ + +// Defines how much of the rear or right view is shown, default is 260. +// Negative values indicate that the reveal width should be computed by substracting the full front view width, +// so the revealed frontView width is kept constant when bounds change as opposed to the rear or right width. +@property (nonatomic) CGFloat rearViewRevealWidth; +@property (nonatomic) CGFloat rightViewRevealWidth; // <-- simetric implementation of the above for the rightViewController + +// Defines how much of an overdraw can occur when dragging further than 'rearViewRevealWidth', default is 60. +@property (nonatomic) CGFloat rearViewRevealOverdraw; +@property (nonatomic) CGFloat rightViewRevealOverdraw; // <-- simetric implementation of the above for the rightViewController + +// Defines how much displacement is applied to the rear view when animating or dragging the front view, default is 40. +@property (nonatomic) CGFloat rearViewRevealDisplacement; +@property (nonatomic) CGFloat rightViewRevealDisplacement; // <-- simetric implementation of the above for the rightViewController + +// Defines a width on the border of the view attached to the panGesturRecognizer where the gesture is allowed, +// default is 0 which means no restriction. +@property (nonatomic) CGFloat draggableBorderWidth; + +// If YES (the default) the controller will bounce to the Left position when dragging further than 'rearViewRevealWidth' +@property (nonatomic) BOOL bounceBackOnOverdraw; +@property (nonatomic) BOOL bounceBackOnLeftOverdraw; // <-- simetric implementation of the above for the rightViewController + +// If YES (default is NO) the controller will allow permanent dragging up to the rightMostPosition +@property (nonatomic) BOOL stableDragOnOverdraw; +@property (nonatomic) BOOL stableDragOnLeftOverdraw; // <-- simetric implementation of the above for the rightViewController + +// If YES (default is NO) the front view controller will be ofsseted vertically by the height of a navigation bar. +// Use this on iOS7 when you add an instance of RevealViewController as a child of a UINavigationController (or another SWRevealViewController) +// and you want the front view controller to be presented below the navigation bar of its UINavigationController grand parent. +// The rearViewController will still appear full size and blurred behind the navigation bar of its UINavigationController grand parent +@property (nonatomic) BOOL presentFrontViewHierarchically; + +// Velocity required for the controller to toggle its state based on a swipe movement, default is 250 +@property (nonatomic) CGFloat quickFlickVelocity; + +// Duration for the revealToggle animation, default is 0.25 +@property (nonatomic) NSTimeInterval toggleAnimationDuration; + +// Animation type, default is SWRevealToggleAnimationTypeSpring +@property (nonatomic) SWRevealToggleAnimationType toggleAnimationType; + +// When animation type is SWRevealToggleAnimationTypeSpring determines the damping ratio, default is 1 +@property (nonatomic) CGFloat springDampingRatio; + +// Duration for animated replacement of view controllers +@property (nonatomic) NSTimeInterval replaceViewAnimationDuration; + +// Defines the radius of the front view's shadow, default is 2.5f +@property (nonatomic) CGFloat frontViewShadowRadius; + +// Defines the radius of the front view's shadow offset default is {0.0f,2.5f} +@property (nonatomic) CGSize frontViewShadowOffset; + +// Defines the front view's shadow opacity, default is 1.0f +@property (nonatomic) CGFloat frontViewShadowOpacity; + +// Defines the front view's shadow color, default is blackColor +@property (nonatomic) UIColor *frontViewShadowColor; + +// Defines whether the controller should clip subviews to its view bounds. Default is NO. +// Set this to YES when you are presenting this controller as a non full-screen child of a +// custom container controller which does not explicitly clips its subviews. +@property (nonatomic) BOOL clipsViewsToBounds; + +// Defines whether your views clicable area extends beyond the bounds of this controller. Default is NO. +// Set this to YES if you are presenting this controller as a non full-screen child of a custom container and you are not +// clipping your front view to this controller bounds. +@property (nonatomic) BOOL extendsPointInsideHit; + +/* The class properly handles all the relevant calls to appearance methods on the contained controllers. + Moreover you can assign a delegate to let the class inform you on positions and animation activity */ + +// Delegate +@property (nonatomic,weak) id delegate; + +@end + + +#pragma mark - SWRevealViewControllerDelegate Protocol + +typedef enum +{ + SWRevealControllerOperationNone, + SWRevealControllerOperationReplaceRearController, + SWRevealControllerOperationReplaceFrontController, + SWRevealControllerOperationReplaceRightController, + +} SWRevealControllerOperation; + + +@protocol SWRevealViewControllerDelegate + +@optional + +// The following delegate methods will be called before and after the front view moves to a position +- (void)revealController:(SWRevealViewController *)revealController willMoveToPosition:(FrontViewPosition)position; +- (void)revealController:(SWRevealViewController *)revealController didMoveToPosition:(FrontViewPosition)position; + +// This will be called inside the reveal animation, thus you can use it to place your own code that will be animated in sync +- (void)revealController:(SWRevealViewController *)revealController animateToPosition:(FrontViewPosition)position; + +// Implement this to return NO when you want the pan gesture recognizer to be ignored +- (BOOL)revealControllerPanGestureShouldBegin:(SWRevealViewController *)revealController; + +// Implement this to return NO when you want the tap gesture recognizer to be ignored +- (BOOL)revealControllerTapGestureShouldBegin:(SWRevealViewController *)revealController; + +// Implement this to return YES if you want other gesture recognizer to share touch events with the pan gesture +- (BOOL)revealController:(SWRevealViewController *)revealController + panGestureRecognizerShouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer; + +// Implement this to return YES if you want other gesture recognizer to share touch events with the tap gesture +- (BOOL)revealController:(SWRevealViewController *)revealController + tapGestureRecognizerShouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer; + +// Called when the gestureRecognizer began and ended +- (void)revealControllerPanGestureBegan:(SWRevealViewController *)revealController; +- (void)revealControllerPanGestureEnded:(SWRevealViewController *)revealController; + +// The following methods provide a means to track the evolution of the gesture recognizer. +// The 'location' parameter is the X origin coordinate of the front view as the user drags it +// The 'progress' parameter is a number ranging from 0 to 1 indicating the front view location relative to the +// rearRevealWidth or rightRevealWidth. 1 is fully revealed, dragging ocurring in the overDraw region will result in values above 1. +// The 'overProgress' parameter is a number ranging from 0 to 1 indicating the front view location relative to the +// overdraw region. 0 is fully revealed, 1 is fully overdrawn. Negative values occur inside the normal reveal region +- (void)revealController:(SWRevealViewController *)revealController panGestureBeganFromLocation:(CGFloat)location progress:(CGFloat)progress overProgress:(CGFloat)overProgress; +- (void)revealController:(SWRevealViewController *)revealController panGestureMovedToLocation:(CGFloat)location progress:(CGFloat)progress overProgress:(CGFloat)overProgress; +- (void)revealController:(SWRevealViewController *)revealController panGestureEndedToLocation:(CGFloat)location progress:(CGFloat)progress overProgress:(CGFloat)overProgress; + +// Notification of child controller replacement +- (void)revealController:(SWRevealViewController *)revealController willAddViewController:(UIViewController *)viewController + forOperation:(SWRevealControllerOperation)operation animated:(BOOL)animated; +- (void)revealController:(SWRevealViewController *)revealController didAddViewController:(UIViewController *)viewController + forOperation:(SWRevealControllerOperation)operation animated:(BOOL)animated; + +// Support for custom transition animations while replacing child controllers. If implemented, it will be fired in response +// to calls to 'setXXViewController' methods +- (id)revealController:(SWRevealViewController *)revealController + animationControllerForOperation:(SWRevealControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC; + +// DEPRECATED - The following delegate methods will be removed some time in the future +- (void)revealController:(SWRevealViewController *)revealController panGestureBeganFromLocation:(CGFloat)location progress:(CGFloat)progress; // (DEPRECATED) +- (void)revealController:(SWRevealViewController *)revealController panGestureMovedToLocation:(CGFloat)location progress:(CGFloat)progress; // (DEPRECATED) +- (void)revealController:(SWRevealViewController *)revealController panGestureEndedToLocation:(CGFloat)location progress:(CGFloat)progress; // (DEPRECATED) +@end + + +#pragma mark - UIViewController(SWRevealViewController) Category + +// A category of UIViewController to let childViewControllers easily access their parent SWRevealViewController +@interface UIViewController(SWRevealViewController) + +- (SWRevealViewController*)revealViewController; + +@end + + +#pragma mark - StoryBoard support Classes + +/* StoryBoard support */ + +// String identifiers to be applied to segues on a storyboard +extern NSString* const SWSegueRearIdentifier; // this is @"sw_rear" +extern NSString* const SWSegueFrontIdentifier; // this is @"sw_front" +extern NSString* const SWSegueRightIdentifier; // this is @"sw_right" + +/* This will allow the class to be defined on a storyboard */ + +// Use this along with one of the above segue identifiers to segue to the initial state +@interface SWRevealViewControllerSegueSetController : UIStoryboardSegue +@end + +// Use this to push a view controller +@interface SWRevealViewControllerSeguePushController : UIStoryboardSegue +@end + + +//#pragma mark - SWRevealViewControllerSegue (DEPRECATED) +// +//@interface SWRevealViewControllerSegue : UIStoryboardSegue // DEPRECATED: USE SWRevealViewControllerSegueSetController instead +//@property (nonatomic, strong) void(^performBlock)( SWRevealViewControllerSegue* segue, UIViewController* svc, UIViewController* dvc ); +//@end diff --git a/Limelight/ViewControllers/SWRevealViewController.m b/Limelight/ViewControllers/SWRevealViewController.m new file mode 100755 index 00000000..de73c51b --- /dev/null +++ b/Limelight/ViewControllers/SWRevealViewController.m @@ -0,0 +1,1897 @@ +/* + + Copyright (c) 2013 Joan Lluch + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is furnished + to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + Early code inspired on a similar class by Philip Kluz (Philip.Kluz@zuui.org) + +*/ + +#import + +#import "SWRevealViewController.h" + + +#pragma mark - StatusBar Helper Function + +// computes the required offset adjustment due to the status bar for the passed in view, +// it will return the statusBar height if view fully overlaps the statusBar, otherwise returns 0.0f +static CGFloat statusBarAdjustment( UIView* view ) +{ + CGFloat adjustment = 0.0f; + UIApplication *app = [UIApplication sharedApplication]; + CGRect viewFrame = [view convertRect:view.bounds toView:[app keyWindow]]; + CGRect statusBarFrame = [app statusBarFrame]; + + if ( CGRectIntersectsRect(viewFrame, statusBarFrame) ) + adjustment = fminf(statusBarFrame.size.width, statusBarFrame.size.height); + + return adjustment; +} + + +#pragma mark - SWRevealView Class + +@interface SWRevealView: UIView +{ + __weak SWRevealViewController *_c; +} + +@property (nonatomic, readonly) UIView *rearView; +@property (nonatomic, readonly) UIView *rightView; +@property (nonatomic, readonly) UIView *frontView; +@property (nonatomic, assign) BOOL disableLayout; + +@end + + +@interface SWRevealViewController() +- (void)_getRevealWidth:(CGFloat*)pRevealWidth revealOverDraw:(CGFloat*)pRevealOverdraw forSymetry:(int)symetry; +- (void)_getBounceBack:(BOOL*)pBounceBack pStableDrag:(BOOL*)pStableDrag forSymetry:(int)symetry; +- (void)_getAdjustedFrontViewPosition:(FrontViewPosition*)frontViewPosition forSymetry:(int)symetry; +@end + + +@implementation SWRevealView + + +static CGFloat scaledValue( CGFloat v1, CGFloat min2, CGFloat max2, CGFloat min1, CGFloat max1) +{ + CGFloat result = min2 + (v1-min1)*((max2-min2)/(max1-min1)); + if ( result != result ) return min2; // nan + if ( result < min2 ) return min2; + if ( result > max2 ) return max2; + return result; +} + + +- (id)initWithFrame:(CGRect)frame controller:(SWRevealViewController*)controller +{ + self = [super initWithFrame:frame]; + if ( self ) + { + _c = controller; + CGRect bounds = self.bounds; + + _frontView = [[UIView alloc] initWithFrame:bounds]; + _frontView.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; + [self reloadShadow]; + + [self addSubview:_frontView]; + } + return self; +} + + +- (void)reloadShadow +{ + CALayer *frontViewLayer = _frontView.layer; + frontViewLayer.shadowColor = [_c.frontViewShadowColor CGColor]; + frontViewLayer.shadowOpacity = _c.frontViewShadowOpacity; + frontViewLayer.shadowOffset = _c.frontViewShadowOffset; + frontViewLayer.shadowRadius = _c.frontViewShadowRadius; +} + + +- (CGRect)hierarchycalFrameAdjustment:(CGRect)frame +{ + if ( _c.presentFrontViewHierarchically ) + { + UINavigationBar *dummyBar = [[UINavigationBar alloc] init]; + CGFloat barHeight = [dummyBar sizeThatFits:CGSizeMake(100,100)].height; + CGFloat offset = barHeight + statusBarAdjustment(self); + frame.origin.y += offset; + frame.size.height -= offset; + } + return frame; +} + + +- (void)prepareRearViewForPosition:(FrontViewPosition)newPosition +{ + if ( _rearView == nil ) + { + _rearView = [[UIView alloc] initWithFrame:self.bounds]; + _rearView.autoresizingMask = /*UIViewAutoresizingFlexibleWidth|*/UIViewAutoresizingFlexibleHeight; + [self insertSubview:_rearView belowSubview:_frontView]; + } + + CGFloat xLocation = [self frontLocationForPosition:_c.frontViewPosition]; + [self _layoutRearViewsForLocation:xLocation]; + [self _prepareForNewPosition:newPosition]; +} + + +- (void)prepareRightViewForPosition:(FrontViewPosition)newPosition +{ + if ( _rightView == nil ) + { + _rightView = [[UIView alloc] initWithFrame:self.bounds]; + _rightView.autoresizingMask = /*UIViewAutoresizingFlexibleWidth|*/UIViewAutoresizingFlexibleHeight; + [self insertSubview:_rightView belowSubview:_frontView]; + } + + CGFloat xLocation = [self frontLocationForPosition:_c.frontViewPosition]; + [self _layoutRearViewsForLocation:xLocation]; + [self _prepareForNewPosition:newPosition]; +} + + +- (CGFloat)frontLocationForPosition:(FrontViewPosition)frontViewPosition +{ + CGFloat revealWidth; + CGFloat revealOverdraw; + + CGFloat location = 0.0f; + + int symetry = frontViewPosition FrontViewPositionRight ) + location = revealWidth + revealOverdraw; + + return location*symetry; +} + + +- (void)dragFrontViewToXLocation:(CGFloat)xLocation +{ + CGRect bounds = self.bounds; + + xLocation = [self _adjustedDragLocationForLocation:xLocation]; + [self _layoutRearViewsForLocation:xLocation]; + + CGRect frame = CGRectMake(xLocation, 0.0f, bounds.size.width, bounds.size.height); + _frontView.frame = [self hierarchycalFrameAdjustment:frame]; +} + + +# pragma mark - overrides + +- (void)layoutSubviews +{ + if ( _disableLayout ) return; + + CGRect bounds = self.bounds; + + FrontViewPosition position = _c.frontViewPosition; + CGFloat xLocation = [self frontLocationForPosition:position]; + + // set rear view frames + [self _layoutRearViewsForLocation:xLocation]; + + // set front view frame + CGRect frame = CGRectMake(xLocation, 0.0f, bounds.size.width, bounds.size.height); + _frontView.frame = [self hierarchycalFrameAdjustment:frame]; + + // setup front view shadow path if needed (front view loaded and not removed) + UIViewController *frontViewController = _c.frontViewController; + BOOL viewLoaded = frontViewController != nil && frontViewController.isViewLoaded; + BOOL viewNotRemoved = position > FrontViewPositionLeftSideMostRemoved && position < FrontViewPositionRightMostRemoved; + CGRect shadowBounds = viewLoaded && viewNotRemoved ? _frontView.bounds : CGRectZero; + + UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:shadowBounds]; + _frontView.layer.shadowPath = shadowPath.CGPath; +} + + +- (BOOL)pointInsideD:(CGPoint)point withEvent:(UIEvent *)event +{ + BOOL isInside = [super pointInside:point withEvent:event]; + if ( _c.extendsPointInsideHit ) + { + if ( !isInside && _rearView && [_c.rearViewController isViewLoaded] ) + { + CGPoint pt = [self convertPoint:point toView:_rearView]; + isInside = [_rearView pointInside:pt withEvent:event]; + } + + if ( !isInside && _frontView && [_c.frontViewController isViewLoaded] ) + { + CGPoint pt = [self convertPoint:point toView:_frontView]; + isInside = [_frontView pointInside:pt withEvent:event]; + } + + if ( !isInside && _rightView && [_c.rightViewController isViewLoaded] ) + { + CGPoint pt = [self convertPoint:point toView:_rightView]; + isInside = [_rightView pointInside:pt withEvent:event]; + } + } + return isInside; +} + + +- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event +{ + BOOL isInside = [super pointInside:point withEvent:event]; + if ( !isInside && _c.extendsPointInsideHit ) + { + UIView *testViews[] = { _rearView, _frontView, _rightView }; + UIViewController *testControllers[] = { _c.rearViewController, _c.frontViewController, _c.rightViewController }; + + for ( NSInteger i=0 ; i<3 && !isInside ; i++ ) + { + if ( testViews[i] && [testControllers[i] isViewLoaded] ) + { + CGPoint pt = [self convertPoint:point toView:testViews[i]]; + isInside = [testViews[i] pointInside:pt withEvent:event]; + } + } + } + return isInside; +} + + +# pragma mark - private + + +- (void)_layoutRearViewsForLocation:(CGFloat)xLocation +{ + CGRect bounds = self.bounds; + + CGFloat rearRevealWidth = _c.rearViewRevealWidth; + if ( rearRevealWidth < 0) rearRevealWidth = bounds.size.width + _c.rearViewRevealWidth; + + CGFloat rearXLocation = scaledValue(xLocation, -_c.rearViewRevealDisplacement, 0, 0, rearRevealWidth); + + CGFloat rearWidth = rearRevealWidth + _c.rearViewRevealOverdraw; + _rearView.frame = CGRectMake(rearXLocation, 0.0, rearWidth, bounds.size.height); + + CGFloat rightRevealWidth = _c.rightViewRevealWidth; + if ( rightRevealWidth < 0) rightRevealWidth = bounds.size.width + _c.rightViewRevealWidth; + + CGFloat rightXLocation = scaledValue(xLocation, 0, _c.rightViewRevealDisplacement, -rightRevealWidth, 0); + + CGFloat rightWidth = rightRevealWidth + _c.rightViewRevealOverdraw; + _rightView.frame = CGRectMake(bounds.size.width-rightWidth+rightXLocation, 0.0f, rightWidth, bounds.size.height); +} + + +- (void)_prepareForNewPosition:(FrontViewPosition)newPosition; +{ + if ( _rearView == nil || _rightView == nil ) + return; + + int symetry = newPosition 0 && rearIndex < rightIndex) ) + [self exchangeSubviewAtIndex:rightIndex withSubviewAtIndex:rearIndex]; +} + + +- (CGFloat)_adjustedDragLocationForLocation:(CGFloat)x +{ + CGFloat result; + + CGFloat revealWidth; + CGFloat revealOverdraw; + BOOL bounceBack; + BOOL stableDrag; + FrontViewPosition position = _c.frontViewPosition; + + int symetry = x<0 ? -1 : 1; + + [_c _getRevealWidth:&revealWidth revealOverDraw:&revealOverdraw forSymetry:symetry]; + [_c _getBounceBack:&bounceBack pStableDrag:&stableDrag forSymetry:symetry]; + + BOOL stableTrack = !bounceBack || stableDrag || position==FrontViewPositionRightMost || position==FrontViewPositionLeftSideMost; + if ( stableTrack ) + { + revealWidth += revealOverdraw; + revealOverdraw = 0.0f; + } + + x = x * symetry; + + if (x <= revealWidth) + result = x; // Translate linearly. + + else if (x <= revealWidth+2*revealOverdraw) + result = revealWidth + (x-revealWidth)/2; // slow down translation by halph the movement. + + else + result = revealWidth+revealOverdraw; // keep at the rightMost location. + + return result * symetry; +} + +@end + + +#pragma mark - SWContextTransitioningObject + +@interface SWContextTransitionObject : NSObject +@end + + +@implementation SWContextTransitionObject +{ + __weak SWRevealViewController *_revealVC; + UIView *_view; + UIViewController *_toVC; + UIViewController *_fromVC; + void (^_completion)(void); +} + +- (id)initWithRevealController:(SWRevealViewController*)revealVC containerView:(UIView*)view fromVC:(UIViewController*)fromVC + toVC:(UIViewController*)toVC completion:(void (^)(void))completion +{ + self = [super init]; + if ( self ) + { + _revealVC = revealVC; + _view = view; + _fromVC = fromVC; + _toVC = toVC; + _completion = completion; + } + return self; +} + + +- (UIView *)containerView +{ + return _view; +} + + +- (BOOL)isAnimated +{ + return YES; +} + + +- (BOOL)isInteractive +{ + return NO; // not supported +} + +- (BOOL)transitionWasCancelled +{ + return NO; // not supported +} + +- (CGAffineTransform)targetTransform { + return CGAffineTransformIdentity; +} + +- (UIModalPresentationStyle)presentationStyle +{ + return UIModalPresentationNone; // not applicable +} + + +- (void)updateInteractiveTransition:(CGFloat)percentComplete +{ + // not supported +} + + +- (void)finishInteractiveTransition +{ + // not supported +} + + +- (void)cancelInteractiveTransition +{ + // not supported +} + + +- (void)completeTransition:(BOOL)didComplete +{ + _completion(); +} + + +- (UIViewController *)viewControllerForKey:(NSString *)key +{ + if ( [key isEqualToString:UITransitionContextFromViewControllerKey] ) + return _fromVC; + + if ( [key isEqualToString:UITransitionContextToViewControllerKey] ) + return _toVC; + + return nil; +} + + +- (UIView *)viewForKey:(NSString *)key +{ + return nil; +} + + +- (CGRect)initialFrameForViewController:(UIViewController *)vc +{ + return _view.bounds; +} + + +- (CGRect)finalFrameForViewController:(UIViewController *)vc +{ + return _view.bounds; +} + +@end + + +#pragma mark - SWDefaultAnimationController Class + +@interface SWDefaultAnimationController : NSObject +@end + +@implementation SWDefaultAnimationController +{ + NSTimeInterval _duration; +} + + +- (id)initWithDuration:(NSTimeInterval)duration +{ + self = [super init]; + if ( self ) + { + _duration = duration; + } + return self; +} + + +- (NSTimeInterval)transitionDuration:(id)transitionContext +{ + return _duration; +} + + +- (void)animateTransition:(id )transitionContext +{ + UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; + UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; + + if ( fromViewController ) + { + [UIView transitionFromView:fromViewController.view toView:toViewController.view duration:_duration + options:UIViewAnimationOptionTransitionCrossDissolve|UIViewAnimationOptionOverrideInheritedOptions + completion:^(BOOL finished) { [transitionContext completeTransition:finished]; }]; + } + else + { + // tansitionFromView does not correctly handle the case where the fromView is nil (at least on iOS7) it just pops up the toView view with no animation, + // so in such case we replace the crossDissolve animation by a simple alpha animation on the appearing view + UIView *toView = toViewController.view; + CGFloat alpha = toView.alpha; + toView.alpha = 0; + + [UIView animateWithDuration:_duration delay:0 options:UIViewAnimationOptionCurveEaseOut + animations:^{ toView.alpha = alpha;} + completion:^(BOOL finished) { [transitionContext completeTransition:finished];}]; + } +} + +@end + + +#pragma mark - SWRevealViewControllerPanGestureRecognizer + +#import + +@interface SWRevealViewControllerPanGestureRecognizer : UIPanGestureRecognizer +@end + +@implementation SWRevealViewControllerPanGestureRecognizer +{ + BOOL _dragging; + CGPoint _beginPoint; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesBegan:touches withEvent:event]; + + UITouch *touch = [touches anyObject]; + _beginPoint = [touch locationInView:self.view]; + _dragging = NO; +} + + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesMoved:touches withEvent:event]; + + if ( _dragging || self.state == UIGestureRecognizerStateFailed) + return; + + const int kDirectionPanThreshold = 5; + + UITouch *touch = [touches anyObject]; + CGPoint nowPoint = [touch locationInView:self.view]; + + if (abs(nowPoint.x - _beginPoint.x) > kDirectionPanThreshold) _dragging = YES; + else if (abs(nowPoint.y - _beginPoint.y) > kDirectionPanThreshold) self.state = UIGestureRecognizerStateFailed; +} + +@end + + +#pragma mark - SWRevealViewController Class + +@interface SWRevealViewController() +{ + SWRevealView *_contentView; + UIPanGestureRecognizer *_panGestureRecognizer; + UITapGestureRecognizer *_tapGestureRecognizer; + FrontViewPosition _frontViewPosition; + FrontViewPosition _rearViewPosition; + FrontViewPosition _rightViewPosition; + SWContextTransitionObject *_rearTransitioningController; + SWContextTransitionObject *_frontTransitioningController; + SWContextTransitionObject *_rightTransitioningController; +} +@end + + +@implementation SWRevealViewController +{ + FrontViewPosition _panInitialFrontPosition; + NSMutableArray *_animationQueue; + BOOL _userInteractionStore; +} + +const int FrontViewPositionNone = 0xff; + + +#pragma mark - Init + +- (id)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if ( self ) + { + [self _initDefaultProperties]; + } + return self; +} + + +- (id)init +{ + return [self initWithRearViewController:nil frontViewController:nil]; +} + + +- (id)initWithRearViewController:(UIViewController *)rearViewController frontViewController:(UIViewController *)frontViewController; +{ + self = [super init]; + if ( self ) + { + [self _initDefaultProperties]; + [self _performTransitionOperation:SWRevealControllerOperationReplaceRearController withViewController:rearViewController animated:NO]; + [self _performTransitionOperation:SWRevealControllerOperationReplaceFrontController withViewController:frontViewController animated:NO]; + } + return self; +} + + +- (void)_initDefaultProperties +{ + _frontViewPosition = FrontViewPositionLeft; + _rearViewPosition = FrontViewPositionLeft; + _rightViewPosition = FrontViewPositionLeft; + _rearViewRevealWidth = 260.0f; + _rearViewRevealOverdraw = 60.0f; + _rearViewRevealDisplacement = 40.0f; + _rightViewRevealWidth = 260.0f; + _rightViewRevealOverdraw = 60.0f; + _rightViewRevealDisplacement = 40.0f; + _bounceBackOnOverdraw = YES; + _bounceBackOnLeftOverdraw = YES; + _stableDragOnOverdraw = NO; + _stableDragOnLeftOverdraw = NO; + _presentFrontViewHierarchically = NO; + _quickFlickVelocity = 250.0f; + _toggleAnimationDuration = 0.3; + _toggleAnimationType = SWRevealToggleAnimationTypeSpring; + _springDampingRatio = 1; + _replaceViewAnimationDuration = 0.25; + _frontViewShadowRadius = 2.5f; + _frontViewShadowOffset = CGSizeMake(0.0f, 2.5f); + _frontViewShadowOpacity = 1.0f; + _frontViewShadowColor = [UIColor blackColor]; + _userInteractionStore = YES; + _animationQueue = [NSMutableArray array]; + _draggableBorderWidth = 0.0f; + _clipsViewsToBounds = NO; + _extendsPointInsideHit = NO; +} + + +#pragma mark - StatusBar + +- (UIViewController *)childViewControllerForStatusBarStyle +{ + int positionDif = _frontViewPosition - FrontViewPositionLeft; + + UIViewController *controller = _frontViewController; + if ( positionDif > 0 ) controller = _rearViewController; + else if ( positionDif < 0 ) controller = _rightViewController; + + return controller; +} + +- (UIViewController *)childViewControllerForStatusBarHidden +{ + UIViewController *controller = [self childViewControllerForStatusBarStyle]; + return controller; +} + + +#pragma mark - View lifecycle + +- (void)loadView +{ + // Do not call super, to prevent the apis from unfruitful looking for inexistent xibs! + //[super loadView]; + + // load any defined front/rear controllers from the storyboard before + [self loadStoryboardControllers]; + + // This is what Apple used to tell us to set as the initial frame, which is of course totally irrelevant + // with view controller containment patterns, let's leave it for the sake of it! + // CGRect frame = [[UIScreen mainScreen] applicationFrame]; + + // On iOS7 the applicationFrame does not return the whole screen. This is possibly a bug. + // As a workaround we use the screen bounds, this still works on iOS6, any zero based frame would work anyway! + CGRect frame = [[UIScreen mainScreen] bounds]; + + // create a custom content view for the controller + _contentView = [[SWRevealView alloc] initWithFrame:frame controller:self]; + + // set the content view to resize along with its superview + [_contentView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; + + // set the content view to clip its bounds if requested + [_contentView setClipsToBounds:_clipsViewsToBounds]; + + // set our contentView to the controllers view + self.view = _contentView; + + // Apple also tells us to do this: + _contentView.backgroundColor = [UIColor blackColor]; + + // we set the current frontViewPosition to none before seting the + // desired initial position, this will force proper controller reload + FrontViewPosition initialPosition = _frontViewPosition; + _frontViewPosition = FrontViewPositionNone; + _rearViewPosition = FrontViewPositionNone; + _rightViewPosition = FrontViewPositionNone; + + // now set the desired initial position + [self _setFrontViewPosition:initialPosition withDuration:0.0]; +} + + +- (void)viewDidAppear:(BOOL)animated +{ + [super viewDidAppear:animated]; + + // Uncomment the following code if you want the child controllers + // to be loaded at this point. + // + // We leave this commented out because we think loading childs here is conceptually wrong. + // Instead, we refrain view loads until necesary, for example we may never load + // the rear controller view -or the front controller view- if it is never displayed. + // + // If you need to manipulate views of any of your child controllers in an override + // of this method, you can load yourself the views explicitly on your overriden method. + // However we discourage it as an app following the MVC principles should never need to do so + +// [_frontViewController view]; +// [_rearViewController view]; + + // we store at this point the view's user interaction state as we may temporarily disable it + // and resume it back to the previous state, it is possible to override this behaviour by + // intercepting it on the panGestureBegan and panGestureEnded delegates + _userInteractionStore = _contentView.userInteractionEnabled; +} + + +- (NSUInteger)supportedInterfaceOrientations +{ + // we could have simply not implemented this, but we choose to call super to make explicit that we + // want the default behavior. + return [super supportedInterfaceOrientations]; +} + + +#pragma mark - Public methods and property accessors + +- (void)setFrontViewController:(UIViewController *)frontViewController +{ + [self setFrontViewController:frontViewController animated:NO]; +} + + +- (void)setFrontViewController:(UIViewController *)frontViewController animated:(BOOL)animated +{ + if ( ![self isViewLoaded]) + { + [self _performTransitionOperation:SWRevealControllerOperationReplaceFrontController withViewController:frontViewController animated:NO]; + return; + } + + [self _dispatchTransitionOperation:SWRevealControllerOperationReplaceFrontController withViewController:frontViewController animated:animated]; +} + + +- (void)pushFrontViewController:(UIViewController *)frontViewController animated:(BOOL)animated +{ + if ( ![self isViewLoaded]) + { + [self _performTransitionOperation:SWRevealControllerOperationReplaceFrontController withViewController:frontViewController animated:NO]; + return; + } + + [self _dispatchPushFrontViewController:frontViewController animated:animated]; +} + + +- (void)setRearViewController:(UIViewController *)rearViewController +{ + [self setRearViewController:rearViewController animated:NO]; +} + + +- (void)setRearViewController:(UIViewController *)rearViewController animated:(BOOL)animated +{ + if ( ![self isViewLoaded]) + { + [self _performTransitionOperation:SWRevealControllerOperationReplaceRearController withViewController:rearViewController animated:NO]; + return; + } + + [self _dispatchTransitionOperation:SWRevealControllerOperationReplaceRearController withViewController:rearViewController animated:animated]; +} + + +- (void)setRightViewController:(UIViewController *)rightViewController +{ + [self setRightViewController:rightViewController animated:NO]; +} + + +- (void)setRightViewController:(UIViewController *)rightViewController animated:(BOOL)animated +{ + if ( ![self isViewLoaded]) + { + [self _performTransitionOperation:SWRevealControllerOperationReplaceRightController withViewController:rightViewController animated:NO]; + return; + } + + [self _dispatchTransitionOperation:SWRevealControllerOperationReplaceRightController withViewController:rightViewController animated:animated]; +} + + +- (void)revealToggleAnimated:(BOOL)animated +{ + FrontViewPosition toggledFrontViewPosition = FrontViewPositionLeft; + if (_frontViewPosition <= FrontViewPositionLeft) + toggledFrontViewPosition = FrontViewPositionRight; + + [self setFrontViewPosition:toggledFrontViewPosition animated:animated]; +} + + +- (void)rightRevealToggleAnimated:(BOOL)animated +{ + FrontViewPosition toggledFrontViewPosition = FrontViewPositionLeft; + if (_frontViewPosition >= FrontViewPositionLeft) + toggledFrontViewPosition = FrontViewPositionLeftSide; + + [self setFrontViewPosition:toggledFrontViewPosition animated:animated]; +} + + +- (void)setFrontViewPosition:(FrontViewPosition)frontViewPosition +{ + [self setFrontViewPosition:frontViewPosition animated:NO]; +} + + +- (void)setFrontViewPosition:(FrontViewPosition)frontViewPosition animated:(BOOL)animated +{ + if ( ![self isViewLoaded] ) + { + _frontViewPosition = frontViewPosition; + _rearViewPosition = frontViewPosition; + _rightViewPosition = frontViewPosition; + return; + } + + [self _dispatchSetFrontViewPosition:frontViewPosition animated:animated]; +} + + +- (void)setFrontViewShadowRadius:(CGFloat)frontViewShadowRadius +{ + _frontViewShadowRadius = frontViewShadowRadius; + [_contentView reloadShadow]; +} + + +- (void)setFrontViewShadowOffset:(CGSize)frontViewShadowOffset +{ + _frontViewShadowOffset = frontViewShadowOffset; + [_contentView reloadShadow]; +} + + +- (void)setFrontViewShadowOpacity:(CGFloat)frontViewShadowOpacity +{ + _frontViewShadowOpacity = frontViewShadowOpacity; + [_contentView reloadShadow]; +} + + +- (void)setFrontViewShadowColor:(UIColor *)frontViewShadowColor +{ + _frontViewShadowColor = frontViewShadowColor; + [_contentView reloadShadow]; +} + + +- (UIPanGestureRecognizer*)panGestureRecognizer +{ + if ( _panGestureRecognizer == nil ) + { + _panGestureRecognizer = [[SWRevealViewControllerPanGestureRecognizer alloc] initWithTarget:self action:@selector(_handleRevealGesture:)]; + _panGestureRecognizer.delegate = self; + [_contentView.frontView addGestureRecognizer:_panGestureRecognizer]; + } + return _panGestureRecognizer; +} + + +- (UITapGestureRecognizer*)tapGestureRecognizer +{ + if ( _tapGestureRecognizer == nil ) + { + UITapGestureRecognizer *tapRecognizer = + [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(_handleTapGesture:)]; + + tapRecognizer.delegate = self; + [_contentView.frontView addGestureRecognizer:tapRecognizer]; + _tapGestureRecognizer = tapRecognizer ; + } + return _tapGestureRecognizer; +} + + +- (void)setClipsViewsToBounds:(BOOL)clipsViewsToBounds +{ + _clipsViewsToBounds = clipsViewsToBounds; + [_contentView setClipsToBounds:clipsViewsToBounds]; +} + + + +#pragma mark - Provided acction methods + +- (IBAction)revealToggle:(id)sender +{ + [self revealToggleAnimated:YES]; +} + + +- (IBAction)rightRevealToggle:(id)sender +{ + [self rightRevealToggleAnimated:YES]; +} + + +#pragma mark - UserInteractionEnabling + +// disable userInteraction on the entire control +- (void)_disableUserInteraction +{ + [_contentView setUserInteractionEnabled:NO]; + [_contentView setDisableLayout:YES]; +} + +// restore userInteraction on the control +- (void)_restoreUserInteraction +{ + // we use the stored userInteraction state just in case a developer decided + // to have our view interaction disabled beforehand + [_contentView setUserInteractionEnabled:_userInteractionStore]; + [_contentView setDisableLayout:NO]; +} + + +#pragma mark - PanGesture progress notification + +- (void)_notifyPanGestureBegan +{ + if ( [_delegate respondsToSelector:@selector(revealControllerPanGestureBegan:)] ) + [_delegate revealControllerPanGestureBegan:self]; + + CGFloat xLocation, dragProgress, overProgress; + [self _getDragLocation:&xLocation progress:&dragProgress overdrawProgress:&overProgress]; + + if ( [_delegate respondsToSelector:@selector(revealController:panGestureBeganFromLocation:progress:overProgress:)] ) + [_delegate revealController:self panGestureBeganFromLocation:xLocation progress:dragProgress overProgress:overProgress]; + + else if ( [_delegate respondsToSelector:@selector(revealController:panGestureBeganFromLocation:progress:)] ) + [_delegate revealController:self panGestureBeganFromLocation:xLocation progress:dragProgress]; +} + +- (void)_notifyPanGestureMoved +{ + CGFloat xLocation, dragProgress, overProgress; + [self _getDragLocation:&xLocation progress:&dragProgress overdrawProgress:&overProgress]; + + if ( [_delegate respondsToSelector:@selector(revealController:panGestureMovedToLocation:progress:overProgress:)] ) + [_delegate revealController:self panGestureMovedToLocation:xLocation progress:dragProgress overProgress:overProgress]; + + else if ( [_delegate respondsToSelector:@selector(revealController:panGestureMovedToLocation:progress:)] ) + [_delegate revealController:self panGestureMovedToLocation:xLocation progress:dragProgress]; +} + +- (void)_notifyPanGestureEnded +{ + CGFloat xLocation, dragProgress, overProgress; + [self _getDragLocation:&xLocation progress:&dragProgress overdrawProgress:&overProgress]; + + if ( [_delegate respondsToSelector:@selector(revealController:panGestureEndedToLocation:progress:overProgress:)] ) + [_delegate revealController:self panGestureEndedToLocation:xLocation progress:dragProgress overProgress:overProgress]; + + else if ( [_delegate respondsToSelector:@selector(revealController:panGestureEndedToLocation:progress:)] ) + [_delegate revealController:self panGestureEndedToLocation:xLocation progress:dragProgress]; + + if ( [_delegate respondsToSelector:@selector(revealControllerPanGestureEnded:)] ) + [_delegate revealControllerPanGestureEnded:self]; +} + + +#pragma mark - Symetry + +- (void)_getRevealWidth:(CGFloat*)pRevealWidth revealOverDraw:(CGFloat*)pRevealOverdraw forSymetry:(int)symetry +{ + if ( symetry < 0 ) *pRevealWidth = _rightViewRevealWidth, *pRevealOverdraw = _rightViewRevealOverdraw; + else *pRevealWidth = _rearViewRevealWidth, *pRevealOverdraw = _rearViewRevealOverdraw; + + if (*pRevealWidth < 0) *pRevealWidth = _contentView.bounds.size.width + *pRevealWidth; +} + +- (void)_getBounceBack:(BOOL*)pBounceBack pStableDrag:(BOOL*)pStableDrag forSymetry:(int)symetry +{ + if ( symetry < 0 ) *pBounceBack = _bounceBackOnLeftOverdraw, *pStableDrag = _stableDragOnLeftOverdraw; + else *pBounceBack = _bounceBackOnOverdraw, *pStableDrag = _stableDragOnOverdraw; +} + +- (void)_getAdjustedFrontViewPosition:(FrontViewPosition*)frontViewPosition forSymetry:(int)symetry +{ + if ( symetry < 0 ) *frontViewPosition = FrontViewPositionLeft + symetry*(*frontViewPosition-FrontViewPositionLeft); +} + +- (void)_getDragLocationx:(CGFloat*)xLocation progress:(CGFloat*)progress +{ + UIView *frontView = _contentView.frontView; + *xLocation = frontView.frame.origin.x; + + int symetry = *xLocation<0 ? -1 : 1; + + CGFloat xWidth = symetry < 0 ? _rightViewRevealWidth : _rearViewRevealWidth; + if ( xWidth < 0 ) xWidth = _contentView.bounds.size.width + xWidth; + + *progress = *xLocation/xWidth * symetry; +} + +- (void)_getDragLocation:(CGFloat*)xLocation progress:(CGFloat*)progress overdrawProgress:(CGFloat*)overProgress +{ + UIView *frontView = _contentView.frontView; + *xLocation = frontView.frame.origin.x; + + int symetry = *xLocation<0 ? -1 : 1; + + CGFloat xWidth = symetry < 0 ? _rightViewRevealWidth : _rearViewRevealWidth; + CGFloat xOverWidth = symetry < 0 ? _rightViewRevealOverdraw : _rearViewRevealOverdraw; + + if ( xWidth < 0 ) xWidth = _contentView.bounds.size.width + xWidth; + + *progress = *xLocation*symetry/xWidth; + *overProgress = (*xLocation*symetry-xWidth)/xOverWidth; +} + + +#pragma mark - Deferred block execution queue + +// Define a convenience macro to enqueue single statements +#define _enqueue(code) [self _enqueueBlock:^{code;}]; + +// Defers the execution of the passed in block until a paired _dequeue call is received, +// or executes the block right away if no pending requests are present. +- (void)_enqueueBlock:(void (^)(void))block +{ + [_animationQueue insertObject:block atIndex:0]; + if ( _animationQueue.count == 1) + { + block(); + } +} + +// Removes the top most block in the queue and executes the following one if any. +// Calls to this method must be paired with calls to _enqueueBlock, particularly it may be called +// from within a block passed to _enqueueBlock to remove itself when done with animations. +- (void)_dequeue +{ + [_animationQueue removeLastObject]; + + if ( _animationQueue.count > 0 ) + { + void (^block)(void) = [_animationQueue lastObject]; + block(); + } +} + + +#pragma mark - Gesture Delegate + +- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)recognizer +{ + // only allow gesture if no previous request is in process + if ( _animationQueue.count == 0 ) + { + if ( recognizer == _panGestureRecognizer ) + return [self _panGestureShouldBegin]; + + if ( recognizer == _tapGestureRecognizer ) + return [self _tapGestureShouldBegin]; + } + + return NO; +} + + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer +{ + if ( gestureRecognizer == _panGestureRecognizer ) + { + if ( [_delegate respondsToSelector:@selector(revealController:panGestureRecognizerShouldRecognizeSimultaneouslyWithGestureRecognizer:)] ) + if ( [_delegate revealController:self panGestureRecognizerShouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer] != NO ) + return YES; + } + if ( gestureRecognizer == _tapGestureRecognizer ) + { + if ( [_delegate respondsToSelector:@selector(revealController:tapGestureRecognizerShouldRecognizeSimultaneouslyWithGestureRecognizer:)] ) + if ( [_delegate revealController:self tapGestureRecognizerShouldRecognizeSimultaneouslyWithGestureRecognizer:otherGestureRecognizer] != NO ) + return YES; + } + + return NO; +} + + +- (BOOL)_tapGestureShouldBegin +{ + if ( _frontViewPosition == FrontViewPositionLeft || + _frontViewPosition == FrontViewPositionRightMostRemoved || + _frontViewPosition == FrontViewPositionLeftSideMostRemoved ) + return NO; + + // forbid gesture if the following delegate is implemented and returns NO + if ( [_delegate respondsToSelector:@selector(revealControllerTapGestureShouldBegin:)] ) + if ( [_delegate revealControllerTapGestureShouldBegin:self] == NO ) + return NO; + + return YES; +} + + +- (BOOL)_panGestureShouldBegin +{ + // forbid gesture if the initial translation is not horizontal + UIView *recognizerView = _panGestureRecognizer.view; + CGPoint translation = [_panGestureRecognizer translationInView:recognizerView]; +// NSLog( @"translation:%@", NSStringFromCGPoint(translation) ); +// if ( fabs(translation.y/translation.x) > 1 ) +// return NO; + + // forbid gesture if the following delegate is implemented and returns NO + if ( [_delegate respondsToSelector:@selector(revealControllerPanGestureShouldBegin:)] ) + if ( [_delegate revealControllerPanGestureShouldBegin:self] == NO ) + return NO; + + CGFloat xLocation = [_panGestureRecognizer locationInView:recognizerView].x; + CGFloat width = recognizerView.bounds.size.width; + + BOOL draggableBorderAllowing = ( + /*_frontViewPosition != FrontViewPositionLeft ||*/ _draggableBorderWidth == 0.0f || + (_rearViewController && xLocation <= _draggableBorderWidth) || + (_rightViewController && xLocation >= (width - _draggableBorderWidth)) ); + + + BOOL translationForbidding = ( _frontViewPosition == FrontViewPositionLeft && + ((_rearViewController == nil && translation.x > 0) || (_rightViewController == nil && translation.x < 0)) ); + + // allow gesture only within the bounds defined by the draggableBorderWidth property + return draggableBorderAllowing && !translationForbidding ; +} + + +#pragma mark - Gesture Based Reveal + +- (void)_handleTapGesture:(UITapGestureRecognizer *)recognizer +{ + NSTimeInterval duration = _toggleAnimationDuration; + [self _setFrontViewPosition:FrontViewPositionLeft withDuration:duration]; +} + + +- (void)_handleRevealGesture:(UIPanGestureRecognizer *)recognizer +{ + switch ( recognizer.state ) + { + case UIGestureRecognizerStateBegan: + [self _handleRevealGestureStateBeganWithRecognizer:recognizer]; + break; + + case UIGestureRecognizerStateChanged: + [self _handleRevealGestureStateChangedWithRecognizer:recognizer]; + break; + + case UIGestureRecognizerStateEnded: + [self _handleRevealGestureStateEndedWithRecognizer:recognizer]; + break; + + case UIGestureRecognizerStateCancelled: + //case UIGestureRecognizerStateFailed: + [self _handleRevealGestureStateCancelledWithRecognizer:recognizer]; + break; + + default: + break; + } +} + + +- (void)_handleRevealGestureStateBeganWithRecognizer:(UIPanGestureRecognizer *)recognizer +{ + // we know that we will not get here unless the animationQueue is empty because the recognizer + // delegate prevents it, however we do not want any forthcoming programatic actions to disturb + // the gesture, so we just enqueue a dummy block to ensure any programatic acctions will be + // scheduled after the gesture is completed + [self _enqueueBlock:^{}]; // <-- dummy block + + // we store the initial position and initialize a target position + _panInitialFrontPosition = _frontViewPosition; + + // we disable user interactions on the views, however programatic accions will still be + // enqueued to be performed after the gesture completes + [self _disableUserInteraction]; + [self _notifyPanGestureBegan]; +} + + +- (void)_handleRevealGestureStateChangedWithRecognizer:(UIPanGestureRecognizer *)recognizer +{ + CGFloat translation = [recognizer translationInView:_contentView].x; + + CGFloat baseLocation = [_contentView frontLocationForPosition:_panInitialFrontPosition]; + CGFloat xLocation = baseLocation + translation; + + if ( xLocation < 0 ) + { + if ( _rightViewController == nil ) xLocation = 0; + [self _rightViewDeploymentForNewFrontViewPosition:FrontViewPositionLeftSide](); + [self _rearViewDeploymentForNewFrontViewPosition:FrontViewPositionLeftSide](); + } + + if ( xLocation > 0 ) + { + if ( _rearViewController == nil ) xLocation = 0; + [self _rightViewDeploymentForNewFrontViewPosition:FrontViewPositionRight](); + [self _rearViewDeploymentForNewFrontViewPosition:FrontViewPositionRight](); + } + + [_contentView dragFrontViewToXLocation:xLocation]; + [self _notifyPanGestureMoved]; +} + + +- (void)_handleRevealGestureStateEndedWithRecognizer:(UIPanGestureRecognizer *)recognizer +{ + UIView *frontView = _contentView.frontView; + + CGFloat xLocation = frontView.frame.origin.x; + CGFloat velocity = [recognizer velocityInView:_contentView].x; + //NSLog( @"Velocity:%1.4f", velocity); + + // depending on position we compute a simetric replacement of widths and positions + int symetry = xLocation<0 ? -1 : 1; + + // simetring computing of widths + CGFloat revealWidth ; + CGFloat revealOverdraw ; + BOOL bounceBack; + BOOL stableDrag; + + [self _getRevealWidth:&revealWidth revealOverDraw:&revealOverdraw forSymetry:symetry]; + [self _getBounceBack:&bounceBack pStableDrag:&stableDrag forSymetry:symetry]; + + // simetric replacement of position + xLocation = xLocation * symetry; + + // initially we assume drag to left and default duration + FrontViewPosition frontViewPosition = FrontViewPositionLeft; + NSTimeInterval duration = _toggleAnimationDuration; + + // Velocity driven change: + if (fabsf(velocity) > _quickFlickVelocity) + { + // we may need to set the drag position and to adjust the animation duration + CGFloat journey = xLocation; + if (velocity*symetry > 0.0f) + { + frontViewPosition = FrontViewPositionRight; + journey = revealWidth - xLocation; + if (xLocation > revealWidth) + { + if (!bounceBack && stableDrag /*&& xPosition > _rearViewRevealWidth+_rearViewRevealOverdraw*0.5f*/) + { + frontViewPosition = FrontViewPositionRightMost; + journey = revealWidth+revealOverdraw - xLocation; + } + } + } + + duration = fabsf(journey/velocity); + } + + // Position driven change: + else + { + // we may need to set the drag position + if (xLocation > revealWidth*0.5f) + { + frontViewPosition = FrontViewPositionRight; + if (xLocation > revealWidth) + { + if (bounceBack) + frontViewPosition = FrontViewPositionLeft; + + else if (stableDrag && xLocation > revealWidth+revealOverdraw*0.5f) + frontViewPosition = FrontViewPositionRightMost; + } + } + } + + // symetric replacement of frontViewPosition + [self _getAdjustedFrontViewPosition:&frontViewPosition forSymetry:symetry]; + + // restore user interaction and animate to the final position + [self _restoreUserInteraction]; + [self _notifyPanGestureEnded]; + [self _setFrontViewPosition:frontViewPosition withDuration:duration]; +} + + +- (void)_handleRevealGestureStateCancelledWithRecognizer:(UIPanGestureRecognizer *)recognizer +{ + [self _restoreUserInteraction]; + [self _notifyPanGestureEnded]; + [self _dequeue]; +} + + +#pragma mark Enqueued position and controller setup + +- (void)_dispatchSetFrontViewPosition:(FrontViewPosition)frontViewPosition animated:(BOOL)animated +{ + NSTimeInterval duration = animated?_toggleAnimationDuration:0.0; + __weak SWRevealViewController *theSelf = self; + _enqueue( [theSelf _setFrontViewPosition:frontViewPosition withDuration:duration] ); +} + + +- (void)_dispatchPushFrontViewController:(UIViewController *)newFrontViewController animated:(BOOL)animated +{ + FrontViewPosition preReplacementPosition = FrontViewPositionLeft; + if ( _frontViewPosition > FrontViewPositionLeft ) preReplacementPosition = FrontViewPositionRightMost; + if ( _frontViewPosition < FrontViewPositionLeft ) preReplacementPosition = FrontViewPositionLeftSideMost; + + NSTimeInterval duration = animated?_toggleAnimationDuration:0.0; + NSTimeInterval firstDuration = duration; + int initialPosDif = abs( _frontViewPosition - preReplacementPosition ); + if ( initialPosDif == 1 ) firstDuration *= 0.8; + else if ( initialPosDif == 0 ) firstDuration = 0; + + __weak SWRevealViewController *theSelf = self; + if ( animated ) + { + _enqueue( [theSelf _setFrontViewPosition:preReplacementPosition withDuration:firstDuration] ); + _enqueue( [theSelf _performTransitionOperation:SWRevealControllerOperationReplaceFrontController withViewController:newFrontViewController animated:NO] ); + _enqueue( [theSelf _setFrontViewPosition:FrontViewPositionLeft withDuration:duration] ); + } + else + { + _enqueue( [theSelf _performTransitionOperation:SWRevealControllerOperationReplaceFrontController withViewController:newFrontViewController animated:NO] ); + } +} + + +- (void)_dispatchTransitionOperation:(SWRevealControllerOperation)operation withViewController:(UIViewController *)newViewController animated:(BOOL)animated +{ + __weak SWRevealViewController *theSelf = self; + _enqueue( [theSelf _performTransitionOperation:operation withViewController:newViewController animated:animated] ); +} + + +#pragma mark Animated view controller deployment and layout + +// Primitive method for view controller deployment and animated layout to the given position. +- (void)_setFrontViewPosition:(FrontViewPosition)newPosition withDuration:(NSTimeInterval)duration +{ + void (^rearDeploymentCompletion)() = [self _rearViewDeploymentForNewFrontViewPosition:newPosition]; + void (^rightDeploymentCompletion)() = [self _rightViewDeploymentForNewFrontViewPosition:newPosition]; + void (^frontDeploymentCompletion)() = [self _frontViewDeploymentForNewFrontViewPosition:newPosition]; + + void (^animations)() = ^() + { + // Calling this in the animation block causes the status bar to appear/dissapear in sync with our own animation + [self setNeedsStatusBarAppearanceUpdate]; + + // We call the layoutSubviews method on the contentView view and send a delegate, which will + // occur inside of an animation block if any animated transition is being performed + [_contentView layoutSubviews]; + + if ([_delegate respondsToSelector:@selector(revealController:animateToPosition:)]) + [_delegate revealController:self animateToPosition:_frontViewPosition]; + }; + + void (^completion)(BOOL) = ^(BOOL finished) + { + rearDeploymentCompletion(); + rightDeploymentCompletion(); + frontDeploymentCompletion(); + [self _dequeue]; + }; + + if ( duration > 0.0 ) + { + if ( _toggleAnimationType == SWRevealToggleAnimationTypeEaseOut ) + { + [UIView animateWithDuration:duration delay:0.0 + options:UIViewAnimationOptionCurveEaseOut animations:animations completion:completion]; + } + else + { + [UIView animateWithDuration:_toggleAnimationDuration delay:0.0 usingSpringWithDamping:_springDampingRatio initialSpringVelocity:1/duration + options:0 animations:animations completion:completion]; + } + } + else + { + animations(); + completion(YES); + } +} + + +// Primitive method for animated controller transition +//- (void)_performTransitionToViewController:(UIViewController*)new operation:(SWRevealControllerOperation)operation animated:(BOOL)animated +- (void)_performTransitionOperation:(SWRevealControllerOperation)operation withViewController:(UIViewController*)new animated:(BOOL)animated +{ + if ( [_delegate respondsToSelector:@selector(revealController:willAddViewController:forOperation:animated:)] ) + [_delegate revealController:self willAddViewController:new forOperation:operation animated:animated]; + + UIViewController *old = nil; + UIView *view = nil; + + if ( operation == SWRevealControllerOperationReplaceRearController ) + old = _rearViewController, _rearViewController = new, view = _contentView.rearView; + + else if ( operation == SWRevealControllerOperationReplaceFrontController ) + old = _frontViewController, _frontViewController = new, view = _contentView.frontView; + + else if ( operation == SWRevealControllerOperationReplaceRightController ) + old = _rightViewController, _rightViewController = new, view = _contentView.rightView; + + void (^completion)() = [self _transitionFromViewController:old toViewController:new inView:view]; + + void (^animationCompletion)() = ^ + { + completion(); + if ( [_delegate respondsToSelector:@selector(revealController:didAddViewController:forOperation:animated:)] ) + [_delegate revealController:self didAddViewController:new forOperation:operation animated:animated]; + + [self _dequeue]; + }; + + if ( animated ) + { + id animationController = nil; + + if ( [_delegate respondsToSelector:@selector(revealController:animationControllerForOperation:fromViewController:toViewController:)] ) + animationController = [_delegate revealController:self animationControllerForOperation:operation fromViewController:old toViewController:new]; + + if ( !animationController ) + animationController = [[SWDefaultAnimationController alloc] initWithDuration:_replaceViewAnimationDuration]; + + SWContextTransitionObject *transitioningObject = [[SWContextTransitionObject alloc] initWithRevealController:self containerView:view + fromVC:old toVC:new completion:animationCompletion]; + + if ( [animationController transitionDuration:transitioningObject] > 0 ) + [animationController animateTransition:transitioningObject]; + else + animationCompletion(); + } + else + { + animationCompletion(); + } +} + + +#pragma mark Position based view controller deployment + +// Deploy/Undeploy of the front view controller following the containment principles. Returns a block +// that must be invoked on animation completion in order to finish deployment +- (void (^)(void))_frontViewDeploymentForNewFrontViewPosition:(FrontViewPosition)newPosition +{ + if ( (_rightViewController == nil && newPosition < FrontViewPositionLeft) || + (_rearViewController == nil && newPosition > FrontViewPositionLeft) ) + newPosition = FrontViewPositionLeft; + + BOOL positionIsChanging = (_frontViewPosition != newPosition); + + BOOL appear = + (_frontViewPosition >= FrontViewPositionRightMostRemoved || _frontViewPosition <= FrontViewPositionLeftSideMostRemoved || _frontViewPosition == FrontViewPositionNone) && + (newPosition < FrontViewPositionRightMostRemoved && newPosition > FrontViewPositionLeftSideMostRemoved); + + BOOL disappear = + (newPosition >= FrontViewPositionRightMostRemoved || newPosition <= FrontViewPositionLeftSideMostRemoved ) && + (_frontViewPosition < FrontViewPositionRightMostRemoved && _frontViewPosition > FrontViewPositionLeftSideMostRemoved && _frontViewPosition != FrontViewPositionNone); + + if ( positionIsChanging ) + { + if ( [_delegate respondsToSelector:@selector(revealController:willMoveToPosition:)] ) + [_delegate revealController:self willMoveToPosition:newPosition]; + } + + _frontViewPosition = newPosition; + + void (^deploymentCompletion)() = + [self _deploymentForViewController:_frontViewController inView:_contentView.frontView appear:appear disappear:disappear]; + + void (^completion)() = ^() + { + deploymentCompletion(); + if ( positionIsChanging ) + { + if ( [_delegate respondsToSelector:@selector(revealController:didMoveToPosition:)] ) + [_delegate revealController:self didMoveToPosition:newPosition]; + } + }; + + return completion; +} + +// Deploy/Undeploy of the left view controller following the containment principles. Returns a block +// that must be invoked on animation completion in order to finish deployment +- (void (^)(void))_rearViewDeploymentForNewFrontViewPosition:(FrontViewPosition)newPosition +{ + if ( _presentFrontViewHierarchically ) + newPosition = FrontViewPositionRight; + + if ( _rearViewController == nil && newPosition > FrontViewPositionLeft ) + newPosition = FrontViewPositionLeft; + + BOOL appear = (_rearViewPosition <= FrontViewPositionLeft || _rearViewPosition == FrontViewPositionNone) && newPosition > FrontViewPositionLeft; + BOOL disappear = newPosition <= FrontViewPositionLeft && (_rearViewPosition > FrontViewPositionLeft && _rearViewPosition != FrontViewPositionNone); + + if ( appear ) + [_contentView prepareRearViewForPosition:newPosition]; + + _rearViewPosition = newPosition; + + return [self _deploymentForViewController:_rearViewController inView:_contentView.rearView appear:appear disappear:disappear]; +} + +// Deploy/Undeploy of the right view controller following the containment principles. Returns a block +// that must be invoked on animation completion in order to finish deployment +- (void (^)(void))_rightViewDeploymentForNewFrontViewPosition:(FrontViewPosition)newPosition +{ + if ( _rightViewController == nil && newPosition < FrontViewPositionLeft ) + newPosition = FrontViewPositionLeft; + + BOOL appear = (_rightViewPosition >= FrontViewPositionLeft || _rightViewPosition == FrontViewPositionNone) && newPosition < FrontViewPositionLeft ; + BOOL disappear = newPosition >= FrontViewPositionLeft && (_rightViewPosition < FrontViewPositionLeft && _rightViewPosition != FrontViewPositionNone); + + if ( appear ) + [_contentView prepareRightViewForPosition:newPosition]; + + _rightViewPosition = newPosition; + + return [self _deploymentForViewController:_rightViewController inView:_contentView.rightView appear:appear disappear:disappear]; +} + + +- (void (^)(void)) _deploymentForViewController:(UIViewController*)controller inView:(UIView*)view appear:(BOOL)appear disappear:(BOOL)disappear +{ + if ( appear ) return [self _deployForViewController:controller inView:view]; + if ( disappear ) return [self _undeployForViewController:controller]; + return ^{}; +} + + +#pragma mark Containment view controller deployment and transition + +// Containment Deploy method. Returns a block to be invoked at the +// animation completion, or right after return in case of non-animated deployment. +- (void (^)(void))_deployForViewController:(UIViewController*)controller inView:(UIView*)view +{ + if ( !controller || !view ) + return ^(void){}; + + CGRect frame = view.bounds; + + UIView *controllerView = controller.view; + controllerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + controllerView.frame = frame; + + if ( [controllerView isKindOfClass:[UIScrollView class]] ) + { + BOOL adjust = controller.automaticallyAdjustsScrollViewInsets; + + if ( adjust ) + { + [(id)controllerView setContentInset:UIEdgeInsetsMake(statusBarAdjustment(_contentView), 0, 0, 0)]; + } + } + + [view addSubview:controllerView]; + + void (^completionBlock)(void) = ^(void) + { + // nothing to do on completion at this stage + }; + + return completionBlock; +} + +// Containment Undeploy method. Returns a block to be invoked at the +// animation completion, or right after return in case of non-animated deployment. +- (void (^)(void))_undeployForViewController:(UIViewController*)controller +{ + if (!controller) + return ^(void){}; + + // nothing to do before completion at this stage + + void (^completionBlock)(void) = ^(void) + { + [controller.view removeFromSuperview]; + }; + + return completionBlock; +} + +// Containment Transition method. Returns a block to be invoked at the +// animation completion, or right after return in case of non-animated transition. +- (void(^)(void))_transitionFromViewController:(UIViewController*)fromController toViewController:(UIViewController*)toController inView:(UIView*)view +{ + if ( fromController == toController ) + return ^(void){}; + + if ( toController ) [self addChildViewController:toController]; + + void (^deployCompletion)() = [self _deployForViewController:toController inView:view]; + + [fromController willMoveToParentViewController:nil]; + + void (^undeployCompletion)() = [self _undeployForViewController:fromController]; + + void (^completionBlock)(void) = ^(void) + { + undeployCompletion() ; + [fromController removeFromParentViewController]; + + deployCompletion() ; + [toController didMoveToParentViewController:self]; + }; + return completionBlock; +} + +// Load any defined front/rear controllers from the storyboard +// This method is intended to be overrided in case the default behavior will not meet your needs +- (void)loadStoryboardControllers +{ + if ( self.storyboard && _rearViewController == nil ) + { + //Try each segue separately so it doesn't break prematurely if either Rear or Right views are not used. + @try + { + [self performSegueWithIdentifier:SWSegueRearIdentifier sender:nil]; + } + @catch(NSException *exception) {} + + @try + { + [self performSegueWithIdentifier:SWSegueFrontIdentifier sender:nil]; + } + @catch(NSException *exception) {} + + @try + { + [self performSegueWithIdentifier:SWSegueRightIdentifier sender:nil]; + } + @catch(NSException *exception) {} + } +} + + +#pragma mark state preservation / restoration + ++ (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder*)coder +{ + SWRevealViewController* vc = nil; + UIStoryboard* sb = [coder decodeObjectForKey:UIStateRestorationViewControllerStoryboardKey]; + + if (sb) + { + vc = (SWRevealViewController*)[sb instantiateViewControllerWithIdentifier:@"SWRevealViewController"]; + vc.restorationIdentifier = [identifierComponents lastObject]; + vc.restorationClass = [SWRevealViewController class]; + } + return vc; +} + + +- (void)encodeRestorableStateWithCoder:(NSCoder *)coder +{ + [coder encodeDouble:_rearViewRevealWidth forKey:@"_rearViewRevealWidth"]; + [coder encodeDouble:_rearViewRevealOverdraw forKey:@"_rearViewRevealOverdraw"]; + [coder encodeDouble:_rearViewRevealDisplacement forKey:@"_rearViewRevealDisplacement"]; + [coder encodeDouble:_rightViewRevealWidth forKey:@"_rightViewRevealWidth"]; + [coder encodeDouble:_rightViewRevealOverdraw forKey:@"_rightViewRevealOverdraw"]; + [coder encodeDouble:_rightViewRevealDisplacement forKey:@"_rightViewRevealDisplacement"]; + [coder encodeBool:_bounceBackOnOverdraw forKey:@"_bounceBackOnOverdraw"]; + [coder encodeBool:_bounceBackOnLeftOverdraw forKey:@"_bounceBackOnLeftOverdraw"]; + [coder encodeBool:_stableDragOnOverdraw forKey:@"_stableDragOnOverdraw"]; + [coder encodeBool:_stableDragOnLeftOverdraw forKey:@"_stableDragOnLeftOverdraw"]; + [coder encodeBool:_presentFrontViewHierarchically forKey:@"_presentFrontViewHierarchically"]; + [coder encodeDouble:_quickFlickVelocity forKey:@"_quickFlickVelocity"]; + [coder encodeDouble:_toggleAnimationDuration forKey:@"_toggleAnimationDuration"]; + [coder encodeInteger:_toggleAnimationType forKey:@"_toggleAnimationType"]; + [coder encodeDouble:_springDampingRatio forKey:@"_springDampingRatio"]; + [coder encodeDouble:_replaceViewAnimationDuration forKey:@"_replaceViewAnimationDuration"]; + [coder encodeDouble:_frontViewShadowRadius forKey:@"_frontViewShadowRadius"]; + [coder encodeCGSize:_frontViewShadowOffset forKey:@"_frontViewShadowOffset"]; + [coder encodeDouble:_frontViewShadowOpacity forKey:@"_frontViewShadowOpacity"]; + [coder encodeObject:_frontViewShadowColor forKey:@"_frontViewShadowColor"]; + [coder encodeBool:_userInteractionStore forKey:@"_userInteractionStore"]; + [coder encodeDouble:_draggableBorderWidth forKey:@"_draggableBorderWidth"]; + [coder encodeBool:_clipsViewsToBounds forKey:@"_clipsViewsToBounds"]; + [coder encodeBool:_extendsPointInsideHit forKey:@"_extendsPointInsideHit"]; + + [coder encodeObject:_rearViewController forKey:@"_rearViewController"]; + [coder encodeObject:_frontViewController forKey:@"_frontViewController"]; + [coder encodeObject:_rightViewController forKey:@"_rightViewController"]; + + [coder encodeInteger:_frontViewPosition forKey:@"_frontViewPosition"]; + + [super encodeRestorableStateWithCoder:coder]; +} + + +- (void)decodeRestorableStateWithCoder:(NSCoder *)coder +{ + _rearViewRevealWidth = [coder decodeDoubleForKey:@"_rearViewRevealWidth"]; + _rearViewRevealOverdraw = [coder decodeDoubleForKey:@"_rearViewRevealOverdraw"]; + _rearViewRevealDisplacement = [coder decodeDoubleForKey:@"_rearViewRevealDisplacement"]; + _rightViewRevealWidth = [coder decodeDoubleForKey:@"_rightViewRevealWidth"]; + _rightViewRevealOverdraw = [coder decodeDoubleForKey:@"_rightViewRevealOverdraw"]; + _rightViewRevealDisplacement = [coder decodeDoubleForKey:@"_rightViewRevealDisplacement"]; + _bounceBackOnOverdraw = [coder decodeBoolForKey:@"_bounceBackOnOverdraw"]; + _bounceBackOnLeftOverdraw = [coder decodeBoolForKey:@"_bounceBackOnLeftOverdraw"]; + _stableDragOnOverdraw = [coder decodeBoolForKey:@"_stableDragOnOverdraw"]; + _stableDragOnLeftOverdraw = [coder decodeBoolForKey:@"_stableDragOnLeftOverdraw"]; + _presentFrontViewHierarchically = [coder decodeBoolForKey:@"_presentFrontViewHierarchically"]; + _quickFlickVelocity = [coder decodeDoubleForKey:@"_quickFlickVelocity"]; + _toggleAnimationDuration = [coder decodeDoubleForKey:@"_toggleAnimationDuration"]; + _toggleAnimationType = [coder decodeIntegerForKey:@"_toggleAnimationType"]; + _springDampingRatio = [coder decodeDoubleForKey:@"_springDampingRatio"]; + _replaceViewAnimationDuration = [coder decodeDoubleForKey:@"_replaceViewAnimationDuration"]; + _frontViewShadowRadius = [coder decodeDoubleForKey:@"_frontViewShadowRadius"]; + _frontViewShadowOffset = [coder decodeCGSizeForKey:@"_frontViewShadowOffset"]; + _frontViewShadowOpacity = [coder decodeDoubleForKey:@"_frontViewShadowOpacity"]; + _frontViewShadowColor = [coder decodeObjectForKey:@"_frontViewShadowColor"]; + _userInteractionStore = [coder decodeBoolForKey:@"_userInteractionStore"]; + _animationQueue = [NSMutableArray array]; + _draggableBorderWidth = [coder decodeDoubleForKey:@"_draggableBorderWidth"]; + _clipsViewsToBounds = [coder decodeBoolForKey:@"_clipsViewsToBounds"]; + _extendsPointInsideHit = [coder decodeBoolForKey:@"_extendsPointInsideHit"]; + + [self setRearViewController:[coder decodeObjectForKey:@"_rearViewController"]]; + [self setFrontViewController:[coder decodeObjectForKey:@"_frontViewController"]]; + [self setRightViewController:[coder decodeObjectForKey:@"_rightViewController"]]; + + [self setFrontViewPosition:[coder decodeIntForKey: @"_frontViewPosition"]]; + + [super decodeRestorableStateWithCoder:coder]; +} + + +- (void)applicationFinishedRestoringState +{ + // nothing to do at this stage +} + + +@end + + +#pragma mark - UIViewController(SWRevealViewController) Category + +@implementation UIViewController(SWRevealViewController) + +- (SWRevealViewController*)revealViewController +{ + UIViewController *parent = self; + Class revealClass = [SWRevealViewController class]; + while ( nil != (parent = [parent parentViewController]) && ![parent isKindOfClass:revealClass] ) {} + return (id)parent; +} + +@end + + +#pragma mark - SWRevealViewControllerSegueSetController segue identifiers + +NSString * const SWSegueRearIdentifier = @"sw_rear"; +NSString * const SWSegueFrontIdentifier = @"sw_front"; +NSString * const SWSegueRightIdentifier = @"sw_right"; + + +#pragma mark - SWRevealViewControllerSegueSetController class + +@implementation SWRevealViewControllerSegueSetController + +- (void)perform +{ + SWRevealControllerOperation operation = SWRevealControllerOperationNone; + + NSString *identifier = self.identifier; + SWRevealViewController *rvc = self.sourceViewController; + UIViewController *dvc = self.destinationViewController; + + if ( [identifier isEqualToString:SWSegueFrontIdentifier] ) + operation = SWRevealControllerOperationReplaceFrontController; + + else if ( [identifier isEqualToString:SWSegueRearIdentifier] ) + operation = SWRevealControllerOperationReplaceRearController; + + else if ( [identifier isEqualToString:SWSegueRightIdentifier] ) + operation = SWRevealControllerOperationReplaceRightController; + + if ( operation != SWRevealControllerOperationNone ) + [rvc _performTransitionOperation:operation withViewController:dvc animated:NO]; +} + +@end + + +#pragma mark - SWRevealViewControllerSeguePushController class + +@implementation SWRevealViewControllerSeguePushController + +- (void)perform +{ + SWRevealViewController *rvc = [self.sourceViewController revealViewController]; + UIViewController *dvc = self.destinationViewController; + [rvc pushFrontViewController:dvc animated:YES]; +} + +@end + + +//#pragma mark - SWRevealViewControllerSegue Class +// +//@implementation SWRevealViewControllerSegue // DEPRECATED +// +//- (void)perform +//{ +// if ( _performBlock ) +// _performBlock( self, self.sourceViewController, self.destinationViewController ); +//} +// +//@end +// +// +//#pragma mark Storyboard support +// +//@implementation SWRevealViewController(deprecated) +// +//- (void)prepareForSegue:(SWRevealViewControllerSegue *)segue sender:(id)sender // TO REMOVE: DEPRECATED IMPLEMENTATION +//{ +// // This method is required for compatibility with SWRevealViewControllerSegue, now deprecated. +// // It can be simply removed when using SWRevealViewControllerSegueSetController and SWRevealViewControlerSeguePushController +// +// NSString *identifier = segue.identifier; +// if ( [segue isKindOfClass:[SWRevealViewControllerSegue class]] && sender == nil ) +// { +// if ( [identifier isEqualToString:SWSegueRearIdentifier] ) +// { +// segue.performBlock = ^(SWRevealViewControllerSegue* rvc_segue, UIViewController* svc, UIViewController* dvc) +// { +// [self _setRearViewController:dvc animated:NO]; +// }; +// } +// else if ( [identifier isEqualToString:SWSegueFrontIdentifier] ) +// { +// segue.performBlock = ^(SWRevealViewControllerSegue* rvc_segue, UIViewController* svc, UIViewController* dvc) +// { +// [self _setFrontViewController:dvc animated:NO]; +// }; +// } +// else if ( [identifier isEqualToString:SWSegueRightIdentifier] ) +// { +// segue.performBlock = ^(SWRevealViewControllerSegue* rvc_segue, UIViewController* svc, UIViewController* dvc) +// { +// [self _setRightViewController:dvc animated:NO]; +// }; +// } +// } +//} +// +//@end + + diff --git a/Limelight/ViewControllers/SettingsViewController.h b/Limelight/ViewControllers/SettingsViewController.h new file mode 100644 index 00000000..704f97ae --- /dev/null +++ b/Limelight/ViewControllers/SettingsViewController.h @@ -0,0 +1,20 @@ +// +// SettingsViewController.h +// Limelight +// +// Created by Diego Waxemberg on 10/27/14. +// Copyright (c) 2014 Limelight Stream. All rights reserved. +// + +#import +#import "AppDelegate.h" + +@interface SettingsViewController : UIViewController +@property (strong, nonatomic) IBOutlet UILabel *bitrateLabel; +@property (strong, nonatomic) IBOutlet UISlider *bitrateSlider; +@property (strong, nonatomic) IBOutlet UISegmentedControl *framerateSelector; +@property (strong, nonatomic) IBOutlet UISegmentedControl *resolutionSelector; + +- (void) saveSettings; + +@end diff --git a/Limelight/ViewControllers/SettingsViewController.m b/Limelight/ViewControllers/SettingsViewController.m new file mode 100644 index 00000000..667283ec --- /dev/null +++ b/Limelight/ViewControllers/SettingsViewController.m @@ -0,0 +1,64 @@ +// +// SettingsViewController.m +// Limelight +// +// Created by Diego Waxemberg on 10/27/14. +// Copyright (c) 2014 Limelight Stream. All rights reserved. +// + +#import "SettingsViewController.h" +#import "Settings.h" +#import "DataManager.h" + +#define BITRATE_INTERVAL 100 + +@implementation SettingsViewController { + NSInteger _bitrate; +} +static NSString* bitrateFormat = @"Bitrate: %d bps"; + + +- (void)viewDidLoad { + [super viewDidLoad]; + + DataManager* dataMan = [[DataManager alloc] init]; + Settings* currentSettings = [dataMan retrieveSettings]; + + _bitrate = [currentSettings.bitrate integerValue]; + NSInteger framerate = [currentSettings.framerate integerValue] == 30 ? 0 : 1; + NSInteger resolution = [currentSettings.height integerValue] == 720 ? 0 : 1; + + [self.resolutionSelector setSelectedSegmentIndex:resolution]; + [self.framerateSelector setSelectedSegmentIndex:framerate]; + [self.bitrateSlider setValue:_bitrate animated:YES]; + [self.bitrateSlider addTarget:self action:@selector(bitrateSliderMoved) forControlEvents:UIControlEventValueChanged]; + [self.bitrateLabel setText:[NSString stringWithFormat:bitrateFormat, (int)_bitrate]]; +} + +- (void) bitrateSliderMoved { + _bitrate = BITRATE_INTERVAL * floor((self.bitrateSlider.value/BITRATE_INTERVAL)+0.5); + [self.bitrateLabel setText:[NSString stringWithFormat:bitrateFormat, (int)_bitrate]]; +} + + +- (void) saveSettings { + DataManager* dataMan = [[DataManager alloc] init]; + NSInteger framerate = [self.framerateSelector selectedSegmentIndex] == 0 ? 30 : 60; + NSInteger height = [self.resolutionSelector selectedSegmentIndex] == 0 ? 720 : 1080; + NSInteger width = height == 720 ? 1280 : 1920; + [dataMan saveSettingsWithBitrate:_bitrate framerate:framerate height:height width:width]; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + + +#pragma mark - Navigation + +- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { +} + + +@end diff --git a/Limelight/ViewControllers/StreamFrameViewController.m b/Limelight/ViewControllers/StreamFrameViewController.m index 0331ee3a..fa3a4c4a 100644 --- a/Limelight/ViewControllers/StreamFrameViewController.m +++ b/Limelight/ViewControllers/StreamFrameViewController.m @@ -25,6 +25,8 @@ { [super viewDidLoad]; + [self.navigationController setNavigationBarHidden:YES animated:YES]; + [self.stageLabel setText:@"Starting App"]; [self.stageLabel sizeToFit]; self.stageLabel.center = CGPointMake(self.view.frame.size.width / 2, self.stageLabel.center.y); @@ -45,9 +47,13 @@ object:nil]; } +- (void) returnToMainFrame { + [self.navigationController popToRootViewControllerAnimated:YES]; +} + - (void)applicationWillResignActive:(NSNotification *)notification { [_streamMan stopStream]; - [self performSegueWithIdentifier:@"returnToMainFrame" sender:self]; + [self returnToMainFrame]; } - (void) connectionStarted { @@ -65,7 +71,7 @@ UIAlertController* conTermAlert = [UIAlertController alertControllerWithTitle:@"Connection Terminated" message:@"The connection terminated unexpectedly" preferredStyle:UIAlertControllerStyleAlert]; [conTermAlert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){ - [self performSegueWithIdentifier:@"returnToMainFrame" sender:self]; + [self returnToMainFrame]; }]]; [self presentViewController:conTermAlert animated:YES completion:nil]; @@ -92,7 +98,7 @@ stageName, errorCode] preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){ - [self performSegueWithIdentifier:@"returnToMainFrame" sender:self]; + [self returnToMainFrame]; }]]; [self presentViewController:alert animated:YES completion:nil]; } @@ -102,7 +108,7 @@ message:message preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){ - [self performSegueWithIdentifier:@"returnToMainFrame" sender:self]; + [self returnToMainFrame]; }]]; [self presentViewController:alert animated:YES completion:nil]; } diff --git a/iPad.storyboard b/iPad.storyboard index 37d9a06f..8ab6bbdc 100644 --- a/iPad.storyboard +++ b/iPad.storyboard @@ -11,10 +11,20 @@ + + + + @@ -60,6 +70,9 @@ + + + diff --git a/iPhone.storyboard b/iPhone.storyboard index efe9f1d9..1068cadb 100644 --- a/iPhone.storyboard +++ b/iPhone.storyboard @@ -1,10 +1,10 @@ - + - + @@ -14,15 +14,119 @@ + + + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -46,20 +150,24 @@ + + - - + + + +