no message

This commit is contained in:
Peter 2015-07-30 01:09:30 +03:00
parent 690b1d572b
commit 0cf72b8a57
41 changed files with 2602 additions and 3 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "submodules/AsyncDisplayKit"]
path = submodules/AsyncDisplayKit
url = https://github.com/facebook/AsyncDisplayKit.git

View File

@ -10,6 +10,45 @@
D05CC2671B69316F00E235A3 /* Display.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC2661B69316F00E235A3 /* Display.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC26E1B69316F00E235A3 /* Display.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05CC2631B69316F00E235A3 /* Display.framework */; };
D05CC2731B69316F00E235A3 /* DisplayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2721B69316F00E235A3 /* DisplayTests.swift */; };
D05CC29A1B69323B00E235A3 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05CC2991B69323B00E235A3 /* SwiftSignalKit.framework */; };
D05CC2A01B69326400E235A3 /* NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC29F1B69326400E235A3 /* NavigationController.swift */; };
D05CC2A21B69326C00E235A3 /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2A11B69326C00E235A3 /* Window.swift */; };
D05CC2B61B69339A00E235A3 /* AsyncDisplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D05CC2B21B6932E900E235A3 /* AsyncDisplayKit.framework */; };
D05CC2E31B69552C00E235A3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2E21B69552C00E235A3 /* ViewController.swift */; };
D05CC2E71B69555800E235A3 /* CAAnimationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2E41B69555800E235A3 /* CAAnimationUtils.swift */; };
D05CC2E81B69555800E235A3 /* CALayer+ImplicitAnimations.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2E51B69555800E235A3 /* CALayer+ImplicitAnimations.m */; };
D05CC2E91B69555800E235A3 /* CALayer+ImplicitAnimations.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC2E61B69555800E235A3 /* CALayer+ImplicitAnimations.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC2EC1B69558A00E235A3 /* RuntimeUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2EA1B69558A00E235A3 /* RuntimeUtils.m */; };
D05CC2ED1B69558A00E235A3 /* RuntimeUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC2EB1B69558A00E235A3 /* RuntimeUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC2F71B6955D000E235A3 /* UIKitUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2EE1B6955D000E235A3 /* UIKitUtils.swift */; };
D05CC2F81B6955D000E235A3 /* UIViewController+Navigation.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2EF1B6955D000E235A3 /* UIViewController+Navigation.m */; };
D05CC2F91B6955D000E235A3 /* UIViewController+Navigation.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC2F01B6955D000E235A3 /* UIViewController+Navigation.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC2FA1B6955D000E235A3 /* UINavigationItem+Proxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2F11B6955D000E235A3 /* UINavigationItem+Proxy.m */; };
D05CC2FB1B6955D000E235A3 /* UINavigationItem+Proxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC2F21B6955D000E235A3 /* UINavigationItem+Proxy.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC2FC1B6955D000E235A3 /* UIKitUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2F31B6955D000E235A3 /* UIKitUtils.m */; };
D05CC2FD1B6955D000E235A3 /* UIKitUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC2F41B6955D000E235A3 /* UIKitUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC2FE1B6955D000E235A3 /* UIWindow+OrientationChange.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2F51B6955D000E235A3 /* UIWindow+OrientationChange.m */; };
D05CC2FF1B6955D000E235A3 /* UIWindow+OrientationChange.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC2F61B6955D000E235A3 /* UIWindow+OrientationChange.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC3031B69568600E235A3 /* NotificationCenterUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3011B69568600E235A3 /* NotificationCenterUtils.m */; };
D05CC3041B69568600E235A3 /* NotificationCenterUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC3021B69568600E235A3 /* NotificationCenterUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC3071B69575900E235A3 /* NSBag.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3051B69575900E235A3 /* NSBag.m */; };
D05CC3081B69575900E235A3 /* NSBag.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC3061B69575900E235A3 /* NSBag.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC3151B695A9600E235A3 /* NavigationTransitionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3091B695A9500E235A3 /* NavigationTransitionView.swift */; };
D05CC3161B695A9600E235A3 /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC30A1B695A9500E235A3 /* NavigationBar.swift */; };
D05CC3171B695A9600E235A3 /* NavigationItemWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC30B1B695A9500E235A3 /* NavigationItemWrapper.swift */; };
D05CC3181B695A9600E235A3 /* NavigationItemTransitionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC30C1B695A9500E235A3 /* NavigationItemTransitionState.swift */; };
D05CC3191B695A9600E235A3 /* NavigationBackButtonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC30D1B695A9500E235A3 /* NavigationBackButtonNode.swift */; };
D05CC31A1B695A9600E235A3 /* NavigationButtonNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC30E1B695A9500E235A3 /* NavigationButtonNode.swift */; };
D05CC31B1B695A9600E235A3 /* NavigationTitleNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC30F1B695A9500E235A3 /* NavigationTitleNode.swift */; };
D05CC31C1B695A9600E235A3 /* BarButtonItemWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3101B695A9600E235A3 /* BarButtonItemWrapper.swift */; };
D05CC31D1B695A9600E235A3 /* UIBarButtonItem+Proxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3111B695A9600E235A3 /* UIBarButtonItem+Proxy.m */; };
D05CC31E1B695A9600E235A3 /* UIBarButtonItem+Proxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC3121B695A9600E235A3 /* UIBarButtonItem+Proxy.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC31F1B695A9600E235A3 /* NavigationControllerProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3131B695A9600E235A3 /* NavigationControllerProxy.m */; };
D05CC3201B695A9600E235A3 /* NavigationControllerProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC3141B695A9600E235A3 /* NavigationControllerProxy.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC3241B695B0700E235A3 /* NavigationBarProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = D05CC3221B695B0700E235A3 /* NavigationBarProxy.h */; settings = {ATTRIBUTES = (Public, ); }; };
D05CC3251B695B0700E235A3 /* NavigationBarProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3231B695B0700E235A3 /* NavigationBarProxy.m */; };
D05CC3271B69725400E235A3 /* NavigationBackArrowLight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D05CC3261B69725400E235A3 /* NavigationBackArrowLight@2x.png */; };
D05CC3291B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3281B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -20,6 +59,41 @@
remoteGlobalIDString = D05CC2621B69316F00E235A3;
remoteInfo = Display;
};
D05CC2AB1B6932E900E235A3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05CC2A41B6932E800E235A3 /* AsyncDisplayKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 058D09AC195D04C000B7D73C;
remoteInfo = AsyncDisplayKit;
};
D05CC2AD1B6932E900E235A3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05CC2A41B6932E800E235A3 /* AsyncDisplayKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 058D09BC195D04C000B7D73C;
remoteInfo = AsyncDisplayKitTests;
};
D05CC2AF1B6932E900E235A3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05CC2A41B6932E800E235A3 /* AsyncDisplayKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 057D02BF1AC0A66700C7AC3C;
remoteInfo = AsyncDisplayKitTestHost;
};
D05CC2B11B6932E900E235A3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05CC2A41B6932E800E235A3 /* AsyncDisplayKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = B35061DA1B010EDF0018CF92;
remoteInfo = "AsyncDisplayKit-iOS";
};
D05CC32A1B697C0900E235A3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = D05CC2A41B6932E800E235A3 /* AsyncDisplayKit.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = B35061D91B010EDF0018CF92;
remoteInfo = "AsyncDisplayKit-iOS";
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@ -29,6 +103,45 @@
D05CC26D1B69316F00E235A3 /* DisplayTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DisplayTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D05CC2721B69316F00E235A3 /* DisplayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayTests.swift; sourceTree = "<group>"; };
D05CC2741B69316F00E235A3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D05CC2991B69323B00E235A3 /* SwiftSignalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftSignalKit.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-gbpsmqzuwcmmxadrqcwyrluaftwp/Build/Products/Debug-iphoneos/SwiftSignalKit.framework"; sourceTree = "<group>"; };
D05CC29F1B69326400E235A3 /* NavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationController.swift; sourceTree = "<group>"; };
D05CC2A11B69326C00E235A3 /* Window.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
D05CC2A41B6932E800E235A3 /* AsyncDisplayKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = AsyncDisplayKit.xcodeproj; path = submodules/AsyncDisplayKit/AsyncDisplayKit.xcodeproj; sourceTree = "<group>"; };
D05CC2E21B69552C00E235A3 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
D05CC2E41B69555800E235A3 /* CAAnimationUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CAAnimationUtils.swift; sourceTree = "<group>"; };
D05CC2E51B69555800E235A3 /* CALayer+ImplicitAnimations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CALayer+ImplicitAnimations.m"; sourceTree = "<group>"; };
D05CC2E61B69555800E235A3 /* CALayer+ImplicitAnimations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CALayer+ImplicitAnimations.h"; sourceTree = "<group>"; };
D05CC2EA1B69558A00E235A3 /* RuntimeUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuntimeUtils.m; sourceTree = "<group>"; };
D05CC2EB1B69558A00E235A3 /* RuntimeUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RuntimeUtils.h; sourceTree = "<group>"; };
D05CC2EE1B6955D000E235A3 /* UIKitUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKitUtils.swift; sourceTree = "<group>"; };
D05CC2EF1B6955D000E235A3 /* UIViewController+Navigation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+Navigation.m"; sourceTree = "<group>"; };
D05CC2F01B6955D000E235A3 /* UIViewController+Navigation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Navigation.h"; sourceTree = "<group>"; };
D05CC2F11B6955D000E235A3 /* UINavigationItem+Proxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UINavigationItem+Proxy.m"; sourceTree = "<group>"; };
D05CC2F21B6955D000E235A3 /* UINavigationItem+Proxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UINavigationItem+Proxy.h"; sourceTree = "<group>"; };
D05CC2F31B6955D000E235A3 /* UIKitUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIKitUtils.m; sourceTree = "<group>"; };
D05CC2F41B6955D000E235A3 /* UIKitUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIKitUtils.h; sourceTree = "<group>"; };
D05CC2F51B6955D000E235A3 /* UIWindow+OrientationChange.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIWindow+OrientationChange.m"; sourceTree = "<group>"; };
D05CC2F61B6955D000E235A3 /* UIWindow+OrientationChange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIWindow+OrientationChange.h"; sourceTree = "<group>"; };
D05CC3011B69568600E235A3 /* NotificationCenterUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NotificationCenterUtils.m; sourceTree = "<group>"; };
D05CC3021B69568600E235A3 /* NotificationCenterUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NotificationCenterUtils.h; sourceTree = "<group>"; };
D05CC3051B69575900E235A3 /* NSBag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSBag.m; sourceTree = "<group>"; };
D05CC3061B69575900E235A3 /* NSBag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSBag.h; sourceTree = "<group>"; };
D05CC3091B695A9500E235A3 /* NavigationTransitionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationTransitionView.swift; sourceTree = "<group>"; };
D05CC30A1B695A9500E235A3 /* NavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBar.swift; sourceTree = "<group>"; };
D05CC30B1B695A9500E235A3 /* NavigationItemWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationItemWrapper.swift; sourceTree = "<group>"; };
D05CC30C1B695A9500E235A3 /* NavigationItemTransitionState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationItemTransitionState.swift; sourceTree = "<group>"; };
D05CC30D1B695A9500E235A3 /* NavigationBackButtonNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBackButtonNode.swift; sourceTree = "<group>"; };
D05CC30E1B695A9500E235A3 /* NavigationButtonNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationButtonNode.swift; sourceTree = "<group>"; };
D05CC30F1B695A9500E235A3 /* NavigationTitleNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationTitleNode.swift; sourceTree = "<group>"; };
D05CC3101B695A9600E235A3 /* BarButtonItemWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarButtonItemWrapper.swift; sourceTree = "<group>"; };
D05CC3111B695A9600E235A3 /* UIBarButtonItem+Proxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBarButtonItem+Proxy.m"; sourceTree = "<group>"; };
D05CC3121B695A9600E235A3 /* UIBarButtonItem+Proxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIBarButtonItem+Proxy.h"; sourceTree = "<group>"; };
D05CC3131B695A9600E235A3 /* NavigationControllerProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NavigationControllerProxy.m; sourceTree = "<group>"; };
D05CC3141B695A9600E235A3 /* NavigationControllerProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NavigationControllerProxy.h; sourceTree = "<group>"; };
D05CC3221B695B0700E235A3 /* NavigationBarProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NavigationBarProxy.h; sourceTree = "<group>"; };
D05CC3231B695B0700E235A3 /* NavigationBarProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NavigationBarProxy.m; sourceTree = "<group>"; };
D05CC3261B69725400E235A3 /* NavigationBackArrowLight@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NavigationBackArrowLight@2x.png"; sourceTree = "<group>"; };
D05CC3281B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InteractiveTransitionGestureRecognizer.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -36,6 +149,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D05CC2B61B69339A00E235A3 /* AsyncDisplayKit.framework in Frameworks */,
D05CC29A1B69323B00E235A3 /* SwiftSignalKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -53,6 +168,7 @@
D05CC2591B69316F00E235A3 = {
isa = PBXGroup;
children = (
D05CC2A31B6932D500E235A3 /* Frameworks */,
D05CC2651B69316F00E235A3 /* Display */,
D05CC2711B69316F00E235A3 /* DisplayTests */,
D05CC2641B69316F00E235A3 /* Products */,
@ -71,8 +187,11 @@
D05CC2651B69316F00E235A3 /* Display */ = {
isa = PBXGroup;
children = (
D05CC2661B69316F00E235A3 /* Display.h */,
D05CC2681B69316F00E235A3 /* Info.plist */,
D05CC3001B6955D500E235A3 /* Utils */,
D05CC3211B695AA600E235A3 /* Navigation */,
D05CC2A11B69326C00E235A3 /* Window.swift */,
D05CC2E21B69552C00E235A3 /* ViewController.swift */,
D05CC2E11B69534100E235A3 /* Supporting Files */,
);
path = Display;
sourceTree = "<group>";
@ -86,6 +205,84 @@
path = DisplayTests;
sourceTree = "<group>";
};
D05CC2A31B6932D500E235A3 /* Frameworks */ = {
isa = PBXGroup;
children = (
D05CC2A41B6932E800E235A3 /* AsyncDisplayKit.xcodeproj */,
D05CC2991B69323B00E235A3 /* SwiftSignalKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
D05CC2A51B6932E800E235A3 /* Products */ = {
isa = PBXGroup;
children = (
D05CC2AC1B6932E900E235A3 /* libAsyncDisplayKit.a */,
D05CC2AE1B6932E900E235A3 /* AsyncDisplayKitTests.xctest */,
D05CC2B01B6932E900E235A3 /* AsyncDisplayKitTestHost.app */,
D05CC2B21B6932E900E235A3 /* AsyncDisplayKit.framework */,
);
name = Products;
sourceTree = "<group>";
};
D05CC2E11B69534100E235A3 /* Supporting Files */ = {
isa = PBXGroup;
children = (
D05CC3261B69725400E235A3 /* NavigationBackArrowLight@2x.png */,
D05CC2661B69316F00E235A3 /* Display.h */,
D05CC2681B69316F00E235A3 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
D05CC3001B6955D500E235A3 /* Utils */ = {
isa = PBXGroup;
children = (
D05CC2EB1B69558A00E235A3 /* RuntimeUtils.h */,
D05CC2EA1B69558A00E235A3 /* RuntimeUtils.m */,
D05CC2F01B6955D000E235A3 /* UIViewController+Navigation.h */,
D05CC2EF1B6955D000E235A3 /* UIViewController+Navigation.m */,
D05CC2EE1B6955D000E235A3 /* UIKitUtils.swift */,
D05CC2F41B6955D000E235A3 /* UIKitUtils.h */,
D05CC2F31B6955D000E235A3 /* UIKitUtils.m */,
D05CC2F21B6955D000E235A3 /* UINavigationItem+Proxy.h */,
D05CC2F11B6955D000E235A3 /* UINavigationItem+Proxy.m */,
D05CC2F61B6955D000E235A3 /* UIWindow+OrientationChange.h */,
D05CC2F51B6955D000E235A3 /* UIWindow+OrientationChange.m */,
D05CC3021B69568600E235A3 /* NotificationCenterUtils.h */,
D05CC3011B69568600E235A3 /* NotificationCenterUtils.m */,
D05CC2E61B69555800E235A3 /* CALayer+ImplicitAnimations.h */,
D05CC2E51B69555800E235A3 /* CALayer+ImplicitAnimations.m */,
D05CC3061B69575900E235A3 /* NSBag.h */,
D05CC3051B69575900E235A3 /* NSBag.m */,
D05CC3121B695A9600E235A3 /* UIBarButtonItem+Proxy.h */,
D05CC3111B695A9600E235A3 /* UIBarButtonItem+Proxy.m */,
D05CC3141B695A9600E235A3 /* NavigationControllerProxy.h */,
D05CC3131B695A9600E235A3 /* NavigationControllerProxy.m */,
D05CC3221B695B0700E235A3 /* NavigationBarProxy.h */,
D05CC3231B695B0700E235A3 /* NavigationBarProxy.m */,
D05CC2E41B69555800E235A3 /* CAAnimationUtils.swift */,
);
name = Utils;
sourceTree = "<group>";
};
D05CC3211B695AA600E235A3 /* Navigation */ = {
isa = PBXGroup;
children = (
D05CC3091B695A9500E235A3 /* NavigationTransitionView.swift */,
D05CC30A1B695A9500E235A3 /* NavigationBar.swift */,
D05CC30C1B695A9500E235A3 /* NavigationItemTransitionState.swift */,
D05CC30D1B695A9500E235A3 /* NavigationBackButtonNode.swift */,
D05CC30E1B695A9500E235A3 /* NavigationButtonNode.swift */,
D05CC30F1B695A9500E235A3 /* NavigationTitleNode.swift */,
D05CC30B1B695A9500E235A3 /* NavigationItemWrapper.swift */,
D05CC3101B695A9600E235A3 /* BarButtonItemWrapper.swift */,
D05CC29F1B69326400E235A3 /* NavigationController.swift */,
D05CC3281B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift */,
);
name = Navigation;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -93,7 +290,18 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
D05CC3041B69568600E235A3 /* NotificationCenterUtils.h in Headers */,
D05CC2ED1B69558A00E235A3 /* RuntimeUtils.h in Headers */,
D05CC3201B695A9600E235A3 /* NavigationControllerProxy.h in Headers */,
D05CC2E91B69555800E235A3 /* CALayer+ImplicitAnimations.h in Headers */,
D05CC2FB1B6955D000E235A3 /* UINavigationItem+Proxy.h in Headers */,
D05CC3241B695B0700E235A3 /* NavigationBarProxy.h in Headers */,
D05CC31E1B695A9600E235A3 /* UIBarButtonItem+Proxy.h in Headers */,
D05CC2FF1B6955D000E235A3 /* UIWindow+OrientationChange.h in Headers */,
D05CC2FD1B6955D000E235A3 /* UIKitUtils.h in Headers */,
D05CC3081B69575900E235A3 /* NSBag.h in Headers */,
D05CC2671B69316F00E235A3 /* Display.h in Headers */,
D05CC2F91B6955D000E235A3 /* UIViewController+Navigation.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -112,6 +320,7 @@
buildRules = (
);
dependencies = (
D05CC32B1B697C0900E235A3 /* PBXTargetDependency */,
);
name = Display;
productName = Display;
@ -142,6 +351,7 @@
D05CC25A1B69316F00E235A3 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = Telegram;
TargetAttributes = {
@ -163,6 +373,12 @@
mainGroup = D05CC2591B69316F00E235A3;
productRefGroup = D05CC2641B69316F00E235A3 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = D05CC2A51B6932E800E235A3 /* Products */;
ProjectRef = D05CC2A41B6932E800E235A3 /* AsyncDisplayKit.xcodeproj */;
},
);
projectRoot = "";
targets = (
D05CC2621B69316F00E235A3 /* Display */,
@ -171,11 +387,43 @@
};
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
D05CC2AC1B6932E900E235A3 /* libAsyncDisplayKit.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libAsyncDisplayKit.a;
remoteRef = D05CC2AB1B6932E900E235A3 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
D05CC2AE1B6932E900E235A3 /* AsyncDisplayKitTests.xctest */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = AsyncDisplayKitTests.xctest;
remoteRef = D05CC2AD1B6932E900E235A3 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
D05CC2B01B6932E900E235A3 /* AsyncDisplayKitTestHost.app */ = {
isa = PBXReferenceProxy;
fileType = wrapper.application;
path = AsyncDisplayKitTestHost.app;
remoteRef = D05CC2AF1B6932E900E235A3 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
D05CC2B21B6932E900E235A3 /* AsyncDisplayKit.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = AsyncDisplayKit.framework;
remoteRef = D05CC2B11B6932E900E235A3 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
D05CC2611B69316F00E235A3 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D05CC3271B69725400E235A3 /* NavigationBackArrowLight@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -193,6 +441,31 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D05CC3181B695A9600E235A3 /* NavigationItemTransitionState.swift in Sources */,
D05CC2F81B6955D000E235A3 /* UIViewController+Navigation.m in Sources */,
D05CC31F1B695A9600E235A3 /* NavigationControllerProxy.m in Sources */,
D05CC3031B69568600E235A3 /* NotificationCenterUtils.m in Sources */,
D05CC2E31B69552C00E235A3 /* ViewController.swift in Sources */,
D05CC2A01B69326400E235A3 /* NavigationController.swift in Sources */,
D05CC3251B695B0700E235A3 /* NavigationBarProxy.m in Sources */,
D05CC2F71B6955D000E235A3 /* UIKitUtils.swift in Sources */,
D05CC3161B695A9600E235A3 /* NavigationBar.swift in Sources */,
D05CC31D1B695A9600E235A3 /* UIBarButtonItem+Proxy.m in Sources */,
D05CC3171B695A9600E235A3 /* NavigationItemWrapper.swift in Sources */,
D05CC3191B695A9600E235A3 /* NavigationBackButtonNode.swift in Sources */,
D05CC3071B69575900E235A3 /* NSBag.m in Sources */,
D05CC31A1B695A9600E235A3 /* NavigationButtonNode.swift in Sources */,
D05CC2E71B69555800E235A3 /* CAAnimationUtils.swift in Sources */,
D05CC31B1B695A9600E235A3 /* NavigationTitleNode.swift in Sources */,
D05CC31C1B695A9600E235A3 /* BarButtonItemWrapper.swift in Sources */,
D05CC2FA1B6955D000E235A3 /* UINavigationItem+Proxy.m in Sources */,
D05CC2E81B69555800E235A3 /* CALayer+ImplicitAnimations.m in Sources */,
D05CC2EC1B69558A00E235A3 /* RuntimeUtils.m in Sources */,
D05CC2FC1B6955D000E235A3 /* UIKitUtils.m in Sources */,
D05CC3291B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift in Sources */,
D05CC2FE1B6955D000E235A3 /* UIWindow+OrientationChange.m in Sources */,
D05CC2A21B69326C00E235A3 /* Window.swift in Sources */,
D05CC3151B695A9600E235A3 /* NavigationTransitionView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -212,6 +485,11 @@
target = D05CC2621B69316F00E235A3 /* Display */;
targetProxy = D05CC26F1B69316F00E235A3 /* PBXContainerItemProxy */;
};
D05CC32B1B697C0900E235A3 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = "AsyncDisplayKit-iOS";
targetProxy = D05CC32A1B697C0900E235A3 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
@ -307,28 +585,35 @@
D05CC2781B69316F00E235A3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Display/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.telegram.Display;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
D05CC2791B69316F00E235A3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Display/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.telegram.Display;
PRODUCT_NAME = "$(TARGET_NAME)";
@ -375,6 +660,7 @@
D05CC2791B69316F00E235A3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
D05CC27A1B69316F00E235A3 /* Build configuration list for PBXNativeTarget "DisplayTests" */ = {
isa = XCConfigurationList;
@ -383,6 +669,7 @@
D05CC27C1B69316F00E235A3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};

View File

@ -0,0 +1,49 @@
import UIKit
import AsyncDisplayKit
internal class BarButtonItemWrapper {
let parentNode: ASDisplayNode
let barButtonItem: UIBarButtonItem
let layoutNeeded: () -> ()
let buttonNode: NavigationButtonNode
private var setEnabledListenerKey: Int!
private var setTitleListenerKey: Int!
init(parentNode: ASDisplayNode, barButtonItem: UIBarButtonItem, layoutNeeded: () -> ()) {
self.parentNode = parentNode
self.barButtonItem = barButtonItem
self.layoutNeeded = layoutNeeded
self.buttonNode = NavigationButtonNode()
self.buttonNode.pressed = { [weak self] in
self?.barButtonItem.performActionOnTarget()
return
}
self.parentNode.addSubnode(self.buttonNode)
self.setEnabledListenerKey = barButtonItem.addSetEnabledListener({ [weak self] enabled in
self?.buttonNode.enabled = enabled
return
})
self.setTitleListenerKey = barButtonItem.addSetTitleListener({ [weak self] title in
self?.buttonNode.text = title
if let layoutNeeded = self?.layoutNeeded {
layoutNeeded()
}
return
})
self.buttonNode.text = barButtonItem.title ?? ""
self.buttonNode.enabled = barButtonItem.enabled ?? true
self.buttonNode.bold = (barButtonItem.style ?? UIBarButtonItemStyle.Plain) == UIBarButtonItemStyle.Done
}
deinit {
self.barButtonItem.removeSetTitleListener(self.setTitleListenerKey)
self.barButtonItem.removeSetEnabledListener(self.setEnabledListenerKey)
self.buttonNode.removeFromSupernode()
}
}

View File

@ -0,0 +1,32 @@
import UIKit
extension CALayer {
internal func animate(from from: NSValue, to: NSValue, keyPath: String, timingFunction: String, duration: NSTimeInterval) {
let k = Float(UIView.animationDurationFactor())
var speed: Float = 1.0
if k != 0 && k != 1 {
speed = Float(1.0) / k
}
let animation = CABasicAnimation(keyPath: keyPath)
animation.fromValue = from
animation.toValue = to
animation.duration = duration
animation.timingFunction = CAMediaTimingFunction(name: timingFunction)
animation.removedOnCompletion = true
animation.fillMode = kCAFillModeForwards
animation.speed = speed
self.addAnimation(animation, forKey: keyPath)
self.setValue(to, forKey: keyPath)
}
internal func animateAlpha(from from: CGFloat, to: CGFloat, duration: NSTimeInterval) {
self.animate(from: NSNumber(float: Float(from)), to: NSNumber(float: Float(to)), keyPath: "opacity", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: duration)
}
internal func animatePosition(from from: CGPoint, to: CGPoint, duration: NSTimeInterval) {
self.animate(from: NSValue(CGPoint: from), to: NSValue(CGPoint: to), keyPath: "position", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: duration)
}
}

View File

@ -0,0 +1,20 @@
#import <UIKit/UIKit.h>
@interface CALayer (ImplicitAnimations)
+ (void)beginRecordingChanges;
+ (NSArray *)endRecordingChanges;
@end
@interface CALayerAnimation : NSObject
@property (nonatomic, weak, readonly) CALayer *layer;
@property (nonatomic, readonly) CGRect startBounds;
@property (nonatomic, readonly) CGRect endBounds;
@property (nonatomic, readonly) CGPoint startPosition;
@property (nonatomic, readonly) CGPoint endPosition;
@end

View File

@ -0,0 +1,142 @@
#import "CALayer+ImplicitAnimations.h"
#import <UIKit/UIKit.h>
#import "RuntimeUtils.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
static bool recordingChanges = false;
static NSMutableArray *currentLayerAnimations()
{
static NSMutableArray *array = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
array = [[NSMutableArray alloc] init];
});
return array;
}
@implementation CALayerAnimation
- (instancetype)initWithLayer:(CALayer *)layer
{
self = [super init];
if (self != nil)
{
_layer = layer;
_startBounds = layer.bounds;
_startPosition = layer.position;
_endBounds = _startBounds;
_endPosition = _startPosition;
}
return self;
}
- (void)setEndBounds:(CGRect)endBounds
{
_endBounds = endBounds;
}
- (void)setEndPosition:(CGPoint)endPosition
{
_endPosition = endPosition;
}
@end
@interface CALayer (_ca836a62_)
@end
@implementation CALayer (_ca836a62_)
- (void)_ca836a62_setBounds:(CGRect)bounds
{
if (recordingChanges && [self.delegate isKindOfClass:[ASDisplayNode class]])
{
CALayerAnimation *animation = nil;
for (CALayerAnimation *listAnimation in currentLayerAnimations())
{
if (listAnimation.layer == self)
{
animation = listAnimation;
break;
}
}
if (animation == nil)
{
animation = [[CALayerAnimation alloc] initWithLayer:self];
[currentLayerAnimations() addObject:animation];
}
[animation setEndBounds:bounds];
}
[self _ca836a62_setBounds:bounds];
}
- (void)_ca836a62_setPosition:(CGPoint)position
{
if (recordingChanges && [self.delegate isKindOfClass:[ASDisplayNode class]])
{
CALayerAnimation *animation = nil;
for (CALayerAnimation *listAnimation in currentLayerAnimations())
{
if (listAnimation.layer == self)
{
animation = listAnimation;
break;
}
}
if (animation == nil)
{
animation = [[CALayerAnimation alloc] initWithLayer:self];
[currentLayerAnimations() addObject:animation];
}
[animation setEndPosition:position];
}
[self _ca836a62_setPosition:position];
}
@end
@interface LayerAnimationExtensions : NSObject
@end
@implementation LayerAnimationExtensions
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
[RuntimeUtils swizzleInstanceMethodOfClass:[CALayer class] currentSelector:@selector(setBounds:) newSelector:@selector(_ca836a62_setBounds:)];
[RuntimeUtils swizzleInstanceMethodOfClass:[CALayer class] currentSelector:@selector(setPosition:) newSelector:@selector(_ca836a62_setPosition:)];
});
}
@end
@implementation CALayer (ImplicitAnimations)
+ (void)beginRecordingChanges
{
recordingChanges = true;
[currentLayerAnimations() removeAllObjects];
}
+ (NSArray *)endRecordingChanges
{
recordingChanges = false;
NSArray *array = [[NSArray alloc] initWithArray:currentLayerAnimations()];
[currentLayerAnimations() removeAllObjects];
return array;
}
@end

View File

@ -16,4 +16,15 @@ FOUNDATION_EXPORT const unsigned char DisplayVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <Display/PublicHeader.h>
#import <Display/RuntimeUtils.h>
#import <Display/UIViewController+Navigation.h>
#import <Display/UIKitUtils.h>
#import <Display/UINavigationItem+Proxy.h>
#import <Display/UIWindow+OrientationChange.h>
#import <Display/NotificationCenterUtils.h>
#import <Display/CALayer+ImplicitAnimations.h>
#import <Display/NSBag.h>
#import <Display/UIBarButtonItem+Proxy.h>
#import <Display/NavigationControllerProxy.h>
#import <Display/NavigationBarProxy.h>
#import <UIKit/UIGestureRecognizerSubclass.h>

View File

@ -0,0 +1,44 @@
import Foundation
import UIKit
class InteractiveTransitionGestureRecognizer: UIPanGestureRecognizer {
var validatedGesture = false
var firstLocation: CGPoint = CGPoint()
override init(target: AnyObject?, action: Selector) {
super.init(target: target, action: action)
self.maximumNumberOfTouches = 1
}
override func reset() {
super.reset()
validatedGesture = false
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
self.firstLocation = touches.first!.locationInView(self.view)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) {
let location = touches.first!.locationInView(self.view)
let translation = CGPoint(x: location.x - firstLocation.x, y: location.y - firstLocation.y)
if !validatedGesture {
if translation.x < 0.0 {
self.state = .Failed
} else if abs(translation.y) >= 2.0 {
self.state = .Failed
} else if translation.x >= 3.0 && translation.x / 3.0 > translation.y {
validatedGesture = true
}
}
if validatedGesture {
super.touchesMoved(touches, withEvent: event)
}
}
}

9
Display/NSBag.h Normal file
View File

@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface NSBag : NSObject
- (NSInteger)addItem:(id)item;
- (void)enumerateItems:(void (^)(id))block;
- (void)removeItem:(NSInteger)key;
@end

64
Display/NSBag.m Normal file
View File

@ -0,0 +1,64 @@
#import "NSBag.h"
@interface NSBag ()
{
NSInteger _nextKey;
NSMutableArray *_items;
NSMutableArray *_itemKeys;
}
@end
@implementation NSBag
- (instancetype)init
{
self = [super init];
if (self != nil)
{
_items = [[NSMutableArray alloc] init];
_itemKeys = [[NSMutableArray alloc] init];
}
return self;
}
- (NSInteger)addItem:(id)item
{
if (item == nil)
return -1;
NSInteger key = _nextKey;
[_items addObject:item];
[_itemKeys addObject:@(key)];
_nextKey++;
return key;
}
- (void)enumerateItems:(void (^)(id))block
{
if (block)
{
for (id item in _items)
{
block(item);
}
}
}
- (void)removeItem:(NSInteger)key
{
NSUInteger index = 0;
for (NSNumber *itemKey in _itemKeys)
{
if ([itemKey integerValue] == key)
{
[_items removeObjectAtIndex:index];
[_itemKeys removeObjectAtIndex:index];
break;
}
index++;
}
}
@end

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,143 @@
import UIKit
import AsyncDisplayKit
public class NavigationBackButtonNode: ASControlNode {
private func fontForCurrentState() -> UIFont {
return UIFont.systemFontOfSize(17.0)
}
private func attributesForCurrentState() -> [String : AnyObject] {
return [
NSFontAttributeName: self.fontForCurrentState(),
NSForegroundColorAttributeName: self.enabled ? UIColor.blueColor() : UIColor.grayColor()
]
}
var suspendLayout = false
let arrow: ASDisplayNode
let label: ASTextNode
private let arrowSpacing: CGFloat = 4.0
private var _text: String = ""
var text: String {
get {
return self._text
}
set(value) {
self._text = value
self.label.attributedString = NSAttributedString(string: text, attributes: self.attributesForCurrentState())
self.invalidateCalculatedLayout()
}
}
private var touchCount = 0
var pressed: () -> () = {}
override init() {
self.arrow = ASDisplayNode()
self.label = ASTextNode()
super.init()
self.userInteractionEnabled = true
self.exclusiveTouch = true
self.hitTestSlop = UIEdgeInsets(top: -16.0, left: -10.0, bottom: -16.0, right: -10.0)
self.displaysAsynchronously = false
self.arrow.displaysAsynchronously = false
self.label.displaysAsynchronously = false
self.addSubnode(self.arrow)
let arrowImage = UIImage(named: "NavigationBackArrowLight", inBundle: NSBundle(forClass: NavigationBackButtonNode.self), compatibleWithTraitCollection: nil)
self.arrow.contents = arrowImage?.CGImage
self.arrow.frame = CGRect(origin: CGPoint(), size: arrowImage?.size ?? CGSize())
self.addSubnode(self.label)
}
public override func calculateSizeThatFits(constrainedSize: CGSize) -> CGSize {
self.label.measure(CGSize(width: max(0.0, constrainedSize.width - self.arrow.frame.size.width - self.arrowSpacing), height: constrainedSize.height))
return CGSize(width: self.arrow.frame.size.width + self.arrowSpacing + self.label.calculatedSize.width, height: max(self.arrow.frame.size.height, self.label.calculatedSize.height))
}
var labelFrame: CGRect {
get {
return CGRect(x: self.arrow.frame.size.width + self.arrowSpacing, y: floor((self.frame.size.height - self.label.calculatedSize.height) / 2.0), width: self.label.calculatedSize.width, height: self.label.calculatedSize.height)
}
}
public override func layout() {
super.layout()
if self.suspendLayout {
return
}
self.arrow.frame = CGRect(x: 0.0, y: floor((self.frame.size.height - arrow.frame.size.height) / 2.0), width: self.arrow.frame.size.width, height: self.arrow.frame.size.height)
self.label.frame = self.labelFrame
}
private func touchInsideApparentBounds(touch: UITouch) -> Bool {
var apparentBounds = self.bounds
let hitTestSlop = self.hitTestSlop
apparentBounds.origin.x += hitTestSlop.left
apparentBounds.size.width -= hitTestSlop.left + hitTestSlop.right
apparentBounds.origin.y += hitTestSlop.top
apparentBounds.size.height -= hitTestSlop.top + hitTestSlop.bottom
return CGRectContainsPoint(apparentBounds, touch.locationInView(self.view))
}
public override func touchesBegan(touches: Set<NSObject>!, withEvent event: UIEvent!) {
super.touchesBegan(touches, withEvent: event)
self.touchCount += touches.count
self.updateHighlightedState(true, animated: false)
}
public override func touchesMoved(touches: Set<NSObject>!, withEvent event: UIEvent!) {
super.touchesMoved(touches, withEvent: event)
self.updateHighlightedState(self.touchInsideApparentBounds(touches.first as! UITouch), animated: true)
}
public override func touchesEnded(touches: Set<NSObject>!, withEvent event: UIEvent!) {
super.touchesEnded(touches, withEvent: event)
self.updateHighlightedState(false, animated: false)
let previousTouchCount = self.touchCount
self.touchCount = max(0, self.touchCount - touches.count)
if previousTouchCount != 0 && self.touchCount == 0 && self.enabled && self.touchInsideApparentBounds(touches.first as! UITouch) {
self.pressed()
}
}
public override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
super.touchesCancelled(touches, withEvent: event)
self.touchCount = max(0, self.touchCount - touches.count)
self.updateHighlightedState(false, animated: false)
}
private var _highlighted = false
private func updateHighlightedState(highlighted: Bool, animated: Bool) {
if _highlighted != highlighted {
_highlighted = highlighted
let alpha: CGFloat = !enabled ? 1.0 : (highlighted ? 0.4 : 1.0)
if animated {
UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.BeginFromCurrentState, animations: { () -> Void in
self.alpha = alpha
}, completion: nil)
}
else {
self.alpha = alpha
}
}
}
}

120
Display/NavigationBar.swift Normal file
View File

@ -0,0 +1,120 @@
import UIKit
import AsyncDisplayKit
private enum ItemAnimation {
case None
case Push
case Pop
}
public class NavigationBar: ASDisplayNode {
private var topItem: UINavigationItem?
private var topItemWrapper: NavigationItemWrapper?
private var tempItem: UINavigationItem?
private var tempItemWrapper: NavigationItemWrapper?
var backPressed: () -> () = { }
private var collapsed: Bool {
get {
return self.frame.size.height < (20.0 + 44.0)
}
}
var _proxy: NavigationBarProxy?
var proxy: NavigationBarProxy? {
get {
return self._proxy
}
set(value) {
self._proxy = value
self._proxy?.setItemsProxy = {[weak self] previousItems, items, animated in
if let strongSelf = self {
var animation = ItemAnimation.None
if animated && previousItems.count != 0 && items.count != 0 {
if previousItems.filter({element in element === items[items.count - 1]}).count != 0 {
animation = .Pop
}
else {
animation = .Push
}
}
let count = items.count
if count != 0 {
strongSelf.updateTopItem(items[count - 1] as! UINavigationItem, previousItem: count >= 2 ? (items[count - 2] as! UINavigationItem) : nil, animation: animation)
}
}
return
}
}
}
let stripeView: UIView
public override init() {
stripeView = UIView()
stripeView.backgroundColor = UIColor(red: 0.6953125, green: 0.6953125, blue: 0.6953125, alpha: 1.0)
super.init()
self.backgroundColor = UIColor(red: 0.968626451, green: 0.968626451, blue: 0.968626451, alpha: 1.0)
self.view.addSubview(stripeView)
}
private func updateTopItem(item: UINavigationItem, previousItem: UINavigationItem?, animation: ItemAnimation) {
if self.topItem !== item {
let previousTopItemWrapper = self.topItemWrapper
self.topItemWrapper = nil
self.topItem = item
self.topItemWrapper = NavigationItemWrapper(parentNode: self, navigationItem: item, previousNavigationItem: previousItem)
self.topItemWrapper?.backPressed = { [weak self] in
if let backPressed = self?.backPressed {
backPressed()
}
}
self.topItemWrapper?.layoutItems()
switch animation {
case .None:
break
case .Push:
self.topItemWrapper?.animatePush(previousTopItemWrapper, duration: 0.3)
break
case .Pop:
self.topItemWrapper?.animatePop(previousTopItemWrapper, duration: 0.3)
break
}
}
}
public func beginInteractivePopProgress(previousItem: UINavigationItem, evenMorePreviousItem: UINavigationItem?) {
self.tempItem = previousItem
self.tempItemWrapper = NavigationItemWrapper(parentNode: self, navigationItem: previousItem, previousNavigationItem: evenMorePreviousItem)
self.tempItemWrapper?.layoutItems()
self.setInteractivePopProgress(0.0)
}
public func endInteractivePopProgress() {
self.tempItem = nil
self.tempItemWrapper = nil
}
public func setInteractivePopProgress(progress: CGFloat) {
if let topItemWrapper = self.topItemWrapper {
self.tempItemWrapper?.setInteractivePopProgress(progress, previousItemWrapper: topItemWrapper)
}
}
public override func layout() {
self.stripeView.frame = CGRect(x: 0.0, y: self.frame.size.height - 0.5, width: self.frame.size.width, height: 0.5)
self.topItemWrapper?.layoutItems()
self.tempItemWrapper?.layoutItems()
}
}

View File

@ -0,0 +1,7 @@
#import <UIKit/UIKit.h>
@interface NavigationBarProxy : UINavigationBar
@property (nonatomic, copy) void (^setItemsProxy)(NSArray *, NSArray *, bool);
@end

View File

@ -0,0 +1,67 @@
#import "NavigationBarProxy.h"
@interface NavigationBarProxy ()
{
NSArray *_items;
}
@end
@implementation NavigationBarProxy
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
}
return self;
}
- (void)pushNavigationItem:(UINavigationItem *)item animated:(BOOL)animated
{
[self setItems:[[self items] arrayByAddingObject:item] animated:animated];
}
- (UINavigationItem *)popNavigationItemAnimated:(BOOL)animated
{
NSMutableArray *items = [[NSMutableArray alloc] initWithArray:[self items]];
UINavigationItem *lastItem = [items lastObject];
[items removeLastObject];
[self setItems:items animated:animated];
return lastItem;
}
- (UINavigationItem *)topItem
{
return [[self items] lastObject];
}
- (UINavigationItem *)backItem
{
NSLog(@"backItem");
return nil;
}
- (NSArray *)items
{
if (_items == nil)
return @[];
return _items;
}
- (void)setItems:(NSArray *)items
{
[self setItems:items animated:false];
}
- (void)setItems:(NSArray *)items animated:(BOOL)animated
{
NSArray *previousItems = _items;
_items = items;
if (_setItemsProxy)
_setItemsProxy(previousItems, items, animated);
}
@end

View File

@ -0,0 +1,126 @@
import UIKit
import AsyncDisplayKit
public class NavigationButtonNode: ASTextNode {
private func fontForCurrentState() -> UIFont {
return self.bold ? UIFont.boldSystemFontOfSize(17.0) : UIFont.systemFontOfSize(17.0)
}
private func attributesForCurrentState() -> [String : AnyObject] {
return [
NSFontAttributeName: self.fontForCurrentState(),
NSForegroundColorAttributeName: self.enabled ? UIColor.blueColor() : UIColor.grayColor()
]
}
private var _text: String?
public var text: String {
get {
return _text ?? ""
}
set(value) {
_text = value
self.attributedString = NSAttributedString(string: text, attributes: self.attributesForCurrentState())
}
}
private var _bold: Bool = false
public var bold: Bool {
get {
return _bold
}
set(value) {
if _bold != value {
_bold = value
self.attributedString = NSAttributedString(string: text, attributes: self.attributesForCurrentState())
}
}
}
private var touchCount = 0
public var pressed: () -> () = {}
public override init() {
super.init()
self.userInteractionEnabled = true
self.exclusiveTouch = true
self.hitTestSlop = UIEdgeInsets(top: -16.0, left: -10.0, bottom: -16.0, right: -10.0)
self.displaysAsynchronously = false
}
private func touchInsideApparentBounds(touch: UITouch) -> Bool {
var apparentBounds = self.bounds
let hitTestSlop = self.hitTestSlop
apparentBounds.origin.x += hitTestSlop.left
apparentBounds.size.width -= hitTestSlop.left + hitTestSlop.right
apparentBounds.origin.y += hitTestSlop.top
apparentBounds.size.height -= hitTestSlop.top + hitTestSlop.bottom
return CGRectContainsPoint(apparentBounds, touch.locationInView(self.view))
}
public override func touchesBegan(touches: Set<NSObject>!, withEvent event: UIEvent!) {
super.touchesBegan(touches, withEvent: event)
self.touchCount += touches.count
self.updateHighlightedState(true, animated: false)
}
public override func touchesMoved(touches: Set<NSObject>!, withEvent event: UIEvent!) {
super.touchesMoved(touches, withEvent: event)
self.updateHighlightedState(self.touchInsideApparentBounds(touches.first as! UITouch), animated: true)
}
public override func touchesEnded(touches: Set<NSObject>!, withEvent event: UIEvent!) {
super.touchesEnded(touches, withEvent: event)
self.updateHighlightedState(false, animated: false)
let previousTouchCount = self.touchCount
self.touchCount = max(0, self.touchCount - touches.count)
if previousTouchCount != 0 && self.touchCount == 0 && self.enabled && self.touchInsideApparentBounds(touches.first as! UITouch) {
self.pressed()
}
}
public override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
super.touchesCancelled(touches, withEvent: event)
self.touchCount = max(0, self.touchCount - touches.count)
self.updateHighlightedState(false, animated: false)
}
private var _highlighted = false
private func updateHighlightedState(highlighted: Bool, animated: Bool) {
if _highlighted != highlighted {
_highlighted = highlighted
let alpha: CGFloat = !enabled ? 1.0 : (highlighted ? 0.4 : 1.0)
if animated {
UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.BeginFromCurrentState, animations: { () -> Void in
self.alpha = alpha
}, completion: nil)
}
else {
self.alpha = alpha
}
}
}
public override var enabled: Bool {
get {
return super.enabled
}
set(value) {
if self.enabled != value {
super.enabled = value
self.attributedString = NSAttributedString(string: text, attributes: self.attributesForCurrentState())
}
}
}
}

View File

@ -0,0 +1,232 @@
import Foundation
import UIKit
import AsyncDisplayKit
public class NavigationController: NavigationControllerProxy, WindowContentController, UIGestureRecognizerDelegate {
private var _navigationBar: NavigationBar?
private var navigationTransitionCoordinator: NavigationTransitionCoordinator?
public override init() {
super.init()
self._navigationBar = NavigationBar()
self._navigationBar?.frame = CGRect(x: 0.0, y: 0.0, width: 320.0, height: 44.0)
self._navigationBar?.proxy = self.navigationBar as? NavigationBarProxy
self._navigationBar?.backPressed = { [weak self] in
if self?.viewControllers.count > 1 {
self?.popViewControllerAnimated(true)
}
return
}
}
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
public required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func loadView() {
super.loadView()
if let _navigationBar = self._navigationBar {
self.navigationBar.superview?.insertSubview(_navigationBar.view, aboveSubview: self.navigationBar)
}
self.navigationBar.removeFromSuperview()
self._navigationBar?.frame = navigationBarFrame(self.view.frame.size)
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: Selector("panGesture:"))
panRecognizer.delegate = self
panRecognizer.cancelsTouchesInView = true
self.view.addGestureRecognizer(panRecognizer)
if self.topViewController != nil {
self.topViewController?.view.frame = CGRect(origin: CGPoint(), size: self.view.frame.size)
}
}
func panGesture(recognizer: UIPanGestureRecognizer) {
switch recognizer.state {
case UIGestureRecognizerState.Began:
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
topController.viewWillDisappear(true)
let topView = topController.view
bottomController.viewWillAppear(true)
let bottomView = bottomController.view
let navigationTransitionCoordinator = NavigationTransitionCoordinator(container: self.view, topView: topView, bottomView: bottomView, navigationBar: self._navigationBar!)
self.navigationTransitionCoordinator = navigationTransitionCoordinator
self._navigationBar?.beginInteractivePopProgress(bottomController.navigationItem, evenMorePreviousItem: self.viewControllers.count >= 3 ? (self.viewControllers[self.viewControllers.count - 3] as UIViewController).navigationItem : nil)
}
case UIGestureRecognizerState.Changed:
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator {
let translation = recognizer.translationInView(self.view).x
navigationTransitionCoordinator.progress = max(0.0, min(1.0, translation / self.view.frame.width))
}
case UIGestureRecognizerState.Ended:
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator {
let velocity = recognizer.velocityInView(self.view).x
if velocity > 1000 || navigationTransitionCoordinator.progress > 0.2 {
navigationTransitionCoordinator.animateCompletion(velocity, completion: {
self.navigationTransitionCoordinator = nil
self._navigationBar?.endInteractivePopProgress()
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
topController.setIgnoreAppearanceMethodInvocations(true)
bottomController.setIgnoreAppearanceMethodInvocations(true)
self.popViewControllerAnimated(false)
topController.setIgnoreAppearanceMethodInvocations(false)
bottomController.setIgnoreAppearanceMethodInvocations(false)
topController.viewDidDisappear(true)
bottomController.viewDidAppear(true)
}
})
}
else {
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
topController.viewWillAppear(true)
bottomController.viewWillDisappear(true)
}
navigationTransitionCoordinator.animateCancel({
self.navigationTransitionCoordinator = nil
self._navigationBar?.endInteractivePopProgress()
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
topController.viewDidAppear(true)
bottomController.viewDidDisappear(true)
}
})
}
}
case .Cancelled:
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator {
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
topController.viewWillAppear(true)
bottomController.viewWillDisappear(true)
}
navigationTransitionCoordinator.animateCancel({
self.navigationTransitionCoordinator = nil
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
topController.viewDidAppear(true)
bottomController.viewDidDisappear(true)
}
})
}
default:
break
}
}
public override func pushViewController(viewController: UIViewController, animated: Bool) {
var controllers = self.viewControllers
controllers.append(viewController)
self.setViewControllers(controllers, animated: animated)
}
public override func popViewControllerAnimated(animated: Bool) -> UIViewController? {
var controller: UIViewController?
var controllers = self.viewControllers
if controllers.count != 0 {
controller = controllers[controllers.count - 1] as UIViewController
controllers.removeAtIndex(controllers.count - 1)
self.setViewControllers(controllers, animated: animated)
}
return controller
}
public override func setViewControllers(viewControllers: [UIViewController], animated: Bool) {
if viewControllers.count > 0 {
let topViewController = viewControllers[viewControllers.count - 1] as UIViewController
if let controller = topViewController as? WindowContentController {
controller.setViewSize(self.view.bounds.size, duration: 0.0)
} else {
topViewController.view.frame = CGRect(origin: CGPoint(), size: self.view.bounds.size)
}
}
super.setViewControllers(viewControllers, animated: animated)
}
private func navigationBarFrame(size: CGSize) -> CGRect {
let condensedBar = (size.height < size.width || size.height <= 320.0) && size.height < 768.0
return CGRect(x: 0.0, y: 0.0, width: size.width, height: 20.0 + (size.height >= size.width ? 44.0 : 32.0))
}
public func setViewSize(toSize: CGSize, duration: NSTimeInterval) {
if duration > DBL_EPSILON {
animateRotation(self.view, toFrame: CGRect(x: 0.0, y: 0.0, width: toSize.width, height: toSize.height), duration: duration)
}
else {
self.view.frame = CGRect(x: 0.0, y: 0.0, width: toSize.width, height: toSize.height)
}
if duration > DBL_EPSILON {
animateRotation(self._navigationBar, toFrame: self.navigationBarFrame(toSize), duration: duration)
}
else {
self._navigationBar?.frame = self.navigationBarFrame(toSize)
}
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator {
//navigationTransitionView.frame = CGRectMake(0.0, 0.0, toSize.width, toSize.height)
if self.viewControllers.count >= 2 {
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
if let controller = bottomController as? WindowContentController {
controller.setViewSize(toSize, duration: duration)
}
bottomController.view.frame = CGRectMake(0.0, 0.0, toSize.width, toSize.height)
}
}
if let topViewController = self.topViewController {
if let controller = topViewController as? WindowContentController {
controller.setViewSize(toSize, duration: duration)
} else {
topViewController.view.frame = CGRectMake(0.0, 0.0, toSize.width, toSize.height)
}
}
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator {
navigationTransitionCoordinator.updateProgress()
}
}
public func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
public func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailByGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}

View File

@ -0,0 +1,7 @@
#import <UIKit/UIKit.h>
@interface NavigationControllerProxy : UINavigationController
- (instancetype)init;
@end

View File

@ -0,0 +1,16 @@
#import "NavigationControllerProxy.h"
#import "NavigationBarProxy.h"
@implementation NavigationControllerProxy
- (instancetype)init
{
self = [super initWithNavigationBarClass:[NavigationBarProxy class] toolbarClass:[UIToolbar class]];
if (self != nil)
{
}
return self;
}
@end

View File

@ -0,0 +1,4 @@
struct NavigationItemTransitionState {
let backButtonPosition: CGPoint?
let titlePosition: CGPoint
}

View File

@ -0,0 +1,332 @@
import UIKit
import AsyncDisplayKit
internal class NavigationItemWrapper {
let parentNode: ASDisplayNode
private var navigationItem: UINavigationItem
private var setTitleListenerKey: Int!
private var setLeftBarButtonItemListenerKey: Int!
private var setRightBarButtonItemListenerKey: Int!
private var previousNavigationItem: UINavigationItem?
private var previousItemSetTitleListenerKey: Int?
private let titleNode: NavigationTitleNode
private var backButtonNode: NavigationBackButtonNode
private var leftBarButtonItem: UIBarButtonItem?
private var leftBarButtonItemWrapper: BarButtonItemWrapper?
private var rightBarButtonItem: UIBarButtonItem?
private var rightBarButtonItemWrapper: BarButtonItemWrapper?
var backPressed: () -> () = { }
var suspendLayout = false
init(parentNode: ASDisplayNode, navigationItem: UINavigationItem, previousNavigationItem: UINavigationItem?) {
self.parentNode = parentNode
self.navigationItem = navigationItem
self.previousNavigationItem = previousNavigationItem
self.titleNode = NavigationTitleNode(text: "")
self.parentNode.addSubnode(titleNode)
self.backButtonNode = NavigationBackButtonNode()
backButtonNode.pressed = { [weak self] in
if let backPressed = self?.backPressed {
backPressed()
}
}
self.parentNode.addSubnode(self.backButtonNode)
self.previousItemSetTitleListenerKey = previousNavigationItem?.addSetTitleListener({ [weak self] title in
self?.setBackButtonTitle(title)
return
})
self.setTitleListenerKey = navigationItem.addSetTitleListener({ [weak self] title in
self?.setTitle(title)
return
})
self.setLeftBarButtonItemListenerKey = navigationItem.addSetLeftBarButtonItemListener({ [weak self] barButtonItem, animated in
self?.setLeftBarButtonItem(barButtonItem, animated: animated)
return
})
self.setRightBarButtonItemListenerKey = navigationItem.addSetRightBarButtonItemListener({ [weak self] barButtonItem, animated in
self?.setRightBarButtonItem(barButtonItem, animated: animated)
return
})
self.setTitle(navigationItem.title ?? "")
self.setBackButtonTitle(previousNavigationItem?.title ?? "Back")
self.setLeftBarButtonItem(navigationItem.leftBarButtonItem, animated: false)
self.setRightBarButtonItem(navigationItem.rightBarButtonItem, animated: false)
}
deinit {
self.navigationItem.removeSetTitleListener(self.setTitleListenerKey)
self.navigationItem.removeSetLeftBarButtonItemListener(self.setLeftBarButtonItemListenerKey)
self.navigationItem.removeSetRightBarButtonItemListener(self.setRightBarButtonItemListenerKey)
if let previousItemSetTitleListenerKey = self.previousItemSetTitleListenerKey {
self.previousNavigationItem?.removeSetTitleListener(previousItemSetTitleListenerKey)
}
self.titleNode.removeFromSupernode()
self.backButtonNode.removeFromSupernode()
}
func setBackButtonTitle(backButtonTitle: String) {
self.backButtonNode.text = backButtonTitle
self.layoutItems()
}
func setTitle(title: String) {
self.titleNode.text = title
self.layoutItems()
}
func setLeftBarButtonItem(leftBarButtonItem: UIBarButtonItem?, animated: Bool) {
if self.leftBarButtonItem !== leftBarButtonItem {
self.leftBarButtonItem = leftBarButtonItem
self.leftBarButtonItemWrapper = nil
if let leftBarButtonItem = leftBarButtonItem {
self.leftBarButtonItemWrapper = BarButtonItemWrapper(parentNode: self.parentNode, barButtonItem: leftBarButtonItem, layoutNeeded: { [weak self] in
self?.layoutItems()
return
})
}
}
self.backButtonNode.hidden = self.previousNavigationItem == nil || self.leftBarButtonItemWrapper != nil
}
func setRightBarButtonItem(rightBarButtonItem: UIBarButtonItem?, animated: Bool) {
if self.rightBarButtonItem !== rightBarButtonItem {
self.rightBarButtonItem = rightBarButtonItem
self.rightBarButtonItemWrapper = nil
if let rightBarButtonItem = rightBarButtonItem {
self.rightBarButtonItemWrapper = BarButtonItemWrapper(parentNode: self.parentNode, barButtonItem: rightBarButtonItem, layoutNeeded: { [weak self] in
self?.layoutItems()
return
})
}
}
}
private var collapsed: Bool {
get {
return self.parentNode.frame.size.height < (20.0 + 44.0)
}
}
var titleFrame: CGRect {
get {
return CGRect(x: floor((self.parentNode.frame.size.width - self.titleNode.calculatedSize.width) / 2.0), y: self.collapsed ? 24.0 : 31.0, width: self.titleNode.calculatedSize.width, height: self.titleNode.calculatedSize.height)
}
}
var titlePosition: CGPoint {
get {
let titleFrame = self.titleFrame
return CGPoint(x: CGRectGetMidX(titleFrame), y: CGRectGetMidY(titleFrame))
}
}
var backButtonFrame: CGRect {
get {
return CGRect(x: self.collapsed ? 15.0 : 8.0, y: self.collapsed ? 24.0 : 31.0, width: backButtonNode.calculatedSize.width, height: backButtonNode.calculatedSize.height)
}
}
var backButtonLabelFrame: CGRect {
get {
let backButtonFrame = self.backButtonFrame
let labelFrame = self.backButtonNode.labelFrame
return CGRect(origin: CGPoint(x: backButtonFrame.origin.x + labelFrame.origin.x, y: backButtonFrame.origin.y + labelFrame.origin.y), size: labelFrame.size)
}
}
var backButtonLabelPosition: CGPoint {
get {
let backButtonLabelFrame = self.backButtonLabelFrame
return CGPoint(x: CGRectGetMidX(backButtonLabelFrame), y: CGRectGetMidY(backButtonLabelFrame))
}
}
var leftButtonFrame: CGRect? {
get {
if let leftBarButtonItemWrapper = self.leftBarButtonItemWrapper {
return CGRect(x: self.collapsed ? 15.0 : 8.0, y: self.collapsed ? 24.0 : 31.0, width: leftBarButtonItemWrapper.buttonNode.calculatedSize.width, height: leftBarButtonItemWrapper.buttonNode.calculatedSize.height)
}
else {
return nil
}
}
}
var rightButtonFrame: CGRect? {
get {
if let rightBarButtonItemWrapper = self.rightBarButtonItemWrapper {
return CGRect(x: self.parentNode.frame.size.width - rightBarButtonItemWrapper.buttonNode.calculatedSize.width - (self.collapsed ? 15.0 : 8.0), y: self.collapsed ? 24.0 : 31.0, width: rightBarButtonItemWrapper.buttonNode.calculatedSize.width, height: rightBarButtonItemWrapper.buttonNode.calculatedSize.height)
}
else {
return nil
}
}
}
var transitionState: NavigationItemTransitionState {
get {
return NavigationItemTransitionState(backButtonPosition: self.backButtonNode.hidden ? nil : self.backButtonLabelPosition, titlePosition: self.titlePosition)
}
}
func layoutItems() {
if suspendLayout {
return
}
self.titleNode.measure(self.parentNode.bounds.size)
self.titleNode.frame = self.titleFrame
self.backButtonNode.measure(self.parentNode.frame.size)
self.backButtonNode.frame = self.backButtonFrame
self.backButtonNode.layout()
if let leftBarButtonItemWrapper = self.leftBarButtonItemWrapper {
leftBarButtonItemWrapper.buttonNode.measure(self.parentNode.frame.size)
leftBarButtonItemWrapper.buttonNode.frame = self.leftButtonFrame!
}
if let rightBarButtonItemWrapper = self.rightBarButtonItemWrapper {
rightBarButtonItemWrapper.buttonNode.measure(self.parentNode.frame.size)
rightBarButtonItemWrapper.buttonNode.frame = self.rightButtonFrame!
}
}
func interpolatePosition(from: CGPoint, _ to: CGPoint, value: CGFloat) -> CGPoint {
return CGPoint(x: from.x * (CGFloat(1.0) - value) + to.x * value, y: from.y * (CGFloat(1.0) - value) + to.y * value)
}
func interpolateValue(from: CGFloat, _ to: CGFloat, value: CGFloat) -> CGFloat {
return (from * (CGFloat(1.0) - value)) + (to * value)
}
func applyPushAnimationProgress(previousItemState previousItemState: NavigationItemTransitionState, value: CGFloat) {
let titleStartPosition = CGPoint(x: self.parentNode.frame.size.width + self.titleNode.frame.size.width / 2.0, y: self.titlePosition.y)
let titleStartAlpha: CGFloat = 0.0
let titleEndPosition = self.titlePosition
let titleEndAlpha: CGFloat = 1.0
self.titleNode.position = self.interpolatePosition(titleStartPosition, titleEndPosition, value: value)
self.titleNode.alpha = self.interpolateValue(titleStartAlpha, titleEndAlpha, value: value)
self.rightBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(0.0, 1.0, value: value)
self.leftBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(0.0, 1.0, value: value)
self.backButtonNode.label.position = self.interpolatePosition(CGPoint(x: previousItemState.titlePosition.x - self.backButtonFrame.origin.x, y: previousItemState.titlePosition.y - self.backButtonFrame.origin.y), CGPoint(x: self.backButtonLabelPosition.x - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), value: value)
self.backButtonNode.alpha = self.interpolateValue(0.0, 1.0, value: value)
}
func applyPushAnimationProgress(nextItemState nextItemState: NavigationItemTransitionState, value: CGFloat) {
let titleStartPosition = self.titlePosition
let titleStartAlpha: CGFloat = 1.0
var titleEndPosition = CGPoint(x: -self.titleNode.frame.size.width / 2.0, y: self.titlePosition.y)
if let nextItemBackButtonPosition = nextItemState.backButtonPosition {
titleEndPosition = nextItemBackButtonPosition
}
let titleEndAlpha: CGFloat = 0.0
self.titleNode.position = self.interpolatePosition(titleStartPosition, titleEndPosition, value: value)
self.titleNode.alpha = self.interpolateValue(titleStartAlpha, titleEndAlpha, value: value)
self.rightBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(1.0, 0.0, value: value)
self.leftBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(1.0, 0.0, value: value)
self.backButtonNode.label.position = self.interpolatePosition(CGPoint(x: self.backButtonLabelPosition.x - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), CGPoint(x: -self.backButtonLabelFrame.size.width - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), value: value)
self.backButtonNode.label.alpha = self.interpolateValue(1.0, 0.0, value: value)
self.backButtonNode.arrow.alpha = self.interpolateValue(1.0, nextItemState.backButtonPosition == nil ? 0.0 : 1.0, value: value)
}
func applyPopAnimationProgress(previousItemState previousItemState: NavigationItemTransitionState, value: CGFloat) {
var titleStartPosition = CGPoint(x: -self.titleNode.frame.size.width / 2.0, y: self.titlePosition.y)
if let previousItemBackButtonPosition = previousItemState.backButtonPosition {
titleStartPosition = previousItemBackButtonPosition
}
let titleStartAlpha: CGFloat = 0.0
let titleEndPosition = self.titlePosition
let titleEndAlpha: CGFloat = 1.0
self.titleNode.position = self.interpolatePosition(titleStartPosition, titleEndPosition, value: value)
self.titleNode.alpha = self.interpolateValue(titleStartAlpha, titleEndAlpha, value: value)
self.rightBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(0.0, 1.0, value: value)
self.leftBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(0.0, 1.0, value: value)
self.backButtonNode.label.position = self.interpolatePosition(CGPoint(x: -self.backButtonLabelFrame.size.width - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), CGPoint(x: self.backButtonLabelPosition.x - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), value: value)
self.backButtonNode.label.alpha = self.interpolateValue(0.0, 1.0, value: value)
self.backButtonNode.arrow.alpha = self.interpolateValue(previousItemState.backButtonPosition == nil ? 0.0 : 1.0, 1.0, value: value)
}
func applyPopAnimationProgress(nextItemState nextItemState: NavigationItemTransitionState, value: CGFloat) {
let titleStartPosition = self.titlePosition
let titleStartAlpha: CGFloat = 1.0
let titleEndPosition = CGPoint(x: self.parentNode.frame.size.width + self.titleNode.frame.size.width / 2.0, y: self.titlePosition.y)
let titleEndAlpha: CGFloat = 0.0
self.titleNode.position = self.interpolatePosition(titleStartPosition, titleEndPosition, value: value)
self.titleNode.alpha = self.interpolateValue(titleStartAlpha, titleEndAlpha, value: value)
self.rightBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(1.0, 0.0, value: value)
self.leftBarButtonItemWrapper?.buttonNode.alpha = self.interpolateValue(1.0, 0.0, value: value)
self.backButtonNode.label.position = self.interpolatePosition(CGPoint(x: self.backButtonLabelPosition.x - self.backButtonFrame.origin.x, y: self.backButtonLabelPosition.y - self.backButtonFrame.origin.y), CGPoint(x: nextItemState.titlePosition.x - self.backButtonFrame.origin.x, y: nextItemState.titlePosition.y - self.backButtonFrame.origin.y), value: value)
self.backButtonNode.label.alpha = self.interpolateValue(1.0, 0.0, value: value)
self.backButtonNode.arrow.alpha = self.interpolateValue(1.0, 0.0, value: value)
}
func animatePush(previousItemWrapper: NavigationItemWrapper?, duration: NSTimeInterval) {
if let previousItemWrapper = previousItemWrapper {
self.suspendLayout = true
self.backButtonNode.suspendLayout = true
let transitionState = self.transitionState
let previousItemState = previousItemWrapper.transitionState
self.applyPushAnimationProgress(previousItemState: previousItemState, value: 0.0)
previousItemWrapper.applyPushAnimationProgress(nextItemState: transitionState, value: 0.0)
UIView.animateWithDuration(duration, delay: 0.0, options: UIViewAnimationOptions(rawValue: 7 << 16), animations: { () -> Void in
self.applyPushAnimationProgress(previousItemState: previousItemState, value: 1.0)
previousItemWrapper.applyPushAnimationProgress(nextItemState: transitionState, value: 1.0)
}, completion: { completed in
self.suspendLayout = false
self.backButtonNode.suspendLayout = false
previousItemWrapper.applyPushAnimationProgress(nextItemState: self.transitionState, value: 1.0)
})
}
}
func animatePop(previousItemWrapper: NavigationItemWrapper?, duration: NSTimeInterval) {
if let previousItemWrapper = previousItemWrapper {
self.applyPopAnimationProgress(previousItemState: previousItemWrapper.transitionState, value: 0.0)
previousItemWrapper.applyPopAnimationProgress(nextItemState: self.transitionState, value: 0.0)
UIView.animateWithDuration(duration, delay: 0.0, options: UIViewAnimationOptions(rawValue: 7 << 16), animations: { () -> Void in
self.applyPopAnimationProgress(previousItemState: previousItemWrapper.transitionState, value: 1.0)
previousItemWrapper.applyPopAnimationProgress(nextItemState: self.transitionState, value: 1.0)
}, completion: { completed in
previousItemWrapper.applyPopAnimationProgress(nextItemState: self.transitionState, value: 0.0)
})
}
}
func setInteractivePopProgress(progress: CGFloat, previousItemWrapper: NavigationItemWrapper) {
self.applyPopAnimationProgress(previousItemState: previousItemWrapper.transitionState, value: progress)
previousItemWrapper.applyPopAnimationProgress(nextItemState: self.transitionState, value: progress)
}
}

View File

@ -0,0 +1,50 @@
import UIKit
import AsyncDisplayKit
public class NavigationTitleNode: ASDisplayNode {
private let label: ASTextNode
private var _text: NSString = ""
public var text: NSString {
get {
return self._text
}
set(value) {
self._text = value
self.setText(value)
}
}
public init(text: NSString) {
self.label = ASTextNode()
self.label.displaysAsynchronously = false
super.init()
self.addSubnode(self.label)
self.setText(text)
}
public required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setText(text: NSString) {
var titleAttributes = [String : AnyObject]()
titleAttributes[NSFontAttributeName] = UIFont.boldSystemFontOfSize(17.0)
titleAttributes[NSForegroundColorAttributeName] = UIColor.blackColor()
let titleString = NSAttributedString(string: text as String, attributes: titleAttributes)
self.label.attributedString = titleString
self.invalidateCalculatedLayout()
}
public override func calculateSizeThatFits(constrainedSize: CGSize) -> CGSize {
self.label.measure(constrainedSize)
return self.label.calculatedSize
}
public override func layout() {
self.label.frame = self.bounds
}
}

View File

@ -0,0 +1,82 @@
import UIKit
class NavigationTransitionCoordinator {
private var _progress: CGFloat = 0.0
var progress: CGFloat {
get {
return self._progress
}
set(value) {
self._progress = value
self.navigationBar.setInteractivePopProgress(value)
self.updateProgress()
}
}
private let container: UIView
private let topView: UIView
private let topViewSuperview: UIView?
private let bottomView: UIView
private let dimView: UIView
private let navigationBar: NavigationBar
init(container: UIView, topView: UIView, bottomView: UIView, navigationBar: NavigationBar) {
self.container = container
self.topView = topView
self.topViewSuperview = topView.superview
self.bottomView = bottomView
self.dimView = UIView()
self.dimView.backgroundColor = UIColor.blackColor()
self.navigationBar = navigationBar
if let topViewSuperview = self.topViewSuperview {
topViewSuperview.insertSubview(bottomView, belowSubview: topView)
}
self.updateProgress()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func updateProgress() {
self.topView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(self.progress * self.container.bounds.size.width), y: 0.0), size: self.container.bounds.size)
self.dimView.frame = self.container.bounds
self.dimView.alpha = (1.0 - self.progress) * 0.1
self.bottomView.frame = CGRect(origin: CGPoint(x: ((self.progress - 1.0) * self.container.bounds.size.width * 0.3), y: 0.0), size: self.container.bounds.size)
}
func animateCancel(completion: () -> ()) {
UIView.animateWithDuration(0.1, delay: 0.0, options: UIViewAnimationOptions(), animations: { () -> Void in
self.progress = 0.0
}) { (completed) -> Void in
if let topViewSuperview = self.topViewSuperview {
topViewSuperview.addSubview(self.topView)
}
else {
self.topView.removeFromSuperview()
}
self.bottomView.removeFromSuperview()
completion()
}
}
func animateCompletion(velocity: CGFloat, completion: () -> ()) {
let distance = (1.0 - self.progress) * self.container.bounds.size.width
UIView.animateWithDuration(NSTimeInterval(max(0.05, min(0.2, abs(distance / velocity)))), delay: 0.0, options: UIViewAnimationOptions(), animations: { () -> Void in
self.progress = 1.0
}) { (completed) -> Void in
if let topViewSuperview = self.topViewSuperview {
topViewSuperview.addSubview(self.topView)
}
else {
self.topView.removeFromSuperview()
}
self.bottomView.removeFromSuperview()
completion()
}
}
}

View File

@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
typedef bool (^NotificationHandlerBlock)(NSString *, id, NSDictionary *);
@interface NotificationCenterUtils : NSObject
+ (void)addNotificationHandler:(NotificationHandlerBlock)handler;
@end

View File

@ -0,0 +1,51 @@
#import "NotificationCenterUtils.h"
#import "RuntimeUtils.h"
static NSMutableArray *notificationHandlers()
{
static NSMutableArray *array = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
array = [[NSMutableArray alloc] init];
});
return array;
}
@interface NSNotificationCenter (_a65afc19)
@end
@implementation NSNotificationCenter (_a65afc19)
- (void)_a65afc19_postNotificationName:(NSString *)aName object:(id)anObject userInfo:(NSDictionary *)aUserInfo
{
for (NotificationHandlerBlock handler in notificationHandlers())
{
if (handler(aName, anObject, aUserInfo))
return;
}
[self _a65afc19_postNotificationName:aName object:anObject userInfo:aUserInfo];
}
@end
@implementation NotificationCenterUtils
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
[RuntimeUtils swizzleInstanceMethodOfClass:[NSNotificationCenter class] currentSelector:@selector(postNotificationName:object:userInfo:) newSelector:@selector(_a65afc19_postNotificationName:object:userInfo:)];
});
}
+ (void)addNotificationHandler:(bool (^)(NSString *, id, NSDictionary *))handler
{
[notificationHandlers() addObject:[handler copy]];
}
@end

20
Display/RuntimeUtils.h Normal file
View File

@ -0,0 +1,20 @@
#import <Foundation/Foundation.h>
typedef enum {
NSObjectAssociationPolicyRetain = 0,
NSObjectAssociationPolicyCopy = 1
} NSObjectAssociationPolicy;
@interface RuntimeUtils : NSObject
+ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector;
@end
@interface NSObject (AssociatedObject)
- (void)setAssociatedObject:(id)object forKey:(void const *)key;
- (void)setAssociatedObject:(id)object forKey:(void const *)key associationPolicy:(NSObjectAssociationPolicy)associationPolicy;
- (id)associatedObjectForKey:(void const *)key;
@end

56
Display/RuntimeUtils.m Normal file
View File

@ -0,0 +1,56 @@
#import "RuntimeUtils.h"
#import <objc/runtime.h>
@implementation RuntimeUtils
+ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector
{
Method origMethod = nil, newMethod = nil;
origMethod = class_getInstanceMethod(targetClass, currentSelector);
newMethod = class_getInstanceMethod(targetClass, newSelector);
if ((origMethod != nil) && (newMethod != nil))
{
if(class_addMethod(targetClass, currentSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
{
class_replaceMethod(targetClass, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
}
else
method_exchangeImplementations(origMethod, newMethod);
}
}
@end
@implementation NSObject (AssociatedObject)
- (void)setAssociatedObject:(id)object forKey:(void const *)key
{
[self setAssociatedObject:object forKey:key associationPolicy:NSObjectAssociationPolicyRetain];
}
- (void)setAssociatedObject:(id)object forKey:(void const *)key associationPolicy:(NSObjectAssociationPolicy)associationPolicy
{
int policy = 0;
switch (associationPolicy)
{
case NSObjectAssociationPolicyRetain:
policy = OBJC_ASSOCIATION_RETAIN_NONATOMIC;
break;
case NSObjectAssociationPolicyCopy:
policy = OBJC_ASSOCIATION_COPY_NONATOMIC;
break;
default:
policy = OBJC_ASSOCIATION_RETAIN_NONATOMIC;
break;
}
objc_setAssociatedObject(self, key, object, policy);
}
- (id)associatedObjectForKey:(void const *)key
{
return objc_getAssociatedObject(self, key);
}
@end

View File

@ -0,0 +1,15 @@
#import <UIKit/UIKit.h>
typedef void (^UIBarButtonItemSetTitleListener)(NSString *);
typedef void (^UIBarButtonItemSetEnabledListener)(BOOL);
@interface UIBarButtonItem (Proxy)
- (void)performActionOnTarget;
- (NSInteger)addSetTitleListener:(UIBarButtonItemSetTitleListener)listener;
- (void)removeSetTitleListener:(NSInteger)key;
- (NSInteger)addSetEnabledListener:(UIBarButtonItemSetEnabledListener)listener;
- (void)removeSetEnabledListener:(NSInteger)key;
@end

View File

@ -0,0 +1,83 @@
#import "UIBarButtonItem+Proxy.h"
#import "NSBag.h"
#import "RuntimeUtils.h"
static const void *setEnabledListenerBagKey = &setEnabledListenerBagKey;
static const void *setTitleListenerBagKey = &setTitleListenerBagKey;
@implementation UIBarButtonItem (Proxy)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
[RuntimeUtils swizzleInstanceMethodOfClass:[UIBarButtonItem class] currentSelector:@selector(setEnabled:) newSelector:@selector(_c1e56039_setEnabled:)];
[RuntimeUtils swizzleInstanceMethodOfClass:[UIBarButtonItem class] currentSelector:@selector(setTitle:) newSelector:@selector(_c1e56039_setTitle:)];
});
}
- (void)_c1e56039_setEnabled:(BOOL)enabled
{
[self _c1e56039_setEnabled:enabled];
[(NSBag *)[self associatedObjectForKey:setEnabledListenerBagKey] enumerateItems:^(UIBarButtonItemSetEnabledListener listener)
{
listener(enabled);
}];
}
- (void)_c1e56039_setTitle:(NSString *)title
{
[self _c1e56039_setTitle:title];
[(NSBag *)[self associatedObjectForKey:setTitleListenerBagKey] enumerateItems:^(UIBarButtonItemSetTitleListener listener)
{
listener(title);
}];
}
- (void)performActionOnTarget
{
NSAssert(self.target != nil, @"self.target != nil");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.target performSelector:self.action];
#pragma clang diagnostic pop
}
- (NSInteger)addSetTitleListener:(UIBarButtonItemSetTitleListener)listener
{
NSBag *bag = [self associatedObjectForKey:setTitleListenerBagKey];
if (bag == nil)
{
bag = [[NSBag alloc] init];
[self setAssociatedObject:bag forKey:setTitleListenerBagKey];
}
return [bag addItem:[listener copy]];
}
- (void)removeSetTitleListener:(NSInteger)key
{
[(NSBag *)[self associatedObjectForKey:setTitleListenerBagKey] removeItem:key];
}
- (NSInteger)addSetEnabledListener:(UIBarButtonItemSetEnabledListener)listener
{
NSBag *bag = [self associatedObjectForKey:setEnabledListenerBagKey];
if (bag == nil)
{
bag = [[NSBag alloc] init];
[self setAssociatedObject:bag forKey:setEnabledListenerBagKey];
}
return [bag addItem:[listener copy]];
}
- (void)removeSetEnabledListener:(NSInteger)key
{
[(NSBag *)[self associatedObjectForKey:setEnabledListenerBagKey] removeItem:key];
}
@end

7
Display/UIKitUtils.h Normal file
View File

@ -0,0 +1,7 @@
#import <UIKit/UIKit.h>
@interface UIView (AnimationUtils)
+ (double)animationDurationFactor;
@end

18
Display/UIKitUtils.m Normal file
View File

@ -0,0 +1,18 @@
#import "UIKitUtils.h"
#if TARGET_IPHONE_SIMULATOR
UIKIT_EXTERN float UIAnimationDragCoefficient(); // UIKit private drag coeffient, use judiciously
#endif
@implementation UIView (AnimationUtils)
+ (double)animationDurationFactor
{
#if TARGET_IPHONE_SIMULATOR
return (double)UIAnimationDragCoefficient();
#endif
return 1.0f;
}
@end

31
Display/UIKitUtils.swift Normal file
View File

@ -0,0 +1,31 @@
import UIKit
public func dumpViews(view: UIView) {
dumpViews(view, indent: "")
}
private func dumpViews(view: UIView, indent: String = "") {
print("\(indent)\(view)")
let nextIndent = indent + "-"
for subview in view.subviews {
dumpViews(subview as UIView, indent: nextIndent)
}
}
public func dumpLayers(layer: CALayer) {
dumpLayers(layer, indent: "")
}
private func dumpLayers(layer: CALayer, indent: String = "") {
print("\(indent)\(layer)(\(layer.frame))")
if layer.sublayers != nil {
let nextIndent = indent + ".."
for sublayer in layer.sublayers ?? [] {
dumpLayers(sublayer as CALayer, indent: nextIndent)
}
}
}
public func floorToScreenPixels(value: CGFloat) -> CGFloat {
return floor(value * 2.0) / 2.0
}

View File

@ -0,0 +1,15 @@
#import <UIKit/UIKit.h>
typedef void (^UINavigationItemSetTitleListener)(NSString *);
typedef void (^UINavigationItemSetBarButtonItemListener)(UIBarButtonItem *, BOOL);
@interface UINavigationItem (Proxy)
- (NSInteger)addSetTitleListener:(UINavigationItemSetTitleListener)listener;
- (void)removeSetTitleListener:(NSInteger)key;
- (NSInteger)addSetLeftBarButtonItemListener:(UINavigationItemSetBarButtonItemListener)listener;
- (void)removeSetLeftBarButtonItemListener:(NSInteger)key;
- (NSInteger)addSetRightBarButtonItemListener:(UINavigationItemSetBarButtonItemListener)listener;
- (void)removeSetRightBarButtonItemListener:(NSInteger)key;
@end

View File

@ -0,0 +1,101 @@
#import "UINavigationItem+Proxy.h"
#import "NSBag.h"
#import "RuntimeUtils.h"
static const void *setTitleListenerBagKey = &setTitleListenerBagKey;
static const void *setLeftBarButtonItemListenerBagKey = &setLeftBarButtonItemListenerBagKey;
static const void *setRightBarButtonItemListenerBagKey = &setRightBarButtonItemListenerBagKey;
@implementation UINavigationItem (Proxy)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
[RuntimeUtils swizzleInstanceMethodOfClass:[UINavigationItem class] currentSelector:@selector(setTitle:) newSelector:@selector(_ac91f40f_setTitle:)];
[RuntimeUtils swizzleInstanceMethodOfClass:[UINavigationItem class] currentSelector:@selector(setLeftBarButtonItem:) newSelector:@selector(_ac91f40f_setLeftBarButtonItem:animated:)];
[RuntimeUtils swizzleInstanceMethodOfClass:[UINavigationItem class] currentSelector:@selector(setRightBarButtonItem:) newSelector:@selector(_ac91f40f_setRightBarButtonItem:animated:)];
});
}
- (void)_ac91f40f_setTitle:(NSString *)title
{
[self _ac91f40f_setTitle:title];
[(NSBag *)[self associatedObjectForKey:setTitleListenerBagKey] enumerateItems:^(UINavigationItemSetTitleListener listener)
{
listener(title);
}];
}
- (void)_ac91f40f_setLeftBarButtonItem:(UIBarButtonItem *)leftBarButtonItem animated:(BOOL)animated
{
[self _ac91f40f_setLeftBarButtonItem:leftBarButtonItem animated:animated];
[(NSBag *)[self associatedObjectForKey:setLeftBarButtonItemListenerBagKey] enumerateItems:^(UINavigationItemSetBarButtonItemListener listener)
{
listener(leftBarButtonItem, animated);
}];
}
- (void)_ac91f40f_setRightBarButtonItem:(UIBarButtonItem *)rightBarButtonItem animated:(BOOL)animated
{
[self _ac91f40f_setRightBarButtonItem:rightBarButtonItem animated:animated];
[(NSBag *)[self associatedObjectForKey:setRightBarButtonItemListenerBagKey] enumerateItems:^(UINavigationItemSetBarButtonItemListener listener)
{
listener(rightBarButtonItem, animated);
}];
}
- (NSInteger)addSetTitleListener:(UINavigationItemSetTitleListener)listener
{
NSBag *bag = [self associatedObjectForKey:setTitleListenerBagKey];
if (bag == nil)
{
bag = [[NSBag alloc] init];
[self setAssociatedObject:bag forKey:setTitleListenerBagKey];
}
return [bag addItem:[listener copy]];
}
- (void)removeSetTitleListener:(NSInteger)key
{
[(NSBag *)[self associatedObjectForKey:setTitleListenerBagKey] removeItem:key];
}
- (NSInteger)addSetLeftBarButtonItemListener:(UINavigationItemSetBarButtonItemListener)listener
{
NSBag *bag = [self associatedObjectForKey:setLeftBarButtonItemListenerBagKey];
if (bag == nil)
{
bag = [[NSBag alloc] init];
[self setAssociatedObject:bag forKey:setLeftBarButtonItemListenerBagKey];
}
return [bag addItem:[listener copy]];
}
- (void)removeSetLeftBarButtonItemListener:(NSInteger)key
{
[(NSBag *)[self associatedObjectForKey:setLeftBarButtonItemListenerBagKey] removeItem:key];
}
- (NSInteger)addSetRightBarButtonItemListener:(UINavigationItemSetBarButtonItemListener)listener
{
NSBag *bag = [self associatedObjectForKey:setRightBarButtonItemListenerBagKey];
if (bag == nil)
{
bag = [[NSBag alloc] init];
[self setAssociatedObject:bag forKey:setRightBarButtonItemListenerBagKey];
}
return [bag addItem:[listener copy]];
}
- (void)removeSetRightBarButtonItemListener:(NSInteger)key
{
[(NSBag *)[self associatedObjectForKey:setRightBarButtonItemListenerBagKey] removeItem:key];
}
@end

View File

@ -0,0 +1,7 @@
#import <UIKit/UIKit.h>
@interface UIViewController (Navigation)
- (void)setIgnoreAppearanceMethodInvocations:(BOOL)ignoreAppearanceMethodInvocations;
@end

View File

@ -0,0 +1,55 @@
#import "UIViewController+Navigation.h"
#import "RuntimeUtils.h"
static const void *UIViewControllerIgnoreAppearanceMethodInvocationsKey = &UIViewControllerIgnoreAppearanceMethodInvocationsKey;
@implementation UIViewController (Navigation)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(viewWillAppear:) newSelector:@selector(_65087dc8_viewWillAppear:)];
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(viewDidAppear:) newSelector:@selector(_65087dc8_viewDidAppear:)];
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(viewWillDisappear:) newSelector:@selector(_65087dc8_viewWillDisappear:)];
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(viewDidDisappear:) newSelector:@selector(_65087dc8_viewDidDisappear:)];
});
}
- (void)setIgnoreAppearanceMethodInvocations:(BOOL)ignoreAppearanceMethodInvocations
{
[self setAssociatedObject:@(ignoreAppearanceMethodInvocations) forKey:UIViewControllerIgnoreAppearanceMethodInvocationsKey];
}
- (BOOL)ignoreAppearanceMethodInvocations
{
return [[self associatedObjectForKey:UIViewControllerIgnoreAppearanceMethodInvocationsKey] boolValue];
}
- (void)_65087dc8_viewWillAppear:(BOOL)animated
{
if (![self ignoreAppearanceMethodInvocations])
[self _65087dc8_viewWillAppear:animated];
}
- (void)_65087dc8_viewDidAppear:(BOOL)animated
{
if (![self ignoreAppearanceMethodInvocations])
[self _65087dc8_viewDidAppear:animated];
}
- (void)_65087dc8_viewWillDisappear:(BOOL)animated
{
if (![self ignoreAppearanceMethodInvocations])
[self _65087dc8_viewWillDisappear:animated];
}
- (void)_65087dc8_viewDidDisappear:(BOOL)animated
{
if (![self ignoreAppearanceMethodInvocations])
[self _65087dc8_viewDidDisappear:animated];
}
@end

View File

@ -0,0 +1,13 @@
#import <UIKit/UIKit.h>
@interface UIWindow (OrientationChange)
- (bool)isRotating;
@end
@interface UINavigationBar (Condensed)
- (void)setCondensed:(BOOL)condensed;
@end

View File

@ -0,0 +1,85 @@
#import "UIWindow+OrientationChange.h"
#import "RuntimeUtils.h"
#import "NotificationCenterUtils.h"
static const void *isRotatingKey = &isRotatingKey;
@implementation UIWindow (OrientationChange)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
[NotificationCenterUtils addNotificationHandler:^bool(NSString *name, id object, NSDictionary *userInfo)
{
if ([name isEqualToString:@"UIWindowWillRotateNotification"])
{
[(UIWindow *)object setRotating:true];
if (NSClassFromString(@"NSUserActivity") == NULL)
{
UIInterfaceOrientation orientation = [userInfo[@"UIWindowNewOrientationUserInfoKey"] integerValue];
CGSize screenSize = [UIScreen mainScreen].bounds.size;
if (screenSize.width > screenSize.height)
{
CGFloat tmp = screenSize.height;
screenSize.height = screenSize.width;
screenSize.width = tmp;
}
CGSize windowSize = CGSizeZero;
CGFloat windowRotation = 0.0;
bool landscape = false;
switch (orientation) {
case UIInterfaceOrientationPortrait:
windowSize = screenSize;
break;
case UIInterfaceOrientationPortraitUpsideDown:
windowRotation = (CGFloat)(M_PI);
windowSize = screenSize;
break;
case UIInterfaceOrientationLandscapeLeft:
landscape = true;
windowRotation = (CGFloat)(-M_PI / 2.0);
windowSize = CGSizeMake(screenSize.height, screenSize.width);
break;
case UIInterfaceOrientationLandscapeRight:
landscape = true;
windowRotation = (CGFloat)(M_PI / 2.0);
windowSize = CGSizeMake(screenSize.height, screenSize.width);
break;
default:
break;
}
[UIView animateWithDuration:0.3 animations:^
{
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformRotate(transform, windowRotation);
((UIWindow *)object).transform = transform;
((UIWindow *)object).bounds = CGRectMake(0.0f, 0.0f, windowSize.width, windowSize.height);
}];
}
}
else if ([name isEqualToString:@"UIWindowDidRotateNotification"])
{
[(UIWindow *)object setRotating:false];
}
return false;
}];
});
}
- (void)setRotating:(bool)rotating
{
[self setAssociatedObject:@(rotating) forKey:isRotatingKey];
}
- (bool)isRotating
{
return [[self associatedObjectForKey:isRotatingKey] boolValue];
}
@end

View File

@ -0,0 +1,51 @@
import Foundation
import UIKit
import AsyncDisplayKit
@objc public class ViewController: UIViewController, WindowContentController {
private var _displayNode: ASDisplayNode?
public var displayNode: ASDisplayNode {
get {
if let value = self._displayNode {
return value
}
else {
self.loadDisplayNode()
if self._displayNode == nil {
fatalError("displayNode should be initialized after loadDisplayNode()")
}
return self._displayNode!
}
}
set(value) {
self._displayNode = value
}
}
public init() {
super.init(nibName: nil, bundle: nil)
self.automaticallyAdjustsScrollViewInsets = false
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func loadView() {
self.view = self.displayNode.view
}
public func loadDisplayNode() {
self.displayNode = ASDisplayNode()
}
public func setViewSize(toSize: CGSize, duration: NSTimeInterval) {
if duration > DBL_EPSILON {
animateRotation(self.displayNode, toFrame: CGRect(x: 0.0, y: 0.0, width: toSize.width, height: toSize.height), duration: duration)
}
else {
self.displayNode.frame = CGRect(x: 0.0, y: 0.0, width: toSize.width, height: toSize.height)
}
}
}

134
Display/Window.swift Normal file
View File

@ -0,0 +1,134 @@
import Foundation
import AsyncDisplayKit
public class WindowRootViewController: UIViewController {
public override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .Default
}
public override func prefersStatusBarHidden() -> Bool {
return false
}
}
@objc
public protocol WindowContentController {
func setViewSize(toSize: CGSize, duration: NSTimeInterval)
var view: UIView! { get }
}
public func animateRotation(view: UIView?, toFrame: CGRect, duration: NSTimeInterval) {
if let view = view {
UIView.animateWithDuration(duration, animations: { () -> Void in
view.frame = toFrame
})
}
}
public func animateRotation(view: ASDisplayNode?, toFrame: CGRect, duration: NSTimeInterval) {
if let view = view {
CALayer.beginRecordingChanges()
view.frame = toFrame
view.layout()
let states = CALayer.endRecordingChanges() as! [CALayerAnimation]
let k = Float(UIView.animationDurationFactor())
var speed: Float = 1.0
if k != 0 && k != 1 {
speed = Float(1.0) / k
}
for state in states {
if let layer = state.layer {
if !CGRectEqualToRect(state.startBounds, state.endBounds) {
let boundsAnimation = CABasicAnimation(keyPath: "bounds")
boundsAnimation.fromValue = NSValue(CGRect: state.startBounds)
boundsAnimation.toValue = NSValue(CGRect: state.endBounds)
boundsAnimation.duration = duration
boundsAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
boundsAnimation.removedOnCompletion = true
boundsAnimation.fillMode = kCAFillModeForwards
boundsAnimation.speed = speed
layer.addAnimation(boundsAnimation, forKey: "_rotationBounds")
}
if !CGPointEqualToPoint(state.startPosition, state.endPosition) {
let positionAnimation = CABasicAnimation(keyPath: "position")
positionAnimation.fromValue = NSValue(CGPoint: state.startPosition)
positionAnimation.toValue = NSValue(CGPoint: state.endPosition)
positionAnimation.duration = duration
positionAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
positionAnimation.removedOnCompletion = true
positionAnimation.fillMode = kCAFillModeForwards
positionAnimation.speed = speed
layer.addAnimation(positionAnimation, forKey: "_rotationPosition")
}
}
}
}
}
public class Window: UIWindow {
public convenience init() {
self.init(frame: UIScreen.mainScreen().bounds)
}
public override init(frame: CGRect) {
super.init(frame: frame)
super.rootViewController = WindowRootViewController()
}
public required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
return self.viewController?.view.hitTest(point, withEvent: event)
}
public override var frame: CGRect {
get {
return super.frame
}
set(value) {
let sizeUpdated = super.frame.size != value.size
super.frame = value
if sizeUpdated {
self.viewController?.setViewSize(value.size, duration: self.isRotating() ? 0.3 : 0.0)
}
}
}
public override var bounds: CGRect {
get {
return super.frame
}
set(value) {
let sizeUpdated = super.bounds.size != value.size
super.bounds = value
if sizeUpdated {
self.viewController?.setViewSize(value.size, duration: self.isRotating() ? 0.3 : 0.0)
}
}
}
private var _rootViewController: WindowContentController?
public var viewController: WindowContentController? {
get {
return _rootViewController
}
set(value) {
self._rootViewController?.view.removeFromSuperview()
self._rootViewController = value
self._rootViewController?.view.frame = self.bounds
/*if let reactiveController = self._rootViewController as? ReactiveViewController {
reactiveController.displayNode.frame = CGRect(x: 0.0, y: 0.0, width: self.frame.size.width, height: self.frame.size.height)
self.addSubview(reactiveController.displayNode.view)
}
else {*/
if let view = self._rootViewController?.view {
self.addSubview(view)
}
//}
}
}
}

@ -0,0 +1 @@
Subproject commit 5482213e2ee8b880ebc23024ccc4643e1491e071