mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
no message
This commit is contained in:
parent
690b1d572b
commit
0cf72b8a57
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "submodules/AsyncDisplayKit"]
|
||||
path = submodules/AsyncDisplayKit
|
||||
url = https://github.com/facebook/AsyncDisplayKit.git
|
@ -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 */
|
||||
};
|
||||
|
49
Display/BarButtonItemWrapper.swift
Normal file
49
Display/BarButtonItemWrapper.swift
Normal 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()
|
||||
}
|
||||
}
|
32
Display/CAAnimationUtils.swift
Normal file
32
Display/CAAnimationUtils.swift
Normal 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)
|
||||
}
|
||||
}
|
20
Display/CALayer+ImplicitAnimations.h
Normal file
20
Display/CALayer+ImplicitAnimations.h
Normal 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
|
142
Display/CALayer+ImplicitAnimations.m
Normal file
142
Display/CALayer+ImplicitAnimations.m
Normal 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
|
@ -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>
|
||||
|
44
Display/InteractiveTransitionGestureRecognizer.swift
Normal file
44
Display/InteractiveTransitionGestureRecognizer.swift
Normal 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
9
Display/NSBag.h
Normal 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
64
Display/NSBag.m
Normal 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
|
BIN
Display/NavigationBackArrowLight@2x.png
Normal file
BIN
Display/NavigationBackArrowLight@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
143
Display/NavigationBackButtonNode.swift
Normal file
143
Display/NavigationBackButtonNode.swift
Normal 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
120
Display/NavigationBar.swift
Normal 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()
|
||||
}
|
||||
}
|
7
Display/NavigationBarProxy.h
Normal file
7
Display/NavigationBarProxy.h
Normal file
@ -0,0 +1,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface NavigationBarProxy : UINavigationBar
|
||||
|
||||
@property (nonatomic, copy) void (^setItemsProxy)(NSArray *, NSArray *, bool);
|
||||
|
||||
@end
|
67
Display/NavigationBarProxy.m
Normal file
67
Display/NavigationBarProxy.m
Normal 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
|
126
Display/NavigationButtonNode.swift
Normal file
126
Display/NavigationButtonNode.swift
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
232
Display/NavigationController.swift
Normal file
232
Display/NavigationController.swift
Normal 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
|
||||
}
|
||||
}
|
7
Display/NavigationControllerProxy.h
Normal file
7
Display/NavigationControllerProxy.h
Normal file
@ -0,0 +1,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface NavigationControllerProxy : UINavigationController
|
||||
|
||||
- (instancetype)init;
|
||||
|
||||
@end
|
16
Display/NavigationControllerProxy.m
Normal file
16
Display/NavigationControllerProxy.m
Normal 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
|
4
Display/NavigationItemTransitionState.swift
Normal file
4
Display/NavigationItemTransitionState.swift
Normal file
@ -0,0 +1,4 @@
|
||||
struct NavigationItemTransitionState {
|
||||
let backButtonPosition: CGPoint?
|
||||
let titlePosition: CGPoint
|
||||
}
|
332
Display/NavigationItemWrapper.swift
Normal file
332
Display/NavigationItemWrapper.swift
Normal 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)
|
||||
}
|
||||
}
|
50
Display/NavigationTitleNode.swift
Normal file
50
Display/NavigationTitleNode.swift
Normal 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
|
||||
}
|
||||
}
|
82
Display/NavigationTransitionView.swift
Normal file
82
Display/NavigationTransitionView.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
9
Display/NotificationCenterUtils.h
Normal file
9
Display/NotificationCenterUtils.h
Normal file
@ -0,0 +1,9 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef bool (^NotificationHandlerBlock)(NSString *, id, NSDictionary *);
|
||||
|
||||
@interface NotificationCenterUtils : NSObject
|
||||
|
||||
+ (void)addNotificationHandler:(NotificationHandlerBlock)handler;
|
||||
|
||||
@end
|
51
Display/NotificationCenterUtils.m
Normal file
51
Display/NotificationCenterUtils.m
Normal 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
20
Display/RuntimeUtils.h
Normal 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
56
Display/RuntimeUtils.m
Normal 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
|
15
Display/UIBarButtonItem+Proxy.h
Normal file
15
Display/UIBarButtonItem+Proxy.h
Normal 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
|
83
Display/UIBarButtonItem+Proxy.m
Normal file
83
Display/UIBarButtonItem+Proxy.m
Normal 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
7
Display/UIKitUtils.h
Normal file
@ -0,0 +1,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIView (AnimationUtils)
|
||||
|
||||
+ (double)animationDurationFactor;
|
||||
|
||||
@end
|
18
Display/UIKitUtils.m
Normal file
18
Display/UIKitUtils.m
Normal 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
31
Display/UIKitUtils.swift
Normal 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
|
||||
}
|
15
Display/UINavigationItem+Proxy.h
Normal file
15
Display/UINavigationItem+Proxy.h
Normal 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
|
101
Display/UINavigationItem+Proxy.m
Normal file
101
Display/UINavigationItem+Proxy.m
Normal 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
|
7
Display/UIViewController+Navigation.h
Normal file
7
Display/UIViewController+Navigation.h
Normal file
@ -0,0 +1,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIViewController (Navigation)
|
||||
|
||||
- (void)setIgnoreAppearanceMethodInvocations:(BOOL)ignoreAppearanceMethodInvocations;
|
||||
|
||||
@end
|
55
Display/UIViewController+Navigation.m
Normal file
55
Display/UIViewController+Navigation.m
Normal 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
|
13
Display/UIWindow+OrientationChange.h
Normal file
13
Display/UIWindow+OrientationChange.h
Normal file
@ -0,0 +1,13 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIWindow (OrientationChange)
|
||||
|
||||
- (bool)isRotating;
|
||||
|
||||
@end
|
||||
|
||||
@interface UINavigationBar (Condensed)
|
||||
|
||||
- (void)setCondensed:(BOOL)condensed;
|
||||
|
||||
@end
|
85
Display/UIWindow+OrientationChange.m
Normal file
85
Display/UIWindow+OrientationChange.m
Normal 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
|
51
Display/ViewController.swift
Normal file
51
Display/ViewController.swift
Normal 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
134
Display/Window.swift
Normal 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)
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
1
submodules/AsyncDisplayKit
Submodule
1
submodules/AsyncDisplayKit
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 5482213e2ee8b880ebc23024ccc4643e1491e071
|
Loading…
x
Reference in New Issue
Block a user