no message

This commit is contained in:
Peter
2016-03-28 17:13:25 +03:00
parent 02e78f9e81
commit 41aa541bbf
39 changed files with 2230 additions and 210 deletions

View File

@@ -7,7 +7,19 @@
objects = {
/* Begin PBXBuildFile section */
D0078A681C92B21400DF6D92 /* StatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0078A671C92B21400DF6D92 /* StatusBar.swift */; };
D0078A6D1C92B3B900DF6D92 /* ActionSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0078A6C1C92B3B900DF6D92 /* ActionSheet.swift */; };
D0078A6F1C92B40300DF6D92 /* ActionSheetItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0078A6E1C92B40300DF6D92 /* ActionSheetItemNode.swift */; };
D02BDB021B6AC703008AFAD2 /* RuntimeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02BDB011B6AC703008AFAD2 /* RuntimeUtils.swift */; };
D03BCCEB1C72AE590097A291 /* DisplayTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03BCCEA1C72AE590097A291 /* DisplayTheme.swift */; };
D03BCCED1C72AEC30097A291 /* DefaultDisplayTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03BCCEC1C72AEC30097A291 /* DefaultDisplayTheme.swift */; };
D03E7DE41C96A90100C07816 /* NavigationShadow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D03E7DE31C96A90100C07816 /* NavigationShadow@2x.png */; };
D03E7DE61C96B96E00C07816 /* NavigationBarTransitionContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E7DE51C96B96E00C07816 /* NavigationBarTransitionContainer.swift */; };
D03E7DF81C96C5F200C07816 /* NSWeakReference.h in Headers */ = {isa = PBXBuildFile; fileRef = D03E7DF61C96C5F200C07816 /* NSWeakReference.h */; settings = {ATTRIBUTES = (Public, ); }; };
D03E7DF91C96C5F200C07816 /* NSWeakReference.m in Sources */ = {isa = PBXBuildFile; fileRef = D03E7DF71C96C5F200C07816 /* NSWeakReference.m */; };
D03E7DFF1C96F7B400C07816 /* StatusBarManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E7DFE1C96F7B400C07816 /* StatusBarManager.swift */; };
D03E7E011C974AB300C07816 /* DisplayLinkDispatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E7E001C974AB300C07816 /* DisplayLinkDispatcher.swift */; };
D03E7E031C98160C00C07816 /* StatusBarSurfaceProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03E7E021C98160C00C07816 /* StatusBarSurfaceProvider.swift */; };
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 */; };
@@ -33,7 +45,7 @@
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 */; };
D05CC3151B695A9600E235A3 /* NavigationTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3091B695A9500E235A3 /* NavigationTransitionCoordinator.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 */; };
@@ -53,6 +65,12 @@
D06EE8451B7140FF00837186 /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06EE8441B7140FF00837186 /* Font.swift */; };
D07921A91B6FC0C0005C23D9 /* KeyboardHostWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07921A81B6FC0C0005C23D9 /* KeyboardHostWindow.swift */; };
D07921AC1B6FC92B005C23D9 /* StatusBarHostWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07921AB1B6FC92B005C23D9 /* StatusBarHostWindow.swift */; };
D0AE2C971C94529600F2FD3C /* StatusBarUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = D0AE2C951C94529600F2FD3C /* StatusBarUtils.h */; settings = {ATTRIBUTES = (Public, ); }; };
D0AE2C981C94529600F2FD3C /* StatusBarUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = D0AE2C961C94529600F2FD3C /* StatusBarUtils.m */; };
D0AE2CA61C94548900F2FD3C /* GenerateImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AE2CA51C94548900F2FD3C /* GenerateImage.swift */; };
D0B367201C94A53A00346D2E /* StatusBarProxyNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B3671F1C94A53A00346D2E /* StatusBarProxyNode.swift */; };
D0C0D28F1C997110001D2851 /* FBAnimationPerformanceTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = D0C0D28D1C997110001D2851 /* FBAnimationPerformanceTracker.h */; settings = {ATTRIBUTES = (Public, ); }; };
D0C0D2901C997110001D2851 /* FBAnimationPerformanceTracker.mm in Sources */ = {isa = PBXBuildFile; fileRef = D0C0D28E1C997110001D2851 /* FBAnimationPerformanceTracker.mm */; };
D0DC48541BF93D8B00F672FD /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC48531BF93D8A00F672FD /* TabBarController.swift */; };
D0DC48561BF945DD00F672FD /* TabBarNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC48551BF945DD00F672FD /* TabBarNode.swift */; };
D0DC485F1BF949FB00F672FD /* TabBarContollerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC485E1BF949FB00F672FD /* TabBarContollerNode.swift */; };
@@ -70,7 +88,19 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
D0078A671C92B21400DF6D92 /* StatusBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBar.swift; sourceTree = "<group>"; };
D0078A6C1C92B3B900DF6D92 /* ActionSheet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheet.swift; sourceTree = "<group>"; };
D0078A6E1C92B40300DF6D92 /* ActionSheetItemNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetItemNode.swift; sourceTree = "<group>"; };
D02BDB011B6AC703008AFAD2 /* RuntimeUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuntimeUtils.swift; sourceTree = "<group>"; };
D03BCCEA1C72AE590097A291 /* DisplayTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayTheme.swift; sourceTree = "<group>"; };
D03BCCEC1C72AEC30097A291 /* DefaultDisplayTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultDisplayTheme.swift; sourceTree = "<group>"; };
D03E7DE31C96A90100C07816 /* NavigationShadow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NavigationShadow@2x.png"; sourceTree = "<group>"; };
D03E7DE51C96B96E00C07816 /* NavigationBarTransitionContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBarTransitionContainer.swift; sourceTree = "<group>"; };
D03E7DF61C96C5F200C07816 /* NSWeakReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSWeakReference.h; sourceTree = "<group>"; };
D03E7DF71C96C5F200C07816 /* NSWeakReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSWeakReference.m; sourceTree = "<group>"; };
D03E7DFE1C96F7B400C07816 /* StatusBarManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarManager.swift; sourceTree = "<group>"; };
D03E7E001C974AB300C07816 /* DisplayLinkDispatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisplayLinkDispatcher.swift; sourceTree = "<group>"; };
D03E7E021C98160C00C07816 /* StatusBarSurfaceProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarSurfaceProvider.swift; sourceTree = "<group>"; };
D05CC2631B69316F00E235A3 /* Display.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Display.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D05CC2661B69316F00E235A3 /* Display.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Display.h; sourceTree = "<group>"; };
D05CC2681B69316F00E235A3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -99,7 +129,7 @@
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>"; };
D05CC3091B695A9500E235A3 /* NavigationTransitionCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationTransitionCoordinator.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>"; };
@@ -119,6 +149,12 @@
D06EE8441B7140FF00837186 /* Font.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = "<group>"; };
D07921A81B6FC0C0005C23D9 /* KeyboardHostWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardHostWindow.swift; sourceTree = "<group>"; };
D07921AB1B6FC92B005C23D9 /* StatusBarHostWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarHostWindow.swift; sourceTree = "<group>"; };
D0AE2C951C94529600F2FD3C /* StatusBarUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StatusBarUtils.h; sourceTree = "<group>"; };
D0AE2C961C94529600F2FD3C /* StatusBarUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StatusBarUtils.m; sourceTree = "<group>"; };
D0AE2CA51C94548900F2FD3C /* GenerateImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GenerateImage.swift; sourceTree = "<group>"; };
D0B3671F1C94A53A00346D2E /* StatusBarProxyNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarProxyNode.swift; sourceTree = "<group>"; };
D0C0D28D1C997110001D2851 /* FBAnimationPerformanceTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBAnimationPerformanceTracker.h; sourceTree = "<group>"; };
D0C0D28E1C997110001D2851 /* FBAnimationPerformanceTracker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FBAnimationPerformanceTracker.mm; sourceTree = "<group>"; };
D0DC48531BF93D8A00F672FD /* TabBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = "<group>"; };
D0DC48551BF945DD00F672FD /* TabBarNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarNode.swift; sourceTree = "<group>"; };
D0DC485E1BF949FB00F672FD /* TabBarContollerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarContollerNode.swift; sourceTree = "<group>"; };
@@ -146,6 +182,15 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
D0078A6B1C92B3A600DF6D92 /* Action Sheet */ = {
isa = PBXGroup;
children = (
D0078A6C1C92B3B900DF6D92 /* ActionSheet.swift */,
D0078A6E1C92B40300DF6D92 /* ActionSheetItemNode.swift */,
);
name = "Action Sheet";
sourceTree = "<group>";
};
D02BDAEC1B6A7053008AFAD2 /* Nodes */ = {
isa = PBXGroup;
children = (
@@ -153,6 +198,15 @@
name = Nodes;
sourceTree = "<group>";
};
D03BCCE91C72AE4B0097A291 /* Theme */ = {
isa = PBXGroup;
children = (
D03BCCEA1C72AE590097A291 /* DisplayTheme.swift */,
D03BCCEC1C72AEC30097A291 /* DefaultDisplayTheme.swift */,
);
name = Theme;
sourceTree = "<group>";
};
D05CC2591B69316F00E235A3 = {
isa = PBXGroup;
children = (
@@ -175,10 +229,12 @@
D05CC2651B69316F00E235A3 /* Display */ = {
isa = PBXGroup;
children = (
D03BCCE91C72AE4B0097A291 /* Theme */,
D05CC3001B6955D500E235A3 /* Utils */,
D07921AA1B6FC911005C23D9 /* Status Bar */,
D07921A71B6FC0AE005C23D9 /* Keyboard */,
D05CC3211B695AA600E235A3 /* Navigation */,
D0078A6B1C92B3A600DF6D92 /* Action Sheet */,
D0DC48521BF93D7C00F672FD /* Tabs */,
D02BDAEC1B6A7053008AFAD2 /* Nodes */,
D0E49C861B83A1680099E553 /* Image Cache */,
@@ -210,6 +266,7 @@
D05CC2E11B69534100E235A3 /* Supporting Files */ = {
isa = PBXGroup;
children = (
D03E7DE31C96A90100C07816 /* NavigationShadow@2x.png */,
D05CC3261B69725400E235A3 /* NavigationBackArrowLight@2x.png */,
D05CC2661B69316F00E235A3 /* Display.h */,
D05CC2681B69316F00E235A3 /* Info.plist */,
@@ -243,9 +300,17 @@
D05CC3131B695A9600E235A3 /* NavigationControllerProxy.m */,
D05CC3221B695B0700E235A3 /* NavigationBarProxy.h */,
D05CC3231B695B0700E235A3 /* NavigationBarProxy.m */,
D0AE2C951C94529600F2FD3C /* StatusBarUtils.h */,
D0AE2C961C94529600F2FD3C /* StatusBarUtils.m */,
D05CC2E41B69555800E235A3 /* CAAnimationUtils.swift */,
D02BDB011B6AC703008AFAD2 /* RuntimeUtils.swift */,
D06EE8441B7140FF00837186 /* Font.swift */,
D0AE2CA51C94548900F2FD3C /* GenerateImage.swift */,
D03E7DF61C96C5F200C07816 /* NSWeakReference.h */,
D03E7DF71C96C5F200C07816 /* NSWeakReference.m */,
D03E7E001C974AB300C07816 /* DisplayLinkDispatcher.swift */,
D0C0D28D1C997110001D2851 /* FBAnimationPerformanceTracker.h */,
D0C0D28E1C997110001D2851 /* FBAnimationPerformanceTracker.mm */,
);
name = Utils;
sourceTree = "<group>";
@@ -253,7 +318,8 @@
D05CC3211B695AA600E235A3 /* Navigation */ = {
isa = PBXGroup;
children = (
D05CC3091B695A9500E235A3 /* NavigationTransitionView.swift */,
D05CC3091B695A9500E235A3 /* NavigationTransitionCoordinator.swift */,
D03E7DE51C96B96E00C07816 /* NavigationBarTransitionContainer.swift */,
D05CC30A1B695A9500E235A3 /* NavigationBar.swift */,
D05CC30C1B695A9500E235A3 /* NavigationItemTransitionState.swift */,
D05CC30D1B695A9500E235A3 /* NavigationBackButtonNode.swift */,
@@ -279,6 +345,10 @@
isa = PBXGroup;
children = (
D07921AB1B6FC92B005C23D9 /* StatusBarHostWindow.swift */,
D0078A671C92B21400DF6D92 /* StatusBar.swift */,
D0B3671F1C94A53A00346D2E /* StatusBarProxyNode.swift */,
D03E7DFE1C96F7B400C07816 /* StatusBarManager.swift */,
D03E7E021C98160C00C07816 /* StatusBarSurfaceProvider.swift */,
);
name = "Status Bar";
sourceTree = "<group>";
@@ -311,13 +381,16 @@
D05CC3041B69568600E235A3 /* NotificationCenterUtils.h in Headers */,
D05CC2ED1B69558A00E235A3 /* RuntimeUtils.h in Headers */,
D05CC3201B695A9600E235A3 /* NavigationControllerProxy.h in Headers */,
D03E7DF81C96C5F200C07816 /* NSWeakReference.h in Headers */,
D05CC2E91B69555800E235A3 /* CALayer+ImplicitAnimations.h in Headers */,
D0AE2C971C94529600F2FD3C /* StatusBarUtils.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 */,
D0C0D28F1C997110001D2851 /* FBAnimationPerformanceTracker.h in Headers */,
D05CC2671B69316F00E235A3 /* Display.h in Headers */,
D05CC2F91B6955D000E235A3 /* UIViewController+Navigation.h in Headers */,
);
@@ -403,6 +476,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D03E7DE41C96A90100C07816 /* NavigationShadow@2x.png in Resources */,
D05CC3271B69725400E235A3 /* NavigationBackArrowLight@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -422,8 +496,11 @@
buildActionMask = 2147483647;
files = (
D0E49C881B83A3580099E553 /* ImageCache.swift in Sources */,
D0078A6F1C92B40300DF6D92 /* ActionSheetItemNode.swift in Sources */,
D0078A6D1C92B3B900DF6D92 /* ActionSheet.swift in Sources */,
D05CC3181B695A9600E235A3 /* NavigationItemTransitionState.swift in Sources */,
D07921AC1B6FC92B005C23D9 /* StatusBarHostWindow.swift in Sources */,
D0078A681C92B21400DF6D92 /* StatusBar.swift in Sources */,
D05CC2F81B6955D000E235A3 /* UIViewController+Navigation.m in Sources */,
D02BDB021B6AC703008AFAD2 /* RuntimeUtils.swift in Sources */,
D05CC31F1B695A9600E235A3 /* NavigationControllerProxy.m in Sources */,
@@ -433,12 +510,17 @@
D06EE8451B7140FF00837186 /* Font.swift in Sources */,
D07921A91B6FC0C0005C23D9 /* KeyboardHostWindow.swift in Sources */,
D05CC3251B695B0700E235A3 /* NavigationBarProxy.m in Sources */,
D03E7DE61C96B96E00C07816 /* NavigationBarTransitionContainer.swift in Sources */,
D05CC2F71B6955D000E235A3 /* UIKitUtils.swift in Sources */,
D03E7DFF1C96F7B400C07816 /* StatusBarManager.swift in Sources */,
D05CC3161B695A9600E235A3 /* NavigationBar.swift in Sources */,
D05CC31D1B695A9600E235A3 /* UIBarButtonItem+Proxy.m in Sources */,
D03E7DF91C96C5F200C07816 /* NSWeakReference.m in Sources */,
D0DC48541BF93D8B00F672FD /* TabBarController.swift in Sources */,
D03E7E011C974AB300C07816 /* DisplayLinkDispatcher.swift in Sources */,
D05CC3171B695A9600E235A3 /* NavigationItemWrapper.swift in Sources */,
D05CC3191B695A9600E235A3 /* NavigationBackButtonNode.swift in Sources */,
D03BCCEB1C72AE590097A291 /* DisplayTheme.swift in Sources */,
D05CC3071B69575900E235A3 /* NSBag.m in Sources */,
D0DC48561BF945DD00F672FD /* TabBarNode.swift in Sources */,
D05CC31A1B695A9600E235A3 /* NavigationButtonNode.swift in Sources */,
@@ -448,12 +530,18 @@
D0DC485F1BF949FB00F672FD /* TabBarContollerNode.swift in Sources */,
D05CC2FA1B6955D000E235A3 /* UINavigationItem+Proxy.m in Sources */,
D05CC2E81B69555800E235A3 /* CALayer+ImplicitAnimations.m in Sources */,
D03BCCED1C72AEC30097A291 /* DefaultDisplayTheme.swift in Sources */,
D03E7E031C98160C00C07816 /* StatusBarSurfaceProvider.swift in Sources */,
D0AE2CA61C94548900F2FD3C /* GenerateImage.swift in Sources */,
D05CC2EC1B69558A00E235A3 /* RuntimeUtils.m in Sources */,
D05CC2FC1B6955D000E235A3 /* UIKitUtils.m in Sources */,
D05CC3291B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift in Sources */,
D0C0D2901C997110001D2851 /* FBAnimationPerformanceTracker.mm in Sources */,
D05CC2FE1B6955D000E235A3 /* UIWindow+OrientationChange.m in Sources */,
D0B367201C94A53A00346D2E /* StatusBarProxyNode.swift in Sources */,
D0AE2C981C94529600F2FD3C /* StatusBarUtils.m in Sources */,
D05CC2A21B69326C00E235A3 /* Window.swift in Sources */,
D05CC3151B695A9600E235A3 /* NavigationTransitionView.swift in Sources */,
D05CC3151B695A9600E235A3 /* NavigationTransitionCoordinator.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -0,0 +1,7 @@
import Foundation
public class ActionSheet {
public init() {
}
}

View File

@@ -0,0 +1,8 @@
import Foundation
import AsyncDisplayKit
public class ActionSheetItemNode: ASDisplayNode {
override public init() {
super.init()
}
}

View File

@@ -61,15 +61,42 @@ public extension CALayer {
//self.setValue(to, forKey: keyPath)
}
public func animateAdditive(from from: NSValue, to: NSValue, keyPath: String, key: String, timingFunction: String, duration: NSTimeInterval, removeOnCompletion: Bool = true, completion: (Bool -> Void)? = nil) {
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 = removeOnCompletion
animation.fillMode = kCAFillModeForwards
animation.speed = speed
animation.additive = true
if let completion = completion {
animation.delegate = CALayerAnimationDelegate(completion: completion)
}
self.addAnimation(animation, forKey: key)
}
public func animateAlpha(from from: CGFloat, to: CGFloat, duration: NSTimeInterval, removeOnCompletion: Bool = true, completion: ((Bool) -> ())? = nil) {
self.animate(from: NSNumber(float: Float(from)), to: NSNumber(float: Float(to)), keyPath: "opacity", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: duration, removeOnCompletion: removeOnCompletion, completion: completion)
}
public func animateScale(from from: CGFloat, to: CGFloat, duration: NSTimeInterval) {
self.animate(from: NSNumber(float: Float(from)), to: NSNumber(float: Float(to)), keyPath: "transform.scale", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: duration, removeOnCompletion: true, completion: nil)
}
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, removeOnCompletion: true)
}
public func animateBoundsOriginYAdditive(from from: CGFloat, to: CGFloat, duration: NSTimeInterval) {
self.animate(from: from as NSNumber, to: to as NSNumber, keyPath: "bounds.origin.y", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: duration, removeOnCompletion: true)
self.animateAdditive(from: from as NSNumber, to: to as NSNumber, keyPath: "bounds.origin.y", key: "boundsOriginYAdditive", timingFunction: kCAMediaTimingFunctionEaseInEaseOut, duration: duration, removeOnCompletion: true)
}
}

View File

@@ -0,0 +1,5 @@
import Foundation
func defaultDisplayTheme() -> DisplayTheme {
return DisplayTheme(tintColor: UIColor.blueColor())
}

View File

@@ -27,4 +27,7 @@ FOUNDATION_EXPORT const unsigned char DisplayVersionString[];
#import <Display/UIBarButtonItem+Proxy.h>
#import <Display/NavigationControllerProxy.h>
#import <Display/NavigationBarProxy.h>
#import <Display/StatusBarUtils.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#import <Display/NSWeakReference.h>
#import <Display/FBAnimationPerformanceTracker.h>

View File

@@ -0,0 +1,34 @@
import Foundation
public class DisplayLinkDispatcher: NSObject {
private var displayLink: CADisplayLink!
private var blocksToDispatch: [Void -> Void] = []
private let limit: Int
public init(limit: Int = 0) {
self.limit = limit
super.init()
self.displayLink = CADisplayLink(target: self, selector: #selector(self.run))
self.displayLink.paused = true
self.displayLink.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
}
public func dispatch(f: Void -> Void) {
self.blocksToDispatch.append(f)
self.displayLink.paused = false
}
@objc func run() {
for _ in 0 ..< (self.limit == 0 ? 1000 : self.limit) {
if self.blocksToDispatch.count == 0 {
self.displayLink.paused = true
break
} else {
let f = self.blocksToDispatch.removeFirst()
f()
}
}
}
}

View File

@@ -0,0 +1,9 @@
import Foundation
public class DisplayTheme {
var tintColor: UIColor
public init(tintColor: UIColor) {
self.tintColor = tintColor
}
}

View File

@@ -0,0 +1,136 @@
#import <Foundation/Foundation.h>
/*
* This is an example provided by Facebook are for non-commercial testing and
* evaluation purposes only.
*
* Facebook reserves all rights not expressly granted.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*
* FBAnimationPerformanceTracker
* -----------------------------------------------------------------------
*
* This class provides animation performance tracking functionality. It basically
* measures the app's frame rate during an operation, and reports this information.
*
* 1) In Foo's designated initializer, construct a tracker object
*
* 2) Add calls to -start and -stop in appropriate places, e.g. for a ScrollView
*
* - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
* [_apTracker start];
* }
*
* - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
* {
* if (!scrollView.dragging) {
* [_apTracker stop];
* }
* }
*
* - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
* if (!decelerate) {
* [_apTracker stop];
* }
* }
*
* Notes
* -----
* [] The tracker operates by creating a CADisplayLink object to measure the frame rate of the display
* during start/stop interval.
*
* [] Calls to -stop that were not preceded by a matching call to -start have no effect.
*
* [] 2 calls to -start in a row will trash the data accumulated so far and not log anything.
*
*
* Configuration object for the core tracker
*
* ===============================================================================
* I highly recommend for you to use the standard configuration provided
* These are essentially here so that the computation of the metric is transparent
* and you can feel confident in what the numbers mean.
* ===============================================================================
*/
struct FBAnimationPerformanceTrackerConfig
{
// Number of frame drop that defines a "small" drop event. By default, 1.
NSInteger smallDropEventFrameNumber;
// Number of frame drop that defines a "large" drop event. By default, 4.
NSInteger largeDropEventFrameNumber;
// Number of maximum frame drops to which the drop will be trimmed down to. Currently 15.
NSInteger maxFrameDropAccount;
// If YES, will report stack traces
BOOL reportStackTraces;
};
typedef struct FBAnimationPerformanceTrackerConfig FBAnimationPerformanceTrackerConfig;
@protocol FBAnimationPerformanceTrackerDelegate <NSObject>
/**
* Core Metric
*
* You are responsible for the aggregation of these metrics (it being on the client or the server). I recommend to implement both
* to limit the payload you are sending to the server.
*
* The final recommended metric being: - SUM(duration) / SUM(smallDropEvent) aka the number of seconds between one frame drop or more
* - SUM(duration) / SUM(largeDropEvent) aka the number of seconds between four frame drops or more
*
* The first metric will tell you how smooth is your scroll view.
* The second metric will tell you how clowny your scroll view can get.
*
* Every time stop is called, this event will fire reporting the performance.
*
* NOTE on this metric:
* - It has been tested at scale on many Facebook apps.
* - It follows the curves of devices.
* - You will need about 100K calls for the number to converge.
* - It is perfectly correlated to X = Percentage of time spent at 60fps. Number of seconds between one frame drop = 1 / ( 1 - Time spent at 60 fps)
* - We report fraction of drops. 7 frame drop = 1.75 of a large frame drop if a large drop is 4 frame drop.
* This is to preserve the correlation mentionned above.
*/
- (void)reportDurationInMS:(NSInteger)duration smallDropEvent:(double)smallDropEvent largeDropEvent:(double)largeDropEvent;
/**
* Stack traces
*
* Dark magic of the animation tracker. In case of a frame drop, this will return a stack trace.
* This will NOT be reported on the main-thread, but off-main thread to save a few CPU cycles.
*
* The slide is constant value that needs to be reported with the stack for processing.
* This currently only allows for symbolication of your own image.
*
* Future work includes symbolicating all modules. I personnaly find it usually
* good enough to know the name of the module.
*
* The stack will have the following format:
* Foundation:0x123|MyApp:0x234|MyApp:0x345|
*
* The slide will have the following format:
* 0x456
*/
- (void)reportStackTrace:(NSString *)stack withSlide:(NSString *)slide;
@end
@interface FBAnimationPerformanceTracker : NSObject
- (instancetype)initWithConfig:(FBAnimationPerformanceTrackerConfig)config;
+ (FBAnimationPerformanceTrackerConfig)standardConfig;
@property (weak, nonatomic, readwrite) id<FBAnimationPerformanceTrackerDelegate> delegate;
- (void)start;
- (void)stop;
@end

View File

@@ -0,0 +1,412 @@
//
// FBAnimationPerformanceTracker.m
// Display
//
// Created by Peter on 3/16/16.
// Copyright © 2016 Telegram. All rights reserved.
//
#import "FBAnimationPerformanceTracker.h"
#import <dlfcn.h>
#import <map>
#import <pthread.h>
#import <QuartzCore/CADisplayLink.h>
#import <mach-o/dyld.h>
#import "execinfo.h"
#include <mach/mach_time.h>
static BOOL _signalSetup;
static pthread_t _mainThread;
static NSThread *_trackerThread;
static std::map<void *, NSString *, std::greater<void *>> _imageNames;
#ifdef __LP64__
typedef mach_header_64 fb_mach_header;
typedef segment_command_64 fb_mach_segment_command;
#define LC_SEGMENT_ARCH LC_SEGMENT_64
#else
typedef mach_header fb_mach_header;
typedef segment_command fb_mach_segment_command;
#define LC_SEGMENT_ARCH LC_SEGMENT
#endif
static volatile BOOL _scrolling;
pthread_mutex_t _scrollingMutex;
pthread_cond_t _scrollingCondVariable;
dispatch_queue_t _symbolicationQueue;
// We record at most 16 frames since I cap the number of frames dropped measured at 15.
// Past 15, something went very wrong (massive contention, priority inversion, rpc call going wrong...) .
// It will only pollute the data to get more.
static const int callstack_max_number = 16;
static int callstack_i;
static bool callstack_dirty;
static int callstack_size[callstack_max_number];
static void *callstacks[callstack_max_number][128];
uint64_t callstack_time_capture;
static void _callstack_signal_handler(int signr, siginfo_t *info, void *secret)
{
// This is run on the main thread every 16 ms or so during scroll.
// Signals are run one by one so there is no risk of concurrency of a signal
// by the same signal.
// The backtrace call is technically signal-safe on Unix-based system
// See: http://www.unix.com/man-page/all/3c/walkcontext/
// WARNING: this is signal handler, no memory allocation is safe.
// Essentially nothing is safe unless specified it is.
callstack_size[callstack_i] = backtrace(callstacks[callstack_i], 128);
callstack_i = (callstack_i + 1) & (callstack_max_number - 1); // & is a cheap modulo (only works for power of 2)
callstack_dirty = true;
}
@interface FBCallstack : NSObject
@property (nonatomic, readonly, assign) int size;
@property (nonatomic, readonly, assign) void **callstack;
- (instancetype)initWithSize:(int)size callstack:(void *)callstack;
@end
@implementation FBCallstack
- (instancetype)initWithSize:(int)size callstack:(void *)callstack
{
if (self = [super init]) {
_size = size;
_callstack = (void **)malloc(size * sizeof(void *));
memcpy(_callstack, callstack, size * sizeof(void *));
}
return self;
}
- (void)dealloc
{
free(_callstack);
}
@end
@implementation FBAnimationPerformanceTracker
{
FBAnimationPerformanceTrackerConfig _config;
BOOL _tracking;
BOOL _firstUpdate;
NSTimeInterval _previousFrameTimestamp;
CADisplayLink *_displayLink;
BOOL _prepared;
// numbers used to track the performance metrics
double _durationTotal;
double _maxFrameTime;
double _smallDrops;
double _largeDrops;
}
- (instancetype)initWithConfig:(FBAnimationPerformanceTrackerConfig)config
{
if (self = [super init]) {
// Stack trace logging is not working well in debug mode
// We don't want the data anyway. So let's bail.
#if defined(DEBUG)
config.reportStackTraces = NO;
#endif
_config = config;
if (config.reportStackTraces) {
[self _setupSignal];
}
}
return self;
}
+ (FBAnimationPerformanceTrackerConfig)standardConfig
{
FBAnimationPerformanceTrackerConfig config = {
.smallDropEventFrameNumber = 1,
.largeDropEventFrameNumber = 4,
.maxFrameDropAccount = 15,
.reportStackTraces = NO
};
return config;
}
+ (void)_trackerLoop
{
while (true) {
// If you are confused by this part,
// Check out https://computing.llnl.gov/tutorials/pthreads/#ConditionVariables
// Lock the mutex
pthread_mutex_lock(&_scrollingMutex);
while (!_scrolling) {
// Unlock the mutex and sleep until the conditional variable is signaled
pthread_cond_wait(&_scrollingCondVariable, &_scrollingMutex);
// The conditional variable was signaled, but we need to check _scrolling
// As nothing guarantees that it is still true
}
// _scrolling is true, go ahead and capture traces for a while.
pthread_mutex_unlock(&_scrollingMutex);
// We are scrolling, yay, capture traces
while (_scrolling) {
usleep(16000);
// Here I use SIGPROF which is a signal supposed to be used for profiling
// I haven't stumbled upon any collision so far.
// There is no guarantee that it won't impact the system in unpredicted ways.
// Use wisely.
pthread_kill(_mainThread, SIGPROF);
}
}
}
- (void)_setupSignal
{
if (!_signalSetup) {
// The signal hook should be setup once and only once
_signalSetup = YES;
// I actually don't know if the main thread can die. If it does, well,
// this is not going to work.
// UPDATE 4/2015: on iOS8, it looks like the main-thread never dies, and this pointer is correct
_mainThread = pthread_self();
callstack_i = 0;
// Setup the signal
struct sigaction sa;
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = _callstack_signal_handler;
sigaction(SIGPROF, &sa, NULL);
pthread_mutex_init(&_scrollingMutex, NULL);
pthread_cond_init (&_scrollingCondVariable, NULL);
// Setup the signal firing loop
_trackerThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(_trackerLoop) object:nil];
// We wanna be higher priority than the main thread
// On iOS8 : this will roughly stick us at priority 61, while the main thread oscillates between 20 and 47
_trackerThread.threadPriority = 1.0;
[_trackerThread start];
_symbolicationQueue = dispatch_queue_create("com.facebook.symbolication", DISPATCH_QUEUE_SERIAL);
dispatch_async(_symbolicationQueue, ^(void) {[self _setupSymbolication];});
}
}
- (void)_setupSymbolication
{
// This extract the starting slide of every module in the app
// This is used to know which module an instruction pointer belongs to.
// These operations is NOT thread-safe according to Apple docs
// Do not call this multiple times
int images = _dyld_image_count();
for (int i = 0; i < images; i ++) {
intptr_t imageSlide = _dyld_get_image_vmaddr_slide(i);
// Here we extract the module name from the full path
// Typically it looks something like: /path/to/lib/UIKit
// And I just extract UIKit
NSString *fullName = [NSString stringWithUTF8String:_dyld_get_image_name(i)];
NSRange range = [fullName rangeOfString:@"/" options:NSBackwardsSearch];
NSUInteger startP = (range.location != NSNotFound) ? range.location + 1 : 0;
NSString *imageName = [fullName substringFromIndex:startP];
// This is parsing the mach header in order to extract the slide.
// See https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/MachORuntime/index.html
// For the structure of mach headers
fb_mach_header *header = (fb_mach_header*)_dyld_get_image_header(i);
if (!header) {
continue;
}
const struct load_command *cmd =
reinterpret_cast<const struct load_command *>(header + 1);
for (unsigned int c = 0; cmd && (c < header->ncmds); c++) {
if (cmd->cmd == LC_SEGMENT_ARCH) {
const fb_mach_segment_command *seg =
reinterpret_cast<const fb_mach_segment_command *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
_imageNames[(void *)(seg->vmaddr + imageSlide)] = imageName;
break;
}
}
cmd = reinterpret_cast<struct load_command*>((char *)cmd + cmd->cmdsize);
}
}
}
- (void)dealloc
{
if (_prepared) {
[self _tearDownCADisplayLink];
}
}
#pragma mark - Tracking
- (void)start
{
if (!_tracking) {
if ([self prepare]) {
_displayLink.paused = NO;
_tracking = YES;
[self _reset];
if (_config.reportStackTraces) {
pthread_mutex_lock(&_scrollingMutex);
_scrolling = YES;
// Signal the tracker thread to start firing the signals
pthread_cond_signal(&_scrollingCondVariable);
pthread_mutex_unlock(&_scrollingMutex);
}
}
}
}
- (void)stop
{
if (_tracking) {
_tracking = NO;
_displayLink.paused = YES;
if (_durationTotal > 0) {
[_delegate reportDurationInMS:round(1000.0 * _durationTotal) smallDropEvent:_smallDrops largeDropEvent:_largeDrops];
if (_config.reportStackTraces) {
pthread_mutex_lock(&_scrollingMutex);
_scrolling = NO;
pthread_mutex_unlock(&_scrollingMutex);
}
}
}
}
- (BOOL)prepare
{
if (_prepared) {
return YES;
}
[self _setUpCADisplayLink];
_prepared = YES;
return YES;
}
- (void)_setUpCADisplayLink
{
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_update)];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
_displayLink.paused = YES;
}
- (void)_tearDownCADisplayLink
{
[_displayLink invalidate];
_displayLink = nil;
}
- (void)_reset
{
_firstUpdate = YES;
_previousFrameTimestamp = 0.0;
_durationTotal = 0;
_maxFrameTime = 0;
_largeDrops = 0;
_smallDrops = 0;
}
- (void)_addFrameTime:(NSTimeInterval)actualFrameTime singleFrameTime:(NSTimeInterval)singleFrameTime
{
_maxFrameTime = MAX(actualFrameTime, _maxFrameTime);
NSInteger frameDropped = round(actualFrameTime / singleFrameTime) - 1;
frameDropped = MAX(frameDropped, 0);
// This is to reduce noise. Massive frame drops will just add noise to your data.
frameDropped = MIN(_config.maxFrameDropAccount, frameDropped);
_durationTotal += (frameDropped + 1) * singleFrameTime;
// We account 2 frame drops as 2 small events. This way the metric correlates perfectly with Time at X fps.
_smallDrops += (frameDropped >= _config.smallDropEventFrameNumber) ? ((double) frameDropped) / (double)_config.smallDropEventFrameNumber : 0.0;
_largeDrops += (frameDropped >= _config.largeDropEventFrameNumber) ? ((double) frameDropped) / (double)_config.largeDropEventFrameNumber : 0.0;
if (frameDropped >= 1) {
if (_config.reportStackTraces) {
callstack_dirty = false;
for (int ci = 0; ci <= frameDropped ; ci ++) {
// This is computing the previous indexes
// callstack - 1 - ci takes us back ci frames
// I want a positive number so I add callstack_max_number
// And then just modulo it, with & (callstack_max_number - 1)
int callstackPreviousIndex = ((callstack_i - 1 - ci) + callstack_max_number) & (callstack_max_number - 1);
FBCallstack *callstackCopy = [[FBCallstack alloc] initWithSize:callstack_size[callstackPreviousIndex] callstack:callstacks[callstackPreviousIndex]];
// Check that in between the beginning and the end of the copy the signal did not fire
if (!callstack_dirty) {
// The copy has been made. We are now fine, let's punt the rest off main-thread.
__weak FBAnimationPerformanceTracker *weakSelf = self;
dispatch_async(_symbolicationQueue, ^(void) {
[weakSelf _reportStackTrace:callstackCopy];
});
}
}
}
}
}
- (void)_update
{
if (!_tracking) {
return;
}
if (_firstUpdate) {
_firstUpdate = NO;
_previousFrameTimestamp = _displayLink.timestamp;
return;
}
NSTimeInterval currentTimestamp = _displayLink.timestamp;
NSTimeInterval frameTime = currentTimestamp - _previousFrameTimestamp;
[self _addFrameTime:frameTime singleFrameTime:_displayLink.duration];
_previousFrameTimestamp = currentTimestamp;
}
- (void)_reportStackTrace:(FBCallstack *)callstack
{
static NSString *slide;
static dispatch_once_t slide_predicate;
dispatch_once(&slide_predicate, ^{
slide = [NSString stringWithFormat:@"%p", (void *)_dyld_get_image_header(0)];
});
@autoreleasepool {
NSMutableString *stack = [NSMutableString string];
for (int j = 2; j < callstack.size; j ++) {
void *instructionPointer = callstack.callstack[j];
auto it = _imageNames.lower_bound(instructionPointer);
NSString *imageName = (it != _imageNames.end()) ? it->second : @"???";
[stack appendString:imageName];
[stack appendString:@":"];
[stack appendString:[NSString stringWithFormat:@"%p", instructionPointer]];
[stack appendString:@"|"];
}
[_delegate reportStackTrace:stack withSlide:slide];
}
}
@end

258
Display/GenerateImage.swift Normal file
View File

@@ -0,0 +1,258 @@
import Foundation
import UIKit
let deviceColorSpace = CGColorSpaceCreateDeviceRGB()
let deviceScale = UIScreen.mainScreen().scale
public func generateImage(size: CGSize, generator: (CGSize, UnsafeMutablePointer<Int8>) -> Void) -> UIImage? {
let scale = deviceScale
let scaledSize = CGSize(width: size.width * scale, height: size.height * scale)
let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
let length = bytesPerRow * Int(scaledSize.height)
let bytes = UnsafeMutablePointer<Int8>(malloc(length))
let provider: CGDataProvider? = CGDataProviderCreateWithData(bytes, bytes, length, { bytes, _, _ in
free(bytes)
})
generator(scaledSize, bytes)
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedFirst.rawValue)
guard let image = CGImageCreate(Int(scaledSize.width), Int(scaledSize.height), 8, 32, bytesPerRow, deviceColorSpace, bitmapInfo, provider, nil, false, .RenderingIntentDefault)
else {
return nil
}
return UIImage(CGImage: image, scale: scale, orientation: .Up)
}
public func generateImage(size: CGSize, generator: (CGSize, CGContextRef) -> Void) -> UIImage? {
let scale = deviceScale
let scaledSize = CGSize(width: size.width * scale, height: size.height * scale)
let bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
let length = bytesPerRow * Int(scaledSize.height)
let bytes = UnsafeMutablePointer<Int8>(malloc(length))
let provider: CGDataProvider? = CGDataProviderCreateWithData(bytes, bytes, length, { bytes, _, _ in
free(bytes)
})
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedFirst.rawValue)
guard let context = CGBitmapContextCreate(bytes, Int(scaledSize.width), Int(scaledSize.height), 8, bytesPerRow, deviceColorSpace, bitmapInfo.rawValue)
else {
return nil
}
CGContextScaleCTM(context, scale, scale)
generator(size, context)
guard let image = CGImageCreate(Int(scaledSize.width), Int(scaledSize.height), 8, 32, bytesPerRow, deviceColorSpace, bitmapInfo, provider, nil, false, .RenderingIntentDefault)
else {
return nil
}
return UIImage(CGImage: image, scale: scale, orientation: .Up)
}
public enum DrawingContextBltMode {
case Alpha
}
public class DrawingContext {
public let size: CGSize
public let scale: CGFloat
private let scaledSize: CGSize
public let bytesPerRow: Int
private let bitmapInfo: CGBitmapInfo
public let length: Int
public let bytes: UnsafeMutablePointer<Int8>
let provider: CGDataProvider?
private var _context: CGContext?
public func withContext(@noescape f: (CGContext) -> ()) {
if self._context == nil {
let c = CGBitmapContextCreate(bytes, Int(scaledSize.width), Int(scaledSize.height), 8, bytesPerRow, deviceColorSpace, self.bitmapInfo.rawValue)
CGContextScaleCTM(c, scale, scale)
self._context = c
}
if let _context = self._context {
CGContextTranslateCTM(_context, self.size.width / 2.0, self.size.height / 2.0)
CGContextScaleCTM(_context, 1.0, -1.0)
CGContextTranslateCTM(_context, -self.size.width / 2.0, -self.size.height / 2.0)
f(_context)
CGContextTranslateCTM(_context, self.size.width / 2.0, self.size.height / 2.0)
CGContextScaleCTM(_context, 1.0, -1.0)
CGContextTranslateCTM(_context, -self.size.width / 2.0, -self.size.height / 2.0)
}
}
public func withFlippedContext(@noescape f: (CGContext) -> ()) {
if self._context == nil {
let c = CGBitmapContextCreate(bytes, Int(scaledSize.width), Int(scaledSize.height), 8, bytesPerRow, deviceColorSpace, self.bitmapInfo.rawValue)
CGContextScaleCTM(c, scale, scale)
self._context = c
}
if let _context = self._context {
f(_context)
}
}
public init(size: CGSize, clear: Bool = false) {
self.size = size
self.scale = deviceScale
self.scaledSize = CGSize(width: size.width * scale, height: size.height * scale)
self.bytesPerRow = (4 * Int(scaledSize.width) + 15) & (~15)
self.length = bytesPerRow * Int(scaledSize.height)
self.bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedFirst.rawValue)
self.bytes = UnsafeMutablePointer<Int8>(malloc(length))
if clear {
memset(self.bytes, 0, self.length)
}
self.provider = CGDataProviderCreateWithData(bytes, bytes, length, { bytes, _, _ in
free(bytes)
})
}
public func generateImage() -> UIImage? {
if let image = CGImageCreate(Int(scaledSize.width), Int(scaledSize.height), 8, 32, bytesPerRow, deviceColorSpace, bitmapInfo, provider, nil, false, .RenderingIntentDefault) {
return UIImage(CGImage: image, scale: scale, orientation: .Up)
} else {
return nil
}
}
public func blt(other: DrawingContext, at: CGPoint, mode: DrawingContextBltMode = .Alpha) {
if abs(other.scale - self.scale) < CGFloat(FLT_EPSILON) {
let srcX = 0
var srcY = 0
let dstX = Int(at.x * self.scale)
var dstY = Int(at.y * self.scale)
let width = min(Int(self.size.width * self.scale) - dstX, Int(other.size.width * self.scale))
let height = min(Int(self.size.height * self.scale) - dstY, Int(other.size.height * self.scale))
let maxDstX = dstX + width
let maxDstY = dstY + height
switch mode {
case .Alpha:
while dstY < maxDstY {
let srcLine = UnsafeMutablePointer<UInt32>(other.bytes + srcY * other.bytesPerRow)
let dstLine = UnsafeMutablePointer<UInt32>(self.bytes + dstY * self.bytesPerRow)
var dx = dstX
var sx = srcX
while dx < maxDstX {
let srcPixel = srcLine + sx
let dstPixel = dstLine + dx
let baseColor = dstPixel.memory
let baseR = (baseColor >> 16) & 0xff
let baseG = (baseColor >> 8) & 0xff
let baseB = baseColor & 0xff
let alpha = srcPixel.memory >> 24
let r = (baseR * alpha) / 255
let g = (baseG * alpha) / 255
let b = (baseB * alpha) / 255
dstPixel.memory = (alpha << 24) | (r << 16) | (g << 8) | b
dx += 1
sx += 1
}
dstY += 1
srcY += 1
}
}
}
}
}
public enum ParsingError: ErrorType {
case Generic
}
public func readCGFloat(inout index: UnsafePointer<UInt8>, end: UnsafePointer<UInt8>, separator: UInt8) throws -> CGFloat {
let begin = index
var seenPoint = false
while index <= end {
let c = index.memory
index = index.successor()
if c == 46 { // .
if seenPoint {
throw ParsingError.Generic
} else {
seenPoint = true
}
} else if c == separator {
break
} else if c < 48 || c > 57 {
throw ParsingError.Generic
}
}
if index == begin {
throw ParsingError.Generic
}
if let value = NSString(bytes: UnsafePointer<Void>(begin), length: index - begin, encoding: NSUTF8StringEncoding)?.floatValue {
return CGFloat(value)
} else {
throw ParsingError.Generic
}
}
public func drawSvgPath(context: CGContextRef, path: StaticString) throws {
var index: UnsafePointer<UInt8> = path.utf8Start
let end = path.utf8Start.advancedBy(path.byteSize)
while index < end {
let c = index.memory
index = index.successor()
if c == 77 { // M
let x = try readCGFloat(&index, end: end, separator: 44)
let y = try readCGFloat(&index, end: end, separator: 32)
//print("Move to \(x), \(y)")
CGContextMoveToPoint(context, x, y)
} else if c == 76 { // L
let x = try readCGFloat(&index, end: end, separator: 44)
let y = try readCGFloat(&index, end: end, separator: 32)
//print("Line to \(x), \(y)")
CGContextAddLineToPoint(context, x, y)
} else if c == 67 { // C
let x1 = try readCGFloat(&index, end: end, separator: 44)
let y1 = try readCGFloat(&index, end: end, separator: 32)
let x2 = try readCGFloat(&index, end: end, separator: 44)
let y2 = try readCGFloat(&index, end: end, separator: 32)
let x = try readCGFloat(&index, end: end, separator: 44)
let y = try readCGFloat(&index, end: end, separator: 32)
CGContextAddCurveToPoint(context, x1, y1, x2, y2, x, y)
//print("Line to \(x), \(y)")
} else if c == 90 { // Z
if index != end && index.memory != 32 {
throw ParsingError.Generic
}
//CGContextClosePath(context)
CGContextFillPath(context)
//CGContextBeginPath(context)
//print("Close")
}
}
}

View File

@@ -110,7 +110,7 @@ public final class ImageCache {
pthread_mutex_lock(&self.mutex);
if let residentImage = self.residentImages[key] {
image = residentImage.image
self.nextAccessIndex++
self.nextAccessIndex += 1
residentImage.accessIndex = self.nextAccessIndex
} else {
if let imageData = self.imageDatas[key] {
@@ -141,14 +141,14 @@ public final class ImageCache {
let currentImageSize = Int(currentImage.image.size.width * currentImage.image.size.height * currentImage.image.scale) * 4
removedSize += currentImageSize
self.residentImages.removeValueForKey(currentImage.key)
i--
i -= 1
}
self.residentImagesSize = max(0, self.residentImagesSize - removedSize)
}
self.residentImagesSize += imageSize
self.nextAccessIndex++
self.nextAccessIndex += 1
self.residentImages[key] = ImageCacheResidentImage(key: key, image: image, accessIndex: self.nextAccessIndex)
}
}

View File

@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
@interface NSWeakReference : NSObject
@property (nonatomic, weak) id value;
- (instancetype)initWithValue:(id)value;
@end

13
Display/NSWeakReference.m Normal file
View File

@@ -0,0 +1,13 @@
#import "NSWeakReference.h"
@implementation NSWeakReference
- (instancetype)initWithValue:(id)value {
self = [super init];
if (self != nil) {
self.value = value;
}
return self;
}
@end

View File

@@ -50,7 +50,7 @@ public class NavigationBackButtonNode: ASControlNode {
self.label.displaysAsynchronously = false
self.addSubnode(self.arrow)
let arrowImage = UIImage(named: "NavigationBackArrowLight", inBundle: NSBundle(forClass: NavigationBackButtonNode.self), compatibleWithTraitCollection: nil)
let arrowImage = UIImage(named: "NavigationBackArrowLight", inBundle: NSBundle(forClass: NavigationBackButtonNode.self), compatibleWithTraitCollection: nil)?.precomposed()
self.arrow.contents = arrowImage?.CGImage
self.arrow.frame = CGRect(origin: CGPoint(), size: arrowImage?.size ?? CGSize())
@@ -92,34 +92,34 @@ public class NavigationBackButtonNode: ASControlNode {
return CGRectContainsPoint(apparentBounds, touch.locationInView(self.view))
}
public override func touchesBegan(touches: Set<NSObject>!, withEvent event: UIEvent!) {
public override func touchesBegan(touches: Set<UITouch>, 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!) {
public override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesMoved(touches, withEvent: event)
self.updateHighlightedState(self.touchInsideApparentBounds(touches.first as! UITouch), animated: true)
self.updateHighlightedState(self.touchInsideApparentBounds(touches.first!), animated: true)
}
public override func touchesEnded(touches: Set<NSObject>!, withEvent event: UIEvent!) {
public override func touchesEnded(touches: Set<UITouch>, 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) {
if previousTouchCount != 0 && self.touchCount == 0 && self.enabled && self.touchInsideApparentBounds(touches.first!) {
self.pressed()
}
}
public override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
public override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
super.touchesCancelled(touches, withEvent: event)
self.touchCount = max(0, self.touchCount - touches.count)
self.touchCount = max(0, self.touchCount - (touches?.count ?? 0))
self.updateHighlightedState(false, animated: false)
}

View File

@@ -1,23 +1,41 @@
import UIKit
import AsyncDisplayKit
private enum ItemAnimation {
case None
case Push
case Pop
public class NavigationBar: ASDisplayNode {
var item: UINavigationItem? {
didSet {
if let item = self.item {
self.itemWrapper = NavigationItemWrapper(parentNode: self, navigationItem: item, previousNavigationItem: self.previousItem)
self.itemWrapper?.backPressed = { [weak self] in
if let backPressed = self?.backPressed {
backPressed()
}
}
} else {
self.itemWrapper = nil
}
}
}
public class NavigationBar: ASDisplayNode {
private var topItem: UINavigationItem?
private var topItemWrapper: NavigationItemWrapper?
var previousItem: UINavigationItem? {
didSet {
if let item = self.item {
self.itemWrapper = NavigationItemWrapper(parentNode: self, navigationItem: item, previousNavigationItem: self.previousItem)
self.itemWrapper?.backPressed = { [weak self] in
if let backPressed = self?.backPressed {
backPressed()
}
}
} else {
self.itemWrapper = nil
}
}
}
private var tempItem: UINavigationItem?
private var tempItemWrapper: NavigationItemWrapper?
private var itemWrapper: NavigationItemWrapper?
private let stripeHeight: CGFloat = 1.0 / UIScreen.mainScreen().scale
//private let effectView: UIVisualEffectView
var backPressed: () -> () = { }
private var collapsed: Bool {
@@ -26,34 +44,6 @@ public class NavigationBar: ASDisplayNode {
}
}
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() {
@@ -70,7 +60,7 @@ public class NavigationBar: ASDisplayNode {
self.view.addSubview(stripeView)
}
private func updateTopItem(item: UINavigationItem, previousItem: UINavigationItem?, animation: ItemAnimation) {
/*private func updateTopItem(item: UINavigationItem, previousItem: UINavigationItem?, animation: ItemAnimation) {
if self.topItem !== item {
let previousTopItemWrapper = self.topItemWrapper
self.topItemWrapper = nil
@@ -116,14 +106,12 @@ public class NavigationBar: ASDisplayNode {
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, width: self.frame.size.width, height: stripeHeight)
self.topItemWrapper?.layoutItems()
self.tempItemWrapper?.layoutItems()
self.itemWrapper?.layoutItems()
//self.effectView.frame = self.bounds
}

View File

@@ -0,0 +1,6 @@
import Foundation
import AsyncDisplayKit
class NavigationBarTransitionContainer: ASDisplayNode {
}

View File

@@ -62,34 +62,34 @@ public class NavigationButtonNode: ASTextNode {
return CGRectContainsPoint(apparentBounds, touch.locationInView(self.view))
}
public override func touchesBegan(touches: Set<NSObject>!, withEvent event: UIEvent!) {
public override func touchesBegan(touches: Set<UITouch>, 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!) {
public override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesMoved(touches, withEvent: event)
self.updateHighlightedState(self.touchInsideApparentBounds(touches.first as! UITouch), animated: true)
self.updateHighlightedState(self.touchInsideApparentBounds(touches.first!), animated: true)
}
public override func touchesEnded(touches: Set<NSObject>!, withEvent event: UIEvent!) {
public override func touchesEnded(touches: Set<UITouch>, 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) {
if previousTouchCount != 0 && self.touchCount == 0 && self.enabled && self.touchInsideApparentBounds(touches.first!) {
self.pressed()
}
}
public override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
public override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
super.touchesCancelled(touches, withEvent: event)
self.touchCount = max(0, self.touchCount - touches.count)
self.touchCount = max(0, self.touchCount - (touches?.count ?? 0))
self.updateHighlightedState(false, animated: false)
}

View File

@@ -8,8 +8,9 @@ private struct NavigationControllerLayout {
let statusBarHeight: CGFloat
}
public class NavigationController: NavigationControllerProxy, WindowContentController, UIGestureRecognizerDelegate {
private var _navigationBar: NavigationBar!
public class NavigationController: NavigationControllerProxy, WindowContentController, UIGestureRecognizerDelegate, StatusBarSurfaceProvider {
private let statusBarSurface: StatusBarSurface = StatusBarSurface()
private var navigationTransitionCoordinator: NavigationTransitionCoordinator?
private var currentPushDisposable = MetaDisposable()
@@ -19,27 +20,30 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
private var layout: NavigationControllerLayout?
private var pendingLayout: (NavigationControllerLayout, NSTimeInterval, Bool)?
private var _presentedViewController: UIViewController?
public override var presentedViewController: UIViewController? {
return self._presentedViewController
}
private var _viewControllers: [UIViewController] = []
public override var viewControllers: [UIViewController] {
get {
return self._viewControllers
} set(value) {
self.setViewControllers(_viewControllers, animated: false)
}
}
public override var topViewController: UIViewController? {
return self._viewControllers.last
}
public override init() {
self._navigationBar = nil
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 let strongSelf = self {
if strongSelf.viewControllers.count > 1 {
strongSelf.popViewControllerAnimated(true)
}
}
return
}
self.statusBarChangeObserver = NSNotificationCenter.defaultCenter().addObserverForName(UIApplicationWillChangeStatusBarFrameNotification, object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: { [weak self] notification in
if let strongSelf = self {
let statusBarHeight: CGFloat = (notification.userInfo?[UIApplicationStatusBarFrameUserInfoKey] as? NSValue)?.CGRectValue().height ?? 20.0
let statusBarHeight: CGFloat = max(20.0, (notification.userInfo?[UIApplicationStatusBarFrameUserInfoKey] as? NSValue)?.CGRectValue().height ?? 20.0)
let previousLayout: NavigationControllerLayout?
if let pendingLayout = strongSelf.pendingLayout {
@@ -48,7 +52,7 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
previousLayout = strongSelf.layout
}
strongSelf.pendingLayout = (NavigationControllerLayout(layout: ViewControllerLayout(size: previousLayout?.layout.size ?? CGSize(), insets: previousLayout?.layout.insets ?? UIEdgeInsets(), inputViewHeight: 0.0), statusBarHeight: statusBarHeight), (strongSelf.pendingLayout?.2 ?? false) ? (strongSelf.pendingLayout?.1 ?? 0.3) : max(strongSelf.pendingLayout?.1 ?? 0.0, 0.35), true)
strongSelf.pendingLayout = (NavigationControllerLayout(layout: ViewControllerLayout(size: previousLayout?.layout.size ?? CGSize(), insets: previousLayout?.layout.insets ?? UIEdgeInsets(), inputViewHeight: 0.0, statusBarHeight: statusBarHeight), statusBarHeight: statusBarHeight), (strongSelf.pendingLayout?.2 ?? false) ? (strongSelf.pendingLayout?.1 ?? 0.3) : max(strongSelf.pendingLayout?.1 ?? 0.0, 0.35), true)
strongSelf.view.setNeedsLayout()
}
@@ -64,12 +68,13 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
}
public override func loadView() {
super.loadView()
self.view = UIView()
//super.loadView()
self.view.clipsToBounds = true
self.navigationBar.superview?.insertSubview(_navigationBar.view, aboveSubview: self.navigationBar)
self.navigationBar.removeFromSuperview()
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: Selector("panGesture:"))
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
panRecognizer.delegate = self
panRecognizer.cancelsTouchesInView = true
self.view.addGestureRecognizer(panRecognizer)
@@ -91,10 +96,19 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
bottomController.viewWillAppear(true)
let bottomView = bottomController.view
let navigationTransitionCoordinator = NavigationTransitionCoordinator(container: self.view, topView: topView, bottomView: bottomView, navigationBar: self._navigationBar)
self.navigationTransitionCoordinator = navigationTransitionCoordinator
var bottomStatusBar: StatusBar?
if let bottomController = bottomController as? ViewController {
bottomStatusBar = bottomController.statusBar
}
self._navigationBar.beginInteractivePopProgress(bottomController.navigationItem, evenMorePreviousItem: self.viewControllers.count >= 3 ? (self.viewControllers[self.viewControllers.count - 3] as UIViewController).navigationItem : nil)
if let bottomStatusBar = bottomStatusBar {
self.statusBarSurface.insertStatusBar(bottomStatusBar, atIndex: 0)
}
(self.view.window as? Window)?.updateStatusBars()
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.view, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar)
self.navigationTransitionCoordinator = navigationTransitionCoordinator
}
case UIGestureRecognizerState.Changed:
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator {
@@ -109,7 +123,7 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
navigationTransitionCoordinator.animateCompletion(velocity, completion: {
self.navigationTransitionCoordinator = nil
self._navigationBar.endInteractivePopProgress()
//self._navigationBar.endInteractivePopProgress()
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
@@ -123,6 +137,12 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
topController.viewDidDisappear(true)
bottomController.viewDidAppear(true)
if let topStatusBar = (topController as? ViewController)?.statusBar {
self.statusBarSurface.removeStatusBar(topStatusBar)
}
(self.view.window as? Window)?.updateStatusBars()
}
})
}
@@ -138,7 +158,7 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
navigationTransitionCoordinator.animateCancel({
self.navigationTransitionCoordinator = nil
self._navigationBar.endInteractivePopProgress()
//self._navigationBar.endInteractivePopProgress()
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
@@ -146,6 +166,11 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
topController.viewDidAppear(true)
bottomController.viewDidDisappear(true)
if let bottomStatusBar = (bottomController as? ViewController)?.statusBar {
self.statusBarSurface.removeStatusBar(bottomStatusBar)
}
(self.view.window as? Window)?.updateStatusBars()
}
})
}
@@ -169,6 +194,11 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
topController.viewDidAppear(true)
bottomController.viewDidDisappear(true)
if let bottomStatusBar = (bottomController as? ViewController)?.statusBar {
self.statusBarSurface.removeStatusBar(bottomStatusBar)
}
(self.view.window as? Window)?.updateStatusBars()
}
})
}
@@ -182,7 +212,7 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
if let currentLayout = self.layout {
layout = currentLayout
} else {
layout = NavigationControllerLayout(layout: ViewControllerLayout(size: self.view.bounds.size, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0), inputViewHeight: 0.0), statusBarHeight: 20.0)
layout = NavigationControllerLayout(layout: ViewControllerLayout(size: self.view.bounds.size, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0), inputViewHeight: 0.0, statusBarHeight: 20.0), statusBarHeight: 20.0)
}
controller.setParentLayout(self.childControllerLayoutForLayout(layout), duration: 0.0, curve: 0)
self.currentPushDisposable.set((controller.ready.get() |> take(1)).start(next: {[weak self] _ in
@@ -220,7 +250,7 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
if let currentLayout = self.layout {
layout = currentLayout
} else {
layout = NavigationControllerLayout(layout: ViewControllerLayout(size: self.view.bounds.size, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0), inputViewHeight: 0.0), statusBarHeight: 20.0)
layout = NavigationControllerLayout(layout: ViewControllerLayout(size: self.view.bounds.size, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0), inputViewHeight: 0.0, statusBarHeight: 20.0), statusBarHeight: 20.0)
}
controller.setParentLayout(self.childControllerLayoutForLayout(layout), duration: 0.0, curve: 0)
@@ -229,17 +259,83 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
}
}
super.setViewControllers(viewControllers, animated: animated)
if animated && self.viewControllers.count != 0 && viewControllers.count != 0 && self.viewControllers.last! !== viewControllers.last! {
let topController = viewControllers.last! as UIViewController
let bottomController = self.viewControllers.last! as UIViewController
if let topController = topController as? ViewController {
topController.navigationBar.previousItem = bottomController.navigationItem
}
private func navigationBarFrame(layout: NavigationControllerLayout) -> CGRect {
return CGRect(x: 0.0, y: layout.statusBarHeight - 20.0, width: layout.layout.size.width, height: 20.0 + (layout.layout.size.height >= layout.layout.size.width ? 44.0 : 32.0))
bottomController.viewWillDisappear(true)
let bottomView = bottomController.view
topController.viewWillAppear(true)
let topView = topController.view
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Push, container: self.view, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar)
self.navigationTransitionCoordinator = navigationTransitionCoordinator
if let topController = topController as? ViewController {
self.statusBarSurface.addStatusBar(topController.statusBar)
}
(self.view.window as? Window)?.updateStatusBars()
navigationTransitionCoordinator.animateCompletion(0.0, completion: { [weak self] in
if let strongSelf = self {
strongSelf.navigationTransitionCoordinator = nil
topController.setIgnoreAppearanceMethodInvocations(true)
bottomController.setIgnoreAppearanceMethodInvocations(true)
strongSelf.setViewControllers(viewControllers, animated: false)
topController.setIgnoreAppearanceMethodInvocations(false)
bottomController.setIgnoreAppearanceMethodInvocations(false)
bottomController.viewDidDisappear(true)
topController.viewDidAppear(true)
if let bottomController = bottomController as? ViewController {
strongSelf.statusBarSurface.removeStatusBar(bottomController.statusBar)
}
(strongSelf.view.window as? Window)?.updateStatusBars()
}
})
} else {
var previousStatusBar: StatusBar?
if let previousController = self.viewControllers.last as? ViewController {
previousStatusBar = previousController.statusBar
}
var newStatusBar: StatusBar?
if let newController = viewControllers.last as? ViewController {
newStatusBar = newController.statusBar
}
if previousStatusBar !== newStatusBar {
if let previousStatusBar = previousStatusBar {
self.statusBarSurface.removeStatusBar(previousStatusBar)
}
if let newStatusBar = newStatusBar {
self.statusBarSurface.addStatusBar(newStatusBar)
}
}
if let topController = self.viewControllers.last where topController.isViewLoaded() {
topController.navigation_setNavigationController(nil)
topController.view.removeFromSuperview()
}
self._viewControllers = viewControllers
if let topController = viewControllers.last {
topController.navigation_setNavigationController(self)
self.view.addSubview(topController.view)
}
//super.setViewControllers(viewControllers, animated: animated)
}
}
private func childControllerLayoutForLayout(layout: NavigationControllerLayout) -> ViewControllerLayout {
var insets = layout.layout.insets
insets.top = self.navigationBarFrame(layout).maxY
return ViewControllerLayout(size: layout.layout.size, insets: insets, inputViewHeight: 0.0)
return ViewControllerLayout(size: layout.layout.size, insets: layout.layout.insets, inputViewHeight: 0.0, statusBarHeight: layout.statusBarHeight)
}
public func setParentLayout(layout: ViewControllerLayout, duration: NSTimeInterval, curve: UInt) {
@@ -250,7 +346,7 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
previousLayout = self.layout
}
self.pendingLayout = (NavigationControllerLayout(layout: layout, statusBarHeight: previousLayout?.statusBarHeight ?? 20.0), duration, false)
self.pendingLayout = (NavigationControllerLayout(layout: ViewControllerLayout(size: layout.size, insets: layout.insets, inputViewHeight: layout.inputViewHeight, statusBarHeight: previousLayout?.statusBarHeight ?? 20.0), statusBarHeight: previousLayout?.statusBarHeight ?? 20.0), duration, false)
self.view.setNeedsLayout()
}
@@ -268,12 +364,12 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
self.view.frame = CGRect(x: 0.0, y: 0.0, width: pendingLayout.0.layout.size.width, height: pendingLayout.0.layout.size.height)
}
if pendingLayout.1 > DBL_EPSILON {
/*if pendingLayout.1 > DBL_EPSILON {
animateRotation(self._navigationBar, toFrame: self.navigationBarFrame(pendingLayout.0), duration: pendingLayout.1)
}
else {
self._navigationBar.frame = self.navigationBarFrame(pendingLayout.0)
}
}*/
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator {
//navigationTransitionView.frame = CGRectMake(0.0, 0.0, toSize.width, toSize.height)
@@ -282,13 +378,20 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
if let controller = bottomController as? WindowContentController {
let layout: NavigationControllerLayout
if let currentLayout = self.layout {
layout = currentLayout
} else {
layout = NavigationControllerLayout(layout: ViewControllerLayout(size: self.view.bounds.size, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0), inputViewHeight: 0.0, statusBarHeight: 20.0), statusBarHeight: 20.0)
}
controller.setParentLayout(self.childControllerLayoutForLayout(pendingLayout.0), duration: pendingLayout.1, curve: 0)
} else {
bottomController.view.frame = CGRectMake(0.0, 0.0, pendingLayout.0.layout.size.width, pendingLayout.0.layout.size.height)
}
}
self._navigationBar.setInteractivePopProgress(navigationTransitionCoordinator.progress)
//self._navigationBar.setInteractivePopProgress(navigationTransitionCoordinator.progress)
}
if let topViewController = self.topViewController {
@@ -299,6 +402,14 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
}
}
if let presentedViewController = self.presentedViewController {
if let controller = presentedViewController as? WindowContentController {
controller.setParentLayout(self.childControllerLayoutForLayout(pendingLayout.0), duration: pendingLayout.1, curve: 0)
} else {
presentedViewController.view.frame = CGRectMake(0.0, 0.0, pendingLayout.0.layout.size.width, pendingLayout.0.layout.size.height)
}
}
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator {
navigationTransitionCoordinator.updateProgress()
}
@@ -307,6 +418,72 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
}
}
override public func presentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) {
if let controller = viewControllerToPresent as? NavigationController {
controller.navigation_setPresentingViewController(self)
self._presentedViewController = controller
self.view.endEditing(true)
let layout: NavigationControllerLayout
if let currentLayout = self.layout {
layout = currentLayout
} else {
layout = NavigationControllerLayout(layout: ViewControllerLayout(size: self.view.bounds.size, insets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0), inputViewHeight: 0.0, statusBarHeight: 20.0), statusBarHeight: 20.0)
}
controller.setParentLayout(self.childControllerLayoutForLayout(layout), duration: 0.0, curve: 0)
if flag {
controller.view.frame = self.view.bounds.offsetBy(dx: 0.0, dy: self.view.bounds.height)
self.view.addSubview(controller.view)
(self.view.window as? Window)?.updateStatusBars()
UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions(rawValue: 7 << 16), animations: {
controller.view.frame = self.view.bounds
(self.view.window as? Window)?.updateStatusBars()
}, completion: { _ in
if let completion = completion {
completion()
}
})
} else {
self.view.addSubview(controller.view)
(self.view.window as? Window)?.updateStatusBars()
if let completion = completion {
completion()
}
}
} else {
preconditionFailure("NavigationController can't present \(viewControllerToPresent). Only subclasses of NavigationController are allowed.")
}
}
override public func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)?) {
if let controller = self.presentedViewController {
if flag {
UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions(rawValue: 7 << 16), animations: {
controller.view.frame = self.view.bounds.offsetBy(dx: 0.0, dy: self.view.bounds.height)
(self.view.window as? Window)?.updateStatusBars()
}, completion: { _ in
controller.view.removeFromSuperview()
self._presentedViewController = nil
(self.view.window as? Window)?.updateStatusBars()
if let completion = completion {
completion()
}
})
} else {
self._presentedViewController = nil
(self.view.window as? Window)?.updateStatusBars()
if let completion = completion {
completion()
}
}
}
}
public func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return false
}
@@ -314,4 +491,12 @@ public class NavigationController: NavigationControllerProxy, WindowContentContr
public func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailByGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return otherGestureRecognizer is UIPanGestureRecognizer
}
func statusBarSurfaces() -> [StatusBarSurface] {
var surfaces: [StatusBarSurface] = [self.statusBarSurface]
if let controller = self.presentedViewController as? StatusBarSurfaceProvider {
surfaces.appendContentsOf(controller.statusBarSurfaces())
}
return surfaces
}
}

View File

@@ -207,7 +207,7 @@ internal class NavigationItemWrapper {
rightBarButtonItemWrapper.buttonNode.frame = self.rightButtonFrame!
}
self.titleNode.measure(CGSize(width: self.parentNode.bounds.size.width - 140.0, height: CGFloat.max))
self.titleNode.measure(CGSize(width: max(0.0, self.parentNode.bounds.size.width - 140.0), height: CGFloat.max))
self.titleNode.frame = self.titleFrame
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -17,7 +17,7 @@ public class NavigationTitleNode: ASDisplayNode {
public init(text: NSString) {
self.label = ASTextNode()
self.label.maximumLineCount = 1
self.label.maximumNumberOfLines = 1
self.label.truncationMode = .ByTruncatingTail
self.label.displaysAsynchronously = false

View File

@@ -0,0 +1,158 @@
import UIKit
enum NavigationTransition {
case Push
case Pop
}
private let shadowWidth: CGFloat = 16.0
private func generateShadow() -> UIImage? {
return UIImage(named: "NavigationShadow", inBundle: NSBundle(forClass: NavigationBackButtonNode.self), compatibleWithTraitCollection: nil)?.precomposed().resizableImageWithCapInsets(UIEdgeInsetsZero, resizingMode: .Tile)
}
private let shadowImage = generateShadow()
class NavigationTransitionCoordinator {
private var _progress: CGFloat = 0.0
var progress: CGFloat {
get {
return self._progress
}
set(value) {
self._progress = value
self.updateProgress()
}
}
private let container: UIView
private let transition: NavigationTransition
private let topView: UIView
private let viewSuperview: UIView?
private let bottomView: UIView
private let topNavigationBar: NavigationBar?
private let bottomNavigationBar: NavigationBar?
private let dimView: UIView
private let shadowView: UIImageView
init(transition: NavigationTransition, container: UIView, topView: UIView, topNavigationBar: NavigationBar?, bottomView: UIView, bottomNavigationBar: NavigationBar?) {
self.transition = transition
self.container = container
self.topView = topView
switch transition {
case .Push:
self.viewSuperview = bottomView.superview
case .Pop:
self.viewSuperview = topView.superview
}
self.bottomView = bottomView
self.topNavigationBar = topNavigationBar
self.bottomNavigationBar = bottomNavigationBar
self.dimView = UIView()
self.dimView.backgroundColor = UIColor.blackColor()
self.shadowView = UIImageView(image: shadowImage)
switch transition {
case .Push:
self.viewSuperview?.insertSubview(topView, belowSubview: topView)
case .Pop:
self.viewSuperview?.insertSubview(bottomView, belowSubview: topView)
}
self.viewSuperview?.insertSubview(self.dimView, belowSubview: topView)
self.viewSuperview?.insertSubview(self.shadowView, belowSubview: dimView)
self.updateProgress()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func updateProgress() {
let position: CGFloat
switch self.transition {
case .Push:
position = 1.0 - progress
case .Pop:
position = progress
}
self.topView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(position * self.container.bounds.size.width), y: 0.0), size: self.container.bounds.size)
self.dimView.frame = CGRect(origin: CGPoint(), size: CGSize(width: max(0.0, self.topView.frame.minX), height: self.container.bounds.size.height))
self.shadowView.frame = CGRect(origin: CGPoint(x: self.dimView.frame.maxX - shadowWidth, y: 0.0), size: CGSize(width: shadowWidth, height: self.container.bounds.size.height))
self.dimView.alpha = (1.0 - position) * 0.15
self.shadowView.alpha = (1.0 - position) * 0.9
self.bottomView.frame = CGRect(origin: CGPoint(x: ((position - 1.0) * self.container.bounds.size.width * 0.3), y: 0.0), size: self.container.bounds.size)
(self.container.window as? Window)?.updateStatusBars()
}
func animateCancel(completion: () -> ()) {
UIView.animateWithDuration(0.1, delay: 0.0, options: UIViewAnimationOptions(), animations: { () -> Void in
self.progress = 0.0
}) { (completed) -> Void in
switch self.transition {
case .Push:
if let viewSuperview = self.viewSuperview {
viewSuperview.addSubview(self.bottomView)
} else {
self.bottomView.removeFromSuperview()
}
self.topView.removeFromSuperview()
case .Pop:
if let viewSuperview = self.viewSuperview {
viewSuperview.addSubview(self.topView)
} else {
self.topView.removeFromSuperview()
}
self.bottomView.removeFromSuperview()
}
self.dimView.removeFromSuperview()
self.shadowView.removeFromSuperview()
completion()
}
}
func animateCompletion(velocity: CGFloat, completion: () -> ()) {
let distance = (1.0 - self.progress) * self.container.bounds.size.width
let f = {
switch self.transition {
case .Push:
if let viewSuperview = self.viewSuperview {
viewSuperview.addSubview(self.bottomView)
} else {
self.bottomView.removeFromSuperview()
}
//self.topView.removeFromSuperview()
case .Pop:
if let viewSuperview = self.viewSuperview {
viewSuperview.addSubview(self.topView)
} else {
self.topView.removeFromSuperview()
}
//self.bottomView.removeFromSuperview()
}
self.dimView.removeFromSuperview()
self.shadowView.removeFromSuperview()
completion()
}
if abs(velocity) < CGFloat(FLT_EPSILON) && abs(self.progress) < CGFloat(FLT_EPSILON) {
UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions(rawValue: 7 << 16), animations: {
self.progress = 1.0
}, completion: { _ in
f()
})
} else {
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
f()
}
}
}
}

View File

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

View File

@@ -8,6 +8,7 @@ typedef enum {
@interface RuntimeUtils : NSObject
+ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector;
+ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector withAnotherClass:(Class)anotherClass newSelector:(SEL)newSelector;
+ (void)swizzleClassMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector;
@end

View File

@@ -18,6 +18,16 @@
}
}
+ (void)swizzleInstanceMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector withAnotherClass:(Class)anotherClass newSelector:(SEL)newSelector {
Method origMethod = nil, newMethod = nil;
origMethod = class_getInstanceMethod(targetClass, currentSelector);
newMethod = class_getInstanceMethod(anotherClass, newSelector);
if ((origMethod != nil) && (newMethod != nil)) {
method_exchangeImplementations(origMethod, newMethod);
}
}
+ (void)swizzleClassMethodOfClass:(Class)targetClass currentSelector:(SEL)currentSelector newSelector:(SEL)newSelector {
Method origMethod = nil, newMethod = nil;

59
Display/StatusBar.swift Normal file
View File

@@ -0,0 +1,59 @@
import Foundation
import AsyncDisplayKit
public class StatusBarSurface {
var statusBars: [StatusBar] = []
func addStatusBar(statusBar: StatusBar) {
self.removeStatusBar(statusBar)
self.statusBars.append(statusBar)
}
func insertStatusBar(statusBar: StatusBar, atIndex index: Int) {
self.removeStatusBar(statusBar)
self.statusBars.insert(statusBar, atIndex: index)
}
func removeStatusBar(statusBar: StatusBar) {
for i in 0 ..< self.statusBars.count {
if self.statusBars[i] === statusBar {
self.statusBars.removeAtIndex(i)
break
}
}
}
}
public class StatusBar: ASDisplayNode {
public var style: StatusBarStyle = .Black
var proxyNode: StatusBarProxyNode?
override init() {
super.init()
self.clipsToBounds = true
self.userInteractionEnabled = false
}
func removeProxyNode() {
self.proxyNode?.hidden = true
self.proxyNode?.removeFromSupernode()
self.proxyNode = nil
}
func updateProxyNode() {
let origin = self.view.convertPoint(CGPoint(), toView: nil)
if let proxyNode = proxyNode {
proxyNode.style = self.style
} else {
self.proxyNode = StatusBarProxyNode(style: self.style)
self.proxyNode!.hidden = false
self.addSubnode(self.proxyNode!)
}
let frame = CGRect(origin: CGPoint(x: -origin.x, y: -origin.y), size: self.proxyNode!.frame.size)
self.proxyNode?.frame = frame
}
}

View File

@@ -0,0 +1,151 @@
import Foundation
import AsyncDisplayKit
private struct MappedStatusBar {
let style: StatusBarStyle
let frame: CGRect
let statusBar: StatusBar?
}
private struct MappedStatusBarSurface {
let statusBars: [MappedStatusBar]
let surface: StatusBarSurface
}
private func mapStatusBar(statusBar: StatusBar) -> MappedStatusBar {
let frame = CGRect(origin: statusBar.view.convertPoint(CGPoint(), toView: nil), size: statusBar.frame.size)
return MappedStatusBar(style: statusBar.style, frame: frame, statusBar: statusBar)
}
private func mappedSurface(surface: StatusBarSurface) -> MappedStatusBarSurface {
return MappedStatusBarSurface(statusBars: surface.statusBars.map(mapStatusBar), surface: surface)
}
private func optimizeMappedSurface(surface: MappedStatusBarSurface) -> MappedStatusBarSurface {
if surface.statusBars.count > 1 {
for i in 1 ..< surface.statusBars.count {
if surface.statusBars[i].style != surface.statusBars[i - 1].style || abs(surface.statusBars[i].frame.origin.y - surface.statusBars[i - 1].frame.origin.y) > CGFloat(FLT_EPSILON) {
return surface
}
}
let size = UIApplication.sharedApplication().statusBarFrame.size
return MappedStatusBarSurface(statusBars: [MappedStatusBar(style: surface.statusBars[0].style, frame: CGRect(origin: CGPoint(x: 0.0, y: surface.statusBars[0].frame.origin.y), size: size), statusBar: nil)], surface: surface.surface)
} else {
return surface
}
}
private func displayHiddenAnimation() -> CAAnimation {
let animation = CABasicAnimation(keyPath: "transform.translation.y")
animation.fromValue = NSNumber(float: -40.0)
animation.toValue = NSNumber(float: -40.0)
animation.fillMode = kCAFillModeBoth
animation.duration = 100000000.0
animation.additive = true
animation.removedOnCompletion = false
return animation
}
class StatusBarManager {
var surfaces: [StatusBarSurface] = [] {
didSet {
self.updateSurfaces(oldValue)
}
}
private func updateSurfaces(previousSurfaces: [StatusBarSurface]) {
let mappedSurfaces = self.surfaces.map({ optimizeMappedSurface(mappedSurface($0)) })
var visibleStatusBars: [StatusBar] = []
var globalStatusBar: (StatusBarStyle, CGFloat)?
for i in 0 ..< mappedSurfaces.count {
if i == mappedSurfaces.count - 1 && mappedSurfaces[i].statusBars.count == 1 {
globalStatusBar = (mappedSurfaces[i].statusBars[0].style, mappedSurfaces[i].statusBars[0].frame.origin.y)
} else {
for mappedStatusBar in mappedSurfaces[i].statusBars {
if let statusBar = mappedStatusBar.statusBar {
visibleStatusBars.append(statusBar)
}
}
}
}
for surface in previousSurfaces {
for statusBar in surface.statusBars {
if !visibleStatusBars.contains({$0 === statusBar}) {
statusBar.removeProxyNode()
}
}
}
for surface in self.surfaces {
for statusBar in surface.statusBars {
if !visibleStatusBars.contains({$0 === statusBar}) {
statusBar.removeProxyNode()
}
}
}
for statusBar in visibleStatusBars {
statusBar.updateProxyNode()
}
if let globalStatusBar = globalStatusBar {
StatusBarUtils.statusBarWindow()!.hidden = false
let statusBarStyle: UIStatusBarStyle = globalStatusBar.0 == .Black ? .Default : .LightContent
if UIApplication.sharedApplication().statusBarStyle != statusBarStyle {
UIApplication.sharedApplication().setStatusBarStyle(statusBarStyle, animated: false)
}
StatusBarUtils.statusBarWindow()!.layer.removeAnimationForKey("displayHidden")
StatusBarUtils.statusBarWindow()!.transform = CGAffineTransformMakeTranslation(0.0, globalStatusBar.1)
} else {
if StatusBarUtils.statusBarWindow()!.layer.animationForKey("displayHidden") == nil {
StatusBarUtils.statusBarWindow()!.layer.addAnimation(displayHiddenAnimation(), forKey: "displayHidden")
}
}
/*if self.items.count == 1 {
self.shouldHide = true
dispatch_async(dispatch_get_main_queue(), {
if self.shouldHide {
self.items[0].1.hidden = true
self.shouldHide = false
}
})
//self.items[0].1.hidden = true
StatusBarUtils.statusBarWindow()!.hidden = false
} else if !self.items.isEmpty {
self.shouldHide = false
for (statusBar, node) in self.items {
node.hidden = false
var frame = statusBar.frame
frame.size.width = self.bounds.size.width
frame.size.height = 20.0
node.frame = frame
//print("origin: \(frame.origin.x)")
//print("style: \(node.style)")
let bounds = frame
node.bounds = bounds
}
UIView.performWithoutAnimation {
StatusBarUtils.statusBarWindow()!.hidden = true
}
}
var statusBarStyle: UIStatusBarStyle = .Default
if let lastItem = self.items.last {
statusBarStyle = lastItem.0.style == .Black ? .Default : .LightContent
}
if UIApplication.sharedApplication().statusBarStyle != statusBarStyle {
UIApplication.sharedApplication().setStatusBarStyle(statusBarStyle, animated: false)
}
//print("window \(StatusBarUtils.statusBarWindow()!)")*/
}
}

View File

@@ -0,0 +1,333 @@
import Foundation
import AsyncDisplayKit
public enum StatusBarStyle {
case Black
case White
}
private enum StatusBarItemType {
case Generic
case Battery
}
func makeStatusBarProxy(style: StatusBarStyle) -> StatusBarProxyNode {
return StatusBarProxyNode(style: style)
}
private class StatusBarItemNode: ASDisplayNode {
var style: StatusBarStyle
var targetView: UIView
init(style: StatusBarStyle, targetView: UIView) {
self.style = style
self.targetView = targetView
super.init()
}
func update() {
let context = DrawingContext(size: self.targetView.frame.size, clear: true)
if let contents = self.targetView.layer.contents where (self.targetView.layer.sublayers?.count ?? 0) == 0 && CFGetTypeID(contents) == CGImageGetTypeID() && false {
let image = contents as! CGImageRef
context.withFlippedContext { c in
CGContextSetAlpha(c, CGFloat(self.targetView.layer.opacity))
CGContextDrawImage(c, CGRect(origin: CGPoint(), size: context.size), image)
CGContextSetAlpha(c, 1.0)
}
if let sublayers = self.targetView.layer.sublayers {
for sublayer in sublayers {
let origin = sublayer.frame.origin
if let contents = sublayer.contents where CFGetTypeID(contents) == CGImageGetTypeID() {
let image = contents as! CGImageRef
context.withFlippedContext { c in
CGContextTranslateCTM(c, origin.x, origin.y)
CGContextDrawImage(c, CGRect(origin: CGPoint(), size: context.size), image)
CGContextTranslateCTM(c, -origin.x, -origin.y)
}
} else {
context.withContext { c in
UIGraphicsPushContext(c)
CGContextTranslateCTM(c, origin.x, origin.y)
sublayer.renderInContext(c)
CGContextTranslateCTM(c, -origin.x, -origin.y)
UIGraphicsPopContext()
}
}
}
}
} else {
context.withContext { c in
UIGraphicsPushContext(c)
self.targetView.layer.renderInContext(c)
UIGraphicsPopContext()
}
}
let type: StatusBarItemType = self.targetView.isKindOfClass(batteryItemClass!) ? .Battery : .Generic
tintStatusBarItem(context, type: type, style: style)
self.contents = context.generateImage()?.CGImage
self.frame = self.targetView.frame
}
}
private func tintStatusBarItem(context: DrawingContext, type: StatusBarItemType, style: StatusBarStyle) {
switch type {
case .Battery:
let minY = 0
let minX = 0
let maxY = Int(context.size.height * context.scale)
let maxX = Int(context.size.width * context.scale)
if minY < maxY && minX < maxX {
let basePixel = UnsafeMutablePointer<UInt32>(context.bytes)
let pixelsPerRow = context.bytesPerRow / 4
let midX = (maxX + minX) / 2
let midY = (maxY + minY) / 2
let baseMidRow = basePixel + pixelsPerRow * midY
var baseX = minX
while baseX < maxX {
let pixel = baseMidRow + baseX
let alpha = pixel.memory & 0xff000000
if alpha != 0 {
break
}
baseX += 1
}
baseX += 2
var targetX = baseX
while targetX < maxX {
let pixel = baseMidRow + targetX
let alpha = pixel.memory & 0xff000000
if alpha == 0 {
break
}
targetX += 1
}
let batteryColor = (baseMidRow + baseX).memory
let batteryR = (batteryColor >> 16) & 0xff
let batteryG = (batteryColor >> 8) & 0xff
let batteryB = batteryColor & 0xff
var baseY = minY
while baseY < maxY {
let baseRow = basePixel + pixelsPerRow * baseY
let pixel = baseRow + midX
let alpha = pixel.memory & 0xff000000
if alpha != 0 {
break
}
baseY += 1
}
var targetY = maxY - 1
while targetY >= baseY {
let baseRow = basePixel + pixelsPerRow * targetY
let pixel = baseRow + midX
let alpha = pixel.memory & 0xff000000
if alpha != 0 {
break
}
targetY -= 1
}
targetY -= 1
let baseColor: UInt32
switch style {
case .Black:
baseColor = 0x000000
case .White:
baseColor = 0xffffff
}
let baseR = (baseColor >> 16) & 0xff
let baseG = (baseColor >> 8) & 0xff
let baseB = baseColor & 0xff
var pixel = UnsafeMutablePointer<UInt32>(context.bytes)
let end = UnsafeMutablePointer<UInt32>(context.bytes + context.length)
while pixel != end {
let alpha = (pixel.memory & 0xff000000) >> 24
let r = (baseR * alpha) / 255
let g = (baseG * alpha) / 255
let b = (baseB * alpha) / 255
pixel.memory = (alpha << 24) | (r << 16) | (g << 8) | b
pixel += 1
}
if batteryColor != 0xffffffff && batteryColor != 0xff000000 {
var y = baseY + 2
while y < targetY {
let baseRow = basePixel + pixelsPerRow * y
var x = baseX
while x < targetX {
let pixel = baseRow + x
let alpha = (pixel.memory >> 24) & 0xff
let r = (batteryR * alpha) / 255
let g = (batteryG * alpha) / 255
let b = (batteryB * alpha) / 255
pixel.memory = (alpha << 24) | (r << 16) | (g << 8) | b
x += 1
}
y += 1
}
}
}
case .Generic:
var pixel = UnsafeMutablePointer<UInt32>(context.bytes)
let end = UnsafeMutablePointer<UInt32>(context.bytes + context.length)
let baseColor: UInt32
switch style {
case .Black:
baseColor = 0x000000
case .White:
baseColor = 0xffffff
}
let baseR = (baseColor >> 16) & 0xff
let baseG = (baseColor >> 8) & 0xff
let baseB = baseColor & 0xff
while pixel != end {
let alpha = (pixel.memory & 0xff000000) >> 24
let r = (baseR * alpha) / 255
let g = (baseG * alpha) / 255
let b = (baseB * alpha) / 255
pixel.memory = (alpha << 24) | (r << 16) | (g << 8) | b
pixel += 1
}
}
}
private let batteryItemClass: AnyClass? = NSClassFromString("UIStatusBarBatteryItemView")
private class StatusBarProxyNodeTimerTarget: NSObject {
let action: () -> Void
init(action: () -> Void) {
self.action = action
}
@objc func tick() {
action()
}
}
class StatusBarProxyNode: ASDisplayNode {
var timer: NSTimer?
var style: StatusBarStyle {
didSet {
if oldValue != self.style {
if !self.hidden {
self.updateItems()
}
}
}
}
private var itemNodes: [StatusBarItemNode] = []
override var hidden: Bool {
get {
return super.hidden
} set(value) {
if super.hidden != value {
super.hidden = value
if !value {
self.updateItems()
self.timer = NSTimer(timeInterval: 5.0, target: StatusBarProxyNodeTimerTarget { [weak self] in
self?.updateItems()
}, selector: #selector(StatusBarProxyNodeTimerTarget.tick), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(self.timer!, forMode: NSRunLoopCommonModes)
} else {
self.timer?.invalidate()
self.timer = nil
}
}
}
}
init(style: StatusBarStyle) {
self.style = style
super.init()
self.hidden = true
self.clipsToBounds = true
//self.backgroundColor = UIColor.blueColor().colorWithAlphaComponent(0.2)
let statusBar = StatusBarUtils.statusBar()!
for subview in statusBar.subviews {
let itemNode = StatusBarItemNode(style: style, targetView: subview)
self.itemNodes.append(itemNode)
self.addSubnode(itemNode)
}
self.frame = statusBar.bounds
}
deinit {
self.timer?.invalidate()
}
private func updateItems() {
let statusBar = StatusBarUtils.statusBar()!
var i = 0
while i < self.itemNodes.count {
var found = false
for subview in statusBar.subviews {
if self.itemNodes[i].targetView == subview {
found = true
break
}
}
if !found {
self.itemNodes[i].removeFromSupernode()
self.itemNodes.removeAtIndex(i)
} else {
self.itemNodes[i].style = self.style
self.itemNodes[i].update()
i += 1
}
}
for subview in statusBar.subviews {
var found = false
for itemNode in self.itemNodes {
if itemNode.targetView == subview {
found = true
break
}
}
if !found {
let itemNode = StatusBarItemNode(style: self.style, targetView: subview)
itemNode.update()
self.itemNodes.append(itemNode)
self.addSubnode(itemNode)
}
}
}
}

View File

@@ -0,0 +1,4 @@
protocol StatusBarSurfaceProvider {
func statusBarSurfaces() -> [StatusBarSurface]
}

9
Display/StatusBarUtils.h Normal file
View File

@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface StatusBarUtils : NSObject
+ (UIView * _Nullable)statusBarWindow;
+ (UIView * _Nullable)statusBar;
@end

32
Display/StatusBarUtils.m Normal file
View File

@@ -0,0 +1,32 @@
#import "StatusBarUtils.h"
@implementation StatusBarUtils
+ (UIView *)statusBarWindow {
UIWindow *window = [[UIApplication sharedApplication] valueForKey:@"statusBarWindow"];
UIView *view = window.subviews.firstObject;
return view;
}
+ (UIView *)statusBar {
UIWindow *window = [[UIApplication sharedApplication] valueForKey:@"statusBarWindow"];
UIView *view = window.subviews.firstObject;
static Class foregroundClass = nil;
static Class batteryClass = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
foregroundClass = NSClassFromString(@"UIStatusBarForegroundView");
batteryClass = NSClassFromString(@"UIStatusBarBatteryItemView");
});
for (UIView *foreground in view.subviews) {
if ([foreground isKindOfClass:foregroundClass]) {
return foreground;
}
}
return nil;
}
@end

View File

@@ -98,7 +98,7 @@ public class TabBarController: ViewController {
private func childControllerLayoutForLayout(layout: ViewControllerLayout) -> ViewControllerLayout {
var insets = layout.insets
insets.bottom += 49.0
return ViewControllerLayout(size: layout.size, insets: insets, inputViewHeight: layout.inputViewHeight)
return ViewControllerLayout(size: layout.size, insets: insets, inputViewHeight: layout.inputViewHeight, statusBarHeight: layout.statusBarHeight)
}
override public func updateLayout(layout: ViewControllerLayout, previousLayout: ViewControllerLayout?, duration: Double, curve: UInt) {

View File

@@ -97,7 +97,7 @@ class TabBarNode: ASDisplayNode {
node.displayWithoutProcessing = true
node.layerBacked = true
if let selectedIndex = self.selectedIndex where selectedIndex == i {
node.image = tabBarItemImage(item.selectedImage, title: item.title ?? "", tintColor: UIColor.blueColor())
node.image = tabBarItemImage(item.selectedImage, title: item.title ?? "", tintColor: UIColor(0x1195f2))
} else {
node.image = tabBarItemImage(item.image, title: item.title ?? "", tintColor: UIColor(0x929292))
}
@@ -116,7 +116,7 @@ class TabBarNode: ASDisplayNode {
let item = self.tabBarItems[index]
if let selectedIndex = self.selectedIndex where selectedIndex == index {
node.image = tabBarItemImage(item.selectedImage, title: item.title ?? "", tintColor: UIColor.blueColor())
node.image = tabBarItemImage(item.selectedImage, title: item.title ?? "", tintColor: UIColor(0x1195f2))
} else {
node.image = tabBarItemImage(item.image, title: item.title ?? "", tintColor: UIColor(0x929292))
}
@@ -145,10 +145,10 @@ class TabBarNode: ASDisplayNode {
}
}
override func touchesBegan(touches: Set<NSObject>!, withEvent event: UIEvent!) {
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
if let touch = touches.first as? UITouch {
if let touch = touches.first {
let location = touch.locationInView(self.view)
var closestNode: (Int, CGFloat)?

View File

@@ -59,7 +59,7 @@ public extension CGSize {
}
}
public func assertNotOnMainThread(file: String = __FILE__, line: Int = __LINE__) {
public func assertNotOnMainThread(file: String = #file, line: Int = #line) {
assert(!NSThread.isMainThread(), "\(file):\(line) running on main thread")
}

View File

@@ -3,5 +3,9 @@
@interface UIViewController (Navigation)
- (void)setIgnoreAppearanceMethodInvocations:(BOOL)ignoreAppearanceMethodInvocations;
- (void)navigation_setNavigationController:(UINavigationController * _Nullable)navigationControlller;
- (void)navigation_setPresentingViewController:(UIViewController * _Nullable)presentingViewController;
@end
void applyKeyboardAutocorrection();

View File

@@ -1,8 +1,59 @@
#import "UIViewController+Navigation.h"
#import "RuntimeUtils.h"
#import <objc/runtime.h>
#import "NSWeakReference.h"
static const void *UIViewControllerIgnoreAppearanceMethodInvocationsKey = &UIViewControllerIgnoreAppearanceMethodInvocationsKey;
static const void *UIViewControllerNavigationControllerKey = &UIViewControllerNavigationControllerKey;
static const void *UIViewControllerPresentingViewControllerKey = &UIViewControllerPresentingViewControllerKey;
static bool notyfyingShiftState = false;
@interface UIKeyboardImpl_65087dc8: UIView
@end
@implementation UIKeyboardImpl_65087dc8
- (void)notifyShiftState {
static void (*impl)(id, SEL) = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method m = class_getInstanceMethod([UIKeyboardImpl_65087dc8 class], @selector(notifyShiftState));
impl = (typeof(impl))method_getImplementation(m);
});
if (impl) {
notyfyingShiftState = true;
impl(self, @selector(notifyShiftState));
notyfyingShiftState = false;
}
}
@end
@interface UIInputWindowController_65087dc8: UIViewController
@end
@implementation UIInputWindowController_65087dc8
- (void)updateViewConstraints {
static void (*impl)(id, SEL) = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method m = class_getInstanceMethod([UIInputWindowController_65087dc8 class], @selector(updateViewConstraints));
impl = (typeof(impl))method_getImplementation(m);
});
if (impl) {
if (!notyfyingShiftState) {
impl(self, @selector(updateViewConstraints));
}
}
}
@end
@implementation UIViewController (Navigation)
@@ -15,6 +66,11 @@ static const void *UIViewControllerIgnoreAppearanceMethodInvocationsKey = &UIVie
[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:)];
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(navigationController) newSelector:@selector(_65087dc8_navigationController)];
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(presentingViewController) newSelector:@selector(_65087dc8_presentingViewController)];
//[RuntimeUtils swizzleInstanceMethodOfClass:NSClassFromString(@"UIKeyboardImpl") currentSelector:@selector(notifyShiftState) withAnotherClass:[UIKeyboardImpl_65087dc8 class] newSelector:@selector(notifyShiftState)];
//[RuntimeUtils swizzleInstanceMethodOfClass:NSClassFromString(@"UIInputWindowController") currentSelector:@selector(updateViewConstraints) withAnotherClass:[UIInputWindowController_65087dc8 class] newSelector:@selector(updateViewConstraints)];
});
}
@@ -52,4 +108,73 @@ static const void *UIViewControllerIgnoreAppearanceMethodInvocationsKey = &UIVie
[self _65087dc8_viewDidDisappear:animated];
}
- (void)navigation_setNavigationController:(UINavigationController * _Nullable)navigationControlller {
[self setAssociatedObject:[[NSWeakReference alloc] initWithValue:navigationControlller] forKey:UIViewControllerNavigationControllerKey];
}
- (void)navigation_setPresentingViewController:(UIViewController * _Nullable)presentingViewController {
[self setAssociatedObject:[[NSWeakReference alloc] initWithValue:presentingViewController] forKey:UIViewControllerPresentingViewControllerKey];
}
- (UINavigationController *)_65087dc8_navigationController {
UINavigationController *navigationController = self._65087dc8_navigationController;
if (navigationController != nil) {
return navigationController;
}
navigationController = self.parentViewController.navigationController;
if (navigationController != nil) {
return navigationController;
}
return ((NSWeakReference *)[self associatedObjectForKey:UIViewControllerNavigationControllerKey]).value;
}
- (UIViewController *)_65087dc8_presentingViewController {
UINavigationController *navigationController = self.navigationController;
if (navigationController.presentingViewController != nil) {
return navigationController.presentingViewController;
}
return ((NSWeakReference *)[self associatedObjectForKey:UIViewControllerPresentingViewControllerKey]).value;
}
@end
static NSString *TGEncodeText(NSString *string, int key)
{
NSMutableString *result = [[NSMutableString alloc] init];
for (int i = 0; i < (int)[string length]; i++)
{
unichar c = [string characterAtIndex:i];
c += key;
[result appendString:[NSString stringWithCharacters:&c length:1]];
}
return result;
}
void applyKeyboardAutocorrection() {
static Class keyboardClass = NULL;
static SEL currentInstanceSelector = NULL;
static SEL applyVariantSelector = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
keyboardClass = NSClassFromString(TGEncodeText(@"VJLfzcpbse", -1));
currentInstanceSelector = NSSelectorFromString(TGEncodeText(@"bdujwfLfzcpbse", -1));
applyVariantSelector = NSSelectorFromString(TGEncodeText(@"bddfquBvupdpssfdujpo", -1));
});
if ([keyboardClass respondsToSelector:currentInstanceSelector])
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
id currentInstance = [keyboardClass performSelector:currentInstanceSelector];
if ([currentInstance respondsToSelector:applyVariantSelector])
[currentInstance performSelector:applyVariantSelector];
#pragma clang diagnostic pop
}
}

View File

@@ -4,7 +4,7 @@ import AsyncDisplayKit
import SwiftSignalKit
public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool {
return lhs.size == rhs.size && lhs.insets == rhs.insets && lhs.inputViewHeight == rhs.inputViewHeight
return lhs.size == rhs.size && lhs.insets == rhs.insets && lhs.inputViewHeight == rhs.inputViewHeight && lhs.statusBarHeight == rhs.statusBarHeight
}
@objc public class ViewController: UIViewController, WindowContentController {
@@ -31,6 +31,9 @@ public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool {
return self._displayNode != nil
}
public let statusBar: StatusBar
public let navigationBar: NavigationBar
private let _ready = Promise<Bool>(true)
public var ready: Promise<Bool> {
return self._ready
@@ -42,8 +45,12 @@ public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool {
var keyboardFrameObserver: AnyObject?
public init() {
self.statusBar = StatusBar()
self.navigationBar = NavigationBar()
super.init(nibName: nil, bundle: nil)
self.navigationBar.item = self.navigationItem
self.automaticallyAdjustsScrollViewInsets = false
self.keyboardFrameObserver = NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillChangeFrameNotification, object: nil, queue: nil, usingBlock: { [weak self] notification in
@@ -61,7 +68,7 @@ public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool {
} else{
previousLayout = strongSelf.layout
}
let layout = ViewControllerLayout(size: previousLayout?.size ?? CGSize(), insets: previousLayout?.insets ?? UIEdgeInsets(), inputViewHeight: keyboardHeight)
let layout = ViewControllerLayout(size: previousLayout?.size ?? CGSize(), insets: previousLayout?.insets ?? UIEdgeInsets(), inputViewHeight: keyboardHeight, statusBarHeight: previousLayout?.statusBarHeight ?? 20.0)
let updated: Bool
if let previousLayout = previousLayout {
updated = previousLayout != layout
@@ -69,7 +76,7 @@ public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool {
updated = true
}
if updated {
print("keyboard layout change: \(layout) rotating: \(strongSelf.view.window?.isRotating())")
//print("keyboard layout change: \(layout) rotating: \(strongSelf.view.window?.isRotating())")
let durationAndCurve: (NSTimeInterval, UInt) = previousDurationAndCurve ?? (duration > DBL_EPSILON ? 0.5 : 0.0, curve)
strongSelf.updateLayoutOnLayout = (layout, durationAndCurve.0, durationAndCurve.1)
@@ -91,10 +98,11 @@ public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool {
public override func loadView() {
self.view = self.displayNode.view
self.displayNode.addSubnode(self.navigationBar)
self.view.addSubview(self.statusBar.view)
}
public func loadDisplayNode() {
self.displayNode = ASDisplayNode()
}
@@ -102,6 +110,7 @@ public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool {
if self._displayNode == nil {
self.loadDisplayNode()
}
let previousLayout: ViewControllerLayout?
if let updateLayoutOnLayout = self.updateLayoutOnLayout {
previousLayout = updateLayoutOnLayout.0
@@ -109,7 +118,10 @@ public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool {
previousLayout = self.layout
}
let layout = ViewControllerLayout(size: layout.size, insets: layout.insets, inputViewHeight: previousLayout?.inputViewHeight ?? 0.0)
var insets = layout.insets
insets.top += 22.0
let layout = ViewControllerLayout(size: layout.size, insets: insets, inputViewHeight: previousLayout?.inputViewHeight ?? 0.0, statusBarHeight: layout.statusBarHeight)
let updated: Bool
if let previousLayout = previousLayout {
updated = previousLayout != layout
@@ -128,6 +140,8 @@ public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool {
}
public func updateLayout(layout: ViewControllerLayout, previousLayout: ViewControllerLayout?, duration: Double, curve: UInt) {
self.statusBar.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: 40.0))
self.navigationBar.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: 44.0 + 20.0))
}
override public func viewDidLayoutSubviews() {
@@ -138,6 +152,7 @@ public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool {
let previousLayout = self.layout
self.layout = updateLayoutOnLayout.0
self.updateLayout(updateLayoutOnLayout.0, previousLayout: previousLayout, duration: updateLayoutOnLayout.1, curve: updateLayoutOnLayout.2)
self.view.frame = CGRect(origin: self.view.frame.origin, size: updateLayoutOnLayout.0.size)
self.updateLayoutOnLayout = nil
} else {
@@ -156,4 +171,12 @@ public func ==(lhs: ViewControllerLayout, rhs: ViewControllerLayout) -> Bool {
}
}
}
override public func presentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) {
if let navigationController = self.navigationController as? NavigationController {
navigationController.presentViewController(viewControllerToPresent, animated: flag, completion: completion)
} else {
super.presentViewController(viewControllerToPresent, animated: flag, completion: completion)
}
}
}

View File

@@ -15,6 +15,7 @@ public struct ViewControllerLayout: Equatable {
public let size: CGSize
public let insets: UIEdgeInsets
public let inputViewHeight: CGFloat
public let statusBarHeight: CGFloat
}
public protocol WindowContentController {
@@ -74,7 +75,7 @@ public func animateRotation(view: ASDisplayNode?, toFrame: CGRect, duration: NST
}
public class Window: UIWindow {
//public let textField: UITextField
private let statusBarManager: StatusBarManager
private var updateViewSizeOnLayout: (Bool, NSTimeInterval) = (false, 0.0)
public var isUpdatingOrientationLayout = false
@@ -88,12 +89,10 @@ public class Window: UIWindow {
}
public override init(frame: CGRect) {
//self.textField = UITextField(frame: CGRect(x: -110.0, y: 0.0, width: 100.0, height: 50.0))
self.statusBarManager = StatusBarManager()
super.init(frame: frame)
//self.addSubview(self.textField)
super.rootViewController = WindowRootViewController()
}
@@ -144,11 +143,13 @@ public class Window: UIWindow {
self._rootViewController?.view.removeFromSuperview()
self._rootViewController = value
self._rootViewController?.view.frame = self.bounds
self._rootViewController?.setParentLayout(ViewControllerLayout(size: self.bounds.size, insets: UIEdgeInsets(), inputViewHeight: 0.0), duration: 0.0, curve: 0)
self._rootViewController?.setParentLayout(ViewControllerLayout(size: self.bounds.size, insets: UIEdgeInsets(), inputViewHeight: 0.0, statusBarHeight: 0.0), duration: 0.0, curve: 0)
if let view = self._rootViewController?.view {
self.addSubview(view)
}
self.updateStatusBars()
}
}
@@ -158,7 +159,7 @@ public class Window: UIWindow {
if self.updateViewSizeOnLayout.0 {
self.updateViewSizeOnLayout.0 = false
self._rootViewController?.setParentLayout(ViewControllerLayout(size: self.bounds.size, insets: UIEdgeInsets(), inputViewHeight: 0.0), duration: updateViewSizeOnLayout.1, curve: 0)
self._rootViewController?.setParentLayout(ViewControllerLayout(size: self.bounds.size, insets: UIEdgeInsets(), inputViewHeight: 0.0, statusBarHeight: 0.0), duration: updateViewSizeOnLayout.1, curve: 0)
}
}
@@ -179,4 +180,8 @@ public class Window: UIWindow {
public func addPostUpdateToInterfaceOrientationBlock(f: Void -> Void) {
postUpdateToInterfaceOrientationBlocks.append(f)
}
func updateStatusBars() {
self.statusBarManager.surfaces = (self._rootViewController as? StatusBarSurfaceProvider)?.statusBarSurfaces() ?? []
}
}