mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-01-04 04:05:00 +00:00
no message
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
D02383861DE0E3B4004018B6 /* ListViewIntermediateState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02383851DE0E3B4004018B6 /* ListViewIntermediateState.swift */; };
|
||||
D02958001D6F096000360E5E /* ContextMenuContainerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02957FF1D6F096000360E5E /* ContextMenuContainerNode.swift */; };
|
||||
D02BDB021B6AC703008AFAD2 /* RuntimeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02BDB011B6AC703008AFAD2 /* RuntimeUtils.swift */; };
|
||||
D036574B1E71C44D00BB1EE4 /* MinimizeKeyboardGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036574A1E71C44D00BB1EE4 /* MinimizeKeyboardGestureRecognizer.swift */; };
|
||||
D03725C11D6DF594007FC290 /* ContextMenuNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03725C01D6DF594007FC290 /* ContextMenuNode.swift */; };
|
||||
D03725C31D6DF7A6007FC290 /* ContextMenuAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03725C21D6DF7A6007FC290 /* ContextMenuAction.swift */; };
|
||||
D03725C51D6DF8B9007FC290 /* ContextMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03725C41D6DF8B9007FC290 /* ContextMenuController.swift */; };
|
||||
@@ -116,6 +117,7 @@
|
||||
D0E49C881B83A3580099E553 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E49C871B83A3580099E553 /* ImageCache.swift */; };
|
||||
D0F1132F1D6F3C20008C3597 /* ContextMenuActionNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F1132E1D6F3C20008C3597 /* ContextMenuActionNode.swift */; };
|
||||
D0F7AB371DCFF6F8009AD9A1 /* ListViewItemHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F7AB361DCFF6F8009AD9A1 /* ListViewItemHeader.swift */; };
|
||||
D0FF9B301E7196F6000C66DB /* KeyboardManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FF9B2F1E7196F6000C66DB /* KeyboardManager.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -145,6 +147,7 @@
|
||||
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>"; };
|
||||
D02BDB011B6AC703008AFAD2 /* RuntimeUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RuntimeUtils.swift; sourceTree = "<group>"; };
|
||||
D036574A1E71C44D00BB1EE4 /* MinimizeKeyboardGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinimizeKeyboardGestureRecognizer.swift; sourceTree = "<group>"; };
|
||||
D03725C01D6DF594007FC290 /* ContextMenuNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextMenuNode.swift; sourceTree = "<group>"; };
|
||||
D03725C21D6DF7A6007FC290 /* ContextMenuAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextMenuAction.swift; sourceTree = "<group>"; };
|
||||
D03725C41D6DF8B9007FC290 /* ContextMenuController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextMenuController.swift; sourceTree = "<group>"; };
|
||||
@@ -242,6 +245,7 @@
|
||||
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>"; };
|
||||
D0F7AB361DCFF6F8009AD9A1 /* ListViewItemHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListViewItemHeader.swift; sourceTree = "<group>"; };
|
||||
D0FF9B2F1E7196F6000C66DB /* KeyboardManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardManager.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -397,6 +401,7 @@
|
||||
D03BCCE91C72AE4B0097A291 /* Theme */,
|
||||
D05CC3001B6955D500E235A3 /* Utils */,
|
||||
D07921AA1B6FC911005C23D9 /* Status Bar */,
|
||||
D0FF9B2E1E7196E2000C66DB /* Keyboard */,
|
||||
D05CC3211B695AA600E235A3 /* Navigation */,
|
||||
D0DC48521BF93D7C00F672FD /* Tabs */,
|
||||
D02BDAEC1B6A7053008AFAD2 /* Nodes */,
|
||||
@@ -584,6 +589,15 @@
|
||||
name = "Image Cache";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D0FF9B2E1E7196E2000C66DB /* Keyboard */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D0FF9B2F1E7196F6000C66DB /* KeyboardManager.swift */,
|
||||
D036574A1E71C44D00BB1EE4 /* MinimizeKeyboardGestureRecognizer.swift */,
|
||||
);
|
||||
name = Keyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
@@ -770,6 +784,7 @@
|
||||
D0DA44521E4DCC11005FDCA7 /* TextAlertController.swift in Sources */,
|
||||
D081229D1D19AA1C005F7395 /* ContainerViewLayout.swift in Sources */,
|
||||
D0C2DFC71CC4431D0044FF83 /* ListViewItemNode.swift in Sources */,
|
||||
D0FF9B301E7196F6000C66DB /* KeyboardManager.swift in Sources */,
|
||||
D01E2BE21D9049F60066BF65 /* GridItemNode.swift in Sources */,
|
||||
D08E903A1D24159200533158 /* ActionSheetItem.swift in Sources */,
|
||||
D0AE2CA61C94548900F2FD3C /* GenerateImage.swift in Sources */,
|
||||
@@ -784,6 +799,7 @@
|
||||
D05BE4AE1D217F6B002BD72C /* MergedLayoutEvents.swift in Sources */,
|
||||
D0C0D2901C997110001D2851 /* FBAnimationPerformanceTracker.mm in Sources */,
|
||||
D015F7521D1AE08D00E269B5 /* ContainableController.swift in Sources */,
|
||||
D036574B1E71C44D00BB1EE4 /* MinimizeKeyboardGestureRecognizer.swift in Sources */,
|
||||
D05CC2FE1B6955D000E235A3 /* UIWindow+OrientationChange.m in Sources */,
|
||||
D0C85DD41D1C1E6A00124894 /* ActionSheetItemGroupNode.swift in Sources */,
|
||||
D08E903E1D24187900533158 /* ActionSheetItemGroup.swift in Sources */,
|
||||
|
||||
@@ -145,7 +145,7 @@ final class ActionSheetItemGroupNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
if !self.scrollView.contentSize.equalTo(scrollViewContentSize) {
|
||||
self.scrollView.contentSize = scrollViewContentSize
|
||||
}
|
||||
var scrollViewContentInsets = UIEdgeInsets(top: max(0.0, self.calculatedSize.height - leadingVisibleNodeSize), left: 0.0, bottom: 0.0, right: 0.0)
|
||||
let scrollViewContentInsets = UIEdgeInsets(top: max(0.0, self.calculatedSize.height - leadingVisibleNodeSize), left: 0.0, bottom: 0.0, right: 0.0)
|
||||
|
||||
if !UIEdgeInsetsEqualToEdgeInsets(self.scrollView.contentInset, scrollViewContentInsets) {
|
||||
self.scrollView.contentInset = scrollViewContentInsets
|
||||
|
||||
@@ -45,7 +45,7 @@ open class AlertController: ViewController {
|
||||
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
|
||||
}
|
||||
|
||||
public func dismiss() {
|
||||
override open func dismiss() {
|
||||
self.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -207,8 +207,8 @@ public extension CALayer {
|
||||
self.animate(from: NSValue(cgRect: from), to: NSValue(cgRect: to), keyPath: "bounds", timingFunction: timingFunction, duration: duration, removeOnCompletion: removeOnCompletion, additive: additive, completion: completion)
|
||||
}
|
||||
|
||||
public func animateBoundsOriginYAdditive(from: CGFloat, to: CGFloat, duration: Double, timingFunction: String = kCAMediaTimingFunctionEaseInEaseOut) {
|
||||
self.animate(from: from as NSNumber, to: to as NSNumber, keyPath: "bounds.origin.y", timingFunction: timingFunction, duration: duration, additive: true)
|
||||
public func animateBoundsOriginYAdditive(from: CGFloat, to: CGFloat, duration: Double, timingFunction: String = kCAMediaTimingFunctionEaseInEaseOut, removeOnCompletion: Bool = true, completion: ((Bool) -> Void)? = nil) {
|
||||
self.animate(from: from as NSNumber, to: to as NSNumber, keyPath: "bounds.origin.y", timingFunction: timingFunction, duration: duration, removeOnCompletion: removeOnCompletion, additive: true, completion: completion)
|
||||
}
|
||||
|
||||
public func animateBoundsOriginYAdditive(from: CGFloat, to: CGFloat, duration: Double, mediaTimingFunction: CAMediaTimingFunction) {
|
||||
@@ -251,4 +251,13 @@ public extension CALayer {
|
||||
partialCompletion()
|
||||
})
|
||||
}
|
||||
|
||||
public func cancelAnimationsRecursive(key: String) {
|
||||
self.removeAnimation(forKey: key)
|
||||
if let sublayers = self.sublayers {
|
||||
for layer in sublayers {
|
||||
layer.cancelAnimationsRecursive(key: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,16 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface CATracingLayerInfo : NSObject
|
||||
|
||||
@property (nonatomic, readonly) bool shouldBeAdjustedToInverseTransform;
|
||||
@property (nonatomic, weak, readonly) id _Nullable userData;
|
||||
@property (nonatomic, readonly) int32_t tracingTag;
|
||||
|
||||
- (instancetype _Nonnull)initWithShouldBeAdjustedToInverseTransform:(bool)shouldBeAdjustedToInverseTransform userData:(id _Nullable)userData tracingTag:(int32_t)tracingTag;
|
||||
|
||||
@end
|
||||
|
||||
@interface UITracingLayerView : UIView
|
||||
|
||||
- (void)scheduleWithLayout:(void (^_Nonnull)())block;
|
||||
@@ -12,15 +22,18 @@
|
||||
|
||||
@interface CALayer (Tracing)
|
||||
|
||||
- (id _Nullable)traceableInfo;
|
||||
- (void)setTraceableInfo:(id _Nullable)info;
|
||||
- (CATracingLayerInfo * _Nullable)traceableInfo;
|
||||
- (void)setTraceableInfo:(CATracingLayerInfo * _Nullable)info;
|
||||
|
||||
- (bool)hasPositionOrOpacityAnimations;
|
||||
- (bool)hasPositionAnimations;
|
||||
|
||||
- (void)setInvalidateTracingSublayers:(void (^_Nullable)())block;
|
||||
- (NSArray<NSArray<CALayer *> *> * _Nonnull)traceableLayerSurfaces;
|
||||
- (NSArray<NSArray<CALayer *> *> * _Nonnull)traceableLayerSurfacesWithTag:(int32_t)tracingTag;
|
||||
- (void)adjustTraceableLayerTransforms:(CGSize)offset;
|
||||
|
||||
- (void)setPositionAnimationMirrorTarget:(CALayer * _Nullable)animationMirrorTarget;
|
||||
|
||||
- (void)invalidateUpTheTree;
|
||||
|
||||
@end
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
static void *CATracingLayerInvalidatedKey = &CATracingLayerInvalidatedKey;
|
||||
static void *CATracingLayerIsInvalidatedBlock = &CATracingLayerIsInvalidatedBlock;
|
||||
static void *CATracingLayerTraceablInfoKey = &CATracingLayerTraceablInfoKey;
|
||||
static void *CATracingLayerTraceableInfoKey = &CATracingLayerTraceableInfoKey;
|
||||
static void *CATracingLayerPositionAnimationMirrorTarget = &CATracingLayerPositionAnimationMirrorTarget;
|
||||
|
||||
@implementation CALayer (Tracing)
|
||||
|
||||
@@ -17,25 +18,30 @@ static void *CATracingLayerTraceablInfoKey = &CATracingLayerTraceablInfoKey;
|
||||
}
|
||||
|
||||
- (bool)isTraceable {
|
||||
return [self associatedObjectForKey:CATracingLayerTraceablInfoKey] != nil || [self isKindOfClass:[CATracingLayer class]];
|
||||
return [self associatedObjectForKey:CATracingLayerTraceableInfoKey] != nil || [self isKindOfClass:[CATracingLayer class]];
|
||||
}
|
||||
|
||||
- (id _Nullable)traceableInfo {
|
||||
return [self associatedObjectForKey:CATracingLayerTraceablInfoKey];
|
||||
- (CATracingLayerInfo * _Nullable)traceableInfo {
|
||||
return [self associatedObjectForKey:CATracingLayerTraceableInfoKey];
|
||||
}
|
||||
|
||||
- (void)setTraceableInfo:(id _Nullable)info {
|
||||
[self setAssociatedObject:info forKey:CATracingLayerTraceablInfoKey];
|
||||
- (void)setTraceableInfo:(CATracingLayerInfo * _Nullable)info {
|
||||
[self setAssociatedObject:info forKey:CATracingLayerTraceableInfoKey];
|
||||
}
|
||||
|
||||
- (bool)hasPositionOrOpacityAnimations {
|
||||
return [self animationForKey:@"position"] != nil || [self animationForKey:@"bounds"] != nil || [self animationForKey:@"sublayerTransform"] != nil || [self animationForKey:@"opacity"] != nil;
|
||||
}
|
||||
|
||||
static void traceLayerSurfaces(int depth, CALayer * _Nonnull layer, NSMutableDictionary<NSNumber *, NSMutableArray<CALayer *> *> *layersByDepth) {
|
||||
- (bool)hasPositionAnimations {
|
||||
return [self animationForKey:@"position"] != nil || [self animationForKey:@"bounds"] != nil;
|
||||
}
|
||||
|
||||
static void traceLayerSurfaces(int32_t tracingTag, int depth, CALayer * _Nonnull layer, NSMutableDictionary<NSNumber *, NSMutableArray<CALayer *> *> *layersByDepth) {
|
||||
bool hadTraceableSublayers = false;
|
||||
for (CALayer *sublayer in layer.sublayers.reverseObjectEnumerator) {
|
||||
if ([sublayer traceableInfo] != nil) {
|
||||
CATracingLayerInfo *sublayerTraceableInfo = [sublayer traceableInfo];
|
||||
if (sublayerTraceableInfo != nil && sublayerTraceableInfo.tracingTag == tracingTag) {
|
||||
NSMutableArray *array = layersByDepth[@(depth)];
|
||||
if (array == nil) {
|
||||
array = [[NSMutableArray alloc] init];
|
||||
@@ -49,16 +55,16 @@ static void traceLayerSurfaces(int depth, CALayer * _Nonnull layer, NSMutableDic
|
||||
if (!hadTraceableSublayers) {
|
||||
for (CALayer *sublayer in layer.sublayers.reverseObjectEnumerator) {
|
||||
if ([sublayer isKindOfClass:[CATracingLayer class]]) {
|
||||
traceLayerSurfaces(depth + 1, sublayer, layersByDepth);
|
||||
traceLayerSurfaces(tracingTag, depth + 1, sublayer, layersByDepth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray<NSArray<CALayer *> *> *)traceableLayerSurfaces {
|
||||
- (NSArray<NSArray<CALayer *> *> * _Nonnull)traceableLayerSurfacesWithTag:(int32_t)tracingTag {
|
||||
NSMutableDictionary<NSNumber *, NSMutableArray<CALayer *> *> *layersByDepth = [[NSMutableDictionary alloc] init];
|
||||
|
||||
traceLayerSurfaces(0, self, layersByDepth);
|
||||
traceLayerSurfaces(tracingTag, 0, self, layersByDepth);
|
||||
|
||||
NSMutableArray<NSMutableArray<CALayer *> *> *result = [[NSMutableArray alloc] init];
|
||||
|
||||
@@ -73,7 +79,8 @@ static void traceLayerSurfaces(int depth, CALayer * _Nonnull layer, NSMutableDic
|
||||
CGRect frame = self.frame;
|
||||
CGSize sublayerOffset = CGSizeMake(frame.origin.x + offset.width, frame.origin.y + offset.height);
|
||||
for (CALayer *sublayer in self.sublayers) {
|
||||
if ([sublayer traceableInfo] != nil) {
|
||||
CATracingLayerInfo *sublayerTraceableInfo = [sublayer traceableInfo];
|
||||
if (sublayerTraceableInfo != nil && sublayerTraceableInfo.shouldBeAdjustedToInverseTransform) {
|
||||
sublayer.sublayerTransform = CATransform3DMakeTranslation(-sublayerOffset.width, -sublayerOffset.height, 0.0f);
|
||||
} else if ([sublayer isKindOfClass:[CATracingLayer class]]) {
|
||||
[(CATracingLayer *)sublayer adjustTraceableLayerTransforms:sublayerOffset];
|
||||
@@ -81,6 +88,14 @@ static void traceLayerSurfaces(int depth, CALayer * _Nonnull layer, NSMutableDic
|
||||
}
|
||||
}
|
||||
|
||||
- (CALayer * _Nullable)animationMirrorTarget {
|
||||
return [self associatedObjectForKey:CATracingLayerPositionAnimationMirrorTarget];
|
||||
}
|
||||
|
||||
- (void)setPositionAnimationMirrorTarget:(CALayer * _Nullable)animationMirrorTarget {
|
||||
[self setAssociatedObject:animationMirrorTarget forKey:CATracingLayerPositionAnimationMirrorTarget associationPolicy:NSObjectAssociationPolicyRetain];
|
||||
}
|
||||
|
||||
- (void)invalidateUpTheTree {
|
||||
CALayer *superlayer = self;
|
||||
while (true) {
|
||||
@@ -235,9 +250,21 @@ static void traceLayerSurfaces(int depth, CALayer * _Nonnull layer, NSMutableDic
|
||||
|
||||
[super addAnimation:anim forKey:key];
|
||||
|
||||
CABasicAnimation *positionAnimCopy = [animCopy copy];
|
||||
positionAnimCopy.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(-to.x + from.x, 0.0, 0.0f)];
|
||||
positionAnimCopy.toValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
|
||||
positionAnimCopy.additive = true;
|
||||
positionAnimCopy.delegate = [[CATracingLayerAnimationDelegate alloc] initWithDelegate:anim.delegate animationStopped:^{
|
||||
__strong CATracingLayer *strongSelf = weakSelf;
|
||||
if (strongSelf != nil) {
|
||||
[strongSelf invalidateUpTheTree];
|
||||
}
|
||||
}];
|
||||
|
||||
[self invalidateUpTheTree];
|
||||
|
||||
[self mirrorAnimationDownTheTree:animCopy key:@"sublayerTransform"];
|
||||
[self mirrorPositionAnimationDownTheTree:positionAnimCopy key:@"sublayerTransform"];
|
||||
} else if ([key isEqualToString:@"opacity"]) {
|
||||
__weak CATracingLayer *weakSelf = self;
|
||||
anim.delegate = [[CATracingLayerAnimationDelegate alloc] initWithDelegate:anim.delegate animationStopped:^{
|
||||
@@ -258,11 +285,25 @@ static void traceLayerSurfaces(int depth, CALayer * _Nonnull layer, NSMutableDic
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mirrorPositionAnimationDownTheTree:(CAAnimation *)animation key:(NSString *)key {
|
||||
if ([animation isKindOfClass:[CABasicAnimation class]]) {
|
||||
if ([((CABasicAnimation *)animation).keyPath isEqualToString:@"sublayerTransform"]) {
|
||||
CALayer *positionAnimationMirrorTarget = [self animationMirrorTarget];
|
||||
if (positionAnimationMirrorTarget != nil) {
|
||||
[positionAnimationMirrorTarget addAnimation:[animation copy] forKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mirrorAnimationDownTheTree:(CAAnimation *)animation key:(NSString *)key {
|
||||
for (CALayer *sublayer in self.sublayers) {
|
||||
if ([sublayer traceableInfo] != nil) {
|
||||
CATracingLayerInfo *traceableInfo = [sublayer traceableInfo];
|
||||
if (traceableInfo != nil && traceableInfo.shouldBeAdjustedToInverseTransform) {
|
||||
[sublayer addAnimation:[animation copy] forKey:key];
|
||||
} else if ([sublayer isKindOfClass:[CATracingLayer class]]) {
|
||||
}
|
||||
|
||||
if ([sublayer isKindOfClass:[CATracingLayer class]]) {
|
||||
[(CATracingLayer *)sublayer mirrorAnimationDownTheTree:animation key:key];
|
||||
}
|
||||
}
|
||||
@@ -270,6 +311,20 @@ static void traceLayerSurfaces(int depth, CALayer * _Nonnull layer, NSMutableDic
|
||||
|
||||
@end
|
||||
|
||||
@implementation CATracingLayerInfo
|
||||
|
||||
- (instancetype _Nonnull)initWithShouldBeAdjustedToInverseTransform:(bool)shouldBeAdjustedToInverseTransform userData:(id _Nullable)userData tracingTag:(int32_t)tracingTag {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_shouldBeAdjustedToInverseTransform = shouldBeAdjustedToInverseTransform;
|
||||
_userData = userData;
|
||||
_tracingTag = tracingTag;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface UITracingLayerView () {
|
||||
void (^_scheduledWithLayout)();
|
||||
}
|
||||
|
||||
@@ -48,6 +48,10 @@ public struct ContainerViewLayout: Equatable {
|
||||
public func addedInsets(insets: UIEdgeInsets) -> ContainerViewLayout {
|
||||
return ContainerViewLayout(size: self.size, intrinsicInsets: UIEdgeInsets(top: self.intrinsicInsets.top + insets.top, left: self.intrinsicInsets.left + insets.left, bottom: self.intrinsicInsets.bottom + insets.bottom, right: self.intrinsicInsets.right + insets.right), statusBarHeight: self.statusBarHeight, inputHeight: self.inputHeight)
|
||||
}
|
||||
|
||||
public func withUpdatedInputHeight(_ inputHeight: CGFloat?) -> ContainerViewLayout {
|
||||
return ContainerViewLayout(size: self.size, intrinsicInsets: self.intrinsicInsets, statusBarHeight: self.statusBarHeight, inputHeight: inputHeight)
|
||||
}
|
||||
}
|
||||
|
||||
public func ==(lhs: ContainerViewLayout, rhs: ContainerViewLayout) -> Bool {
|
||||
|
||||
@@ -27,7 +27,7 @@ final class ContextMenuNode: ASDisplayNode {
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
let dismissNode = { [weak self] in
|
||||
let dismissNode = {
|
||||
dismiss()
|
||||
}
|
||||
for actionNode in self.actionNodes {
|
||||
|
||||
@@ -16,7 +16,7 @@ public struct Font {
|
||||
|
||||
public static func bold(_ size: CGFloat) -> UIFont {
|
||||
if #available(iOS 8.2, *) {
|
||||
return UIFont.systemFont(ofSize: size, weight: UIFontWeightBold)
|
||||
return UIFont.boldSystemFont(ofSize: size)
|
||||
} else {
|
||||
return CTFontCreateWithName("HelveticaNeue-Bold" as CFString, size, nil)
|
||||
}
|
||||
|
||||
@@ -114,6 +114,25 @@ public func generateFilledCircleImage(diameter: CGFloat, color: UIColor?, backgr
|
||||
})
|
||||
}
|
||||
|
||||
public func generateCircleImage(diameter: CGFloat, lineWidth: CGFloat, color: UIColor?, backgroundColor: UIColor? = nil) -> UIImage? {
|
||||
return generateImage(CGSize(width: diameter, height: diameter), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
if let backgroundColor = backgroundColor {
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
}
|
||||
|
||||
if let color = color {
|
||||
context.setStrokeColor(color.cgColor)
|
||||
} else {
|
||||
context.setStrokeColor(UIColor.clear.cgColor)
|
||||
context.setBlendMode(.copy)
|
||||
}
|
||||
context.setLineWidth(lineWidth)
|
||||
context.strokeEllipse(in: CGRect(origin: CGPoint(x: lineWidth / 2.0, y: lineWidth / 2.0), size: CGSize(width: size.width - lineWidth, height: size.height - lineWidth)))
|
||||
})
|
||||
}
|
||||
|
||||
public func generateStretchableFilledCircleImage(radius: CGFloat, color: UIColor?, backgroundColor: UIColor? = nil) -> UIImage? {
|
||||
let intRadius = Int(radius)
|
||||
let cap = intRadius == 1 ? 2 : intRadius
|
||||
@@ -283,7 +302,7 @@ public class DrawingContext {
|
||||
}
|
||||
|
||||
public func blt(_ other: DrawingContext, at: CGPoint, mode: DrawingContextBltMode = .Alpha) {
|
||||
if abs(other.scale - self.scale) < CGFloat(FLT_EPSILON) {
|
||||
if abs(other.scale - self.scale) < CGFloat.ulpOfOne {
|
||||
let srcX = 0
|
||||
var srcY = 0
|
||||
let dstX = Int(at.x * self.scale)
|
||||
|
||||
@@ -35,13 +35,15 @@ public struct GridNodeScrollToItem {
|
||||
public let transition: ContainedViewLayoutTransition
|
||||
public let directionHint: GridNodePreviousItemsTransitionDirectionHint
|
||||
public let adjustForSection: Bool
|
||||
public let adjustForTopInset: Bool
|
||||
|
||||
public init(index: Int, position: GridNodeScrollToItemPosition, transition: ContainedViewLayoutTransition, directionHint: GridNodePreviousItemsTransitionDirectionHint, adjustForSection: Bool) {
|
||||
public init(index: Int, position: GridNodeScrollToItemPosition, transition: ContainedViewLayoutTransition, directionHint: GridNodePreviousItemsTransitionDirectionHint, adjustForSection: Bool, adjustForTopInset: Bool = false) {
|
||||
self.index = index
|
||||
self.position = position
|
||||
self.transition = transition
|
||||
self.directionHint = directionHint
|
||||
self.adjustForSection = adjustForSection
|
||||
self.adjustForTopInset = adjustForTopInset
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +158,12 @@ private struct GridNodePresentationLayoutTransition {
|
||||
let transition: ContainedViewLayoutTransition
|
||||
}
|
||||
|
||||
public struct GridNodeCurrentPresentationLayout {
|
||||
public let layout: GridNodeLayout
|
||||
public let contentOffset: CGPoint
|
||||
public let contentSize: CGSize
|
||||
}
|
||||
|
||||
private final class GridNodeItemLayout {
|
||||
let contentSize: CGSize
|
||||
let items: [GridNodePresentationItem]
|
||||
@@ -225,6 +233,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
private var applyingContentOffset = false
|
||||
|
||||
public var visibleItemsUpdated: ((GridNodeVisibleItems) -> Void)?
|
||||
public var presentationLayoutUpdated: ((GridNodeCurrentPresentationLayout, ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
public override init() {
|
||||
super.init()
|
||||
@@ -241,6 +250,9 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
|
||||
public func transaction(_ transaction: GridNodeTransaction, completion: (GridNodeDisplayedItemRange) -> Void) {
|
||||
if transaction.deleteItems.isEmpty && transaction.insertItems.isEmpty && transaction.scrollToItem == nil && transaction.updateItems.isEmpty && (transaction.updateLayout == nil || transaction.updateLayout!.layout == self.gridLayout && (transaction.updateFirstIndexInSectionOffset == nil || transaction.updateFirstIndexInSectionOffset == self.firstIndexInSectionOffset)) {
|
||||
if let presentationLayoutUpdated = self.presentationLayoutUpdated {
|
||||
presentationLayoutUpdated(GridNodeCurrentPresentationLayout(layout: self.gridLayout, contentOffset: self.scrollView.contentOffset, contentSize: self.itemLayout.contentSize), .immediate)
|
||||
}
|
||||
completion(self.displayedItemRange())
|
||||
return
|
||||
}
|
||||
@@ -249,7 +261,9 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
self.firstIndexInSectionOffset = updateFirstIndexInSectionOffset
|
||||
}
|
||||
|
||||
var layoutTransactionOffset: CGFloat = 0.0
|
||||
if let updateLayout = transaction.updateLayout {
|
||||
layoutTransactionOffset += updateLayout.layout.insets.top - self.gridLayout.insets.top
|
||||
self.gridLayout = updateLayout.layout
|
||||
}
|
||||
|
||||
@@ -316,7 +330,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
self.itemNodes = remappedInsertionItemNodes
|
||||
}
|
||||
|
||||
var previousLayoutWasEmpty = self.itemLayout.items.isEmpty
|
||||
let previousLayoutWasEmpty = self.itemLayout.items.isEmpty
|
||||
|
||||
self.itemLayout = self.generateItemLayout()
|
||||
|
||||
@@ -324,19 +338,19 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
if let scrollToItem = transaction.scrollToItem {
|
||||
generatedScrollToItem = scrollToItem
|
||||
} else if previousLayoutWasEmpty {
|
||||
generatedScrollToItem = GridNodeScrollToItem(index: 0, position: .top, transition: .immediate, directionHint: .up, adjustForSection: true)
|
||||
generatedScrollToItem = GridNodeScrollToItem(index: 0, position: .top, transition: .immediate, directionHint: .up, adjustForSection: true, adjustForTopInset: true)
|
||||
} else {
|
||||
generatedScrollToItem = nil
|
||||
}
|
||||
|
||||
self.applyPresentaionLayoutTransition(self.generatePresentationLayoutTransition(stationaryItems: transaction.stationaryItems, scrollToItem: generatedScrollToItem), removedNodes: removedNodes)
|
||||
self.applyPresentaionLayoutTransition(self.generatePresentationLayoutTransition(stationaryItems: transaction.stationaryItems, layoutTransactionOffset: layoutTransactionOffset, scrollToItem: generatedScrollToItem), removedNodes: removedNodes, updateLayoutTransition: transaction.updateLayout?.transition)
|
||||
|
||||
completion(self.displayedItemRange())
|
||||
}
|
||||
|
||||
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if !self.applyingContentOffset {
|
||||
self.applyPresentaionLayoutTransition(self.generatePresentationLayoutTransition(), removedNodes: [])
|
||||
self.applyPresentaionLayoutTransition(self.generatePresentationLayoutTransition(layoutTransactionOffset: 0.0), removedNodes: [], updateLayoutTransition: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,7 +441,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func generatePresentationLayoutTransition(stationaryItems: GridNodeStationaryItems = .none, scrollToItem: GridNodeScrollToItem? = nil) -> GridNodePresentationLayoutTransition {
|
||||
private func generatePresentationLayoutTransition(stationaryItems: GridNodeStationaryItems = .none, layoutTransactionOffset: CGFloat, scrollToItem: GridNodeScrollToItem? = nil) -> GridNodePresentationLayoutTransition {
|
||||
if CGFloat(0.0).isLess(than: gridLayout.size.width) && CGFloat(0.0).isLess(than: gridLayout.size.height) && !self.itemLayout.items.isEmpty {
|
||||
var transitionDirectionHint: GridNodePreviousItemsTransitionDirectionHint = .up
|
||||
var transition: ContainedViewLayoutTransition = .immediate
|
||||
@@ -438,7 +452,9 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
let itemFrame = self.itemLayout.items[scrollToItem.index]
|
||||
|
||||
var additionalOffset: CGFloat = 0.0
|
||||
if scrollToItem.adjustForSection {
|
||||
if scrollToItem.adjustForTopInset {
|
||||
additionalOffset = -gridLayout.insets.top
|
||||
} else if scrollToItem.adjustForSection {
|
||||
var adjustForSection: GridSection?
|
||||
if scrollToItem.index == 0 {
|
||||
if let itemSection = self.items[scrollToItem.index].section {
|
||||
@@ -485,7 +501,18 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
|
||||
contentOffset = CGPoint(x: 0.0, y: verticalOffset)
|
||||
} else {
|
||||
contentOffset = self.scrollView.contentOffset
|
||||
if !layoutTransactionOffset.isZero {
|
||||
var verticalOffset = self.scrollView.contentOffset.y - layoutTransactionOffset
|
||||
if verticalOffset > self.itemLayout.contentSize.height + self.gridLayout.insets.bottom - self.gridLayout.size.height {
|
||||
verticalOffset = self.itemLayout.contentSize.height + self.gridLayout.insets.bottom - self.gridLayout.size.height
|
||||
}
|
||||
if verticalOffset < -self.gridLayout.insets.top {
|
||||
verticalOffset = -self.gridLayout.insets.top
|
||||
}
|
||||
contentOffset = CGPoint(x: 0.0, y: verticalOffset)
|
||||
} else {
|
||||
contentOffset = self.scrollView.contentOffset
|
||||
}
|
||||
}
|
||||
case let .indices(stationaryItemIndices):
|
||||
var selectedContentOffset: CGPoint?
|
||||
@@ -548,7 +575,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func applyPresentaionLayoutTransition(_ presentationLayoutTransition: GridNodePresentationLayoutTransition, removedNodes: [GridItemNode]) {
|
||||
private func applyPresentaionLayoutTransition(_ presentationLayoutTransition: GridNodePresentationLayoutTransition, removedNodes: [GridItemNode], updateLayoutTransition: ContainedViewLayoutTransition?) {
|
||||
var previousItemFrames: ([WrappedGridItemNode: CGRect])?
|
||||
switch presentationLayoutTransition.transition {
|
||||
case .animated:
|
||||
@@ -783,6 +810,10 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
visibleItemsUpdated(GridNodeVisibleItems(top: nil, bottom: nil, topVisible: nil, bottomVisible: nil, topSectionVisible: nil, count: self.items.count))
|
||||
}
|
||||
}
|
||||
|
||||
if let presentationLayoutUpdated = self.presentationLayoutUpdated {
|
||||
presentationLayoutUpdated(GridNodeCurrentPresentationLayout(layout: presentationLayoutTransition.layout.layout, contentOffset: presentationLayoutTransition.layout.contentOffset, contentSize: presentationLayoutTransition.layout.contentSize), updateLayoutTransition ?? presentationLayoutTransition.transition)
|
||||
}
|
||||
}
|
||||
|
||||
private func addItemNode(index: Int, itemNode: GridItemNode) {
|
||||
@@ -817,9 +848,28 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
public func forEachItemNode(_ f: @noescape(ASDisplayNode) -> Void) {
|
||||
public func forEachItemNode(_ f: (ASDisplayNode) -> Void) {
|
||||
for (_, node) in self.itemNodes {
|
||||
f(node)
|
||||
}
|
||||
}
|
||||
|
||||
public func forEachRow(_ f: ([ASDisplayNode]) -> Void) {
|
||||
var row: [ASDisplayNode] = []
|
||||
var previousMinY: CGFloat?
|
||||
for index in self.itemNodes.keys.sorted() {
|
||||
let itemNode = self.itemNodes[index]!
|
||||
if let previousMinY = previousMinY, !previousMinY.isEqual(to: itemNode.frame.minY) {
|
||||
if !row.isEmpty {
|
||||
f(row)
|
||||
row.removeAll()
|
||||
}
|
||||
}
|
||||
previousMinY = itemNode.frame.minY
|
||||
row.append(itemNode)
|
||||
}
|
||||
if !row.isEmpty {
|
||||
f(row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
147
Display/KeyboardManager.swift
Normal file
147
Display/KeyboardManager.swift
Normal file
@@ -0,0 +1,147 @@
|
||||
import Foundation
|
||||
import AsyncDisplayKit
|
||||
|
||||
struct KeyboardSurface {
|
||||
let host: UIView
|
||||
}
|
||||
|
||||
private func hasFirstResponder(_ view: UIView) -> Bool {
|
||||
if view.isFirstResponder {
|
||||
return true
|
||||
} else {
|
||||
for subview in view.subviews {
|
||||
if hasFirstResponder(subview) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private func findKeyboardBackdrop(_ view: UIView) -> UIView? {
|
||||
if NSStringFromClass(type(of: view)) == "UIKBInputBackdropView" {
|
||||
return view
|
||||
}
|
||||
for subview in view.subviews {
|
||||
if let result = findKeyboardBackdrop(subview) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
class KeyboardManager {
|
||||
private let host: StatusBarHost
|
||||
|
||||
private weak var previousPositionAnimationMirrorSource: CATracingLayer?
|
||||
private weak var previousFirstResponderView: UIView?
|
||||
|
||||
var gestureRecognizer: MinimizeKeyboardGestureRecognizer? = nil
|
||||
|
||||
var minimized: Bool = false
|
||||
var minimizedUpdated: (() -> Void)?
|
||||
|
||||
var updatedMinimizedBackdrop = false
|
||||
|
||||
var surfaces: [KeyboardSurface] = [] {
|
||||
didSet {
|
||||
self.updateSurfaces(oldValue)
|
||||
}
|
||||
}
|
||||
|
||||
init(host: StatusBarHost) {
|
||||
self.host = host
|
||||
}
|
||||
|
||||
private func updateSurfaces(_ previousSurfaces: [KeyboardSurface]) {
|
||||
guard let keyboardWindow = self.host.keyboardWindow else {
|
||||
return
|
||||
}
|
||||
|
||||
if let keyboardView = self.host.keyboardView {
|
||||
if self.minimized {
|
||||
let normalizedHeight = floor(0.85 * keyboardView.frame.size.height)
|
||||
let factor = normalizedHeight / keyboardView.frame.size.height
|
||||
let scaleTransform = CATransform3DMakeScale(factor, factor, 1.0)
|
||||
let horizontalOffset = (keyboardView.frame.size.width - keyboardView.frame.size.width * factor) / 2.0
|
||||
let verticalOffset = (keyboardView.frame.size.height - keyboardView.frame.size.height * factor) / 2.0
|
||||
let translate = CATransform3DMakeTranslation(horizontalOffset, verticalOffset, 0.0)
|
||||
keyboardView.layer.sublayerTransform = CATransform3DConcat(scaleTransform, translate)
|
||||
|
||||
self.updatedMinimizedBackdrop = false
|
||||
|
||||
if let backdrop = findKeyboardBackdrop(keyboardView) {
|
||||
let scale = CATransform3DMakeScale(1.0 / factor, 1.0, 0.0)
|
||||
let translate = CATransform3DMakeTranslation(-horizontalOffset * (1.0 / factor), 0.0, 0.0)
|
||||
backdrop.layer.sublayerTransform = CATransform3DConcat(scale, translate)
|
||||
}
|
||||
} else {
|
||||
keyboardView.layer.sublayerTransform = CATransform3DIdentity
|
||||
if !self.updatedMinimizedBackdrop {
|
||||
if let backdrop = findKeyboardBackdrop(keyboardView) {
|
||||
backdrop.layer.sublayerTransform = CATransform3DIdentity
|
||||
}
|
||||
|
||||
self.updatedMinimizedBackdrop = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let gestureRecognizer = self.gestureRecognizer {
|
||||
if keyboardWindow.gestureRecognizers == nil || !keyboardWindow.gestureRecognizers!.contains(gestureRecognizer) {
|
||||
keyboardWindow.addGestureRecognizer(gestureRecognizer)
|
||||
}
|
||||
} else {
|
||||
let gestureRecognizer = MinimizeKeyboardGestureRecognizer(target: self, action: #selector(self.minimizeGesture(_:)))
|
||||
self.gestureRecognizer = gestureRecognizer
|
||||
keyboardWindow.addGestureRecognizer(gestureRecognizer)
|
||||
}
|
||||
|
||||
var firstResponderView: UIView?
|
||||
for surface in surfaces {
|
||||
if hasFirstResponder(surface.host) {
|
||||
firstResponderView = surface.host
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if let firstResponderView = firstResponderView {
|
||||
let containerOrigin = firstResponderView.convert(CGPoint(), to: nil)
|
||||
let horizontalTranslation = CATransform3DMakeTranslation(containerOrigin.x, 0.0, 0.0)
|
||||
keyboardWindow.layer.sublayerTransform = horizontalTranslation
|
||||
if let tracingLayer = firstResponderView.layer as? CATracingLayer {
|
||||
if let previousPositionAnimationMirrorSource = self.previousPositionAnimationMirrorSource, previousPositionAnimationMirrorSource !== tracingLayer {
|
||||
previousPositionAnimationMirrorSource.setPositionAnimationMirrorTarget(nil)
|
||||
}
|
||||
tracingLayer.setPositionAnimationMirrorTarget(keyboardWindow.layer)
|
||||
self.previousPositionAnimationMirrorSource = tracingLayer
|
||||
} else if let previousPositionAnimationMirrorSource = self.previousPositionAnimationMirrorSource {
|
||||
previousPositionAnimationMirrorSource.setPositionAnimationMirrorTarget(nil)
|
||||
self.previousPositionAnimationMirrorSource = nil
|
||||
}
|
||||
} else {
|
||||
keyboardWindow.layer.sublayerTransform = CATransform3DIdentity
|
||||
if let previousPositionAnimationMirrorSource = self.previousPositionAnimationMirrorSource {
|
||||
previousPositionAnimationMirrorSource.setPositionAnimationMirrorTarget(nil)
|
||||
self.previousPositionAnimationMirrorSource = nil
|
||||
}
|
||||
if let previousFirstResponderView = previousFirstResponderView {
|
||||
if previousFirstResponderView.window == nil {
|
||||
keyboardWindow.isHidden = true
|
||||
keyboardWindow.layer.cancelAnimationsRecursive(key: "position")
|
||||
keyboardWindow.layer.cancelAnimationsRecursive(key: "bounds")
|
||||
keyboardWindow.isHidden = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.previousFirstResponderView = firstResponderView
|
||||
}
|
||||
|
||||
@objc func minimizeGesture(_ recognizer: UISwipeGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.minimized = !self.minimized
|
||||
self.minimizedUpdated?()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ open class LegacyPresentedController: ViewController {
|
||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition)
|
||||
}
|
||||
|
||||
public func dismiss() {
|
||||
override open func dismiss() {
|
||||
switch self.presentation {
|
||||
case .modal:
|
||||
self.controllerNode.animateModalOut { [weak self] in
|
||||
|
||||
@@ -118,6 +118,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
public final var stackFromBottomInsetItemFactor: CGFloat = 0.0
|
||||
public final var limitHitTestToNodes: Bool = false
|
||||
public final var keepBottomItemOverscrollBackground: Bool = false
|
||||
public final var snapToBottomInsetUntilFirstInteraction: Bool = false
|
||||
|
||||
private var bottomItemOverscrollBackground: ASDisplayNode?
|
||||
|
||||
@@ -359,6 +360,10 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
self.resetHeaderItemsFlashTimer(start: false)
|
||||
self.updateHeaderItemsFlashing(animated: true)
|
||||
|
||||
if self.snapToBottomInsetUntilFirstInteraction {
|
||||
self.snapToBottomInsetUntilFirstInteraction = false
|
||||
}
|
||||
|
||||
/*if usePerformanceTracker {
|
||||
self.performanceTracker.start()
|
||||
}*/
|
||||
@@ -640,7 +645,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
}
|
||||
}
|
||||
|
||||
if abs(offset) > CGFloat(FLT_EPSILON) {
|
||||
if abs(offset) > CGFloat.ulpOfOne {
|
||||
for itemNode in self.itemNodes {
|
||||
var frame = itemNode.frame
|
||||
frame.origin.y += offset
|
||||
@@ -914,7 +919,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
|
||||
let widthUpdated: Bool
|
||||
if let updateSizeAndInsets = updateSizeAndInsets {
|
||||
widthUpdated = abs(state.visibleSize.width - updateSizeAndInsets.size.width) > CGFloat(FLT_EPSILON)
|
||||
widthUpdated = abs(state.visibleSize.width - updateSizeAndInsets.size.width) > CGFloat.ulpOfOne
|
||||
|
||||
state.visibleSize = updateSizeAndInsets.size
|
||||
state.insets = updateSizeAndInsets.insets
|
||||
@@ -1142,9 +1147,9 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
if options.contains(.PreferSynchronousResourceLoading) {
|
||||
var currentReadySignals: [Signal<Void, NoError>] = []
|
||||
for i in 0 ..< updatedOperations.count {
|
||||
if case let .InsertNode(index, offsetDirection, node, layout, apply) = updatedOperations[i] {
|
||||
if case let .InsertNode(index, offsetDirection, nodeAnimated, node, layout, apply) = updatedOperations[i] {
|
||||
let (ready, commitApply) = apply()
|
||||
updatedOperations[i] = .InsertNode(index: index, offsetDirection: offsetDirection, node: node, layout: layout, apply: {
|
||||
updatedOperations[i] = .InsertNode(index: index, offsetDirection: offsetDirection, animated: nodeAnimated, node: node, layout: layout, apply: {
|
||||
return (nil, commitApply)
|
||||
})
|
||||
if let ready = ready {
|
||||
@@ -1409,7 +1414,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
let nextNode = self.itemNodes[nodeIndex + 1]
|
||||
if nextNode.index == nil {
|
||||
let nextHeight = nextNode.apparentHeight
|
||||
if abs(nextHeight - previousApparentHeight) < CGFloat(FLT_EPSILON) {
|
||||
if abs(nextHeight - previousApparentHeight) < CGFloat.ulpOfOne {
|
||||
if let animation = nextNode.animationForKey("apparentHeight") {
|
||||
node.apparentHeight = previousApparentHeight
|
||||
|
||||
@@ -1424,7 +1429,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
|
||||
takenAnimation = true
|
||||
|
||||
if abs(layout.size.height - previousApparentHeight) > CGFloat(FLT_EPSILON) {
|
||||
if abs(layout.size.height - previousApparentHeight) > CGFloat.ulpOfOne {
|
||||
node.addApparentHeightAnimation(layout.size.height, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp, update: { [weak node] progress, currentValue in
|
||||
if let node = node {
|
||||
node.animateFrameTransition(progress, currentValue)
|
||||
@@ -1490,7 +1495,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
}
|
||||
}
|
||||
|
||||
if node.apparentHeight > CGFloat(FLT_EPSILON) {
|
||||
if node.apparentHeight > CGFloat.ulpOfOne {
|
||||
switch offsetDirection {
|
||||
case .Up:
|
||||
var i = nodeIndex - 1
|
||||
@@ -1587,7 +1592,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
|
||||
var takenPreviousNodes = Set<ListViewItemNode>()
|
||||
for operation in operations {
|
||||
if case let .InsertNode(_, _, node, _, _) = operation {
|
||||
if case let .InsertNode(_, _, _, node, _, _) = operation {
|
||||
takenPreviousNodes.insert(node)
|
||||
}
|
||||
}
|
||||
@@ -1596,7 +1601,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
case let .InsertNode(index, offsetDirection, node, layout, apply):
|
||||
case let .InsertNode(index, offsetDirection, nodeAnimated, node, layout, apply):
|
||||
var previousFrame: CGRect?
|
||||
for (previousNode, frame) in previousApparentFrames {
|
||||
if previousNode === node {
|
||||
@@ -1613,8 +1618,8 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
updatedPreviousFrame = nil
|
||||
}
|
||||
|
||||
self.insertNodeAtIndex(animated: animated, animateAlpha: animateAlpha, forceAnimateInsertion: forceAnimateInsertion, previousFrame: updatedPreviousFrame, nodeIndex: index, offsetDirection: offsetDirection, node: node, layout: layout, apply: apply, timestamp: timestamp)
|
||||
if let updatedPreviousFrame = updatedPreviousFrame {
|
||||
self.insertNodeAtIndex(animated: nodeAnimated, animateAlpha: animateAlpha, forceAnimateInsertion: forceAnimateInsertion, previousFrame: updatedPreviousFrame, nodeIndex: index, offsetDirection: offsetDirection, node: node, layout: layout, apply: apply, timestamp: timestamp)
|
||||
if let _ = updatedPreviousFrame {
|
||||
if let lowestHeaderNode = lowestHeaderNode {
|
||||
self.insertSubnode(node, belowSubnode: lowestHeaderNode)
|
||||
} else {
|
||||
@@ -1706,7 +1711,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
node.addInsetsAnimationToValue(updatedInsets, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp)
|
||||
}
|
||||
|
||||
if abs(updatedApparentHeight - previousApparentHeight) > CGFloat(FLT_EPSILON) {
|
||||
if abs(updatedApparentHeight - previousApparentHeight) > CGFloat.ulpOfOne {
|
||||
node.apparentHeight = previousApparentHeight
|
||||
node.animateFrameTransition(0.0, previousApparentHeight)
|
||||
node.addApparentHeightAnimation(updatedApparentHeight, duration: insertionAnimationDuration * UIView.animationDurationFactor(), beginAt: timestamp, update: { [weak node] progress, currentValue in
|
||||
@@ -1779,7 +1784,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
offset = self.insets.top - itemNode.apparentFrame.minY - itemNode.scrollPositioningInsets.top
|
||||
case let .Center(overflow):
|
||||
let contentAreaHeight = self.visibleSize.height - self.insets.bottom - self.insets.top
|
||||
if itemNode.apparentFrame.size.height <= contentAreaHeight + CGFloat(FLT_EPSILON) {
|
||||
if itemNode.apparentFrame.size.height <= contentAreaHeight + CGFloat.ulpOfOne {
|
||||
offset = self.insets.top + floor(((self.visibleSize.height - self.insets.bottom - self.insets.top) - itemNode.frame.size.height) / 2.0) - itemNode.apparentFrame.minY
|
||||
} else {
|
||||
switch overflow {
|
||||
@@ -1807,7 +1812,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
if previousNode === itemNode {
|
||||
let offset = previousFrame.minY - itemNode.frame.minY
|
||||
|
||||
if abs(offset) > CGFloat(FLT_EPSILON) {
|
||||
if abs(offset) > CGFloat.ulpOfOne {
|
||||
for itemNode in self.itemNodes {
|
||||
var frame = itemNode.frame
|
||||
frame.origin.y += offset
|
||||
@@ -1835,7 +1840,12 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
let previousVisibleSize = self.visibleSize
|
||||
self.visibleSize = updateSizeAndInsets.size
|
||||
|
||||
var offsetFix = updateSizeAndInsets.insets.top - self.insets.top
|
||||
var offsetFix: CGFloat
|
||||
if self.snapToBottomInsetUntilFirstInteraction {
|
||||
offsetFix = -updateSizeAndInsets.insets.bottom + self.insets.bottom
|
||||
} else {
|
||||
offsetFix = updateSizeAndInsets.insets.top - self.insets.top
|
||||
}
|
||||
|
||||
self.insets = updateSizeAndInsets.insets
|
||||
self.visibleSize = updateSizeAndInsets.size
|
||||
@@ -2025,7 +2035,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
|
||||
self.updateItemHeaders(headerNodesTransition, animateInsertion: animated || !requestItemInsertionAnimationsIndices.isEmpty)
|
||||
|
||||
if let offset = offset , abs(offset) > CGFloat(FLT_EPSILON) {
|
||||
if let offset = offset , abs(offset) > CGFloat.ulpOfOne {
|
||||
let lowestHeaderNode = self.lowestHeaderNode()
|
||||
for itemNode in temporaryPreviousNodes {
|
||||
itemNode.frame = itemNode.frame.offsetBy(dx: 0.0, dy: offset)
|
||||
@@ -2144,7 +2154,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
if self.debugInfo {
|
||||
var previousMaxY: CGFloat?
|
||||
for node in self.itemNodes {
|
||||
if let previousMaxY = previousMaxY , abs(previousMaxY - node.apparentFrame.minY) > CGFloat(FLT_EPSILON) {
|
||||
if let previousMaxY = previousMaxY , abs(previousMaxY - node.apparentFrame.minY) > CGFloat.ulpOfOne {
|
||||
print("monotonity violated")
|
||||
break
|
||||
}
|
||||
@@ -2454,7 +2464,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
var i = 0
|
||||
while i < self.itemNodes.count {
|
||||
let node = self.itemNodes[i]
|
||||
if node.index == nil && node.apparentHeight <= CGFloat(FLT_EPSILON) {
|
||||
if node.index == nil && node.apparentHeight <= CGFloat.ulpOfOne {
|
||||
self.removeItemNodeAtIndex(i)
|
||||
ASDeallocQueue.sharedDeallocation().releaseObject(inBackground: node)
|
||||
} else {
|
||||
@@ -2590,15 +2600,15 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
}
|
||||
let updatedApparentHeight = itemNode.apparentHeight
|
||||
let apparentHeightDelta = updatedApparentHeight - previousApparentHeight
|
||||
if abs(apparentHeightDelta) > CGFloat(FLT_EPSILON) {
|
||||
if itemNode.apparentFrame.maxY < self.insets.top + CGFloat(FLT_EPSILON) {
|
||||
if abs(apparentHeightDelta) > CGFloat.ulpOfOne {
|
||||
if itemNode.apparentFrame.maxY < self.insets.top + CGFloat.ulpOfOne {
|
||||
offsetRanges.offset(IndexRange(first: 0, last: index), offset: -apparentHeightDelta)
|
||||
} else {
|
||||
offsetRanges.offset(IndexRange(first: index + 1, last: Int.max), offset: apparentHeightDelta)
|
||||
}
|
||||
}
|
||||
|
||||
if itemNode.index == nil && updatedApparentHeight <= CGFloat(FLT_EPSILON) {
|
||||
if itemNode.index == nil && updatedApparentHeight <= CGFloat.ulpOfOne {
|
||||
requestUpdateVisibleItems = true
|
||||
}
|
||||
|
||||
|
||||
@@ -133,10 +133,10 @@ public final class ListViewAnimation {
|
||||
public func applyAt(_ timestamp: Double) {
|
||||
var t = CGFloat((timestamp - self.startTime) / self.duration)
|
||||
let ct: CGFloat
|
||||
if t <= 0.0 + CGFloat(FLT_EPSILON) {
|
||||
if t <= 0.0 + CGFloat.ulpOfOne {
|
||||
t = 0.0
|
||||
ct = 0.0
|
||||
} else if t >= 1.0 - CGFloat(FLT_EPSILON) {
|
||||
} else if t >= 1.0 - CGFloat.ulpOfOne {
|
||||
t = 1.0
|
||||
ct = 1.0
|
||||
} else {
|
||||
|
||||
@@ -312,7 +312,7 @@ struct ListViewState {
|
||||
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) {
|
||||
if node.frame.size.height <= contentAreaHeight + CGFloat.ulpOfOne {
|
||||
offset = self.insets.top + floor((contentAreaHeight - node.frame.size.height) / 2.0) - node.frame.minY
|
||||
} else {
|
||||
switch overflow {
|
||||
@@ -340,7 +340,7 @@ struct ListViewState {
|
||||
additionalOffset = self.insets.top - minY
|
||||
}
|
||||
|
||||
if abs(additionalOffset) > CGFloat(FLT_EPSILON) {
|
||||
if abs(additionalOffset) > CGFloat.ulpOfOne {
|
||||
for i in 0 ..< self.nodes.count {
|
||||
var frame = self.nodes[i].frame
|
||||
frame = frame.offsetBy(dx: 0.0, dy: additionalOffset)
|
||||
@@ -358,7 +358,7 @@ struct ListViewState {
|
||||
if node.index == stationaryIndex {
|
||||
let offset = stationaryOffset - node.frame.minY
|
||||
|
||||
if abs(offset) > CGFloat(FLT_EPSILON) {
|
||||
if abs(offset) > CGFloat.ulpOfOne {
|
||||
for i in 0 ..< self.nodes.count {
|
||||
var frame = self.nodes[i].frame
|
||||
frame = frame.offsetBy(dx: 0.0, dy: offset)
|
||||
@@ -447,7 +447,7 @@ struct ListViewState {
|
||||
}
|
||||
}
|
||||
|
||||
if abs(offset) > CGFloat(FLT_EPSILON) {
|
||||
if abs(offset) > CGFloat.ulpOfOne {
|
||||
for i in 0 ..< self.nodes.count {
|
||||
var frame = self.nodes[i].frame
|
||||
frame.origin.y += offset
|
||||
@@ -511,9 +511,9 @@ struct ListViewState {
|
||||
let node = self.nodes[i]
|
||||
if let index = node.index {
|
||||
if index != currentUpperNode.index - 1 {
|
||||
if currentUpperNode.frame.minY > -self.invisibleInset - CGFloat(FLT_EPSILON) {
|
||||
if currentUpperNode.frame.minY > -self.invisibleInset - CGFloat.ulpOfOne {
|
||||
var directionHint: ListViewInsertionOffsetDirection?
|
||||
if let hint = insertDirectionHints[currentUpperNode.index - 1] , currentUpperNode.frame.minY > self.insets.top - CGFloat(FLT_EPSILON) {
|
||||
if let hint = insertDirectionHints[currentUpperNode.index - 1] , currentUpperNode.frame.minY > self.insets.top - CGFloat.ulpOfOne {
|
||||
directionHint = ListViewInsertionOffsetDirection(hint)
|
||||
}
|
||||
return ListViewInsertionPoint(index: currentUpperNode.index - 1, point: CGPoint(x: 0.0, y: currentUpperNode.frame.minY), direction: directionHint ?? .Up)
|
||||
@@ -525,9 +525,9 @@ struct ListViewState {
|
||||
}
|
||||
}
|
||||
|
||||
if currentUpperNode.index != 0 && currentUpperNode.frame.minY > -self.invisibleInset - CGFloat(FLT_EPSILON) {
|
||||
if currentUpperNode.index != 0 && currentUpperNode.frame.minY > -self.invisibleInset - CGFloat.ulpOfOne {
|
||||
var directionHint: ListViewInsertionOffsetDirection?
|
||||
if let hint = insertDirectionHints[currentUpperNode.index - 1] , currentUpperNode.frame.minY > self.insets.top - CGFloat(FLT_EPSILON) {
|
||||
if let hint = insertDirectionHints[currentUpperNode.index - 1] , currentUpperNode.frame.minY > self.insets.top - CGFloat.ulpOfOne {
|
||||
directionHint = ListViewInsertionOffsetDirection(hint)
|
||||
}
|
||||
|
||||
@@ -540,9 +540,9 @@ struct ListViewState {
|
||||
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) {
|
||||
if currentLowerNode.frame.maxY < self.visibleSize.height + self.invisibleInset - CGFloat.ulpOfOne {
|
||||
var directionHint: ListViewInsertionOffsetDirection?
|
||||
if let hint = insertDirectionHints[currentLowerNode.index + 1] , currentLowerNode.frame.maxY < self.visibleSize.height - self.insets.bottom + CGFloat(FLT_EPSILON) {
|
||||
if let hint = insertDirectionHints[currentLowerNode.index + 1] , currentLowerNode.frame.maxY < self.visibleSize.height - self.insets.bottom + CGFloat.ulpOfOne {
|
||||
directionHint = ListViewInsertionOffsetDirection(hint)
|
||||
}
|
||||
return ListViewInsertionPoint(index: currentLowerNode.index + 1, point: CGPoint(x: 0.0, y: currentLowerNode.frame.maxY), direction: directionHint ?? .Down)
|
||||
@@ -555,9 +555,9 @@ struct ListViewState {
|
||||
}
|
||||
}
|
||||
|
||||
if currentLowerNode.index != itemCount - 1 && currentLowerNode.frame.maxY < self.visibleSize.height + self.invisibleInset - CGFloat(FLT_EPSILON) {
|
||||
if currentLowerNode.index != itemCount - 1 && currentLowerNode.frame.maxY < self.visibleSize.height + self.invisibleInset - CGFloat.ulpOfOne {
|
||||
var directionHint: ListViewInsertionOffsetDirection?
|
||||
if let hint = insertDirectionHints[currentLowerNode.index + 1] , currentLowerNode.frame.maxY < self.visibleSize.height - self.insets.bottom + CGFloat(FLT_EPSILON) {
|
||||
if let hint = insertDirectionHints[currentLowerNode.index + 1] , currentLowerNode.frame.maxY < self.visibleSize.height - self.insets.bottom + CGFloat.ulpOfOne {
|
||||
directionHint = ListViewInsertionOffsetDirection(hint)
|
||||
}
|
||||
return ListViewInsertionPoint(index: currentLowerNode.index + 1, point: CGPoint(x: 0.0, y: currentLowerNode.frame.maxY), direction: directionHint ?? .Down)
|
||||
@@ -593,7 +593,7 @@ struct ListViewState {
|
||||
}
|
||||
}
|
||||
|
||||
let upperBound = -self.invisibleInset + CGFloat(FLT_EPSILON)
|
||||
let upperBound = -self.invisibleInset + CGFloat.ulpOfOne
|
||||
for i in 0 ..< self.nodes.count {
|
||||
let node = self.nodes[i]
|
||||
if let index = node.index , node.frame.maxY > upperBound {
|
||||
@@ -617,7 +617,7 @@ struct ListViewState {
|
||||
}
|
||||
}
|
||||
|
||||
let lowerBound = self.visibleSize.height + self.invisibleInset - CGFloat(FLT_EPSILON)
|
||||
let lowerBound = self.visibleSize.height + self.invisibleInset - CGFloat.ulpOfOne
|
||||
for i in (0 ..< self.nodes.count).reversed() {
|
||||
let node = self.nodes[i]
|
||||
if let index = node.index , node.frame.minY < lowerBound {
|
||||
@@ -717,7 +717,7 @@ struct ListViewState {
|
||||
|
||||
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))
|
||||
operations.append(.InsertNode(index: insertionIndex, offsetDirection: offsetDirection, animated: animated, node: node, layout: layout, apply: apply))
|
||||
self.nodes.insert(.Node(index: itemIndex, frame: nodeFrame, referenceNode: nil), at: insertionIndex)
|
||||
|
||||
if !animated {
|
||||
@@ -770,7 +770,7 @@ struct ListViewState {
|
||||
if let direction = direction {
|
||||
offsetDirection = ListViewInsertionOffsetDirection(direction)
|
||||
} else {
|
||||
if nodeFrame.maxY < self.insets.top + CGFloat(FLT_EPSILON) {
|
||||
if nodeFrame.maxY < self.insets.top + CGFloat.ulpOfOne {
|
||||
offsetDirection = .Down
|
||||
} else {
|
||||
offsetDirection = .Up
|
||||
@@ -782,8 +782,8 @@ struct ListViewState {
|
||||
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) {
|
||||
if nodeFrame.maxY > self.insets.top - CGFloat.ulpOfOne {
|
||||
if let direction = direction , direction == .Down && node.frame.minY < self.visibleSize.height - self.insets.bottom + CGFloat.ulpOfOne {
|
||||
for i in (0 ..< index).reversed() {
|
||||
var frame = self.nodes[i].frame
|
||||
frame.origin.y += nodeFrame.size.height
|
||||
@@ -820,7 +820,7 @@ struct ListViewState {
|
||||
if let direction = direction {
|
||||
offsetDirection = ListViewInsertionOffsetDirection(direction)
|
||||
} else {
|
||||
if node.frame.maxY < self.insets.top + CGFloat(FLT_EPSILON) {
|
||||
if node.frame.maxY < self.insets.top + CGFloat.ulpOfOne {
|
||||
offsetDirection = .Down
|
||||
} else {
|
||||
offsetDirection = .Up
|
||||
@@ -865,7 +865,7 @@ struct ListViewState {
|
||||
}
|
||||
|
||||
enum ListViewStateOperation {
|
||||
case InsertNode(index: Int, offsetDirection: ListViewInsertionOffsetDirection, node: ListViewItemNode, layout: ListViewItemNodeLayout, apply: () -> (Signal<Void, NoError>?, () -> Void))
|
||||
case InsertNode(index: Int, offsetDirection: ListViewInsertionOffsetDirection, animated: Bool, node: ListViewItemNode, layout: ListViewItemNodeLayout, apply: () -> (Signal<Void, NoError>?, () -> Void))
|
||||
case InsertDisappearingPlaceholder(index: Int, referenceNode: ListViewItemNode, offsetDirection: ListViewInsertionOffsetDirection)
|
||||
case Remove(index: Int, offsetDirection: ListViewInsertionOffsetDirection)
|
||||
case Remap([Int: Int])
|
||||
|
||||
@@ -408,7 +408,7 @@ open class ListViewItemNode: ASDisplayNode {
|
||||
public func modifyApparentHeightAnimation(_ value: CGFloat, beginAt: Double) {
|
||||
if let previousAnimation = self.animationForKey("apparentHeight") {
|
||||
var duration = previousAnimation.startTime + previousAnimation.duration - beginAt
|
||||
if abs(self.apparentHeight - value) < CGFloat(FLT_EPSILON) {
|
||||
if abs(self.apparentHeight - value) < CGFloat.ulpOfOne {
|
||||
duration = 0.0
|
||||
}
|
||||
|
||||
|
||||
20
Display/MinimizeKeyboardGestureRecognizer.swift
Normal file
20
Display/MinimizeKeyboardGestureRecognizer.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
final class MinimizeKeyboardGestureRecognizer: UISwipeGestureRecognizer, UIGestureRecognizerDelegate {
|
||||
override init(target: Any?, action: Selector?) {
|
||||
super.init(target: target, action: action)
|
||||
|
||||
self.cancelsTouchesInView = false
|
||||
self.delaysTouchesBegan = false
|
||||
self.delaysTouchesEnded = false
|
||||
self.delegate = self
|
||||
|
||||
self.direction = [.left, .right]
|
||||
self.numberOfTouchesRequired = 2
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -210,9 +210,14 @@ open class NavigationController: NavigationControllerProxy, ContainableControlle
|
||||
}
|
||||
|
||||
public func pushViewController(_ controller: ViewController) {
|
||||
controller.containerLayoutUpdated(self.containerLayout, transition: .immediate)
|
||||
self.view.endEditing(true)
|
||||
let appliedLayout = self.containerLayout.withUpdatedInputHeight(nil)
|
||||
controller.containerLayoutUpdated(appliedLayout, transition: .immediate)
|
||||
self.currentPushDisposable.set((controller.ready.get() |> take(1)).start(next: {[weak self] _ in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.containerLayout.withUpdatedInputHeight(nil) != appliedLayout {
|
||||
controller.containerLayoutUpdated(strongSelf.containerLayout.withUpdatedInputHeight(nil), transition: .immediate)
|
||||
}
|
||||
strongSelf.pushViewController(controller, animated: true)
|
||||
}
|
||||
}))
|
||||
@@ -227,6 +232,7 @@ open class NavigationController: NavigationControllerProxy, ContainableControlle
|
||||
}
|
||||
|
||||
public func replaceTopController(_ controller: ViewController, animated: Bool, ready: ValuePromise<Bool>? = nil) {
|
||||
self.view.endEditing(true)
|
||||
controller.containerLayoutUpdated(self.containerLayout, transition: .immediate)
|
||||
self.currentPushDisposable.set((controller.ready.get() |> take(1)).start(next: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
@@ -240,6 +246,7 @@ open class NavigationController: NavigationControllerProxy, ContainableControlle
|
||||
}
|
||||
|
||||
public func replaceAllButRootController(_ controller: ViewController, animated: Bool, ready: ValuePromise<Bool>? = nil) {
|
||||
self.view.endEditing(true)
|
||||
controller.containerLayoutUpdated(self.containerLayout, transition: .immediate)
|
||||
self.currentPushDisposable.set((controller.ready.get() |> take(1)).start(next: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
@@ -262,6 +269,26 @@ open class NavigationController: NavigationControllerProxy, ContainableControlle
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
}
|
||||
|
||||
override open func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? {
|
||||
var poppedControllers: [UIViewController] = []
|
||||
var found = false
|
||||
var controllers = self.viewControllers
|
||||
while !controllers.isEmpty {
|
||||
if controllers[controllers.count - 1] === viewController {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
poppedControllers.insert(controllers[controllers.count - 1], at: 0)
|
||||
controllers.removeLast()
|
||||
}
|
||||
if found {
|
||||
self.setViewControllers(controllers, animated: animated)
|
||||
return poppedControllers
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
open override func popViewController(animated: Bool) -> UIViewController? {
|
||||
var controller: UIViewController?
|
||||
var controllers = self.viewControllers
|
||||
|
||||
@@ -204,7 +204,7 @@ class NavigationTransitionCoordinator {
|
||||
completion()
|
||||
}
|
||||
|
||||
if abs(velocity) < CGFloat(FLT_EPSILON) && abs(self.progress) < CGFloat(FLT_EPSILON) {
|
||||
if abs(velocity) < CGFloat.ulpOfOne && abs(self.progress) < CGFloat.ulpOfOne {
|
||||
UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions(rawValue: 7 << 16), animations: {
|
||||
self.progress = 1.0
|
||||
}, completion: { _ in
|
||||
|
||||
@@ -40,7 +40,7 @@ public class StatusBar: ASDisplayNode {
|
||||
return UITracingLayerView()
|
||||
}, didLoad: nil)
|
||||
|
||||
self.layer.setTraceableInfo(NSWeakReference(value: self))
|
||||
self.layer.setTraceableInfo(CATracingLayerInfo(shouldBeAdjustedToInverseTransform: true, userData: self, tracingTag: Window.statusBarTracingTag))
|
||||
|
||||
self.clipsToBounds = true
|
||||
self.isUserInteractionEnabled = false
|
||||
|
||||
@@ -6,5 +6,6 @@ public protocol StatusBarHost {
|
||||
var statusBarWindow: UIView? { get }
|
||||
var statusBarView: UIView? { get }
|
||||
|
||||
var keyboardWindow: UIWindow? { get }
|
||||
var keyboardView: UIView? { get }
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ private func mappedSurface(_ surface: StatusBarSurface) -> MappedStatusBarSurfac
|
||||
private func optimizeMappedSurface(statusBarSize: CGSize, surface: MappedStatusBarSurface) -> MappedStatusBarSurface {
|
||||
if surface.statusBars.count > 1 {
|
||||
for i in 1 ..< surface.statusBars.count {
|
||||
if surface.statusBars[i].style != surface.statusBars[i - 1].style || abs(surface.statusBars[i].frame.origin.y - surface.statusBars[i - 1].frame.origin.y) > CGFloat(FLT_EPSILON) {
|
||||
if surface.statusBars[i].style != surface.statusBars[i - 1].style || abs(surface.statusBars[i].frame.origin.y - surface.statusBars[i - 1].frame.origin.y) > CGFloat.ulpOfOne {
|
||||
return surface
|
||||
}
|
||||
if let lhsStatusBar = surface.statusBars[i - 1].statusBar, let rhsStatusBar = surface.statusBars[i].statusBar , !lhsStatusBar.alpha.isEqual(to: rhsStatusBar.alpha) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import Foundation
|
||||
|
||||
public class Theme {
|
||||
public let tintColor: UIColor
|
||||
public let accentColor: UIColor
|
||||
|
||||
public init(tintColor: UIColor) {
|
||||
self.tintColor = tintColor
|
||||
public init(accentColor: UIColor) {
|
||||
self.accentColor = accentColor
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ CABasicAnimation * _Nonnull makeSpringBounceAnimation(NSString * _Nonnull keyPat
|
||||
});
|
||||
if (canSetInitialVelocity) {
|
||||
springAnimation.initialVelocity = initialVelocity;
|
||||
springAnimation.duration = springAnimation.settlingDuration;
|
||||
}
|
||||
springAnimation.duration = springAnimation.settlingDuration;
|
||||
springAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
|
||||
return springAnimation;
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ static bool notyfyingShiftState = false;
|
||||
return controller;
|
||||
}
|
||||
|
||||
UIView *result = [self associatedObjectForKey:UIViewControllerPresentingProxyControllerKey];
|
||||
UIViewController *result = [self associatedObjectForKey:UIViewControllerPresentingProxyControllerKey];
|
||||
if (result != nil) {
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -168,6 +168,9 @@ open class ViewControllerPresentationArguments {
|
||||
}
|
||||
|
||||
open func displayNodeDidLoad() {
|
||||
if let layer = self.displayNode.layer as? CATracingLayer {
|
||||
layer.setTraceableInfo(CATracingLayerInfo(shouldBeAdjustedToInverseTransform: false, userData: self.displayNode.layer, tracingTag: Window.keyboardTracingTag))
|
||||
}
|
||||
self.updateScrollToTopView()
|
||||
}
|
||||
|
||||
@@ -243,4 +246,7 @@ open class ViewControllerPresentationArguments {
|
||||
|
||||
super.viewDidAppear(animated)
|
||||
}
|
||||
|
||||
open func dismiss() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ private struct WindowLayout: Equatable {
|
||||
public let size: CGSize
|
||||
public let statusBarHeight: CGFloat?
|
||||
public let inputHeight: CGFloat?
|
||||
public let inputMinimized: Bool
|
||||
}
|
||||
|
||||
private func ==(lhs: WindowLayout, rhs: WindowLayout) -> Bool {
|
||||
@@ -54,6 +55,10 @@ private func ==(lhs: WindowLayout, rhs: WindowLayout) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if lhs.inputMinimized != rhs.inputMinimized {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -76,19 +81,25 @@ private struct UpdatingLayout {
|
||||
mutating func update(size: CGSize, transition: ContainedViewLayoutTransition, overrideTransition: Bool) {
|
||||
self.update(transition: transition, override: overrideTransition)
|
||||
|
||||
self.layout = WindowLayout(size: size, statusBarHeight: self.layout.statusBarHeight, inputHeight: self.layout.inputHeight)
|
||||
self.layout = WindowLayout(size: size, statusBarHeight: self.layout.statusBarHeight, inputHeight: self.layout.inputHeight, inputMinimized: self.layout.inputMinimized)
|
||||
}
|
||||
|
||||
mutating func update(statusBarHeight: CGFloat?, transition: ContainedViewLayoutTransition, overrideTransition: Bool) {
|
||||
self.update(transition: transition, override: overrideTransition)
|
||||
|
||||
self.layout = WindowLayout(size: self.layout.size, statusBarHeight: statusBarHeight, inputHeight: self.layout.inputHeight)
|
||||
self.layout = WindowLayout(size: self.layout.size, statusBarHeight: statusBarHeight, inputHeight: self.layout.inputHeight, inputMinimized: self.layout.inputMinimized)
|
||||
}
|
||||
|
||||
mutating func update(inputHeight: CGFloat?, transition: ContainedViewLayoutTransition, overrideTransition: Bool) {
|
||||
self.update(transition: transition, override: overrideTransition)
|
||||
|
||||
self.layout = WindowLayout(size: self.layout.size, statusBarHeight: self.layout.statusBarHeight, inputHeight: inputHeight)
|
||||
self.layout = WindowLayout(size: self.layout.size, statusBarHeight: self.layout.statusBarHeight, inputHeight: inputHeight, inputMinimized: self.layout.inputMinimized)
|
||||
}
|
||||
|
||||
mutating func update(inputMinimized: Bool, transition: ContainedViewLayoutTransition, overrideTransition: Bool) {
|
||||
self.update(transition: transition, override: overrideTransition)
|
||||
|
||||
self.layout = WindowLayout(size: self.layout.size, statusBarHeight: self.layout.statusBarHeight, inputHeight: self.layout.inputHeight, inputMinimized: inputMinimized)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,12 +107,21 @@ private let orientationChangeDuration: Double = UIDevice.current.userInterfaceId
|
||||
private let statusBarHiddenInLandscape: Bool = UIDevice.current.userInterfaceIdiom == .phone
|
||||
|
||||
private func containedLayoutForWindowLayout(_ layout: WindowLayout) -> ContainerViewLayout {
|
||||
return ContainerViewLayout(size: layout.size, intrinsicInsets: UIEdgeInsets(), statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight)
|
||||
var inputHeight: CGFloat? = layout.inputHeight
|
||||
if let inputHeightValue = inputHeight, layout.inputMinimized {
|
||||
inputHeight = floor(0.85 * inputHeightValue)
|
||||
}
|
||||
|
||||
return ContainerViewLayout(size: layout.size, intrinsicInsets: UIEdgeInsets(), statusBarHeight: layout.statusBarHeight, inputHeight: inputHeight)
|
||||
}
|
||||
|
||||
public class Window: UIWindow {
|
||||
public static let statusBarTracingTag: Int32 = 0
|
||||
public static let keyboardTracingTag: Int32 = 1
|
||||
|
||||
private let statusBarHost: StatusBarHost?
|
||||
private let statusBarManager: StatusBarManager?
|
||||
private let keyboardManager: KeyboardManager?
|
||||
private var statusBarChangeObserver: AnyObject?
|
||||
private var keyboardFrameChangeObserver: AnyObject?
|
||||
|
||||
@@ -122,11 +142,21 @@ public class Window: UIWindow {
|
||||
if let statusBarHost = statusBarHost {
|
||||
self.statusBarManager = StatusBarManager(host: statusBarHost)
|
||||
statusBarHeight = statusBarHost.statusBarFrame.size.height
|
||||
self.keyboardManager = KeyboardManager(host: statusBarHost)
|
||||
} else {
|
||||
self.statusBarManager = nil
|
||||
self.keyboardManager = nil
|
||||
statusBarHeight = 20.0
|
||||
}
|
||||
self.windowLayout = WindowLayout(size: frame.size, statusBarHeight: statusBarHeight, inputHeight: 0.0)
|
||||
|
||||
let minimized: Bool
|
||||
if let keyboardManager = self.keyboardManager {
|
||||
minimized = keyboardManager.minimized
|
||||
} else {
|
||||
minimized = false
|
||||
}
|
||||
|
||||
self.windowLayout = WindowLayout(size: frame.size, statusBarHeight: statusBarHeight, inputHeight: 0.0, inputMinimized: minimized)
|
||||
self.presentationContext = PresentationContext()
|
||||
|
||||
super.init(frame: frame)
|
||||
@@ -135,6 +165,14 @@ public class Window: UIWindow {
|
||||
self?.invalidateTracingStatusBars()
|
||||
}
|
||||
|
||||
self.keyboardManager?.minimizedUpdated = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateLayout { current in
|
||||
current.update(inputMinimized: strongSelf.keyboardManager!.minimized, transition: .immediate, overrideTransition: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.presentationContext.view = self
|
||||
self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: .immediate)
|
||||
|
||||
@@ -170,7 +208,7 @@ public class Window: UIWindow {
|
||||
if duration > DBL_EPSILON {
|
||||
duration = 0.5
|
||||
}
|
||||
var curve: UInt = (notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber)?.uintValue ?? 7
|
||||
let curve: UInt = (notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber)?.uintValue ?? 7
|
||||
|
||||
let transitionCurve: ContainedViewLayoutTransitionCurve
|
||||
if curve == 7 {
|
||||
@@ -271,20 +309,19 @@ public class Window: UIWindow {
|
||||
override public func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
if self.tracingStatusBarsInvalidated, let statusBarManager = statusBarManager {
|
||||
if self.tracingStatusBarsInvalidated, let statusBarManager = statusBarManager, let keyboardManager = keyboardManager {
|
||||
self.tracingStatusBarsInvalidated = false
|
||||
|
||||
if self.statusBarHidden {
|
||||
statusBarManager.surfaces = []
|
||||
} else {
|
||||
var statusBarSurfaces: [StatusBarSurface] = []
|
||||
for layers in self.layer.traceableLayerSurfaces() {
|
||||
for layers in self.layer.traceableLayerSurfaces(withTag: Window.statusBarTracingTag) {
|
||||
let surface = StatusBarSurface()
|
||||
for layer in layers {
|
||||
if let weakInfo = layer.traceableInfo() as? NSWeakReference {
|
||||
if let statusBar = weakInfo.value as? StatusBar {
|
||||
surface.addStatusBar(statusBar)
|
||||
}
|
||||
let traceableInfo = layer.traceableInfo()
|
||||
if let statusBar = traceableInfo?.userData as? StatusBar {
|
||||
surface.addStatusBar(statusBar)
|
||||
}
|
||||
}
|
||||
statusBarSurfaces.append(surface)
|
||||
@@ -292,6 +329,16 @@ public class Window: UIWindow {
|
||||
self.layer.adjustTraceableLayerTransforms(CGSize())
|
||||
statusBarManager.surfaces = statusBarSurfaces
|
||||
}
|
||||
|
||||
var keyboardSurfaces: [KeyboardSurface] = []
|
||||
for layers in self.layer.traceableLayerSurfaces(withTag: Window.keyboardTracingTag) {
|
||||
for layer in layers {
|
||||
if let view = layer.delegate as? UITracingLayerView {
|
||||
keyboardSurfaces.append(KeyboardSurface(host: view))
|
||||
}
|
||||
}
|
||||
}
|
||||
keyboardManager.surfaces = keyboardSurfaces
|
||||
}
|
||||
|
||||
if !Window.isDeviceRotating() {
|
||||
@@ -331,7 +378,7 @@ public class Window: UIWindow {
|
||||
postUpdateToInterfaceOrientationBlocks.append(f)
|
||||
}
|
||||
|
||||
private func updateLayout(_ update: @noescape(inout UpdatingLayout) -> ()) {
|
||||
private func updateLayout(_ update: (inout UpdatingLayout) -> ()) {
|
||||
if self.updatingLayout == nil {
|
||||
self.updatingLayout = UpdatingLayout(layout: self.windowLayout, transition: .immediate)
|
||||
}
|
||||
@@ -349,7 +396,7 @@ public class Window: UIWindow {
|
||||
} else {
|
||||
statusBarHeight = 20.0
|
||||
}
|
||||
var statusBarWasHidden = self.statusBarHidden
|
||||
let statusBarWasHidden = self.statusBarHidden
|
||||
if statusBarHiddenInLandscape && updatingLayout.layout.size.width > updatingLayout.layout.size.height {
|
||||
statusBarHeight = 0.0
|
||||
self.statusBarHidden = true
|
||||
@@ -360,7 +407,7 @@ public class Window: UIWindow {
|
||||
self.tracingStatusBarsInvalidated = true
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
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, inputMinimized: updatingLayout.layout.inputMinimized)
|
||||
|
||||
self._rootController?.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: updatingLayout.transition)
|
||||
self.presentationContext.containerLayoutUpdated(containedLayoutForWindowLayout(self.windowLayout), transition: updatingLayout.transition)
|
||||
|
||||
Reference in New Issue
Block a user