diff --git a/.gitignore b/.gitignore index 89c499e4..bf6b99dc 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,7 @@ profile *.moved-aside DerivedData .idea/ + +# CocoaPods +Demo/Pods/ +Podfile.lock diff --git a/Demo/Podfile b/Demo/Podfile new file mode 100644 index 00000000..ea269bcc --- /dev/null +++ b/Demo/Podfile @@ -0,0 +1,10 @@ +source 'https://github.com/CocoaPods/Specs.git' + +platform :ios, '9.0' + +target 'SCStackViewController' do + + pod 'SCScrollView', '~> 1.1' + pod 'ChameleonFramework' + +end \ No newline at end of file diff --git a/Demo/SCStackViewController.xcodeproj/project.pbxproj b/Demo/SCStackViewController.xcodeproj/project.pbxproj index 87c577f5..a74df6d0 100644 --- a/Demo/SCStackViewController.xcodeproj/project.pbxproj +++ b/Demo/SCStackViewController.xcodeproj/project.pbxproj @@ -8,51 +8,100 @@ /* Begin PBXBuildFile section */ 1823A8E117BEAC7500D58582 /* UIColor+RandomColors.m in Sources */ = {isa = PBXBuildFile; fileRef = 1823A8D017BEAC7500D58582 /* UIColor+RandomColors.m */; }; - 1823A8E217BEAC7500D58582 /* UIViewController+Shadows.m in Sources */ = {isa = PBXBuildFile; fileRef = 1823A8D217BEAC7500D58582 /* UIViewController+Shadows.m */; }; + 1823A8E217BEAC7500D58582 /* UIView+Shadows.m in Sources */ = {isa = PBXBuildFile; fileRef = 1823A8D217BEAC7500D58582 /* UIView+Shadows.m */; }; 1823A8E317BEAC7500D58582 /* SCAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1823A8D417BEAC7500D58582 /* SCAppDelegate.m */; }; - 1823A8E417BEAC7500D58582 /* SCMenuViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1823A8D617BEAC7500D58582 /* SCMenuViewController.m */; }; - 1823A8E517BEAC7500D58582 /* SCMenuViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1823A8D717BEAC7500D58582 /* SCMenuViewController.xib */; }; 1823A8E817BEAC7500D58582 /* SCRootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1823A8DF17BEAC7500D58582 /* SCRootViewController.m */; }; 1823A8E917BEAC7500D58582 /* SCRootViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1823A8E017BEAC7500D58582 /* SCRootViewController.xib */; }; + 1830BFDE1886C060004ED34D /* SCStackNavigationStep.m in Sources */ = {isa = PBXBuildFile; fileRef = 1830BFDD1886C060004ED34D /* SCStackNavigationStep.m */; }; 18364B4817BFB6450066F67A /* SCMainViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18364B4617BFB6450066F67A /* SCMainViewController.m */; }; 18364B4917BFB6450066F67A /* SCMainViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18364B4717BFB6450066F67A /* SCMainViewController.xib */; }; - 18C4D6AB17F0C82A0046FACD /* SCReversedStackLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C4D69B17F0C82A0046FACD /* SCReversedStackLayouter.m */; }; - 18C4D6AC17F0C82A0046FACD /* SCGoogleMapsStackLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C4D69D17F0C82A0046FACD /* SCGoogleMapsStackLayouter.m */; }; - 18C4D6AD17F0C82A0046FACD /* SCMerryGoRoundStackLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C4D69F17F0C82A0046FACD /* SCMerryGoRoundStackLayouter.m */; }; + 183913261A6ADD7700BD9B59 /* SCStackViewControllerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 183913251A6ADD7700BD9B59 /* SCStackViewControllerView.m */; }; + 18439FB518912EB10094674E /* SCResizingStackLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18439FB418912EB10094674E /* SCResizingStackLayouter.m */; }; + 186814A71B36A82B00697D20 /* panorama.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 186814A61B36A82B00697D20 /* panorama.jpg */; }; + 186F755C1B00A0FB0073714C /* left-menu-tab.png in Resources */ = {isa = PBXBuildFile; fileRef = 186F75581B00A0FB0073714C /* left-menu-tab.png */; }; + 186F755D1B00A0FB0073714C /* left-menu-tab@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 186F75591B00A0FB0073714C /* left-menu-tab@2x.png */; }; + 186F755E1B00A0FB0073714C /* right-menu-tab.png in Resources */ = {isa = PBXBuildFile; fileRef = 186F755A1B00A0FB0073714C /* right-menu-tab.png */; }; + 186F755F1B00A0FB0073714C /* right-menu-tab@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 186F755B1B00A0FB0073714C /* right-menu-tab@2x.png */; }; + 1870D3C61AFEC0EE0041C6D8 /* feathers.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 1870D3C51AFEC0EE0041C6D8 /* feathers.jpg */; }; + 189627941AFE8212003D96F1 /* SCReversedStackLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 189627931AFE8212003D96F1 /* SCReversedStackLayouter.m */; }; + 189627961AFE8423003D96F1 /* london.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 189627951AFE8423003D96F1 /* london.jpg */; }; + 18B382381B01D6AF008E7C4B /* SCImagesLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B382261B01D6AF008E7C4B /* SCImagesLayouter.m */; }; + 18B382391B01D6AF008E7C4B /* SCImageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B382281B01D6AF008E7C4B /* SCImageViewController.m */; }; + 18B3823A1B01D6AF008E7C4B /* SCImageViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18B382291B01D6AF008E7C4B /* SCImageViewController.xib */; }; + 18B3823B1B01D6AF008E7C4B /* SCMenusLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B3822C1B01D6AF008E7C4B /* SCMenusLayouter.m */; }; + 18B3823C1B01D6AF008E7C4B /* SCMenuViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B3822E1B01D6AF008E7C4B /* SCMenuViewController.m */; }; + 18B3823D1B01D6AF008E7C4B /* SCMenuViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18B3822F1B01D6AF008E7C4B /* SCMenuViewController.xib */; }; + 18B3823E1B01D6AF008E7C4B /* SCModalViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B382321B01D6AF008E7C4B /* SCModalViewController.m */; }; + 18B3823F1B01D6AF008E7C4B /* SCModalViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18B382331B01D6AF008E7C4B /* SCModalViewController.xib */; }; + 18B382401B01D6AF008E7C4B /* SCTitleBarViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B382361B01D6AF008E7C4B /* SCTitleBarViewController.m */; }; + 18B382411B01D6AF008E7C4B /* SCTitleBarViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18B382371B01D6AF008E7C4B /* SCTitleBarViewController.xib */; }; + 18B382471B01D929008E7C4B /* SCModalLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B382461B01D929008E7C4B /* SCModalLayouter.m */; }; + 18B601781840F0F500E3A0DD /* SCOverlayView.m in Sources */ = {isa = PBXBuildFile; fileRef = 18B601771840F0F500E3A0DD /* SCOverlayView.m */; }; + 18B6017A1840F10700E3A0DD /* SCOverlayView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18B601791840F10700E3A0DD /* SCOverlayView.xib */; }; 18C4D6AE17F0C82A0046FACD /* SCParallaxStackLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C4D6A117F0C82A0046FACD /* SCParallaxStackLayouter.m */; }; 18C4D6AF17F0C82A0046FACD /* SCSlidingStackLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C4D6A317F0C82A0046FACD /* SCSlidingStackLayouter.m */; }; 18C4D6B017F0C82A0046FACD /* SCStackLayouter.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C4D6A517F0C82A0046FACD /* SCStackLayouter.m */; }; 18C4D6B117F0C82A0046FACD /* SCStackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C4D6A817F0C82A0046FACD /* SCStackViewController.m */; }; - 18C4D6B217F0C82A0046FACD /* UIViewController+SCStackViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C4D6AA17F0C82A0046FACD /* UIViewController+SCStackViewController.m */; }; 18C8B23317BFC443000FAEE5 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18C8B23217BFC443000FAEE5 /* QuartzCore.framework */; }; + 18ED27791B02481800659E87 /* SCTitleBarCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 18ED27771B02481800659E87 /* SCTitleBarCollectionViewCell.m */; }; + 18ED277A1B02481800659E87 /* SCTitleBarCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18ED27781B02481800659E87 /* SCTitleBarCollectionViewCell.xib */; }; 18FEAAE617B3ED2F00788EF0 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FEAAE517B3ED2F00788EF0 /* UIKit.framework */; }; 18FEAAE817B3ED2F00788EF0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FEAAE717B3ED2F00788EF0 /* Foundation.framework */; }; 18FEAAEA17B3ED2F00788EF0 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 18FEAAE917B3ED2F00788EF0 /* CoreGraphics.framework */; }; 18FEAAF217B3ED2F00788EF0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 18FEAAF117B3ED2F00788EF0 /* main.m */; }; + 337A2AFDA433D08548B8EAAD /* libPods-SCStackViewController.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3860B666A25773930F16D279 /* libPods-SCStackViewController.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 1823A8CF17BEAC7500D58582 /* UIColor+RandomColors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIColor+RandomColors.h"; sourceTree = ""; }; 1823A8D017BEAC7500D58582 /* UIColor+RandomColors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+RandomColors.m"; sourceTree = ""; }; - 1823A8D117BEAC7500D58582 /* UIViewController+Shadows.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Shadows.h"; sourceTree = ""; }; - 1823A8D217BEAC7500D58582 /* UIViewController+Shadows.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Shadows.m"; sourceTree = ""; }; + 1823A8D117BEAC7500D58582 /* UIView+Shadows.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Shadows.h"; sourceTree = ""; }; + 1823A8D217BEAC7500D58582 /* UIView+Shadows.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Shadows.m"; sourceTree = ""; }; 1823A8D317BEAC7500D58582 /* SCAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCAppDelegate.h; sourceTree = ""; }; 1823A8D417BEAC7500D58582 /* SCAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCAppDelegate.m; sourceTree = ""; }; - 1823A8D517BEAC7500D58582 /* SCMenuViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCMenuViewController.h; sourceTree = ""; }; - 1823A8D617BEAC7500D58582 /* SCMenuViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCMenuViewController.m; sourceTree = ""; }; - 1823A8D717BEAC7500D58582 /* SCMenuViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCMenuViewController.xib; sourceTree = ""; }; 1823A8DE17BEAC7500D58582 /* SCRootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCRootViewController.h; sourceTree = ""; }; 1823A8DF17BEAC7500D58582 /* SCRootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCRootViewController.m; sourceTree = ""; }; 1823A8E017BEAC7500D58582 /* SCRootViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCRootViewController.xib; sourceTree = ""; }; + 1830BFDC1886C060004ED34D /* SCStackNavigationStep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCStackNavigationStep.h; sourceTree = ""; }; + 1830BFDD1886C060004ED34D /* SCStackNavigationStep.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCStackNavigationStep.m; sourceTree = ""; }; 18364B4517BFB6450066F67A /* SCMainViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCMainViewController.h; sourceTree = ""; }; 18364B4617BFB6450066F67A /* SCMainViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCMainViewController.m; sourceTree = ""; }; 18364B4717BFB6450066F67A /* SCMainViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCMainViewController.xib; sourceTree = ""; }; - 18C4D69A17F0C82A0046FACD /* SCReversedStackLayouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCReversedStackLayouter.h; sourceTree = ""; }; - 18C4D69B17F0C82A0046FACD /* SCReversedStackLayouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCReversedStackLayouter.m; sourceTree = ""; }; - 18C4D69C17F0C82A0046FACD /* SCGoogleMapsStackLayouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCGoogleMapsStackLayouter.h; sourceTree = ""; }; - 18C4D69D17F0C82A0046FACD /* SCGoogleMapsStackLayouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCGoogleMapsStackLayouter.m; sourceTree = ""; }; - 18C4D69E17F0C82A0046FACD /* SCMerryGoRoundStackLayouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCMerryGoRoundStackLayouter.h; sourceTree = ""; }; - 18C4D69F17F0C82A0046FACD /* SCMerryGoRoundStackLayouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCMerryGoRoundStackLayouter.m; sourceTree = ""; }; + 183913241A6ADD7700BD9B59 /* SCStackViewControllerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCStackViewControllerView.h; sourceTree = ""; }; + 183913251A6ADD7700BD9B59 /* SCStackViewControllerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCStackViewControllerView.m; sourceTree = ""; }; + 18439FB318912EB10094674E /* SCResizingStackLayouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCResizingStackLayouter.h; sourceTree = ""; }; + 18439FB418912EB10094674E /* SCResizingStackLayouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCResizingStackLayouter.m; sourceTree = ""; }; + 186814A61B36A82B00697D20 /* panorama.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = panorama.jpg; sourceTree = ""; }; + 186F75581B00A0FB0073714C /* left-menu-tab.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "left-menu-tab.png"; sourceTree = ""; }; + 186F75591B00A0FB0073714C /* left-menu-tab@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "left-menu-tab@2x.png"; sourceTree = ""; }; + 186F755A1B00A0FB0073714C /* right-menu-tab.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "right-menu-tab.png"; sourceTree = ""; }; + 186F755B1B00A0FB0073714C /* right-menu-tab@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "right-menu-tab@2x.png"; sourceTree = ""; }; + 1870D3C21AFEA7C40041C6D8 /* SCStackedViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCStackedViewControllerProtocol.h; sourceTree = ""; }; + 1870D3C51AFEC0EE0041C6D8 /* feathers.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = feathers.jpg; sourceTree = ""; }; + 189627921AFE8212003D96F1 /* SCReversedStackLayouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCReversedStackLayouter.h; sourceTree = ""; }; + 189627931AFE8212003D96F1 /* SCReversedStackLayouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCReversedStackLayouter.m; sourceTree = ""; }; + 189627951AFE8423003D96F1 /* london.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = london.jpg; sourceTree = ""; }; + 18B382251B01D6AF008E7C4B /* SCImagesLayouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCImagesLayouter.h; sourceTree = ""; }; + 18B382261B01D6AF008E7C4B /* SCImagesLayouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCImagesLayouter.m; sourceTree = ""; }; + 18B382271B01D6AF008E7C4B /* SCImageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCImageViewController.h; sourceTree = ""; }; + 18B382281B01D6AF008E7C4B /* SCImageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCImageViewController.m; sourceTree = ""; }; + 18B382291B01D6AF008E7C4B /* SCImageViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCImageViewController.xib; sourceTree = ""; }; + 18B3822B1B01D6AF008E7C4B /* SCMenusLayouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCMenusLayouter.h; sourceTree = ""; }; + 18B3822C1B01D6AF008E7C4B /* SCMenusLayouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCMenusLayouter.m; sourceTree = ""; }; + 18B3822D1B01D6AF008E7C4B /* SCMenuViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCMenuViewController.h; sourceTree = ""; }; + 18B3822E1B01D6AF008E7C4B /* SCMenuViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCMenuViewController.m; sourceTree = ""; }; + 18B3822F1B01D6AF008E7C4B /* SCMenuViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCMenuViewController.xib; sourceTree = ""; }; + 18B382311B01D6AF008E7C4B /* SCModalViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCModalViewController.h; sourceTree = ""; }; + 18B382321B01D6AF008E7C4B /* SCModalViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCModalViewController.m; sourceTree = ""; }; + 18B382331B01D6AF008E7C4B /* SCModalViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCModalViewController.xib; sourceTree = ""; }; + 18B382351B01D6AF008E7C4B /* SCTitleBarViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCTitleBarViewController.h; sourceTree = ""; }; + 18B382361B01D6AF008E7C4B /* SCTitleBarViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCTitleBarViewController.m; sourceTree = ""; }; + 18B382371B01D6AF008E7C4B /* SCTitleBarViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCTitleBarViewController.xib; sourceTree = ""; }; + 18B382451B01D929008E7C4B /* SCModalLayouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCModalLayouter.h; sourceTree = ""; }; + 18B382461B01D929008E7C4B /* SCModalLayouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCModalLayouter.m; sourceTree = ""; }; + 18B601761840F0F500E3A0DD /* SCOverlayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCOverlayView.h; sourceTree = ""; }; + 18B601771840F0F500E3A0DD /* SCOverlayView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCOverlayView.m; sourceTree = ""; }; + 18B601791840F10700E3A0DD /* SCOverlayView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCOverlayView.xib; sourceTree = ""; }; 18C4D6A017F0C82A0046FACD /* SCParallaxStackLayouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCParallaxStackLayouter.h; sourceTree = ""; }; 18C4D6A117F0C82A0046FACD /* SCParallaxStackLayouter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCParallaxStackLayouter.m; sourceTree = ""; }; 18C4D6A217F0C82A0046FACD /* SCSlidingStackLayouter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCSlidingStackLayouter.h; sourceTree = ""; }; @@ -62,16 +111,22 @@ 18C4D6A617F0C82A0046FACD /* SCStackLayouterProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCStackLayouterProtocol.h; sourceTree = ""; }; 18C4D6A717F0C82A0046FACD /* SCStackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCStackViewController.h; sourceTree = ""; }; 18C4D6A817F0C82A0046FACD /* SCStackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCStackViewController.m; sourceTree = ""; }; - 18C4D6A917F0C82A0046FACD /* UIViewController+SCStackViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+SCStackViewController.h"; sourceTree = ""; }; - 18C4D6AA17F0C82A0046FACD /* UIViewController+SCStackViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+SCStackViewController.m"; sourceTree = ""; }; 18C8B23217BFC443000FAEE5 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + 18ED27761B02481800659E87 /* SCTitleBarCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCTitleBarCollectionViewCell.h; sourceTree = ""; }; + 18ED27771B02481800659E87 /* SCTitleBarCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SCTitleBarCollectionViewCell.m; sourceTree = ""; }; + 18ED27781B02481800659E87 /* SCTitleBarCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SCTitleBarCollectionViewCell.xib; sourceTree = ""; }; 18FEAAE217B3ED2F00788EF0 /* SCStackViewController.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SCStackViewController.app; sourceTree = BUILT_PRODUCTS_DIR; }; 18FEAAE517B3ED2F00788EF0 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 18FEAAE717B3ED2F00788EF0 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 18FEAAE917B3ED2F00788EF0 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 18FEAAED17B3ED2F00788EF0 /* SCStackViewController-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SCStackViewController-Info.plist"; sourceTree = ""; }; 18FEAAF117B3ED2F00788EF0 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 18FEAAF317B3ED2F00788EF0 /* SCStackViewController-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SCStackViewController-Prefix.pch"; sourceTree = ""; }; + 3860B666A25773930F16D279 /* libPods-SCStackViewController.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SCStackViewController.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 642CD41E6249C9730BE87603 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + B350B7897D79F062B8C2C2FC /* Pods-SCStackViewController.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SCStackViewController.release.xcconfig"; path = "Pods/Target Support Files/Pods-SCStackViewController/Pods-SCStackViewController.release.xcconfig"; sourceTree = ""; }; + EBCFE08D7F870C09CCE0F816 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + EBD53A29BD55EE85F062AF39 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + F5F7A1638BCE9DE9FE3728FD /* Pods-SCStackViewController.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SCStackViewController.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SCStackViewController/Pods-SCStackViewController.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -83,6 +138,7 @@ 18FEAAE617B3ED2F00788EF0 /* UIKit.framework in Frameworks */, 18FEAAE817B3ED2F00788EF0 /* Foundation.framework in Frameworks */, 18FEAAEA17B3ED2F00788EF0 /* CoreGraphics.framework in Frameworks */, + 337A2AFDA433D08548B8EAAD /* libPods-SCStackViewController.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -94,20 +150,93 @@ children = ( 1823A8CF17BEAC7500D58582 /* UIColor+RandomColors.h */, 1823A8D017BEAC7500D58582 /* UIColor+RandomColors.m */, - 1823A8D117BEAC7500D58582 /* UIViewController+Shadows.h */, - 1823A8D217BEAC7500D58582 /* UIViewController+Shadows.m */, + 1823A8D117BEAC7500D58582 /* UIView+Shadows.h */, + 1823A8D217BEAC7500D58582 /* UIView+Shadows.m */, ); path = Helpers; sourceTree = ""; }; + 1896276A1AFE70CB003D96F1 /* Resources */ = { + isa = PBXGroup; + children = ( + 1896276B1AFE70CB003D96F1 /* Images */, + ); + path = Resources; + sourceTree = ""; + }; + 1896276B1AFE70CB003D96F1 /* Images */ = { + isa = PBXGroup; + children = ( + 186814A61B36A82B00697D20 /* panorama.jpg */, + 1870D3C51AFEC0EE0041C6D8 /* feathers.jpg */, + 186F75581B00A0FB0073714C /* left-menu-tab.png */, + 186F75591B00A0FB0073714C /* left-menu-tab@2x.png */, + 189627951AFE8423003D96F1 /* london.jpg */, + 186F755A1B00A0FB0073714C /* right-menu-tab.png */, + 186F755B1B00A0FB0073714C /* right-menu-tab@2x.png */, + ); + path = Images; + sourceTree = ""; + }; + 18B382241B01D6AF008E7C4B /* ImagesDemo */ = { + isa = PBXGroup; + children = ( + 18B382251B01D6AF008E7C4B /* SCImagesLayouter.h */, + 18B382261B01D6AF008E7C4B /* SCImagesLayouter.m */, + 18B382271B01D6AF008E7C4B /* SCImageViewController.h */, + 18B382281B01D6AF008E7C4B /* SCImageViewController.m */, + 18B382291B01D6AF008E7C4B /* SCImageViewController.xib */, + ); + path = ImagesDemo; + sourceTree = ""; + }; + 18B3822A1B01D6AF008E7C4B /* MenusDemo */ = { + isa = PBXGroup; + children = ( + 18B3822B1B01D6AF008E7C4B /* SCMenusLayouter.h */, + 18B3822C1B01D6AF008E7C4B /* SCMenusLayouter.m */, + 18B3822D1B01D6AF008E7C4B /* SCMenuViewController.h */, + 18B3822E1B01D6AF008E7C4B /* SCMenuViewController.m */, + 18B3822F1B01D6AF008E7C4B /* SCMenuViewController.xib */, + ); + path = MenusDemo; + sourceTree = ""; + }; + 18B382301B01D6AF008E7C4B /* ModalDemo */ = { + isa = PBXGroup; + children = ( + 18B382451B01D929008E7C4B /* SCModalLayouter.h */, + 18B382461B01D929008E7C4B /* SCModalLayouter.m */, + 18B382311B01D6AF008E7C4B /* SCModalViewController.h */, + 18B382321B01D6AF008E7C4B /* SCModalViewController.m */, + 18B382331B01D6AF008E7C4B /* SCModalViewController.xib */, + ); + path = ModalDemo; + sourceTree = ""; + }; + 18B382341B01D6AF008E7C4B /* TitleBarDemo */ = { + isa = PBXGroup; + children = ( + 18B382351B01D6AF008E7C4B /* SCTitleBarViewController.h */, + 18B382361B01D6AF008E7C4B /* SCTitleBarViewController.m */, + 18B382371B01D6AF008E7C4B /* SCTitleBarViewController.xib */, + 18ED27761B02481800659E87 /* SCTitleBarCollectionViewCell.h */, + 18ED27771B02481800659E87 /* SCTitleBarCollectionViewCell.m */, + 18ED27781B02481800659E87 /* SCTitleBarCollectionViewCell.xib */, + ); + path = TitleBarDemo; + sourceTree = ""; + }; 18C4D69717F0C82A0046FACD /* SCStackViewController */ = { isa = PBXGroup; children = ( 18C4D69817F0C82A0046FACD /* Layouters */, 18C4D6A717F0C82A0046FACD /* SCStackViewController.h */, 18C4D6A817F0C82A0046FACD /* SCStackViewController.m */, - 18C4D6A917F0C82A0046FACD /* UIViewController+SCStackViewController.h */, - 18C4D6AA17F0C82A0046FACD /* UIViewController+SCStackViewController.m */, + 183913241A6ADD7700BD9B59 /* SCStackViewControllerView.h */, + 183913251A6ADD7700BD9B59 /* SCStackViewControllerView.m */, + 1830BFDC1886C060004ED34D /* SCStackNavigationStep.h */, + 1830BFDD1886C060004ED34D /* SCStackNavigationStep.m */, ); name = SCStackViewController; path = ../../SCStackViewController; @@ -116,37 +245,28 @@ 18C4D69817F0C82A0046FACD /* Layouters */ = { isa = PBXGroup; children = ( - 18C4D69917F0C82A0046FACD /* Reversed */, - 18C4D69C17F0C82A0046FACD /* SCGoogleMapsStackLayouter.h */, - 18C4D69D17F0C82A0046FACD /* SCGoogleMapsStackLayouter.m */, - 18C4D69E17F0C82A0046FACD /* SCMerryGoRoundStackLayouter.h */, - 18C4D69F17F0C82A0046FACD /* SCMerryGoRoundStackLayouter.m */, - 18C4D6A017F0C82A0046FACD /* SCParallaxStackLayouter.h */, - 18C4D6A117F0C82A0046FACD /* SCParallaxStackLayouter.m */, - 18C4D6A217F0C82A0046FACD /* SCSlidingStackLayouter.h */, - 18C4D6A317F0C82A0046FACD /* SCSlidingStackLayouter.m */, + 18C4D6A617F0C82A0046FACD /* SCStackLayouterProtocol.h */, 18C4D6A417F0C82A0046FACD /* SCStackLayouter.h */, 18C4D6A517F0C82A0046FACD /* SCStackLayouter.m */, - 18C4D6A617F0C82A0046FACD /* SCStackLayouterProtocol.h */, + 18C4D6A217F0C82A0046FACD /* SCSlidingStackLayouter.h */, + 18C4D6A317F0C82A0046FACD /* SCSlidingStackLayouter.m */, + 18C4D6A017F0C82A0046FACD /* SCParallaxStackLayouter.h */, + 18C4D6A117F0C82A0046FACD /* SCParallaxStackLayouter.m */, + 18439FB318912EB10094674E /* SCResizingStackLayouter.h */, + 18439FB418912EB10094674E /* SCResizingStackLayouter.m */, + 189627921AFE8212003D96F1 /* SCReversedStackLayouter.h */, + 189627931AFE8212003D96F1 /* SCReversedStackLayouter.m */, ); path = Layouters; sourceTree = ""; }; - 18C4D69917F0C82A0046FACD /* Reversed */ = { - isa = PBXGroup; - children = ( - 18C4D69A17F0C82A0046FACD /* SCReversedStackLayouter.h */, - 18C4D69B17F0C82A0046FACD /* SCReversedStackLayouter.m */, - ); - path = Reversed; - sourceTree = ""; - }; 18FEAAD917B3ED2F00788EF0 = { isa = PBXGroup; children = ( 18FEAAEB17B3ED2F00788EF0 /* Demo */, 18FEAAE417B3ED2F00788EF0 /* Frameworks */, 18FEAAE317B3ED2F00788EF0 /* Products */, + 329D4B3A1FB8B2A2FB5B5FD5 /* Pods */, ); sourceTree = ""; }; @@ -165,6 +285,8 @@ 18FEAAE517B3ED2F00788EF0 /* UIKit.framework */, 18FEAAE717B3ED2F00788EF0 /* Foundation.framework */, 18FEAAE917B3ED2F00788EF0 /* CoreGraphics.framework */, + EBCFE08D7F870C09CCE0F816 /* libPods.a */, + 3860B666A25773930F16D279 /* libPods-SCStackViewController.a */, ); name = Frameworks; sourceTree = ""; @@ -181,10 +303,16 @@ 18364B4517BFB6450066F67A /* SCMainViewController.h */, 18364B4617BFB6450066F67A /* SCMainViewController.m */, 18364B4717BFB6450066F67A /* SCMainViewController.xib */, - 1823A8D517BEAC7500D58582 /* SCMenuViewController.h */, - 1823A8D617BEAC7500D58582 /* SCMenuViewController.m */, - 1823A8D717BEAC7500D58582 /* SCMenuViewController.xib */, + 18B601761840F0F500E3A0DD /* SCOverlayView.h */, + 18B601771840F0F500E3A0DD /* SCOverlayView.m */, + 18B601791840F10700E3A0DD /* SCOverlayView.xib */, + 1870D3C21AFEA7C40041C6D8 /* SCStackedViewControllerProtocol.h */, + 18B382241B01D6AF008E7C4B /* ImagesDemo */, + 18B3822A1B01D6AF008E7C4B /* MenusDemo */, + 18B382301B01D6AF008E7C4B /* ModalDemo */, + 18B382341B01D6AF008E7C4B /* TitleBarDemo */, 1823A8CE17BEAC7500D58582 /* Helpers */, + 1896276A1AFE70CB003D96F1 /* Resources */, 18FEAAEC17B3ED2F00788EF0 /* Supporting Files */, ); name = Demo; @@ -196,11 +324,21 @@ children = ( 18FEAAED17B3ED2F00788EF0 /* SCStackViewController-Info.plist */, 18FEAAF117B3ED2F00788EF0 /* main.m */, - 18FEAAF317B3ED2F00788EF0 /* SCStackViewController-Prefix.pch */, ); name = "Supporting Files"; sourceTree = ""; }; + 329D4B3A1FB8B2A2FB5B5FD5 /* Pods */ = { + isa = PBXGroup; + children = ( + EBD53A29BD55EE85F062AF39 /* Pods.debug.xcconfig */, + 642CD41E6249C9730BE87603 /* Pods.release.xcconfig */, + F5F7A1638BCE9DE9FE3728FD /* Pods-SCStackViewController.debug.xcconfig */, + B350B7897D79F062B8C2C2FC /* Pods-SCStackViewController.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -208,9 +346,12 @@ isa = PBXNativeTarget; buildConfigurationList = 18FEAB0517B3ED2F00788EF0 /* Build configuration list for PBXNativeTarget "SCStackViewController" */; buildPhases = ( + 21D9A396F93B88E1EB241738 /* [CP] Check Pods Manifest.lock */, 18FEAADE17B3ED2F00788EF0 /* Sources */, 18FEAADF17B3ED2F00788EF0 /* Frameworks */, 18FEAAE017B3ED2F00788EF0 /* Resources */, + 1A11E5B23902903FCF335764 /* [CP] Embed Pods Frameworks */, + 902C904DA49472C377C079CF /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -228,7 +369,7 @@ isa = PBXProject; attributes = { CLASSPREFIX = SC; - LastUpgradeCheck = 0460; + LastUpgradeCheck = 0720; ORGANIZATIONNAME = "Stefan Ceriu"; }; buildConfigurationList = 18FEAADD17B3ED2F00788EF0 /* Build configuration list for PBXProject "SCStackViewController" */; @@ -253,7 +394,19 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1823A8E517BEAC7500D58582 /* SCMenuViewController.xib in Resources */, + 18B382411B01D6AF008E7C4B /* SCTitleBarViewController.xib in Resources */, + 18B6017A1840F10700E3A0DD /* SCOverlayView.xib in Resources */, + 1870D3C61AFEC0EE0041C6D8 /* feathers.jpg in Resources */, + 186814A71B36A82B00697D20 /* panorama.jpg in Resources */, + 18ED277A1B02481800659E87 /* SCTitleBarCollectionViewCell.xib in Resources */, + 186F755D1B00A0FB0073714C /* left-menu-tab@2x.png in Resources */, + 186F755C1B00A0FB0073714C /* left-menu-tab.png in Resources */, + 18B3823D1B01D6AF008E7C4B /* SCMenuViewController.xib in Resources */, + 189627961AFE8423003D96F1 /* london.jpg in Resources */, + 18B3823A1B01D6AF008E7C4B /* SCImageViewController.xib in Resources */, + 186F755E1B00A0FB0073714C /* right-menu-tab.png in Resources */, + 186F755F1B00A0FB0073714C /* right-menu-tab@2x.png in Resources */, + 18B3823F1B01D6AF008E7C4B /* SCModalViewController.xib in Resources */, 1823A8E917BEAC7500D58582 /* SCRootViewController.xib in Resources */, 18364B4917BFB6450066F67A /* SCMainViewController.xib in Resources */, ); @@ -261,26 +414,85 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 1A11E5B23902903FCF335764 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SCStackViewController/Pods-SCStackViewController-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 21D9A396F93B88E1EB241738 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-SCStackViewController-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 902C904DA49472C377C079CF /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SCStackViewController/Pods-SCStackViewController-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 18FEAADE17B3ED2F00788EF0 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 18C4D6B217F0C82A0046FACD /* UIViewController+SCStackViewController.m in Sources */, + 18B3823C1B01D6AF008E7C4B /* SCMenuViewController.m in Sources */, 18FEAAF217B3ED2F00788EF0 /* main.m in Sources */, - 18C4D6AC17F0C82A0046FACD /* SCGoogleMapsStackLayouter.m in Sources */, 18C4D6AE17F0C82A0046FACD /* SCParallaxStackLayouter.m in Sources */, 18C4D6B017F0C82A0046FACD /* SCStackLayouter.m in Sources */, - 18C4D6AD17F0C82A0046FACD /* SCMerryGoRoundStackLayouter.m in Sources */, + 1830BFDE1886C060004ED34D /* SCStackNavigationStep.m in Sources */, 1823A8E117BEAC7500D58582 /* UIColor+RandomColors.m in Sources */, - 1823A8E217BEAC7500D58582 /* UIViewController+Shadows.m in Sources */, + 189627941AFE8212003D96F1 /* SCReversedStackLayouter.m in Sources */, + 18B382391B01D6AF008E7C4B /* SCImageViewController.m in Sources */, + 18B3823E1B01D6AF008E7C4B /* SCModalViewController.m in Sources */, + 1823A8E217BEAC7500D58582 /* UIView+Shadows.m in Sources */, + 18B382471B01D929008E7C4B /* SCModalLayouter.m in Sources */, 18C4D6B117F0C82A0046FACD /* SCStackViewController.m in Sources */, 1823A8E317BEAC7500D58582 /* SCAppDelegate.m in Sources */, - 1823A8E417BEAC7500D58582 /* SCMenuViewController.m in Sources */, - 18C4D6AB17F0C82A0046FACD /* SCReversedStackLayouter.m in Sources */, + 18ED27791B02481800659E87 /* SCTitleBarCollectionViewCell.m in Sources */, + 18439FB518912EB10094674E /* SCResizingStackLayouter.m in Sources */, 1823A8E817BEAC7500D58582 /* SCRootViewController.m in Sources */, 18C4D6AF17F0C82A0046FACD /* SCSlidingStackLayouter.m in Sources */, + 18B382401B01D6AF008E7C4B /* SCTitleBarViewController.m in Sources */, + 18B3823B1B01D6AF008E7C4B /* SCMenusLayouter.m in Sources */, + 18B601781840F0F500E3A0DD /* SCOverlayView.m in Sources */, 18364B4817BFB6450066F67A /* SCMainViewController.m in Sources */, + 18B382381B01D6AF008E7C4B /* SCImagesLayouter.m in Sources */, + 183913261A6ADD7700BD9B59 /* SCStackViewControllerView.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -295,12 +507,14 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -327,6 +541,7 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES; @@ -347,11 +562,39 @@ }; 18FEAB0617B3ED2F00788EF0 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F5F7A1638BCE9DE9FE3728FD /* Pods-SCStackViewController.debug.xcconfig */; buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "SCStackViewController/SCStackViewController-Prefix.pch"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES_ERROR; + CLANG_WARN_BOOL_CONVERSION = YES_ERROR; + CLANG_WARN_COMMA = YES; + CLANG_WARN_FLOAT_CONVERSION = NO; + CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES_ERROR; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; + CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = NO; INFOPLIST_FILE = "SCStackViewController/SCStackViewController-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 5.0; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.stefanceriu.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = 2; WRAPPER_EXTENSION = app; @@ -360,11 +603,39 @@ }; 18FEAB0717B3ED2F00788EF0 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = B350B7897D79F062B8C2C2FC /* Pods-SCStackViewController.release.xcconfig */; buildSettings = { - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "SCStackViewController/SCStackViewController-Prefix.pch"; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_ASSIGN_ENUM = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES_ERROR; + CLANG_WARN_BOOL_CONVERSION = YES_ERROR; + CLANG_WARN_COMMA = YES; + CLANG_WARN_FLOAT_CONVERSION = NO; + CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES_ERROR; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO; + CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + GCC_PRECOMPILE_PREFIX_HEADER = NO; + GCC_PREFIX_HEADER = ""; + GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; + GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; + GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO; + GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_PARAMETER = NO; INFOPLIST_FILE = "SCStackViewController/SCStackViewController-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 5.0; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + PRODUCT_BUNDLE_IDENTIFIER = "com.stefanceriu.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TARGETED_DEVICE_FAMILY = 2; WRAPPER_EXTENSION = app; diff --git a/Demo/SCStackViewController/Helpers/UIColor+RandomColors.h b/Demo/SCStackViewController/Helpers/UIColor+RandomColors.h index 0edd4263..4710f5d5 100644 --- a/Demo/SCStackViewController/Helpers/UIColor+RandomColors.h +++ b/Demo/SCStackViewController/Helpers/UIColor+RandomColors.h @@ -6,8 +6,10 @@ // Copyright (c) 2013 Stefan Ceriu. All rights reserved. // +@import UIKit; + @interface UIColor (RandomColors) -+ (UIColor *)randomColor; ++ (UIColor *)randomColorWithAlpha:(CGFloat)alpha; @end diff --git a/Demo/SCStackViewController/Helpers/UIColor+RandomColors.m b/Demo/SCStackViewController/Helpers/UIColor+RandomColors.m index 4bc6b7f5..768e33ae 100644 --- a/Demo/SCStackViewController/Helpers/UIColor+RandomColors.m +++ b/Demo/SCStackViewController/Helpers/UIColor+RandomColors.m @@ -7,15 +7,25 @@ // #import "UIColor+RandomColors.h" +#import "Chameleon.h" @implementation UIColor (RandomColors) -+ (UIColor *)randomColor ++ (UIColor *)randomColorWithAlpha:(CGFloat)alpha { - CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0 - CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white - CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black - return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1]; + static NSMutableArray *colorArray; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + UIColor *baseColor = [UIColor blueColor]; + + colorArray = [NSMutableArray array]; + [colorArray addObjectsFromArray:[NSArray arrayOfColorsWithColorScheme:ColorSchemeTriadic usingColor:baseColor withFlatScheme:YES]]; + [colorArray addObjectsFromArray:[NSArray arrayOfColorsWithColorScheme:ColorSchemeComplementary usingColor:baseColor withFlatScheme:YES]]; + [colorArray addObjectsFromArray:[NSArray arrayOfColorsWithColorScheme:ColorSchemeAnalogous usingColor:baseColor withFlatScheme:YES]]; + }); + + return colorArray[(arc4random()%colorArray.count)]; } @end diff --git a/Demo/SCStackViewController/Helpers/UIViewController+Shadows.h b/Demo/SCStackViewController/Helpers/UIView+Shadows.h similarity index 85% rename from Demo/SCStackViewController/Helpers/UIViewController+Shadows.h rename to Demo/SCStackViewController/Helpers/UIView+Shadows.h index e6660ed1..41104ac9 100644 --- a/Demo/SCStackViewController/Helpers/UIViewController+Shadows.h +++ b/Demo/SCStackViewController/Helpers/UIView+Shadows.h @@ -1,19 +1,21 @@ // -// UIViewController+Shadows.h +// UIView+Shadows.h // SCStackViewController // // Created by Stefan Ceriu on 16/08/2013. // Copyright (c) 2013 Stefan Ceriu. All rights reserved. // -typedef enum { +@import UIKit; + +typedef NS_ENUM(NSUInteger, SCShadowEdge) { SCShadowEdgeNone = 0, SCShadowEdgeTop = 1 << 0, SCShadowEdgeLeft = 1 << 1, SCShadowEdgeBottom = 1 << 2, SCShadowEdgeRight = 1 << 3, SCShadowEdgeAll = SCShadowEdgeTop | SCShadowEdgeLeft | SCShadowEdgeBottom | SCShadowEdgeRight -} SCShadowEdge; +}; @interface UIView (Shadows) diff --git a/Demo/SCStackViewController/Helpers/UIViewController+Shadows.m b/Demo/SCStackViewController/Helpers/UIView+Shadows.m similarity index 86% rename from Demo/SCStackViewController/Helpers/UIViewController+Shadows.m rename to Demo/SCStackViewController/Helpers/UIView+Shadows.m index e508d08d..35f7207e 100644 --- a/Demo/SCStackViewController/Helpers/UIViewController+Shadows.m +++ b/Demo/SCStackViewController/Helpers/UIView+Shadows.m @@ -1,12 +1,12 @@ // -// UIViewController+Shadows.m +// UIView+Shadows.m // SCStackViewController // // Created by Stefan Ceriu on 16/08/2013. // Copyright (c) 2013 Stefan Ceriu. All rights reserved. // -#import "UIViewController+Shadows.h" +#import "UIView+Shadows.h" #import static const CGFloat shadowSize = 10; @@ -18,12 +18,10 @@ - (void)castShadowWithPosition:(SCShadowEdge)position; CGRect shadowRect = self.bounds; if(position & SCShadowEdgeTop) { - shadowRect.origin.y -= shadowSize; shadowRect.size.height += shadowSize; } if(position & SCShadowEdgeLeft) { - shadowRect.origin.x -= shadowSize; shadowRect.size.width += shadowSize; } diff --git a/Demo/SCStackViewController/ImagesDemo/SCImageViewController.h b/Demo/SCStackViewController/ImagesDemo/SCImageViewController.h new file mode 100644 index 00000000..eb5f66a5 --- /dev/null +++ b/Demo/SCStackViewController/ImagesDemo/SCImageViewController.h @@ -0,0 +1,17 @@ +// +// SCImageViewController.h +// SCStackViewController +// +// Created by Stefan Ceriu on 08/08/2013. +// Copyright (c) 2013 Stefan Ceriu. All rights reserved. +// + +#import "SCStackViewController.h" +#import "SCStackedViewControllerProtocol.h" + +@interface SCImageViewController : UIViewController + +- (instancetype)initWithPosition:(SCStackViewControllerPosition)position; + +@end + diff --git a/Demo/SCStackViewController/ImagesDemo/SCImageViewController.m b/Demo/SCStackViewController/ImagesDemo/SCImageViewController.m new file mode 100644 index 00000000..2a0b0d46 --- /dev/null +++ b/Demo/SCStackViewController/ImagesDemo/SCImageViewController.m @@ -0,0 +1,88 @@ +// +// SCImageViewController.m +// SCStackViewController +// +// Created by Stefan Ceriu on 08/08/2013. +// Copyright (c) 2013 Stefan Ceriu. All rights reserved. +// + +#import "SCImageViewController.h" +#import "UIView+Shadows.h" + +@interface SCImageViewController () + +@property (nonatomic, assign) SCStackViewControllerPosition position; + +@property (nonatomic, weak) IBOutlet UIImageView *backgroundImageView; +@property (nonatomic, weak) IBOutlet UIView *controlsContainer; +@property (nonatomic, weak) IBOutlet UILabel *visiblePercentageLabel; + +@end + +@implementation SCImageViewController +@synthesize delegate; + +- (instancetype)initWithPosition:(SCStackViewControllerPosition)position +{ + if(self = [super init]) { + self.position = position; + } + + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + [self updateShadow]; + + [self.backgroundImageView setImage:[UIImage imageNamed:@"panorama.jpg"]]; +} + +- (void)viewWillLayoutSubviews +{ + [super viewWillLayoutSubviews]; + [self updateShadow]; +} + +- (void)setVisiblePercentage:(CGFloat)percentage +{ + [self.visiblePercentageLabel setText:[NSString stringWithFormat:@"%.3f%%", percentage]]; +} + +- (IBAction)onPushButtonTap:(id)sender +{ + if([self.delegate respondsToSelector:@selector(stackedViewControllerDidRequestPush:)]) { + [self.delegate stackedViewControllerDidRequestPush:self]; + } +} + +- (IBAction)onPopButtonTap:(id)sender +{ + if([self.delegate respondsToSelector:@selector(stackedViewControllerDidRequestPop:)]) { + [self.delegate stackedViewControllerDidRequestPop:self]; + } +} + +- (IBAction)onScrollToMeButtonTapped:(id)sender +{ + [self.sc_stackViewController navigateToViewController:self animated:YES completion:nil]; +} + +- (void)updateShadow +{ + static NSDictionary *positionToShadowEdge; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + positionToShadowEdge = (@{ + @(SCStackViewControllerPositionTop) : @(SCShadowEdgeTop), + @(SCStackViewControllerPositionLeft) : @(SCShadowEdgeLeft), + @(SCStackViewControllerPositionBottom) : @(SCShadowEdgeBottom), + @(SCStackViewControllerPositionRight) : @(SCShadowEdgeRight) + }); + }); + + [self.backgroundImageView castShadowWithPosition:(SCShadowEdge)[positionToShadowEdge[@(self.position)] intValue]]; +} + +@end diff --git a/Demo/SCStackViewController/ImagesDemo/SCImageViewController.xib b/Demo/SCStackViewController/ImagesDemo/SCImageViewController.xib new file mode 100644 index 00000000..3775b0f8 --- /dev/null +++ b/Demo/SCStackViewController/ImagesDemo/SCImageViewController.xib @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/SCStackViewController/ImagesDemo/SCImagesLayouter.h b/Demo/SCStackViewController/ImagesDemo/SCImagesLayouter.h new file mode 100644 index 00000000..ffa56679 --- /dev/null +++ b/Demo/SCStackViewController/ImagesDemo/SCImagesLayouter.h @@ -0,0 +1,13 @@ +// +// SCImagesLayouter.h +// SCStackViewController +// +// Created by Stefan Ceriu on 5/10/15. +// Copyright (c) 2015 Stefan Ceriu. All rights reserved. +// + +#import "SCStackLayouter.h" + +@interface SCImagesLayouter : SCStackLayouter + +@end diff --git a/Demo/SCStackViewController/ImagesDemo/SCImagesLayouter.m b/Demo/SCStackViewController/ImagesDemo/SCImagesLayouter.m new file mode 100644 index 00000000..b0755a38 --- /dev/null +++ b/Demo/SCStackViewController/ImagesDemo/SCImagesLayouter.m @@ -0,0 +1,48 @@ +// +// SCImagesLayouter.m +// SCStackViewController +// +// Created by Stefan Ceriu on 5/10/15. +// Copyright (c) 2015 Stefan Ceriu. All rights reserved. +// + +#import "SCImagesLayouter.h" + +@implementation SCImagesLayouter + +- (CATransform3D)sublayerTransformForViewController:(UIViewController *)viewController + withIndex:(NSUInteger)index + atPosition:(SCStackViewControllerPosition)position + finalFrame:(CGRect)finalFrame + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackViewController +{ + CATransform3D transform = CATransform3DIdentity; + transform.m34 = 1.0 / -500; + + CGFloat visiblePercentage = [stackViewController visiblePercentageForViewController:viewController]; + CGFloat angle = (90.0f - visiblePercentage * 90.0f) * M_PI / 180.0f; + + switch (position) { + case SCStackViewControllerPositionTop: { + transform = CATransform3DRotate(transform, angle, 1.0f, 0.0f, 0.0f); + break; + } + case SCStackViewControllerPositionLeft: { + transform = CATransform3DRotate(transform, angle, 0.0f, 1.0f, 0.0f); + break; + } + case SCStackViewControllerPositionBottom: { + transform = CATransform3DRotate(transform, angle, 1.0f, 0.0f, 0.0f); + break; + } + case SCStackViewControllerPositionRight: { + transform = CATransform3DRotate(transform, angle, 0.0f, 1.0f, 0.0f); + break; + } + } + + return transform; +} + +@end diff --git a/Demo/SCStackViewController/MenusDemo/SCMenuViewController.h b/Demo/SCStackViewController/MenusDemo/SCMenuViewController.h new file mode 100644 index 00000000..37dba3de --- /dev/null +++ b/Demo/SCStackViewController/MenusDemo/SCMenuViewController.h @@ -0,0 +1,16 @@ +// +// SCMenuViewController.h +// SCStackViewController +// +// Created by Stefan Ceriu on 08/08/2013. +// Copyright (c) 2013 Stefan Ceriu. All rights reserved. +// + +#import "SCStackViewController.h" +#import "SCStackedViewControllerProtocol.h" + +@interface SCMenuViewController : UIViewController + +- (instancetype)initWithPosition:(SCStackViewControllerPosition)position; + +@end diff --git a/Demo/SCStackViewController/MenusDemo/SCMenuViewController.m b/Demo/SCStackViewController/MenusDemo/SCMenuViewController.m new file mode 100644 index 00000000..026b9737 --- /dev/null +++ b/Demo/SCStackViewController/MenusDemo/SCMenuViewController.m @@ -0,0 +1,108 @@ +// +// SCMenuViewController.m +// SCStackViewController +// +// Created by Stefan Ceriu on 08/08/2013. +// Copyright (c) 2013 Stefan Ceriu. All rights reserved. +// + +#import "SCMenuViewController.h" +#import "UIView+Shadows.h" +#import "UIColor+RandomColors.h" + +@interface SCMenuViewController () + +@property (nonatomic, assign) SCStackViewControllerPosition position; + +@property (nonatomic, weak) IBOutlet UIView *contentView; + +@property (nonatomic, weak) IBOutlet UIImageView *backgroundImageView; +@property (nonatomic, weak) IBOutlet UIView *controlsContainer; +@property (nonatomic, weak) IBOutlet UILabel *visiblePercentageLabel; + +@end + +@implementation SCMenuViewController +@synthesize delegate; + +- (instancetype)initWithPosition:(SCStackViewControllerPosition)position +{ + if(self = [super init]) { + self.position = position; + } + + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + [self updateShadow]; + + [self.contentView setBackgroundColor:[UIColor randomColorWithAlpha:1.0f]]; + + switch (self.position) { + case SCStackViewControllerPositionTop: + self.visiblePercentageLabel.center = CGPointMake(self.view.center.x, CGRectGetHeight(self.view.bounds) - CGRectGetHeight(self.visiblePercentageLabel.bounds)); + self.controlsContainer.center = CGPointMake(self.view.center.x, CGRectGetHeight(self.controlsContainer.bounds)); + break; + case SCStackViewControllerPositionBottom: + self.visiblePercentageLabel.center = CGPointMake(self.view.center.x, CGRectGetHeight(self.visiblePercentageLabel.bounds)); + self.controlsContainer.center = CGPointMake(self.view.center.x, CGRectGetHeight(self.view.bounds) - CGRectGetHeight(self.controlsContainer.bounds)); + break; + case SCStackViewControllerPositionLeft: + self.visiblePercentageLabel.transform = CGAffineTransformMakeRotation(-M_PI/2); + self.visiblePercentageLabel.center = CGPointMake(CGRectGetWidth(self.visiblePercentageLabel.bounds), 100.0f); + break; + case SCStackViewControllerPositionRight: + self.visiblePercentageLabel.transform = CGAffineTransformMakeRotation(M_PI/2); + self.visiblePercentageLabel.center = CGPointMake(CGRectGetWidth(self.view.bounds) - CGRectGetWidth(self.visiblePercentageLabel.bounds), 100.0f); + break; + } +} + +- (void)viewWillLayoutSubviews +{ + [super viewWillLayoutSubviews]; + [self updateShadow]; +} + +- (void)setVisiblePercentage:(CGFloat)percentage +{ + [self.visiblePercentageLabel setText:[NSString stringWithFormat:@"%.3f%%", percentage]]; +} + +- (IBAction)onPushButtonTap:(id)sender +{ + if([self.delegate respondsToSelector:@selector(stackedViewControllerDidRequestPush:)]) { + [self.delegate stackedViewControllerDidRequestPush:self]; + } +} + +- (IBAction)onPopButtonTap:(id)sender +{ + if([self.delegate respondsToSelector:@selector(stackedViewControllerDidRequestPop:)]) { + [self.delegate stackedViewControllerDidRequestPop:self]; + } +} + +- (IBAction)onScrollToMeButtonTapped:(id)sender +{ + [self.sc_stackViewController navigateToViewController:self animated:YES completion:nil]; +} + +- (void)updateShadow +{ + static NSDictionary *positionToShadowEdge; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + positionToShadowEdge = (@{@(SCStackViewControllerPositionTop) : @(SCShadowEdgeTop), + @(SCStackViewControllerPositionLeft) : @(SCShadowEdgeLeft), + @(SCStackViewControllerPositionBottom) : @(SCShadowEdgeBottom), + @(SCStackViewControllerPositionRight) : @(SCShadowEdgeRight)}); + }); + + [self.contentView castShadowWithPosition:(SCShadowEdge)[positionToShadowEdge[@(self.position)] intValue]]; +} + +@end diff --git a/Demo/SCStackViewController/MenusDemo/SCMenuViewController.xib b/Demo/SCStackViewController/MenusDemo/SCMenuViewController.xib new file mode 100644 index 00000000..3b036d50 --- /dev/null +++ b/Demo/SCStackViewController/MenusDemo/SCMenuViewController.xib @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/SCStackViewController/MenusDemo/SCMenusLayouter.h b/Demo/SCStackViewController/MenusDemo/SCMenusLayouter.h new file mode 100644 index 00000000..747a1ee4 --- /dev/null +++ b/Demo/SCStackViewController/MenusDemo/SCMenusLayouter.h @@ -0,0 +1,13 @@ +// +// SCMenusLayouter.h +// SCStackViewController +// +// Created by Stefan Ceriu on 5/10/15. +// Copyright (c) 2015 Stefan Ceriu. All rights reserved. +// + +#import "SCParallaxStackLayouter.h" + +@interface SCMenusLayouter : SCParallaxStackLayouter + +@end diff --git a/Demo/SCStackViewController/MenusDemo/SCMenusLayouter.m b/Demo/SCStackViewController/MenusDemo/SCMenusLayouter.m new file mode 100644 index 00000000..a39657cc --- /dev/null +++ b/Demo/SCStackViewController/MenusDemo/SCMenusLayouter.m @@ -0,0 +1,32 @@ +// +// SCMenusLayouter.m +// SCStackViewController +// +// Created by Stefan Ceriu on 5/10/15. +// Copyright (c) 2015 Stefan Ceriu. All rights reserved. +// + +#import "SCMenusLayouter.h" + +@implementation SCMenusLayouter + +- (CATransform3D)sublayerTransformForRootViewController:(UIViewController *)rootViewController + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackViewController +{ + CGFloat visiblePercentage = [stackViewController visiblePercentageForViewController:rootViewController]; + CATransform3D transform = CATransform3DIdentity; + + transform = CATransform3DScale(transform, visiblePercentage, visiblePercentage, 1.0f); + + CGFloat translation = (1.0f - visiblePercentage) * 1000; + if(contentOffset.x > 0) { + translation = -translation; + } + + transform = CATransform3DTranslate(transform, translation, 0.0f, 0.0f); + + return transform; +} + +@end diff --git a/Demo/SCStackViewController/ModalDemo/SCModalLayouter.h b/Demo/SCStackViewController/ModalDemo/SCModalLayouter.h new file mode 100644 index 00000000..d6f6b008 --- /dev/null +++ b/Demo/SCStackViewController/ModalDemo/SCModalLayouter.h @@ -0,0 +1,13 @@ +// +// SCModalLayouter.h +// SCStackViewController +// +// Created by Stefan Ceriu on 5/10/15. +// Copyright (c) 2015 Stefan Ceriu. All rights reserved. +// + +#import "SCParallaxStackLayouter.h" + +@interface SCModalLayouter : SCParallaxStackLayouter + +@end diff --git a/Demo/SCStackViewController/ModalDemo/SCModalLayouter.m b/Demo/SCStackViewController/ModalDemo/SCModalLayouter.m new file mode 100644 index 00000000..ccd15f7c --- /dev/null +++ b/Demo/SCStackViewController/ModalDemo/SCModalLayouter.m @@ -0,0 +1,33 @@ +// +// SCModalLayouter.m +// SCStackViewController +// +// Created by Stefan Ceriu on 5/10/15. +// Copyright (c) 2015 Stefan Ceriu. All rights reserved. +// + +#import "SCModalLayouter.h" + +@implementation SCModalLayouter + +- (CATransform3D)sublayerTransformForViewController:(UIViewController *)viewController + withIndex:(NSUInteger)index + atPosition:(SCStackViewControllerPosition)position + finalFrame:(CGRect)finalFrame + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackViewController +{ + CGFloat visiblePercentage = [stackViewController visiblePercentageForViewController:viewController]; + + CATransform3D transform = CATransform3DIdentity; + + CGFloat translation = (1.0f - visiblePercentage) * 400; + transform = CATransform3DTranslate(transform, translation, translation, 0.0f); + + CGFloat angle = (90.0f - visiblePercentage * 90.0f) * M_PI / 180.0f; + transform = CATransform3DRotate(transform, angle, 0.0f, 0.0f, 1.0f); + + return transform; +} + +@end diff --git a/Demo/SCStackViewController/ModalDemo/SCModalViewController.h b/Demo/SCStackViewController/ModalDemo/SCModalViewController.h new file mode 100644 index 00000000..62e89dfd --- /dev/null +++ b/Demo/SCStackViewController/ModalDemo/SCModalViewController.h @@ -0,0 +1,16 @@ +// +// SCModalViewController.h +// SCStackViewController +// +// Created by Stefan Ceriu on 08/08/2013. +// Copyright (c) 2013 Stefan Ceriu. All rights reserved. +// + +#import "SCStackViewController.h" +#import "SCStackedViewControllerProtocol.h" + +@interface SCModalViewController : UIViewController + +- (instancetype)initWithPosition:(SCStackViewControllerPosition)position; + +@end diff --git a/Demo/SCStackViewController/ModalDemo/SCModalViewController.m b/Demo/SCStackViewController/ModalDemo/SCModalViewController.m new file mode 100644 index 00000000..115a1951 --- /dev/null +++ b/Demo/SCStackViewController/ModalDemo/SCModalViewController.m @@ -0,0 +1,77 @@ +// +// SCModalViewController.m +// SCStackViewController +// +// Created by Stefan Ceriu on 08/08/2013. +// Copyright (c) 2013 Stefan Ceriu. All rights reserved. +// + +#import "SCModalViewController.h" +#import "UIView+Shadows.h" +#import "UIColor+RandomColors.h" + +@interface SCModalViewController () + +@property (nonatomic, assign) SCStackViewControllerPosition position; + +@property (nonatomic, weak) IBOutlet UIView *contentView; + +@property (nonatomic, weak) IBOutlet UIImageView *backgroundImageView; +@property (nonatomic, weak) IBOutlet UIView *controlsContainer; +@property (nonatomic, weak) IBOutlet UILabel *visiblePercentageLabel; + +@end + +@implementation SCModalViewController +@synthesize delegate; + +- (instancetype)initWithPosition:(SCStackViewControllerPosition)position +{ + if(self = [super init]) { + self.position = position; + } + + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + [self updateShadow]; + + [self.contentView setBackgroundColor:[UIColor randomColorWithAlpha:1.0f]]; +} + +- (void)viewWillLayoutSubviews +{ + [super viewWillLayoutSubviews]; + [self updateShadow]; +} + +- (void)setVisiblePercentage:(CGFloat)percentage +{ + [self.visiblePercentageLabel setText:[NSString stringWithFormat:@"%.3f%%", percentage]]; +} + +- (IBAction)onPopButtonTap:(id)sender +{ + if([self.delegate respondsToSelector:@selector(stackedViewControllerDidRequestPop:)]) { + [self.delegate stackedViewControllerDidRequestPop:self]; + } +} + +- (void)updateShadow +{ + static NSDictionary *positionToShadowEdge; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + positionToShadowEdge = (@{@(SCStackViewControllerPositionTop) : @(SCShadowEdgeTop), + @(SCStackViewControllerPositionLeft) : @(SCShadowEdgeLeft), + @(SCStackViewControllerPositionBottom) : @(SCShadowEdgeBottom), + @(SCStackViewControllerPositionRight) : @(SCShadowEdgeRight)}); + }); + + [self.contentView castShadowWithPosition:(SCShadowEdge)[positionToShadowEdge[@(self.position)] intValue]]; +} + +@end diff --git a/Demo/SCStackViewController/ModalDemo/SCModalViewController.xib b/Demo/SCStackViewController/ModalDemo/SCModalViewController.xib new file mode 100644 index 00000000..057f6d03 --- /dev/null +++ b/Demo/SCStackViewController/ModalDemo/SCModalViewController.xib @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/SCStackViewController/Resources/Images/feathers.jpg b/Demo/SCStackViewController/Resources/Images/feathers.jpg new file mode 100644 index 00000000..b55fa594 Binary files /dev/null and b/Demo/SCStackViewController/Resources/Images/feathers.jpg differ diff --git a/Demo/SCStackViewController/Resources/Images/left-menu-tab.png b/Demo/SCStackViewController/Resources/Images/left-menu-tab.png new file mode 100644 index 00000000..ade369bf Binary files /dev/null and b/Demo/SCStackViewController/Resources/Images/left-menu-tab.png differ diff --git a/Demo/SCStackViewController/Resources/Images/left-menu-tab@2x.png b/Demo/SCStackViewController/Resources/Images/left-menu-tab@2x.png new file mode 100644 index 00000000..de7ab390 Binary files /dev/null and b/Demo/SCStackViewController/Resources/Images/left-menu-tab@2x.png differ diff --git a/Demo/SCStackViewController/Resources/Images/london.jpg b/Demo/SCStackViewController/Resources/Images/london.jpg new file mode 100644 index 00000000..d7903252 Binary files /dev/null and b/Demo/SCStackViewController/Resources/Images/london.jpg differ diff --git a/Demo/SCStackViewController/Resources/Images/panorama.jpg b/Demo/SCStackViewController/Resources/Images/panorama.jpg new file mode 100644 index 00000000..3e13d494 Binary files /dev/null and b/Demo/SCStackViewController/Resources/Images/panorama.jpg differ diff --git a/Demo/SCStackViewController/Resources/Images/right-menu-tab.png b/Demo/SCStackViewController/Resources/Images/right-menu-tab.png new file mode 100644 index 00000000..34df18db Binary files /dev/null and b/Demo/SCStackViewController/Resources/Images/right-menu-tab.png differ diff --git a/Demo/SCStackViewController/Resources/Images/right-menu-tab@2x.png b/Demo/SCStackViewController/Resources/Images/right-menu-tab@2x.png new file mode 100644 index 00000000..ea87f55e Binary files /dev/null and b/Demo/SCStackViewController/Resources/Images/right-menu-tab@2x.png differ diff --git a/Demo/SCStackViewController/SCAppDelegate.h b/Demo/SCStackViewController/SCAppDelegate.h index b2dda810..c1cf463e 100644 --- a/Demo/SCStackViewController/SCAppDelegate.h +++ b/Demo/SCStackViewController/SCAppDelegate.h @@ -6,6 +6,8 @@ // Copyright (c) 2013 Stefan Ceriu. All rights reserved. // +@import UIKit; + @interface SCAppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; diff --git a/Demo/SCStackViewController/SCAppDelegate.m b/Demo/SCStackViewController/SCAppDelegate.m index 8d1335d6..23688653 100644 --- a/Demo/SCStackViewController/SCAppDelegate.m +++ b/Demo/SCStackViewController/SCAppDelegate.m @@ -13,10 +13,10 @@ @implementation SCAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - self.window.rootViewController = [[SCRootViewController alloc] initWithNibName:NSStringFromClass([SCRootViewController class]) bundle:nil]; - [self.window makeKeyAndVisible]; - return YES; + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.window.rootViewController = [[SCRootViewController alloc] initWithNibName:NSStringFromClass([SCRootViewController class]) bundle:nil]; + [self.window makeKeyAndVisible]; + return YES; } @end diff --git a/Demo/SCStackViewController/SCMainViewController.h b/Demo/SCStackViewController/SCMainViewController.h index 475fb888..6c2ce430 100644 --- a/Demo/SCStackViewController/SCMainViewController.h +++ b/Demo/SCStackViewController/SCMainViewController.h @@ -6,27 +6,50 @@ // Copyright (c) 2013 Stefan Ceriu. All rights reserved. // -typedef enum { - SCStackLayouterTypePlain, - SCStackLayouterTypeSliding, - SCStackLayouterTypeParallax, - SCStackLayouterTypeGoogleMaps, - SCStackLayouterTypeMerryGoRound, - SCStackLayouterTypeReversed, - SCStackLayouterTypeCount -} SCStackLayouterType; +#import "SCEasingFunction.h" + +typedef NS_ENUM(NSUInteger, SCStackDemoType) { + SCStackDemoTypeVerticalImages, + SCStackDemoTypeSideMenus, + SCStackDemoTypeTitleBar, + SCStackDemoTypeModal, + SCStackDemoTypeGeneric, +}; + +typedef NS_ENUM(NSUInteger, SCStackLayouterType) { + SCStackLayouterTypePlain, + SCStackLayouterTypeSliding, + SCStackLayouterTypeParallax, + SCStackLayouterTypeReversed, + SCStacklayouterTypePlainResizing, + SCStackLayouterTypeCount +}; @protocol SCMainViewControllerDelegate; @interface SCMainViewController : UIViewController -@property (nonatomic, weak) id delegate; +@property (nonatomic, weak) IBOutlet id delegate; + +- (void)setVisiblePercentage:(CGFloat)percentage; + +- (void)showAnimationOptionsAnimated:(BOOL)animated; +- (void)hideAnimationOptionsAnimated:(BOOL)animated; @end @protocol SCMainViewControllerDelegate - (void)mainViewController:(SCMainViewController *)mainViewController - didSelectLayouterType:(SCStackLayouterType)type; + didChangeDemoType:(SCStackDemoType)type; + +- (void)mainViewController:(SCMainViewController *)mainViewController + didChangeLayouterType:(SCStackLayouterType)type; + +- (void)mainViewController:(SCMainViewController *)mainViewController + didChangeAnimationType:(SCEasingFunctionType)type; + +- (void)mainViewController:(SCMainViewController *)mainViewController +didChangeAnimationDuration:(NSTimeInterval)duration; @end diff --git a/Demo/SCStackViewController/SCMainViewController.m b/Demo/SCStackViewController/SCMainViewController.m index 2c25574a..433e7151 100644 --- a/Demo/SCStackViewController/SCMainViewController.m +++ b/Demo/SCStackViewController/SCMainViewController.m @@ -6,56 +6,202 @@ // Copyright (c) 2013 Stefan Ceriu. All rights reserved. // +@import UIKit; + #import "SCMainViewController.h" -@interface SCMainViewController () +typedef NS_ENUM(NSUInteger, SCPickerViewComponentType) { + SCPickerViewComponentTypeLayouter, + SCPickerViewComponentTypeEasingFunction, + SCPickerViewComponentTypeAnimationDuration +}; + +@interface SCMainViewController () + +@property (nonatomic, assign) SCStackDemoType currentDemoType; + +@property (nonatomic, weak) IBOutlet UIPickerView *pickerView; +@property (nonatomic, weak) IBOutlet UISegmentedControl *segmentedControl; +@property (nonatomic, weak) IBOutlet UILabel *visiblePercentageLabel; @end @implementation SCMainViewController -#pragma mark - UITableViewDataSource +#pragma mark - Public + +- (void)setVisiblePercentage:(CGFloat)percentage +{ + [self.visiblePercentageLabel setText:[NSString stringWithFormat:@"%.3f%%", percentage]]; +} + +- (void)setCurrentDemoType:(SCStackDemoType)currentDemoType +{ + _currentDemoType = currentDemoType; +} + +- (void)showAnimationOptionsAnimated:(BOOL)animated +{ + [self setAnimationOptionsVisible:YES animated:animated]; +} + +- (void)hideAnimationOptionsAnimated:(BOOL)animated +{ + // [self setAnimationOptionsVisible:normal animated:animated]; +} + +- (void)setAnimationOptionsVisible:(BOOL)visible animated:(BOOL)animated +{ + [UIView animateWithDuration:(animated ? 0.25f : 0.0f) animations:^{ + [self.pickerView setAlpha:visible]; + + for(SCPickerViewComponentType i=0; i - - - 1552 - 12E55 - 3084 - 1187.39 - 626.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 2083 - - - IBProxyObject - IBUITableView - IBUIView - - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - PluginDependencyRecalculationVersion - - - - - IBFilesOwner - IBIPadFramework - - - IBFirstResponder - IBIPadFramework - - - - 274 - - - - 274 - {{362, 249}, {300, 270}} - - - _NS:9 - - 3 - MCAwAA - - YES - IBIPadFramework - NO - YES - 1 - 2 - 0 - YES - 44 - 10 - 10 - - - {1024, 768} - - - - - 3 - MC44MDIwMDk4Mjg2AA - - YES - NO - - 3 - 3 - - IBIPadFramework - - - - - - - view - - - - 3 - - - - dataSource - - - - 6 - - - - delegate - - - - 7 - - - - - - 0 - - - - - - -1 - - - File's Owner - - - -2 - - - - - 2 - - - - - - - - 5 - - - - - - - SCMainViewController - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - UIResponder - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - - - 7 - - - - - SCMainViewController - UIViewController - - IBProjectSource - ./Classes/SCMainViewController.h - - - - - 0 - IBIPadFramework - YES - 3 - 2083 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/SCStackViewController/SCMenuViewController.h b/Demo/SCStackViewController/SCMenuViewController.h deleted file mode 100644 index b2f20810..00000000 --- a/Demo/SCStackViewController/SCMenuViewController.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// SCMenuViewController.h -// SCStackViewController -// -// Created by Stefan Ceriu on 08/08/2013. -// Copyright (c) 2013 Stefan Ceriu. All rights reserved. -// - -#import "SCStackViewController.h" - -@protocol SCMenuViewControllerDelegate; - -@interface SCMenuViewController : UIViewController - -- (instancetype)initWithPosition:(SCStackViewControllerPosition)position; - -@property (nonatomic, weak) id delegate; - -@property (nonatomic, readonly) SCStackViewControllerPosition position; - -- (IBAction)onScrollToMeButtonTapped:(id)sender; -@end - -@protocol SCMenuViewControllerDelegate - -@optional -- (void)menuViewControllerDidRequestPush:(SCMenuViewController *)menuViewController; -- (void)menuViewControllerDidRequestPop:(SCMenuViewController *)menuViewController; - -@end \ No newline at end of file diff --git a/Demo/SCStackViewController/SCMenuViewController.m b/Demo/SCStackViewController/SCMenuViewController.m deleted file mode 100644 index 0f1ac7e0..00000000 --- a/Demo/SCStackViewController/SCMenuViewController.m +++ /dev/null @@ -1,56 +0,0 @@ -// -// SCMenuViewController.m -// SCStackViewController -// -// Created by Stefan Ceriu on 08/08/2013. -// Copyright (c) 2013 Stefan Ceriu. All rights reserved. -// - -#import "SCMenuViewController.h" -#import "UIColor+RandomColors.h" - -#import "UIViewController+SCStackViewController.h" - -@interface SCMenuViewController () - -@property (nonatomic, assign) SCStackViewControllerPosition position; - -@end - -@implementation SCMenuViewController - -- (instancetype)initWithPosition:(SCStackViewControllerPosition)position -{ - if(self = [super init]) { - self.position = position; - } - - return self; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - [self.view setBackgroundColor:[UIColor randomColor]]; -} - -- (IBAction)onPushButtonTap:(id)sender -{ - if([self.delegate respondsToSelector:@selector(menuViewControllerDidRequestPush:)]) { - [self.delegate menuViewControllerDidRequestPush:self]; - } -} - -- (IBAction)onPopButtonTap:(id)sender -{ - if([self.delegate respondsToSelector:@selector(menuViewControllerDidRequestPop:)]) { - [self.delegate menuViewControllerDidRequestPop:self]; - } -} - -- (IBAction)onScrollToMeButtonTapped:(id)sender -{ - [self.stackViewController navigateToViewController:self animated:YES completion:nil]; -} - -@end \ No newline at end of file diff --git a/Demo/SCStackViewController/SCMenuViewController.xib b/Demo/SCStackViewController/SCMenuViewController.xib deleted file mode 100644 index 0431382b..00000000 --- a/Demo/SCStackViewController/SCMenuViewController.xib +++ /dev/null @@ -1,274 +0,0 @@ - - - - 1552 - 12E55 - 3084 - 1187.39 - 626.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 2083 - - - IBProxyObject - IBUIButton - IBUIView - - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - PluginDependencyRecalculationVersion - - - - - IBFilesOwner - IBIPadFramework - - - IBFirstResponder - IBIPadFramework - - - - 292 - - - - 301 - {{154, 363}, {60, 44}} - - - - _NS:9 - NO - IBIPadFramework - 0 - 0 - 1 - Push - - 3 - MQA - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - 3 - MC41AA - - - 2 - 15 - - - Helvetica-Bold - 15 - 16 - - - - - 301 - {{86, 363}, {60, 44}} - - - - _NS:9 - NO - IBIPadFramework - 0 - 0 - 1 - Pop - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - - - - - - 292 - {{86, 457}, {128, 44}} - - - _NS:9 - NO - IBIPadFramework - 0 - 0 - 1 - Scroll to me - - - 1 - MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA - - - - - - - {300, 768} - - - - - 3 - MC42NjY2NjY2NjY3AA - - YES - NO - - 3 - 3 - - IBIPadFramework - - - - - - - view - - - - 3 - - - - onPushButtonTap: - - - 7 - - 18 - - - - onPopButtonTap: - - - 7 - - 21 - - - - onScrollToMeButtonTapped: - - - 7 - - 25 - - - - - - 0 - - - - - - -1 - - - File's Owner - - - -2 - - - - - 2 - - - - - - - - - - 17 - - - - - 19 - - - - - 24 - - - - - - - SCMenuViewController - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - UIResponder - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - - - 25 - - - - - SCMenuViewController - UIViewController - - id - id - id - - - - onPopButtonTap: - id - - - onPushButtonTap: - id - - - onScrollToMeButtonTapped: - id - - - - IBProjectSource - ./Classes/SCMenuViewController.h - - - - - 0 - IBIPadFramework - YES - 3 - 2083 - - diff --git a/Demo/SCStackViewController/SCOverlayView.h b/Demo/SCStackViewController/SCOverlayView.h new file mode 100644 index 00000000..1f26231c --- /dev/null +++ b/Demo/SCStackViewController/SCOverlayView.h @@ -0,0 +1,26 @@ +// +// SCOverlayView.h +// SCStackViewController +// +// Created by Stefan Ceriu on 23/11/2013. +// Copyright (c) 2013 Stefan Ceriu. All rights reserved. +// + +@import UIKit; + +@protocol SCOverlayViewDelegate; + +@interface SCOverlayView : UIView + +@property (nonatomic, weak) id delegate; + ++ (instancetype)overlayView; + +@end + +@protocol SCOverlayViewDelegate + +@optional +- (void)overlayViewDidReceiveTap:(SCOverlayView *)overlayView; + +@end diff --git a/Demo/SCStackViewController/SCOverlayView.m b/Demo/SCStackViewController/SCOverlayView.m new file mode 100644 index 00000000..4a6ad68c --- /dev/null +++ b/Demo/SCStackViewController/SCOverlayView.m @@ -0,0 +1,38 @@ +// +// SCOverlayView.m +// SCStackViewController +// +// Created by Stefan Ceriu on 23/11/2013. +// Copyright (c) 2013 Stefan Ceriu. All rights reserved. +// + +#import "SCOverlayView.h" + +@implementation SCOverlayView + ++ (instancetype)overlayView +{ + SCOverlayView *overlayView = nil; + NSArray *nibContents = [[UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil] instantiateWithOwner:nil options:nil]; + if(nibContents.count > 0) { + overlayView = (SCOverlayView *)[nibContents objectAtIndex:0]; + } + return overlayView; +} + +- (void)awakeFromNib +{ + [super awakeFromNib]; + + UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTap:)]; + [self addGestureRecognizer:tapGesture]; +} + +- (void)onTap:(UITapGestureRecognizer *)sender +{ + if([self.delegate respondsToSelector:@selector(overlayViewDidReceiveTap:)]) { + [self.delegate overlayViewDidReceiveTap:self]; + } +} + +@end diff --git a/Demo/SCStackViewController/SCOverlayView.xib b/Demo/SCStackViewController/SCOverlayView.xib new file mode 100644 index 00000000..3a651f97 --- /dev/null +++ b/Demo/SCStackViewController/SCOverlayView.xib @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/SCStackViewController/SCRootViewController.h b/Demo/SCStackViewController/SCRootViewController.h index d787bcd3..fa4399fa 100644 --- a/Demo/SCStackViewController/SCRootViewController.h +++ b/Demo/SCStackViewController/SCRootViewController.h @@ -6,6 +6,8 @@ // Copyright (c) 2013 Stefan Ceriu. All rights reserved. // +@import UIKit; + @interface SCRootViewController : UIViewController @end diff --git a/Demo/SCStackViewController/SCRootViewController.m b/Demo/SCStackViewController/SCRootViewController.m index 3290f543..c7985c10 100644 --- a/Demo/SCStackViewController/SCRootViewController.m +++ b/Demo/SCStackViewController/SCRootViewController.m @@ -9,21 +9,41 @@ #import "SCRootViewController.h" #import "SCStackViewController.h" +#import "SCScrollView.h" + +#import "SCStackNavigationStep.h" +#import "SCEasingFunction.h" + #import "SCMainViewController.h" +#import "SCOverlayView.h" #import "SCStackLayouter.h" #import "SCParallaxStackLayouter.h" #import "SCSlidingStackLayouter.h" -#import "SCGoogleMapsStackLayouter.h" -#import "SCMerryGoRoundStackLayouter.h" #import "SCReversedStackLayouter.h" +#import "SCResizingStackLayouter.h" + +#import "SCImageViewController.h" +#import "SCImagesLayouter.h" #import "SCMenuViewController.h" -#import "UIViewController+Shadows.h" +#import "SCMenusLayouter.h" + +#import "SCTitleBarViewController.h" + +#import "SCModalViewController.h" +#import "SCModalLayouter.h" -@interface SCRootViewController () +@interface SCRootViewController () -@property (nonatomic, strong) SCStackViewController *stackViewController; +@property (nonatomic, strong) IBOutlet SCStackViewController *stackViewController; +@property (nonatomic, strong) IBOutlet SCMainViewController *mainViewController; +@property (nonatomic, strong) SCOverlayView *overlayView; + +@property (nonatomic, strong) UIButton *leftMenuButton; +@property (nonatomic, strong) UIButton *rightMenuButton; + +@property (nonatomic, assign) SCStackDemoType currentDemoType; @end @@ -31,109 +51,388 @@ @implementation SCRootViewController - (void)viewDidLoad { - [super viewDidLoad]; - - SCMainViewController *mainViewController = [[SCMainViewController alloc] init]; - [mainViewController setDelegate:self]; - [mainViewController.view castShadowWithPosition:SCShadowEdgeAll]; - - self.stackViewController = [[SCStackViewController alloc] initWithRootViewController:mainViewController]; - [self.stackViewController.view setFrame:self.view.bounds]; - //[self.stackViewController setTouchRefusalArea:[UIBezierPath bezierPathWithRect:CGRectInset(self.view.bounds, 50, 50)]]; - - [self addChildViewController:self.stackViewController]; - [self.view addSubview:self.stackViewController.view]; - [self.stackViewController didMoveToParentViewController:self]; - - [self mainViewController:mainViewController didSelectLayouterType:SCStackLayouterTypeParallax]; + [super viewDidLoad]; + + self.overlayView = [SCOverlayView overlayView]; + [self.overlayView setDelegate:self]; + [self.overlayView setAlpha:0.0f]; + [self.mainViewController.view addSubview:self.overlayView]; + [self.overlayView setFrame:self.mainViewController.view.bounds]; + + + [self.stackViewController willMoveToParentViewController:self]; + + [self.view addSubview:self.stackViewController.view]; + [self.stackViewController.view setFrame:self.view.bounds]; + + [self addChildViewController:self.stackViewController]; + [self.stackViewController didMoveToParentViewController:self]; +} + +- (void)viewDidAppear:(BOOL)animated +{ + [self _setupImagesDemo]; +} + +- (void)setCurrentDemoType:(SCStackDemoType)currentDemoType +{ + _currentDemoType = currentDemoType; + + for(NSUInteger i=SCStackViewControllerPositionTop; i<=SCStackViewControllerPositionRight; i++) { + [self.stackViewController popToRootViewControllerFromPosition:(SCStackViewControllerPosition)i + animated:YES + completion:nil]; + } + + switch (currentDemoType) { + case SCStackDemoTypeVerticalImages: { + [self _setupImagesDemo]; + break; + } + case SCStackDemoTypeSideMenus: { + [self _setupSideMenusDemo]; + break; + } + case SCStackDemoTypeTitleBar: { + [self _setupTitleBarDemo]; + break; + } + case SCStackDemoTypeModal: { + [self _setupModalDemo]; + break; + } + case SCStackDemoTypeGeneric: { + [self _setupGenericDemo]; + break; + } + } } #pragma mark - SCMainViewControllerDelegate -- (void)mainViewController:(SCMainViewController *)mainViewController didSelectLayouterType:(SCStackLayouterType)type -{ - static NSDictionary *typeToLayouter; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - typeToLayouter = (@{ - @(SCStackLayouterTypePlain) : [SCStackLayouter class], - @(SCStackLayouterTypeSliding) : [SCSlidingStackLayouter class], - @(SCStackLayouterTypeParallax) : [SCParallaxStackLayouter class], - @(SCStackLayouterTypeGoogleMaps) : [SCGoogleMapsStackLayouter class], - @(SCStackLayouterTypeMerryGoRound) : [SCMerryGoRoundStackLayouter class], - @(SCStackLayouterTypeReversed) : [SCReversedStackLayouter class] - }); - }); - - id layouter = [[typeToLayouter[@(type)] alloc] init]; - - [self.stackViewController registerLayouter:layouter forPosition:SCStackViewControllerPositionLeft]; - [self.stackViewController registerLayouter:layouter forPosition:SCStackViewControllerPositionRight]; - - [self.stackViewController popToRootViewControllerFromPosition:SCStackViewControllerPositionLeft - animated:YES - completion:nil]; - - SCMenuViewController *leftViewController = [[SCMenuViewController alloc] initWithPosition:SCStackViewControllerPositionLeft]; - [leftViewController.view castShadowWithPosition:SCShadowEdgeLeft]; - [leftViewController setDelegate:self]; - [self.stackViewController pushViewController:leftViewController - atPosition:SCStackViewControllerPositionLeft - unfold:NO - animated:NO - completion:nil]; - - [self.stackViewController popToRootViewControllerFromPosition:SCStackViewControllerPositionRight - animated:YES - completion:nil]; - - SCMenuViewController *rightViewController = [[SCMenuViewController alloc] initWithPosition:SCStackViewControllerPositionRight]; - [rightViewController.view castShadowWithPosition:SCShadowEdgeRight]; - [rightViewController setDelegate:self]; - [self.stackViewController pushViewController:rightViewController - atPosition:SCStackViewControllerPositionRight - unfold:NO - animated:NO - completion:nil]; -} - -#pragma mark - SCMenuViewControllerDelegate - -- (void)menuViewControllerDidRequestPush:(SCMenuViewController *)menuViewController -{ - SCMenuViewController *newMenuViewController = [[SCMenuViewController alloc] initWithPosition:menuViewController.position]; - [newMenuViewController setDelegate:self]; - - static NSDictionary *positionToShadowEdge; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - positionToShadowEdge = (@{ - @(SCStackViewControllerPositionTop) : @(SCShadowEdgeTop), - @(SCStackViewControllerPositionLeft) : @(SCShadowEdgeLeft), - @(SCStackViewControllerPositionBottom) : @(SCShadowEdgeBottom), - @(SCStackViewControllerPositionRight) : @(SCShadowEdgeRight) - }); - }); - - [newMenuViewController.view castShadowWithPosition:[positionToShadowEdge[@(menuViewController.position)] intValue]]; - [self.stackViewController pushViewController:newMenuViewController - atPosition:menuViewController.position - unfold:YES - animated:YES - completion:nil]; -} - -- (void)menuViewControllerDidRequestPop:(SCMenuViewController *)menuViewController -{ - [self.stackViewController popViewControllerAtPosition:menuViewController.position - animated:YES - completion:nil]; -} - - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation -{ - return UIInterfaceOrientationIsLandscape(toInterfaceOrientation); +- (void)mainViewController:(SCMainViewController *)mainViewController didChangeDemoType:(SCStackDemoType)type +{ + self.currentDemoType = type; +} + +- (void)mainViewController:(SCMainViewController *)mainViewController didChangeLayouterType:(SCStackLayouterType)type +{ + static NSDictionary *typeToLayouter; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + typeToLayouter = (@{ + @(SCStackLayouterTypePlain) : [SCStackLayouter class], + @(SCStackLayouterTypeSliding) : [SCSlidingStackLayouter class], + @(SCStackLayouterTypeParallax) : [SCParallaxStackLayouter class], + @(SCStackLayouterTypeReversed) : [SCReversedStackLayouter class], + @(SCStacklayouterTypePlainResizing) : [SCResizingStackLayouter class] + }); + }); + + id layouter = [[typeToLayouter[@(type)] alloc] init]; + [layouter setShouldStackControllersAboveRoot:YES]; + [self _registerLayouter:layouter]; +} + +- (void)mainViewController:(SCMainViewController *)mainViewController didChangeAnimationType:(SCEasingFunctionType)type +{ + [self.stackViewController setEasingFunction:[SCEasingFunction easingFunctionWithType:type]]; +} + +- (void)mainViewController:(SCMainViewController *)mainViewController didChangeAnimationDuration:(NSTimeInterval)duration +{ + [self.stackViewController setAnimationDuration:duration]; +} + +#pragma mark - SCStackedViewControllerDelegate + +- (void)stackedViewControllerDidRequestPush:(SCImageViewController *)menuViewController +{ + UIViewController *newViewController; + + switch (self.currentDemoType) { + case SCStackDemoTypeVerticalImages: { + newViewController = [[SCImageViewController alloc] initWithPosition:menuViewController.position]; + break; + } + case SCStackDemoTypeSideMenus: { + newViewController = [[SCMenuViewController alloc] initWithPosition:menuViewController.position]; + break; + } + case SCStackDemoTypeGeneric: { + newViewController = [[SCMenuViewController alloc] initWithPosition:menuViewController.position]; + break; + } + default: { + break; + } + } + + [newViewController setDelegate:self]; + + [self.stackViewController pushViewController:newViewController + atPosition:menuViewController.position + unfold:YES + animated:YES + completion:nil]; +} + +- (void)stackedViewControllerDidRequestPop:(SCImageViewController *)menuViewController +{ + [self.stackViewController popViewControllerAtPosition:menuViewController.position + animated:YES + completion:nil]; +} + +#pragma mark - SCStackViewControllerDelegate + +- (void)stackViewController:(SCStackViewController *)stackViewController didNavigateToOffset:(CGPoint)offset +{ + [self.overlayView setAlpha:ABS((offset.x ?: offset.y) / 300.0f)]; + + for(SCStackViewControllerPosition position = SCStackViewControllerPositionTop; position <= SCStackViewControllerPositionRight; position++) { + for(SCImageViewController *viewController in [self.stackViewController viewControllersForPosition:position]) { + [viewController setVisiblePercentage:[stackViewController visiblePercentageForViewController:viewController]]; + } + } + + [self.mainViewController setVisiblePercentage:[stackViewController visiblePercentageForViewController:self.mainViewController]]; + + + [self _updateMenuButtonsWithContentOffset:offset]; +} + +#pragma mark - SCOverlayViewDelegate + +- (void)overlayViewDidReceiveTap:(SCOverlayView *)overlayView +{ + [self.stackViewController navigateToViewController:self.stackViewController.rootViewController animated:YES completion:nil]; +} + +#pragma mark - Private + +- (void)_setupImagesDemo +{ + SCImagesLayouter *imagesLayouter = [[SCImagesLayouter alloc] init]; + [imagesLayouter setShouldStackControllersAboveRoot:YES]; + [self _registerLayouter:imagesLayouter]; + + [self.stackViewController setAnimationDuration:1.25f]; + [self.stackViewController setEasingFunction:[SCEasingFunction easingFunctionWithType:SCEasingFunctionTypeElasticEaseOut]]; + + SCImageViewController *topViewController = [[SCImageViewController alloc] initWithPosition:SCStackViewControllerPositionTop]; + [topViewController setDelegate:self]; + + [self.stackViewController pushViewController:topViewController + atPosition:SCStackViewControllerPositionTop + unfold:YES + animated:YES + completion:nil]; + + [self _hideMenuButtons]; + [self _enableOverlayView]; + [self.mainViewController hideAnimationOptionsAnimated:YES]; +} + +- (void)_setupSideMenusDemo +{ + SCMenusLayouter *menusLayouter = [[SCMenusLayouter alloc] init]; + [menusLayouter setShouldStackControllersAboveRoot:YES]; + [self _registerLayouter:menusLayouter]; + + [self.stackViewController setAnimationDuration:0.75f]; + [self.stackViewController setEasingFunction:[SCEasingFunction easingFunctionWithType:SCEasingFunctionTypeExponentialEaseOut]]; + + SCMenuViewController *leftViewController = [[SCMenuViewController alloc] initWithPosition:SCStackViewControllerPositionLeft]; + [leftViewController setDelegate:self]; + + [self.stackViewController pushViewController:leftViewController + atPosition:SCStackViewControllerPositionLeft + unfold:YES + animated:YES + completion:nil]; + + SCMenuViewController *rightViewController = [[SCMenuViewController alloc] initWithPosition:SCStackViewControllerPositionRight]; + [rightViewController setDelegate:self]; + + [self.stackViewController pushViewController:rightViewController + atPosition:SCStackViewControllerPositionRight + unfold:NO + animated:NO + completion:nil]; + + [self _showMenuButtons]; + [self _enableOverlayView]; + [self.mainViewController hideAnimationOptionsAnimated:YES]; +} + +- (void)_setupTitleBarDemo +{ + SCStackLayouter *titleBarLayouter = [[SCStackLayouter alloc] init]; + [titleBarLayouter setShouldStackControllersAboveRoot:YES]; + [self _registerLayouter:titleBarLayouter]; + + [self.stackViewController setAnimationDuration:1.0f]; + [self.stackViewController setEasingFunction:[SCEasingFunction easingFunctionWithType:SCEasingFunctionTypeBounceEaseOut]]; + + SCTitleBarViewController *topViewController = [[SCTitleBarViewController alloc] initWithPosition:SCStackViewControllerPositionTop]; + [topViewController setDelegate:self]; + + [self.stackViewController pushViewController:topViewController atPosition:SCStackViewControllerPositionTop unfold:NO animated:NO completion:^{ + + SCStackNavigationStep *minimizedStep = [SCStackNavigationStep navigationStepWithPercentage:0.1f blockType:SCStackNavigationStepBlockTypeReverse]; + SCStackNavigationStep *intermediateStep = [SCStackNavigationStep navigationStepWithPercentage:0.3f]; + + [self.stackViewController registerNavigationSteps:@[minimizedStep,intermediateStep] forViewController:topViewController]; + + [self.stackViewController navigateToStep:minimizedStep inViewController:topViewController animated:YES completion:nil]; + }]; + + [self _hideMenuButtons]; + [self _disableOverlayView]; + [self.mainViewController hideAnimationOptionsAnimated:YES]; +} + +- (void)_setupModalDemo +{ + SCModalLayouter *modalLayouter = [[SCModalLayouter alloc] init]; + [modalLayouter setShouldStackControllersAboveRoot:YES]; + [self _registerLayouter:modalLayouter]; + + [self.stackViewController setAnimationDuration:1.5f]; + [self.stackViewController setEasingFunction:[SCEasingFunction easingFunctionWithType:SCEasingFunctionTypeElasticEaseOut]]; + + SCModalViewController *modalViewController = [[SCModalViewController alloc] initWithPosition:SCStackViewControllerPositionBottom]; + [modalViewController setDelegate:self]; + + [self.stackViewController pushViewController:modalViewController + atPosition:SCStackViewControllerPositionBottom + unfold:YES + animated:YES + completion:nil]; + + [self _hideMenuButtons]; + [self _disableOverlayView]; + [self.mainViewController hideAnimationOptionsAnimated:YES]; +} + +- (void)_setupGenericDemo +{ + SCStackLayouter *plainLayouter = [[SCStackLayouter alloc] init]; + [plainLayouter setShouldStackControllersAboveRoot:YES]; + [self _registerLayouter:plainLayouter]; + + [self.stackViewController setAnimationDuration:0.25f]; + [self.stackViewController setEasingFunction:[SCEasingFunction easingFunctionWithType:SCEasingFunctionTypeLinear]]; + + SCMenuViewController *leftViewController = [[SCMenuViewController alloc] initWithPosition:SCStackViewControllerPositionLeft]; + [leftViewController setDelegate:self]; + + [self.stackViewController pushViewController:leftViewController + atPosition:SCStackViewControllerPositionLeft + unfold:NO + animated:NO + completion:nil]; + + SCMenuViewController *rightViewController = [[SCMenuViewController alloc] initWithPosition:SCStackViewControllerPositionRight]; + [rightViewController setDelegate:self]; + + [self.stackViewController pushViewController:rightViewController + atPosition:SCStackViewControllerPositionRight + unfold:YES + animated:YES + completion:nil]; + + [self _showMenuButtons]; + [self _enableOverlayView]; + [self.mainViewController showAnimationOptionsAnimated:YES]; +} + +- (void)_registerLayouter:(id)layouter +{ + for(NSUInteger i=SCStackViewControllerPositionTop; i<=SCStackViewControllerPositionRight; i++) { + [self.stackViewController registerLayouter:layouter forPosition:(SCStackViewControllerPosition)i animated:NO]; + } +} + +- (void)_showMenuButtons +{ + if(self.leftMenuButton == nil) { + self.leftMenuButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [self.leftMenuButton setFrame:CGRectMake(0.0f, 20.0f, 40.0f, 64.0f)]; + [self.leftMenuButton setImage:[UIImage imageNamed:@"left-menu-tab.png"] forState:UIControlStateNormal]; + [self.leftMenuButton setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin]; + [self.leftMenuButton setAlpha:0.0f]; + [self.leftMenuButton addTarget:self action:@selector(_onLeftMenuButtonTap:) forControlEvents:UIControlEventTouchUpInside]; + [self.stackViewController.view addSubview:self.leftMenuButton]; + } + + if(self.rightMenuButton == nil) { + self.rightMenuButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [self.rightMenuButton setFrame:CGRectMake(CGRectGetWidth(self.view.bounds) - 40.0f, 20.0f, 40.0f, 64.0f)]; + [self.rightMenuButton setImage:[UIImage imageNamed:@"right-menu-tab.png"] forState:UIControlStateNormal]; + [self.rightMenuButton setAutoresizingMask:UIViewAutoresizingFlexibleLeftMargin]; + [self.rightMenuButton setAlpha:0.0f]; + [self.rightMenuButton addTarget:self action:@selector(_onRightMenuButtonTap:) forControlEvents:UIControlEventTouchUpInside]; + [self.stackViewController.view addSubview:self.rightMenuButton]; + } + + [UIView animateWithDuration:0.25f animations:^{ + [self.leftMenuButton setAlpha:1.0f]; + [self.rightMenuButton setAlpha:1.0f]; + }]; +} + +- (void)_hideMenuButtons +{ + [UIView animateWithDuration:0.25f animations:^{ + [self.leftMenuButton setAlpha:0.0f]; + [self.rightMenuButton setAlpha:0.0f]; + }]; +} + +- (void)_enableOverlayView +{ + [self.overlayView setHidden:NO]; +} + +- (void)_disableOverlayView +{ + [self.overlayView setHidden:YES]; +} + +- (void)_onLeftMenuButtonTap:(UIButton *)sender +{ + if(self.stackViewController.scrollView.contentOffset.x >= 0.0f) { + [self.stackViewController navigateToViewController:[[self.stackViewController viewControllersForPosition:SCStackViewControllerPositionLeft] firstObject] + animated:YES + completion:nil]; + } else if(self.stackViewController.scrollView.contentOffset.x < 0.0f) { + [self.stackViewController navigateToViewController:self.stackViewController.rootViewController animated:YES completion:nil]; + } +} + +- (void)_onRightMenuButtonTap:(UIButton *)sender +{ + if(self.stackViewController.scrollView.contentOffset.x <= 0.0f) { + [self.stackViewController navigateToViewController:[[self.stackViewController viewControllersForPosition:SCStackViewControllerPositionRight] firstObject] + animated:YES + completion:nil]; + } else if(self.stackViewController.scrollView.contentOffset.x > 0.0f) { + [self.stackViewController navigateToViewController:self.stackViewController.rootViewController animated:YES completion:nil]; + } +} + +- (void)_updateMenuButtonsWithContentOffset:(CGPoint)offset +{ + CGRect frame = self.leftMenuButton.frame; + frame.origin.x = (offset.x > 0.0f ? 0.0f : ABS(offset.x)); + self.leftMenuButton.frame = frame; + + frame = self.rightMenuButton.frame; + frame.origin.x = CGRectGetWidth(self.stackViewController.view.bounds) - frame.size.width; + frame.origin.x += (offset.x < 0.0f ?: -offset.x); + self.rightMenuButton.frame = frame; } @end diff --git a/Demo/SCStackViewController/SCRootViewController.xib b/Demo/SCStackViewController/SCRootViewController.xib index 1d3ccf2a..b5326eaa 100644 --- a/Demo/SCStackViewController/SCRootViewController.xib +++ b/Demo/SCStackViewController/SCRootViewController.xib @@ -1,121 +1,51 @@ - - - - 1552 - 12E55 - 3084 - 1187.39 - 626.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 2083 - - - IBProxyObject - IBUIView - - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - PluginDependencyRecalculationVersion - - - - - IBFilesOwner - IBIPadFramework - - - IBFirstResponder - IBIPadFramework - - - - 274 - {1024, 768} - - - - 3 - MQA - - 2 - - - - 3 - 3 - - IBIPadFramework - - - - - - - view - - - - 3 - - - - - - 0 - - - - - - -1 - - - File's Owner - - - -2 - - - - - 2 - - - - - - - SCRootViewController - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - UIResponder - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - - - 3 - - - - - SCRootViewController - UIViewController - - IBProjectSource - ./Classes/SCRootViewController.h - - - - - 0 - IBIPadFramework - YES - 3 - 2083 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/SCStackViewController/SCStackViewController-Info.plist b/Demo/SCStackViewController/SCStackViewController-Info.plist index 3eaf9e95..73082af2 100644 --- a/Demo/SCStackViewController/SCStackViewController-Info.plist +++ b/Demo/SCStackViewController/SCStackViewController-Info.plist @@ -9,7 +9,7 @@ CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier - com.stefanceriu.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -36,11 +36,17 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown UISupportedInterfaceOrientations~ipad UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIViewControllerBasedStatusBarAppearance + diff --git a/Demo/SCStackViewController/SCStackViewController-Prefix.pch b/Demo/SCStackViewController/SCStackViewController-Prefix.pch deleted file mode 100644 index 71c326b4..00000000 --- a/Demo/SCStackViewController/SCStackViewController-Prefix.pch +++ /dev/null @@ -1,8 +0,0 @@ -// -// Prefix header for all source files of the 'SCStackViewController' target in the 'SCStackViewController' project -// - -#ifdef __OBJC__ - #import - #import -#endif diff --git a/Demo/SCStackViewController/SCStackedViewControllerProtocol.h b/Demo/SCStackViewController/SCStackedViewControllerProtocol.h new file mode 100644 index 00000000..1fbe7c2d --- /dev/null +++ b/Demo/SCStackViewController/SCStackedViewControllerProtocol.h @@ -0,0 +1,29 @@ +// +// SCStackableViewControllerProtocol.h +// SCStackViewController +// +// Created by Stefan Ceriu on 5/9/15. +// Copyright (c) 2015 Stefan Ceriu. All rights reserved. +// + +#import + +@protocol SCStackedViewControllerDelegate; + +@protocol SCStackedViewControllerProtocol + +@property (nonatomic, weak) id delegate; +@property (nonatomic, readonly) SCStackViewControllerPosition position; + +- (void)setVisiblePercentage:(CGFloat)percentage; + +@end + +@protocol SCStackedViewControllerDelegate + +@optional +- (void)stackedViewControllerDidRequestPush:(id)stackedViewController; +- (void)stackedViewControllerDidRequestPop:(id)stackedViewController; + +@end + diff --git a/Demo/SCStackViewController/TitleBarDemo/SCTitleBarCollectionViewCell.h b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarCollectionViewCell.h new file mode 100644 index 00000000..c3b66f9b --- /dev/null +++ b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarCollectionViewCell.h @@ -0,0 +1,15 @@ +// +// SCTitleBarCollectionViewCell.h +// SCStackViewController +// +// Created by Stefan Ceriu on 5/12/15. +// Copyright (c) 2015 Stefan Ceriu. All rights reserved. +// + +#import + +@interface SCTitleBarCollectionViewCell : UICollectionViewCell + +@property (nonatomic, weak, readonly) UIImageView *imageView; + +@end diff --git a/Demo/SCStackViewController/TitleBarDemo/SCTitleBarCollectionViewCell.m b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarCollectionViewCell.m new file mode 100644 index 00000000..ca74b6ff --- /dev/null +++ b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarCollectionViewCell.m @@ -0,0 +1,26 @@ +// +// SCTitleBarCollectionViewCell.m +// SCStackViewController +// +// Created by Stefan Ceriu on 5/12/15. +// Copyright (c) 2015 Stefan Ceriu. All rights reserved. +// + +#import "SCTitleBarCollectionViewCell.h" + +@interface SCTitleBarCollectionViewCell () + +@property (nonatomic, weak) IBOutlet UIImageView *imageView; + +@end + +@implementation SCTitleBarCollectionViewCell + +- (void)awakeFromNib +{ + [super awakeFromNib]; + + [self.imageView.layer setCornerRadius:10.0f]; +} + +@end diff --git a/Demo/SCStackViewController/TitleBarDemo/SCTitleBarCollectionViewCell.xib b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarCollectionViewCell.xib new file mode 100644 index 00000000..00e0d9f9 --- /dev/null +++ b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarCollectionViewCell.xib @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/SCStackViewController/TitleBarDemo/SCTitleBarViewController.h b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarViewController.h new file mode 100644 index 00000000..a8484411 --- /dev/null +++ b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarViewController.h @@ -0,0 +1,16 @@ +// +// SCMenuViewController.h +// SCStackViewController +// +// Created by Stefan Ceriu on 08/08/2013. +// Copyright (c) 2013 Stefan Ceriu. All rights reserved. +// + +#import "SCStackViewController.h" +#import "SCStackedViewControllerProtocol.h" + +@interface SCTitleBarViewController : UIViewController + +- (instancetype)initWithPosition:(SCStackViewControllerPosition)position; + +@end diff --git a/Demo/SCStackViewController/TitleBarDemo/SCTitleBarViewController.m b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarViewController.m new file mode 100644 index 00000000..ee42360e --- /dev/null +++ b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarViewController.m @@ -0,0 +1,111 @@ +// +// SCMenuViewController.m +// SCStackViewController +// +// Created by Stefan Ceriu on 08/08/2013. +// Copyright (c) 2013 Stefan Ceriu. All rights reserved. +// + +#import "SCTitleBarViewController.h" + +#import "SCStackViewController.h" +#import "SCStackNavigationStep.h" + +#import "UIView+Shadows.h" +#import "UIColor+RandomColors.h" +#import "SCTitleBarCollectionViewCell.h" + +@interface SCTitleBarViewController () + +@property (nonatomic, assign) SCStackViewControllerPosition position; +@property (nonatomic, weak) IBOutlet UILabel *visiblePercentageLabel; + +@property (nonatomic, weak) IBOutlet UICollectionView *collectionView; + +@property (nonatomic, weak) IBOutlet UIButton *minimizeButton; +@property (nonatomic, weak) IBOutlet UIButton *maximizeButton; + +@end + +@implementation SCTitleBarViewController +@synthesize delegate; + +- (instancetype)initWithPosition:(SCStackViewControllerPosition)position +{ + if(self = [super init]) { + self.position = position; + } + + return self; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + [self updateShadow]; + + NSString *identifier =NSStringFromClass([SCTitleBarCollectionViewCell class]); + [self.collectionView registerNib:[UINib nibWithNibName:identifier bundle:nil] forCellWithReuseIdentifier:identifier]; +} + +- (void)viewWillLayoutSubviews +{ + [super viewWillLayoutSubviews]; + [self updateShadow]; +} + +- (void)setVisiblePercentage:(CGFloat)percentage +{ + [self.visiblePercentageLabel setText:[NSString stringWithFormat:@"%.3f%%", percentage]]; + + CGFloat normalized = MAX(0, MIN(1, (percentage - 0.1f) / 0.2f)); + + CGRect frame = self.visiblePercentageLabel.frame; + frame.origin.y = 300.0f - (normalized * 40.0f); + self.visiblePercentageLabel.frame = frame; + + [self.minimizeButton setAlpha:normalized]; + [self.maximizeButton setAlpha:normalized]; +} + +- (IBAction)onMinimizeButtonTap:(id)sender +{ + [self.sc_stackViewController navigateToStep:[SCStackNavigationStep navigationStepWithPercentage:0.1f] inViewController:self animated:YES completion:nil]; +} + +- (IBAction)onMaximizeButtonTap:(id)sender +{ + [self.sc_stackViewController navigateToStep:[SCStackNavigationStep navigationStepWithPercentage:1.0f] inViewController:self animated:YES completion:nil]; +} + +#pragma mark - UICollectionViewDataSource + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + return 20; +} + +- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath +{ + SCTitleBarCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([SCTitleBarCollectionViewCell class]) forIndexPath:indexPath]; + [cell.imageView setBackgroundColor:[UIColor randomColorWithAlpha:1.0f]]; + return cell; +} + +- (void)updateShadow +{ + static NSDictionary *positionToShadowEdge; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + positionToShadowEdge = (@{ + @(SCStackViewControllerPositionTop) : @(SCShadowEdgeTop), + @(SCStackViewControllerPositionLeft) : @(SCShadowEdgeLeft), + @(SCStackViewControllerPositionBottom) : @(SCShadowEdgeBottom), + @(SCStackViewControllerPositionRight) : @(SCShadowEdgeRight) + }); + }); + + [self.view castShadowWithPosition:(SCShadowEdge)[positionToShadowEdge[@(self.position)] intValue]]; +} + +@end diff --git a/Demo/SCStackViewController/TitleBarDemo/SCTitleBarViewController.xib b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarViewController.xib new file mode 100644 index 00000000..efdc7d60 --- /dev/null +++ b/Demo/SCStackViewController/TitleBarDemo/SCTitleBarViewController.xib @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index 2a663d71..4e77e558 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,30 @@ # SCStackViewController -SCStackViewController is a container view controller which allows you to stack other view controllers on the top/left/bottom/right of the root and build custom transitions between them while providing correct physics and appearance calls. +![Version](https://img.shields.io/cocoapods/v/SCStackViewController.svg) ![License](https://img.shields.io/cocoapods/l/SCStackViewController.svg) ![Platform](https://img.shields.io/cocoapods/p/SCStackViewController.svg) -SCStackViewController is a little bit from the all the other stack implementations available. It was build with the following points in mind: +SCStackViewController is a generic container view controller which allows you to stack child view controllers on the top/left/bottom/right of the root and build custom transitions between them while providing correct physics and appearance calls, custom layouts, easing functions, custom navigation steps and more. -1. Simple to understand and modify -2. Left/right and top/bottom stacking -3. Correct physics -4. Correct appearance calls -5. Customizable transitions -6. Pagination -7. Customizable interaction area -8. Completion blocks for everything +It was built with versatility in mind so it can be applied to quite a few different scenarios, some of which you can see bellow: -## Implementation details +### Demo - for more, 'pod try scstackviewcontroller' or see the included project +##### Top stacked view controllers with elastic ease out easing function and perspective transform +![Images](https://drive.google.com/u/0/uc?id=1NJtb8fOGmZ7rEl132bhW8NQg5pl0Qjus&export=download) -SCStackViewController comes in at just under 500 lines and is build on top of an UIScrollView which gives us the physics we need, content insets for all the 4 positions, callbacks for linking the custom transitions to and easy to build pagination. By overriding the scrollView's shouldReceiveTouch: method we also get the customizable interaction area. +##### Side stacked with parallax and root resizing +![SideMenus](https://drive.google.com/u/0/uc?id=1YzzcK8u6yzY6A2eNpXD6DyPx3jo9QUgH&export=download) -The stack itself relies on layouters to know where to place the stacked controllers at every point. They are build on top of a simple protocol and the demo project contains 6 examples with various effects. +##### Title bar with custom navigation steps and a bounce ease out easing function +![TitleBar](https://drive.google.com/u/0/uc?id=1tuKFx2nk8HpsNSJoNWSgnAX1d6uyOp-B&export=download) -## Examples +#### A modal view controller using a scale and rotation transform +![Modal](https://drive.google.com/u/0/uc?id=1ENeyTr3oPmA1HXtmtjCc4A3bsqI-Mj6h&export=download) -##### Plain Stack Layouter -It places the view controllers to their final position and doesn't modify them while dragging. - -![PlainStackLayouter](https://dl.dropboxusercontent.com/u/12748201/Recordings/Plain.gif) - -##### Reversed Stack Layouter -Reverses the direction used in the plain layouter - -![ReversedStackLayouter](https://dl.dropboxusercontent.com/u/12748201/Recordings/Reversed.gif) - -##### Sliding Stack Layouter -It reveals every new controller from beneath the previous one through sliding - -![SlidingStackLayouter](https://dl.dropboxusercontent.com/u/12748201/Recordings/Sliding.gif) - -##### Parallax Stack Layouter -Add a nice parallax effect while revealing the stacked controllers - -![ParallaxStackLayouter](https://dl.dropboxusercontent.com/u/12748201/Recordings/Parallax.gif) - -##### GoogleMaps Stack Layouter -The effect seen in the Google Maps app when opening the drawer - -![GoogleMapsStackLayouter](https://dl.dropboxusercontent.com/u/12748201/Recordings/GoogleMaps.gif) - -##### MerryGoRound Stack Layouter -Just something I was playing with.. :) - -![MerryGoRoundStackLayouter](https://dl.dropboxusercontent.com/u/12748201/Recordings/MerryGoRound.gif) - -## Usage - -- Import the stack into your project - -``` -#import "SCStackViewController.h" -``` - -- Create a new instance - -``` -stackViewController = [[SCStackViewController alloc] initWithRootViewController:rootViewController]; -``` - -- Set a touch refusal area (optional) - -``` -[stackViewController setTouchRefusalArea:[UIBezierPath bezierPathWithRect:CGRectInset(self.view.bounds, 50, 50)]] -``` - -- Register layouters - -``` -id layouter = [[SCParallaxStackLayouter alloc] init]; -[stackViewController registerLayouter:layouter forPosition:SCStackViewControllerPositionLeft]; -``` - -- Push view controllers - -``` -[self.stackViewController pushViewController:leftViewController - atPosition:SCStackViewControllerPositionLeft - unfold:NO - animated:NO - completion:nil]; -``` - -######Check out the demo project for more details. +#### And even a [navigation controller](https://github.com/stefanceriu/SCNavigationController) :) +![SCNavigationController](https://drive.google.com/u/0/uc?id=1TbewTm1Y_WzY6lZ7Gf0lRApYci_u7Q62&export=download) ## License SCStackViewController is released under the MIT License (MIT) (see the LICENSE file) ## Contact Any suggestions or improvements are more than welcome. -Feel free to contact me at [stefan.ceriu@yahoo.com](mailto:stefan.ceriu@yahoo.com) or [@stefanceriu](https://twitter.com/stefanceriu). \ No newline at end of file +Feel free to contact me at [stefan.ceriu@gmail.com](mailto:stefan.ceriu@gmail.com) or [@stefanceriu](https://twitter.com/stefanceriu). diff --git a/SCStackViewController.podspec b/SCStackViewController.podspec index 76612e0e..8992df9e 100644 --- a/SCStackViewController.podspec +++ b/SCStackViewController.podspec @@ -1,35 +1,21 @@ Pod::Spec.new do |s| s.name = 'SCStackViewController' - s.version = '1.0.1' + s.version = '3.3.6' s.platform = :ios - s.ios.deployment_target = '5.0' + s.ios.deployment_target = '6.0' s.summary = 'SCStackViewController is a container controller which allows you to stack other view controllers and build custom transitions between them.' s.description = <<-DESC - SCStackViewController is a container view controller which allows you to stack other view controllers on the top/left/bottom/right of the root and build custom transitions between them while providing correct physics and appearance calls. - - SCStackViewController is a little bit from the all the other stack implementations available. It was build with the following points in mind: - - * Simple to understand and modify - * Left/right and top/bottom stacking - * Correct physics - * Correct appearance calls - * Customizable transitions - * Pagination - * Customizable interaction area - * Completion blocks for everything' + SCStackViewController is a generic container view controller which allows you to stack child view controllers on the top/left/bottom/right of the root and build custom transitions between them while providing correct physics and appearance calls, custom layouts, easing functions, custom navigation steps and more. DESC s.homepage = 'https://github.com/stefanceriu/SCStackViewController' s.author = { 'Stefan Ceriu' => 'stefan.ceriu@yahoo.com' } + s.social_media_url = 'https://twitter.com/stefanceriu' s.source = { :git => 'https://github.com/stefanceriu/SCStackViewController.git', :tag => "v#{s.version}" } s.license = { :type => 'MIT License', :file => 'LICENSE' } - s.source_files = 'SCStackViewController/*', 'SCStackViewController/Layouters/*', 'SCStackViewController/Layouters/Reversed/*' + s.source_files = 'SCStackViewController/*', 'SCStackViewController/Layouters/*' s.requires_arc = true - s.frameworks = 'UIKit', 'QuartzCore', 'CoreGraphics', 'Foundation' - s.screenshots = ["https://dl.dropboxusercontent.com/u/12748201/Recordings/Plain.gif", - "https://dl.dropboxusercontent.com/u/12748201/Recordings/Reversed.gif", - "https://dl.dropboxusercontent.com/u/12748201/Recordings/Sliding.gif", - "https://dl.dropboxusercontent.com/u/12748201/Recordings/Parallax.gif", - "https://dl.dropboxusercontent.com/u/12748201/Recordings/GoogleMaps.gif", - "https://dl.dropboxusercontent.com/u/12748201/Recordings/MerryGoRound.gif"] + + s.dependency 'SCScrollView', '~> 1.1' + end \ No newline at end of file diff --git a/SCStackViewController/Layouters/Reversed/SCReversedStackLayouter.m b/SCStackViewController/Layouters/Reversed/SCReversedStackLayouter.m deleted file mode 100644 index 21b842b0..00000000 --- a/SCStackViewController/Layouters/Reversed/SCReversedStackLayouter.m +++ /dev/null @@ -1,75 +0,0 @@ -// -// SCReversedParallaxStackLayouter.m -// SCStackViewController -// -// Created by Stefan Ceriu on 18/08/2013. -// Copyright (c) 2013 Stefan Ceriu. All rights reserved. -// - -#import "SCReversedStackLayouter.h" - -@implementation SCReversedStackLayouter - -- (CGRect)finalFrameForViewController:(UIViewController *)viewController - withIndex:(NSUInteger)index - atPosition:(SCStackViewControllerPosition)position - withinGroup:(NSArray *)viewControllers - inStackController:(SCStackViewController *)stackController -{ - CGRect finalFrame = viewController.view.frame; - switch (position) { - case SCStackViewControllerPositionTop: { - NSArray *previousViewControllers = [viewControllers subarrayWithRange:NSMakeRange(0, [viewControllers indexOfObject:viewController])]; - CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.viewHeight"] floatValue]; - - finalFrame.origin.y = - totalSize + [[previousViewControllers valueForKeyPath:@"@sum.viewHeight"] floatValue]; - break; - } - case SCStackViewControllerPositionLeft: { - NSArray *previousViewControllers = [viewControllers subarrayWithRange:NSMakeRange(0, [viewControllers indexOfObject:viewController])]; - CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.viewWidth"] floatValue]; - - finalFrame.origin.x = - totalSize + [[previousViewControllers valueForKeyPath:@"@sum.viewWidth"] floatValue]; - break; - } - case SCStackViewControllerPositionBottom: { - NSArray *previousViewControllers = [viewControllers subarrayWithRange:NSMakeRange(0, [viewControllers indexOfObject:viewController] + 1)]; - CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.viewHeight"] floatValue]; - - finalFrame.origin.y = CGRectGetHeight(stackController.view.bounds) + totalSize - [[previousViewControllers valueForKeyPath:@"@sum.viewHeight"] floatValue]; - break; - } - case SCStackViewControllerPositionRight: { - NSArray *previousViewControllers = [viewControllers subarrayWithRange:NSMakeRange(0, [viewControllers indexOfObject:viewController] + 1)]; - CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.viewWidth"] floatValue]; - - finalFrame.origin.x = CGRectGetWidth(stackController.view.bounds) + totalSize - [[previousViewControllers valueForKeyPath:@"@sum.viewWidth"] floatValue]; - break; - } - default: - break; - } - - return finalFrame; -} - -- (CGRect)currentFrameForViewController:(UIViewController *)viewController - withIndex:(NSUInteger)index - atPosition:(SCStackViewControllerPosition)position - finalFrame:(CGRect)finalFrame - contentOffset:(CGPoint)contentOffset - inStackController:(SCStackViewController *)stackController -{ -// [UIView animateWithDuration:0.25f animations:^{ -// viewController.view.frame = finalFrame; -// }]; - - return finalFrame; -} - -- (BOOL)isReversed -{ - return YES; -} - -@end diff --git a/SCStackViewController/Layouters/SCGoogleMapsStackLayouter.h b/SCStackViewController/Layouters/SCGoogleMapsStackLayouter.h deleted file mode 100644 index 12c9474b..00000000 --- a/SCStackViewController/Layouters/SCGoogleMapsStackLayouter.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// SCGoogleMapsStackLayouter.h -// SCStackViewController -// -// Created by Stefan Ceriu on 24/08/2013. -// Copyright (c) 2013 Stefan Ceriu. All rights reserved. -// - -#import "SCSlidingStackLayouter.h" - -@interface SCGoogleMapsStackLayouter : SCSlidingStackLayouter - -@end diff --git a/SCStackViewController/Layouters/SCGoogleMapsStackLayouter.m b/SCStackViewController/Layouters/SCGoogleMapsStackLayouter.m deleted file mode 100644 index 852d4a0e..00000000 --- a/SCStackViewController/Layouters/SCGoogleMapsStackLayouter.m +++ /dev/null @@ -1,58 +0,0 @@ -// -// SCGoogleMapsStackLayouter.m -// SCStackViewController -// -// Created by Stefan Ceriu on 24/08/2013. -// Copyright (c) 2013 Stefan Ceriu. All rights reserved. -// - -#import "SCGoogleMapsStackLayouter.h" -#import - -@implementation SCGoogleMapsStackLayouter - -- (CGRect)currentFrameForViewController:(UIViewController *)viewController - withIndex:(NSUInteger)index - atPosition:(SCStackViewControllerPosition)position - finalFrame:(CGRect)finalFrame - contentOffset:(CGPoint)contentOffset - inStackController:(SCStackViewController *)stackController -{ - CGRect frame = [super currentFrameForViewController:viewController - withIndex:index - atPosition:position - finalFrame:finalFrame - contentOffset:contentOffset - inStackController:stackController]; - - CGFloat ratio = 1.0f; - - switch (position) { - case SCStackViewControllerPositionTop: { - ratio = contentOffset.y / CGRectGetMinY(finalFrame); - break; - } - case SCStackViewControllerPositionLeft: { - ratio = contentOffset.x / CGRectGetMinX(finalFrame); - break; - } - case SCStackViewControllerPositionBottom: { - ratio = contentOffset.y / (CGRectGetMaxY(finalFrame) - CGRectGetHeight(stackController.view.bounds)); - break; - } - case SCStackViewControllerPositionRight: { - ratio = contentOffset.x / (CGRectGetMaxX(finalFrame) - CGRectGetWidth(stackController.view.bounds)); - break; - } - default: - break; - } - - ratio = MAX(0.0f, MIN(ratio, 1.0f)); - - viewController.view.layer.sublayerTransform = CATransform3DMakeScale(ratio, ratio, 1.0f); - - return frame; -} - -@end diff --git a/SCStackViewController/Layouters/SCMerryGoRoundStackLayouter.h b/SCStackViewController/Layouters/SCMerryGoRoundStackLayouter.h deleted file mode 100644 index cbcde634..00000000 --- a/SCStackViewController/Layouters/SCMerryGoRoundStackLayouter.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// SCMerryGoRoundStackLayouter.h -// SCStackViewController -// -// Created by Stefan Ceriu on 24/08/2013. -// Copyright (c) 2013 Stefan Ceriu. All rights reserved. -// - -#import "SCStackLayouter.h" - -@interface SCMerryGoRoundStackLayouter : SCStackLayouter - -@end diff --git a/SCStackViewController/Layouters/SCMerryGoRoundStackLayouter.m b/SCStackViewController/Layouters/SCMerryGoRoundStackLayouter.m deleted file mode 100644 index c60feea6..00000000 --- a/SCStackViewController/Layouters/SCMerryGoRoundStackLayouter.m +++ /dev/null @@ -1,59 +0,0 @@ -// -// SCMerryGoRoundStackLayouter.m -// SCStackViewController -// -// Created by Stefan Ceriu on 24/08/2013. -// Copyright (c) 2013 Stefan Ceriu. All rights reserved. -// - -#import "SCMerryGoRoundStackLayouter.h" -#import - -@implementation SCMerryGoRoundStackLayouter - -- (CGRect)currentFrameForViewController:(UIViewController *)viewController - withIndex:(NSUInteger)index - atPosition:(SCStackViewControllerPosition)position - finalFrame:(CGRect)finalFrame - contentOffset:(CGPoint)contentOffset - inStackController:(SCStackViewController *)stackController -{ - CGRect frame = [super currentFrameForViewController:viewController - withIndex:index - atPosition:position - finalFrame:finalFrame - contentOffset:contentOffset - inStackController:stackController]; - - CGFloat ratio = 1.0f; - - switch (position) { - case SCStackViewControllerPositionTop: { - ratio = contentOffset.y / CGRectGetMinY(finalFrame); - break; - } - case SCStackViewControllerPositionLeft: { - ratio = contentOffset.x / CGRectGetMinX(finalFrame); - break; - } - case SCStackViewControllerPositionBottom: { - ratio = contentOffset.y / (CGRectGetMaxY(finalFrame) - CGRectGetHeight(stackController.view.bounds)); - break; - } - case SCStackViewControllerPositionRight: { - ratio = contentOffset.x / (CGRectGetMaxX(finalFrame) - CGRectGetWidth(stackController.view.bounds)); - break; - } - default: - break; - } - - ratio = MAX(0.0f, MIN(ratio, 1.0f)); - - viewController.view.layer.sublayerTransform = CATransform3DConcat(CATransform3DMakeScale(ratio, ratio, 1.0f), - CATransform3DMakeRotation(45.0f - 45.0f/ratio, 0.0f, 0.0f, 1.0f));; - - return frame; -} - -@end diff --git a/SCStackViewController/Layouters/SCParallaxStackLayouter.m b/SCStackViewController/Layouters/SCParallaxStackLayouter.m index 2c7fe951..ff12f10a 100644 --- a/SCStackViewController/Layouters/SCParallaxStackLayouter.m +++ b/SCStackViewController/Layouters/SCParallaxStackLayouter.m @@ -12,44 +12,66 @@ @implementation SCParallaxStackLayouter - (CGRect)currentFrameForViewController:(UIViewController *)viewController - withIndex:(NSUInteger)index - atPosition:(SCStackViewControllerPosition)position - finalFrame:(CGRect)finalFrame - contentOffset:(CGPoint)contentOffset - inStackController:(SCStackViewController *)stackController + withIndex:(NSUInteger)index + atPosition:(SCStackViewControllerPosition)position + finalFrame:(CGRect)finalFrame + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackController { - if(index == 0) { - return finalFrame; - } - - CGRect frame = viewController.view.frame; - - switch (position) { - case SCStackViewControllerPositionTop: { - CGFloat ratio = contentOffset.y / CGRectGetMinY(finalFrame); - frame.origin.y = CGRectGetMaxY(finalFrame) - CGRectGetHeight(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); - break; - } - case SCStackViewControllerPositionLeft: { - CGFloat ratio = contentOffset.x / CGRectGetMinX(finalFrame); - frame.origin.x = CGRectGetMaxX(finalFrame) - CGRectGetWidth(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); - break; - } - case SCStackViewControllerPositionBottom: { - CGFloat ratio = contentOffset.y / (CGRectGetMaxY(finalFrame) - CGRectGetHeight(stackController.view.bounds)); - frame.origin.y = (CGRectGetMinY(finalFrame) - CGRectGetHeight(finalFrame)) + CGRectGetHeight(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); - break; - } - case SCStackViewControllerPositionRight: { - CGFloat ratio = contentOffset.x / (CGRectGetMaxX(finalFrame) - CGRectGetWidth(stackController.view.bounds)); - frame.origin.x = (CGRectGetMinX(finalFrame) - CGRectGetWidth(finalFrame)) + CGRectGetWidth(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); - break; - } - default: - break; - } - - return frame; + if(self.shouldStackControllersAboveRoot && index == 0) { + + switch (position) { + case SCStackViewControllerPositionTop: + finalFrame.size.width = CGRectGetWidth(stackController.view.bounds); + break; + case SCStackViewControllerPositionLeft: + finalFrame.size.height = CGRectGetHeight(stackController.view.bounds); + break; + case SCStackViewControllerPositionBottom: + finalFrame.size.width = CGRectGetWidth(stackController.view.bounds); + break; + case SCStackViewControllerPositionRight: + finalFrame.size.height = CGRectGetHeight(stackController.view.bounds); + break; + default: + break; + } + + return finalFrame; + } + + CGRect frame = viewController.view.frame; + + switch (position) { + case SCStackViewControllerPositionTop: { + CGFloat ratio = (contentOffset.y - CGRectGetHeight(finalFrame) / 2) / (CGRectGetMinY(finalFrame) - CGRectGetHeight(finalFrame) / 2); + frame.origin.y = CGRectGetMaxY(finalFrame) - CGRectGetHeight(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); + frame.size.width = CGRectGetWidth(stackController.view.bounds); + break; + } + case SCStackViewControllerPositionLeft: { + CGFloat ratio = (contentOffset.x - CGRectGetWidth(finalFrame) / 2) / (CGRectGetMinX(finalFrame) - CGRectGetWidth(finalFrame) / 2); + frame.origin.x = CGRectGetMaxX(finalFrame) - CGRectGetWidth(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); + frame.size.height = CGRectGetHeight(stackController.view.bounds); + break; + } + case SCStackViewControllerPositionBottom: { + CGFloat ratio = (contentOffset.y + CGRectGetHeight(finalFrame) / 2) / ((CGRectGetMaxY(finalFrame) - CGRectGetHeight(stackController.view.bounds)) + CGRectGetHeight(finalFrame) / 2); + frame.origin.y = (CGRectGetMinY(finalFrame) - CGRectGetHeight(finalFrame)) + CGRectGetHeight(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); + frame.size.width = CGRectGetWidth(stackController.view.bounds); + break; + } + case SCStackViewControllerPositionRight: { + CGFloat ratio = (contentOffset.x + CGRectGetWidth(finalFrame) / 2) / ((CGRectGetMaxX(finalFrame) - CGRectGetWidth(stackController.view.bounds)) + CGRectGetWidth(finalFrame) / 2); + frame.origin.x = (CGRectGetMinX(finalFrame) - CGRectGetWidth(finalFrame)) + CGRectGetWidth(finalFrame) * MAX(0.0f, MIN(1.0f, ratio)); + frame.size.height = CGRectGetHeight(stackController.view.bounds); + break; + } + default: + break; + } + + return frame; } @end diff --git a/SCStackViewController/Layouters/SCResizingStackLayouter.h b/SCStackViewController/Layouters/SCResizingStackLayouter.h new file mode 100644 index 00000000..f3ce6b19 --- /dev/null +++ b/SCStackViewController/Layouters/SCResizingStackLayouter.h @@ -0,0 +1,13 @@ +// +// SCPlainResizingLayouter.h +// SCStackViewController +// +// Created by Stefan Ceriu on 23/01/2014. +// Copyright (c) 2014 Stefan Ceriu. All rights reserved. +// + +#import "SCStackLayouter.h" + +@interface SCResizingStackLayouter : SCStackLayouter + +@end diff --git a/SCStackViewController/Layouters/SCResizingStackLayouter.m b/SCStackViewController/Layouters/SCResizingStackLayouter.m new file mode 100644 index 00000000..b3c957c9 --- /dev/null +++ b/SCStackViewController/Layouters/SCResizingStackLayouter.m @@ -0,0 +1,29 @@ +// +// SCPlainResizingLayouter.m +// SCStackViewController +// +// Created by Stefan Ceriu on 23/01/2014. +// Copyright (c) 2014 Stefan Ceriu. All rights reserved. +// + +#import "SCResizingStackLayouter.h" + +@implementation SCResizingStackLayouter + +- (id)init +{ + if(self = [super init]) { + self.shouldStackControllersAboveRoot = YES; + } + + return self; +} + +- (CGRect)currentFrameForRootViewController:(UIViewController *)rootViewController + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackController +{ + return CGRectMake(MAX(0,contentOffset.x), MAX(0,contentOffset.y), MAX(0, CGRectGetWidth(stackController.view.bounds) - ABS(contentOffset.x)), MAX(0, CGRectGetHeight(stackController.view.bounds) - ABS(contentOffset.y))); +} + +@end diff --git a/SCStackViewController/Layouters/Reversed/SCReversedStackLayouter.h b/SCStackViewController/Layouters/SCReversedStackLayouter.h similarity index 100% rename from SCStackViewController/Layouters/Reversed/SCReversedStackLayouter.h rename to SCStackViewController/Layouters/SCReversedStackLayouter.h diff --git a/SCStackViewController/Layouters/SCReversedStackLayouter.m b/SCStackViewController/Layouters/SCReversedStackLayouter.m new file mode 100644 index 00000000..52e54b4c --- /dev/null +++ b/SCStackViewController/Layouters/SCReversedStackLayouter.m @@ -0,0 +1,121 @@ +// +// SCReversedParallaxStackLayouter.m +// SCStackViewController +// +// Created by Stefan Ceriu on 18/08/2013. +// Copyright (c) 2013 Stefan Ceriu. All rights reserved. +// + +#import "SCReversedStackLayouter.h" + +@implementation SCReversedStackLayouter +@synthesize isReversed; +@synthesize shouldStackControllersAboveRoot; + +- (CGRect)finalFrameForViewController:(UIViewController *)viewController + withIndex:(NSUInteger)index + atPosition:(SCStackViewControllerPosition)position + withinGroup:(NSArray *)viewControllers + inStackController:(SCStackViewController *)stackController +{ + CGRect finalFrame = viewController.view.frame; + switch (position) { + case SCStackViewControllerPositionTop: { + NSArray *previousViewControllers = [viewControllers subarrayWithRange:NSMakeRange(0, [viewControllers indexOfObject:viewController])]; + CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.sc_viewHeight"] floatValue]; + + finalFrame.origin.y = - totalSize + [[previousViewControllers valueForKeyPath:@"@sum.sc_viewHeight"] floatValue]; + break; + } + case SCStackViewControllerPositionLeft: { + NSArray *previousViewControllers = [viewControllers subarrayWithRange:NSMakeRange(0, [viewControllers indexOfObject:viewController])]; + CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.sc_viewWidth"] floatValue]; + + finalFrame.origin.x = - totalSize + [[previousViewControllers valueForKeyPath:@"@sum.sc_viewWidth"] floatValue]; + break; + } + case SCStackViewControllerPositionBottom: { + NSArray *previousViewControllers = [viewControllers subarrayWithRange:NSMakeRange(0, [viewControllers indexOfObject:viewController] + 1)]; + CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.sc_viewHeight"] floatValue]; + + finalFrame.origin.y = CGRectGetHeight(stackController.view.bounds) + totalSize - [[previousViewControllers valueForKeyPath:@"@sum.sc_viewHeight"] floatValue]; + break; + } + case SCStackViewControllerPositionRight: { + NSArray *previousViewControllers = [viewControllers subarrayWithRange:NSMakeRange(0, [viewControllers indexOfObject:viewController] + 1)]; + CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.sc_viewWidth"] floatValue]; + + finalFrame.origin.x = CGRectGetWidth(stackController.view.bounds) + totalSize - [[previousViewControllers valueForKeyPath:@"@sum.sc_viewWidth"] floatValue]; + break; + } + default: + break; + } + + return finalFrame; +} + +- (CGRect)currentFrameForViewController:(UIViewController *)viewController + withIndex:(NSUInteger)index + atPosition:(SCStackViewControllerPosition)position + finalFrame:(CGRect)finalFrame + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackController +{ + NSArray *viewControllers = [stackController viewControllersForPosition:position]; + + CGRect frame = finalFrame; + + switch (position) { + case SCStackViewControllerPositionTop: + { + CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.sc_viewHeight"] floatValue]; + frame.origin.y = MIN(-CGRectGetHeight(((UIViewController*)viewControllers[0]).view.frame), finalFrame.origin.y + (totalSize + contentOffset.y)); + frame.size.width = CGRectGetWidth(stackController.view.bounds); + break; + } + case SCStackViewControllerPositionLeft: + { + CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.sc_viewWidth"] floatValue]; + frame.origin.x = MIN(-CGRectGetWidth(((UIViewController*)viewControllers[0]).view.frame), finalFrame.origin.x + (totalSize + contentOffset.x)); + frame.size.height = CGRectGetHeight(stackController.view.bounds); + break; + } + case SCStackViewControllerPositionBottom: + { + CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.sc_viewHeight"] floatValue]; + frame.origin.y = MAX(CGRectGetMaxY(stackController.view.bounds), CGRectGetMinY(finalFrame) - (totalSize - contentOffset.y)); + frame.size.width = CGRectGetWidth(stackController.view.bounds); + break; + } + case SCStackViewControllerPositionRight: + { + CGFloat totalSize = [[viewControllers valueForKeyPath:@"@sum.sc_viewWidth"] floatValue]; + frame.origin.x = MAX(CGRectGetMaxX(stackController.view.bounds), CGRectGetMinX(finalFrame) - (totalSize - contentOffset.x)); + frame.size.height = CGRectGetHeight(stackController.view.bounds); + break; + } + default: + break; + } + + return frame; +} + +- (BOOL)isReversed +{ + return YES; +} + +- (CGRect)currentFrameForRootViewController:(UIViewController *)rootViewController + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackController +{ + if(self.shouldStackControllersAboveRoot) { + return CGRectMake(contentOffset.x, contentOffset.y, rootViewController.view.bounds.size.width, rootViewController.view.bounds.size.height); + } else { + return stackController.view.bounds; + } +} + +@end diff --git a/SCStackViewController/Layouters/SCSlidingStackLayouter.m b/SCStackViewController/Layouters/SCSlidingStackLayouter.m index cf81252d..02218b39 100644 --- a/SCStackViewController/Layouters/SCSlidingStackLayouter.m +++ b/SCStackViewController/Layouters/SCSlidingStackLayouter.m @@ -12,32 +12,53 @@ @implementation SCSlidingStackLayouter - (CGRect)currentFrameForViewController:(UIViewController *)viewController - withIndex:(NSUInteger)index - atPosition:(SCStackViewControllerPosition)position - finalFrame:(CGRect)finalFrame - contentOffset:(CGPoint)contentOffset - inStackController:(SCStackViewController *)stackController + withIndex:(NSUInteger)index + atPosition:(SCStackViewControllerPosition)position + finalFrame:(CGRect)finalFrame + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackController { - CGRect frame = finalFrame; - - switch (position) { - case SCStackViewControllerPositionTop: - frame.origin.y = MIN(0, MAX(CGRectGetMinY(finalFrame), contentOffset.y)); - break; - case SCStackViewControllerPositionLeft: - frame.origin.x = MIN(0, MAX(CGRectGetMinX(finalFrame), contentOffset.x)); - break; - case SCStackViewControllerPositionBottom: - frame.origin.y = MAX(0, MIN(CGRectGetMaxY(finalFrame) - CGRectGetHeight(finalFrame), CGRectGetHeight(stackController.view.bounds) - CGRectGetHeight(finalFrame) + contentOffset.y)); - break; - case SCStackViewControllerPositionRight: - frame.origin.x = MAX(0, MIN(CGRectGetMaxX(finalFrame) - CGRectGetWidth(finalFrame), CGRectGetWidth(stackController.view.bounds) - CGRectGetWidth(finalFrame) + contentOffset.x)); - break; - default: - break; - } - - return frame; + CGRect frame = finalFrame; + + switch (position) { + case SCStackViewControllerPositionTop: + frame.size.width = CGRectGetWidth(stackController.view.bounds); + break; + case SCStackViewControllerPositionLeft: + frame.size.height = CGRectGetHeight(stackController.view.bounds); + break; + case SCStackViewControllerPositionBottom: + frame.size.width = CGRectGetWidth(stackController.view.bounds); + break; + case SCStackViewControllerPositionRight: + frame.size.height = CGRectGetHeight(stackController.view.bounds); + break; + default: + break; + } + + if(self.shouldStackControllersAboveRoot && index == 0) { + return frame; + } + + switch (position) { + case SCStackViewControllerPositionTop: + frame.origin.y = MIN(finalFrame.origin.y + finalFrame.size.height, MAX(CGRectGetMinY(finalFrame), contentOffset.y)); + break; + case SCStackViewControllerPositionLeft: + frame.origin.x = MIN(finalFrame.origin.x + finalFrame.size.width, MAX(CGRectGetMinX(finalFrame), contentOffset.x)); + break; + case SCStackViewControllerPositionBottom: + frame.origin.y = MAX(finalFrame.origin.y - finalFrame.size.height, MIN(CGRectGetMaxY(finalFrame) - CGRectGetHeight(finalFrame), CGRectGetHeight(stackController.view.bounds) - CGRectGetHeight(finalFrame) + contentOffset.y)); + break; + case SCStackViewControllerPositionRight: + frame.origin.x = MAX(finalFrame.origin.x - finalFrame.size.width, MIN(CGRectGetMaxX(finalFrame) - CGRectGetWidth(finalFrame), CGRectGetWidth(stackController.view.bounds) - CGRectGetWidth(finalFrame) + contentOffset.x)); + break; + default: + break; + } + + return frame; } @end diff --git a/SCStackViewController/Layouters/SCStackLayouter.m b/SCStackViewController/Layouters/SCStackLayouter.m index 71033ccc..515e6f27 100644 --- a/SCStackViewController/Layouters/SCStackLayouter.m +++ b/SCStackViewController/Layouters/SCStackLayouter.m @@ -9,48 +9,82 @@ #import "SCStackLayouter.h" @implementation SCStackLayouter +@synthesize isReversed; +@synthesize shouldStackControllersAboveRoot; - (CGRect)finalFrameForViewController:(UIViewController *)viewController - withIndex:(NSUInteger)index - atPosition:(SCStackViewControllerPosition)position - withinGroup:(NSArray *)viewControllers - inStackController:(SCStackViewController *)stackController + withIndex:(NSUInteger)index + atPosition:(SCStackViewControllerPosition)position + withinGroup:(NSArray *)viewControllers + inStackController:(SCStackViewController *)stackController { - viewControllers = [viewControllers subarrayWithRange:NSMakeRange(0, [viewControllers indexOfObject:viewController] + 1)]; - - CGRect finalFrame = viewController.view.frame; - switch (position) { - case SCStackViewControllerPositionTop: { - finalFrame.origin.y = - [[viewControllers valueForKeyPath:@"@sum.viewHeight"] floatValue]; - break; - } - case SCStackViewControllerPositionLeft: { - finalFrame.origin.x = - [[viewControllers valueForKeyPath:@"@sum.viewWidth"] floatValue]; - break; - } - case SCStackViewControllerPositionBottom: { - finalFrame.origin.y = CGRectGetHeight(stackController.view.bounds) + [[viewControllers valueForKeyPath:@"@sum.viewHeight"] floatValue] - finalFrame.size.height; - break; - } - case SCStackViewControllerPositionRight: { - finalFrame.origin.x = CGRectGetWidth(stackController.view.bounds) + [[viewControllers valueForKeyPath:@"@sum.viewWidth"] floatValue] - finalFrame.size.width; - break; - } - default: - break; - } - - return finalFrame; + viewControllers = [viewControllers subarrayWithRange:NSMakeRange(0, [viewControllers indexOfObject:viewController] + 1)]; + + CGRect finalFrame = viewController.view.frame; + switch (position) { + case SCStackViewControllerPositionTop: { + finalFrame.origin.y = - [[viewControllers valueForKeyPath:@"@sum.sc_viewHeight"] floatValue]; + break; + } + case SCStackViewControllerPositionLeft: { + finalFrame.origin.x = - [[viewControllers valueForKeyPath:@"@sum.sc_viewWidth"] floatValue]; + break; + } + case SCStackViewControllerPositionBottom: { + finalFrame.origin.y = CGRectGetHeight(stackController.view.bounds) + [[viewControllers valueForKeyPath:@"@sum.sc_viewHeight"] floatValue] - finalFrame.size.height; + break; + } + case SCStackViewControllerPositionRight: { + finalFrame.origin.x = CGRectGetWidth(stackController.view.bounds) + [[viewControllers valueForKeyPath:@"@sum.sc_viewWidth"] floatValue] - finalFrame.size.width; + break; + } + default: + break; + } + + return finalFrame; } - (CGRect)currentFrameForViewController:(UIViewController *)viewController - withIndex:(NSUInteger)index - atPosition:(SCStackViewControllerPosition)position - finalFrame:(CGRect)finalFrame - contentOffset:(CGPoint)contentOffset - inStackController:(SCStackViewController *)stackController + withIndex:(NSUInteger)index + atPosition:(SCStackViewControllerPosition)position + finalFrame:(CGRect)finalFrame + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackController { - return finalFrame; + switch (position) { + case SCStackViewControllerPositionTop: + finalFrame.size.width = CGRectGetWidth(stackController.view.bounds); + finalFrame.size.height = CGRectGetHeight(viewController.view.bounds); + break; + case SCStackViewControllerPositionLeft: + finalFrame.size.height = CGRectGetHeight(stackController.view.bounds); + finalFrame.size.width = CGRectGetWidth(viewController.view.bounds); + break; + case SCStackViewControllerPositionBottom: + finalFrame.size.width = CGRectGetWidth(stackController.view.bounds); + finalFrame.size.height = CGRectGetHeight(viewController.view.bounds); + break; + case SCStackViewControllerPositionRight: + finalFrame.size.height = CGRectGetHeight(stackController.view.bounds); + finalFrame.size.width = CGRectGetWidth(viewController.view.bounds); + break; + default: + break; + } + + return finalFrame; +} + +- (CGRect)currentFrameForRootViewController:(UIViewController *)rootViewController + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackViewController +{ + if(self.shouldStackControllersAboveRoot) { + return CGRectMake(contentOffset.x, contentOffset.y, rootViewController.view.bounds.size.width, rootViewController.view.bounds.size.height); + } else { + return stackViewController.view.bounds; + } } @end diff --git a/SCStackViewController/Layouters/SCStackLayouterProtocol.h b/SCStackViewController/Layouters/SCStackLayouterProtocol.h index a3f3d1f9..cfe4b347 100644 --- a/SCStackViewController/Layouters/SCStackLayouterProtocol.h +++ b/SCStackViewController/Layouters/SCStackLayouterProtocol.h @@ -8,23 +8,115 @@ #import "SCStackViewController.h" +/** An object adopting the SCStackLayouter protocol is responsible for returning + * the itermediate and final frames for the Stack's children when called. They + * have access the the actual children so that they can customize the navigation + * effects at each point of the transition. + */ + @protocol SCStackLayouterProtocol +/** Returns the final frame for the given view controller + * + * @param viewController The view controller for which to calculate the frame + * @param index The index of the view controller in the Stack's children array + * @param position The position in the stack + * @param viewControllers The full children array for the given position + * @param stackViewController The calling StackViewController + * + * @return The frame for the viewController's view + * + */ - (CGRect)finalFrameForViewController:(UIViewController *)viewController - withIndex:(NSUInteger)index - atPosition:(SCStackViewControllerPosition)position - withinGroup:(NSArray *)viewControllers - inStackController:(SCStackViewController *)stackController; + withIndex:(NSUInteger)index + atPosition:(SCStackViewControllerPosition)position + withinGroup:(NSArray *)viewControllers + inStackController:(SCStackViewController *)stackViewController; +/** Returns the intermediate frame for the given view controller and current + * offset + * + * @param viewController The view controller for which to calculate the frame + * @param index The index of the view controller in the Stack's children array + * @param position The position in the stack + * @param finalFrame previously calculate final frame for this view controller + * @param contentOffset current offset in the Stack's scrollView + * @param stackViewController The calling StackViewController + * + * @return The frame for the viewController's view + * + */ - (CGRect)currentFrameForViewController:(UIViewController *)viewController - withIndex:(NSUInteger)index - atPosition:(SCStackViewControllerPosition)position - finalFrame:(CGRect)finalFrame - contentOffset:(CGPoint)contentOffset - inStackController:(SCStackViewController *)stackController; + withIndex:(NSUInteger)index + atPosition:(SCStackViewControllerPosition)position + finalFrame:(CGRect)finalFrame + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackViewController; + @optional -- (BOOL)isReversed; +/** Returns the view controller sublayer transformation that should be used + * for the currnet stack offset + * + * @param viewController The view controller for which to calculate the frame + * @param index The index of the view controller in the Stack's children array + * @param position The position in the stack + * @param stackViewController The calling StackViewController + * + * @return The sublayer transformation to be applied + * + */ +- (CATransform3D)sublayerTransformForViewController:(UIViewController *)viewController + withIndex:(NSUInteger)index + atPosition:(SCStackViewControllerPosition)position + finalFrame:(CGRect)finalFrame + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackViewController; + + +/** Returns the rootViewController's intermediate frame for + * the given offset + * + * @param rootViewController the stack's root view controller + * @param contentOffset current offset in the Stack's scrollView + * @param stackViewController The calling StackViewController + * + * @return The frame for the rootViewController's view + * + */ +- (CGRect)currentFrameForRootViewController:(UIViewController *)rootViewController + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackViewController; + +/** Returns the root view controller sublayer transformation that should be used + * for the currnet stack offset + * + * @param rootViewController the stack's root view controller + * @param contentOffset current offset in the Stack's scrollView + * @param stackViewController The calling StackViewController + * + * @return The sublayer transformation to be applied + * + */ +- (CATransform3D)sublayerTransformForRootViewController:(UIViewController *)rootViewController + contentOffset:(CGPoint)contentOffset + inStackController:(SCStackViewController *)stackViewController; + + +/** + * @return BOOL value that controls whether this layouter reverses the + * arrangement the children (from the bounds of the screen towards the rootView + * and not the other way around) + */ +@property (nonatomic, assign) BOOL isReversed; + + +/** + * @return BOOL value that controls whether this layouter requires the + * controllers to be stacked on top of the root view controller + */ +@property (nonatomic, assign) BOOL shouldStackControllersAboveRoot; + @end diff --git a/SCStackViewController/SCStackNavigationStep.h b/SCStackViewController/SCStackNavigationStep.h new file mode 100644 index 00000000..61634287 --- /dev/null +++ b/SCStackViewController/SCStackNavigationStep.h @@ -0,0 +1,41 @@ +// +// SCStackNavigationStep.h +// SCStackViewController +// +// Created by Stefan Ceriu on 15/01/2014. +// Copyright (c) 2014 Stefan Ceriu. All rights reserved. +// + +/** A stack navigation step defines a relative position in a view + * controller at which the scrolling should stop when reached. + * I works as both a pagination point and a navigation contraint (the + * scroll view bounces on it) + * + * It is initialized with a percentage which will be transform into an + * actual offset based on the view controller's frame. + */ + +@import Foundation; +@import CoreGraphics; + +typedef NS_ENUM(NSUInteger, SCStackNavigationStepBlockType) { + SCStackNavigationStepBlockTypeNone, + SCStackNavigationStepBlockTypeForward, + SCStackNavigationStepBlockTypeReverse +}; + +@interface SCStackNavigationStep : NSObject + +@property (nonatomic, readonly) CGFloat percentage; + +@property (nonatomic, readonly) SCStackNavigationStepBlockType blockType; + ++ (instancetype)navigationStepWithPercentage:(CGFloat)percentage; + ++ (instancetype)navigationStepWithPercentage:(CGFloat)percentage blockType:(SCStackNavigationStepBlockType)blockType; + +- (instancetype)initWithPercentage:(CGFloat)percentage; + +- (instancetype)initWithPercentage:(CGFloat)percentage blockType:(SCStackNavigationStepBlockType)blockType; + +@end diff --git a/SCStackViewController/SCStackNavigationStep.m b/SCStackViewController/SCStackNavigationStep.m new file mode 100644 index 00000000..b852131b --- /dev/null +++ b/SCStackViewController/SCStackNavigationStep.m @@ -0,0 +1,48 @@ +// +// SCStackNavigationStep.m +// SCStackViewController +// +// Created by Stefan Ceriu on 15/01/2014. +// Copyright (c) 2014 Stefan Ceriu. All rights reserved. +// + +#import "SCStackNavigationStep.h" + +@interface SCStackNavigationStep () + +@property (nonatomic, assign) CGFloat percentage; + +@end + +@implementation SCStackNavigationStep + ++ (instancetype)navigationStepWithPercentage:(CGFloat)percentage +{ + return [[SCStackNavigationStep alloc] initWithPercentage:percentage]; +} + ++ (instancetype)navigationStepWithPercentage:(CGFloat)percentage blockType:(SCStackNavigationStepBlockType)blockType +{ + return [[SCStackNavigationStep alloc] initWithPercentage:percentage blockType:blockType]; +} + +- (instancetype)initWithPercentage:(CGFloat)percentage +{ + if(self = [self initWithPercentage:percentage blockType:SCStackNavigationStepBlockTypeNone]) { + + } + + return self; +} + +- (instancetype)initWithPercentage:(CGFloat)percentage blockType:(SCStackNavigationStepBlockType)blockType +{ + if(self = [super init]) { + _percentage = percentage; + _blockType = blockType; + } + + return self; +} + +@end diff --git a/SCStackViewController/SCStackViewController.h b/SCStackViewController/SCStackViewController.h index db85e07c..678a2183 100644 --- a/SCStackViewController/SCStackViewController.h +++ b/SCStackViewController/SCStackViewController.h @@ -6,44 +6,389 @@ // Copyright (c) 2013 Stefan Ceriu. All rights reserved. // -typedef enum { - SCStackViewControllerPositionTop, - SCStackViewControllerPositionLeft, - SCStackViewControllerPositionBottom, - SCStackViewControllerPositionRight -} SCStackViewControllerPosition; +@import UIKit; + +/** Sides on which view controllers may be stacked */ +typedef NS_ENUM(NSUInteger, SCStackViewControllerPosition) { + SCStackViewControllerPositionTop, + SCStackViewControllerPositionLeft, + SCStackViewControllerPositionBottom, + SCStackViewControllerPositionRight +}; + + +/** Navigation contraint types that can be used used when continuous + * navigation is disabled + */ +typedef NS_OPTIONS(NSUInteger, SCStackViewControllerNavigationContraintType) { + SCStackViewControllerNavigationContraintTypeForward = 1 << 0, /** Scroll view bounces on steps only when unfolding the stack*/ + SCStackViewControllerNavigationContraintTypeReverse = 1 << 1 /** Scroll view bounces on steps only when folding the stack*/ +}; + @protocol SCStackLayouterProtocol; +@protocol SCEasingFunctionProtocol; + +@protocol SCStackViewControllerDelegate; + +@class SCScrollView; + +@class SCStackNavigationStep; + +/** SCStackViewController is a container view controller which allows you to + * stack other view controllers on the top/left/bottom/right of the root and + * build custom transitions between them while providing correct physics and + * appearance calls. + * + * @warning One should not attempt to use the stack with both vertical and + * horizontal layouters at the same time. The behavior is undefined. + */ @interface SCStackViewController : UIViewController -@property (nonatomic, strong) UIBezierPath *touchRefusalArea; -@property (nonatomic, assign) BOOL bounces; +/**----------------------------------------------------------------------------- + * @name Properties + * ----------------------------------------------------------------------------- + */ + +/** The stack's root view controller. */ +@property (nonatomic, strong, nonnull, readonly) UIViewController *rootViewController; + + +/** Stack Delegate */ +@property (nonatomic, nullable, weak) IBOutlet id delegate; + + +/** A Boolean value that determines whether paging is enabled for the Stack's + * scrollView. + * + * Default value is set to true + */ +@property (nonatomic, assign) BOOL pagingEnabled; + + +/** A Boolean value that determines whether the user can freely scroll between + * pages + * + * When set to true the Stack's scrollView bounces on every page. Navigating + * through more than 1 page will require multiple swipes. + * + * Default value is set to false + */ +@property (nonatomic, assign) BOOL continuousNavigationEnabled; + + +/** A bitmask determining whether the stack's scroll bounces when folding and + * unfolding view controllers + * + * Only used when continuous navigation is disabled + * + * Defaults to Forward and Reverse + */ +@property (nonatomic, assign) SCStackViewControllerNavigationContraintType navigationContaintType; + + +/** Timing function used in push/pop/navigate operations + * + * Default value is set to SCEasingFunctionTypeSineEaseInOut + */ +@property (nonatomic, strong, nonnull) id easingFunction; + + +/** Animation duration for push/pop/navigate operations + * + * Default value is set to 0.25f + */ +@property (nonatomic, assign) NSTimeInterval animationDuration; + + +/** The stack's internal scroll view */ +@property (nonatomic, strong, nonnull, readonly) SCScrollView *scrollView; + + +/** + * @return Blocks interaction while animations are running + * + */ +@property (nonatomic, assign) BOOL shouldBlockInteractionWhileAnimating; + + +/** Whether the stack's view is visible or not */ +@property (nonatomic, readonly) BOOL visible; + + +/**----------------------------------------------------------------------------- + * @name Initializing the Stack + * ----------------------------------------------------------------------------- + */ + +/** Creates and returns a new SCStackVieController + * + * @param rootViewController The view controller which provides the view for the + * center area of the Stack. + */ +- (nullable instancetype)initWithRootViewController:(nonnull UIViewController *)rootViewController; + + +/** Registers a layouter for the given position + * + * The Stack uses the layouters to get view frames and calculate contentInsets, + * pagination etc. + * + * @param layouter Object that adopts the SCStackLayouterProtocol and provides + * final and intermediate view frames. + * @param position The SCStackViewControllerPosition for which the given + * layouter is responsible + */ +- (void)registerLayouter:(nonnull id)layouter + forPosition:(SCStackViewControllerPosition)position __attribute((deprecated(" Use -registerLayouter:forPosition:animated: instead."))); + + +/** Registers a layouter for the given position + * + * The Stack uses the layouters to get view frames and calculate contentInsets, + * pagination etc. + * + * @param layouter Object that adopts the SCStackLayouterProtocol and provides + * final and intermediate view frames. + * @param position The SCStackViewControllerPosition for which the given + * layouter is responsible + * @param animated Controls whether the change will be animated + */ +- (void)registerLayouter:(nonnull id)layouter + forPosition:(SCStackViewControllerPosition)position + animated:(BOOL)animated; + +/** Retrieve the layouter for the given position + * + * @param position The SCStackViewControllerPosition to fetch the layouter for + */ +- (nullable id)layouterForPosition:(SCStackViewControllerPosition)position; + +/**----------------------------------------------------------------------------- + * @name Performing an Operation + * ----------------------------------------------------------------------------- + */ -- (instancetype)initWithRootViewController:(UIViewController *)rootViewController; +/** Pushes a new view controller on the stack at the given position + * + * During the animation the layouters will be called and effects will be used + * + * @param viewController The view controller to be pushed + * @param position The position at which to push the view cotroller + * @param unfold Controls whether the Stack should navigate to the newly pushed + * view controller + * @param animated Controls whether the unfold will be animated + * @param completion Completion block called when the push is done + */ +- (void)pushViewController:(nonnull UIViewController *)viewController + atPosition:(SCStackViewControllerPosition)position + unfold:(BOOL)unfold + animated:(BOOL)animated + completion:(nullable void(^)(void))completion; -- (void)registerLayouter:(id)layouter - forPosition:(SCStackViewControllerPosition)position; -- (void)pushViewController:(UIViewController *)viewController - atPosition:(SCStackViewControllerPosition)position - unfold:(BOOL)unfold - animated:(BOOL)animated - completion:(void(^)(BOOL finished))completion; +- (void)registerNavigationSteps:(nullable NSArray *)navigationSteps forViewController:(nonnull UIViewController *)viewController; + +/** Pops the last pushed view controller from the given position + * + * During the animation the layouters will be called and effects will be used + * + * @param position The position from which to pop the view cotroller + * @param animated Controls whether the pop will be animated + * @param completion Completion block called when the pop is done + */ - (void)popViewControllerAtPosition:(SCStackViewControllerPosition)position - animated:(BOOL)animated - completion:(void(^)(BOOL finished))completion; + animated:(BOOL)animated + completion:(nullable void(^)(void))completion; + +/** Pops the given view controller + * + * @param viewController The view controller to top + * @param animated Controls whether the pop will be animated + * @param completion Completion block called when the pop is done + */ +- (void)popViewController:(nonnull UIViewController *)viewController + animated:(BOOL)animated + completion:(nullable void(^)(void))completion; + +/** Pops all the view controllers from the given position + * + * During the animation the layouters will be called and effects will be used + * + * @param position The position from which to pop the view cotrollers + * @param animated Controls whether the pop will be animated + * @param completion Completion block called when the pop is done + */ - (void)popToRootViewControllerFromPosition:(SCStackViewControllerPosition)position - animated:(BOOL)animated - completion:(void(^)(BOOL finished))completion; + animated:(BOOL)animated + completion:(nullable void(^)(void))completion; + + +/** Unfolds to the given view controller + * + * The root view controller may be passed in order to hide all the side views. + * During the animation the layouters will be called and effects will be used + * + * @param viewController The view controller to displayed + * @param animated Controls whether the navigation will be animated + * @param completion Completion block called when the action is finished + */ +- (void)navigateToViewController:(nonnull UIViewController *)viewController + animated:(BOOL)animated + completion:(nullable void(^)(void))completion; + + +/** Unfolds to the given step + * + * The root view controller may be passed in order to hide all the side views. + * During the animation the layouters will be called and effects will be used + * + * @param step The step to be displayed + * @param viewController The view controller owning the step + * @param animated Controls whether the navigation will be animated + * @param completion Completion block called when the action is finished + */ +- (void)navigateToStep:(nonnull SCStackNavigationStep *)step + inViewController:(nonnull UIViewController *)viewController + animated:(BOOL)animated + completion:(nullable void(^)(void))completion; + +/**----------------------------------------------------------------------------- + * @name Querying the Stack state + * ----------------------------------------------------------------------------- + */ + + +/** + * @return An NSArray of view controllers that the Stack holds for the given + * position + * @param position The position for which to return the view controllers array + */ +- (nonnull NSArray *)viewControllersForPosition:(SCStackViewControllerPosition)position; + +/** + * @return An NSArray of view controllers that are currently visible. Includes + * root view controller. + * + */ +- (nonnull NSArray *)visibleViewControllers; + + +/** + * @return BOOL value representing the visibility of the passed view controller + * @param viewController The view controller for which to check the + * visibility + * + * A view controller is visible when any part of it is visible (within the + * Stack's scrollView bounds and not covered by any other view) + * + */ +- (BOOL)isViewControllerVisible:(nonnull UIViewController *)viewController; + +/** + * @return Float value representing the visible percentage + * @param viewController The view controller for which to fetch the + * visible percentage + * + * A view controller is visible when any part of it is visible (within the + * Stack's scrollView bounds and not covered by any other view) + * + * Ranges from 0.0f to 1.0f + */ +- (CGFloat)visiblePercentageForViewController:(nonnull UIViewController *)viewController; + +@end + + +/**----------------------------------------------------------------------------- + * @name Stack delegate + * ----------------------------------------------------------------------------- + */ + +@protocol SCStackViewControllerDelegate + +@optional + +/** Delegate method that the Stack calls when a view controller becomes visible + * + * @param stackViewController The calling StackViewController + * @param controller The view controller that became visible + * @param position The position where the view controller resides + * + * A view controller is visible when any part of it is visible (within the + * Stack's scrollView bounds and not covered by any other view) + * + */ +- (void)stackViewController:(nonnull SCStackViewController *)stackViewController + didShowViewController:(nonnull UIViewController *)controller + position:(SCStackViewControllerPosition)position; + + +/** Delegate method that the Stack calls when a view controller is hidden + * @param stackViewController The calling StackViewController + * @param controller The view controller that was hidden + * @param position The position where the view controller resides + * + * A view controller is hidden when it view's frame rests outside the Stack's + * scrollView bounds or when it is fully overlapped by other views + * + */ +- (void)stackViewController:(nonnull SCStackViewController *)stackViewController + didHideViewController:(nonnull UIViewController *)controller + position:(SCStackViewControllerPosition)position; + + +/** Delegate method that the Stack calls when its scrollView scrolls + * @param stackViewController The calling StackViewController + * @param offset The current offset in the Stack's scrollView + * + */ +- (void)stackViewController:(nonnull SCStackViewController *)stackViewController + didNavigateToOffset:(CGPoint)offset; + + +/** Delegate method that the Stack calls when its scrollView rests on a predefined step + * @param stackViewController The calling StackViewController + * @param step The step it stopped on + * @param controller The view controller that own the step + * + */ +- (void)stackViewController:(nonnull SCStackViewController *)stackViewController + didNavigateToStep:(nonnull SCStackNavigationStep *)step + inViewController:(nonnull UIViewController *)controller; + +@end + + + +/**----------------------------------------------------------------------------- + * @name UIViewController Additions + * ----------------------------------------------------------------------------- + */ + +@interface UIViewController (SCStackViewController) + + +/** + * @return First SCStackViewController instance found by looking up the + * responder chain + */ +- (nonnull SCStackViewController *)sc_stackViewController; + + +/** + * @return ViewController's view width + */ +- (CGFloat)sc_viewWidth; -- (void)navigateToViewController:(UIViewController *)viewController - animated:(BOOL)animated - completion:(void(^)(BOOL finished))completion; -- (NSArray *)viewControllersForPosition:(SCStackViewControllerPosition)position; +/** + * @return ViewController's view height + */ +- (CGFloat)sc_viewHeight; -@end \ No newline at end of file +@end diff --git a/SCStackViewController/SCStackViewController.m b/SCStackViewController/SCStackViewController.m index 10c34ca4..f5f299ef 100644 --- a/SCStackViewController/SCStackViewController.m +++ b/SCStackViewController/SCStackViewController.m @@ -7,491 +7,1389 @@ // #import "SCStackViewController.h" -#import "SCStackLayouterProtocol.h" - -#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending) +#import "SCStackViewControllerView.h" -static const CGFloat kDefaultAnimationDuration = 0.25f; +#import "SCScrollView.h" +#import "SCEasingFunction.h" +#import "SCStackNavigationStep.h" +#import "SCStackLayouterProtocol.h" -@interface SCStackScrollView : UIScrollView +@interface SCStackViewController () -@property (nonatomic, strong) UIBezierPath *touchRefusalArea; +@property (nonatomic, strong) IBOutlet UIViewController *rootViewController; -@end +@property (nonatomic, strong) SCScrollView *scrollView; -@implementation SCStackScrollView +@property (nonatomic, strong) NSDictionary *loadedControllers; +@property (nonatomic, strong) NSMutableArray *visibleControllers; -- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch -{ - if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) { - CGPoint touchPoint = [touch locationInView:self]; - return ![self.touchRefusalArea containsPoint:touchPoint]; - } - - return YES; -} - -@end +@property (nonatomic, strong) NSMutableDictionary *layouters; +@property (nonatomic, strong) NSMutableDictionary *finalFrames; -@interface SCStackViewController () +@property (nonatomic, strong) NSMutableDictionary *navigationSteps; +@property (nonatomic, strong) NSMutableDictionary *previousNavigationSteps; +@property (nonatomic, strong) NSMutableDictionary *stepsForOffsets; -@property (nonatomic, strong) UIViewController *rootViewController; +@property (nonatomic, strong) NSMutableDictionary *visiblePercentages; -@property (nonatomic, strong) SCStackScrollView *scrollView; +@property (nonatomic, assign) BOOL isViewVisible; +@property (nonatomic, assign) BOOL isRootViewControllerVisible; -@property (nonatomic, strong) NSMutableDictionary *layouters; -@property (nonatomic, strong) NSMutableDictionary *finalFrames; +@property (nonatomic, assign) BOOL didIgnoreNavigationalConstraints; -@property (nonatomic, strong) NSDictionary *viewControllers; -@property (nonatomic, strong) NSMutableArray *visibleViewControllers; +@property (nonatomic, strong) id lastUsedLayouter; @end @implementation SCStackViewController -@dynamic bounces; -@dynamic touchRefusalArea; -#pragma mark - Constructors +- (void)dealloc +{ + [self.scrollView setDelegate:nil]; +} - (instancetype)initWithRootViewController:(UIViewController *)rootViewController { - if(self = [super init]) { - self.rootViewController = rootViewController; - } - - return self; + if(self = [super init]) { + self.rootViewController = rootViewController; + [self setup]; + } + + return self; +} + +- (void)awakeFromNib +{ + [super awakeFromNib]; + [self setup]; +} + +- (void)setup +{ + self.loadedControllers = (@{@(SCStackViewControllerPositionTop) : [NSMutableArray array], + @(SCStackViewControllerPositionLeft) : [NSMutableArray array], + @(SCStackViewControllerPositionBottom): [NSMutableArray array], + @(SCStackViewControllerPositionRight) : [NSMutableArray array]}); + + self.visibleControllers = [NSMutableArray array]; + + self.layouters = [NSMutableDictionary dictionary]; + self.finalFrames = [NSMutableDictionary dictionary]; + self.navigationSteps = [NSMutableDictionary dictionary]; + self.previousNavigationSteps = [NSMutableDictionary dictionary]; + self.stepsForOffsets = [NSMutableDictionary dictionary]; + self.visiblePercentages = [NSMutableDictionary dictionary]; + + self.easingFunction = [SCEasingFunction easingFunctionWithType:SCEasingFunctionTypeSineEaseInOut]; + self.animationDuration = 0.25f; + + self.navigationContaintType = SCStackViewControllerNavigationContraintTypeForward | SCStackViewControllerNavigationContraintTypeReverse; + + self.scrollView = [[SCScrollView alloc] init]; + [self.scrollView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; + [self.scrollView setDirectionalLockEnabled:YES]; + [self.scrollView setDecelerationRate:UIScrollViewDecelerationRateFast]; + [self.scrollView setDelegate:self]; + + [self setPagingEnabled:YES]; + [self.scrollView setShowsHorizontalScrollIndicator:NO]; + [self.scrollView setShowsVerticalScrollIndicator:NO]; + + [self.scrollView setContentOffset:CGPointZero]; // Overrides whatever _adjustContentOffsetIfNecessary might do } #pragma mark - Public Methods - (void)registerLayouter:(id)layouter - forPosition:(SCStackViewControllerPosition)position + forPosition:(SCStackViewControllerPosition)position + animated:(BOOL)animated +{ + [self.layouters setObject:layouter forKey:@(position)]; + + if (!self.isViewLoaded) { + return; + } + + if(animated) { + [UIView animateWithDuration:self.animationDuration animations:^{ + [self updateFramesAndTriggerAppearanceCallbacks]; + }]; + } else { + [self updateFramesAndTriggerAppearanceCallbacks]; + } +} + +- (void)registerLayouter:(id)layouter + forPosition:(SCStackViewControllerPosition)position +{ + [self registerLayouter:layouter forPosition:position animated:YES]; +} + +- (id)layouterForPosition:(SCStackViewControllerPosition)position { - [self.layouters setObject:layouter forKey:@(position)]; + return self.layouters[@(position)]; } +- (void)registerNavigationSteps:(NSArray *)navigationSteps forViewController:(UIViewController *)viewController +{ + if(navigationSteps == nil) { + [self.navigationSteps removeObjectForKey:@([viewController hash])]; + return; + } + + navigationSteps = [navigationSteps sortedArrayUsingComparator:^NSComparisonResult(SCStackNavigationStep *obj1, SCStackNavigationStep *obj2) { + return obj1.percentage > obj2.percentage; + }]; + + [self.navigationSteps setObject:navigationSteps forKey:@([viewController hash])]; +} + + - (void)pushViewController:(UIViewController *)viewController - atPosition:(SCStackViewControllerPosition)position - unfold:(BOOL)unfold - animated:(BOOL)animated - completion:(void(^)(BOOL finished))completion + atPosition:(SCStackViewControllerPosition)position + unfold:(BOOL)unfold + animated:(BOOL)animated + completion:(void(^)(void))completion { - NSAssert(viewController != nil, @"Trying to push nil view controller"); - NSAssert(![[self.viewControllers.allValues valueForKeyPath:@"@unionOfArrays.self"] containsObject:viewController], @"Trying to push an already pushed view controller"); - - NSMutableArray *viewControllers = self.viewControllers[@(position)]; - [viewControllers addObject:viewController]; - - [self updateFinalFramesForPosition:position]; - - [self addChildViewController:viewController]; - viewController.view.frame = [self.finalFrames[@(viewController.hash)] CGRectValue]; - [self.scrollView insertSubview:viewController.view atIndex:0]; - [viewController didMoveToParentViewController:self]; - - [self updateBounds]; - - BOOL isReversed = NO; - if([self.layouters[@(position)] respondsToSelector:@selector(isReversed)]) { - isReversed = [self.layouters[@(position)] isReversed]; - } - - if(unfold) { - [UIView animateWithDuration:(animated ? kDefaultAnimationDuration : 0.0f) - delay:0.0f - options:UIViewAnimationOptionCurveEaseInOut - animations:^{ - [self.scrollView setContentOffset:[self offsetForPosition:position] animated:(isReversed ? NO : animated)]; - } completion:completion]; - } else if(completion) { - completion(YES); - } + if(self.scrollView.isRunningAnimation && self.shouldBlockInteractionWhileAnimating) { + return; + } + + NSAssert(viewController != nil, @"Trying to push a nil view controller"); + + if([[self.loadedControllers.allValues valueForKeyPath:@"@unionOfArrays.self"] containsObject:viewController]) { + NSLog(@"Trying to push an already pushed view controller"); + + if(unfold) { + [self navigateToViewController:viewController animated:animated completion:completion]; + } else if(completion) { + completion(); + } + return; + } + + NSMutableArray *viewControllers = self.loadedControllers[@(position)]; + [viewControllers addObject:viewController]; + + [self updateFinalFramesForPosition:position]; + + id layouter = self.layouters[@(position)]; + + viewController.view.frame = [self.finalFrames[@(viewController.hash)] CGRectValue]; + + BOOL shouldStackAboveRoot = NO; + if([layouter respondsToSelector:@selector(shouldStackControllersAboveRoot)]) { + shouldStackAboveRoot = [layouter shouldStackControllersAboveRoot]; + } + + [viewController willMoveToParentViewController:self]; + if(shouldStackAboveRoot) { + [self.scrollView insertSubview:viewController.view aboveSubview:self.rootViewController.view]; + } else { + [self.scrollView insertSubview:viewController.view atIndex:0]; + } + + [self addChildViewController:viewController]; + [viewController didMoveToParentViewController:self]; + + [self updateBoundsIgnoringNavigationContraints]; + + __weak typeof(self) weakSelf = self; + if(unfold) { + + void(^cleanup)(void) = ^{ + [weakSelf updateBoundsUsingNavigationContraints]; + if(completion) { + completion(); + } + }; + + if(animated) { + [self.scrollView setContentOffset:[self maximumInsetForPosition:position] easingFunction:self.easingFunction duration:self.animationDuration completion:cleanup]; + } else { + [self.scrollView setContentOffset:[self maximumInsetForPosition:position]]; + cleanup(); + } + + } else { + + [self updateBoundsUsingNavigationContraints]; + if(completion) { + completion(); + } + } } - (void)popViewControllerAtPosition:(SCStackViewControllerPosition)position - animated:(BOOL)animated - completion:(void(^)(BOOL finished))completion + animated:(BOOL)animated + completion:(void(^)(void))completion { - UIViewController *lastViewController = [self.viewControllers[@(position)] lastObject]; - [self.viewControllers[@(position)] removeObject:lastViewController]; - [self updateFinalFramesForPosition:position]; - - [UIView animateWithDuration:(animated ? kDefaultAnimationDuration : 0.0f) - delay:0.0f - options:UIViewAnimationOptionCurveEaseInOut - animations:^{ - [self scrollViewDidScroll:self.scrollView]; - [self updateBounds]; - } completion:^(BOOL finished) { - - if([self.visibleViewControllers containsObject:lastViewController]) { - [lastViewController beginAppearanceTransition:NO animated:animated]; - } - - [lastViewController willMoveToParentViewController:nil]; - [lastViewController.view removeFromSuperview]; - [lastViewController removeFromParentViewController]; - - if([self.visibleViewControllers containsObject:lastViewController]) { - [lastViewController endAppearanceTransition]; - [self.visibleViewControllers removeObject:lastViewController]; - } - - if(completion) { - completion(finished); - } - }]; + if(self.scrollView.isRunningAnimation && self.shouldBlockInteractionWhileAnimating) { + return; + } + + UIViewController *lastViewController = [self.loadedControllers[@(position)] lastObject]; + + if(lastViewController == nil) { + if(completion) { + completion(); + } + + return; + } + + [self popViewController:lastViewController + animated:animated + completion:completion]; +} + +- (void)popViewController:(UIViewController *)viewController + animated:(BOOL)animated + completion:(void(^)(void))completion +{ + if(self.scrollView.isRunningAnimation && self.shouldBlockInteractionWhileAnimating) { + return; + } + + if(viewController == nil) { + return; + } + + SCStackViewControllerPosition position = [self positionForViewController:viewController]; + + UIViewController *previousViewController; + if([self.loadedControllers[@(position)] indexOfObject:viewController] == 0) { + previousViewController = self.rootViewController; + } else { + previousViewController = [self.loadedControllers[@(position)] objectAtIndex:[self.loadedControllers[@(position)] indexOfObject:viewController] - 1]; + } + + void(^cleanup)(void) = ^{ + [self.loadedControllers[@(position)] removeObject:viewController]; + [self.finalFrames removeObjectForKey:@([viewController hash])]; + [self.visiblePercentages removeObjectForKey:@([viewController hash])]; + [self updateFinalFramesForPosition:position]; + [self updateBoundsIgnoringNavigationContraints]; + + if([self.visibleControllers containsObject:viewController]) { + [viewController beginAppearanceTransition:NO animated:animated]; + } + + [viewController willMoveToParentViewController:nil]; + [viewController.view removeFromSuperview]; + [viewController removeFromParentViewController]; + + if([self.visibleControllers containsObject:viewController]) { + [viewController endAppearanceTransition]; + [self.visibleControllers removeObject:viewController]; + } + + [self updateBoundsUsingNavigationContraints]; + + if(completion) { + completion(); + } + }; + + if([self.visibleControllers containsObject:viewController]) { + [self navigateToViewController:previousViewController animated:animated completion:cleanup]; + } else { + cleanup(); + } } - (void)popToRootViewControllerFromPosition:(SCStackViewControllerPosition)position - animated:(BOOL)animated - completion:(void(^)(BOOL finished))completion + animated:(BOOL)animated + completion:(void(^)(void))completion { - NSArray *controllersToBeRemoved = [self.viewControllers[@(position)] copy]; - [self.viewControllers[@(position)] removeAllObjects]; - [self updateFinalFramesForPosition:position]; - - [UIView animateWithDuration:(animated ? kDefaultAnimationDuration : 0.0f) - delay:0.0f - options:UIViewAnimationOptionCurveEaseInOut - animations:^{ - [self scrollViewDidScroll:self.scrollView]; - [self updateBounds]; - } completion:^(BOOL finished) { - for(UIViewController *controller in controllersToBeRemoved) { - - if([self.visibleViewControllers containsObject:controller]) { - [controller beginAppearanceTransition:NO animated:animated]; - } - - [controller willMoveToParentViewController:nil]; - [controller.view removeFromSuperview]; - [controller removeFromParentViewController]; - - if([self.visibleViewControllers containsObject:controller]) { - [controller endAppearanceTransition]; - [self.visibleViewControllers removeObject:controller]; - } - if(completion) { - completion(finished); - } - } - }]; + [self navigateToViewController:self.rootViewController + animated:animated + completion:^{ + NSMutableArray *viewControllers = self.loadedControllers[@(position)]; + + for(int i=0; viewControllers.count; i++) { + [self popViewControllerAtPosition:position animated:NO completion:nil]; + } + + [viewControllers removeAllObjects]; + + if(completion) { + completion(); + } + }]; } - (void)navigateToViewController:(UIViewController *)viewController - animated:(BOOL)animated - completion:(void(^)(BOOL finished))completion; + animated:(BOOL)animated + completion:(void(^)(void))completion { - CGRect finalFrame = [[self.finalFrames objectForKey:@(viewController.hash)] CGRectValue]; - [self.scrollView setContentOffset:finalFrame.origin animated:YES]; - - - SCStackViewControllerPosition controllerPosition = -1; - for(SCStackViewControllerPosition position = SCStackViewControllerPositionTop; position <=SCStackViewControllerPositionRight; position++) { + [self navigateToStep:[SCStackNavigationStep navigationStepWithPercentage:1.0f] inViewController:viewController animated:animated completion:completion]; +} + +- (void)navigateToStep:(SCStackNavigationStep *)step + inViewController:(UIViewController *)viewController + animated:(BOOL)animated + completion:(void(^)(void))completion +{ + if(self.scrollView.isRunningAnimation && self.shouldBlockInteractionWhileAnimating) { + return; + } + + CGPoint offset = CGPointZero; + CGRect finalFrame = CGRectZero; + + [self updateBoundsIgnoringNavigationContraints]; + + // Save the original navigation steps and just use the given one + if(self.previousNavigationSteps[@([viewController hash])] == nil) { + NSArray *previousSteps = self.navigationSteps[@([viewController hash])]; + if(previousSteps) { + self.previousNavigationSteps[@([viewController hash])] = previousSteps; + } + } + + [self registerNavigationSteps:(step ? @[step] : nil) forViewController:viewController]; + + if(![viewController isEqual:self.rootViewController]) { + + finalFrame = [[self.finalFrames objectForKey:@(viewController.hash)] CGRectValue]; + + SCStackViewControllerPosition position = [self positionForViewController:viewController]; + + BOOL isReversed = NO; + if([self.layouters[@(position)] respondsToSelector:@selector(isReversed)]) { + isReversed = [self.layouters[@(position)] isReversed]; + } + + SCStackNavigationStep *currentStep = [SCStackNavigationStep navigationStepWithPercentage:[self visiblePercentageForViewController:viewController]]; + + switch (position) { + case SCStackViewControllerPositionTop: + { + CGPoint velocity = currentStep.percentage > step.percentage ? CGPointMake(0.0f, 1.0f) : CGPointMake(0.0f, -1.0f); + + if(velocity.y >= 0.0f) { + offset.y = (isReversed ? ([self maximumInsetForPosition:position].y - CGRectGetMaxY(finalFrame)) : CGRectGetMinY(finalFrame)); + } else { + offset.y = (isReversed ? ([self maximumInsetForPosition:position].y - CGRectGetMinY(finalFrame)) : CGRectGetMaxY(finalFrame)); + } + + + offset = [self nextStepOffsetForViewController:viewController position:position velocity:velocity reversed:isReversed contentOffset:offset paginating:NO]; + break; + } + case SCStackViewControllerPositionLeft: + { + CGPoint velocity = currentStep.percentage > step.percentage ? CGPointMake(1.0f, 0.0f) : CGPointMake(-1.0f, 0.0f); + + if(velocity.x >= 0.0f) { + offset.x = (isReversed ? ([self maximumInsetForPosition:position].x - CGRectGetMaxX(finalFrame)) : CGRectGetMinX(finalFrame)); + } else { + offset.x = (isReversed ? ([self maximumInsetForPosition:position].x - CGRectGetMinX(finalFrame)) : CGRectGetMaxX(finalFrame)); + } + + offset = [self nextStepOffsetForViewController:viewController position:position velocity:velocity reversed:isReversed contentOffset:offset paginating:NO]; + break; + } + case SCStackViewControllerPositionBottom: + { + CGPoint velocity = currentStep.percentage > step.percentage ? CGPointMake(0.0f, -1.0f) : CGPointMake(0.0f, 1.0f); + + if(velocity.y >= 0.0f) { + offset.y = (isReversed ? ([self maximumInsetForPosition:position].y - CGRectGetMaxY(finalFrame) + CGRectGetHeight(self.view.bounds)) : CGRectGetMinY(finalFrame) - CGRectGetHeight(self.view.bounds)); + } else { + offset.y = (isReversed ? ([self maximumInsetForPosition:position].y - CGRectGetMinY(finalFrame) + CGRectGetHeight(self.view.bounds)) : CGRectGetMaxY(finalFrame) - CGRectGetHeight(self.view.bounds)); + } + + offset = [self nextStepOffsetForViewController:viewController position:position velocity:velocity reversed:isReversed contentOffset:offset paginating:NO]; + break; + } + case SCStackViewControllerPositionRight: + { + CGPoint velocity = currentStep.percentage > step.percentage ? CGPointMake(-1.0f, 0.0f) : CGPointMake(1.0f, 0.0f); + + if(velocity.x >= 0.0f) { + offset.x = (isReversed ? ([self maximumInsetForPosition:position].x - CGRectGetMaxX(finalFrame) + CGRectGetWidth(self.view.bounds)) : CGRectGetMinX(finalFrame) - CGRectGetWidth(self.view.bounds)); + } else { + offset.x = (isReversed ? ([self maximumInsetForPosition:position].x - CGRectGetMinX(finalFrame) + CGRectGetWidth(self.view.bounds)) : CGRectGetMaxX(finalFrame) - CGRectGetWidth(self.view.bounds)); + } + + offset = [self nextStepOffsetForViewController:viewController position:position velocity:velocity reversed:isReversed contentOffset:offset paginating:NO]; + break; + } + default: + break; + } + } + + // Navigate to the determined offset, restore the previous navigation states and update navigation contraints + __weak typeof(self) weakSelf = self; + void(^cleanup)(void) = ^{ - if([self.viewControllers[@(position)] containsObject:viewController]) { - controllerPosition = position; + NSArray *navigationSteps = self.previousNavigationSteps[@([viewController hash])]; + if(navigationSteps.count) { + [weakSelf registerNavigationSteps:self.previousNavigationSteps[@([viewController hash])] forViewController:viewController]; } - } - - CGPoint offset = CGPointZero; - switch (controllerPosition) { - case SCStackViewControllerPositionTop: - offset.y = CGRectGetMinY(finalFrame); - break; - case SCStackViewControllerPositionLeft: - offset.x = CGRectGetMinX(finalFrame); - break; - case SCStackViewControllerPositionBottom: - offset.y = CGRectGetMaxY(finalFrame) - CGRectGetHeight(self.view.bounds); - break; - case SCStackViewControllerPositionRight: - offset.x = CGRectGetMaxX(finalFrame) - CGRectGetWidth(self.view.bounds); - break; - default: - break; - } - - [UIView animateWithDuration:(animated ? kDefaultAnimationDuration : 0.0f) - delay:0.0f - options:UIViewAnimationOptionCurveEaseInOut - animations:^{ - [self.scrollView setContentOffset:offset animated:animated]; - } completion:completion]; + + if(![weakSelf.scrollView isRunningAnimation]) { + [weakSelf.previousNavigationSteps removeObjectForKey:@([viewController hash])]; + } + + [weakSelf updateBoundsUsingNavigationContraints]; + + if(completion) { + completion(); + } + }; + + if(animated) { + [self.scrollView setContentOffset:offset easingFunction:self.easingFunction duration:self.animationDuration completion:cleanup]; + } else { + [self.scrollView setContentOffset:offset]; + cleanup(); + } } - (NSArray *)viewControllersForPosition:(SCStackViewControllerPosition)position { - return [self.viewControllers[@(position)] copy]; + return [self.loadedControllers[@(position)] copy]; } -#pragma mark - Private Methods +- (NSArray *)visibleViewControllers +{ + NSArray *viewControllers = [self viewControllersForPosition:[self positionForViewController:self.visibleControllers.lastObject]]; + + NSArray *sortedViewControllers = [self.visibleControllers sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { + return [@([viewControllers indexOfObject:obj1]) compare:@([viewControllers indexOfObject:obj2])]; + }]; + + if(self.isRootViewControllerVisible) { + return [[NSArray arrayWithObject:self.rootViewController] arrayByAddingObjectsFromArray:sortedViewControllers]; + } + + return sortedViewControllers; +} -- (void)updateFinalFramesForPosition:(SCStackViewControllerPosition)position +- (BOOL)isViewControllerVisible:(UIViewController *)viewController { - NSMutableArray *viewControllers = self.viewControllers[@(position)]; - [viewControllers enumerateObjectsUsingBlock:^(UIViewController *x, NSUInteger idx, BOOL *stop) { - CGRect finalFrame = [self.layouters[@(position)] finalFrameForViewController:x - withIndex:idx - atPosition:position - withinGroup:viewControllers - inStackController:self]; - - [self.finalFrames setObject:[NSValue valueWithCGRect:finalFrame] forKey:@([x hash])]; - }]; + if([viewController isEqual:self.rootViewController]) { + return self.isRootViewControllerVisible; + } + + return [self.visibleViewControllers containsObject:viewController]; +} + +- (CGFloat)visiblePercentageForViewController:(UIViewController *)viewController +{ + if([self isViewControllerVisible:viewController] == NO) { + return 0.0f; + } + + return [self.visiblePercentages[@([viewController hash])] floatValue]; +} + +- (BOOL)visible +{ + return self.isViewVisible; +} + +#pragma mark - UIViewController View Events + +- (void)loadView +{ + SCStackViewControllerView *view = [[SCStackViewControllerView alloc] init]; + [view setDelegate:self]; + self.view = view; } - (void)viewDidLoad { - [super viewDidLoad]; - - [self.view setAutoresizingMask:UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; + [super viewDidLoad]; + + [self.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; + + [self.rootViewController willMoveToParentViewController:self]; + [self.scrollView addSubview:self.rootViewController.view]; + [self addChildViewController:self.rootViewController]; + [self.rootViewController didMoveToParentViewController:self]; + + [self.scrollView setFrame:self.view.bounds]; - self.scrollView = [[SCStackScrollView alloc] initWithFrame:self.view.bounds]; - [self.scrollView setAutoresizingMask:UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; - [self.scrollView setDirectionalLockEnabled:YES]; - [self.scrollView setShowsHorizontalScrollIndicator:NO]; - [self.scrollView setDecelerationRate:UIScrollViewDecelerationRateFast]; - [self.scrollView setDelegate:self]; + // Prevents _adjustContentOffsetIfNecessary from triggering + UIView *scrollViewWrapper = [[UIView alloc] initWithFrame:self.view.bounds]; + [scrollViewWrapper setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; + [scrollViewWrapper addSubview:self.scrollView]; - [self addChildViewController:self.rootViewController]; - [self.rootViewController.view setFrame:self.scrollView.bounds]; - [self.rootViewController beginAppearanceTransition:YES animated:NO]; - [self.scrollView addSubview:self.rootViewController.view]; - [self.rootViewController endAppearanceTransition]; - [self.rootViewController didMoveToParentViewController:self]; - - [self.view addSubview:self.scrollView]; + [self.view addSubview:scrollViewWrapper]; } -- (void)viewDidAppear:(BOOL)animated +- (void)viewWillLayoutSubviews { - [super viewDidAppear:animated]; - [self.scrollView setContentOffset:CGPointZero]; + [super viewWillLayoutSubviews]; + + for(SCStackViewControllerPosition position = SCStackViewControllerPositionTop; position <= SCStackViewControllerPositionRight; position++) { + [self updateFinalFramesForPosition:position]; + } + + [self updateBoundsIgnoringNavigationContraints]; + + UIViewController *lastVisibleViewController = [self.visibleViewControllers lastObject]; + if(lastVisibleViewController) { + CGFloat visiblePercentage = [self visiblePercentageForViewController:lastVisibleViewController]; + [self navigateToStep:[SCStackNavigationStep navigationStepWithPercentage:visiblePercentage] inViewController:lastVisibleViewController animated:NO completion:nil]; + } + + [self updateFramesAndTriggerAppearanceCallbacks]; + [self updateBoundsUsingNavigationContraints]; } -- (void)updateBounds +- (void)viewDidAppear:(BOOL)animated { - UIEdgeInsets insets = UIEdgeInsetsZero; - - for(SCStackViewControllerPosition position = SCStackViewControllerPositionTop; position <=SCStackViewControllerPositionRight; position++) { - - NSArray *viewControllerHashes = [self.viewControllers[@(position)] valueForKeyPath:@"@distinctUnionOfObjects.hash"]; - NSArray *finalFrameKeys = [self.finalFrames.allKeys filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSNumber *hash, NSDictionary *bindings) { - return [viewControllerHashes containsObject:hash]; - }]]; - - NSArray *frames = [self.finalFrames objectsForKeys:finalFrameKeys notFoundMarker:[NSNull null]]; - for(NSValue *value in frames) { - switch (position) { - case SCStackViewControllerPositionTop: - insets.top = MAX(insets.top, ABS(CGRectGetMinY([value CGRectValue]))); - break; - case SCStackViewControllerPositionLeft: - insets.left = MAX(insets.left, ABS(CGRectGetMinX([value CGRectValue]))); - break; - case SCStackViewControllerPositionBottom: - insets.bottom = MAX(insets.bottom, CGRectGetMaxY([value CGRectValue]) - CGRectGetHeight(self.view.bounds)); - break; - case SCStackViewControllerPositionRight: - insets.right = MAX(insets.right, CGRectGetMaxX([value CGRectValue]) - CGRectGetWidth(self.view.bounds)); - break; - default: - break; - } - } - } + [super viewDidAppear:animated]; + + self.isViewVisible = YES; - [self.scrollView setContentInset:insets]; - [self.scrollView setContentSize:self.view.bounds.size]; + [self updateFramesAndTriggerAppearanceCallbacks]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + self.isViewVisible = NO; + + [self updateFramesAndTriggerAppearanceCallbacks]; +} + +#pragma mark - Stack Management + +- (void)updateFinalFramesForPosition:(SCStackViewControllerPosition)position +{ + NSMutableArray *viewControllers = self.loadedControllers[@(position)]; + [viewControllers enumerateObjectsUsingBlock:^(UIViewController *controller, NSUInteger idx, BOOL *stop) { + CGRect finalFrame = [self.layouters[@(position)] finalFrameForViewController:controller withIndex:idx atPosition:position withinGroup:viewControllers inStackController:self]; + [self.finalFrames setObject:[NSValue valueWithCGRect:finalFrame] forKey:@([controller hash])]; + }]; +} + +#pragma mark Navigation Contraints + +// Sets the insets to the summed up sizes of all the participating view controllers (used before pushing and popping) +- (void)updateBoundsIgnoringNavigationContraints +{ + UIEdgeInsets insets = UIEdgeInsetsZero; + + for(SCStackViewControllerPosition position = SCStackViewControllerPositionTop; position <= SCStackViewControllerPositionRight; position++) { + + NSArray *viewControllerHashes = [self.loadedControllers[@(position)] valueForKeyPath:@"@distinctUnionOfObjects.hash"]; + NSArray *finalFrameKeys = [self.finalFrames.allKeys filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSNumber *hash, NSDictionary *bindings) { + return [viewControllerHashes containsObject:hash]; + }]]; + + NSArray *frames = [self.finalFrames objectsForKeys:finalFrameKeys notFoundMarker:[NSNull null]]; + for(NSValue *value in frames) { + switch (position) { + case SCStackViewControllerPositionTop: + insets.top = MAX(insets.top, ABS(CGRectGetMinY([value CGRectValue]))); + break; + case SCStackViewControllerPositionLeft: + insets.left = MAX(insets.left, ABS(CGRectGetMinX([value CGRectValue]))); + break; + case SCStackViewControllerPositionBottom: + insets.bottom = MAX(insets.bottom, CGRectGetMaxY([value CGRectValue]) - CGRectGetHeight(self.view.bounds)); + break; + case SCStackViewControllerPositionRight: + insets.right = MAX(insets.right, CGRectGetMaxX([value CGRectValue]) - CGRectGetWidth(self.view.bounds)); + break; + default: + break; + } + } + } + + [self.scrollView setDelegate:nil]; + + CGPoint offset = self.scrollView.contentOffset; + [self.scrollView setContentInset:UIEdgeInsetsIntegral(insets)]; + if((self.scrollView.contentInset.left <= insets.left) || (self.scrollView.contentInset.top <= insets.top)) { + [self.scrollView setContentOffset:offset]; + } + + [self.scrollView setContentSize:self.view.bounds.size]; + [self.scrollView setDelegate:self]; } +// Sets the insets to the first encountered navigation steps in all directions or full size when SCStackViewControllerNavigationContraintTypeForward is not used (when stack is centred on the root) +- (void)updateBoundsUsingDefaultNavigationContraints +{ + if(!(self.navigationContaintType & SCStackViewControllerNavigationContraintTypeForward)) { + [self updateBoundsIgnoringNavigationContraints]; + return; + } + + UIEdgeInsets insets = UIEdgeInsetsZero; + + for(SCStackViewControllerPosition position = SCStackViewControllerPositionTop; position <=SCStackViewControllerPositionRight; position++) { + NSArray *viewControllers = self.loadedControllers[@(position)]; + + if(viewControllers.count == 0) { + continue; + } + + BOOL isReversed = NO; + if([self.layouters[@(position)] respondsToSelector:@selector(isReversed)]) { + isReversed = [self.layouters[@(position)] isReversed]; + } + + switch (position) { + case SCStackViewControllerPositionTop: + insets.top = ABS([self nextStepOffsetForViewController:viewControllers[0] position:position velocity:CGPointMake(0.0f, -1.0f) reversed:isReversed contentOffset:CGPointZero paginating:NO].y); + break; + case SCStackViewControllerPositionLeft: + insets.left = ABS([self nextStepOffsetForViewController:viewControllers[0] position:position velocity:CGPointMake(-1.0f, 0.0f) reversed:isReversed contentOffset:CGPointZero paginating:NO].x); + break; + case SCStackViewControllerPositionBottom: + insets.bottom = ABS([self nextStepOffsetForViewController:viewControllers[0] position:position velocity:CGPointMake(0.0f, 1.0f) reversed:isReversed contentOffset:CGPointZero paginating:NO].y); + break; + case SCStackViewControllerPositionRight: + insets.right = ABS([self nextStepOffsetForViewController:viewControllers[0] position:position velocity:CGPointMake(1.0f, 0.0f) reversed:isReversed contentOffset:CGPointZero paginating:NO].x); + break; + } + } + + [self.scrollView setContentInset:UIEdgeInsetsIntegral(insets)]; +} + +// Sets the insets to the next navigation steps based on the current state +- (void)updateBoundsUsingNavigationContraints +{ + if(self.continuousNavigationEnabled) { + return; + } + + UIEdgeInsets insets = UIEdgeInsetsZero; + UIViewController *lastVisibleController = [self.visibleViewControllers lastObject]; + + if(CGPointEqualToPoint(self.scrollView.contentOffset, CGPointZero) || lastVisibleController == nil) { + [self updateBoundsUsingDefaultNavigationContraints]; + self.didIgnoreNavigationalConstraints = YES; + return; + } + + SCStackViewControllerPosition lastVisibleControllerPosition = [self positionForViewController:lastVisibleController]; + NSArray *viewControllersArray = self.loadedControllers[@(lastVisibleControllerPosition)]; + NSUInteger visibleControllerIndex = [viewControllersArray indexOfObject:lastVisibleController]; + + BOOL isReversed = NO; + if([self.layouters[@(lastVisibleControllerPosition)] respondsToSelector:@selector(isReversed)]) { + isReversed = [self.layouters[@(lastVisibleControllerPosition)] isReversed]; + } + + if(self.navigationContaintType & SCStackViewControllerNavigationContraintTypeReverse) { + switch (lastVisibleControllerPosition) { + case SCStackViewControllerPositionTop: { + insets.top = -[self maximumInsetForPosition:lastVisibleControllerPosition].y; + insets.bottom = [self nextStepOffsetForViewController:lastVisibleController position:lastVisibleControllerPosition velocity:CGPointMake(0.0f, 1.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].y; + break; + } + case SCStackViewControllerPositionLeft: { + insets.left = -[self maximumInsetForPosition:lastVisibleControllerPosition].x; + insets.right = [self nextStepOffsetForViewController:lastVisibleController position:lastVisibleControllerPosition velocity:CGPointMake(1.0f, 0.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].x; + break; + } + case SCStackViewControllerPositionBottom: { + insets.bottom = [self maximumInsetForPosition:lastVisibleControllerPosition].y; + insets.top = -[self nextStepOffsetForViewController:lastVisibleController position:lastVisibleControllerPosition velocity:CGPointMake(0.0f, -1.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].y; + break; + } + case SCStackViewControllerPositionRight: { + insets.right = [self maximumInsetForPosition:lastVisibleControllerPosition].x; + insets.left = -[self nextStepOffsetForViewController:lastVisibleController position:lastVisibleControllerPosition velocity:CGPointMake(-1.0f, 0.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].x; + break; + } + } + } + + if(self.navigationContaintType & SCStackViewControllerNavigationContraintTypeForward) { + switch (lastVisibleControllerPosition) { + case SCStackViewControllerPositionTop: { + // Fetch the next step and set it as the current inset + insets.top = ABS([self nextStepOffsetForViewController:lastVisibleController position:lastVisibleControllerPosition velocity:CGPointMake(0.0f, -1.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].y); + + // If the next step is the upper bound of the current view controller and there are more view controllers on the stack, fetch the following view controller's first navigation step and use that + if(ABS(self.scrollView.contentOffset.y) == insets.top && visibleControllerIndex < viewControllersArray.count - 1) { + insets.top = ABS([self nextStepOffsetForViewController:viewControllersArray[visibleControllerIndex + 1] position:lastVisibleControllerPosition velocity:CGPointMake(0.0f, -1.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].y); + } + + break; + } + case SCStackViewControllerPositionLeft: { + insets.left = ABS([self nextStepOffsetForViewController:lastVisibleController position:lastVisibleControllerPosition velocity:CGPointMake(-1.0f, 0.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].x); + + if(ABS(self.scrollView.contentOffset.x) == insets.left && visibleControllerIndex < viewControllersArray.count - 1) { + insets.left = ABS([self nextStepOffsetForViewController:viewControllersArray[visibleControllerIndex + 1] position:lastVisibleControllerPosition velocity:CGPointMake(-1.0f, 0.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].x); + } + + break; + } + case SCStackViewControllerPositionBottom: { + insets.bottom = ABS([self nextStepOffsetForViewController:lastVisibleController position:lastVisibleControllerPosition velocity:CGPointMake(0.0f, 1.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].y); + + if(ABS(self.scrollView.contentOffset.y) == insets.bottom && visibleControllerIndex < viewControllersArray.count - 1) { + insets.bottom = ABS([self nextStepOffsetForViewController:viewControllersArray[visibleControllerIndex + 1] position:lastVisibleControllerPosition velocity:CGPointMake(0.0f, 1.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].y); + } + + break; + } + case SCStackViewControllerPositionRight: { + insets.right = ABS([self nextStepOffsetForViewController:lastVisibleController position:lastVisibleControllerPosition velocity:CGPointMake(1.0f, 0.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].x); + + if(ABS(self.scrollView.contentOffset.x) == insets.right && visibleControllerIndex < viewControllersArray.count - 1) { + insets.right = ABS([self nextStepOffsetForViewController:viewControllersArray[visibleControllerIndex + 1] position:lastVisibleControllerPosition velocity:CGPointMake(1.0f, 0.0f) reversed:isReversed contentOffset:self.scrollView.contentOffset paginating:NO].x); + } + + break; + } + } + } + + [self.scrollView setContentInset:UIEdgeInsetsIntegral(insets)]; +} + +#pragma mark Appearance callbacks and framesetting + - (BOOL)shouldAutomaticallyForwardAppearanceMethods { - return NO; + return NO; +} + +- (void)updateFramesAndTriggerAppearanceCallbacks +{ + CGPoint offset = self.scrollView.contentOffset; + + // Fetch the active layouter based on the current offset and use it to set the root's frame + id activeLayouter; + if(offset.y < 0.0f) { + activeLayouter = self.layouters[@(SCStackViewControllerPositionTop)]; + } else if(offset.x < 0.0f) { + activeLayouter = self.layouters[@(SCStackViewControllerPositionLeft)]; + } else if(offset.y > 0.0f){ + activeLayouter = self.layouters[@(SCStackViewControllerPositionBottom)]; + } else if(offset.x > 0.0f) { + activeLayouter = self.layouters[@(SCStackViewControllerPositionRight)]; + } else { + activeLayouter = self.lastUsedLayouter; + } + + self.lastUsedLayouter = activeLayouter; + + CGRect newRootViewControllerFrame; + if([activeLayouter respondsToSelector:@selector(currentFrameForRootViewController:contentOffset:inStackController:)]) { + newRootViewControllerFrame = [activeLayouter currentFrameForRootViewController:self.rootViewController contentOffset:offset inStackController:self]; + } else { + newRootViewControllerFrame = self.view.bounds; + } + + __block CGRect rootRemainder = CGRectIntersection(self.scrollView.bounds, newRootViewControllerFrame); + + for(SCStackViewControllerPosition position = SCStackViewControllerPositionTop; position <= SCStackViewControllerPositionRight; position++) { + + id layouter = self.layouters[@(position)]; + + BOOL shouldStackControllersAboveRoot = NO; + if([layouter respondsToSelector:@selector(shouldStackControllersAboveRoot)]) { + shouldStackControllersAboveRoot = [layouter shouldStackControllersAboveRoot]; + } + + CGRectEdge edge = [self edgeFromOffset:offset]; + __block CGRect remainder; + + // Determine the amount of unobstructed space the stacked view controllers might be seen through + if(shouldStackControllersAboveRoot) { + remainder = [self subtractRect:CGRectIntersection(self.scrollView.bounds, self.view.bounds) fromRect:self.scrollView.bounds withEdge:edge]; + } else { + remainder = [self subtractRect:CGRectIntersection(self.scrollView.bounds, newRootViewControllerFrame) fromRect:self.scrollView.bounds withEdge:edge]; + } + + BOOL isReversed = NO; + if([layouter respondsToSelector:@selector(isReversed)]) { + isReversed = [layouter isReversed]; + } + + NSArray *viewControllersArray = self.loadedControllers[@(position)]; + [viewControllersArray enumerateObjectsUsingBlock:^(UIViewController *viewController, NSUInteger index, BOOL *stop) { + + CGRect nextFrame = [layouter currentFrameForViewController:viewController withIndex:index atPosition:position finalFrame:[self.finalFrames[@(viewController.hash)] CGRectValue] contentOffset:offset inStackController:self]; + + // If using a reversed layouter adjust the frame to normal + CGRect adjustedFrame = nextFrame; + + if(index > 0) { + if(isReversed) { + switch (position) { + case SCStackViewControllerPositionTop: { + NSArray *remainingViewControllers = [viewControllersArray subarrayWithRange:NSMakeRange(index + 1, viewControllersArray.count - index - 1)]; + CGFloat totalSize = [[remainingViewControllers valueForKeyPath:@"@sum.sc_viewHeight"] floatValue]; + adjustedFrame.origin.y = [self maximumInsetForPosition:position].y + totalSize; + break; + } + case SCStackViewControllerPositionLeft: { + NSArray *remainingViewControllers = [viewControllersArray subarrayWithRange:NSMakeRange(index + 1, viewControllersArray.count - index - 1)]; + CGFloat totalSize = [[remainingViewControllers valueForKeyPath:@"@sum.sc_viewWidth"] floatValue]; + adjustedFrame.origin.x = [self maximumInsetForPosition:position].x + totalSize; + break; + } + case SCStackViewControllerPositionBottom: { + NSArray *remainingViewControllers = [viewControllersArray subarrayWithRange:NSMakeRange(index, viewControllersArray.count - index)]; + CGFloat totalSize = [[remainingViewControllers valueForKeyPath:@"@sum.sc_viewHeight"] floatValue]; + adjustedFrame.origin.y = CGRectGetHeight(self.view.bounds) + [self maximumInsetForPosition:position].y - totalSize; + break; + } + case SCStackViewControllerPositionRight: { + NSArray *remainingViewControllers = [viewControllersArray subarrayWithRange:NSMakeRange(index, viewControllersArray.count - index)]; + CGFloat totalSize = [[remainingViewControllers valueForKeyPath:@"@sum.sc_viewWidth"] floatValue]; + adjustedFrame.origin.x = CGRectGetWidth(self.view.bounds) + [self maximumInsetForPosition:position].x - totalSize; + break; + } + } + } else { + NSArray *previousViewControllers = [viewControllersArray subarrayWithRange:NSMakeRange(0, index)]; + for(UIViewController *controller in previousViewControllers) { + adjustedFrame = [self subtractRect:controller.view.frame fromRect:adjustedFrame withEdge:edge]; + } + } + } + + CGRect intersection = CGRectIntersection(remainder, adjustedFrame); + + // If a view controller's frame does intersect the remainder then it's visible + BOOL visible = ((position == SCStackViewControllerPositionLeft || position == SCStackViewControllerPositionRight) && CGRectGetWidth(intersection) > 0.0f); + visible = visible || ((position == SCStackViewControllerPositionTop || position == SCStackViewControllerPositionBottom) && CGRectGetHeight(intersection) > 0.0f); + + visible = visible && self.isViewVisible; + + if(visible) { + + switch (position) { + case SCStackViewControllerPositionTop: + case SCStackViewControllerPositionBottom: + { + [self.visiblePercentages setObject:@(roundf((CGRectGetHeight(intersection) * 1000) / CGRectGetHeight(adjustedFrame))/1000.0f) forKey:@([viewController hash])]; + break; + } + case SCStackViewControllerPositionLeft: + case SCStackViewControllerPositionRight: + { + [self.visiblePercentages setObject:@(roundf((CGRectGetWidth(intersection) * 1000) / CGRectGetWidth(adjustedFrame))/1000.0f) forKey:@([viewController hash])]; + break; + } + } + + // And if it's visible then we prepare for the next view controller by reducing the remainder some more + remainder = [self subtractRect:CGRectIntersection(remainder, adjustedFrame) fromRect:remainder withEdge:edge]; + + if(shouldStackControllersAboveRoot) { + rootRemainder = [self subtractRect:CGRectIntersection(rootRemainder, adjustedFrame) fromRect:rootRemainder withEdge:edge]; + } + } + + // Finally, trigger appearance callbacks and new frame + if(visible && ![self.visibleControllers containsObject:viewController]) { + [self.visibleControllers addObject:viewController]; + [viewController beginAppearanceTransition:YES animated:NO]; + [viewController.view setFrame:nextFrame]; + [viewController endAppearanceTransition]; + + dispatch_async(dispatch_get_main_queue(), ^{ + if([self.delegate respondsToSelector:@selector(stackViewController:didShowViewController:position:)]) { + [self.delegate stackViewController:self didShowViewController:viewController position:position]; + } + }); + + } else if(!visible && [self.visibleControllers containsObject:viewController]) { + [self.visibleControllers removeObject:viewController]; + [viewController beginAppearanceTransition:NO animated:NO]; + [viewController.view setFrame:nextFrame]; + [viewController endAppearanceTransition]; + + dispatch_async(dispatch_get_main_queue(), ^{ + if([self.delegate respondsToSelector:@selector(stackViewController:didHideViewController:position:)]) { + [self.delegate stackViewController:self didHideViewController:viewController position:position]; + } + }); + + } else { + [viewController.view setFrame:nextFrame]; + } + + if([layouter respondsToSelector:@selector(sublayerTransformForViewController:withIndex:atPosition:finalFrame:contentOffset:inStackController:)]) { + CATransform3D transform = [layouter sublayerTransformForViewController:viewController + withIndex:index + atPosition:position + finalFrame:[self.finalFrames[@(viewController.hash)] CGRectValue] + contentOffset:offset + inStackController:self]; + [viewController.view.layer setSublayerTransform:transform]; + } + }]; + } + + // Figure out if the root is still visible or not and call its appearance methods + BOOL hasVerticalControllers = ([self.loadedControllers[@(SCStackViewControllerPositionTop)] count] || [self.loadedControllers[@(SCStackViewControllerPositionBottom)] count]); + BOOL hasHorizontalController = ([self.loadedControllers[@(SCStackViewControllerPositionLeft)] count] || [self.loadedControllers[@(SCStackViewControllerPositionRight)] count]); + + BOOL visible = (hasHorizontalController && CGRectGetWidth(rootRemainder) > 0.0f); + visible = visible || (hasVerticalControllers && CGRectGetHeight(rootRemainder) > 0.0f); + visible = visible || (!hasHorizontalController && !hasVerticalControllers); + + visible = visible && self.isViewVisible; + + if(visible && !self.isRootViewControllerVisible) { + self.isRootViewControllerVisible = YES; + [self.rootViewController beginAppearanceTransition:YES animated:NO]; + [self.rootViewController.view setFrame:newRootViewControllerFrame]; + [self.rootViewController endAppearanceTransition]; + + dispatch_async(dispatch_get_main_queue(), ^{ + if([self.delegate respondsToSelector:@selector(stackViewController:didShowViewController:position:)]) { + [self.delegate stackViewController:self didShowViewController:self.rootViewController position:-1]; + } + }); + + } else if(!visible && self.isRootViewControllerVisible) { + self.isRootViewControllerVisible = NO; + [self.rootViewController beginAppearanceTransition:NO animated:NO]; + [self.rootViewController.view setFrame:newRootViewControllerFrame]; + [self.rootViewController endAppearanceTransition]; + + dispatch_async(dispatch_get_main_queue(), ^{ + if([self.delegate respondsToSelector:@selector(stackViewController:didHideViewController:position:)]) { + [self.delegate stackViewController:self didHideViewController:self.rootViewController position:-1]; + } + }); + + } else { + + [self.rootViewController.view setFrame:newRootViewControllerFrame]; + + if(hasVerticalControllers) { + [self.visiblePercentages setObject:@(roundf((CGRectGetHeight(rootRemainder) * 1000) / CGRectGetHeight(newRootViewControllerFrame))/1000.0f) forKey:@([self.rootViewController hash])]; + } else if(hasHorizontalController) { + [self.visiblePercentages setObject:@(roundf((CGRectGetWidth(rootRemainder) * 1000) / CGRectGetWidth(newRootViewControllerFrame))/1000.0f) forKey:@([self.rootViewController hash])]; + } + } + + if([activeLayouter respondsToSelector:@selector(sublayerTransformForRootViewController:contentOffset:inStackController:)]) { + CATransform3D transform = [activeLayouter sublayerTransformForRootViewController:self.rootViewController + contentOffset:offset + inStackController:self]; + [self.rootViewController.view.layer setSublayerTransform:transform]; + } +} + +#pragma mark Pagination + +- (void)adjustTargetContentOffset:(inout CGPoint *)targetContentOffset withVelocity:(CGPoint)velocity +{ + if(!self.pagingEnabled && self.continuousNavigationEnabled) { + return; + } + + for(SCStackViewControllerPosition position = SCStackViewControllerPositionTop; position <= SCStackViewControllerPositionRight; position++) { + + BOOL isReversed = NO; + if([self.layouters[@(position)] respondsToSelector:@selector(isReversed)]) { + isReversed = [self.layouters[@(position)] isReversed]; + } + + CGPoint adjustedOffset = *targetContentOffset; + + if(isReversed) { + if(position == SCStackViewControllerPositionLeft && targetContentOffset->x < 0.0f) { + adjustedOffset.x = [self maximumInsetForPosition:position].x - targetContentOffset->x; + } + else if(position == SCStackViewControllerPositionRight && targetContentOffset->x >= 0.0f) { + adjustedOffset.x = [self maximumInsetForPosition:position].x - targetContentOffset->x; + } + else if(position == SCStackViewControllerPositionTop && targetContentOffset->y < 0.0f) { + adjustedOffset.y = [self maximumInsetForPosition:position].y - targetContentOffset->y; + } + else if(position == SCStackViewControllerPositionBottom && targetContentOffset->y >= 0.0f) { + adjustedOffset.y = [self maximumInsetForPosition:position].y - targetContentOffset->y; + } + } + + NSArray *viewControllersArray = self.loadedControllers[@(position)]; + + __block BOOL keepGoing = YES; + + // Enumerate through all the VCs and figure out which one contains the targeted offset + [viewControllersArray enumerateObjectsUsingBlock:^(UIViewController *viewController, NSUInteger index, BOOL *stop) { + + CGRect frame = [self.finalFrames[@(viewController.hash)] CGRectValue]; + frame.origin.x = frame.origin.x > 0.0f ? CGRectGetMinX(frame) - CGRectGetWidth(self.view.bounds) : CGRectGetMinX(frame); + frame.origin.y = frame.origin.y > 0.0f ? CGRectGetMinY(frame) - CGRectGetHeight(self.view.bounds) : CGRectGetMinY(frame); + + frame = CGRectOffset(CGRectInset(frame, -0.5f, -0.5f), 0.5f, 0.5f); //consider the maximum X and maximum Y edges + + if(CGRectContainsPoint(frame, adjustedOffset)) { + + // If the velocity is zero then jump to the closest navigation step + if(CGPointEqualToPoint(CGPointZero, velocity)) { + + switch (position) { + case SCStackViewControllerPositionTop: + case SCStackViewControllerPositionBottom: + { + CGPoint previousStepOffset = [self nextStepOffsetForViewController:viewController position:position velocity:CGPointMake(0.0f, -1.0f) reversed:isReversed contentOffset:*targetContentOffset paginating:YES]; + CGPoint nextStepOffset = [self nextStepOffsetForViewController:viewController position:position velocity:CGPointMake(0.0f, 1.0f) reversed:isReversed contentOffset:*targetContentOffset paginating:YES]; + + *targetContentOffset = ABS(targetContentOffset->y - previousStepOffset.y) > ABS(targetContentOffset->y - nextStepOffset.y) ? nextStepOffset : previousStepOffset; + break; + } + case SCStackViewControllerPositionLeft: + case SCStackViewControllerPositionRight: + { + CGPoint previousStepOffset = [self nextStepOffsetForViewController:viewController position:position velocity:CGPointMake(-1.0f, 0.0f) reversed:isReversed contentOffset:*targetContentOffset paginating:YES]; + CGPoint nextStepOffset = [self nextStepOffsetForViewController:viewController position:position velocity:CGPointMake(1.0f, 0.0f) reversed:isReversed contentOffset:*targetContentOffset paginating:YES]; + + *targetContentOffset = ABS(targetContentOffset->x - previousStepOffset.x) > ABS(targetContentOffset->x - nextStepOffset.x) ? nextStepOffset : previousStepOffset; + break; + } + } + + } else { + // Calculate the next step of the pagination (either a navigationStep or a controller edge) + *targetContentOffset = [self nextStepOffsetForViewController:viewController position:position velocity:velocity reversed:isReversed contentOffset:*targetContentOffset paginating:YES]; + } + + keepGoing = NO; + *stop = YES; + } + }]; + + if(!keepGoing) { + return; + } + } +} + +#pragma mark Shared + +- (CGPoint)nextStepOffsetForViewController:(UIViewController *)viewController + position:(SCStackViewControllerPosition)position + velocity:(CGPoint)velocity + reversed:(BOOL)isReversed + contentOffset:(CGPoint)contentOffset + paginating:(BOOL)paginating + +{ + CGPoint nextStepOffset = CGPointZero; + + NSArray *navigationSteps = self.navigationSteps[@([viewController hash])]; + + CGRect finalFrame = [self.finalFrames[@(viewController.hash)] CGRectValue]; + + // Reverse the step search when folding view controllers + if((velocity.y > 0.0f && position == SCStackViewControllerPositionTop) || (velocity.x > 0.0f && position == SCStackViewControllerPositionLeft) || + (velocity.y < 0.0f && position == SCStackViewControllerPositionBottom) || (velocity.x < 0.0f && position == SCStackViewControllerPositionRight)) { + navigationSteps = [[navigationSteps reverseObjectEnumerator] allObjects]; + } + + // Fetch the next navigation step and calculate its offset + for(SCStackNavigationStep *nextStep in navigationSteps) { + + if(position == SCStackViewControllerPositionTop) { + if(isReversed) { + nextStepOffset.y = [self maximumInsetForPosition:position].y - CGRectGetMaxY(finalFrame) + CGRectGetHeight(finalFrame) * (1.0f - nextStep.percentage); + } else { + nextStepOffset.y = CGRectGetMaxY(finalFrame) - CGRectGetHeight(finalFrame) * nextStep.percentage; + } + } else if(position == SCStackViewControllerPositionLeft) { + if(isReversed) { + nextStepOffset.x = [self maximumInsetForPosition:position].x - CGRectGetMaxX(finalFrame) + CGRectGetWidth(finalFrame) * (1.0f - nextStep.percentage); + } else { + nextStepOffset.x = CGRectGetMaxX(finalFrame) - CGRectGetWidth(finalFrame) * nextStep.percentage; + } + } else if(position == SCStackViewControllerPositionBottom) { + if(isReversed) { + nextStepOffset.y = [self maximumInsetForPosition:position].y - CGRectGetMaxY(finalFrame) + CGRectGetHeight(finalFrame) * nextStep.percentage + CGRectGetHeight(self.view.bounds); + } else { + nextStepOffset.y = CGRectGetMinY(finalFrame) + CGRectGetHeight(finalFrame) * nextStep.percentage - CGRectGetHeight(self.view.bounds); + } + } else if(position == SCStackViewControllerPositionRight) { + if(isReversed) { + nextStepOffset.x = [self maximumInsetForPosition:position].x - CGRectGetMaxX(finalFrame) + CGRectGetWidth(finalFrame) * nextStep.percentage + CGRectGetWidth(self.view.bounds); + } else { + nextStepOffset.x = CGRectGetMinX(finalFrame) + CGRectGetWidth(finalFrame) * nextStep.percentage - CGRectGetWidth(self.view.bounds); + } + } + + nextStepOffset.x = roundf(nextStepOffset.x); + nextStepOffset.y = roundf(nextStepOffset.y); + + // Cache the steps to avoid having to recalculate them later. Will clear the cache when the pagination is done. + [self.stepsForOffsets setObject:nextStep forKey:[NSValue valueWithCGPoint:nextStepOffset]]; + + if(!paginating) { + // Trick the calculations into blocking + if(nextStep.blockType == SCStackNavigationStepBlockTypeForward) { + nextStepOffset.y -= 0.01f; + } + + if(nextStep.blockType == SCStackNavigationStepBlockTypeReverse) { + nextStepOffset.y += 0.01f; + } + } + + if((velocity.y > 0.0f && nextStepOffset.y > contentOffset.y) || (velocity.y < 0.0f && nextStepOffset.y < contentOffset.y) || + (velocity.x > 0.0f && nextStepOffset.x > contentOffset.x) || (velocity.x < 0.0f && nextStepOffset.x < contentOffset.x)) { + return nextStepOffset; + } + } + + // If no navigation step is found use the view controller's bounds + if(velocity.y > 0.0f && isReversed) { + nextStepOffset.y = [self maximumInsetForPosition:position].y - CGRectGetMinY(finalFrame); + } else if(velocity.x > 0.0f && isReversed) { + nextStepOffset.x = [self maximumInsetForPosition:position].x - CGRectGetMinX(finalFrame); + } + + else if(velocity.y < 0.0f && isReversed) { + nextStepOffset.y = [self maximumInsetForPosition:position].y - CGRectGetMaxY(finalFrame); + } else if(velocity.x < 0.0f && isReversed) { + nextStepOffset.x = [self maximumInsetForPosition:position].x - CGRectGetMaxX(finalFrame); + } + + else if(velocity.y > 0.0f && !isReversed) { + nextStepOffset.y = CGRectGetMaxY(finalFrame); + } else if(velocity.x > 0.0f && !isReversed) { + nextStepOffset.x = CGRectGetMaxX(finalFrame); + } + + else if(velocity.y < 0.0f && !isReversed) { + nextStepOffset.y = CGRectGetMinY(finalFrame); + } + else if(velocity.x < 0.0f && !isReversed) { + nextStepOffset.x = CGRectGetMinX(finalFrame); + } + + if(position == SCStackViewControllerPositionBottom) { + nextStepOffset.y = nextStepOffset.y + (isReversed ? CGRectGetHeight(self.view.bounds) : -CGRectGetHeight(self.view.bounds)); + } + + if(position == SCStackViewControllerPositionRight) { + nextStepOffset.x = nextStepOffset.x + (isReversed ? CGRectGetWidth(self.view.bounds) : -CGRectGetWidth(self.view.bounds)); + } + + return nextStepOffset; +} + +#pragma mark - SCStackViewControllerViewDelegate + +- (void)stackViewControllerViewWillChangeFrame:(SCStackViewControllerView *)stackViewControllerView +{ + [self.scrollView setDelegate:nil]; +} + +- (void)stackViewControllerViewDidChangeFrame:(SCStackViewControllerView *)stackViewControllerView +{ + [self.scrollView setDelegate:self]; } #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { - for(int position=SCStackViewControllerPositionTop; position<=SCStackViewControllerPositionRight; position++) { - - NSArray *viewControllersArray = self.viewControllers[@(position)]; - [viewControllersArray enumerateObjectsUsingBlock:^(UIViewController *viewController, NSUInteger index, BOOL *stop) { - CGRect frame = [self.layouters[@(position)] currentFrameForViewController:viewController - withIndex:index - atPosition:position - finalFrame:[self.finalFrames[@(viewController.hash)] CGRectValue] - contentOffset:scrollView.contentOffset - inStackController:self]; - - CGRect intersection = CGRectIntersection(self.scrollView.bounds, frame); - __block BOOL visible = YES; - [viewControllersArray enumerateObjectsUsingBlock:^(UIViewController *obj, NSUInteger idx, BOOL *stop) { - if(CGRectContainsRect(obj.view.frame, intersection) && ![viewController isEqual:obj]) { - visible = NO; - *stop = YES; - } - }]; - - if(CGRectContainsRect(self.rootViewController.view.frame, intersection)) { - visible = NO; - } - - if(visible && ![self.visibleViewControllers containsObject:viewController]) { - [self.visibleViewControllers addObject:viewController]; - [viewController beginAppearanceTransition:YES animated:NO]; - [viewController.view setFrame:frame]; - [viewController endAppearanceTransition]; - } else if(!visible && [self.visibleViewControllers containsObject:viewController]) { - [self.visibleViewControllers removeObject:viewController]; - [viewController beginAppearanceTransition:NO animated:NO]; - [viewController.view setFrame:frame]; - [viewController endAppearanceTransition]; - } else { - [viewController.view setFrame:frame]; - } - }]; - } + [self updateFramesAndTriggerAppearanceCallbacks]; + + if(self.didIgnoreNavigationalConstraints) { + [self updateBoundsUsingNavigationContraints]; + self.didIgnoreNavigationalConstraints = NO; + } + + if([self.delegate respondsToSelector:@selector(stackViewController:didNavigateToOffset:)]) { + [self.delegate stackViewController:self didNavigateToOffset:self.scrollView.contentOffset]; + } } -- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset +- (void)triggerNavigationStepsDelegateCalls { - CGRect finalFrame = CGRectMake(targetContentOffset->x, targetContentOffset->y, 0, 0); - for(NSValue *frameValue in self.finalFrames.allValues) { - - CGRect frame = [frameValue CGRectValue]; - frame.origin.x = frame.origin.x > 0 ? CGRectGetMinX(frame) - CGRectGetWidth(self.view.bounds) : CGRectGetMinX(frame); - frame.origin.y = frame.origin.y > 0 ? CGRectGetMinY(frame) - CGRectGetHeight(self.view.bounds) : CGRectGetMinY(frame); - - if(CGRectContainsPoint(frame, *targetContentOffset)) { - finalFrame = frame; - break; - } - } - - CGFloat iOS5Adjustment = 0.0f; - if(SYSTEM_VERSION_LESS_THAN(@"6.0")) { - iOS5Adjustment = 0.1f; - } - - if(velocity.x || velocity.y == 0) { - if (velocity.x >= 0.0) { - targetContentOffset->x = CGRectGetMaxX(finalFrame) - iOS5Adjustment; - } - else if (velocity.x < -0.1) { - targetContentOffset->x = CGRectGetMinX(finalFrame) + iOS5Adjustment; - } - } else { - if (velocity.y >= 0.0) { - targetContentOffset->y = CGRectGetMaxY(finalFrame) - iOS5Adjustment; - } - else if (velocity.y < -0.1) { - targetContentOffset->y = CGRectGetMinY(finalFrame) + iOS5Adjustment; - } - } + if(!self.pagingEnabled && self.continuousNavigationEnabled) { + return; + } + + UIViewController *lastVisibleViewController = [self.visibleViewControllers lastObject]; + + SCStackNavigationStep *step; + if(lastVisibleViewController == nil) { + step = [SCStackNavigationStep navigationStepWithPercentage:0.0f]; + } else { + step = [self.stepsForOffsets objectForKey:[NSValue valueWithCGPoint:self.scrollView.contentOffset]]; + + if(step == nil) { + step = [SCStackNavigationStep navigationStepWithPercentage:1.0f]; + } + } + + if([self.delegate respondsToSelector:@selector(stackViewController:didNavigateToStep:inViewController:)]) { + [self.delegate stackViewController:self didNavigateToStep:step inViewController:lastVisibleViewController]; + } + + [self.stepsForOffsets removeAllObjects]; } -#pragma mark - Rotation Handling - -- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration +- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - [self.scrollView setContentOffset:CGPointZero]; + //FIXME: Without this the scroll might get stuck in between pages, if setting the insets before the animation is finished. With it jumping steps is harder. Find another way of fixing it. + if(self.scrollView.isTracking) { + return; + } + + [self updateBoundsUsingNavigationContraints]; + [self triggerNavigationStepsDelegateCalls]; } -- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation +- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { - for(int position=SCStackViewControllerPositionTop; position<=SCStackViewControllerPositionRight; position++) { - [self updateFinalFramesForPosition:position]; - } - - [UIView animateWithDuration:kDefaultAnimationDuration animations:^{ - [self scrollViewDidScroll:self.scrollView]; - [self updateBounds]; - }]; + [self updateBoundsUsingNavigationContraints]; + [self triggerNavigationStepsDelegateCalls]; } -#pragma mark - Properties +- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate +{ + if(decelerate == NO) { + [self updateBoundsUsingNavigationContraints]; + [self triggerNavigationStepsDelegateCalls]; + } +} -- (BOOL)bounces +- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { - return [self.scrollView bounces]; + // Bouncing target content offset when fix. + // When trying to adjust content offset while bouncing the velocity drops down to almost nothing. + // Seems to be an internal UIScrollView issue + if(self.scrollView.contentOffset.y < -self.scrollView.contentInset.top) { + targetContentOffset->y = - roundf(self.scrollView.contentInset.top); + } else if(self.scrollView.contentOffset.x < -self.scrollView.contentInset.left) { + targetContentOffset->x = - roundf(self.scrollView.contentInset.left); + } else if(self.scrollView.contentOffset.y > self.scrollView.contentInset.bottom) { + targetContentOffset->y = roundf(self.scrollView.contentInset.bottom); + } else if(self.scrollView.contentOffset.x > self.scrollView.contentInset.right) { + targetContentOffset->x = roundf(self.scrollView.contentInset.right); + } + // Normal pagination + else { + [self adjustTargetContentOffset:targetContentOffset withVelocity:velocity]; + } } -- (void)setBounces:(BOOL)bounces +#pragma mark - Rotation Handling + +- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - [self.scrollView setBounces:bounces]; + // Leave enough room for the scrollView's content to adjust. Will be recalculated on layoutSubviews + [self.scrollView setContentInset:UIEdgeInsetsMake(CGFLOAT_MAX, CGFLOAT_MAX, CGFLOAT_MAX, CGFLOAT_MAX)]; } -- (UIBezierPath *)touchRefusalArea +#pragma mark - Helpers + +- (CGPoint)maximumInsetForPosition:(SCStackViewControllerPosition)position { - return [self.scrollView touchRefusalArea]; + switch (position) { + case SCStackViewControllerPositionTop: + return CGPointMake(0, -[[self.loadedControllers[@(position)] valueForKeyPath:@"@sum.sc_viewHeight"] floatValue]); + case SCStackViewControllerPositionLeft: + return CGPointMake(-[[self.loadedControllers[@(position)] valueForKeyPath:@"@sum.sc_viewWidth"] floatValue], 0); + case SCStackViewControllerPositionBottom: + return CGPointMake(0, [[self.loadedControllers[@(position)] valueForKeyPath:@"@sum.sc_viewHeight"] floatValue]); + case SCStackViewControllerPositionRight: + return CGPointMake([[self.loadedControllers[@(position)] valueForKeyPath:@"@sum.sc_viewWidth"] floatValue], 0); + default: + return CGPointZero; + } } -- (void)setTouchRefusalArea:(UIBezierPath *)path +- (SCStackViewControllerPosition)positionForViewController:(UIViewController *)viewController { - [self.scrollView setTouchRefusalArea:path]; + for(SCStackViewControllerPosition position = SCStackViewControllerPositionTop; position <= SCStackViewControllerPositionRight; position++) { + if([self.loadedControllers[@(position)] containsObject:viewController]) { + return position; + } + } + + return -1; } -- (NSMutableDictionary *)layouters +- (CGRectEdge)edgeFromOffset:(CGPoint)offset { - if(_layouters == nil) { - _layouters = [NSMutableDictionary dictionary]; - } - - return _layouters; + CGRectEdge edge = -1; + + if(offset.x > 0.0f) { + edge = CGRectMinXEdge; + } else if(offset.x < 0.0f) { + edge = CGRectMaxXEdge; + } else if(offset.y > 0.0f) { + edge = CGRectMinYEdge; + } else if(offset.y < 0.0f) { + edge = CGRectMaxYEdge; + } + + return edge; } -- (NSMutableDictionary *)finalFrames +UIEdgeInsets UIEdgeInsetsIntegral(UIEdgeInsets edgeInsets) { - if(_finalFrames == nil) { - _finalFrames = [NSMutableDictionary dictionary]; - } - - return _finalFrames; + edgeInsets.top = roundf(edgeInsets.top); + edgeInsets.left = roundf(edgeInsets.left); + edgeInsets.bottom = roundf(edgeInsets.bottom); + edgeInsets.right = roundf(edgeInsets.right); + + return edgeInsets; } -- (NSDictionary *)viewControllers +- (CGRect)subtractRect:(CGRect)r2 fromRect:(CGRect)r1 withEdge:(CGRectEdge)edge { - if(_viewControllers == nil) { - _viewControllers = (@{ - @(SCStackViewControllerPositionTop) : [NSMutableArray array], - @(SCStackViewControllerPositionLeft) : [NSMutableArray array], - @(SCStackViewControllerPositionBottom): [NSMutableArray array], - @(SCStackViewControllerPositionRight) : [NSMutableArray array] - }); - } - - return _viewControllers; + if(CGRectEqualToRect(r1, r2)) { + return CGRectZero; + } + + CGRect intersection = CGRectIntersection(r1, r2); + if (CGRectIsNull(intersection)) { + return r1; + } + + float chopAmount = (edge == CGRectMinXEdge || edge == CGRectMaxXEdge) ? CGRectGetWidth(intersection) : CGRectGetHeight(intersection); + + CGRect remainder, throwaway; + CGRectDivide(r1, &throwaway, &remainder, chopAmount, edge); + return remainder; } -- (NSMutableArray *)visibleViewControllers +@end + + +@implementation UIViewController (SCStackViewController) + +- (SCStackViewController *)sc_stackViewController { - if(_visibleViewControllers == nil) { - _visibleViewControllers = [NSMutableArray array]; - } - - return _visibleViewControllers; + UIResponder *responder = self; + while ((responder = [responder nextResponder])) { + if ([responder isKindOfClass:[SCStackViewController class]]) { + return (SCStackViewController *)responder; + } + } + return nil; } -#pragma mark - Helpers +- (CGFloat)sc_viewWidth +{ + return CGRectGetWidth(self.view.bounds); +} -- (CGPoint)offsetForPosition:(SCStackViewControllerPosition)position -{ - switch (position) { - case SCStackViewControllerPositionTop: - return CGPointMake(0, -self.scrollView.contentInset.top); - case SCStackViewControllerPositionLeft: - return CGPointMake(-self.scrollView.contentInset.left, 0); - case SCStackViewControllerPositionBottom: - return CGPointMake(0, self.scrollView.contentInset.bottom); - case SCStackViewControllerPositionRight: - return CGPointMake(self.scrollView.contentInset.right, 0); - default: - return CGPointZero; - } +- (CGFloat)sc_viewHeight +{ + return CGRectGetHeight(self.view.bounds); } @end diff --git a/SCStackViewController/SCStackViewControllerView.h b/SCStackViewController/SCStackViewControllerView.h new file mode 100644 index 00000000..9d3f7240 --- /dev/null +++ b/SCStackViewController/SCStackViewControllerView.h @@ -0,0 +1,25 @@ +// +// SCStackViewControllerView.h +// SCStackViewController +// +// Created by Stefan Ceriu on 17/01/2015. +// Copyright (c) 2015 Stefan Ceriu. All rights reserved. +// + +@import UIKit; + +@protocol SCStackViewControllerViewDelegate; + +@interface SCStackViewControllerView : UIView + +@property (nonatomic, weak) id delegate; + +@end + +@protocol SCStackViewControllerViewDelegate + +- (void)stackViewControllerViewWillChangeFrame:(SCStackViewControllerView *)stackViewControllerView; +- (void)stackViewControllerViewDidChangeFrame:(SCStackViewControllerView *)stackViewControllerView; + +@end + diff --git a/SCStackViewController/SCStackViewControllerView.m b/SCStackViewController/SCStackViewControllerView.m new file mode 100644 index 00000000..cebe8aa6 --- /dev/null +++ b/SCStackViewController/SCStackViewControllerView.m @@ -0,0 +1,22 @@ +// +// SCStackViewControllerView.m +// SCStackViewController +// +// Created by Stefan Ceriu on 17/01/2015. +// Copyright (c) 2015 Stefan Ceriu. All rights reserved. +// + +#import "SCStackViewControllerView.h" + +@implementation SCStackViewControllerView + +- (void)setFrame:(CGRect)frame +{ + [self.delegate stackViewControllerViewWillChangeFrame:self]; + + super.frame = frame; + + [self.delegate stackViewControllerViewDidChangeFrame:self]; +} + +@end diff --git a/SCStackViewController/UIViewController+SCStackViewController.h b/SCStackViewController/UIViewController+SCStackViewController.h deleted file mode 100644 index 448c6a63..00000000 --- a/SCStackViewController/UIViewController+SCStackViewController.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// UIViewController+SCStackViewController.h -// SCStackViewController -// -// Created by Stefan Ceriu on 16/08/2013. -// Copyright (c) 2013 Stefan Ceriu. All rights reserved. -// - -@class SCStackViewController; - -@interface UIViewController (SCStackViewController) - -- (SCStackViewController *)stackViewController; - -- (CGFloat)viewWidth; -- (CGFloat)viewHeight; - -@end - diff --git a/SCStackViewController/UIViewController+SCStackViewController.m b/SCStackViewController/UIViewController+SCStackViewController.m deleted file mode 100644 index a2304e5e..00000000 --- a/SCStackViewController/UIViewController+SCStackViewController.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// UIViewController+SCStackViewController.m -// SCStackViewController -// -// Created by Stefan Ceriu on 16/08/2013. -// Copyright (c) 2013 Stefan Ceriu. All rights reserved. -// - -#import "UIViewController+SCStackViewController.h" -#import "SCStackViewController.h" - -@implementation UIViewController (SCStackViewController) - -- (SCStackViewController *)stackViewController -{ - UIResponder *responder = self; - while ((responder = [responder nextResponder])) { - if ([responder isKindOfClass:[SCStackViewController class]]) { - return (SCStackViewController *)responder; - } - } - return nil; -} - -- (CGFloat)viewWidth -{ - return self.view.bounds.size.width; -} - -- (CGFloat)viewHeight -{ - return self.view.bounds.size.height; -} - -@end