mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-10 00:01:44 +00:00
no message
This commit is contained in:
parent
dc9cec5a0e
commit
147dfe39ca
@ -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;
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
@ -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]) {
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
import UIKit
|
||||
#if os(macOS)
|
||||
import Cocoa
|
||||
#else
|
||||
import UIKit
|
||||
#endif
|
||||
|
||||
@objc private class CALayerAnimationDelegate: NSObject, CAAnimationDelegate {
|
||||
var completion: ((Bool) -> Void)?
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 }
|
||||
|
||||
|
||||
560
Display/ContainedViewLayoutTransition.swift
Normal file
560
Display/ContainedViewLayoutTransition.swift
Normal 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
|
||||
@ -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? {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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,9 +2236,9 @@ 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 let scrollToItem = scrollToItem, scrollToItem.animated {
|
||||
if self.itemNodes.count != 0 {
|
||||
var offset: CGFloat?
|
||||
|
||||
@ -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 {
|
||||
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)
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import Foundation
|
||||
import AsyncDisplayKit
|
||||
#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) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
8
Display/ListViewFloatingHeaderNode.swift
Normal file
8
Display/ListViewFloatingHeaderNode.swift
Normal 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
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,9 @@
|
||||
import Foundation
|
||||
#if os(macOS)
|
||||
import SwiftSignalKitMac
|
||||
#else
|
||||
import SwiftSignalKit
|
||||
#endif
|
||||
|
||||
public enum ListViewCenterScrollPositionOverflow {
|
||||
case top
|
||||
|
||||
@ -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 }
|
||||
|
||||
@ -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) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
31
Display/ListViewOverscrollBackgroundNode.swift
Normal file
31
Display/ListViewOverscrollBackgroundNode.swift
Normal 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))
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,7 @@
|
||||
#if os(macOS)
|
||||
#else
|
||||
import UIKit
|
||||
#endif
|
||||
|
||||
class ListViewScroller: UIScrollView, UIGestureRecognizerDelegate {
|
||||
override init(frame: CGRect) {
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
import Foundation
|
||||
import AppKit
|
||||
|
||||
class ListViewScroller: CALayer {
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import UIKit
|
||||
|
||||
protocol MergeableLayoutEvent {
|
||||
|
||||
}
|
||||
|
||||
final class MergedLayoutEvents {
|
||||
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) -> ()) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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];
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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) {
|
||||
|
||||
84
DisplayMac/ASDisplayNode.swift
Normal file
84
DisplayMac/ASDisplayNode.swift
Normal 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) {
|
||||
|
||||
}
|
||||
}
|
||||
12
DisplayMac/NSValueAdditions.swift
Normal file
12
DisplayMac/NSValueAdditions.swift
Normal 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))
|
||||
}
|
||||
}
|
||||
47
DisplayMac/UIGestureRecognizer.swift
Normal file
47
DisplayMac/UIGestureRecognizer.swift
Normal 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
71
DisplayMac/UIKit.swift
Normal 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
|
||||
}
|
||||
24
DisplayMac/UIScrollView.swift
Normal file
24
DisplayMac/UIScrollView.swift
Normal 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
|
||||
}
|
||||
5
DisplayMac/UISlider.swift
Normal file
5
DisplayMac/UISlider.swift
Normal file
@ -0,0 +1,5 @@
|
||||
import Foundation
|
||||
|
||||
final class UISlider: UIView {
|
||||
|
||||
}
|
||||
5
DisplayMac/UITouch.swift
Normal file
5
DisplayMac/UITouch.swift
Normal file
@ -0,0 +1,5 @@
|
||||
import Foundation
|
||||
|
||||
public final class UITouch: NSObject {
|
||||
|
||||
}
|
||||
49
DisplayMac/UIView.swift
Normal file
49
DisplayMac/UIView.swift
Normal 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) {
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user