no message

This commit is contained in:
Peter 2018-01-09 13:17:48 +04:00
parent dc9cec5a0e
commit 147dfe39ca
46 changed files with 2042 additions and 808 deletions

View File

@ -10,11 +10,32 @@
D0078A681C92B21400DF6D92 /* StatusBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0078A671C92B21400DF6D92 /* StatusBar.swift */; };
D00C7CD21E3657570080C3D5 /* TextFieldNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00C7CD11E3657570080C3D5 /* TextFieldNode.swift */; };
D01159BB1F40E96C0039383E /* DisplayMac.h in Headers */ = {isa = PBXBuildFile; fileRef = D01159B91F40E96C0039383E /* DisplayMac.h */; settings = {ATTRIBUTES = (Public, ); }; };
D01159C21F40EA120039383E /* ListViewScrollerAppkit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01159C11F40EA120039383E /* ListViewScrollerAppkit.swift */; };
D015F7521D1AE08D00E269B5 /* ContainableController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015F7511D1AE08D00E269B5 /* ContainableController.swift */; };
D015F7541D1B0F6C00E269B5 /* SystemContainedControllerTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015F7531D1B0F6C00E269B5 /* SystemContainedControllerTransitionCoordinator.swift */; };
D015F7581D1B467200E269B5 /* ActionSheetController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015F7571D1B467200E269B5 /* ActionSheetController.swift */; };
D015F75A1D1B46B600E269B5 /* ActionSheetControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D015F7591D1B46B600E269B5 /* ActionSheetControllerNode.swift */; };
D01847611FFA703B00075256 /* UIKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01847601FFA703B00075256 /* UIKit.swift */; };
D01847631FFA70FC00075256 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01847621FFA70FC00075256 /* UIView.swift */; };
D01847641FFA723600075256 /* CAAnimationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC2E41B69555800E235A3 /* CAAnimationUtils.swift */; };
D01847661FFA72E000075256 /* ContainedViewLayoutTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01847651FFA72E000075256 /* ContainedViewLayoutTransition.swift */; };
D01847671FFA72E000075256 /* ContainedViewLayoutTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01847651FFA72E000075256 /* ContainedViewLayoutTransition.swift */; };
D01847681FFA749F00075256 /* ListViewAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFC01CC4431D0044FF83 /* ListViewAnimation.swift */; };
D01847691FFA756600075256 /* ListViewAccessoryItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFC51CC4431D0044FF83 /* ListViewAccessoryItemNode.swift */; };
D018476A1FFA75EE00075256 /* Spring.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBD1CC4431D0044FF83 /* Spring.swift */; };
D018476D1FFA765D00075256 /* NSValueAdditions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018476B1FFA765D00075256 /* NSValueAdditions.swift */; };
D018476E1FFA76DC00075256 /* ListViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBF1CC4431D0044FF83 /* ListViewItem.swift */; };
D018476F1FFA76FD00075256 /* ListViewAccessoryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFC31CC4431D0044FF83 /* ListViewAccessoryItem.swift */; };
D01847701FFA773100075256 /* ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBE1CC4431D0044FF83 /* ListView.swift */; };
D01847711FFA778100075256 /* ListViewIntermediateState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02383851DE0E3B4004018B6 /* ListViewIntermediateState.swift */; };
D01847741FFA780400075256 /* UIScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01847721FFA780400075256 /* UIScrollView.swift */; };
D01847751FFA78B200075256 /* ListViewScroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFC41CC4431D0044FF83 /* ListViewScroller.swift */; };
D01847771FFA78C100075256 /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01847761FFA78C100075256 /* UIGestureRecognizer.swift */; };
D01847791FFA7A4E00075256 /* UITouch.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01847781FFA7A4E00075256 /* UITouch.swift */; };
D018477A1FFA7A8800075256 /* ListViewOverscrollBackgroundNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AA840F1FED2887005C6E91 /* ListViewOverscrollBackgroundNode.swift */; };
D018477C1FFA7ABF00075256 /* UISlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D018477B1FFA7ABF00075256 /* UISlider.swift */; };
D01C06C21FC254F8001561AB /* ASDisplayNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01C06C11FC254F8001561AB /* ASDisplayNode.swift */; };
D01C06C31FC2552C001561AB /* ListViewItemHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F7AB361DCFF6F8009AD9A1 /* ListViewItemHeader.swift */; };
D01C06C41FC25561001561AB /* ListViewItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C2DFBC1CC4431D0044FF83 /* ListViewItemNode.swift */; };
D01C06C51FC2558F001561AB /* SwiftSignalKitMac.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D01C06C61FC2558F001561AB /* SwiftSignalKitMac.framework */; };
D01E2BDE1D9049620066BF65 /* GridNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BDD1D9049620066BF65 /* GridNode.swift */; };
D01E2BE01D90498E0066BF65 /* GridNodeScroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BDF1D90498E0066BF65 /* GridNodeScroller.swift */; };
D01E2BE21D9049F60066BF65 /* GridItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BE11D9049F60066BF65 /* GridItemNode.swift */; };
@ -42,7 +63,6 @@
D053CB601D22B4F200DD41DF /* CATracingLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = D053CB5E1D22B4F200DD41DF /* CATracingLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
D053CB611D22B4F200DD41DF /* CATracingLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = D053CB5F1D22B4F200DD41DF /* CATracingLayer.m */; };
D05BE4AB1D1F25E3002BD72C /* PresentationContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05BE4AA1D1F25E3002BD72C /* PresentationContext.swift */; };
D05BE4AE1D217F6B002BD72C /* MergedLayoutEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05BE4AD1D217F6B002BD72C /* MergedLayoutEvents.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 */; };
@ -90,6 +110,8 @@
D08E90471D243C2F00533158 /* HighlightTrackingButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08E90461D243C2F00533158 /* HighlightTrackingButton.swift */; };
D096A4501EA64F580000A7AE /* ActionSheetCheckboxItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D096A44F1EA64F580000A7AE /* ActionSheetCheckboxItem.swift */; };
D0A749951E3A9E7B00AD786E /* SwitchNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A749941E3A9E7B00AD786E /* SwitchNode.swift */; };
D0AA840E1FEBFB72005C6E91 /* ListViewFloatingHeaderNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AA840D1FEBFB72005C6E91 /* ListViewFloatingHeaderNode.swift */; };
D0AA84101FED2887005C6E91 /* ListViewOverscrollBackgroundNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AA840F1FED2887005C6E91 /* ListViewOverscrollBackgroundNode.swift */; };
D0AE2CA61C94548900F2FD3C /* GenerateImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AE2CA51C94548900F2FD3C /* GenerateImage.swift */; };
D0AE3D4D1D25C816001CCE13 /* NavigationBarTransitionState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AE3D4C1D25C816001CCE13 /* NavigationBarTransitionState.swift */; };
D0B367201C94A53A00346D2E /* StatusBarProxyNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B3671F1C94A53A00346D2E /* StatusBarProxyNode.swift */; };
@ -150,11 +172,19 @@
D01159B71F40E96B0039383E /* DisplayMac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DisplayMac.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D01159B91F40E96C0039383E /* DisplayMac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DisplayMac.h; sourceTree = "<group>"; };
D01159BA1F40E96C0039383E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D01159C11F40EA120039383E /* ListViewScrollerAppkit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewScrollerAppkit.swift; sourceTree = "<group>"; };
D015F7511D1AE08D00E269B5 /* ContainableController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContainableController.swift; sourceTree = "<group>"; };
D015F7531D1B0F6C00E269B5 /* SystemContainedControllerTransitionCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemContainedControllerTransitionCoordinator.swift; sourceTree = "<group>"; };
D015F7571D1B467200E269B5 /* ActionSheetController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetController.swift; sourceTree = "<group>"; };
D015F7591D1B46B600E269B5 /* ActionSheetControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetControllerNode.swift; sourceTree = "<group>"; };
D01847601FFA703B00075256 /* UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKit.swift; sourceTree = "<group>"; };
D01847621FFA70FC00075256 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
D01847651FFA72E000075256 /* ContainedViewLayoutTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainedViewLayoutTransition.swift; sourceTree = "<group>"; };
D018476B1FFA765D00075256 /* NSValueAdditions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSValueAdditions.swift; sourceTree = "<group>"; };
D01847721FFA780400075256 /* UIScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScrollView.swift; sourceTree = "<group>"; };
D01847761FFA78C100075256 /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = "<group>"; };
D01847781FFA7A4E00075256 /* UITouch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITouch.swift; sourceTree = "<group>"; };
D018477B1FFA7ABF00075256 /* UISlider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UISlider.swift; sourceTree = "<group>"; };
D01C06C11FC254F8001561AB /* ASDisplayNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASDisplayNode.swift; sourceTree = "<group>"; };
D01C06C61FC2558F001561AB /* SwiftSignalKitMac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SwiftSignalKitMac.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D01E2BDD1D9049620066BF65 /* GridNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridNode.swift; sourceTree = "<group>"; };
D01E2BDF1D90498E0066BF65 /* GridNodeScroller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridNodeScroller.swift; sourceTree = "<group>"; };
D01E2BE11D9049F60066BF65 /* GridItemNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridItemNode.swift; sourceTree = "<group>"; };
@ -182,7 +212,6 @@
D053CB5E1D22B4F200DD41DF /* CATracingLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CATracingLayer.h; sourceTree = "<group>"; };
D053CB5F1D22B4F200DD41DF /* CATracingLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CATracingLayer.m; sourceTree = "<group>"; };
D05BE4AA1D1F25E3002BD72C /* PresentationContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationContext.swift; sourceTree = "<group>"; };
D05BE4AD1D217F6B002BD72C /* MergedLayoutEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MergedLayoutEvents.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>"; };
@ -233,6 +262,8 @@
D08E90461D243C2F00533158 /* HighlightTrackingButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighlightTrackingButton.swift; sourceTree = "<group>"; };
D096A44F1EA64F580000A7AE /* ActionSheetCheckboxItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetCheckboxItem.swift; sourceTree = "<group>"; };
D0A749941E3A9E7B00AD786E /* SwitchNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwitchNode.swift; sourceTree = "<group>"; };
D0AA840D1FEBFB72005C6E91 /* ListViewFloatingHeaderNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewFloatingHeaderNode.swift; sourceTree = "<group>"; };
D0AA840F1FED2887005C6E91 /* ListViewOverscrollBackgroundNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewOverscrollBackgroundNode.swift; sourceTree = "<group>"; };
D0AE2CA51C94548900F2FD3C /* GenerateImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GenerateImage.swift; sourceTree = "<group>"; };
D0AE3D4C1D25C816001CCE13 /* NavigationBarTransitionState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBarTransitionState.swift; sourceTree = "<group>"; };
D0B3671F1C94A53A00346D2E /* StatusBarProxyNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarProxyNode.swift; sourceTree = "<group>"; };
@ -283,6 +314,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D01C06C51FC2558F001561AB /* SwiftSignalKitMac.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -311,6 +343,14 @@
children = (
D01159B91F40E96C0039383E /* DisplayMac.h */,
D01159BA1F40E96C0039383E /* Info.plist */,
D01C06C11FC254F8001561AB /* ASDisplayNode.swift */,
D01847601FFA703B00075256 /* UIKit.swift */,
D01847621FFA70FC00075256 /* UIView.swift */,
D01847721FFA780400075256 /* UIScrollView.swift */,
D018476B1FFA765D00075256 /* NSValueAdditions.swift */,
D01847761FFA78C100075256 /* UIGestureRecognizer.swift */,
D01847781FFA7A4E00075256 /* UITouch.swift */,
D018477B1FFA7ABF00075256 /* UISlider.swift */,
);
path = DisplayMac;
sourceTree = "<group>";
@ -379,6 +419,7 @@
isa = PBXGroup;
children = (
D0CD12151CCFEB4E000DE7BC /* ScrollToTopProxyView.swift */,
D08E90461D243C2F00533158 /* HighlightTrackingButton.swift */,
D0E35A021DE473B900BC6096 /* HighlightableButton.swift */,
D00C7CD11E3657570080C3D5 /* TextFieldNode.swift */,
D0A749941E3A9E7B00AD786E /* SwitchNode.swift */,
@ -410,13 +451,11 @@
D05BE4AC1D217F33002BD72C /* Utils */ = {
isa = PBXGroup;
children = (
D05BE4AD1D217F6B002BD72C /* MergedLayoutEvents.swift */,
D015F7531D1B0F6C00E269B5 /* SystemContainedControllerTransitionCoordinator.swift */,
D053CB5E1D22B4F200DD41DF /* CATracingLayer.h */,
D053CB5F1D22B4F200DD41DF /* CATracingLayer.m */,
D08E90461D243C2F00533158 /* HighlightTrackingButton.swift */,
D05174B11EAA833200A1BF36 /* CASeeThroughTracingLayer.h */,
D05174B21EAA833200A1BF36 /* CASeeThroughTracingLayer.m */,
D01847651FFA72E000075256 /* ContainedViewLayoutTransition.swift */,
);
name = Utils;
sourceTree = "<group>";
@ -472,6 +511,7 @@
D05CC2A31B6932D500E235A3 /* Frameworks */ = {
isa = PBXGroup;
children = (
D01C06C61FC2558F001561AB /* SwiftSignalKitMac.framework */,
D0C2DFFB1CC528B70044FF83 /* SwiftSignalKit.framework */,
D0E1D6711CBC201E00B04029 /* AsyncDisplayKit.framework */,
D0E1D6351CBC159C00B04029 /* AVFoundation.framework */,
@ -609,9 +649,10 @@
D0C2DFC21CC4431D0044FF83 /* ListViewTransactionQueue.swift */,
D0C2DFC31CC4431D0044FF83 /* ListViewAccessoryItem.swift */,
D0C2DFC41CC4431D0044FF83 /* ListViewScroller.swift */,
D01159C11F40EA120039383E /* ListViewScrollerAppkit.swift */,
D0C2DFC51CC4431D0044FF83 /* ListViewAccessoryItemNode.swift */,
D0F7AB361DCFF6F8009AD9A1 /* ListViewItemHeader.swift */,
D0AA840D1FEBFB72005C6E91 /* ListViewFloatingHeaderNode.swift */,
D0AA840F1FED2887005C6E91 /* ListViewOverscrollBackgroundNode.swift */,
);
name = "List Node";
sourceTree = "<group>";
@ -816,7 +857,27 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D01159C21F40EA120039383E /* ListViewScrollerAppkit.swift in Sources */,
D01847691FFA756600075256 /* ListViewAccessoryItemNode.swift in Sources */,
D01847611FFA703B00075256 /* UIKit.swift in Sources */,
D01847701FFA773100075256 /* ListView.swift in Sources */,
D01847771FFA78C100075256 /* UIGestureRecognizer.swift in Sources */,
D01C06C21FC254F8001561AB /* ASDisplayNode.swift in Sources */,
D01847631FFA70FC00075256 /* UIView.swift in Sources */,
D01847791FFA7A4E00075256 /* UITouch.swift in Sources */,
D01C06C41FC25561001561AB /* ListViewItemNode.swift in Sources */,
D018476D1FFA765D00075256 /* NSValueAdditions.swift in Sources */,
D01847641FFA723600075256 /* CAAnimationUtils.swift in Sources */,
D018476A1FFA75EE00075256 /* Spring.swift in Sources */,
D01C06C31FC2552C001561AB /* ListViewItemHeader.swift in Sources */,
D018477C1FFA7ABF00075256 /* UISlider.swift in Sources */,
D01847671FFA72E000075256 /* ContainedViewLayoutTransition.swift in Sources */,
D018476E1FFA76DC00075256 /* ListViewItem.swift in Sources */,
D01847751FFA78B200075256 /* ListViewScroller.swift in Sources */,
D01847711FFA778100075256 /* ListViewIntermediateState.swift in Sources */,
D018476F1FFA76FD00075256 /* ListViewAccessoryItem.swift in Sources */,
D018477A1FFA7A8800075256 /* ListViewOverscrollBackgroundNode.swift in Sources */,
D01847741FFA780400075256 /* UIScrollView.swift in Sources */,
D01847681FFA749F00075256 /* ListViewAnimation.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -885,6 +946,7 @@
D03725C51D6DF8B9007FC290 /* ContextMenuController.swift in Sources */,
D03725C31D6DF7A6007FC290 /* ContextMenuAction.swift in Sources */,
D015F75A1D1B46B600E269B5 /* ActionSheetControllerNode.swift in Sources */,
D01847661FFA72E000075256 /* ContainedViewLayoutTransition.swift in Sources */,
D03725C11D6DF594007FC290 /* ContextMenuNode.swift in Sources */,
D053CB611D22B4F200DD41DF /* CATracingLayer.m in Sources */,
D01E2BE41D904A000066BF65 /* GridItem.swift in Sources */,
@ -898,14 +960,15 @@
D05CC2EC1B69558A00E235A3 /* RuntimeUtils.m in Sources */,
D0E35A031DE473B900BC6096 /* HighlightableButton.swift in Sources */,
D0CD12161CCFEB4E000DE7BC /* ScrollToTopProxyView.swift in Sources */,
D0AA840E1FEBFB72005C6E91 /* ListViewFloatingHeaderNode.swift in Sources */,
D0C2DFCD1CC4431D0044FF83 /* ListViewTransactionQueue.swift in Sources */,
D0AA84101FED2887005C6E91 /* ListViewOverscrollBackgroundNode.swift in Sources */,
D02383821DDF798E004018B6 /* LegacyPresentedControllerNode.swift in Sources */,
D05CC2FC1B6955D000E235A3 /* UIKitUtils.m in Sources */,
D0C2DFC61CC4431D0044FF83 /* ASTransformLayerNode.swift in Sources */,
D05CC3291B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift in Sources */,
D077B8E91F4637040046D27A /* NavigationBarBadge.swift in Sources */,
D0CE67921F7DA11700FFB557 /* ActionSheetTheme.swift in Sources */,
D05BE4AE1D217F6B002BD72C /* MergedLayoutEvents.swift in Sources */,
D0C0D2901C997110001D2851 /* FBAnimationPerformanceTracker.mm in Sources */,
D015F7521D1AE08D00E269B5 /* ContainableController.swift in Sources */,
D036574B1E71C44D00BB1EE4 /* MinimizeKeyboardGestureRecognizer.swift in Sources */,
@ -919,7 +982,6 @@
D0DA444E1E4DCA6E005FDCA7 /* AlertControllerNode.swift in Sources */,
D0B367201C94A53A00346D2E /* StatusBarProxyNode.swift in Sources */,
D05CC2A21B69326C00E235A3 /* WindowContent.swift in Sources */,
D015F7541D1B0F6C00E269B5 /* SystemContainedControllerTransitionCoordinator.swift in Sources */,
D05CC3151B695A9600E235A3 /* NavigationTransitionCoordinator.swift in Sources */,
D03B0E701D6331FB00955575 /* StatusBarHost.swift in Sources */,
D02383801DDF7916004018B6 /* LegacyPresentedController.swift in Sources */,
@ -1527,6 +1589,144 @@
};
name = "Release AppStore";
};
D0924FD41FE52BE9003F693F /* Release Hockeyapp Internal */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = "Release Hockeyapp Internal";
};
D0924FD51FE52BE9003F693F /* Release Hockeyapp Internal */ = {
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = YES;
INFOPLIST_FILE = Display/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_SWIFT_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = org.telegram.Display;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = X834Q8SBVP/;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_REFLECTION_METADATA_LEVEL = none;
SWIFT_VERSION = 4.0;
};
name = "Release Hockeyapp Internal";
};
D0924FD61FE52BE9003F693F /* Release Hockeyapp Internal */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = DisplayTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.telegram.DisplayTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0;
};
name = "Release Hockeyapp Internal";
};
D0924FD71FE52BE9003F693F /* Release Hockeyapp Internal */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = X834Q8SBVP;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_VERSION = A;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = DisplayMac/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = org.Telegram.DisplayMac;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_VERSION = 4.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = "Release Hockeyapp Internal";
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -1536,6 +1736,7 @@
D01159BC1F40E96C0039383E /* Debug Hockeyapp */,
D01159BD1F40E96C0039383E /* Debug AppStore */,
D01159BE1F40E96C0039383E /* Release Hockeyapp */,
D0924FD71FE52BE9003F693F /* Release Hockeyapp Internal */,
D01159BF1F40E96C0039383E /* Release AppStore */,
);
defaultConfigurationIsVisible = 0;
@ -1547,6 +1748,7 @@
D05CC2751B69316F00E235A3 /* Debug Hockeyapp */,
D079FD091F06BD9C0038FADE /* Debug AppStore */,
D05CC2761B69316F00E235A3 /* Release Hockeyapp */,
D0924FD41FE52BE9003F693F /* Release Hockeyapp Internal */,
D086A56E1CC0115D00F08284 /* Release AppStore */,
);
defaultConfigurationIsVisible = 0;
@ -1558,6 +1760,7 @@
D05CC2781B69316F00E235A3 /* Debug Hockeyapp */,
D079FD0A1F06BD9C0038FADE /* Debug AppStore */,
D05CC2791B69316F00E235A3 /* Release Hockeyapp */,
D0924FD51FE52BE9003F693F /* Release Hockeyapp Internal */,
D086A56F1CC0115D00F08284 /* Release AppStore */,
);
defaultConfigurationIsVisible = 0;
@ -1569,6 +1772,7 @@
D05CC27B1B69316F00E235A3 /* Debug Hockeyapp */,
D079FD0B1F06BD9C0038FADE /* Debug AppStore */,
D05CC27C1B69316F00E235A3 /* Release Hockeyapp */,
D0924FD61FE52BE9003F693F /* Release Hockeyapp Internal */,
D086A5701CC0115D00F08284 /* Release AppStore */,
);
defaultConfigurationIsVisible = 0;

View File

@ -7,17 +7,17 @@
<key>Display.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>22</integer>
<integer>23</integer>
</dict>
<key>DisplayMac.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>25</integer>
<integer>26</integer>
</dict>
<key>DisplayTests.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>23</integer>
<integer>24</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>

View File

@ -55,6 +55,7 @@ public class ActionSheetButtonNode: ActionSheetItemNode {
self.label.isLayerBacked = true
self.label.maximumNumberOfLines = 1
self.label.displaysAsynchronously = false
self.label.truncationMode = .byTruncatingTail
super.init(theme: theme)
@ -108,7 +109,7 @@ public class ActionSheetButtonNode: ActionSheetItemNode {
self.button.frame = CGRect(origin: CGPoint(), size: size)
let labelSize = self.label.measure(size)
let labelSize = self.label.measure(CGSize(width: max(1.0, size.width - 10.0), height: size.height))
self.label.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - labelSize.width) / 2.0), y: floorToScreenPixels((size.height - labelSize.height) / 2.0)), size: labelSize)
}

View File

@ -25,6 +25,8 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
var dismiss: () -> Void = { }
private var validLayout: ContainerViewLayout?
init(theme: ActionSheetControllerTheme) {
self.theme = theme
@ -76,7 +78,11 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
let insets = layout.insets(options: [.statusBar])
var insets = layout.insets(options: [.statusBar])
insets.left += layout.safeInsets.left
insets.right += layout.safeInsets.right
self.validLayout = layout
self.scrollView.frame = CGRect(origin: CGPoint(), size: layout.size)
self.dismissTapView.frame = CGRect(origin: CGPoint(), size: layout.size)
@ -85,7 +91,7 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
self.itemGroupsContainerNode.frame = CGRect(origin: CGPoint(x: insets.left + containerInsets.left, y: layout.size.height - insets.bottom - containerInsets.bottom - self.itemGroupsContainerNode.calculatedSize.height), size: self.itemGroupsContainerNode.calculatedSize)
self.itemGroupsContainerNode.layout()
self.updateScrollDimViews(size: layout.size)
self.updateScrollDimViews(size: layout.size, safeInsets: layout.safeInsets)
}
func animateIn() {
@ -135,7 +141,9 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.updateScrollDimViews(size: self.scrollView.frame.size)
if let validLayout = self.validLayout {
self.updateScrollDimViews(size: validLayout.size, safeInsets: validLayout.safeInsets)
}
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
@ -147,15 +155,15 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
}
}
func updateScrollDimViews(size: CGSize) {
func updateScrollDimViews(size: CGSize, safeInsets: UIEdgeInsets) {
let additionalTopHeight = max(0.0, -self.scrollView.contentOffset.y)
let additionalBottomHeight = -min(0.0, -self.scrollView.contentOffset.y)
self.topDimView.frame = CGRect(x: containerInsets.left, y: -additionalTopHeight, width: size.width - containerInsets.left - containerInsets.right, height: max(0.0, self.itemGroupsContainerNode.frame.minY + additionalTopHeight))
self.bottomDimView.frame = CGRect(x: containerInsets.left, y: self.itemGroupsContainerNode.frame.maxY, width: size.width - containerInsets.left - containerInsets.right, height: max(0.0, size.height - self.itemGroupsContainerNode.frame.maxY + additionalBottomHeight))
self.leftDimView.frame = CGRect(x: 0.0, y: -additionalTopHeight, width: containerInsets.left, height: size.height + additionalTopHeight + additionalBottomHeight)
self.rightDimView.frame = CGRect(x: size.width - containerInsets.right, y: -additionalTopHeight, width: containerInsets.right, height: size.height + additionalTopHeight + additionalBottomHeight)
self.leftDimView.frame = CGRect(x: 0.0, y: -additionalTopHeight, width: containerInsets.left + safeInsets.left, height: size.height + additionalTopHeight + additionalBottomHeight)
self.rightDimView.frame = CGRect(x: size.width - containerInsets.right, y: -additionalTopHeight, width: containerInsets.right + safeInsets.right, height: size.height + additionalTopHeight + additionalBottomHeight)
}
func setGroups(_ groups: [ActionSheetItemGroup]) {

View File

@ -1,4 +1,8 @@
#if os(macOS)
import Cocoa
#else
import UIKit
#endif
@objc private class CALayerAnimationDelegate: NSObject, CAAnimationDelegate {
var completion: ((Bool) -> Void)?

View File

@ -32,8 +32,8 @@ public func childWindowHostView(parent: UIView) -> WindowHostView {
let hostView = WindowHostView(view: view, isRotating: {
return false
}, updateSupportedInterfaceOrientations: { orientations in
//rootViewController.orientations = orientations
}, updateDeferScreenEdgeGestures: { edges in
}, updatePreferNavigationUIHidden: { value in
})
view.updateSize = { [weak hostView] size in

View File

@ -1,418 +1,6 @@
import UIKit
import AsyncDisplayKit
public enum ContainedViewLayoutTransitionCurve {
case easeInOut
case spring
}
public extension ContainedViewLayoutTransitionCurve {
var timingFunction: String {
switch self {
case .easeInOut:
return kCAMediaTimingFunctionEaseInEaseOut
case .spring:
return kCAMediaTimingFunctionSpring
}
}
var viewAnimationOptions: UIViewAnimationOptions {
switch self {
case .easeInOut:
return [.curveEaseInOut]
case .spring:
return UIViewAnimationOptions(rawValue: 7 << 16)
}
}
}
public enum ContainedViewLayoutTransition {
case immediate
case animated(duration: Double, curve: ContainedViewLayoutTransitionCurve)
public var isAnimated: Bool {
if case .immediate = self {
return false
} else {
return true
}
}
}
public extension ContainedViewLayoutTransition {
func updateFrame(node: ASDisplayNode, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
if node.frame.equalTo(frame) && !force {
completion?(true)
} else {
switch self {
case .immediate:
node.frame = frame
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousFrame = node.frame
node.frame = frame
node.layer.animateFrame(from: previousFrame, to: frame, duration: duration, timingFunction: curve.timingFunction, force: force, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func updateBounds(node: ASDisplayNode, bounds: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
if node.bounds.equalTo(bounds) && !force {
completion?(true)
} else {
switch self {
case .immediate:
node.bounds = bounds
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousBounds = node.bounds
node.bounds = bounds
node.layer.animateBounds(from: previousBounds, to: bounds, duration: duration, timingFunction: curve.timingFunction, force: force, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func updatePosition(node: ASDisplayNode, position: CGPoint, completion: ((Bool) -> Void)? = nil) {
if node.position.equalTo(position) {
completion?(true)
} else {
switch self {
case .immediate:
node.position = position
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousPosition = node.position
node.position = position
node.layer.animatePosition(from: previousPosition, to: position, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func animatePosition(node: ASDisplayNode, from position: CGPoint, completion: ((Bool) -> Void)? = nil) {
switch self {
case .immediate:
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
node.layer.animatePosition(from: position, to: node.position, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func animatePosition(node: ASDisplayNode, to position: CGPoint, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
if node.position.equalTo(position) {
completion?(true)
} else {
switch self {
case .immediate:
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
node.layer.animatePosition(from: node.position, to: position, duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: removeOnCompletion, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func animateFrame(node: ASDisplayNode, from frame: CGRect, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
switch self {
case .immediate:
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
node.layer.animateFrame(from: frame, to: node.layer.frame, duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: removeOnCompletion, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func animateBounds(layer: CALayer, from bounds: CGRect, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
switch self {
case .immediate:
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
layer.animateBounds(from: bounds, to: layer.bounds, duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: removeOnCompletion, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func animateOffsetAdditive(node: ASDisplayNode, offset: CGFloat) {
switch self {
case .immediate:
break
case let .animated(duration, curve):
let timingFunction: String
switch curve {
case .easeInOut:
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
case .spring:
timingFunction = kCAMediaTimingFunctionSpring
}
node.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, timingFunction: timingFunction)
}
}
func animateOffsetAdditive(layer: CALayer, offset: CGFloat, completion: (() -> Void)? = nil) {
switch self {
case .immediate:
completion?()
case let .animated(duration, curve):
let timingFunction: String
switch curve {
case .easeInOut:
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
case .spring:
timingFunction = kCAMediaTimingFunctionSpring
}
layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, timingFunction: timingFunction, completion: { _ in
completion?()
})
}
}
func animatePositionAdditive(node: ASDisplayNode, offset: CGFloat) {
switch self {
case .immediate:
break
case let .animated(duration, curve):
let timingFunction: String
switch curve {
case .easeInOut:
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
case .spring:
timingFunction = kCAMediaTimingFunctionSpring
}
node.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
}
}
func updateFrame(view: UIView, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
if view.frame.equalTo(frame) && !force {
completion?(true)
} else {
switch self {
case .immediate:
view.frame = frame
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousFrame = view.frame
view.frame = frame
view.layer.animateFrame(from: previousFrame, to: frame, duration: duration, timingFunction: curve.timingFunction, force: force, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func updateFrame(layer: CALayer, frame: CGRect, completion: ((Bool) -> Void)? = nil) {
if layer.frame.equalTo(frame) {
completion?(true)
} else {
switch self {
case .immediate:
layer.frame = frame
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousFrame = layer.frame
layer.frame = frame
layer.animateFrame(from: previousFrame, to: frame, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func updateAlpha(node: ASDisplayNode, alpha: CGFloat, completion: ((Bool) -> Void)? = nil) {
if node.alpha.isEqual(to: alpha) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
node.alpha = alpha
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousAlpha = node.alpha
node.alpha = alpha
node.layer.animateAlpha(from: previousAlpha, to: alpha, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func updateAlpha(layer: CALayer, alpha: CGFloat, completion: ((Bool) -> Void)? = nil) {
if layer.opacity.isEqual(to: Float(alpha)) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
layer.opacity = Float(alpha)
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousAlpha = layer.opacity
layer.opacity = Float(alpha)
layer.animateAlpha(from: CGFloat(previousAlpha), to: alpha, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func updateBackgroundColor(node: ASDisplayNode, color: UIColor, completion: ((Bool) -> Void)? = nil) {
if let nodeColor = node.backgroundColor, nodeColor.isEqual(color) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
node.backgroundColor = color
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
if let nodeColor = node.backgroundColor {
node.backgroundColor = color
node.layer.animate(from: nodeColor.cgColor, to: color.cgColor, keyPath: "backgroundColor", timingFunction: curve.timingFunction, duration: duration, completion: { result in
if let completion = completion {
completion(result)
}
})
} else {
node.backgroundColor = color
if let completion = completion {
completion(true)
}
}
}
}
func updateTransformScale(node: ASDisplayNode, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
let t = node.layer.transform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
if currentScale.isEqual(to: scale) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
node.layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
node.layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
node.layer.animateScale(from: currentScale, to: scale, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func updateSublayerTransformOffset(layer: CALayer, offset: CGPoint, completion: ((Bool) -> Void)? = nil) {
print("update to \(offset) animated: \(self.isAnimated)")
let t = layer.transform
let currentOffset = CGPoint(x: t.m41, y: t.m42)
if currentOffset == offset {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
layer.sublayerTransform = CATransform3DMakeTranslation(offset.x, offset.y, 0.0)
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
layer.sublayerTransform = CATransform3DMakeTranslation(offset.x, offset.y, 0.0)
layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: nil, removeOnCompletion: true, additive: false, completion: {
result in
if let completion = completion {
completion(result)
}
})
}
}
}
public extension ContainedViewLayoutTransition {
public func animateView(_ f: @escaping () -> Void) {
switch self {
case .immediate:
f()
case let .animated(duration, curve):
UIView.animate(withDuration: duration, delay: 0.0, options: curve.viewAnimationOptions, animations: {
f()
}, completion: nil)
}
}
}
public protocol ContainableController: class {
var view: UIView! { get }

View File

@ -0,0 +1,560 @@
import Foundation
#if os(macOS)
import QuartzCore
#else
import AsyncDisplayKit
#endif
public enum ContainedViewLayoutTransitionCurve {
case easeInOut
case spring
}
public extension ContainedViewLayoutTransitionCurve {
var timingFunction: String {
switch self {
case .easeInOut:
return kCAMediaTimingFunctionEaseInEaseOut
case .spring:
return kCAMediaTimingFunctionSpring
}
}
#if os(iOS)
var viewAnimationOptions: UIViewAnimationOptions {
switch self {
case .easeInOut:
return [.curveEaseInOut]
case .spring:
return UIViewAnimationOptions(rawValue: 7 << 16)
}
}
#endif
}
public enum ContainedViewLayoutTransition {
case immediate
case animated(duration: Double, curve: ContainedViewLayoutTransitionCurve)
public var isAnimated: Bool {
if case .immediate = self {
return false
} else {
return true
}
}
}
public extension ContainedViewLayoutTransition {
func updateFrame(node: ASDisplayNode, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
if node.frame.equalTo(frame) && !force {
completion?(true)
} else {
switch self {
case .immediate:
node.frame = frame
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousFrame = node.frame
node.frame = frame
node.layer.animateFrame(from: previousFrame, to: frame, duration: duration, timingFunction: curve.timingFunction, force: force, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func updateBounds(node: ASDisplayNode, bounds: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
if node.bounds.equalTo(bounds) && !force {
completion?(true)
} else {
switch self {
case .immediate:
node.bounds = bounds
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousBounds = node.bounds
node.bounds = bounds
node.layer.animateBounds(from: previousBounds, to: bounds, duration: duration, timingFunction: curve.timingFunction, force: force, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func updatePosition(node: ASDisplayNode, position: CGPoint, completion: ((Bool) -> Void)? = nil) {
if node.position.equalTo(position) {
completion?(true)
} else {
switch self {
case .immediate:
node.position = position
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousPosition = node.position
node.position = position
node.layer.animatePosition(from: previousPosition, to: position, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func updatePosition(layer: CALayer, position: CGPoint, completion: ((Bool) -> Void)? = nil) {
if layer.position.equalTo(position) {
completion?(true)
} else {
switch self {
case .immediate:
layer.position = position
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousPosition = layer.position
layer.position = position
layer.animatePosition(from: previousPosition, to: position, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func animatePosition(node: ASDisplayNode, from position: CGPoint, completion: ((Bool) -> Void)? = nil) {
switch self {
case .immediate:
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
node.layer.animatePosition(from: position, to: node.position, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func animatePosition(node: ASDisplayNode, to position: CGPoint, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
if node.position.equalTo(position) {
completion?(true)
} else {
switch self {
case .immediate:
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
node.layer.animatePosition(from: node.position, to: position, duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: removeOnCompletion, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func animateFrame(node: ASDisplayNode, from frame: CGRect, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
switch self {
case .immediate:
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
node.layer.animateFrame(from: frame, to: node.layer.frame, duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: removeOnCompletion, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func animateBounds(layer: CALayer, from bounds: CGRect, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
switch self {
case .immediate:
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
layer.animateBounds(from: bounds, to: layer.bounds, duration: duration, timingFunction: curve.timingFunction, removeOnCompletion: removeOnCompletion, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func animateOffsetAdditive(node: ASDisplayNode, offset: CGFloat) {
switch self {
case .immediate:
break
case let .animated(duration, curve):
let timingFunction: String
switch curve {
case .easeInOut:
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
case .spring:
timingFunction = kCAMediaTimingFunctionSpring
}
node.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, timingFunction: timingFunction)
}
}
func animateOffsetAdditive(layer: CALayer, offset: CGFloat, completion: (() -> Void)? = nil) {
switch self {
case .immediate:
completion?()
case let .animated(duration, curve):
let timingFunction: String
switch curve {
case .easeInOut:
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
case .spring:
timingFunction = kCAMediaTimingFunctionSpring
}
layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, timingFunction: timingFunction, completion: { _ in
completion?()
})
}
}
func animatePositionAdditive(node: ASDisplayNode, offset: CGFloat) {
switch self {
case .immediate:
break
case let .animated(duration, curve):
let timingFunction: String
switch curve {
case .easeInOut:
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
case .spring:
timingFunction = kCAMediaTimingFunctionSpring
}
node.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
}
}
func animatePositionAdditive(node: ASDisplayNode, offset: CGPoint, completion: (() -> Void)? = nil) {
switch self {
case .immediate:
break
case let .animated(duration, curve):
let timingFunction: String
switch curve {
case .easeInOut:
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
case .spring:
timingFunction = kCAMediaTimingFunctionSpring
}
node.layer.animatePosition(from: offset, to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true, completion: { _ in
completion?()
})
}
}
func animatePositionAdditive(layer: CALayer, offset: CGPoint, completion: (() -> Void)? = nil) {
switch self {
case .immediate:
break
case let .animated(duration, curve):
let timingFunction: String
switch curve {
case .easeInOut:
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
case .spring:
timingFunction = kCAMediaTimingFunctionSpring
}
layer.animatePosition(from: offset, to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true, completion: { _ in
completion?()
})
}
}
func updateFrame(view: UIView, frame: CGRect, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
if view.frame.equalTo(frame) && !force {
completion?(true)
} else {
switch self {
case .immediate:
view.frame = frame
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousFrame = view.frame
view.frame = frame
view.layer.animateFrame(from: previousFrame, to: frame, duration: duration, timingFunction: curve.timingFunction, force: force, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func updateFrame(layer: CALayer, frame: CGRect, completion: ((Bool) -> Void)? = nil) {
if layer.frame.equalTo(frame) {
completion?(true)
} else {
switch self {
case .immediate:
layer.frame = frame
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousFrame = layer.frame
layer.frame = frame
layer.animateFrame(from: previousFrame, to: frame, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func updateAlpha(node: ASDisplayNode, alpha: CGFloat, completion: ((Bool) -> Void)? = nil) {
if node.alpha.isEqual(to: alpha) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
node.alpha = alpha
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousAlpha = node.alpha
node.alpha = alpha
node.layer.animateAlpha(from: previousAlpha, to: alpha, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func updateAlpha(layer: CALayer, alpha: CGFloat, completion: ((Bool) -> Void)? = nil) {
if layer.opacity.isEqual(to: Float(alpha)) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
layer.opacity = Float(alpha)
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let previousAlpha = layer.opacity
layer.opacity = Float(alpha)
layer.animateAlpha(from: CGFloat(previousAlpha), to: alpha, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func updateBackgroundColor(node: ASDisplayNode, color: UIColor, completion: ((Bool) -> Void)? = nil) {
if let nodeColor = node.backgroundColor, nodeColor.isEqual(color) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
node.backgroundColor = color
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
if let nodeColor = node.backgroundColor {
node.backgroundColor = color
node.layer.animate(from: nodeColor.cgColor, to: color.cgColor, keyPath: "backgroundColor", timingFunction: curve.timingFunction, duration: duration, completion: { result in
if let completion = completion {
completion(result)
}
})
} else {
node.backgroundColor = color
if let completion = completion {
completion(true)
}
}
}
}
func animateTransformScale(node: ASDisplayNode, from fromScale: CGFloat, completion: ((Bool) -> Void)? = nil) {
let t = node.layer.transform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
if currentScale.isEqual(to: fromScale) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
node.layer.animateScale(from: fromScale, to: currentScale, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func updateTransformScale(node: ASDisplayNode, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
let t = node.layer.transform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
if currentScale.isEqual(to: scale) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
node.layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
node.layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
node.layer.animateScale(from: currentScale, to: scale, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func updateTransformScale(layer: CALayer, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
let t = layer.transform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
if currentScale.isEqual(to: scale) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
layer.animateScale(from: currentScale, to: scale, duration: duration, timingFunction: curve.timingFunction, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
func updateSublayerTransformScale(node: ASDisplayNode, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
let t = node.layer.sublayerTransform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
if currentScale.isEqual(to: scale) {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0)
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0)
node.layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: node.layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: nil, removeOnCompletion: true, additive: false, completion: {
result in
if let completion = completion {
completion(result)
}
})
}
}
func updateSublayerTransformOffset(layer: CALayer, offset: CGPoint, completion: ((Bool) -> Void)? = nil) {
print("update to \(offset) animated: \(self.isAnimated)")
let t = layer.transform
let currentOffset = CGPoint(x: t.m41, y: t.m42)
if currentOffset == offset {
if let completion = completion {
completion(true)
}
return
}
switch self {
case .immediate:
layer.sublayerTransform = CATransform3DMakeTranslation(offset.x, offset.y, 0.0)
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
layer.sublayerTransform = CATransform3DMakeTranslation(offset.x, offset.y, 0.0)
layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: nil, removeOnCompletion: true, additive: false, completion: {
result in
if let completion = completion {
completion(result)
}
})
}
}
}
#if os(iOS)
public extension ContainedViewLayoutTransition {
public func animateView(_ f: @escaping () -> Void) {
switch self {
case .immediate:
f()
case let .animated(duration, curve):
UIView.animate(withDuration: duration, delay: 0.0, options: curve.viewAnimationOptions, animations: {
f()
}, completion: nil)
}
}
}
#endif

View File

@ -297,6 +297,9 @@ public class DrawingContext {
self.provider = CGDataProvider(dataInfo: bytes, data: bytes, size: length, releaseData: { bytes, _, _ in
free(bytes)
})
assert(self.bytesPerRow % 16 == 0)
assert(unsafeBitCast(self.bytes, to: Int64.self) % 16 == 0)
}
public func generateImage() -> UIImage? {

View File

@ -35,6 +35,13 @@ class KeyboardManager {
self.host = host
}
func getCurrentKeyboardHeight() -> CGFloat {
guard let keyboardView = self.host.keyboardView else {
return 0.0
}
return keyboardView.bounds.height
}
func updateInteractiveInputOffset(_ offset: CGFloat, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) {
guard let keyboardView = self.host.keyboardView else {
return
@ -60,24 +67,31 @@ class KeyboardManager {
}
var firstResponderView: UIView?
var firstResponderDisablesAutomaticKeyboardHandling = false
var firstResponderDisableAutomaticKeyboardHandling: UIResponderDisableAutomaticKeyboardHandling = []
for surface in self.surfaces {
if let view = getFirstResponder(surface.host) {
firstResponderView = surface.host
firstResponderDisablesAutomaticKeyboardHandling = view.disablesAutomaticKeyboardHandling
firstResponderDisableAutomaticKeyboardHandling = view.disableAutomaticKeyboardHandling
break
}
}
if let firstResponderView = firstResponderView {
let containerOrigin = firstResponderView.convert(CGPoint(), to: nil)
let horizontalTranslation = CATransform3DMakeTranslation(firstResponderDisablesAutomaticKeyboardHandling ? 0.0 : containerOrigin.x, 0.0, 0.0)
var filteredTranslation = containerOrigin.x
if firstResponderDisableAutomaticKeyboardHandling.contains(.forward) {
filteredTranslation = max(0.0, filteredTranslation)
}
if firstResponderDisableAutomaticKeyboardHandling.contains(.backward) {
filteredTranslation = min(0.0, filteredTranslation)
}
let horizontalTranslation = CATransform3DMakeTranslation(filteredTranslation, 0.0, 0.0)
let currentTransform = keyboardWindow.layer.sublayerTransform
if !CATransform3DEqualToTransform(horizontalTranslation, currentTransform) {
//print("set to \(CGPoint(x: containerOrigin.x, y: self.interactiveInputOffset))")
keyboardWindow.layer.sublayerTransform = horizontalTranslation
}
if let tracingLayer = firstResponderView.layer as? CATracingLayer, !firstResponderDisablesAutomaticKeyboardHandling {
if let tracingLayer = firstResponderView.layer as? CATracingLayer, firstResponderDisableAutomaticKeyboardHandling.isEmpty {
if let previousPositionAnimationMirrorSource = self.previousPositionAnimationMirrorSource, previousPositionAnimationMirrorSource !== tracingLayer {
previousPositionAnimationMirrorSource.setPositionAnimationMirrorTarget(nil)
}

View File

@ -1,6 +1,10 @@
#if os(macOS)
import SwiftSignalKitMac
#else
import UIKit
import AsyncDisplayKit
import SwiftSignalKit
#endif
private let usePerformanceTracker = false
private let useDynamicTuning = false
@ -97,10 +101,30 @@ public enum ListViewVisibleContentOffset {
case none
}
public struct ListViewKeepTopItemOverscrollBackground {
public let color: UIColor
public let direction: Bool
public init(color: UIColor, direction: Bool) {
self.color = color
self.direction = direction
}
fileprivate func isEqual(to: ListViewKeepTopItemOverscrollBackground) -> Bool {
if !self.color.isEqual(to.color) {
return false
}
if self.direction != to.direction {
return false
}
return true
}
}
open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDelegate {
private final let scroller: ListViewScroller
private final var visibleSize: CGSize = CGSize()
private final var insets = UIEdgeInsets()
public private(set) final var insets = UIEdgeInsets()
private final var lastContentOffset: CGPoint = CGPoint()
private final var lastContentOffsetTimestamp: CFAbsoluteTime = 0.0
private final var ignoreScrollingEvents: Bool = false
@ -124,12 +148,12 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
public final var stackFromBottom: Bool = false
public final var stackFromBottomInsetItemFactor: CGFloat = 0.0
public final var limitHitTestToNodes: Bool = false
public final var keepTopItemOverscrollBackground: UIColor? {
public final var keepTopItemOverscrollBackground: ListViewKeepTopItemOverscrollBackground? {
didSet {
if let color = self.keepTopItemOverscrollBackground {
self.topItemOverscrollBackground?.backgroundColor = color
if let value = self.keepTopItemOverscrollBackground {
self.topItemOverscrollBackground?.color = value.color
}
self.updateTopItemOverscrollBackground()
self.updateTopItemOverscrollBackground(transition: .immediate)
}
}
public final var keepBottomItemOverscrollBackground: UIColor? {
@ -142,7 +166,13 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
public final var snapToBottomInsetUntilFirstInteraction: Bool = false
private var topItemOverscrollBackground: ASDisplayNode?
public final var updateFloatingHeaderOffset: ((CGFloat, ContainedViewLayoutTransition) -> Void)? {
didSet {
}
}
private var topItemOverscrollBackground: ListViewOverscrollBackgroundNode?
private var bottomItemOverscrollBackground: ASDisplayNode?
private var touchesPosition = CGPoint()
@ -186,6 +216,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
private var selectionTouchLocation: CGPoint?
private var selectionTouchDelayTimer: Foundation.Timer?
private var selectionLongTapDelayTimer: Foundation.Timer?
private var flashNodesDelayTimer: Foundation.Timer?
private var highlightedItemIndex: Int?
@ -290,18 +321,23 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
self.displayLink.invalidate()
if useBackgroundDeallocation {
for itemNode in self.itemNodes {
ASDeallocQueue.sharedDeallocation().releaseObject(inBackground: itemNode)
assertionFailure()
/*for itemNode in self.itemNodes {
ASDeallocQueue.sharedDeallocation.releaseObject(inBackground: UnsafeMutablePointer(itemNode))
}
for itemHeaderNode in self.itemHeaderNodes {
ASDeallocQueue.sharedDeallocation().releaseObject(inBackground: itemHeaderNode)
}
ASDeallocQueue.sharedDeallocatio.releaseObject(inBackground: itemHeaderNode)
}*/
} else {
for itemNode in self.itemNodes {
ASPerformMainThreadDeallocation(itemNode)
for i in (0 ..< self.itemNodes.count).reversed() {
var itemNode: AnyObject? = self.itemNodes[i]
self.itemNodes.remove(at: i)
ASPerformMainThreadDeallocation(&itemNode)
}
for itemHeaderNode in self.itemHeaderNodes {
ASPerformMainThreadDeallocation(itemHeaderNode)
for key in self.itemHeaderNodes.keys {
var itemHeaderNode: AnyObject? = self.itemHeaderNodes[key]
self.itemHeaderNodes.removeValue(forKey: key)
ASPerformMainThreadDeallocation(&itemHeaderNode)
}
}
@ -443,7 +479,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
let deltaY = scrollView.contentOffset.y - self.lastContentOffset.y
self.lastContentOffset = scrollView.contentOffset
if self.lastContentOffsetTimestamp > DBL_EPSILON {
if !self.lastContentOffsetTimestamp.isZero {
self.lastContentOffsetTimestamp = CACurrentMediaTime()
}
@ -494,14 +530,11 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
if !self.snapToBounds(snapTopItem: false, stackFromBottom: self.stackFromBottom).offset.isZero {
self.updateVisibleContentOffset()
}
self.updateScroller()
self.updateScroller(transition: .immediate)
self.updateItemHeaders()
self.updateItemHeaders(leftInset: self.insets.left, rightInset: self.insets.right)
for (_, headerNode) in self.itemHeaderNodes {
//let position = headerNode.position
//headerNode.position = CGPoint(x: position.x, y: position.y - deltaY)
if headerNode.wantsScrollDynamics {
useScrollDynamics = true
@ -692,6 +725,9 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
var frame = itemNode.frame
frame.origin.y += offset
itemNode.frame = frame
if let accessoryItemNode = itemNode.accessoryItemNode {
itemNode.layoutAccessoryItemNode(accessoryItemNode, leftInset: self.insets.left, rightInset: self.insets.right)
}
}
}
@ -731,15 +767,17 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
self.ignoreScrollingEvents = wasIgnoringScrollingEvents
}
private func updateTopItemOverscrollBackground() {
if let color = self.keepTopItemOverscrollBackground {
let topItemOverscrollBackground: ASDisplayNode
private func updateTopItemOverscrollBackground(transition: ContainedViewLayoutTransition) {
if let value = self.keepTopItemOverscrollBackground {
var applyTransition = transition
let topItemOverscrollBackground: ListViewOverscrollBackgroundNode
if let current = self.topItemOverscrollBackground {
topItemOverscrollBackground = current
} else {
topItemOverscrollBackground = ASDisplayNode()
applyTransition = .immediate
topItemOverscrollBackground = ListViewOverscrollBackgroundNode(color: value.color)
topItemOverscrollBackground.isLayerBacked = true
topItemOverscrollBackground.backgroundColor = color
self.topItemOverscrollBackground = topItemOverscrollBackground
self.insertSubnode(topItemOverscrollBackground, at: 0)
}
@ -752,25 +790,85 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
topItemFound = true
}
var backgroundFrame: CGRect
if topItemFound {
let realTopItemEdge = itemNodes.first!.apparentFrame.origin.y
let realTopItemEdgeOffset = max(0.0, realTopItemEdge)
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.visibleSize.width, height: realTopItemEdgeOffset))
if !backgroundFrame.equalTo(topItemOverscrollBackground.frame) {
topItemOverscrollBackground.frame = backgroundFrame
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.visibleSize.width, height: realTopItemEdgeOffset))
if value.direction {
backgroundFrame.origin.y = 0.0
backgroundFrame.size.height = realTopItemEdgeOffset
} else {
backgroundFrame.origin.y = min(self.insets.top, realTopItemEdgeOffset)
backgroundFrame.size.height = max(0.0, self.visibleSize.height - backgroundFrame.origin.y) + 400.0
}
} else {
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.visibleSize.width, height: 0.0))
if !backgroundFrame.equalTo(topItemOverscrollBackground.frame) {
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: self.visibleSize.width, height: 0.0))
if value.direction {
backgroundFrame.origin.y = 0.0
} else {
backgroundFrame.origin.y = 0.0
backgroundFrame.size.height = self.visibleSize.height
}
}
let previousFrame = topItemOverscrollBackground.frame
if !previousFrame.equalTo(backgroundFrame) {
topItemOverscrollBackground.frame = backgroundFrame
let positionDelta = CGPoint(x: backgroundFrame.minX - previousFrame.minX, y: backgroundFrame.minY - previousFrame.minY)
applyTransition.animateOffsetAdditive(node: topItemOverscrollBackground, offset: positionDelta.y)
}
}
topItemOverscrollBackground.updateLayout(size: backgroundFrame.size, transition: applyTransition)
} else if let topItemOverscrollBackground = self.topItemOverscrollBackground {
self.topItemOverscrollBackground = nil
topItemOverscrollBackground.removeFromSupernode()
}
}
private func updateFloatingHeaderNode(transition: ContainedViewLayoutTransition) {
guard let updateFloatingHeaderOffset = self.updateFloatingHeaderOffset else {
return
}
var topItemFound = false
var topItemNodeIndex: Int?
if !self.itemNodes.isEmpty {
topItemNodeIndex = self.itemNodes[0].index
}
if topItemNodeIndex == 0 {
topItemFound = true
}
var topOffset: CGFloat
if topItemFound {
let realTopItemEdge = itemNodes.first!.apparentFrame.origin.y
let realTopItemEdgeOffset = max(0.0, realTopItemEdge)
topOffset = realTopItemEdgeOffset
} else {
if !self.itemNodes.isEmpty {
if self.stackFromBottom {
topOffset = 0.0
} else {
topOffset = self.visibleSize.height
}
} else {
if self.stackFromBottom {
topOffset = self.visibleSize.height
} else {
topOffset = 0.0
}
}
}
updateFloatingHeaderOffset(topOffset, transition)
}
private func updateBottomItemOverscrollBackground() {
if let color = self.keepBottomItemOverscrollBackground {
var bottomItemFound = false
@ -812,7 +910,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
}
private func updateScroller() {
private func updateScroller(transition: ContainedViewLayoutTransition) {
if itemNodes.count == 0 {
return
}
@ -862,8 +960,9 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
}
self.updateTopItemOverscrollBackground()
self.updateTopItemOverscrollBackground(transition: transition)
self.updateBottomItemOverscrollBackground()
self.updateFloatingHeaderNode(transition: transition)
let wasIgnoringScrollingEvents = self.ignoreScrollingEvents
self.ignoreScrollingEvents = true
@ -897,7 +996,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
DispatchQueue.global().async(execute: f)
}
private func nodeForItem(synchronous: Bool, item: ListViewItem, previousNode: ListViewItemNode?, index: Int, previousItem: ListViewItem?, nextItem: ListViewItem?, width: CGFloat, updateAnimation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNode, ListViewItemNodeLayout, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void) {
private func nodeForItem(synchronous: Bool, item: ListViewItem, previousNode: ListViewItemNode?, index: Int, previousItem: ListViewItem?, nextItem: ListViewItem?, params: ListViewItemLayoutParams, updateAnimation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNode, ListViewItemNodeLayout, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void) {
if let previousNode = previousNode {
item.updateNode(async: { f in
if synchronous {
@ -905,7 +1004,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
} else {
self.async(f)
}
}, node: previousNode, width: width, previousItem: previousItem, nextItem: nextItem, animation: updateAnimation, completion: { (layout, apply) in
}, node: previousNode, params: params, previousItem: previousItem, nextItem: nextItem, animation: updateAnimation, completion: { (layout, apply) in
if Thread.isMainThread {
if synchronous {
completion(previousNode, layout, {
@ -934,13 +1033,13 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
})
} else {
item.nodeConfiguredForWidth(async: { f in
item.nodeConfiguredForParams(async: { f in
if synchronous {
f()
} else {
self.async(f)
}
}, width: width, previousItem: previousItem, nextItem: nextItem, completion: { itemNode, apply in
}, params: params, previousItem: previousItem, nextItem: nextItem, completion: { itemNode, apply in
itemNode.index = index
completion(itemNode, ListViewItemNodeLayout(contentSize: itemNode.contentSize, insets: itemNode.insets), apply)
})
@ -1000,7 +1099,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
self.scroller.contentOffset = self.lastContentOffset
self.ignoreScrollingEvents = wasIgnoringScrollingEvents
self.updateScroller()
self.updateScroller(transition: .immediate)
completion()
return
@ -1330,7 +1429,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
} else {
self.async(f)
}
}, node: referenceNode, width: state.visibleSize.width, previousItem: index == 0 ? nil : self.items[index - 1], nextItem: index == self.items.count - 1 ? nil : self.items[index + 1], animation: updateAnimation, completion: { layout, apply in
}, node: referenceNode, params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right), previousItem: index == 0 ? nil : self.items[index - 1], nextItem: index == self.items.count - 1 ? nil : self.items[index + 1], animation: updateAnimation, completion: { layout, apply in
var updatedState = state
var updatedOperations = operations
@ -1398,14 +1497,14 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
if self.debugInfo {
print("insertionItemIndexAndDirection \(insertionItemIndexAndDirection)")
print("insertionItemIndexAndDirection \(String(describing: insertionItemIndexAndDirection))")
}
if let insertionItemIndexAndDirection = insertionItemIndexAndDirection {
let index = insertionItemIndexAndDirection.0
let threadId = pthread_self()
var tailRecurse = false
self.nodeForItem(synchronous: synchronous, item: self.items[index], previousNode: previousNodes[index], index: index, previousItem: index == 0 ? nil : self.items[index - 1], nextItem: self.items.count == index + 1 ? nil : self.items[index + 1], width: state.visibleSize.width, updateAnimation: updateAnimation, completion: { (node, layout, apply) in
self.nodeForItem(synchronous: synchronous, item: self.items[index], previousNode: previousNodes[index], index: index, previousItem: index == 0 ? nil : self.items[index - 1], nextItem: self.items.count == index + 1 ? nil : self.items[index + 1], params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right), updateAnimation: updateAnimation, completion: { (node, layout, apply) in
if pthread_equal(pthread_self(), threadId) != 0 && !tailRecurse {
tailRecurse = true
@ -1441,7 +1540,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
} else {
let updateItem = updateIndicesAndItems[0]
if let previousNode = previousNodes[updateItem.index] {
self.nodeForItem(synchronous: synchronous, item: updateItem.item, previousNode: previousNode, index: updateItem.index, previousItem: updateItem.index == 0 ? nil : self.items[updateItem.index - 1], nextItem: updateItem.index == (self.items.count - 1) ? nil : self.items[updateItem.index + 1], width: state.visibleSize.width, updateAnimation: animated ? .System(duration: insertionAnimationDuration) : .None, completion: { _, layout, apply in
self.nodeForItem(synchronous: synchronous, item: updateItem.item, previousNode: previousNode, index: updateItem.index, previousItem: updateItem.index == 0 ? nil : self.items[updateItem.index - 1], nextItem: updateItem.index == (self.items.count - 1) ? nil : self.items[updateItem.index + 1], params: ListViewItemLayoutParams(width: state.visibleSize.width, leftInset: state.insets.left, rightInset: state.insets.right), updateAnimation: animated ? .System(duration: insertionAnimationDuration) : .None, completion: { _, layout, apply in
state.updateNodeAtItemIndex(updateItem.index, layout: layout, direction: updateItem.directionHint, animation: animated ? .System(duration: insertionAnimationDuration) : .None, apply: apply, operations: &operations)
updateIndicesAndItems.remove(at: 0)
@ -1471,7 +1570,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
}
private func insertNodeAtIndex(animated: Bool, animateAlpha: Bool, forceAnimateInsertion: Bool, previousFrame: CGRect?, nodeIndex: Int, offsetDirection: ListViewInsertionOffsetDirection, node: ListViewItemNode, layout: ListViewItemNodeLayout, apply: () -> (Signal<Void, NoError>?, () -> Void), timestamp: Double) {
private func insertNodeAtIndex(animated: Bool, animateAlpha: Bool, forceAnimateInsertion: Bool, previousFrame: CGRect?, nodeIndex: Int, offsetDirection: ListViewInsertionOffsetDirection, node: ListViewItemNode, layout: ListViewItemNodeLayout, apply: () -> (Signal<Void, NoError>?, () -> Void), timestamp: Double, listInsets: UIEdgeInsets) {
let insertionOrigin = self.referencePointForInsertionAtIndex(nodeIndex)
let nodeOrigin: CGPoint
@ -1491,6 +1590,9 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
node.insets = layout.insets
node.apparentHeight = animated ? 0.0 : layout.size.height
node.frame = nodeFrame
if let accessoryItemNode = node.accessoryItemNode {
node.layoutAccessoryItemNode(accessoryItemNode, leftInset: listInsets.left, rightInset: listInsets.right)
}
apply().1()
self.itemNodes.insert(node, at: nodeIndex)
@ -1505,7 +1607,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
if let _ = previousFrame , animated && node.index != nil && nodeIndex != self.itemNodes.count - 1 {
let nextNode = self.itemNodes[nodeIndex + 1]
if nextNode.index == nil {
if nextNode.index == nil && nextNode.subnodes.isEmpty {
let nextHeight = nextNode.apparentHeight
if abs(nextHeight - previousApparentHeight) < CGFloat.ulpOfOne {
if let animation = nextNode.animationForKey("apparentHeight") {
@ -1539,10 +1641,29 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
if node.index == nil {
node.addHeightAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp)
node.addApparentHeightAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp)
node.animateRemoved(timestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor())
} else if animated {
if !takenAnimation {
if takenAnimation {
if let previousFrame = previousFrame {
if self.debugInfo {
assert(true)
}
let transitionOffsetDelta = nodeFrame.origin.y - previousFrame.origin.y
if node.rotated {
node.transitionOffset -= transitionOffsetDelta - previousApparentHeight + layout.size.height
} else {
node.transitionOffset += transitionOffsetDelta
}
node.addTransitionOffsetAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp)
if previousInsets != layout.insets {
node.insets = previousInsets
node.addInsetsAnimationToValue(layout.insets, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp)
}
}
} else {
if !nodeFrame.size.height.isEqual(to: node.apparentHeight) {
node.addApparentHeightAnimation(nodeFrame.size.height, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp, update: { [weak node] progress, currentValue in
if let node = node {
@ -1596,6 +1717,9 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
var frame = self.itemNodes[i].frame
frame.origin.y -= offsetHeight
self.itemNodes[i].frame = frame
if let accessoryItemNode = self.itemNodes[i].accessoryItemNode {
self.itemNodes[i].layoutAccessoryItemNode(accessoryItemNode, leftInset: listInsets.left, rightInset: listInsets.right)
}
i -= 1
}
case .Down:
@ -1604,6 +1728,9 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
var frame = self.itemNodes[i].frame
frame.origin.y += offsetHeight
self.itemNodes[i].frame = frame
if let accessoryItemNode = self.itemNodes[i].accessoryItemNode {
self.itemNodes[i].layoutAccessoryItemNode(accessoryItemNode, leftInset: listInsets.left, rightInset: listInsets.right)
}
i += 1
}
}
@ -1665,6 +1792,8 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
private func replayOperations(animated: Bool, animateAlpha: Bool, animateCrossfade: Bool, animateTopItemVerticalOrigin: Bool, operations: [ListViewStateOperation], requestItemInsertionAnimationsIndices: Set<Int>, scrollToItem: ListViewScrollToItem?, additionalScrollDistance: CGFloat, updateSizeAndInsets: ListViewUpdateSizeAndInsets?, stationaryItemIndex: Int?, updateOpaqueState: Any?, completion: () -> Void) {
let timestamp = CACurrentMediaTime()
let listInsets = updateSizeAndInsets?.insets ?? self.insets
if let updateOpaqueState = updateOpaqueState {
self.opaqueTransactionState = updateOpaqueState
}
@ -1713,7 +1842,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
updatedPreviousFrame = nil
}
self.insertNodeAtIndex(animated: nodeAnimated, animateAlpha: animateAlpha, forceAnimateInsertion: forceAnimateInsertion, previousFrame: updatedPreviousFrame, nodeIndex: index, offsetDirection: offsetDirection, node: node, layout: layout, apply: apply, timestamp: timestamp)
self.insertNodeAtIndex(animated: nodeAnimated, animateAlpha: animateAlpha, forceAnimateInsertion: forceAnimateInsertion, previousFrame: updatedPreviousFrame, nodeIndex: index, offsetDirection: offsetDirection, node: node, layout: layout, apply: apply, timestamp: timestamp, listInsets: listInsets)
if let _ = updatedPreviousFrame {
if let lowestHeaderNode = lowestHeaderNode {
self.insertSubnode(node, belowSubnode: lowestHeaderNode)
@ -1722,7 +1851,11 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
} else {
if animated {
if let topItemOverscrollBackground = self.topItemOverscrollBackground {
self.insertSubnode(node, aboveSubnode: topItemOverscrollBackground)
} else {
self.insertSubnode(node, at: 0)
}
} else {
if let lowestHeaderNode = lowestHeaderNode {
self.insertSubnode(node, belowSubnode: lowestHeaderNode)
@ -1745,10 +1878,10 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
if let height = height, let previousLayout = previousLayout {
if takenPreviousNodes.contains(referenceNode) {
self.insertNodeAtIndex(animated: false, animateAlpha: false, forceAnimateInsertion: false, previousFrame: nil, nodeIndex: index, offsetDirection: offsetDirection, node: ListViewItemNode(layerBacked: true), layout: ListViewItemNodeLayout(contentSize: CGSize(width: self.visibleSize.width, height: height), insets: UIEdgeInsets()), apply: { return (nil, {}) }, timestamp: timestamp)
self.insertNodeAtIndex(animated: false, animateAlpha: false, forceAnimateInsertion: false, previousFrame: nil, nodeIndex: index, offsetDirection: offsetDirection, node: ListViewItemNode(layerBacked: true), layout: ListViewItemNodeLayout(contentSize: CGSize(width: self.visibleSize.width, height: height), insets: UIEdgeInsets()), apply: { return (nil, {}) }, timestamp: timestamp, listInsets: listInsets)
} else {
referenceNode.index = nil
self.insertNodeAtIndex(animated: false, animateAlpha: false, forceAnimateInsertion: false, previousFrame: nil, nodeIndex: index, offsetDirection: offsetDirection, node: referenceNode, layout: previousLayout, apply: { return (nil, {}) }, timestamp: timestamp)
self.insertNodeAtIndex(animated: false, animateAlpha: false, forceAnimateInsertion: false, previousFrame: nil, nodeIndex: index, offsetDirection: offsetDirection, node: referenceNode, layout: previousLayout, apply: { return (nil, {}) }, timestamp: timestamp, listInsets: listInsets)
self.addSubnode(referenceNode)
}
} else {
@ -1772,6 +1905,9 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
var frame = self.itemNodes[i].frame
frame.origin.y -= height
self.itemNodes[i].frame = frame
if let accessoryItemNode = self.itemNodes[i].accessoryItemNode {
self.itemNodes[i].layoutAccessoryItemNode(accessoryItemNode, leftInset: listInsets.left, rightInset: listInsets.right)
}
}
}
case .Down:
@ -1780,6 +1916,9 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
var frame = self.itemNodes[i].frame
frame.origin.y += height
self.itemNodes[i].frame = frame
if let accessoryItemNode = self.itemNodes[i].accessoryItemNode {
self.itemNodes[i].layoutAccessoryItemNode(accessoryItemNode, leftInset: listInsets.left, rightInset: listInsets.right)
}
}
}
}
@ -1815,8 +1954,8 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
})
let insetPart: CGFloat = previousInsets.top - layout.insets.top
if node.rotated {
let insetPart: CGFloat = previousInsets.bottom - layout.insets.bottom
node.transitionOffset += previousApparentHeight - layout.size.height - insetPart
node.addTransitionOffsetAnimation(0.0, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp)
}
@ -1844,6 +1983,10 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
}
if let accessoryItemNode = node.accessoryItemNode {
node.layoutAccessoryItemNode(accessoryItemNode, leftInset: listInsets.left, rightInset: listInsets.right)
}
var index = 0
for itemNode in self.itemNodes {
let offset = offsetRanges.offsetForIndex(index)
@ -1895,6 +2038,9 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
var frame = itemNode.frame
frame.origin.y += offset
itemNode.frame = frame
if let accessoryItemNode = itemNode.accessoryItemNode {
itemNode.layoutAccessoryItemNode(accessoryItemNode, leftInset: listInsets.left, rightInset: listInsets.right)
}
}
break
@ -1912,6 +2058,9 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
var frame = itemNode.frame
frame.origin.y += offset
itemNode.frame = frame
if let accessoryItemNode = itemNode.accessoryItemNode {
itemNode.layoutAccessoryItemNode(accessoryItemNode, leftInset: listInsets.left, rightInset: listInsets.right)
}
}
}
@ -1923,11 +2072,6 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
} else if !additionalScrollDistance.isZero {
self.stopScrolling()
/*for itemNode in self.itemNodes {
var frame = itemNode.frame
frame.origin.y += additionalScrollDistance
itemNode.frame = frame
}*/
}
self.insertNodesInBatches(nodes: [], completion: {
@ -2092,7 +2236,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
}
self.updateAccessoryNodes(animated: animated, currentTimestamp: timestamp)
self.updateAccessoryNodes(animated: animated, currentTimestamp: timestamp, leftInset: listInsets.left, rightInset: listInsets.right)
if let scrollToItem = scrollToItem, scrollToItem.animated {
if self.itemNodes.count != 0 {
@ -2146,7 +2290,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
previousItemHeaderNodes.append(headerNode)
}
self.updateItemHeaders(headerNodesTransition, animateInsertion: animated || !requestItemInsertionAnimationsIndices.isEmpty)
self.updateItemHeaders(leftInset: listInsets.left, rightInset: listInsets.right, transition: headerNodesTransition, animateInsertion: animated || !requestItemInsertionAnimationsIndices.isEmpty)
if let offset = offset , abs(offset) > CGFloat.ulpOfOne {
let lowestHeaderNode = self.lowestHeaderNode()
@ -2201,29 +2345,32 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
for itemNode in temporaryPreviousNodes {
itemNode.removeFromSupernode()
if useBackgroundDeallocation {
ASDeallocQueue.sharedDeallocation().releaseObject(inBackground: itemNode)
assertionFailure()
//ASDeallocQueue.sharedDeallocation().releaseObject(inBackground: itemNode)
} else {
ASPerformMainThreadDeallocation(itemNode)
//ASPerformMainThreadDeallocation(itemNode)
}
}
for headerNode in temporaryHeaderNodes {
headerNode.removeFromSupernode()
if useBackgroundDeallocation {
ASDeallocQueue.sharedDeallocation().releaseObject(inBackground: headerNode)
assertionFailure()
//ASDeallocQueue.sharedDeallocation().releaseObject(inBackground: headerNode)
} else {
ASPerformMainThreadDeallocation(headerNode)
//ASPerformMainThreadDeallocation(headerNode)
}
}
}
self.layer.add(animation, forKey: nil)
} else {
if useBackgroundDeallocation {
for itemNode in temporaryPreviousNodes {
assertionFailure()
/*for itemNode in temporaryPreviousNodes {
ASDeallocQueue.sharedDeallocation().releaseObject(inBackground: itemNode)
}
}*/
} else {
for itemNode in temporaryPreviousNodes {
ASPerformMainThreadDeallocation(itemNode)
//ASPerformMainThreadDeallocation(itemNode)
}
}
}
@ -2231,39 +2378,50 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
self.updateItemNodesVisibilities()
self.updateScroller()
self.updateScroller(transition: headerNodesTransition.0)
if let topItemOverscrollBackground = self.topItemOverscrollBackground {
headerNodesTransition.0.animatePositionAdditive(node: topItemOverscrollBackground, offset: -headerNodesTransition.2)
}
self.setNeedsAnimations()
self.updateVisibleContentOffset()
if self.debugInfo {
let delta = CACurrentMediaTime() - timestamp
//let delta = CACurrentMediaTime() - timestamp
//print("replayOperations \(delta * 1000.0) ms")
}
completion()
} else {
self.updateItemHeaders(headerNodesTransition, animateInsertion: animated || !requestItemInsertionAnimationsIndices.isEmpty)
self.updateItemHeaders(leftInset: listInsets.left, rightInset: listInsets.right, transition: headerNodesTransition, animateInsertion: animated || !requestItemInsertionAnimationsIndices.isEmpty)
self.updateItemNodesVisibilities()
if animated {
self.setNeedsAnimations()
}
self.updateScroller()
self.updateScroller(transition: headerNodesTransition.0)
if let topItemOverscrollBackground = self.topItemOverscrollBackground {
headerNodesTransition.0.animatePositionAdditive(node: topItemOverscrollBackground, offset: -headerNodesTransition.2)
}
self.updateVisibleContentOffset()
if self.debugInfo {
let delta = CACurrentMediaTime() - timestamp
//let delta = CACurrentMediaTime() - timestamp
//print("replayOperations \(delta * 1000.0) ms")
}
for (previousNode, _) in previousApparentFrames {
if previousNode.supernode == nil {
if useBackgroundDeallocation {
ASDeallocQueue.sharedDeallocation().releaseObject(inBackground: previousNode)
assertionFailure()
//ASDeallocQueue.sharedDeallocatio.releaseObject(inBackground: previousNode)
} else {
ASPerformMainThreadDeallocation(previousNode)
//ASPerformMainThreadDeallocation(previousNode)
}
}
}
@ -2303,12 +2461,12 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
node.removeFromSupernode()
node.accessoryItemNode?.removeFromSupernode()
node.accessoryItemNode = nil
node.setAccessoryItemNode(nil, leftInset: self.insets.left, rightInset: self.insets.right)
node.headerAccessoryItemNode?.removeFromSupernode()
node.headerAccessoryItemNode = nil
}
private func updateItemHeaders(_ transition: (ContainedViewLayoutTransition, Bool, CGFloat) = (.immediate, false, 0.0), animateInsertion: Bool = false) {
private func updateItemHeaders(leftInset: CGFloat, rightInset: CGFloat, transition: (ContainedViewLayoutTransition, Bool, CGFloat) = (.immediate, false, 0.0), animateInsertion: Bool = false) {
let upperDisplayBound = self.insets.top
let lowerDisplayBound = self.visibleSize.height - self.insets.bottom
var visibleHeaderNodes = Set<Int64>()
@ -2354,6 +2512,8 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
}
}
headerNode.updateLayoutInternal(size: headerFrame.size, leftInset: leftInset, rightInset: rightInset)
headerNode.updateInternalStickLocationDistanceFactor(stickLocationDistanceFactor, animated: true)
headerNode.internalStickLocationDistance = stickLocationDistance
if !hasValidNodes && !headerNode.alpha.isZero {
@ -2372,6 +2532,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
let headerNode = item.node()
headerNode.updateFlashingOnScrolling(flashing, animated: false)
headerNode.frame = headerFrame
headerNode.updateLayoutInternal(size: headerFrame.size, leftInset: leftInset, rightInset: rightInset)
headerNode.updateInternalStickLocationDistanceFactor(stickLocationDistanceFactor, animated: false)
self.itemHeaderNodes[id] = headerNode
self.addSubnode(headerNode)
@ -2432,7 +2593,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
}
private func updateAccessoryNodes(animated: Bool, currentTimestamp: Double) {
private func updateAccessoryNodes(animated: Bool, currentTimestamp: Double, leftInset: CGFloat, rightInset: CGFloat) {
var index = -1
let count = self.itemNodes.count
for itemNode in self.itemNodes {
@ -2468,18 +2629,21 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
nextAccessoryItemNode.removeFromSupernode()
itemNode.addSubnode(nextAccessoryItemNode)
itemNode.accessoryItemNode = nextAccessoryItemNode
self.itemNodes[i].accessoryItemNode = nil
itemNode.setAccessoryItemNode(nextAccessoryItemNode, leftInset: leftInset, rightInset: rightInset)
self.itemNodes[i].setAccessoryItemNode(nil, leftInset: leftInset, rightInset: rightInset)
var updatedAccessoryItemNodeOrigin = nextAccessoryItemNode.frame.origin
let updatedParentOrigin = itemNode.frame.origin
let updatedParentOrigin = itemNode.apparentFrame.origin
updatedAccessoryItemNodeOrigin.x += updatedParentOrigin.x
updatedAccessoryItemNodeOrigin.y += updatedParentOrigin.y
updatedAccessoryItemNodeOrigin.y -= itemNode.bounds.origin.y
//updatedAccessoryItemNodeOrigin.y += itemNode.transitionOffset
let deltaHeight = itemNode.frame.size.height - nextItemNode.frame.size.height
var deltaHeight = itemNode.frame.size.height - nextItemNode.frame.size.height
//deltaHeight = 0.0
nextAccessoryItemNode.animateTransitionOffset(CGPoint(x: 0.0, y: updatedAccessoryItemNodeOrigin.y - previousAccessoryItemNodeOrigin.y - deltaHeight), beginAt: currentTimestamp, duration: insertionAnimationDuration * UIView.animationDurationFactor(), curve: listViewAnimationCurveSystem)
}
} else {
break
@ -2491,12 +2655,12 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
if !didStealAccessoryNode {
let accessoryNode = accessoryItem.node()
itemNode.addSubnode(accessoryNode)
itemNode.accessoryItemNode = accessoryNode
itemNode.setAccessoryItemNode(accessoryNode, leftInset: leftInset, rightInset: rightInset)
}
}
} else {
itemNode.accessoryItemNode?.removeFromSupernode()
itemNode.accessoryItemNode = nil
itemNode.setAccessoryItemNode(nil, leftInset: leftInset, rightInset: rightInset)
}
}
@ -2597,9 +2761,10 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
if node.index == nil && node.apparentHeight <= CGFloat.ulpOfOne {
self.removeItemNodeAtIndex(i)
if useBackgroundDeallocation {
ASDeallocQueue.sharedDeallocation().releaseObject(inBackground: node)
assertionFailure()
//ASDeallocQueue.sharedDeallocation().releaseObject(inBackground: node)
} else {
ASPerformMainThreadDeallocation(node)
//ASPerformMainThreadDeallocation(node)
}
} else {
i += 1
@ -2740,6 +2905,10 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
} else {
offsetRanges.offset(IndexRange(first: index + 1, last: Int.max), offset: apparentHeightDelta)
}
if let accessoryItemNode = itemNode.accessoryItemNode {
itemNode.layoutAccessoryItemNode(accessoryItemNode, leftInset: self.insets.left, rightInset: self.insets.right)
}
}
if itemNode.index == nil && updatedApparentHeight <= CGFloat.ulpOfOne {
@ -2786,6 +2955,20 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
}
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touchesPosition = touches.first!.location(in: self.view)
if let index = self.itemIndexAtPoint(touchesPosition) {
for i in 0 ..< self.itemNodes.count {
if self.itemNodes[i].preventsTouchesToOtherItems {
if index != self.itemNodes[i].index {
self.itemNodes[i].touchesToOtherItemsPrevented()
return
}
break
}
}
}
let offset = self.visibleContentOffset()
switch offset {
case let .known(value) where value <= 10.0:
@ -2794,27 +2977,57 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
self.beganTrackingAtTopOrigin = false
}
self.touchesPosition = touches.first!.location(in: self.view)
self.touchesPosition = touchesPosition
self.selectionTouchLocation = touches.first!.location(in: self.view)
self.selectionTouchDelayTimer?.invalidate()
self.selectionLongTapDelayTimer?.invalidate()
self.selectionLongTapDelayTimer = nil
let timer = Timer(timeInterval: 0.08, target: ListViewTimerProxy { [weak self] in
if let strongSelf = self, strongSelf.selectionTouchLocation != nil {
strongSelf.clearHighlightAnimated(false)
if let index = strongSelf.itemIndexAtPoint(strongSelf.touchesPosition) {
if strongSelf.items[index].selectable {
var canBeSelectedOrLongTapped = false
for itemNode in strongSelf.itemNodes {
if itemNode.index == index && (strongSelf.items[index].selectable && itemNode.canBeSelected) || itemNode.canBeLongTapped {
canBeSelectedOrLongTapped = true
}
}
if canBeSelectedOrLongTapped {
strongSelf.highlightedItemIndex = index
for itemNode in strongSelf.itemNodes {
if itemNode.index == index && itemNode.canBeSelected {
if true { //!(itemNode.hitTest(CGPoint(x: strongSelf.touchesPosition.x - itemNode.frame.minX, y: strongSelf.touchesPosition.y - itemNode.frame.minY), with: event) is UIControl) {
if true {
if !itemNode.isLayerBacked {
strongSelf.view.bringSubview(toFront: itemNode.view)
for (_, headerNode) in strongSelf.itemHeaderNodes {
strongSelf.view.bringSubview(toFront: headerNode.view)
}
}
itemNode.setHighlighted(true, animated: false)
let itemNodeFrame = itemNode.frame
let itemNodeBounds = itemNode.bounds
if strongSelf.items[index].selectable {
itemNode.setHighlighted(true, at: strongSelf.touchesPosition.offsetBy(dx: -itemNodeFrame.minX + itemNodeBounds.minX, dy: -itemNodeFrame.minY + itemNodeBounds.minY), animated: false)
}
if itemNode.canBeLongTapped {
let timer = Timer(timeInterval: 0.3, target: ListViewTimerProxy {
if let strongSelf = self, strongSelf.highlightedItemIndex == index {
for itemNode in strongSelf.itemNodes {
if itemNode.index == index && itemNode.canBeLongTapped {
itemNode.longTapped()
strongSelf.clearHighlightAnimated(true)
strongSelf.selectionTouchLocation = nil
break
}
}
}
}, selector: #selector(ListViewTimerProxy.timerEvent), userInfo: nil, repeats: false)
strongSelf.selectionLongTapDelayTimer = timer
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
}
}
break
}
@ -2828,14 +3041,14 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
super.touchesBegan(touches, with: event)
self.updateScroller()
self.updateScroller(transition: .immediate)
}
public func clearHighlightAnimated(_ animated: Bool) {
if let highlightedItemIndex = self.highlightedItemIndex {
for itemNode in self.itemNodes {
if itemNode.index == highlightedItemIndex {
itemNode.setHighlighted(false, animated: animated)
itemNode.setHighlighted(false, at: CGPoint(), animated: animated)
break
}
}
@ -2845,7 +3058,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
private func itemIndexAtPoint(_ point: CGPoint) -> Int? {
for itemNode in self.itemNodes {
if itemNode.apparentFrame.contains(point) {
if itemNode.apparentContentFrame.contains(point) {
return itemNode.index
}
}
@ -2891,7 +3104,9 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
if distance.x * distance.x + distance.y * distance.y > maxMovementDistance * maxMovementDistance {
self.selectionTouchLocation = nil
self.selectionTouchDelayTimer?.invalidate()
self.selectionLongTapDelayTimer?.invalidate()
self.selectionTouchDelayTimer = nil
self.selectionLongTapDelayTimer = nil
self.clearHighlightAnimated(false)
}
}
@ -2918,7 +3133,8 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
self.view.bringSubview(toFront: headerNode.view)
}
}
itemNode.setHighlighted(true, animated: false)
let itemNodeFrame = itemNode.frame
itemNode.setHighlighted(true, at: selectionTouchLocation.offsetBy(dx: -itemNodeFrame.minX, dy: -itemNodeFrame.minY), animated: false)
} else {
self.highlightedItemIndex = nil
}
@ -2941,6 +3157,8 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
self.selectionTouchLocation = nil
self.selectionTouchDelayTimer?.invalidate()
self.selectionTouchDelayTimer = nil
self.selectionLongTapDelayTimer?.invalidate()
self.selectionLongTapDelayTimer = nil
self.clearHighlightAnimated(false)
super.touchesCancelled(touches, with: event)

View File

@ -1,5 +1,8 @@
import Foundation
#if os(macOS)
#else
import AsyncDisplayKit
#endif
open class ListViewAccessoryItemNode: ASDisplayNode {
var transitionOffset: CGPoint = CGPoint() {
@ -37,4 +40,13 @@ open class ListViewAccessoryItemNode: ASDisplayNode {
return false
}
override open func layout() {
super.layout()
self.updateLayout(size: self.bounds.size, leftInset: 0.0, rightInset: 0.0)
}
open func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) {
}
}

View File

@ -91,6 +91,7 @@ public let listViewAnimationCurveLinear: (CGFloat) -> CGFloat = { t in
return t
}
#if os(iOS)
public func listViewAnimationCurveFromAnimationOptions(animationOptions: UIViewAnimationOptions) -> (CGFloat) -> CGFloat {
if animationOptions.rawValue == UInt(7 << 16) {
return listViewAnimationCurveSystem
@ -98,6 +99,7 @@ public func listViewAnimationCurveFromAnimationOptions(animationOptions: UIViewA
return listViewAnimationCurveLinear
}
}
#endif
public final class ListViewAnimation {
let from: Interpolatable

View File

@ -0,0 +1,8 @@
import Foundation
import AsyncDisplayKit
open class ListViewFloatingHeaderNode: ASDisplayNode {
open func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
return 0.0
}
}

View File

@ -1,5 +1,9 @@
import Foundation
#if os(macOS)
import SwiftSignalKitMac
#else
import SwiftSignalKit
#endif
public enum ListViewCenterScrollPositionOverflow {
case top

View File

@ -1,5 +1,9 @@
import Foundation
#if os(macOS)
import SwiftSignalKitMac
#else
import SwiftSignalKit
#endif
public enum ListViewItemUpdateAnimation {
case None
@ -29,8 +33,8 @@ public struct ListViewItemConfigureNodeFlags: OptionSet {
}
public protocol ListViewItem {
func nodeConfiguredForWidth(async: @escaping (@escaping () -> Void) -> Void, width: CGFloat, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void)
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: ListViewItemNode, width: CGFloat, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping () -> Void) -> Void)
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, () -> Void)) -> Void)
func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping () -> Void) -> Void)
var accessoryItem: ListViewAccessoryItem? { get }
var headerAccessoryItem: ListViewAccessoryItem? { get }

View File

@ -1,5 +1,7 @@
import Foundation
#if !os(macOS)
import AsyncDisplayKit
#endif
public enum ListViewItemHeaderStickDirection {
case top
@ -33,21 +35,6 @@ open class ListViewItemHeaderNode: ASDisplayNode {
self.isFlashingOnScrolling = isFlashingOnScrolling
self.updateFlashingOnScrolling(isFlashingOnScrolling, animated: animated)
}
/*if self.isFlashing {
if self.alpha.isZero {
self.alpha = 1.0
if animated {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
}
} else {
if !self.alpha.isZero {
self.alpha = 0.0
if animated {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
}
}
}*/
}
open func updateFlashingOnScrolling(_ isFlashingOnScrolling: Bool, animated: Bool) {
@ -139,4 +126,24 @@ open class ListViewItemHeaderNode: ASDisplayNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false)
self.layer.animateScale(from: 1.0, to: 0.2, duration: duration, removeOnCompletion: false)
}
private var cachedLayout: (CGSize, CGFloat, CGFloat)?
func updateLayoutInternal(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) {
var update = false
if let cachedLayout = self.cachedLayout {
if cachedLayout.0 != size || cachedLayout.1 != leftInset || cachedLayout.2 != rightInset {
update = true
}
} else {
update = true
}
if update {
self.cachedLayout = (size, leftInset, rightInset)
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset)
}
}
open func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) {
}
}

View File

@ -1,6 +1,10 @@
import Foundation
#if os(macOS)
import SwiftSignalKitMac
#else
import AsyncDisplayKit
import SwiftSignalKit
#endif
var testSpringFrictionLimits: (CGFloat, CGFloat) = (3.0, 60.0)
var testSpringFriction: CGFloat = 31.8211269378662
@ -27,12 +31,6 @@ struct ListViewItemSpring {
}
}
private class ListViewItemView: UIView {
/*override class var layerClass: AnyClass {
return ASTransformLayer.self
}*/
}
public struct ListViewItemNodeLayout {
public let contentSize: CGSize
public let insets: UIEdgeInsets
@ -58,15 +56,28 @@ public enum ListViewItemNodeVisibility {
case visible
}
public struct ListViewItemLayoutParams {
public let width: CGFloat
public let leftInset: CGFloat
public let rightInset: CGFloat
public init(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat) {
self.width = width
self.leftInset = leftInset
self.rightInset = rightInset
}
}
open class ListViewItemNode: ASDisplayNode {
let rotated: Bool
final var index: Int?
public final var accessoryItemNode: ListViewAccessoryItemNode? {
didSet {
if let accessoryItemNode = self.accessoryItemNode {
self.layoutAccessoryItemNode(accessoryItemNode)
}
public private(set) var accessoryItemNode: ListViewAccessoryItemNode?
func setAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode?, leftInset: CGFloat, rightInset: CGFloat) {
self.accessoryItemNode = accessoryItemNode
if let accessoryItemNode = accessoryItemNode {
self.layoutAccessoryItemNode(accessoryItemNode, leftInset: leftInset, rightInset: rightInset)
}
}
@ -95,18 +106,27 @@ open class ListViewItemNode: ASDisplayNode {
return true
}
open var canBeLongTapped: Bool {
return false
}
open var preventsTouchesToOtherItems: Bool {
return false
}
open func touchesToOtherItemsPrevented() {
}
open func longTapped() {
}
public final var insets: UIEdgeInsets = UIEdgeInsets() {
didSet {
let effectiveInsets = self.insets
self.frame = CGRect(origin: self.frame.origin, size: CGSize(width: self.contentSize.width, height: self.contentSize.height + effectiveInsets.top + effectiveInsets.bottom))
let bounds = self.bounds
self.bounds = CGRect(origin: CGPoint(x: bounds.origin.x, y: -effectiveInsets.top + self.contentOffset + self.transitionOffset), size: bounds.size)
if oldValue != self.insets {
if let accessoryItemNode = self.accessoryItemNode {
self.layoutAccessoryItemNode(accessoryItemNode)
}
}
}
}
@ -167,22 +187,6 @@ open class ListViewItemNode: ASDisplayNode {
self.rotated = rotated
//super.init()
//self.layerBacked = layerBacked
/*if layerBacked {
super.init(layerBlock: {
return ASTransformLayer()
})
} else {
super.init()
self.setViewBlock({
return ListViewItemView()
})
}*/
if seeThrough {
if (layerBacked) {
super.init()
@ -203,12 +207,6 @@ open class ListViewItemNode: ASDisplayNode {
}
}
/*deinit {
if Thread.isMainThread {
print("deallocating on main thread")
}
}*/
var apparentHeight: CGFloat = 0.0
private var _bounds: CGRect = CGRect()
private var _position: CGPoint = CGPoint()
@ -226,9 +224,6 @@ open class ListViewItemNode: ASDisplayNode {
self._contentSize = CGSize(width: value.size.width, height: value.size.height - effectiveInsets.top - effectiveInsets.bottom)
if previousSize != value.size {
if let accessoryItemNode = self.accessoryItemNode {
self.layoutAccessoryItemNode(accessoryItemNode)
}
if let headerAccessoryItemNode = self.headerAccessoryItemNode {
self.layoutHeaderAccessoryItemNode(headerAccessoryItemNode)
}
@ -248,9 +243,6 @@ open class ListViewItemNode: ASDisplayNode {
self._contentSize = CGSize(width: value.size.width, height: value.size.height - effectiveInsets.top - effectiveInsets.bottom)
if previousSize != value.size {
if let accessoryItemNode = self.accessoryItemNode {
self.layoutAccessoryItemNode(accessoryItemNode)
}
if let headerAccessoryItemNode = self.headerAccessoryItemNode {
self.layoutHeaderAccessoryItemNode(headerAccessoryItemNode)
}
@ -279,13 +271,21 @@ open class ListViewItemNode: ASDisplayNode {
return frame
}
public final var apparentContentFrame: CGRect {
var frame = self.frame
let insets = self.insets
frame.origin.y += insets.top
frame.size.height = self.apparentHeight - insets.top - insets.bottom
return frame
}
public final var apparentBounds: CGRect {
var bounds = self.bounds
bounds.size.height = self.apparentHeight
return bounds
}
open func layoutAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode) {
open func layoutAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode, leftInset: CGFloat, rightInset: CGFloat) {
}
open func layoutHeaderAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode) {
@ -371,7 +371,7 @@ open class ListViewItemNode: ASDisplayNode {
return continueAnimations
}
open func layoutForWidth(_ width: CGFloat, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
open func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
}
public func animationForKey(_ key: String) -> ListViewAnimation? {
@ -417,6 +417,19 @@ open class ListViewItemNode: ASDisplayNode {
self.setAnimationForKey("insets", animation: animation)
}
public func addHeightAnimation(_ value: CGFloat, duration: Double, beginAt: Double, update: ((CGFloat, CGFloat) -> Void)? = nil) {
let animation = ListViewAnimation(from: self.bounds.height, to: value, duration: duration, curve: listViewAnimationCurveSystem, beginAt: beginAt, update: { [weak self] progress, currentValue in
if let strongSelf = self {
let frame = strongSelf.frame
strongSelf.frame = CGRect(origin: frame.origin, size: CGSize(width: frame.width, height: currentValue))
if let update = update {
update(progress, currentValue)
}
}
})
self.setAnimationForKey("height", animation: animation)
}
public func addApparentHeightAnimation(_ value: CGFloat, duration: Double, beginAt: Double, update: ((CGFloat, CGFloat) -> Void)? = nil) {
let animation = ListViewAnimation(from: self.apparentHeight, to: value, duration: duration, curve: listViewAnimationCurveSystem, beginAt: beginAt, update: { [weak self] progress, currentValue in
if let strongSelf = self {
@ -468,7 +481,7 @@ open class ListViewItemNode: ASDisplayNode {
open func animateRemoved(_ currentTimestamp: Double, duration: Double) {
}
open func setHighlighted(_ highlighted: Bool, animated: Bool) {
open func setHighlighted(_ highlighted: Bool, at point: CGPoint, animated: Bool) {
}
open func animateFrameTransition(_ progress: CGFloat, _ currentValue: CGFloat) {

View File

@ -0,0 +1,31 @@
import Foundation
#if os(macOS)
#else
import AsyncDisplayKit
#endif
final class ListViewOverscrollBackgroundNode: ASDisplayNode {
private let backgroundNode: ASDisplayNode
var color: UIColor {
didSet {
self.backgroundNode.backgroundColor = color
}
}
init(color: UIColor) {
self.color = color
self.backgroundNode = ASDisplayNode()
self.backgroundNode.backgroundColor = color
self.backgroundNode.isLayerBacked = true
super.init()
self.addSubnode(self.backgroundNode)
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
}
}

View File

@ -1,4 +1,7 @@
#if os(macOS)
#else
import UIKit
#endif
class ListViewScroller: UIScrollView, UIGestureRecognizerDelegate {
override init(frame: CGRect) {

View File

@ -1,5 +0,0 @@
import Foundation
import AppKit
class ListViewScroller: CALayer {
}

View File

@ -1,9 +0,0 @@
import UIKit
protocol MergeableLayoutEvent {
}
final class MergedLayoutEvents {
}

View File

@ -36,6 +36,16 @@ private class WindowRootViewController: UIViewController {
}
}
var preferNavigationUIHidden: Bool = false {
didSet {
if oldValue != self.preferNavigationUIHidden {
if #available(iOSApplicationExtension 11.0, *) {
self.setNeedsUpdateOfHomeIndicatorAutoHidden()
}
}
}
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .default
}
@ -51,6 +61,10 @@ private class WindowRootViewController: UIViewController {
override func preferredScreenEdgesDeferringSystemGestures() -> UIRectEdge {
return self.gestureEdges
}
override func prefersHomeIndicatorAutoHidden() -> Bool {
return self.preferNavigationUIHidden
}
}
private final class NativeWindow: UIWindow, WindowHost {
@ -62,6 +76,8 @@ private final class NativeWindow: UIWindow, WindowHost {
var hitTestImpl: ((CGPoint, UIEvent?) -> UIView?)?
var presentNativeImpl: ((UIViewController) -> Void)?
var invalidateDeferScreenEdgeGestureImpl: (() -> Void)?
var invalidatePreferNavigationUIHiddenImpl: (() -> Void)?
var cancelInteractiveKeyboardGesturesImpl: (() -> Void)?
private var frameTransition: ContainedViewLayoutTransition?
@ -98,6 +114,20 @@ private final class NativeWindow: UIWindow, WindowHost {
}
}
override init(frame: CGRect) {
super.init(frame: frame)
if let gestureRecognizers = self.gestureRecognizers {
for recognizer in gestureRecognizers {
recognizer.delaysTouchesBegan = false
}
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
@ -147,6 +177,14 @@ private final class NativeWindow: UIWindow, WindowHost {
func invalidateDeferScreenEdgeGestures() {
self.invalidateDeferScreenEdgeGestureImpl?()
}
func invalidatePreferNavigationUIHidden() {
self.invalidatePreferNavigationUIHiddenImpl?()
}
func cancelInteractiveKeyboardGestures() {
self.cancelInteractiveKeyboardGesturesImpl?()
}
}
public func nativeWindowHostView() -> WindowHostView {
@ -164,6 +202,8 @@ public func nativeWindowHostView() -> WindowHostView {
rootViewController.orientations = orientations
}, updateDeferScreenEdgeGestures: { edges in
rootViewController.gestureEdges = edges
}, updatePreferNavigationUIHidden: { value in
rootViewController.preferNavigationUIHidden = value
})
window.updateSize = { [weak hostView] size in
@ -198,6 +238,14 @@ public func nativeWindowHostView() -> WindowHostView {
return hostView?.invalidateDeferScreenEdgeGesture?()
}
window.invalidatePreferNavigationUIHiddenImpl = { [weak hostView] in
return hostView?.invalidatePreferNavigationUIHidden?()
}
window.cancelInteractiveKeyboardGesturesImpl = { [weak hostView] in
hostView?.cancelInteractiveKeyboardGestures?()
}
rootViewController.presentController = { [weak hostView] controller, level, animated, completion in
if let strongSelf = hostView {
strongSelf.present?(LegacyPresentedController(legacyController: controller, presentation: .custom), level)

View File

@ -61,6 +61,9 @@ private func backArrowImage(color: UIColor) -> UIImage? {
open class NavigationBar: ASDisplayNode {
private var theme: NavigationBarTheme
private var validLayout: (CGSize, CGFloat, CGFloat)?
private var requestedLayout: Bool = false
var backPressed: () -> () = { }
private var collapsed: Bool {
@ -156,7 +159,7 @@ open class NavigationBar: ASDisplayNode {
strongSelf.updateLeftButton(animated: animated)
strongSelf.invalidateCalculatedLayout()
strongSelf.setNeedsLayout()
strongSelf.requestLayout()
}
}
@ -177,7 +180,7 @@ open class NavigationBar: ASDisplayNode {
strongSelf.updateRightButton(animated: animated)
strongSelf.invalidateCalculatedLayout()
strongSelf.setNeedsLayout()
strongSelf.requestLayout()
}
}
@ -196,6 +199,7 @@ open class NavigationBar: ASDisplayNode {
self.updateRightButton(animated: false)
}
self.invalidateCalculatedLayout()
self.requestLayout()
}
}
@ -211,7 +215,7 @@ open class NavigationBar: ASDisplayNode {
}
self.invalidateCalculatedLayout()
self.setNeedsLayout()
self.requestLayout()
}
}
@ -226,7 +230,7 @@ open class NavigationBar: ASDisplayNode {
}
self.invalidateCalculatedLayout()
self.setNeedsLayout()
self.requestLayout()
}
}
@ -261,6 +265,7 @@ open class NavigationBar: ASDisplayNode {
strongSelf.backButtonNode.text = previousItem.title ?? ""
}
strongSelf.invalidateCalculatedLayout()
strongSelf.requestLayout()
}
}
@ -272,12 +277,14 @@ open class NavigationBar: ASDisplayNode {
strongSelf.backButtonNode.text = previousItem.title ?? ""
}
strongSelf.invalidateCalculatedLayout()
strongSelf.requestLayout()
}
}
}
self.updateLeftButton(animated: false)
self.invalidateCalculatedLayout()
self.requestLayout()
}
}
@ -288,13 +295,13 @@ open class NavigationBar: ASDisplayNode {
self.badgeNode.isHidden = actualText.isEmpty
self.invalidateCalculatedLayout()
self.setNeedsLayout()
self.requestLayout()
}
}
private func updateLeftButton(animated: Bool) {
if let item = self.item {
if let leftBarButtonItem = item.leftBarButtonItem {
if let leftBarButtonItem = item.leftBarButtonItem, !leftBarButtonItem.backButtonAppearance {
if animated {
if self.leftButtonNode.view.superview != nil {
if let snapshotView = self.leftButtonNode.view.snapshotContentTree() {
@ -365,13 +372,19 @@ open class NavigationBar: ASDisplayNode {
}
self.leftButtonNode.removeFromSupernode()
if let previousItem = self.previousItem {
var backTitle: String?
if let leftBarButtonItem = item.leftBarButtonItem, leftBarButtonItem.backButtonAppearance {
backTitle = leftBarButtonItem.title
} else if let previousItem = self.previousItem {
if let backBarButtonItem = previousItem.backBarButtonItem {
self.backButtonNode.text = backBarButtonItem.title ?? "Back"
backTitle = backBarButtonItem.title ?? "Back"
} else {
self.backButtonNode.text = previousItem.title ?? "Back"
backTitle = previousItem.title ?? "Back"
}
}
if let backTitle = backTitle {
self.backButtonNode.text = backTitle
if self.backButtonNode.supernode == nil {
self.clippingNode.addSubnode(self.backButtonNode)
self.clippingNode.addSubnode(self.backButtonArrow)
@ -500,6 +513,7 @@ open class NavigationBar: ASDisplayNode {
}
}
self.requestedLayout = true
self.layout()
}
}
@ -557,7 +571,13 @@ open class NavigationBar: ASDisplayNode {
}
}
self.backButtonNode.pressed = { [weak self] in
self?.backPressed()
if let strongSelf = self {
if let leftBarButtonItem = strongSelf.item?.leftBarButtonItem, leftBarButtonItem.backButtonAppearance {
leftBarButtonItem.performActionOnTarget()
} else {
strongSelf.backPressed()
}
}
}
self.leftButtonNode.pressed = { [weak self] in
@ -592,25 +612,41 @@ open class NavigationBar: ASDisplayNode {
}
}
open override func layout() {
let size = self.bounds.size
private func requestLayout() {
self.requestedLayout = true
self.setNeedsLayout()
}
let leftButtonInset: CGFloat = 16.0
let backButtonInset: CGFloat = 27.0
override open func layout() {
super.layout()
self.clippingNode.frame = CGRect(origin: CGPoint(), size: size)
self.contentNode?.frame = CGRect(origin: CGPoint(), size: size)
if let validLayout = self.validLayout, self.requestedLayout {
self.requestedLayout = false
self.updateLayout(size: validLayout.0, leftInset: validLayout.1, rightInset: validLayout.2, transition: .immediate)
}
}
self.stripeNode.frame = CGRect(x: 0.0, y: size.height, width: size.width, height: UIScreenPixel)
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, leftInset, rightInset)
let leftButtonInset: CGFloat = leftInset + 16.0
let backButtonInset: CGFloat = leftInset + 27.0
transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: size))
if let contentNode = self.contentNode {
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: size.width - leftInset - rightInset, height: size.height)))
}
transition.updateFrame(node: self.stripeNode, frame: CGRect(x: 0.0, y: size.height, width: size.width, height: UIScreenPixel))
let nominalHeight: CGFloat = self.collapsed ? 32.0 : 44.0
let contentVerticalOrigin = size.height - nominalHeight
var leftTitleInset: CGFloat = 8.0
var rightTitleInset: CGFloat = 8.0
var leftTitleInset: CGFloat = leftInset + 4.0
var rightTitleInset: CGFloat = rightInset + 4.0
if self.backButtonNode.supernode != nil {
let backButtonSize = self.backButtonNode.measure(CGSize(width: size.width, height: nominalHeight))
leftTitleInset += backButtonSize.width + backButtonInset + 8.0 + 8.0
leftTitleInset += backButtonSize.width + backButtonInset + 4.0 + 4.0
let topHitTestSlop = (nominalHeight - backButtonSize.height) * 0.5
self.backButtonNode.hitTestSlop = UIEdgeInsetsMake(-topHitTestSlop, -27.0, -topHitTestSlop, -8.0)
@ -636,21 +672,21 @@ open class NavigationBar: ASDisplayNode {
transitionTitleNode.alpha = progress * progress
}
self.backButtonArrow.frame = CGRect(origin: CGPoint(x: 8.0 - progress * size.width, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0))
self.backButtonArrow.frame = CGRect(origin: CGPoint(x: leftInset + 8.0 - progress * size.width, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0))
self.backButtonArrow.alpha = max(0.0, 1.0 - progress * 1.3)
self.badgeNode.alpha = max(0.0, 1.0 - progress * 1.3)
case .bottom:
self.backButtonNode.alpha = 1.0
self.backButtonNode.frame = CGRect(origin: CGPoint(x: backButtonInset, y: contentVerticalOrigin + floor((nominalHeight - backButtonSize.height) / 2.0)), size: backButtonSize)
self.backButtonArrow.alpha = 1.0
self.backButtonArrow.frame = CGRect(origin: CGPoint(x: 8.0, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0))
self.backButtonArrow.frame = CGRect(origin: CGPoint(x: leftInset + 8.0, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0))
self.badgeNode.alpha = 1.0
}
} else {
self.backButtonNode.alpha = 1.0
self.backButtonNode.frame = CGRect(origin: CGPoint(x: backButtonInset, y: contentVerticalOrigin + floor((nominalHeight - backButtonSize.height) / 2.0)), size: backButtonSize)
self.backButtonArrow.alpha = 1.0
self.backButtonArrow.frame = CGRect(origin: CGPoint(x: 8.0, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0))
self.backButtonArrow.frame = CGRect(origin: CGPoint(x: leftInset + 8.0, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0))
self.badgeNode.alpha = 1.0
}
} else if self.leftButtonNode.supernode != nil {
@ -689,8 +725,8 @@ open class NavigationBar: ASDisplayNode {
}
if let transitionBackArrowNode = self.transitionBackArrowNode {
let initialX: CGFloat = 8.0 + size.width * 0.3
let finalX: CGFloat = 8.0
let initialX: CGFloat = leftInset + 8.0 + size.width * 0.3
let finalX: CGFloat = leftInset + 8.0
transitionBackArrowNode.frame = CGRect(origin: CGPoint(x: initialX * (1.0 - progress) + finalX * progress, y: contentVerticalOrigin + floor((nominalHeight - 22.0) / 2.0)), size: CGSize(width: 13.0, height: 22.0))
transitionBackArrowNode.alpha = max(0.0, 1.0 - progress * 1.3)
@ -739,7 +775,7 @@ open class NavigationBar: ASDisplayNode {
}
if let titleView = self.titleView {
let titleSize = CGSize(width: max(1.0, size.width - leftTitleInset - leftTitleInset), height: nominalHeight)
let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight)
titleView.frame = CGRect(origin: CGPoint(x: leftTitleInset, y: contentVerticalOrigin), size: titleSize)
if let transitionState = self.transitionState, let otherNavigationBar = transitionState.navigationBar {
@ -859,9 +895,10 @@ open class NavigationBar: ASDisplayNode {
}
if !self.bounds.size.width.isZero {
self.requestedLayout = true
self.layout()
} else {
self.setNeedsLayout()
self.requestLayout()
}
} else if self.clippingNode.alpha.isZero {
self.clippingNode.alpha = 1.0

View File

@ -73,7 +73,7 @@ open class NavigationController: UINavigationController, ContainableController,
self.loadView()
}
self.containerLayout = layout
self.view.frame = CGRect(origin: self.view.frame.origin, size: layout.size)
transition.updateFrame(view: self.view, frame: CGRect(origin: self.view.frame.origin, size: layout.size))
let containedLayout = ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging)
@ -81,7 +81,7 @@ open class NavigationController: UINavigationController, ContainableController,
if let topViewController = topViewController as? ContainableController {
topViewController.containerLayoutUpdated(containedLayout, transition: transition)
} else {
topViewController.view.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
transition.updateFrame(view: topViewController.view, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height))
}
}
@ -89,7 +89,7 @@ open class NavigationController: UINavigationController, ContainableController,
if let presentedViewController = presentedViewController as? ContainableController {
presentedViewController.containerLayoutUpdated(containedLayout, transition: transition)
} else {
presentedViewController.view.frame = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
transition.updateFrame(view: presentedViewController.view, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height))
}
}
@ -109,6 +109,7 @@ open class NavigationController: UINavigationController, ContainableController,
let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
panRecognizer.delegate = self
panRecognizer.delaysTouchesBegan = false
panRecognizer.cancelsTouchesInView = true
self.view.addGestureRecognizer(panRecognizer)
@ -349,9 +350,9 @@ open class NavigationController: UINavigationController, ContainableController,
}
}
bottomController.viewWillDisappear(true)
bottomController.viewWillAppear(true)
let bottomView = bottomController.view!
topController.viewWillAppear(true)
topController.viewWillDisappear(true)
let topView = topController.view!
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.view, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar)

View File

@ -120,7 +120,7 @@ public final class StatusBar: ASDisplayNode {
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
}
func updateState(statusBar: UIView?, inCallText: String?, animated: Bool) {
func updateState(statusBar: UIView?, withSafeInsets: Bool, inCallText: String?, animated: Bool) {
if let statusBar = statusBar {
self.removeProxyNodeScheduled = false
let resolvedStyle: StatusBarStyle
@ -166,7 +166,9 @@ public final class StatusBar: ASDisplayNode {
if (resolvedInCallText != nil) != (self.inCallText != nil) {
if let _ = resolvedInCallText {
if !withSafeInsets {
self.addSubnode(self.inCallLabel)
}
addInCallAnimation(self.inCallLabel.layer)
self.inCallBackgroundNode.layer.backgroundColor = inCallBackgroundColor.cgColor

View File

@ -79,13 +79,13 @@ class StatusBarManager {
self.host = host
}
func updateState(surfaces: [StatusBarSurface], forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool, animated: Bool) {
func updateState(surfaces: [StatusBarSurface], withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool, animated: Bool) {
let previousSurfaces = self.surfaces
self.surfaces = surfaces
self.updateSurfaces(previousSurfaces, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: animated)
self.updateSurfaces(previousSurfaces, withSafeInsets: withSafeInsets, forceInCallStatusBarText: forceInCallStatusBarText, forceHiddenBySystemWindows: forceHiddenBySystemWindows, animated: animated)
}
private func updateSurfaces(_ previousSurfaces: [StatusBarSurface], forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool, animated: Bool) {
private func updateSurfaces(_ previousSurfaces: [StatusBarSurface], withSafeInsets: Bool, forceInCallStatusBarText: String?, forceHiddenBySystemWindows: Bool, animated: Bool) {
let statusBarFrame = self.host.statusBarFrame
guard let statusBarView = self.host.statusBarView else {
return
@ -197,7 +197,7 @@ class StatusBarManager {
for surface in previousSurfaces {
for statusBar in surface.statusBars {
if !visibleStatusBars.contains(where: {$0 === statusBar}) {
statusBar.updateState(statusBar: nil, inCallText: forceInCallStatusBarText, animated: animated)
statusBar.updateState(statusBar: nil, withSafeInsets: withSafeInsets, inCallText: forceInCallStatusBarText, animated: animated)
}
}
}
@ -206,13 +206,13 @@ class StatusBarManager {
for statusBar in surface.statusBars {
statusBar.inCallNavigate = self.inCallNavigate
if !visibleStatusBars.contains(where: {$0 === statusBar}) {
statusBar.updateState(statusBar: nil, inCallText: forceInCallStatusBarText, animated: animated)
statusBar.updateState(statusBar: nil, withSafeInsets: withSafeInsets, inCallText: forceInCallStatusBarText, animated: animated)
}
}
}
for statusBar in visibleStatusBars {
statusBar.updateState(statusBar: statusBarView, inCallText: forceInCallStatusBarText, animated: animated)
statusBar.updateState(statusBar: statusBarView, withSafeInsets: withSafeInsets, inCallText: forceInCallStatusBarText, animated: animated)
}
if let globalStatusBar = globalStatusBar, !forceHiddenBySystemWindows {

View File

@ -1,73 +0,0 @@
import UIKit
final class SystemContainedControllerTransitionCoordinator: NSObject, UIViewControllerTransitionCoordinator {
public var isAnimated: Bool {
return false
}
public var presentationStyle: UIModalPresentationStyle {
return .fullScreen
}
public var initiallyInteractive: Bool {
return false
}
public let isInterruptible: Bool = false
public var isInteractive: Bool {
return false
}
public var isCancelled: Bool {
return false
}
public var transitionDuration: TimeInterval {
return 0.6
}
public var percentComplete: CGFloat {
return 0.0
}
public var completionVelocity: CGFloat {
return 0.0
}
public var completionCurve: UIViewAnimationCurve {
return .easeInOut
}
public func viewController(forKey key: UITransitionContextViewControllerKey) -> UIViewController? {
return nil
}
public func view(forKey key: UITransitionContextViewKey) -> UIView? {
return nil
}
public var containerView: UIView {
return UIView()
}
public var targetTransform: CGAffineTransform {
return CGAffineTransform.identity
}
public func animate(alongsideTransition animation: ((UIViewControllerTransitionCoordinatorContext) -> Swift.Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Swift.Void)? = nil) -> Bool {
return false
}
public func animateAlongsideTransition(in view: UIView?, animation: ((UIViewControllerTransitionCoordinatorContext) -> Swift.Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Swift.Void)? = nil) -> Bool {
return false
}
public func notifyWhenInteractionEnds(_ handler: @escaping (UIViewControllerTransitionCoordinatorContext) -> ()) {
}
public func notifyWhenInteractionChanges(_ handler: @escaping (UIViewControllerTransitionCoordinatorContext) -> ()) {
}
}

View File

@ -36,11 +36,16 @@ final class TabBarControllerNode: ASDisplayNode {
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
let update = {
let tabBarHeight = 49.0 + layout.insets(options: []).bottom
self.tabBarNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - tabBarHeight), size: CGSize(width: layout.size.width, height: tabBarHeight))
if self.tabBarNode.isNodeLoaded {
self.tabBarNode.layout()
let tabBarHeight: CGFloat
let bottomInset: CGFloat = layout.insets(options: []).bottom
if !layout.safeInsets.left.isZero {
tabBarHeight = 34.0 + bottomInset
} else {
tabBarHeight = 49.0 + bottomInset
}
transition.updateFrame(node: self.tabBarNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - tabBarHeight), size: CGSize(width: layout.size.width, height: tabBarHeight)))
self.tabBarNode.updateLayout(size: layout.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: bottomInset, transition: transition)
}
switch transition {

View File

@ -3,18 +3,30 @@ import UIKit
import AsyncDisplayKit
private let separatorHeight: CGFloat = 1.0 / UIScreen.main.scale
private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor: UIColor, tintColor: UIColor) -> UIImage? {
let font = Font.medium(10.0)
private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor: UIColor, tintColor: UIColor, horizontal: Bool) -> UIImage? {
let font = horizontal ? Font.regular(13.0) : Font.medium(10.0)
let titleSize = (title as NSString).boundingRect(with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin], attributes: [NSAttributedStringKey.font: font], context: nil).size
let imageSize: CGSize
if let image = image {
if horizontal {
let factor: CGFloat = 0.8
imageSize = CGSize(width: floor(image.size.width * factor), height: floor(image.size.height * factor))
} else {
imageSize = image.size
}
} else {
imageSize = CGSize()
}
let size = CGSize(width: max(ceil(titleSize.width), imageSize.width), height: 45.0)
let horizontalSpacing: CGFloat = 4.0
let size: CGSize
if horizontal {
size = CGSize(width: ceil(titleSize.width) + horizontalSpacing + imageSize.width, height: 34.0)
} else {
size = CGSize(width: max(ceil(titleSize.width), imageSize.width), height: 45.0)
}
UIGraphicsBeginImageContextWithOptions(size, true, 0.0)
if let context = UIGraphicsGetCurrentContext() {
@ -22,6 +34,17 @@ private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor:
context.fill(CGRect(origin: CGPoint(), size: size))
if let image = image {
if horizontal {
let imageRect = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)
context.saveGState()
context.translateBy(x: imageRect.midX, y: imageRect.midY)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -imageRect.midX, y: -imageRect.midY)
context.clip(to: imageRect, mask: image.cgImage!)
context.setFillColor(tintColor.cgColor)
context.fill(imageRect)
context.restoreGState()
} else {
let imageRect = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - imageSize.width) / 2.0), y: 1.0), size: imageSize)
context.saveGState()
context.translateBy(x: imageRect.midX, y: imageRect.midY)
@ -33,8 +56,13 @@ private func tabBarItemImage(_ image: UIImage?, title: String, backgroundColor:
context.restoreGState()
}
}
}
if horizontal {
(title as NSString).draw(at: CGPoint(x: imageSize.width + horizontalSpacing, y: floor((size.height - titleSize.height) / 2.0) - 2.0), withAttributes: [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: tintColor])
} else {
(title as NSString).draw(at: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: size.height - titleSize.height - 2.0), withAttributes: [NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: tintColor])
}
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
@ -52,6 +80,7 @@ private final class TabBarNodeContainer {
let updateSelectedImageListenerIndex: Int
let imageNode: ASImageNode
let badgeContainerNode: ASDisplayNode
let badgeBackgroundNode: ASImageNode
let badgeTextNode: ASTextNode
@ -72,6 +101,9 @@ private final class TabBarNodeContainer {
self.imageNode = imageNode
self.badgeContainerNode = ASDisplayNode()
self.badgeContainerNode.isLayerBacked = true
self.badgeBackgroundNode = ASImageNode()
self.badgeBackgroundNode.isLayerBacked = true
self.badgeBackgroundNode.displayWithoutProcessing = true
@ -82,6 +114,9 @@ private final class TabBarNodeContainer {
self.badgeTextNode.isLayerBacked = true
self.badgeTextNode.displaysAsynchronously = false
self.badgeContainerNode.addSubnode(self.badgeBackgroundNode)
self.badgeContainerNode.addSubnode(self.badgeTextNode)
self.badgeValue = item.badgeValue ?? ""
self.updateBadgeListenerIndex = UITabBarItem_addSetBadgeListener(item, { value in
updateBadge(value ?? "")
@ -122,11 +157,11 @@ class TabBarNode: ASDisplayNode {
didSet {
if self.selectedIndex != oldValue {
if let oldValue = oldValue {
self.updateNodeImage(oldValue)
self.updateNodeImage(oldValue, layout: true)
}
if let selectedIndex = self.selectedIndex {
self.updateNodeImage(selectedIndex)
self.updateNodeImage(selectedIndex, layout: true)
}
}
}
@ -135,6 +170,8 @@ class TabBarNode: ASDisplayNode {
private let itemSelected: (Int) -> Void
private var theme: TabBarControllerTheme
private var validLayout: (CGSize, CGFloat, CGFloat, CGFloat)?
private var horizontal: Bool = false
private var badgeImage: UIImage
@ -175,7 +212,7 @@ class TabBarNode: ASDisplayNode {
}
for i in 0 ..< self.tabBarItems.count {
self.updateNodeImage(i)
self.updateNodeImage(i, layout: false)
self.tabBarNodeContainers[i].badgeBackgroundNode.image = self.badgeImage
}
@ -185,8 +222,7 @@ class TabBarNode: ASDisplayNode {
private func reloadTabBarItems() {
for node in self.tabBarNodeContainers {
node.imageNode.removeFromSupernode()
node.badgeBackgroundNode.removeFromSupernode()
node.badgeTextNode.removeFromSupernode()
node.badgeContainerNode.removeFromSupernode()
}
var tabBarNodeContainers: [TabBarNodeContainer] = []
@ -199,16 +235,16 @@ class TabBarNode: ASDisplayNode {
let container = TabBarNodeContainer(item: item, imageNode: node, updateBadge: { [weak self] value in
self?.updateNodeBadge(i, value: value)
}, updateTitle: { [weak self] _, _ in
self?.updateNodeImage(i)
self?.updateNodeImage(i, layout: true)
}, updateImage: { [weak self] _ in
self?.updateNodeImage(i)
self?.updateNodeImage(i, layout: true)
}, updateSelectedImage: { [weak self] _ in
self?.updateNodeImage(i)
self?.updateNodeImage(i, layout: true)
})
if let selectedIndex = self.selectedIndex, selectedIndex == i {
node.image = tabBarItemImage(item.selectedImage, title: item.title ?? "", backgroundColor: self.theme.tabBarBackgroundColor, tintColor: self.theme.tabBarSelectedTextColor)
node.image = tabBarItemImage(item.selectedImage, title: item.title ?? "", backgroundColor: self.theme.tabBarBackgroundColor, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal)
} else {
node.image = tabBarItemImage(item.image, title: item.title ?? "", backgroundColor: self.theme.tabBarBackgroundColor, tintColor: self.theme.tabBarTextColor)
node.image = tabBarItemImage(item.image, title: item.title ?? "", backgroundColor: self.theme.tabBarBackgroundColor, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal)
}
container.badgeBackgroundNode.image = self.badgeImage
tabBarNodeContainers.append(container)
@ -216,8 +252,7 @@ class TabBarNode: ASDisplayNode {
}
for container in tabBarNodeContainers {
self.addSubnode(container.badgeBackgroundNode)
self.addSubnode(container.badgeTextNode)
self.addSubnode(container.badgeContainerNode)
}
self.tabBarNodeContainers = tabBarNodeContainers
@ -225,19 +260,21 @@ class TabBarNode: ASDisplayNode {
self.setNeedsLayout()
}
private func updateNodeImage(_ index: Int) {
private func updateNodeImage(_ index: Int, layout: Bool) {
if index < self.tabBarNodeContainers.count && index < self.tabBarItems.count {
let node = self.tabBarNodeContainers[index].imageNode
let item = self.tabBarItems[index]
let previousImage = node.image
if let selectedIndex = self.selectedIndex, selectedIndex == index {
node.image = tabBarItemImage(item.selectedImage, title: item.title ?? "", backgroundColor: self.theme.tabBarBackgroundColor, tintColor: self.theme.tabBarSelectedTextColor)
node.image = tabBarItemImage(item.selectedImage, title: item.title ?? "", backgroundColor: self.theme.tabBarBackgroundColor, tintColor: self.theme.tabBarSelectedTextColor, horizontal: self.horizontal)
} else {
node.image = tabBarItemImage(item.image, title: item.title ?? "", backgroundColor: self.theme.tabBarBackgroundColor, tintColor: self.theme.tabBarTextColor)
node.image = tabBarItemImage(item.image, title: item.title ?? "", backgroundColor: self.theme.tabBarBackgroundColor, tintColor: self.theme.tabBarTextColor, horizontal: self.horizontal)
}
if previousImage?.size != node.image?.size {
self.layout()
if let validLayout = self.validLayout, layout {
self.updateLayout(size: validLayout.0, leftInset: validLayout.1, rightInset: validLayout.2, bottomInset: validLayout.3, transition: .immediate)
}
}
}
}
@ -245,23 +282,33 @@ class TabBarNode: ASDisplayNode {
private func updateNodeBadge(_ index: Int, value: String) {
self.tabBarNodeContainers[index].badgeValue = value
if self.tabBarNodeContainers[index].badgeValue != self.tabBarNodeContainers[index].appliedBadgeValue {
self.layout()
if let validLayout = self.validLayout {
self.updateLayout(size: validLayout.0, leftInset: validLayout.1, rightInset: validLayout.2, bottomInset: validLayout.3, transition: .immediate)
}
}
}
private func updateNodeTitle(_ index: Int, value: String) {
self.tabBarNodeContainers[index].titleValue = value
if self.tabBarNodeContainers[index].titleValue != self.tabBarNodeContainers[index].appliedTitleValue {
self.layout()
if let validLayout = self.validLayout {
self.updateLayout(size: validLayout.0, leftInset: validLayout.1, rightInset: validLayout.2, bottomInset: validLayout.3, transition: .immediate)
}
}
}
override func layout() {
super.layout()
func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, leftInset, rightInset, bottomInset)
let size = self.bounds.size
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -separatorHeight), size: CGSize(width: size.width, height: separatorHeight)))
self.separatorNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -separatorHeight), size: CGSize(width: size.width, height: separatorHeight))
let horizontal = !leftInset.isZero
if self.horizontal != horizontal {
self.horizontal = horizontal
for i in 0 ..< self.tabBarItems.count {
self.updateNodeImage(i, layout: false)
}
}
if self.tabBarNodeContainers.count != 0 {
let distanceBetweenNodes = size.width / CGFloat(self.tabBarNodeContainers.count)
@ -275,26 +322,33 @@ class TabBarNode: ASDisplayNode {
let nodeSize = node.image?.size ?? CGSize()
let originX = floor(leftNodeOriginX + CGFloat(i) * distanceBetweenNodes - nodeSize.width / 2.0)
node.frame = CGRect(origin: CGPoint(x: originX, y: 4.0), size: nodeSize)
transition.updateFrame(node: node, frame: CGRect(origin: CGPoint(x: originX, y: 4.0), size: nodeSize))
if container.badgeValue != container.appliedBadgeValue {
container.appliedBadgeValue = container.badgeValue
if let badgeValue = container.badgeValue, !badgeValue.isEmpty {
container.badgeTextNode.attributedText = NSAttributedString(string: badgeValue, font: badgeFont, textColor: self.theme.tabBarBadgeTextColor)
container.badgeBackgroundNode.isHidden = false
container.badgeTextNode.isHidden = false
container.badgeContainerNode.isHidden = false
} else {
container.badgeBackgroundNode.isHidden = true
container.badgeTextNode.isHidden = true
container.badgeContainerNode.isHidden = true
}
}
if !container.badgeBackgroundNode.isHidden {
if !container.badgeContainerNode.isHidden {
let badgeSize = container.badgeTextNode.measure(CGSize(width: 200.0, height: 100.0))
let backgroundSize = CGSize(width: max(18.0, badgeSize.width + 10.0 + 1.0), height: 18.0)
let backgroundFrame = CGRect(origin: CGPoint(x: floor(originX + node.frame.width / 2.0) - 3.0 + node.frame.width - backgroundSize.width - 1.0, y: 2.0), size: backgroundSize)
container.badgeBackgroundNode.frame = backgroundFrame
container.badgeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(backgroundFrame.midX - badgeSize.width / 2.0), y: 3.0), size: badgeSize)
let backgroundFrame: CGRect
if horizontal {
backgroundFrame = CGRect(origin: CGPoint(x: originX, y: 2.0), size: backgroundSize)
} else {
backgroundFrame = CGRect(origin: CGPoint(x: floor(originX + node.frame.width / 2.0) - 3.0 + node.frame.width - backgroundSize.width - 1.0, y: 2.0), size: backgroundSize)
}
transition.updateFrame(node: container.badgeContainerNode, frame: backgroundFrame)
container.badgeBackgroundNode.frame = CGRect(origin: CGPoint(), size: backgroundFrame.size)
let scaleFactor: CGFloat = horizontal ? 0.8 : 1.0
container.badgeContainerNode.subnodeTransform = CATransform3DMakeScale(scaleFactor, scaleFactor, 1.0)
container.badgeTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundFrame.size.width - badgeSize.width) / 2.0), y: 1.0), size: badgeSize)
}
}
}
@ -303,8 +357,11 @@ class TabBarNode: ASDisplayNode {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
if let touch = touches.first {
if let touch = touches.first, let bottomInset = self.validLayout?.3 {
let location = touch.location(in: self.view)
if location.y > self.bounds.size.height - bottomInset {
return
}
var closestNode: (Int, CGFloat)?
for i in 0 ..< self.tabBarNodeContainers.count {

View File

@ -4,6 +4,7 @@ import AsyncDisplayKit
public enum TextAlertActionType {
case genericAction
case defaultAction
case destructiveAction
}
public struct TextAlertAction {
@ -34,7 +35,15 @@ private final class TextAlertContentActionNode: HighlightableButtonNode {
super.init()
self.titleNode.maximumNumberOfLines = 2
self.setAttributedTitle(NSAttributedString(string: action.title, font: Font.regular(17.0), textColor: UIColor(rgb: 0x007ee5), paragraphAlignment: .center), for: [])
let font = Font.regular(17.0)
var color = UIColor(rgb: 0x007ee5)
switch action.type {
case .defaultAction, .genericAction:
break
case .destructiveAction:
color = UIColor(rgb: 0xff3b30)
}
self.setAttributedTitle(NSAttributedString(string: action.title, font: font, textColor: color, paragraphAlignment: .center), for: [])
self.highligthedChanged = { [weak self] value in
if let strongSelf = self {
@ -154,7 +163,8 @@ final class TextAlertContentNode: AlertContentNode {
let resultSize: CGSize
if let titleNode = titleNode, let titleSize = titleSize {
let contentWidth = max(max(titleSize.width, textSize.width), minActionsWidth)
var contentWidth = max(max(titleSize.width, textSize.width), minActionsWidth)
contentWidth = max(contentWidth, 150.0)
let spacing: CGFloat = 6.0
let titleFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - titleSize.width) / 2.0), y: insets.top), size: titleSize)
@ -165,10 +175,13 @@ final class TextAlertContentNode: AlertContentNode {
resultSize = CGSize(width: contentWidth + insets.left + insets.right, height: titleSize.height + spacing + textSize.height + actionsHeight + insets.top + insets.bottom)
} else {
let textFrame = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: textSize)
var contentWidth = max(textSize.width, minActionsWidth)
contentWidth = max(contentWidth, 150.0)
let textFrame = CGRect(origin: CGPoint(x: insets.left + floor((contentWidth - textSize.width) / 2.0), y: insets.top), size: textSize)
transition.updateFrame(node: self.textNode, frame: textFrame)
resultSize = CGSize(width: textSize.width + insets.left + insets.right, height: textSize.height + actionsHeight + insets.top + insets.bottom)
resultSize = CGSize(width: contentWidth + insets.left + insets.right, height: textSize.height + actionsHeight + insets.top + insets.bottom)
}
self.actionNodesSeparator.frame = CGRect(origin: CGPoint(x: 0.0, y: resultSize.height - actionsHeight - UIScreenPixel), size: CGSize(width: resultSize.width, height: UIScreenPixel))

View File

@ -1,5 +1,7 @@
#import "UIKitUtils.h"
#import <objc/runtime.h>
#if TARGET_IPHONE_SIMULATOR
UIKIT_EXTERN float UIAnimationDragCoefficient(); // UIKit private drag coeffient, use judiciously
#endif
@ -19,14 +21,35 @@ UIKIT_EXTERN float UIAnimationDragCoefficient(); // UIKit private drag coeffient
@interface CASpringAnimation ()
- (float)_solveForInput:(float)arg1;
@end
@implementation CASpringAnimation (AnimationUtils)
- (CGFloat)valueAt:(CGFloat)t {
return [self _solveForInput:t];
static dispatch_once_t onceToken;
static float (*impl)(id, float) = NULL;
static double (*dimpl)(id, double) = NULL;
dispatch_once(&onceToken, ^{
Method method = class_getInstanceMethod([CASpringAnimation class], NSSelectorFromString([@"_" stringByAppendingString:@"solveForInput:"]));
if (method) {
const char *encoding = method_getTypeEncoding(method);
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:encoding];
const char *argType = [signature getArgumentTypeAtIndex:2];
if (strncmp(argType, "f", 1) == 0) {
impl = (float (*)(id, float))method_getImplementation(method);
} else if (strncmp(argType, "d", 1) == 0) {
dimpl = (double (*)(id, double))method_getImplementation(method);
}
}
});
if (impl) {
float result = impl(self, (float)t);
return (CGFloat)result;
} else if (dimpl) {
double result = dimpl(self, (double)t);
return (CGFloat)result;
}
return t;
}
@end
@ -62,5 +85,5 @@ CABasicAnimation * _Nonnull makeSpringBounceAnimation(NSString * _Nonnull keyPat
}
CGFloat springAnimationValueAt(CABasicAnimation * _Nonnull animation, CGFloat t) {
return [(CASpringAnimation *)animation _solveForInput:t];
return [(CASpringAnimation *)animation valueAt:t];
}

View File

@ -148,6 +148,7 @@ private func makeSubtreeSnapshot(layer: CALayer) -> UIView? {
let subtree = makeSubtreeSnapshot(layer: sublayer)
if let subtree = subtree {
subtree.frame = sublayer.frame
subtree.bounds = sublayer.bounds
view.addSubview(subtree)
} else {
return nil
@ -157,6 +158,32 @@ private func makeSubtreeSnapshot(layer: CALayer) -> UIView? {
return view
}
private func makeLayerSubtreeSnapshot(layer: CALayer) -> CALayer? {
let view = CALayer()
//view.layer.isHidden = layer.isHidden
view.opacity = layer.opacity
view.contents = layer.contents
view.contentsRect = layer.contentsRect
view.contentsScale = layer.contentsScale
view.contentsCenter = layer.contentsCenter
view.contentsGravity = layer.contentsGravity
view.masksToBounds = layer.masksToBounds
view.cornerRadius = layer.cornerRadius
if let sublayers = layer.sublayers {
for sublayer in sublayers {
let subtree = makeLayerSubtreeSnapshot(layer: sublayer)
if let subtree = subtree {
subtree.frame = sublayer.frame
subtree.bounds = sublayer.bounds
layer.addSublayer(subtree)
} else {
return nil
}
}
}
return view
}
public extension UIView {
public func snapshotContentTree() -> UIView? {
if let snapshot = makeSubtreeSnapshot(layer: self.layer) {
@ -168,6 +195,17 @@ public extension UIView {
}
}
public extension CALayer {
public func snapshotContentTree() -> CALayer? {
if let snapshot = makeLayerSubtreeSnapshot(layer: self) {
snapshot.frame = self.frame
return snapshot
} else {
return nil
}
}
}
public extension CGRect {
public var topLeft: CGPoint {
return self.origin

View File

@ -1,5 +1,10 @@
#import <UIKit/UIKit.h>
typedef NS_OPTIONS(NSUInteger, UIResponderDisableAutomaticKeyboardHandling) {
UIResponderDisableAutomaticKeyboardHandlingForward = 1 << 0,
UIResponderDisableAutomaticKeyboardHandlingBackward = 1 << 1
};
@interface UIViewController (Navigation)
- (void)setIgnoreAppearanceMethodInvocations:(BOOL)ignoreAppearanceMethodInvocations;
@ -14,7 +19,7 @@
@interface UIView (Navigation)
@property (nonatomic) bool disablesInteractiveTransitionGestureRecognizer;
@property (nonatomic) bool disablesAutomaticKeyboardHandling;
@property (nonatomic) UIResponderDisableAutomaticKeyboardHandling disableAutomaticKeyboardHandling;
- (void)input_setInputAccessoryHeightProvider:(CGFloat (^_Nullable)())block;
- (CGFloat)input_getInputAccessoryHeight;

View File

@ -35,7 +35,7 @@ static const void *UIViewControllerNavigationControllerKey = &UIViewControllerNa
static const void *UIViewControllerPresentingControllerKey = &UIViewControllerPresentingControllerKey;
static const void *UIViewControllerPresentingProxyControllerKey = &UIViewControllerPresentingProxyControllerKey;
static const void *disablesInteractiveTransitionGestureRecognizerKey = &disablesInteractiveTransitionGestureRecognizerKey;
static const void *disablesAutomaticKeyboardHandlingKey = &disablesAutomaticKeyboardHandlingKey;
static const void *disableAutomaticKeyboardHandlingKey = &disableAutomaticKeyboardHandlingKey;
static const void *setNeedsStatusBarAppearanceUpdateKey = &setNeedsStatusBarAppearanceUpdateKey;
static const void *inputAccessoryHeightProviderKey = &inputAccessoryHeightProviderKey;
@ -218,12 +218,12 @@ static bool notyfyingShiftState = false;
[self setAssociatedObject:@(disablesInteractiveTransitionGestureRecognizer) forKey:disablesInteractiveTransitionGestureRecognizerKey];
}
- (bool)disablesAutomaticKeyboardHandling {
return [[self associatedObjectForKey:disablesAutomaticKeyboardHandlingKey] boolValue];
- (UIResponderDisableAutomaticKeyboardHandling)disableAutomaticKeyboardHandling {
return (UIResponderDisableAutomaticKeyboardHandling)[[self associatedObjectForKey:disableAutomaticKeyboardHandlingKey] unsignedIntegerValue];
}
- (void)setDisablesAutomaticKeyboardHandling:(bool)disablesAutomaticKeyboardHandling {
[self setAssociatedObject:@(disablesAutomaticKeyboardHandling) forKey:disablesAutomaticKeyboardHandlingKey];
- (void)setDisableAutomaticKeyboardHandling:(UIResponderDisableAutomaticKeyboardHandling)disableAutomaticKeyboardHandling {
[self setAssociatedObject:@(disableAutomaticKeyboardHandling) forKey:disableAutomaticKeyboardHandlingKey];
}
- (void)input_setInputAccessoryHeightProvider:(CGFloat (^_Nullable)())block {

View File

@ -46,8 +46,16 @@ open class ViewControllerPresentationArguments {
}
}
override open func preferredScreenEdgesDeferringSystemGestures() -> UIRectEdge {
return .bottom
public final var preferNavigationUIHidden: Bool = false {
didSet {
if self.preferNavigationUIHidden != oldValue {
self.window?.invalidatePreferNavigationUIHidden()
}
}
}
override open func prefersHomeIndicatorAutoHidden() -> Bool {
return self.preferNavigationUIHidden
}
public private(set) var presentationArguments: Any?
@ -196,6 +204,7 @@ open class ViewControllerPresentationArguments {
if let navigationBar = self.navigationBar {
transition.updateFrame(node: navigationBar, frame: navigationBarFrame)
navigationBar.updateLayout(size: navigationBarFrame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, transition: transition)
}
self.presentationContext.containerLayoutUpdated(layout, transition: transition)

View File

@ -1,5 +1,6 @@
import Foundation
import AsyncDisplayKit
import SwiftSignalKit
private class WindowRootViewController: UIViewController {
var presentController: ((UIViewController, Bool, (() -> Void)?) -> Void)?
@ -88,10 +89,10 @@ private struct UpdatingLayout {
}
}
mutating func update(size: CGSize, metrics: LayoutMetrics, forceInCallStatusBarText: String?, transition: ContainedViewLayoutTransition, overrideTransition: Bool) {
mutating func update(size: CGSize, metrics: LayoutMetrics, safeInsets: UIEdgeInsets, forceInCallStatusBarText: String?, transition: ContainedViewLayoutTransition, overrideTransition: Bool) {
self.update(transition: transition, override: overrideTransition)
self.layout = WindowLayout(size: size, metrics: metrics, statusBarHeight: self.layout.statusBarHeight, forceInCallStatusBarText: forceInCallStatusBarText, inputHeight: self.layout.inputHeight, safeInsets: self.layout.safeInsets, onScreenNavigationHeight: self.layout.onScreenNavigationHeight, upperKeyboardInputPositionBound: self.layout.upperKeyboardInputPositionBound)
self.layout = WindowLayout(size: size, metrics: metrics, statusBarHeight: self.layout.statusBarHeight, forceInCallStatusBarText: forceInCallStatusBarText, inputHeight: self.layout.inputHeight, safeInsets: safeInsets, onScreenNavigationHeight: self.layout.onScreenNavigationHeight, upperKeyboardInputPositionBound: self.layout.upperKeyboardInputPositionBound)
}
@ -146,7 +147,7 @@ private func containedLayoutForWindowLayout(_ layout: WindowLayout) -> Container
let resolvedStatusBarHeight: CGFloat?
if let statusBarHeight = layout.statusBarHeight {
if layout.forceInCallStatusBarText != nil {
resolvedStatusBarHeight = 40.0
resolvedStatusBarHeight = max(40.0, layout.safeInsets.top)
} else {
resolvedStatusBarHeight = statusBarHeight
}
@ -250,6 +251,7 @@ public final class WindowHostView {
let updateSupportedInterfaceOrientations: (UIInterfaceOrientationMask) -> Void
let updateDeferScreenEdgeGestures: (UIRectEdge) -> Void
let updatePreferNavigationUIHidden: (Bool) -> Void
var present: ((ViewController, PresentationSurfaceLevel) -> Void)?
var presentNative: ((UIViewController) -> Void)?
@ -259,12 +261,15 @@ public final class WindowHostView {
var isUpdatingOrientationLayout = false
var hitTest: ((CGPoint, UIEvent?) -> UIView?)?
var invalidateDeferScreenEdgeGesture: (() -> Void)?
var invalidatePreferNavigationUIHidden: (() -> Void)?
var cancelInteractiveKeyboardGestures: (() -> Void)?
init(view: UIView, isRotating: @escaping () -> Bool, updateSupportedInterfaceOrientations: @escaping (UIInterfaceOrientationMask) -> Void, updateDeferScreenEdgeGestures: @escaping (UIRectEdge) -> Void) {
init(view: UIView, isRotating: @escaping () -> Bool, updateSupportedInterfaceOrientations: @escaping (UIInterfaceOrientationMask) -> Void, updateDeferScreenEdgeGestures: @escaping (UIRectEdge) -> Void, updatePreferNavigationUIHidden: @escaping (Bool) -> Void) {
self.view = view
self.isRotating = isRotating
self.updateSupportedInterfaceOrientations = updateSupportedInterfaceOrientations
self.updateDeferScreenEdgeGestures = updateDeferScreenEdgeGestures
self.updatePreferNavigationUIHidden = updatePreferNavigationUIHidden
}
}
@ -276,12 +281,25 @@ public struct WindowTracingTags {
public protocol WindowHost {
func present(_ controller: ViewController, on level: PresentationSurfaceLevel)
func invalidateDeferScreenEdgeGestures()
func invalidatePreferNavigationUIHidden()
func cancelInteractiveKeyboardGestures()
}
private func layoutMetricsForScreenSize(_ size: CGSize) -> LayoutMetrics {
return LayoutMetrics(widthClass: .compact, heightClass: .compact)
}
private func safeInsetsForScreenSize(_ size: CGSize) -> UIEdgeInsets {
if (size.width.isEqual(to: 375.0) && size.height.isEqual(to: 812.0)) || size.height.isEqual(to: 375.0) && size.width.isEqual(to: 812.0) {
if size.width.isEqual(to: 375.0) {
return UIEdgeInsets(top: 44.0, left: 0.0, bottom: 0.0, right: 0.0)
} else {
return UIEdgeInsets(top: 0.0, left: 44.0, bottom: 0.0, right: 44.0)
}
}
return UIEdgeInsets()
}
private final class KeyboardGestureRecognizerDelegate: NSObject, UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
@ -300,6 +318,7 @@ public class Window1 {
private let keyboardManager: KeyboardManager?
private var statusBarChangeObserver: AnyObject?
private var keyboardFrameChangeObserver: AnyObject?
private var keyboardTypeChangeObserver: AnyObject?
private var windowLayout: WindowLayout
private var updatingLayout: UpdatingLayout?
@ -312,6 +331,7 @@ public class Window1 {
private var tracingStatusBarsInvalidated = false
private var shouldUpdateDeferScreenEdgeGestures = false
private var shouldInvalidatePreferNavigationUIHidden = false
private var statusBarHidden = false
@ -329,6 +349,8 @@ public class Window1 {
private var keyboardGestureBeginLocation: CGPoint?
private var keyboardGestureAccessoryHeight: CGFloat?
private var keyboardTypeChangeTimer: SwiftSignalKit.Timer?
public init(hostView: WindowHostView, statusBarHost: StatusBarHost?) {
self.hostView = hostView
@ -348,12 +370,10 @@ public class Window1 {
var onScreenNavigationHeight: CGFloat?
if (boundsSize.width.isEqual(to: 375.0) && boundsSize.height.isEqual(to: 812.0)) || boundsSize.height.isEqual(to: 375.0) && boundsSize.width.isEqual(to: 812.0) {
onScreenNavigationHeight = 20.0
onScreenNavigationHeight = 34.0
}
let safeInsets = UIEdgeInsets()
self.windowLayout = WindowLayout(size: boundsSize, metrics: layoutMetricsForScreenSize(self.hostView.view.bounds.size), statusBarHeight: statusBarHeight, forceInCallStatusBarText: self.forceInCallStatusBarText, inputHeight: 0.0, safeInsets: safeInsets, onScreenNavigationHeight: onScreenNavigationHeight, upperKeyboardInputPositionBound: nil)
self.windowLayout = WindowLayout(size: boundsSize, metrics: layoutMetricsForScreenSize(boundsSize), statusBarHeight: statusBarHeight, forceInCallStatusBarText: self.forceInCallStatusBarText, inputHeight: 0.0, safeInsets: safeInsetsForScreenSize(boundsSize), onScreenNavigationHeight: onScreenNavigationHeight, upperKeyboardInputPositionBound: nil)
self.presentationContext = PresentationContext()
self.hostView.present = { [weak self] controller, level in
@ -388,6 +408,14 @@ public class Window1 {
self?.invalidateDeferScreenEdgeGestures()
}
self.hostView.invalidatePreferNavigationUIHidden = { [weak self] in
self?.invalidatePreferNavigationUIHidden()
}
self.hostView.cancelInteractiveKeyboardGestures = { [weak self] in
self?.cancelInteractiveKeyboardGestures()
}
self.presentationContext.view = self.hostView.view
self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: .immediate)
@ -421,6 +449,34 @@ public class Window1 {
}
})
if #available(iOSApplicationExtension 11.0, *) {
self.keyboardTypeChangeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextInputCurrentInputModeDidChange, object: nil, queue: nil, using: { [weak self] notification in
if let strongSelf = self, let initialInputHeight = strongSelf.windowLayout.inputHeight, let firstResponder = getFirstResponderAndAccessoryHeight(strongSelf.hostView.view).0 {
if firstResponder.textInputMode?.primaryLanguage != nil {
return
}
strongSelf.keyboardTypeChangeTimer?.invalidate()
let timer = SwiftSignalKit.Timer(timeout: 0.1, repeat: false, completion: {
if let strongSelf = self, let firstResponder = getFirstResponderAndAccessoryHeight(strongSelf.hostView.view).0 {
if firstResponder.textInputMode?.primaryLanguage != nil {
return
}
if let keyboardManager = strongSelf.keyboardManager {
let updatedKeyboardHeight = keyboardManager.getCurrentKeyboardHeight()
if !updatedKeyboardHeight.isEqual(to: initialInputHeight) {
strongSelf.updateLayout({ $0.update(inputHeight: updatedKeyboardHeight, transition: .immediate, overrideTransition: false) })
}
}
}
}, queue: Queue.mainQueue())
strongSelf.keyboardTypeChangeTimer = timer
timer.start()
}
})
}
let recognizer = WindowPanRecognizer(target: self, action: #selector(self.panGesture(_:)))
recognizer.cancelsTouchesInView = false
recognizer.delaysTouchesBegan = false
@ -449,6 +505,9 @@ public class Window1 {
if let keyboardFrameChangeObserver = self.keyboardFrameChangeObserver {
NotificationCenter.default.removeObserver(keyboardFrameChangeObserver)
}
if let keyboardTypeChangeObserver = self.keyboardTypeChangeObserver {
NotificationCenter.default.removeObserver(keyboardTypeChangeObserver)
}
}
public func setForceInCallStatusBar(_ forceInCallStatusBarText: String?, transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)) {
@ -471,6 +530,23 @@ public class Window1 {
self.hostView.view.setNeedsLayout()
}
public func invalidatePreferNavigationUIHidden() {
self.shouldInvalidatePreferNavigationUIHidden = true
self.hostView.view.setNeedsLayout()
}
public func cancelInteractiveKeyboardGestures() {
if self.windowLayout.upperKeyboardInputPositionBound != nil {
self.updateLayout {
$0.update(upperKeyboardInputPositionBound: nil, transition: .animated(duration: 0.25, curve: .spring), overrideTransition: false)
}
}
if self.keyboardGestureBeginLocation != nil {
self.keyboardGestureBeginLocation = nil
}
}
public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
for view in self.hostView.view.subviews.reversed() {
if NSStringFromClass(type(of: view)) == "UITransitionView" {
@ -499,7 +575,7 @@ public class Window1 {
} else {
transition = .immediate
}
self.updateLayout { $0.update(size: value, metrics: layoutMetricsForScreenSize(value), forceInCallStatusBarText: self.forceInCallStatusBarText, transition: transition, overrideTransition: true) }
self.updateLayout { $0.update(size: value, metrics: layoutMetricsForScreenSize(value), safeInsets: safeInsetsForScreenSize(value), forceInCallStatusBarText: self.forceInCallStatusBarText, transition: transition, overrideTransition: true) }
}
private var _rootController: ContainableController?
@ -563,7 +639,7 @@ public class Window1 {
self.tracingStatusBarsInvalidated = false
if self.statusBarHidden {
statusBarManager.updateState(surfaces: [], forceInCallStatusBarText: nil, forceHiddenBySystemWindows: false, animated: false)
statusBarManager.updateState(surfaces: [], withSafeInsets: false, forceInCallStatusBarText: nil, forceHiddenBySystemWindows: false, animated: false)
} else {
var statusBarSurfaces: [StatusBarSurface] = []
for layers in self.hostView.view.layer.traceableLayerSurfaces(withTag: WindowTracingTags.statusBar) {
@ -584,7 +660,7 @@ public class Window1 {
}
}
self.cachedWindowSubviewCount = self.hostView.view.window?.subviews.count ?? 0
statusBarManager.updateState(surfaces: statusBarSurfaces, forceInCallStatusBarText: self.forceInCallStatusBarText, forceHiddenBySystemWindows: hasPreview, animated: animatedUpdate)
statusBarManager.updateState(surfaces: statusBarSurfaces, withSafeInsets: !self.windowLayout.safeInsets.top.isZero, forceInCallStatusBarText: self.forceInCallStatusBarText, forceHiddenBySystemWindows: hasPreview, animated: animatedUpdate)
}
var keyboardSurfaces: [KeyboardSurface] = []
@ -599,12 +675,16 @@ public class Window1 {
self.hostView.updateSupportedInterfaceOrientations(self.presentationContext.combinedSupportedOrientations())
self.hostView.updateDeferScreenEdgeGestures(self.collectScreenEdgeGestures())
self.hostView.updatePreferNavigationUIHidden(self.collectPreferNavigationUIHidden())
self.shouldUpdateDeferScreenEdgeGestures = false
} else if self.shouldUpdateDeferScreenEdgeGestures {
self.shouldInvalidatePreferNavigationUIHidden = false
} else if self.shouldUpdateDeferScreenEdgeGestures || self.shouldInvalidatePreferNavigationUIHidden {
self.shouldUpdateDeferScreenEdgeGestures = false
self.shouldInvalidatePreferNavigationUIHidden = false
self.hostView.updateDeferScreenEdgeGestures(self.collectScreenEdgeGestures())
self.hostView.updatePreferNavigationUIHidden(self.collectPreferNavigationUIHidden())
}
if !UIWindow.isDeviceRotating() {
@ -716,6 +796,10 @@ public class Window1 {
}
private func panGestureBegan(location: CGPoint) {
if self.windowLayout.upperKeyboardInputPositionBound != nil {
return
}
let keyboardGestureBeginLocation = location
let view = self.hostView.view
let (firstResponder, accessoryHeight) = getFirstResponderAndAccessoryHeight(view)
@ -746,9 +830,23 @@ public class Window1 {
}
private func panGestureEnded(location: CGPoint, velocity: CGPoint?) {
if self.keyboardGestureBeginLocation == nil {
return
}
self.keyboardGestureBeginLocation = nil
let currentLocation = location
if let velocity = velocity, let inputHeight = self.windowLayout.inputHeight, velocity.y > 100.0 && currentLocation.y + (self.keyboardGestureAccessoryHeight ?? 0.0) > self.windowLayout.size.height - inputHeight {
let accessoryHeight = (self.keyboardGestureAccessoryHeight ?? 0.0)
var canDismiss = false
if let upperKeyboardInputPositionBound = self.windowLayout.upperKeyboardInputPositionBound, upperKeyboardInputPositionBound >= self.windowLayout.size.height - accessoryHeight {
canDismiss = true
} else if let velocity = velocity, velocity.y > 100.0 {
canDismiss = true
}
if canDismiss, let inputHeight = self.windowLayout.inputHeight, currentLocation.y + (self.keyboardGestureAccessoryHeight ?? 0.0) > self.windowLayout.size.height - inputHeight {
self.updateLayout {
$0.update(upperKeyboardInputPositionBound: self.windowLayout.size.height, transition: .animated(duration: 0.25, curve: .spring), overrideTransition: false)
}
@ -786,6 +884,10 @@ public class Window1 {
return edges
}
private func collectPreferNavigationUIHidden() -> Bool {
return false
}
public func forEachViewController(_ f: (ViewController) -> Bool) {
for controller in self.presentationContext.controllers {
if !f(controller) {

View File

@ -0,0 +1,84 @@
import Foundation
open class ASDisplayNode: NSObject {
var layer: CALayer {
preconditionFailure()
}
var view: UIView {
preconditionFailure()
}
open var frame: CGRect {
get {
return self.layer.frame
} set(value) {
self.layer.frame = value
}
}
open var bounds: CGRect {
get {
return self.layer.bounds
} set(value) {
self.layer.bounds = value
}
}
open var position: CGPoint {
get {
return self.layer.position
} set(value) {
self.layer.position = value
}
}
var alpha: CGFloat {
get {
return CGFloat(self.layer.opacity)
} set(value) {
self.layer.opacity = Float(value)
}
}
var backgroundColor: UIColor? {
get {
if let backgroundColor = self.layer.backgroundColor {
return UIColor(cgColor: backgroundColor)
} else {
return nil
}
} set(value) {
self.layer.backgroundColor = value?.cgColor
}
}
var isLayerBacked: Bool = false
override init() {
super.init()
}
func setLayerBlock(_ f: @escaping () -> CALayer) {
}
func setViewBlock(_ f: @escaping () -> UIView) {
}
open func layout() {
}
open func addSubnode(_ subnode: ASDisplayNode) {
}
open func insertSubnode(belowSubnode: ASDisplayNode) {
}
open func insertSubnode(aboveSubnode: ASDisplayNode) {
}
}

View File

@ -0,0 +1,12 @@
import Foundation
import Cocoa
extension NSValue {
convenience init(cgRect: CGRect) {
self.init(rect: NSRect(origin: cgRect.origin, size: cgRect.size))
}
convenience init(cgPoint: CGPoint) {
self.init(point: NSPoint(x: cgPoint.x, y: cgPoint.y))
}
}

View File

@ -0,0 +1,47 @@
import Foundation
public enum UIGestureRecognizerState : Int {
case possible
case began
case changed
case ended
case cancelled
case failed
}
open class UIGestureRecognizer: NSObject {
public init(target: Any?, action: Selector?) {
super.init()
}
open var state: UIGestureRecognizerState = .possible {
didSet {
}
}
weak open var delegate: UIGestureRecognizerDelegate?
open var isEnabled: Bool = true
open var view: UIView? {
return nil
}
open var cancelsTouchesInView: Bool = true
open var delaysTouchesBegan: Bool = false
open var delaysTouchesEnded: Bool = true
open func location(in view: UIView?) -> CGPoint {
return CGPoint()
}
open var numberOfTouches: Int {
return 0
}
}
@objc public protocol UIGestureRecognizerDelegate : NSObjectProtocol {
@objc optional func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool
@objc optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool
@objc optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
}

71
DisplayMac/UIKit.swift Normal file
View File

@ -0,0 +1,71 @@
import Foundation
import QuartzCore
public struct UIEdgeInsets: Equatable {
public let top: CGFloat
public let left: CGFloat
public let bottom: CGFloat
public let right: CGFloat
public init() {
self.top = 0.0
self.left = 0.0
self.bottom = 0.0
self.right = 0.0
}
public init(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) {
self.top = top
self.left = left
self.bottom = bottom
self.right = right
}
public static func ==(lhs: UIEdgeInsets, rhs: UIEdgeInsets) -> Bool {
if !lhs.top.isEqual(to: rhs.top) {
return false
}
if !lhs.left.isEqual(to: rhs.left) {
return false
}
if !lhs.bottom.isEqual(to: rhs.bottom) {
return false
}
if !lhs.right.isEqual(to: rhs.right) {
return false
}
return true
}
}
public final class UIColor: NSObject {
let cgColor: CGColor
init(rgb: Int32) {
preconditionFailure()
}
init(cgColor: CGColor) {
self.cgColor = cgColor
}
}
open class CASeeThroughTracingLayer: CALayer {
}
open class CASeeThroughTracingView: UIView {
}
func makeSpringAnimation(_ keyPath: String) -> CABasicAnimation {
return CABasicAnimation(keyPath: keyPath)
}
func makeSpringBounceAnimation(_ keyPath: String, _ initialVelocity: CGFloat, _ damping: CGFloat) -> CABasicAnimation {
return CABasicAnimation(keyPath: keyPath)
}
func springAnimationValueAt(_ animation: CABasicAnimation, _ t: CGFloat) -> CGFloat {
return t
}

View File

@ -0,0 +1,24 @@
import Foundation
import QuartzCore
public protocol UIScrollViewDelegate {
}
open class UIScrollView: UIView {
public var contentOffset: CGPoint {
get {
return self.bounds.origin
} set(value) {
self.bounds.origin = value
}
}
public var contentSize: CGSize = CGSize() {
didSet {
}
}
public var alwaysBoundsVertical: Bool = false
public var alwaysBoundsHorizontal: Bool = false
}

View File

@ -0,0 +1,5 @@
import Foundation
final class UISlider: UIView {
}

5
DisplayMac/UITouch.swift Normal file
View File

@ -0,0 +1,5 @@
import Foundation
public final class UITouch: NSObject {
}

49
DisplayMac/UIView.swift Normal file
View File

@ -0,0 +1,49 @@
import Foundation
import QuartzCore
open class UIView: NSObject {
public let layer: CALayer
open var frame: CGRect {
get {
return self.layer.frame
} set(value) {
self.layer.frame = value
}
}
open var bounds: CGRect {
get {
return self.layer.bounds
} set(value) {
self.layer.bounds = value
}
}
open var center: CGPoint {
get {
return self.layer.position
} set(value) {
self.layer.position = value
}
}
init(frame: CGRect) {
self.layer = CALayer()
self.layer.frame = frame
super.init()
}
convenience override init() {
self.init(frame: CGRect())
}
static func animationDurationFactor() -> Double {
return 1.0
}
public func bringSubview(toFront: UIView) {
}
}