mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 19:30:29 +00:00
no message
This commit is contained in:
parent
962940aae9
commit
28457a2e16
@ -17,6 +17,9 @@
|
|||||||
D01E2BE01D90498E0066BF65 /* GridNodeScroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BDF1D90498E0066BF65 /* GridNodeScroller.swift */; };
|
D01E2BE01D90498E0066BF65 /* GridNodeScroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BDF1D90498E0066BF65 /* GridNodeScroller.swift */; };
|
||||||
D01E2BE21D9049F60066BF65 /* GridItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BE11D9049F60066BF65 /* GridItemNode.swift */; };
|
D01E2BE21D9049F60066BF65 /* GridItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BE11D9049F60066BF65 /* GridItemNode.swift */; };
|
||||||
D01E2BE41D904A000066BF65 /* GridItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BE31D904A000066BF65 /* GridItem.swift */; };
|
D01E2BE41D904A000066BF65 /* GridItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01E2BE31D904A000066BF65 /* GridItem.swift */; };
|
||||||
|
D02383801DDF7916004018B6 /* LegacyPresentedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D023837F1DDF7916004018B6 /* LegacyPresentedController.swift */; };
|
||||||
|
D02383821DDF798E004018B6 /* LegacyPresentedControllerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02383811DDF798E004018B6 /* LegacyPresentedControllerNode.swift */; };
|
||||||
|
D02383861DE0E3B4004018B6 /* ListViewIntermediateState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02383851DE0E3B4004018B6 /* ListViewIntermediateState.swift */; };
|
||||||
D02958001D6F096000360E5E /* ContextMenuContainerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02957FF1D6F096000360E5E /* ContextMenuContainerNode.swift */; };
|
D02958001D6F096000360E5E /* ContextMenuContainerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02957FF1D6F096000360E5E /* ContextMenuContainerNode.swift */; };
|
||||||
D02BDB021B6AC703008AFAD2 /* RuntimeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02BDB011B6AC703008AFAD2 /* RuntimeUtils.swift */; };
|
D02BDB021B6AC703008AFAD2 /* RuntimeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02BDB011B6AC703008AFAD2 /* RuntimeUtils.swift */; };
|
||||||
D03725C11D6DF594007FC290 /* ContextMenuNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03725C01D6DF594007FC290 /* ContextMenuNode.swift */; };
|
D03725C11D6DF594007FC290 /* ContextMenuNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03725C01D6DF594007FC290 /* ContextMenuNode.swift */; };
|
||||||
@ -103,6 +106,7 @@
|
|||||||
D0DC48561BF945DD00F672FD /* TabBarNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC48551BF945DD00F672FD /* TabBarNode.swift */; };
|
D0DC48561BF945DD00F672FD /* TabBarNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC48551BF945DD00F672FD /* TabBarNode.swift */; };
|
||||||
D0DC485F1BF949FB00F672FD /* TabBarContollerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC485E1BF949FB00F672FD /* TabBarContollerNode.swift */; };
|
D0DC485F1BF949FB00F672FD /* TabBarContollerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DC485E1BF949FB00F672FD /* TabBarContollerNode.swift */; };
|
||||||
D0E1D6721CBC201E00B04029 /* AsyncDisplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E1D6711CBC201E00B04029 /* AsyncDisplayKit.framework */; };
|
D0E1D6721CBC201E00B04029 /* AsyncDisplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E1D6711CBC201E00B04029 /* AsyncDisplayKit.framework */; };
|
||||||
|
D0E35A031DE473B900BC6096 /* HighlightableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A021DE473B900BC6096 /* HighlightableButton.swift */; };
|
||||||
D0E49C881B83A3580099E553 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E49C871B83A3580099E553 /* ImageCache.swift */; };
|
D0E49C881B83A3580099E553 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E49C871B83A3580099E553 /* ImageCache.swift */; };
|
||||||
D0F1132F1D6F3C20008C3597 /* ContextMenuActionNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F1132E1D6F3C20008C3597 /* ContextMenuActionNode.swift */; };
|
D0F1132F1D6F3C20008C3597 /* ContextMenuActionNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F1132E1D6F3C20008C3597 /* ContextMenuActionNode.swift */; };
|
||||||
D0F7AB371DCFF6F8009AD9A1 /* ListViewItemHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F7AB361DCFF6F8009AD9A1 /* ListViewItemHeader.swift */; };
|
D0F7AB371DCFF6F8009AD9A1 /* ListViewItemHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F7AB361DCFF6F8009AD9A1 /* ListViewItemHeader.swift */; };
|
||||||
@ -129,6 +133,9 @@
|
|||||||
D01E2BDF1D90498E0066BF65 /* GridNodeScroller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridNodeScroller.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>"; };
|
D01E2BE11D9049F60066BF65 /* GridItemNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridItemNode.swift; sourceTree = "<group>"; };
|
||||||
D01E2BE31D904A000066BF65 /* GridItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridItem.swift; sourceTree = "<group>"; };
|
D01E2BE31D904A000066BF65 /* GridItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridItem.swift; sourceTree = "<group>"; };
|
||||||
|
D023837F1DDF7916004018B6 /* LegacyPresentedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyPresentedController.swift; sourceTree = "<group>"; };
|
||||||
|
D02383811DDF798E004018B6 /* LegacyPresentedControllerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyPresentedControllerNode.swift; sourceTree = "<group>"; };
|
||||||
|
D02383851DE0E3B4004018B6 /* ListViewIntermediateState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewIntermediateState.swift; sourceTree = "<group>"; };
|
||||||
D02957FF1D6F096000360E5E /* ContextMenuContainerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextMenuContainerNode.swift; sourceTree = "<group>"; };
|
D02957FF1D6F096000360E5E /* ContextMenuContainerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextMenuContainerNode.swift; sourceTree = "<group>"; };
|
||||||
D02BDB011B6AC703008AFAD2 /* RuntimeUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuntimeUtils.swift; sourceTree = "<group>"; };
|
D02BDB011B6AC703008AFAD2 /* RuntimeUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuntimeUtils.swift; sourceTree = "<group>"; };
|
||||||
D03725C01D6DF594007FC290 /* ContextMenuNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextMenuNode.swift; sourceTree = "<group>"; };
|
D03725C01D6DF594007FC290 /* ContextMenuNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextMenuNode.swift; sourceTree = "<group>"; };
|
||||||
@ -219,6 +226,7 @@
|
|||||||
D0DC485E1BF949FB00F672FD /* TabBarContollerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarContollerNode.swift; sourceTree = "<group>"; };
|
D0DC485E1BF949FB00F672FD /* TabBarContollerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabBarContollerNode.swift; sourceTree = "<group>"; };
|
||||||
D0E1D6351CBC159C00B04029 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
D0E1D6351CBC159C00B04029 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||||
D0E1D6711CBC201E00B04029 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
D0E1D6711CBC201E00B04029 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
D0E35A021DE473B900BC6096 /* HighlightableButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HighlightableButton.swift; sourceTree = "<group>"; };
|
||||||
D0E49C871B83A3580099E553 /* ImageCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = "<group>"; };
|
D0E49C871B83A3580099E553 /* ImageCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = "<group>"; };
|
||||||
D0F1132E1D6F3C20008C3597 /* ContextMenuActionNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextMenuActionNode.swift; sourceTree = "<group>"; };
|
D0F1132E1D6F3C20008C3597 /* ContextMenuActionNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextMenuActionNode.swift; sourceTree = "<group>"; };
|
||||||
D0F7AB361DCFF6F8009AD9A1 /* ListViewItemHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewItemHeader.swift; sourceTree = "<group>"; };
|
D0F7AB361DCFF6F8009AD9A1 /* ListViewItemHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewItemHeader.swift; sourceTree = "<group>"; };
|
||||||
@ -303,6 +311,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
D0CD12151CCFEB4E000DE7BC /* ScrollToTopProxyView.swift */,
|
D0CD12151CCFEB4E000DE7BC /* ScrollToTopProxyView.swift */,
|
||||||
|
D0E35A021DE473B900BC6096 /* HighlightableButton.swift */,
|
||||||
);
|
);
|
||||||
name = Nodes;
|
name = Nodes;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -504,6 +513,8 @@
|
|||||||
D007B9A71D1D3B5400DA746D /* PresentableViewController.swift */,
|
D007B9A71D1D3B5400DA746D /* PresentableViewController.swift */,
|
||||||
D05BE4AA1D1F25E3002BD72C /* PresentationContext.swift */,
|
D05BE4AA1D1F25E3002BD72C /* PresentationContext.swift */,
|
||||||
D05CC2E21B69552C00E235A3 /* ViewController.swift */,
|
D05CC2E21B69552C00E235A3 /* ViewController.swift */,
|
||||||
|
D023837F1DDF7916004018B6 /* LegacyPresentedController.swift */,
|
||||||
|
D02383811DDF798E004018B6 /* LegacyPresentedControllerNode.swift */,
|
||||||
D05BE4A71D1F1DCC002BD72C /* Master */,
|
D05BE4A71D1F1DCC002BD72C /* Master */,
|
||||||
D081229A1D19A9EB005F7395 /* Navigation */,
|
D081229A1D19A9EB005F7395 /* Navigation */,
|
||||||
D015F7551D1B142300E269B5 /* Tab Bar */,
|
D015F7551D1B142300E269B5 /* Tab Bar */,
|
||||||
@ -519,6 +530,7 @@
|
|||||||
D0C2DFBB1CC4431D0044FF83 /* ASTransformLayerNode.swift */,
|
D0C2DFBB1CC4431D0044FF83 /* ASTransformLayerNode.swift */,
|
||||||
D0C2DFBD1CC4431D0044FF83 /* Spring.swift */,
|
D0C2DFBD1CC4431D0044FF83 /* Spring.swift */,
|
||||||
D0C2DFBE1CC4431D0044FF83 /* ListView.swift */,
|
D0C2DFBE1CC4431D0044FF83 /* ListView.swift */,
|
||||||
|
D02383851DE0E3B4004018B6 /* ListViewIntermediateState.swift */,
|
||||||
D0C2DFBF1CC4431D0044FF83 /* ListViewItem.swift */,
|
D0C2DFBF1CC4431D0044FF83 /* ListViewItem.swift */,
|
||||||
D0C2DFBC1CC4431D0044FF83 /* ListViewItemNode.swift */,
|
D0C2DFBC1CC4431D0044FF83 /* ListViewItemNode.swift */,
|
||||||
D0C2DFC01CC4431D0044FF83 /* ListViewAnimation.swift */,
|
D0C2DFC01CC4431D0044FF83 /* ListViewAnimation.swift */,
|
||||||
@ -731,8 +743,10 @@
|
|||||||
D08E903A1D24159200533158 /* ActionSheetItem.swift in Sources */,
|
D08E903A1D24159200533158 /* ActionSheetItem.swift in Sources */,
|
||||||
D0AE2CA61C94548900F2FD3C /* GenerateImage.swift in Sources */,
|
D0AE2CA61C94548900F2FD3C /* GenerateImage.swift in Sources */,
|
||||||
D05CC2EC1B69558A00E235A3 /* RuntimeUtils.m in Sources */,
|
D05CC2EC1B69558A00E235A3 /* RuntimeUtils.m in Sources */,
|
||||||
|
D0E35A031DE473B900BC6096 /* HighlightableButton.swift in Sources */,
|
||||||
D0CD12161CCFEB4E000DE7BC /* ScrollToTopProxyView.swift in Sources */,
|
D0CD12161CCFEB4E000DE7BC /* ScrollToTopProxyView.swift in Sources */,
|
||||||
D0C2DFCD1CC4431D0044FF83 /* ListViewTransactionQueue.swift in Sources */,
|
D0C2DFCD1CC4431D0044FF83 /* ListViewTransactionQueue.swift in Sources */,
|
||||||
|
D02383821DDF798E004018B6 /* LegacyPresentedControllerNode.swift in Sources */,
|
||||||
D05CC2FC1B6955D000E235A3 /* UIKitUtils.m in Sources */,
|
D05CC2FC1B6955D000E235A3 /* UIKitUtils.m in Sources */,
|
||||||
D0C2DFC61CC4431D0044FF83 /* ASTransformLayerNode.swift in Sources */,
|
D0C2DFC61CC4431D0044FF83 /* ASTransformLayerNode.swift in Sources */,
|
||||||
D05CC3291B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift in Sources */,
|
D05CC3291B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift in Sources */,
|
||||||
@ -742,11 +756,13 @@
|
|||||||
D05CC2FE1B6955D000E235A3 /* UIWindow+OrientationChange.m in Sources */,
|
D05CC2FE1B6955D000E235A3 /* UIWindow+OrientationChange.m in Sources */,
|
||||||
D0C85DD41D1C1E6A00124894 /* ActionSheetItemGroupNode.swift in Sources */,
|
D0C85DD41D1C1E6A00124894 /* ActionSheetItemGroupNode.swift in Sources */,
|
||||||
D08E903E1D24187900533158 /* ActionSheetItemGroup.swift in Sources */,
|
D08E903E1D24187900533158 /* ActionSheetItemGroup.swift in Sources */,
|
||||||
|
D02383861DE0E3B4004018B6 /* ListViewIntermediateState.swift in Sources */,
|
||||||
D0B367201C94A53A00346D2E /* StatusBarProxyNode.swift in Sources */,
|
D0B367201C94A53A00346D2E /* StatusBarProxyNode.swift in Sources */,
|
||||||
D05CC2A21B69326C00E235A3 /* Window.swift in Sources */,
|
D05CC2A21B69326C00E235A3 /* Window.swift in Sources */,
|
||||||
D015F7541D1B0F6C00E269B5 /* SystemContainedControllerTransitionCoordinator.swift in Sources */,
|
D015F7541D1B0F6C00E269B5 /* SystemContainedControllerTransitionCoordinator.swift in Sources */,
|
||||||
D05CC3151B695A9600E235A3 /* NavigationTransitionCoordinator.swift in Sources */,
|
D05CC3151B695A9600E235A3 /* NavigationTransitionCoordinator.swift in Sources */,
|
||||||
D03B0E701D6331FB00955575 /* StatusBarHost.swift in Sources */,
|
D03B0E701D6331FB00955575 /* StatusBarHost.swift in Sources */,
|
||||||
|
D02383801DDF7916004018B6 /* LegacyPresentedController.swift in Sources */,
|
||||||
D08E90471D243C2F00533158 /* HighlightTrackingButton.swift in Sources */,
|
D08E90471D243C2F00533158 /* HighlightTrackingButton.swift in Sources */,
|
||||||
D0F7AB371DCFF6F8009AD9A1 /* ListViewItemHeader.swift in Sources */,
|
D0F7AB371DCFF6F8009AD9A1 /* ListViewItemHeader.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,22 +1,26 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
public class HighlightTrackingButton: UIButton {
|
open class HighlightTrackingButton: UIButton {
|
||||||
|
public var internalHighligthedChanged: (Bool) -> Void = { _ in }
|
||||||
public var highligthedChanged: (Bool) -> Void = { _ in }
|
public var highligthedChanged: (Bool) -> Void = { _ in }
|
||||||
|
|
||||||
public override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
open override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
|
||||||
self.highligthedChanged(true)
|
self.highligthedChanged(true)
|
||||||
|
self.internalHighligthedChanged(true)
|
||||||
|
|
||||||
return super.beginTracking(touch, with: event)
|
return super.beginTracking(touch, with: event)
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
open override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
|
||||||
self.highligthedChanged(false)
|
self.highligthedChanged(false)
|
||||||
|
self.internalHighligthedChanged(false)
|
||||||
|
|
||||||
super.endTracking(touch, with: event)
|
super.endTracking(touch, with: event)
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func cancelTracking(with event: UIEvent?) {
|
open override func cancelTracking(with event: UIEvent?) {
|
||||||
self.highligthedChanged(false)
|
self.highligthedChanged(false)
|
||||||
|
self.internalHighligthedChanged(false)
|
||||||
|
|
||||||
super.cancelTracking(with: event)
|
super.cancelTracking(with: event)
|
||||||
}
|
}
|
||||||
|
|||||||
26
Display/HighlightableButton.swift
Normal file
26
Display/HighlightableButton.swift
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
open class HighlightableButton: HighlightTrackingButton {
|
||||||
|
override public init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.adjustsImageWhenHighlighted = false
|
||||||
|
self.adjustsImageWhenDisabled = false
|
||||||
|
self.internalHighligthedChanged = { [weak self] highlighted in
|
||||||
|
if let strongSelf = self {
|
||||||
|
if highlighted {
|
||||||
|
strongSelf.layer.removeAnimation(forKey: "opacity")
|
||||||
|
strongSelf.alpha = 0.4
|
||||||
|
} else {
|
||||||
|
strongSelf.alpha = 1.0
|
||||||
|
strongSelf.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
149
Display/LegacyPresentedController.swift
Normal file
149
Display/LegacyPresentedController.swift
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import AsyncDisplayKit
|
||||||
|
|
||||||
|
public enum LegacyPresentedControllerPresentation {
|
||||||
|
case custom
|
||||||
|
case modal
|
||||||
|
}
|
||||||
|
|
||||||
|
private func passControllerAppearanceAnimated(presentation: LegacyPresentedControllerPresentation) -> Bool {
|
||||||
|
switch presentation {
|
||||||
|
case .custom:
|
||||||
|
return false
|
||||||
|
case .modal:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open class LegacyPresentedController: ViewController {
|
||||||
|
private let legacyController: UIViewController
|
||||||
|
private let presentation: LegacyPresentedControllerPresentation
|
||||||
|
|
||||||
|
private var controllerNode: LegacyPresentedControllerNode {
|
||||||
|
return self.displayNode as! LegacyPresentedControllerNode
|
||||||
|
}
|
||||||
|
private var loadedController = false
|
||||||
|
|
||||||
|
var controllerLoaded: (() -> Void)?
|
||||||
|
|
||||||
|
private let asPresentable = true
|
||||||
|
|
||||||
|
public init(legacyController: UIViewController, presentation: LegacyPresentedControllerPresentation) {
|
||||||
|
self.legacyController = legacyController
|
||||||
|
self.presentation = presentation
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.navigationBar.isHidden = true
|
||||||
|
/*legacyController.navigation_setDismiss { [weak self] in
|
||||||
|
self?.dismiss()
|
||||||
|
}*/
|
||||||
|
if !asPresentable {
|
||||||
|
self.addChildViewController(legacyController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func loadDisplayNode() {
|
||||||
|
self.displayNode = LegacyPresentedControllerNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
if self.ignoreAppearanceMethodInvocations() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !loadedController && !asPresentable {
|
||||||
|
loadedController = true
|
||||||
|
|
||||||
|
self.controllerNode.controllerView = self.legacyController.view
|
||||||
|
self.controllerNode.view.addSubview(self.legacyController.view)
|
||||||
|
self.legacyController.didMove(toParentViewController: self)
|
||||||
|
|
||||||
|
if let controllerLoaded = self.controllerLoaded {
|
||||||
|
controllerLoaded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !asPresentable {
|
||||||
|
self.legacyController.viewWillAppear(animated && passControllerAppearanceAnimated(presentation: self.presentation))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func viewWillDisappear(_ animated: Bool) {
|
||||||
|
super.viewWillDisappear(animated)
|
||||||
|
|
||||||
|
if self.ignoreAppearanceMethodInvocations() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !asPresentable {
|
||||||
|
self.legacyController.viewWillDisappear(animated && passControllerAppearanceAnimated(presentation: self.presentation))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
|
if self.ignoreAppearanceMethodInvocations() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if asPresentable {
|
||||||
|
if !loadedController {
|
||||||
|
loadedController = true
|
||||||
|
//self.legacyController.modalPresentationStyle = .currentContext
|
||||||
|
self.present(self.legacyController, animated: false, completion: nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch self.presentation {
|
||||||
|
case .modal:
|
||||||
|
self.controllerNode.animateModalIn()
|
||||||
|
self.legacyController.viewDidAppear(true)
|
||||||
|
case .custom:
|
||||||
|
self.legacyController.viewDidAppear(animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func viewDidDisappear(_ animated: Bool) {
|
||||||
|
super.viewDidDisappear(animated)
|
||||||
|
|
||||||
|
if !self.asPresentable {
|
||||||
|
self.legacyController.viewDidDisappear(animated && passControllerAppearanceAnimated(presentation: self.presentation))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override open func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
|
||||||
|
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationBar.frame.maxY, transition: transition)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func dismiss() {
|
||||||
|
switch self.presentation {
|
||||||
|
case .modal:
|
||||||
|
self.controllerNode.animateModalOut { [weak self] in
|
||||||
|
/*if let controller = self?.legacyController as? TGViewController {
|
||||||
|
controller.didDismiss()
|
||||||
|
} else if let controller = self?.legacyController as? TGNavigationController {
|
||||||
|
controller.didDismiss()
|
||||||
|
}*/
|
||||||
|
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||||
|
}
|
||||||
|
case .custom:
|
||||||
|
/*if let controller = self.legacyController as? TGViewController {
|
||||||
|
controller.didDismiss()
|
||||||
|
} else if let controller = self.legacyController as? TGNavigationController {
|
||||||
|
controller.didDismiss()
|
||||||
|
}*/
|
||||||
|
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Display/LegacyPresentedControllerNode.swift
Normal file
38
Display/LegacyPresentedControllerNode.swift
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import Foundation
|
||||||
|
import AsyncDisplayKit
|
||||||
|
import Display
|
||||||
|
|
||||||
|
final class LegacyPresentedControllerNode: ASDisplayNode {
|
||||||
|
private var containerLayout: ContainerViewLayout?
|
||||||
|
|
||||||
|
var controllerView: UIView? {
|
||||||
|
didSet {
|
||||||
|
if let controllerView = self.controllerView, let containerLayout = self.containerLayout {
|
||||||
|
controllerView.frame = CGRect(origin: CGPoint(), size: containerLayout.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
super.init(viewBlock: {
|
||||||
|
return UITracingLayerView()
|
||||||
|
}, didLoad: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
|
self.containerLayout = layout
|
||||||
|
if let controllerView = self.controllerView {
|
||||||
|
controllerView.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateModalIn() {
|
||||||
|
self.layer.animatePosition(from: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), to: self.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateModalOut(completion: @escaping () -> Void) {
|
||||||
|
self.layer.animatePosition(from: self.layer.position, to: CGPoint(x: self.layer.position.x, y: self.layer.position.y + self.layer.bounds.size.height), duration: 0.2, timingFunction: kCAMediaTimingFunctionEaseInEaseOut, removeOnCompletion: false, completion: { _ in
|
||||||
|
completion()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
869
Display/ListViewIntermediateState.swift
Normal file
869
Display/ListViewIntermediateState.swift
Normal file
@ -0,0 +1,869 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum ListViewCenterScrollPositionOverflow {
|
||||||
|
case Top
|
||||||
|
case Bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ListViewScrollPosition: Equatable {
|
||||||
|
case Top
|
||||||
|
case Bottom
|
||||||
|
case Center(ListViewCenterScrollPositionOverflow)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func ==(lhs: ListViewScrollPosition, rhs: ListViewScrollPosition) -> Bool {
|
||||||
|
switch lhs {
|
||||||
|
case .Top:
|
||||||
|
switch rhs {
|
||||||
|
case .Top:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case .Bottom:
|
||||||
|
switch rhs {
|
||||||
|
case .Bottom:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case let .Center(lhsOverflow):
|
||||||
|
switch rhs {
|
||||||
|
case let .Center(rhsOverflow) where lhsOverflow == rhsOverflow:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ListViewScrollToItemDirectionHint {
|
||||||
|
case Up
|
||||||
|
case Down
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ListViewAnimationCurve {
|
||||||
|
case Spring(duration: Double)
|
||||||
|
case Default
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ListViewScrollToItem {
|
||||||
|
public let index: Int
|
||||||
|
public let position: ListViewScrollPosition
|
||||||
|
public let animated: Bool
|
||||||
|
public let curve: ListViewAnimationCurve
|
||||||
|
public let directionHint: ListViewScrollToItemDirectionHint
|
||||||
|
|
||||||
|
public init(index: Int, position: ListViewScrollPosition, animated: Bool, curve: ListViewAnimationCurve, directionHint: ListViewScrollToItemDirectionHint) {
|
||||||
|
self.index = index
|
||||||
|
self.position = position
|
||||||
|
self.animated = animated
|
||||||
|
self.curve = curve
|
||||||
|
self.directionHint = directionHint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ListViewItemOperationDirectionHint {
|
||||||
|
case Up
|
||||||
|
case Down
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ListViewDeleteItem {
|
||||||
|
public let index: Int
|
||||||
|
public let directionHint: ListViewItemOperationDirectionHint?
|
||||||
|
|
||||||
|
public init(index: Int, directionHint: ListViewItemOperationDirectionHint?) {
|
||||||
|
self.index = index
|
||||||
|
self.directionHint = directionHint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ListViewInsertItem {
|
||||||
|
public let index: Int
|
||||||
|
public let previousIndex: Int?
|
||||||
|
public let item: ListViewItem
|
||||||
|
public let directionHint: ListViewItemOperationDirectionHint?
|
||||||
|
public let forceAnimateInsertion: Bool
|
||||||
|
|
||||||
|
public init(index: Int, previousIndex: Int?, item: ListViewItem, directionHint: ListViewItemOperationDirectionHint?, forceAnimateInsertion: Bool = false) {
|
||||||
|
self.index = index
|
||||||
|
self.previousIndex = previousIndex
|
||||||
|
self.item = item
|
||||||
|
self.directionHint = directionHint
|
||||||
|
self.forceAnimateInsertion = forceAnimateInsertion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ListViewUpdateItem {
|
||||||
|
public let index: Int
|
||||||
|
public let previousIndex: Int
|
||||||
|
public let item: ListViewItem
|
||||||
|
public let directionHint: ListViewItemOperationDirectionHint?
|
||||||
|
|
||||||
|
public init(index: Int, previousIndex: Int, item: ListViewItem, directionHint: ListViewItemOperationDirectionHint?) {
|
||||||
|
self.index = index
|
||||||
|
self.previousIndex = previousIndex
|
||||||
|
self.item = item
|
||||||
|
self.directionHint = directionHint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ListViewDeleteAndInsertOptions: OptionSet {
|
||||||
|
public let rawValue: Int
|
||||||
|
|
||||||
|
public init(rawValue: Int) {
|
||||||
|
self.rawValue = rawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
public static let AnimateInsertion = ListViewDeleteAndInsertOptions(rawValue: 1)
|
||||||
|
public static let AnimateAlpha = ListViewDeleteAndInsertOptions(rawValue: 2)
|
||||||
|
public static let LowLatency = ListViewDeleteAndInsertOptions(rawValue: 4)
|
||||||
|
public static let Synchronous = ListViewDeleteAndInsertOptions(rawValue: 8)
|
||||||
|
public static let RequestItemInsertionAnimations = ListViewDeleteAndInsertOptions(rawValue: 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ListViewUpdateSizeAndInsets {
|
||||||
|
public let size: CGSize
|
||||||
|
public let insets: UIEdgeInsets
|
||||||
|
public let duration: Double
|
||||||
|
public let curve: ListViewAnimationCurve
|
||||||
|
|
||||||
|
public init(size: CGSize, insets: UIEdgeInsets, duration: Double, curve: ListViewAnimationCurve) {
|
||||||
|
self.size = size
|
||||||
|
self.insets = insets
|
||||||
|
self.duration = duration
|
||||||
|
self.curve = curve
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ListViewItemRange: Equatable {
|
||||||
|
public let firstIndex: Int
|
||||||
|
public let lastIndex: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
public func ==(lhs: ListViewItemRange, rhs: ListViewItemRange) -> Bool {
|
||||||
|
return lhs.firstIndex == rhs.firstIndex && lhs.lastIndex == rhs.lastIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct ListViewDisplayedItemRange: Equatable {
|
||||||
|
public let loadedRange: ListViewItemRange?
|
||||||
|
public let visibleRange: ListViewItemRange?
|
||||||
|
}
|
||||||
|
|
||||||
|
public func ==(lhs: ListViewDisplayedItemRange, rhs: ListViewDisplayedItemRange) -> Bool {
|
||||||
|
return lhs.loadedRange == rhs.loadedRange && lhs.visibleRange == rhs.visibleRange
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IndexRange {
|
||||||
|
let first: Int
|
||||||
|
let last: Int
|
||||||
|
|
||||||
|
func contains(_ index: Int) -> Bool {
|
||||||
|
return index >= first && index <= last
|
||||||
|
}
|
||||||
|
|
||||||
|
var empty: Bool {
|
||||||
|
return first > last
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OffsetRanges {
|
||||||
|
var offsets: [(IndexRange, CGFloat)] = []
|
||||||
|
|
||||||
|
mutating func append(_ other: OffsetRanges) {
|
||||||
|
self.offsets.append(contentsOf: other.offsets)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func offset(_ indexRange: IndexRange, offset: CGFloat) {
|
||||||
|
self.offsets.append((indexRange, offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
func offsetForIndex(_ index: Int) -> CGFloat {
|
||||||
|
var result: CGFloat = 0.0
|
||||||
|
for offset in self.offsets {
|
||||||
|
if offset.0.contains(index) {
|
||||||
|
result += offset.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func binarySearch(_ inputArr: [Int], searchItem: Int) -> Int? {
|
||||||
|
var lowerIndex = 0;
|
||||||
|
var upperIndex = inputArr.count - 1
|
||||||
|
|
||||||
|
if lowerIndex > upperIndex {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
let currentIndex = (lowerIndex + upperIndex) / 2
|
||||||
|
if (inputArr[currentIndex] == searchItem) {
|
||||||
|
return currentIndex
|
||||||
|
} else if (lowerIndex > upperIndex) {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
if (inputArr[currentIndex] > searchItem) {
|
||||||
|
upperIndex = currentIndex - 1
|
||||||
|
} else {
|
||||||
|
lowerIndex = currentIndex + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransactionState {
|
||||||
|
let visibleSize: CGSize
|
||||||
|
let items: [ListViewItem]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PendingNode {
|
||||||
|
let index: Int
|
||||||
|
let node: ListViewItemNode
|
||||||
|
let apply: () -> ()
|
||||||
|
let frame: CGRect
|
||||||
|
let apparentHeight: CGFloat
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ListViewStateNode {
|
||||||
|
case Node(index: Int, frame: CGRect, referenceNode: ListViewItemNode?)
|
||||||
|
case Placeholder(frame: CGRect)
|
||||||
|
|
||||||
|
var index: Int? {
|
||||||
|
switch self {
|
||||||
|
case .Node(let index, _, _):
|
||||||
|
return index
|
||||||
|
case .Placeholder(_):
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var frame: CGRect {
|
||||||
|
get {
|
||||||
|
switch self {
|
||||||
|
case .Node(_, let frame, _):
|
||||||
|
return frame
|
||||||
|
case .Placeholder(let frame):
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
} set(value) {
|
||||||
|
switch self {
|
||||||
|
case let .Node(index, _, referenceNode):
|
||||||
|
self = .Node(index: index, frame: value, referenceNode: referenceNode)
|
||||||
|
case .Placeholder(_):
|
||||||
|
self = .Placeholder(frame: value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ListViewInsertionOffsetDirection {
|
||||||
|
case Up
|
||||||
|
case Down
|
||||||
|
|
||||||
|
init(_ hint: ListViewItemOperationDirectionHint) {
|
||||||
|
switch hint {
|
||||||
|
case .Up:
|
||||||
|
self = .Up
|
||||||
|
case .Down:
|
||||||
|
self = .Down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func inverted() -> ListViewInsertionOffsetDirection {
|
||||||
|
switch self {
|
||||||
|
case .Up:
|
||||||
|
return .Down
|
||||||
|
case .Down:
|
||||||
|
return .Up
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ListViewInsertionPoint {
|
||||||
|
let index: Int
|
||||||
|
let point: CGPoint
|
||||||
|
let direction: ListViewInsertionOffsetDirection
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ListViewState {
|
||||||
|
var insets: UIEdgeInsets
|
||||||
|
var visibleSize: CGSize
|
||||||
|
let invisibleInset: CGFloat
|
||||||
|
var nodes: [ListViewStateNode]
|
||||||
|
var scrollPosition: (Int, ListViewScrollPosition)?
|
||||||
|
var stationaryOffset: (Int, CGFloat)?
|
||||||
|
let stackFromBottom: Bool
|
||||||
|
|
||||||
|
mutating func fixScrollPostition(_ itemCount: Int) {
|
||||||
|
if let (fixedIndex, fixedPosition) = self.scrollPosition {
|
||||||
|
for node in self.nodes {
|
||||||
|
if let index = node.index , index == fixedIndex {
|
||||||
|
let offset: CGFloat
|
||||||
|
switch fixedPosition {
|
||||||
|
case .Bottom:
|
||||||
|
offset = (self.visibleSize.height - self.insets.bottom) - node.frame.maxY
|
||||||
|
case .Top:
|
||||||
|
offset = self.insets.top - node.frame.minY
|
||||||
|
case let .Center(overflow):
|
||||||
|
let contentAreaHeight = self.visibleSize.height - self.insets.bottom - self.insets.top
|
||||||
|
if node.frame.size.height <= contentAreaHeight + CGFloat(FLT_EPSILON) {
|
||||||
|
offset = self.insets.top + floor((contentAreaHeight - node.frame.size.height) / 2.0) - node.frame.minY
|
||||||
|
} else {
|
||||||
|
switch overflow {
|
||||||
|
case .Top:
|
||||||
|
offset = self.insets.top - node.frame.minY
|
||||||
|
case .Bottom:
|
||||||
|
offset = (self.visibleSize.height - self.insets.bottom) - node.frame.maxY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var minY: CGFloat = CGFloat.greatestFiniteMagnitude
|
||||||
|
var maxY: CGFloat = 0.0
|
||||||
|
for i in 0 ..< self.nodes.count {
|
||||||
|
var frame = self.nodes[i].frame
|
||||||
|
frame = frame.offsetBy(dx: 0.0, dy: offset)
|
||||||
|
self.nodes[i].frame = frame
|
||||||
|
|
||||||
|
minY = min(minY, frame.minY)
|
||||||
|
maxY = max(maxY, frame.maxY)
|
||||||
|
}
|
||||||
|
|
||||||
|
var additionalOffset: CGFloat = 0.0
|
||||||
|
if minY > self.insets.top {
|
||||||
|
additionalOffset = self.insets.top - minY
|
||||||
|
}
|
||||||
|
|
||||||
|
if abs(additionalOffset) > CGFloat(FLT_EPSILON) {
|
||||||
|
for i in 0 ..< self.nodes.count {
|
||||||
|
var frame = self.nodes[i].frame
|
||||||
|
frame = frame.offsetBy(dx: 0.0, dy: additionalOffset)
|
||||||
|
self.nodes[i].frame = frame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.snapToBounds(itemCount, snapTopItem: true, stackFromBottom: self.stackFromBottom)
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let (stationaryIndex, stationaryOffset) = self.stationaryOffset {
|
||||||
|
for node in self.nodes {
|
||||||
|
if node.index == stationaryIndex {
|
||||||
|
let offset = stationaryOffset - node.frame.minY
|
||||||
|
|
||||||
|
if abs(offset) > CGFloat(FLT_EPSILON) {
|
||||||
|
for i in 0 ..< self.nodes.count {
|
||||||
|
var frame = self.nodes[i].frame
|
||||||
|
frame = frame.offsetBy(dx: 0.0, dy: offset)
|
||||||
|
self.nodes[i].frame = frame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func setupStationaryOffset(_ index: Int, boundary: Int, frames: [Int: CGRect]) {
|
||||||
|
if index < boundary {
|
||||||
|
for node in self.nodes {
|
||||||
|
if let nodeIndex = node.index , nodeIndex >= index {
|
||||||
|
if let frame = frames[nodeIndex] {
|
||||||
|
self.stationaryOffset = (nodeIndex, frame.minY)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for node in self.nodes.reversed() {
|
||||||
|
if let nodeIndex = node.index , nodeIndex <= index {
|
||||||
|
if let frame = frames[nodeIndex] {
|
||||||
|
self.stationaryOffset = (nodeIndex, frame.minY)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func snapToBounds(_ itemCount: Int, snapTopItem: Bool, stackFromBottom: Bool) {
|
||||||
|
var completeHeight: CGFloat = 0.0
|
||||||
|
var topItemFound = false
|
||||||
|
var bottomItemFound = false
|
||||||
|
var topItemEdge: CGFloat = 0.0
|
||||||
|
var bottomItemEdge: CGFloat = 0.0
|
||||||
|
|
||||||
|
for node in self.nodes {
|
||||||
|
if let index = node.index {
|
||||||
|
if index == 0 {
|
||||||
|
topItemFound = true
|
||||||
|
topItemEdge = node.frame.minY
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for node in self.nodes.reversed() {
|
||||||
|
if let index = node.index {
|
||||||
|
if index == itemCount - 1 {
|
||||||
|
bottomItemFound = true
|
||||||
|
bottomItemEdge = node.frame.maxY
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if topItemFound && bottomItemFound {
|
||||||
|
for node in self.nodes {
|
||||||
|
completeHeight += node.frame.size.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let overscroll: CGFloat = 0.0
|
||||||
|
|
||||||
|
var offset: CGFloat = 0.0
|
||||||
|
if topItemFound && bottomItemFound {
|
||||||
|
let areaHeight = min(completeHeight, self.visibleSize.height - self.insets.bottom - self.insets.top)
|
||||||
|
if bottomItemEdge < self.insets.top + areaHeight - overscroll {
|
||||||
|
offset = self.insets.top + areaHeight - overscroll - bottomItemEdge
|
||||||
|
} else if topItemEdge > self.insets.top - overscroll && snapTopItem {
|
||||||
|
offset = (self.insets.top - overscroll) - topItemEdge
|
||||||
|
}
|
||||||
|
} else if topItemFound {
|
||||||
|
if topItemEdge > self.insets.top - overscroll && snapTopItem {
|
||||||
|
offset = (self.insets.top - overscroll) - topItemEdge
|
||||||
|
}
|
||||||
|
} else if bottomItemFound {
|
||||||
|
if bottomItemEdge < self.visibleSize.height - self.insets.bottom - overscroll {
|
||||||
|
offset = self.visibleSize.height - self.insets.bottom - overscroll - bottomItemEdge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if abs(offset) > CGFloat(FLT_EPSILON) {
|
||||||
|
for i in 0 ..< self.nodes.count {
|
||||||
|
var frame = self.nodes[i].frame
|
||||||
|
frame.origin.y += offset
|
||||||
|
self.nodes[i].frame = frame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func insertionPoint(_ insertDirectionHints: [Int: ListViewItemOperationDirectionHint], itemCount: Int) -> ListViewInsertionPoint? {
|
||||||
|
var fixedNode: (nodeIndex: Int, index: Int, frame: CGRect)?
|
||||||
|
|
||||||
|
if let (fixedIndex, _) = self.scrollPosition {
|
||||||
|
for i in 0 ..< self.nodes.count {
|
||||||
|
let node = self.nodes[i]
|
||||||
|
if let index = node.index , index == fixedIndex {
|
||||||
|
fixedNode = (i, index, node.frame)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fixedNode == nil {
|
||||||
|
return ListViewInsertionPoint(index: fixedIndex, point: CGPoint(), direction: .Down)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fixedNode == nil {
|
||||||
|
if let (fixedIndex, _) = self.stationaryOffset {
|
||||||
|
for i in 0 ..< self.nodes.count {
|
||||||
|
let node = self.nodes[i]
|
||||||
|
if let index = node.index , index == fixedIndex {
|
||||||
|
fixedNode = (i, index, node.frame)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fixedNode == nil {
|
||||||
|
for i in 0 ..< self.nodes.count {
|
||||||
|
let node = self.nodes[i]
|
||||||
|
if let index = node.index , node.frame.maxY >= self.insets.top {
|
||||||
|
fixedNode = (i, index, node.frame)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fixedNode == nil && self.nodes.count != 0 {
|
||||||
|
for i in (0 ..< self.nodes.count).reversed() {
|
||||||
|
let node = self.nodes[i]
|
||||||
|
if let index = node.index {
|
||||||
|
fixedNode = (i, index, node.frame)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let fixedNode = fixedNode {
|
||||||
|
var currentUpperNode = fixedNode
|
||||||
|
for i in (0 ..< fixedNode.nodeIndex).reversed() {
|
||||||
|
let node = self.nodes[i]
|
||||||
|
if let index = node.index {
|
||||||
|
if index != currentUpperNode.index - 1 {
|
||||||
|
if currentUpperNode.frame.minY > -self.invisibleInset - CGFloat(FLT_EPSILON) {
|
||||||
|
var directionHint: ListViewInsertionOffsetDirection?
|
||||||
|
if let hint = insertDirectionHints[currentUpperNode.index - 1] , currentUpperNode.frame.minY > self.insets.top - CGFloat(FLT_EPSILON) {
|
||||||
|
directionHint = ListViewInsertionOffsetDirection(hint)
|
||||||
|
}
|
||||||
|
return ListViewInsertionPoint(index: currentUpperNode.index - 1, point: CGPoint(x: 0.0, y: currentUpperNode.frame.minY), direction: directionHint ?? .Up)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentUpperNode = (i, index, node.frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentUpperNode.index != 0 && currentUpperNode.frame.minY > -self.invisibleInset - CGFloat(FLT_EPSILON) {
|
||||||
|
var directionHint: ListViewInsertionOffsetDirection?
|
||||||
|
if let hint = insertDirectionHints[currentUpperNode.index - 1] , currentUpperNode.frame.minY > self.insets.top - CGFloat(FLT_EPSILON) {
|
||||||
|
directionHint = ListViewInsertionOffsetDirection(hint)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListViewInsertionPoint(index: currentUpperNode.index - 1, point: CGPoint(x: 0.0, y: currentUpperNode.frame.minY), direction: directionHint ?? .Up)
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentLowerNode = fixedNode
|
||||||
|
if fixedNode.nodeIndex + 1 < self.nodes.count {
|
||||||
|
for i in (fixedNode.nodeIndex + 1) ..< self.nodes.count {
|
||||||
|
let node = self.nodes[i]
|
||||||
|
if let index = node.index {
|
||||||
|
if index != currentLowerNode.index + 1 {
|
||||||
|
if currentLowerNode.frame.maxY < self.visibleSize.height + self.invisibleInset - CGFloat(FLT_EPSILON) {
|
||||||
|
var directionHint: ListViewInsertionOffsetDirection?
|
||||||
|
if let hint = insertDirectionHints[currentLowerNode.index + 1] , currentLowerNode.frame.maxY < self.visibleSize.height - self.insets.bottom + CGFloat(FLT_EPSILON) {
|
||||||
|
directionHint = ListViewInsertionOffsetDirection(hint)
|
||||||
|
}
|
||||||
|
return ListViewInsertionPoint(index: currentLowerNode.index + 1, point: CGPoint(x: 0.0, y: currentLowerNode.frame.maxY), direction: directionHint ?? .Down)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentLowerNode = (i, index, node.frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentLowerNode.index != itemCount - 1 && currentLowerNode.frame.maxY < self.visibleSize.height + self.invisibleInset - CGFloat(FLT_EPSILON) {
|
||||||
|
var directionHint: ListViewInsertionOffsetDirection?
|
||||||
|
if let hint = insertDirectionHints[currentLowerNode.index + 1] , currentLowerNode.frame.maxY < self.visibleSize.height - self.insets.bottom + CGFloat(FLT_EPSILON) {
|
||||||
|
directionHint = ListViewInsertionOffsetDirection(hint)
|
||||||
|
}
|
||||||
|
return ListViewInsertionPoint(index: currentLowerNode.index + 1, point: CGPoint(x: 0.0, y: currentLowerNode.frame.maxY), direction: directionHint ?? .Down)
|
||||||
|
}
|
||||||
|
} else if itemCount != 0 {
|
||||||
|
return ListViewInsertionPoint(index: 0, point: CGPoint(x: 0.0, y: self.insets.top), direction: .Down)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func removeInvisibleNodes(_ operations: inout [ListViewStateOperation]) {
|
||||||
|
var i = 0
|
||||||
|
var visibleItemNodeHeight: CGFloat = 0.0
|
||||||
|
while i < self.nodes.count {
|
||||||
|
visibleItemNodeHeight += self.nodes[i].frame.height
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if visibleItemNodeHeight > (self.visibleSize.height + self.invisibleInset + self.invisibleInset) {
|
||||||
|
i = self.nodes.count - 1
|
||||||
|
while i >= 0 {
|
||||||
|
let itemNode = self.nodes[i]
|
||||||
|
let frame = itemNode.frame
|
||||||
|
//print("node \(i) frame \(frame)")
|
||||||
|
if frame.maxY < -self.invisibleInset || frame.origin.y > self.visibleSize.height + self.invisibleInset {
|
||||||
|
//print("remove invisible 1 \(i) frame \(frame)")
|
||||||
|
operations.append(.Remove(index: i, offsetDirection: frame.maxY < -self.invisibleInset ? .Down : .Up))
|
||||||
|
self.nodes.remove(at: i)
|
||||||
|
}
|
||||||
|
|
||||||
|
i -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let upperBound = -self.invisibleInset + CGFloat(FLT_EPSILON)
|
||||||
|
for i in 0 ..< self.nodes.count {
|
||||||
|
let node = self.nodes[i]
|
||||||
|
if let index = node.index , node.frame.maxY > upperBound {
|
||||||
|
if i != 0 {
|
||||||
|
var previousIndex = index
|
||||||
|
for j in (0 ..< i).reversed() {
|
||||||
|
if self.nodes[j].frame.maxY < upperBound {
|
||||||
|
if let index = self.nodes[j].index {
|
||||||
|
if index != previousIndex - 1 {
|
||||||
|
//print("remove monotonity \(j) (\(index))")
|
||||||
|
operations.append(.Remove(index: j, offsetDirection: .Down))
|
||||||
|
self.nodes.remove(at: j)
|
||||||
|
} else {
|
||||||
|
previousIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lowerBound = self.visibleSize.height + self.invisibleInset - CGFloat(FLT_EPSILON)
|
||||||
|
for i in (0 ..< self.nodes.count).reversed() {
|
||||||
|
let node = self.nodes[i]
|
||||||
|
if let index = node.index , node.frame.minY < lowerBound {
|
||||||
|
if i != self.nodes.count - 1 {
|
||||||
|
var previousIndex = index
|
||||||
|
var removeIndices: [Int] = []
|
||||||
|
for j in (i + 1) ..< self.nodes.count {
|
||||||
|
if self.nodes[j].frame.minY > lowerBound {
|
||||||
|
if let index = self.nodes[j].index {
|
||||||
|
if index != previousIndex + 1 {
|
||||||
|
removeIndices.append(j)
|
||||||
|
} else {
|
||||||
|
previousIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !removeIndices.isEmpty {
|
||||||
|
for i in removeIndices.reversed() {
|
||||||
|
//print("remove monotonity \(i) (\(self.nodes[i].index!))")
|
||||||
|
operations.append(.Remove(index: i, offsetDirection: .Up))
|
||||||
|
self.nodes.remove(at: i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeInsertionPointAndIndex(_ itemIndex: Int) -> (CGPoint, Int) {
|
||||||
|
if self.nodes.count == 0 {
|
||||||
|
return (CGPoint(x: 0.0, y: self.insets.top), 0)
|
||||||
|
} else {
|
||||||
|
var index = 0
|
||||||
|
var lastNodeWithIndex = -1
|
||||||
|
for node in self.nodes {
|
||||||
|
if let nodeItemIndex = node.index {
|
||||||
|
if nodeItemIndex > itemIndex {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
lastNodeWithIndex = index
|
||||||
|
}
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
lastNodeWithIndex += 1
|
||||||
|
return (CGPoint(x: 0.0, y: lastNodeWithIndex == 0 ? self.nodes[0].frame.minY : self.nodes[lastNodeWithIndex - 1].frame.maxY), lastNodeWithIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func continuousHeightRelativeToNodeIndex(_ fixedNodeIndex: Int) -> CGFloat {
|
||||||
|
let fixedIndex = self.nodes[fixedNodeIndex].index!
|
||||||
|
|
||||||
|
var height: CGFloat = 0.0
|
||||||
|
|
||||||
|
if fixedNodeIndex != 0 {
|
||||||
|
var upperIndex = fixedIndex
|
||||||
|
for i in (0 ..< fixedNodeIndex).reversed() {
|
||||||
|
if let index = self.nodes[i].index {
|
||||||
|
if index == upperIndex - 1 {
|
||||||
|
height += self.nodes[i].frame.size.height
|
||||||
|
upperIndex = index
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fixedNodeIndex != self.nodes.count - 1 {
|
||||||
|
var lowerIndex = fixedIndex
|
||||||
|
for i in (fixedNodeIndex + 1) ..< self.nodes.count {
|
||||||
|
if let index = self.nodes[i].index {
|
||||||
|
if index == lowerIndex + 1 {
|
||||||
|
height += self.nodes[i].frame.size.height
|
||||||
|
lowerIndex = index
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return height
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func insertNode(_ itemIndex: Int, node: ListViewItemNode, layout: ListViewItemNodeLayout, apply: @escaping () -> (), offsetDirection: ListViewInsertionOffsetDirection, animated: Bool, operations: inout [ListViewStateOperation], itemCount: Int) {
|
||||||
|
let (insertionOrigin, insertionIndex) = self.nodeInsertionPointAndIndex(itemIndex)
|
||||||
|
|
||||||
|
let nodeOrigin: CGPoint
|
||||||
|
switch offsetDirection {
|
||||||
|
case .Up:
|
||||||
|
nodeOrigin = CGPoint(x: insertionOrigin.x, y: insertionOrigin.y - (animated ? 0.0 : layout.size.height))
|
||||||
|
case .Down:
|
||||||
|
nodeOrigin = insertionOrigin
|
||||||
|
}
|
||||||
|
|
||||||
|
let nodeFrame = CGRect(origin: nodeOrigin, size: CGSize(width: layout.size.width, height: animated ? 0.0 : layout.size.height))
|
||||||
|
|
||||||
|
operations.append(.InsertNode(index: insertionIndex, offsetDirection: offsetDirection, node: node, layout: layout, apply: apply))
|
||||||
|
self.nodes.insert(.Node(index: itemIndex, frame: nodeFrame, referenceNode: nil), at: insertionIndex)
|
||||||
|
|
||||||
|
if !animated {
|
||||||
|
switch offsetDirection {
|
||||||
|
case .Up:
|
||||||
|
var i = insertionIndex - 1
|
||||||
|
while i >= 0 {
|
||||||
|
var frame = self.nodes[i].frame
|
||||||
|
frame.origin.y -= nodeFrame.size.height
|
||||||
|
self.nodes[i].frame = frame
|
||||||
|
i -= 1
|
||||||
|
}
|
||||||
|
case .Down:
|
||||||
|
var i = insertionIndex + 1
|
||||||
|
while i < self.nodes.count {
|
||||||
|
var frame = self.nodes[i].frame
|
||||||
|
frame.origin.y += nodeFrame.size.height
|
||||||
|
self.nodes[i].frame = frame
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var previousIndex: Int?
|
||||||
|
for node in self.nodes {
|
||||||
|
if let index = node.index {
|
||||||
|
if let currentPreviousIndex = previousIndex {
|
||||||
|
if index <= currentPreviousIndex {
|
||||||
|
print("index <= previousIndex + 1")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
previousIndex = index
|
||||||
|
} else {
|
||||||
|
previousIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let _ = self.scrollPosition {
|
||||||
|
self.fixScrollPostition(itemCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func removeNodeAtIndex(_ index: Int, direction: ListViewItemOperationDirectionHint?, animated: Bool, operations: inout [ListViewStateOperation]) {
|
||||||
|
let node = self.nodes[index]
|
||||||
|
if case let .Node(_, _, referenceNode) = node {
|
||||||
|
let nodeFrame = node.frame
|
||||||
|
self.nodes.remove(at: index)
|
||||||
|
let offsetDirection: ListViewInsertionOffsetDirection
|
||||||
|
if let direction = direction {
|
||||||
|
offsetDirection = ListViewInsertionOffsetDirection(direction)
|
||||||
|
} else {
|
||||||
|
if nodeFrame.maxY < self.insets.top + CGFloat(FLT_EPSILON) {
|
||||||
|
offsetDirection = .Down
|
||||||
|
} else {
|
||||||
|
offsetDirection = .Up
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operations.append(.Remove(index: index, offsetDirection: offsetDirection))
|
||||||
|
|
||||||
|
if let referenceNode = referenceNode , animated {
|
||||||
|
self.nodes.insert(.Placeholder(frame: nodeFrame), at: index)
|
||||||
|
operations.append(.InsertDisappearingPlaceholder(index: index, referenceNode: referenceNode, offsetDirection: offsetDirection.inverted()))
|
||||||
|
} else {
|
||||||
|
if nodeFrame.maxY > self.insets.top - CGFloat(FLT_EPSILON) {
|
||||||
|
if let direction = direction , direction == .Down && node.frame.minY < self.visibleSize.height - self.insets.bottom + CGFloat(FLT_EPSILON) {
|
||||||
|
for i in (0 ..< index).reversed() {
|
||||||
|
var frame = self.nodes[i].frame
|
||||||
|
frame.origin.y += nodeFrame.size.height
|
||||||
|
self.nodes[i].frame = frame
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i in index ..< self.nodes.count {
|
||||||
|
var frame = self.nodes[i].frame
|
||||||
|
frame.origin.y -= nodeFrame.size.height
|
||||||
|
self.nodes[i].frame = frame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if index != 0 {
|
||||||
|
for i in (0 ..< index).reversed() {
|
||||||
|
var frame = self.nodes[i].frame
|
||||||
|
frame.origin.y += nodeFrame.size.height
|
||||||
|
self.nodes[i].frame = frame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func updateNodeAtItemIndex(_ itemIndex: Int, layout: ListViewItemNodeLayout, direction: ListViewItemOperationDirectionHint?, animation: ListViewItemUpdateAnimation, apply: @escaping () -> Void, operations: inout [ListViewStateOperation]) {
|
||||||
|
var i = -1
|
||||||
|
for node in self.nodes {
|
||||||
|
i += 1
|
||||||
|
if node.index == itemIndex {
|
||||||
|
switch animation {
|
||||||
|
case .None:
|
||||||
|
let offsetDirection: ListViewInsertionOffsetDirection
|
||||||
|
if let direction = direction {
|
||||||
|
offsetDirection = ListViewInsertionOffsetDirection(direction)
|
||||||
|
} else {
|
||||||
|
if node.frame.maxY < self.insets.top + CGFloat(FLT_EPSILON) {
|
||||||
|
offsetDirection = .Down
|
||||||
|
} else {
|
||||||
|
offsetDirection = .Up
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch offsetDirection {
|
||||||
|
case .Up:
|
||||||
|
let offsetDelta = -(layout.size.height - node.frame.size.height)
|
||||||
|
var updatedFrame = node.frame
|
||||||
|
updatedFrame.origin.y += offsetDelta
|
||||||
|
updatedFrame.size.height = layout.size.height
|
||||||
|
self.nodes[i].frame = updatedFrame
|
||||||
|
|
||||||
|
for j in 0 ..< i {
|
||||||
|
var frame = self.nodes[j].frame
|
||||||
|
frame.origin.y += offsetDelta
|
||||||
|
self.nodes[j].frame = frame
|
||||||
|
}
|
||||||
|
case .Down:
|
||||||
|
let offsetDelta = layout.size.height - node.frame.size.height
|
||||||
|
var updatedFrame = node.frame
|
||||||
|
updatedFrame.size.height = layout.size.height
|
||||||
|
self.nodes[i].frame = updatedFrame
|
||||||
|
|
||||||
|
for j in i + 1 ..< self.nodes.count {
|
||||||
|
var frame = self.nodes[j].frame
|
||||||
|
frame.origin.y += offsetDelta
|
||||||
|
self.nodes[j].frame = frame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operations.append(.UpdateLayout(index: i, layout: layout, apply: apply))
|
||||||
|
case .System:
|
||||||
|
operations.append(.UpdateLayout(index: i, layout: layout, apply: apply))
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ListViewStateOperation {
|
||||||
|
case InsertNode(index: Int, offsetDirection: ListViewInsertionOffsetDirection, node: ListViewItemNode, layout: ListViewItemNodeLayout, apply: () -> ())
|
||||||
|
case InsertDisappearingPlaceholder(index: Int, referenceNode: ListViewItemNode, offsetDirection: ListViewInsertionOffsetDirection)
|
||||||
|
case Remove(index: Int, offsetDirection: ListViewInsertionOffsetDirection)
|
||||||
|
case Remap([Int: Int])
|
||||||
|
case UpdateLayout(index: Int, layout: ListViewItemNodeLayout, apply: () -> ())
|
||||||
|
}
|
||||||
@ -368,11 +368,11 @@ open class NavigationController: NavigationControllerProxy, ContainableControlle
|
|||||||
|
|
||||||
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||||
if let controller = viewControllerToPresent as? NavigationController {
|
if let controller = viewControllerToPresent as? NavigationController {
|
||||||
controller.navigation_setDismiss { [weak self] in
|
controller.navigation_setDismiss({ [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.dismiss(animated: false, completion: nil)
|
strongSelf.dismiss(animated: false, completion: nil)
|
||||||
}
|
}
|
||||||
}
|
}, rootController: self.view!.window!.rootViewController)
|
||||||
self._presentedViewController = controller
|
self._presentedViewController = controller
|
||||||
|
|
||||||
self.view.endEditing(true)
|
self.view.endEditing(true)
|
||||||
|
|||||||
@ -52,11 +52,12 @@ final class PresentationContext {
|
|||||||
|
|
||||||
strongSelf.controllers.append(controller)
|
strongSelf.controllers.append(controller)
|
||||||
if let view = strongSelf.view, let layout = strongSelf.layout {
|
if let view = strongSelf.view, let layout = strongSelf.layout {
|
||||||
controller.navigation_setDismiss { [weak strongSelf, weak controller] in
|
controller.navigation_setDismiss({ [weak strongSelf, weak controller] in
|
||||||
if let strongSelf = strongSelf, let controller = controller {
|
if let strongSelf = strongSelf, let controller = controller {
|
||||||
strongSelf.dismiss(controller)
|
strongSelf.dismiss(controller)
|
||||||
}
|
}
|
||||||
}
|
}, rootController: nil)
|
||||||
|
controller.setIgnoreAppearanceMethodInvocations(true)
|
||||||
if layout != initialLayout {
|
if layout != initialLayout {
|
||||||
controller.view.frame = CGRect(origin: CGPoint(), size: layout.size)
|
controller.view.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||||
view.addSubview(controller.view)
|
view.addSubview(controller.view)
|
||||||
@ -64,6 +65,7 @@ final class PresentationContext {
|
|||||||
} else {
|
} else {
|
||||||
view.addSubview(controller.view)
|
view.addSubview(controller.view)
|
||||||
}
|
}
|
||||||
|
controller.setIgnoreAppearanceMethodInvocations(false)
|
||||||
view.layer.invalidateUpTheTree()
|
view.layer.invalidateUpTheTree()
|
||||||
controller.viewWillAppear(false)
|
controller.viewWillAppear(false)
|
||||||
controller.viewDidAppear(false)
|
controller.viewDidAppear(false)
|
||||||
|
|||||||
@ -3,9 +3,10 @@
|
|||||||
@interface UIViewController (Navigation)
|
@interface UIViewController (Navigation)
|
||||||
|
|
||||||
- (void)setIgnoreAppearanceMethodInvocations:(BOOL)ignoreAppearanceMethodInvocations;
|
- (void)setIgnoreAppearanceMethodInvocations:(BOOL)ignoreAppearanceMethodInvocations;
|
||||||
|
- (BOOL)ignoreAppearanceMethodInvocations;
|
||||||
- (void)navigation_setNavigationController:(UINavigationController * _Nullable)navigationControlller;
|
- (void)navigation_setNavigationController:(UINavigationController * _Nullable)navigationControlller;
|
||||||
- (void)navigation_setPresentingViewController:(UIViewController * _Nullable)presentingViewController;
|
- (void)navigation_setPresentingViewController:(UIViewController * _Nullable)presentingViewController;
|
||||||
- (void)navigation_setDismiss:(void (^_Nullable)())dismiss;
|
- (void)navigation_setDismiss:(void (^_Nullable)())dismiss rootController:(UIViewController *)rootController;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@ -8,12 +8,14 @@
|
|||||||
@interface UIViewControllerPresentingProxy : UIViewController
|
@interface UIViewControllerPresentingProxy : UIViewController
|
||||||
|
|
||||||
@property (nonatomic, copy) void (^dismiss)();
|
@property (nonatomic, copy) void (^dismiss)();
|
||||||
|
@property (nonatomic, strong, readonly) UIViewController *rootController;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation UIViewControllerPresentingProxy
|
@implementation UIViewControllerPresentingProxy
|
||||||
|
|
||||||
- (instancetype)init {
|
- (instancetype)initWithRootController:(UIViewController *)rootController {
|
||||||
|
_rootController = rootController;
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,6 +94,7 @@ static bool notyfyingShiftState = false;
|
|||||||
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(viewDidDisappear:) newSelector:@selector(_65087dc8_viewDidDisappear:)];
|
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(viewDidDisappear:) newSelector:@selector(_65087dc8_viewDidDisappear:)];
|
||||||
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(navigationController) newSelector:@selector(_65087dc8_navigationController)];
|
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(navigationController) newSelector:@selector(_65087dc8_navigationController)];
|
||||||
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(presentingViewController) newSelector:@selector(_65087dc8_presentingViewController)];
|
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(presentingViewController) newSelector:@selector(_65087dc8_presentingViewController)];
|
||||||
|
[RuntimeUtils swizzleInstanceMethodOfClass:[UIViewController class] currentSelector:@selector(presentViewController:animated:completion:) newSelector:@selector(_65087dc8_presentViewController:animated:completion:)];
|
||||||
|
|
||||||
//[RuntimeUtils swizzleInstanceMethodOfClass:NSClassFromString(@"UIKeyboardImpl") currentSelector:@selector(notifyShiftState) withAnotherClass:[UIKeyboardImpl_65087dc8 class] newSelector:@selector(notifyShiftState)];
|
//[RuntimeUtils swizzleInstanceMethodOfClass:NSClassFromString(@"UIKeyboardImpl") currentSelector:@selector(notifyShiftState) withAnotherClass:[UIKeyboardImpl_65087dc8 class] newSelector:@selector(notifyShiftState)];
|
||||||
//[RuntimeUtils swizzleInstanceMethodOfClass:NSClassFromString(@"UIInputWindowController") currentSelector:@selector(updateViewConstraints) withAnotherClass:[UIInputWindowController_65087dc8 class] newSelector:@selector(updateViewConstraints)];
|
//[RuntimeUtils swizzleInstanceMethodOfClass:NSClassFromString(@"UIInputWindowController") currentSelector:@selector(updateViewConstraints) withAnotherClass:[UIInputWindowController_65087dc8 class] newSelector:@selector(updateViewConstraints)];
|
||||||
@ -156,8 +159,8 @@ static bool notyfyingShiftState = false;
|
|||||||
[self setAssociatedObject:[[NSWeakReference alloc] initWithValue:presentingViewController] forKey:UIViewControllerPresentingControllerKey];
|
[self setAssociatedObject:[[NSWeakReference alloc] initWithValue:presentingViewController] forKey:UIViewControllerPresentingControllerKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)navigation_setDismiss:(void (^_Nullable)())dismiss {
|
- (void)navigation_setDismiss:(void (^_Nullable)())dismiss rootController:(UIViewController *)rootController {
|
||||||
UIViewControllerPresentingProxy *proxy = [[UIViewControllerPresentingProxy alloc] init];
|
UIViewControllerPresentingProxy *proxy = [[UIViewControllerPresentingProxy alloc] initWithRootController:rootController];
|
||||||
proxy.dismiss = dismiss;
|
proxy.dismiss = dismiss;
|
||||||
[self setAssociatedObject:proxy forKey:UIViewControllerPresentingProxyControllerKey];
|
[self setAssociatedObject:proxy forKey:UIViewControllerPresentingProxyControllerKey];
|
||||||
}
|
}
|
||||||
@ -173,7 +176,16 @@ static bool notyfyingShiftState = false;
|
|||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [self associatedObjectForKey:UIViewControllerPresentingProxyControllerKey];
|
UIView *result = [self associatedObjectForKey:UIViewControllerPresentingProxyControllerKey];
|
||||||
|
if (result != nil) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [self _65087dc8_presentingViewController];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_65087dc8_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
|
||||||
|
[self _65087dc8_presentViewController:viewControllerToPresent animated:flag completion:completion];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -174,9 +174,9 @@ private func findCurrentResponder(_ view: UIView) -> UIResponder? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
/*override open func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||||
preconditionFailure("use present(_:in)")
|
preconditionFailure("use present(_:in)")
|
||||||
}
|
}*/
|
||||||
|
|
||||||
override open func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
override open func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||||
if let navigationController = self.navigationController as? NavigationController {
|
if let navigationController = self.navigationController as? NavigationController {
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import Foundation
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
|
|
||||||
private class WindowRootViewController: UIViewController {
|
private class WindowRootViewController: UIViewController {
|
||||||
|
var presentController: ((UIViewController, Bool, (() -> Void)?) -> Void)?
|
||||||
|
|
||||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||||
return .default
|
return .default
|
||||||
}
|
}
|
||||||
@ -9,6 +11,12 @@ private class WindowRootViewController: UIViewController {
|
|||||||
override var prefersStatusBarHidden: Bool {
|
override var prefersStatusBarHidden: Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
|
||||||
|
if let presentController = self.presentController {
|
||||||
|
presentController(viewControllerToPresent, flag, completion)
|
||||||
|
}
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct WindowLayout: Equatable {
|
private struct WindowLayout: Equatable {
|
||||||
@ -130,9 +138,20 @@ public class Window: UIWindow {
|
|||||||
self.presentationContext.view = self
|
self.presentationContext.view = self
|
||||||
self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: .immediate)
|
self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: .immediate)
|
||||||
|
|
||||||
super.rootViewController = WindowRootViewController()
|
let rootViewController = WindowRootViewController()
|
||||||
super.rootViewController?.viewWillAppear(false)
|
super.rootViewController = rootViewController
|
||||||
super.rootViewController?.viewDidAppear(false)
|
rootViewController.viewWillAppear(false)
|
||||||
|
rootViewController.viewDidAppear(false)
|
||||||
|
rootViewController.view.isHidden = true
|
||||||
|
|
||||||
|
rootViewController.presentController = { [weak self] controller, animated, completion in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.present(LegacyPresentedController(legacyController: controller, presentation: .custom))
|
||||||
|
if let completion = completion {
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.statusBarChangeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil, queue: OperationQueue.main, using: { [weak self] notification in
|
self.statusBarChangeObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil, queue: OperationQueue.main, using: { [weak self] notification in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -230,18 +249,18 @@ public class Window: UIWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var rootController: ContainableController?
|
private var _rootController: ContainableController?
|
||||||
public var viewController: ContainableController? {
|
public var viewController: ContainableController? {
|
||||||
get {
|
get {
|
||||||
return rootController
|
return _rootController
|
||||||
}
|
}
|
||||||
set(value) {
|
set(value) {
|
||||||
if let rootController = self.rootController {
|
if let rootController = self._rootController {
|
||||||
rootController.view.removeFromSuperview()
|
rootController.view.removeFromSuperview()
|
||||||
}
|
}
|
||||||
self.rootController = value
|
self._rootController = value
|
||||||
|
|
||||||
if let rootController = self.rootController {
|
if let rootController = self._rootController {
|
||||||
rootController.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: .immediate)
|
rootController.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: .immediate)
|
||||||
|
|
||||||
self.addSubview(rootController.view)
|
self.addSubview(rootController.view)
|
||||||
@ -343,14 +362,13 @@ public class Window: UIWindow {
|
|||||||
}
|
}
|
||||||
self.windowLayout = WindowLayout(size: updatingLayout.layout.size, statusBarHeight: statusBarHeight, inputHeight: updatingLayout.layout.inputHeight)
|
self.windowLayout = WindowLayout(size: updatingLayout.layout.size, statusBarHeight: statusBarHeight, inputHeight: updatingLayout.layout.inputHeight)
|
||||||
|
|
||||||
self.rootController?.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: updatingLayout.transition)
|
self._rootController?.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: updatingLayout.transition)
|
||||||
|
|
||||||
self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: updatingLayout.transition)
|
self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: updatingLayout.transition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func present(_ controller: ViewController) {
|
public func present(_ controller: ViewController) {
|
||||||
self.presentationContext.present(controller)
|
self.presentationContext.present(controller)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user