mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-08 23:30:22 +00:00
no message
This commit is contained in:
parent
cad4955b71
commit
95614c8121
@ -111,6 +111,7 @@
|
||||
D05CC3251B695B0700E235A3 /* NavigationBarProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3231B695B0700E235A3 /* NavigationBarProxy.m */; };
|
||||
D05CC3271B69725400E235A3 /* NavigationBackArrowLight@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D05CC3261B69725400E235A3 /* NavigationBackArrowLight@2x.png */; };
|
||||
D05CC3291B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05CC3281B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift */; };
|
||||
D06B76DB20592A97006E9EEA /* LayoutSizes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06B76DA20592A97006E9EEA /* LayoutSizes.swift */; };
|
||||
D06EE8451B7140FF00837186 /* Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06EE8441B7140FF00837186 /* Font.swift */; };
|
||||
D077B8E91F4637040046D27A /* NavigationBarBadge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D077B8E81F4637040046D27A /* NavigationBarBadge.swift */; };
|
||||
D081229D1D19AA1C005F7395 /* ContainerViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = D081229C1D19AA1C005F7395 /* ContainerViewLayout.swift */; };
|
||||
@ -149,6 +150,8 @@
|
||||
D0C85DD01D1C082E00124894 /* ActionSheetItemGroupsContainerNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C85DCF1D1C082E00124894 /* ActionSheetItemGroupsContainerNode.swift */; };
|
||||
D0C85DD21D1C08AE00124894 /* ActionSheetItemNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C85DD11D1C08AE00124894 /* ActionSheetItemNode.swift */; };
|
||||
D0C85DD41D1C1E6A00124894 /* ActionSheetItemGroupNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C85DD31D1C1E6A00124894 /* ActionSheetItemGroupNode.swift */; };
|
||||
D0CA3F8A2073F7650042D2B6 /* LinkHighlightingNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CA3F892073F7650042D2B6 /* LinkHighlightingNode.swift */; };
|
||||
D0CA3F8C2073F8240042D2B6 /* TapLongTapOrDoubleTapGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CA3F8B2073F8240042D2B6 /* TapLongTapOrDoubleTapGestureRecognizer.swift */; };
|
||||
D0CB78901F9822F8004AB79B /* WindowPanRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CB788F1F9822F8004AB79B /* WindowPanRecognizer.swift */; };
|
||||
D0CD12161CCFEB4E000DE7BC /* ScrollToTopProxyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CD12151CCFEB4E000DE7BC /* ScrollToTopProxyView.swift */; };
|
||||
D0CE67921F7DA11700FFB557 /* ActionSheetTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CE67911F7DA11700FFB557 /* ActionSheetTheme.swift */; };
|
||||
@ -282,6 +285,7 @@
|
||||
D05CC3231B695B0700E235A3 /* NavigationBarProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NavigationBarProxy.m; sourceTree = "<group>"; };
|
||||
D05CC3261B69725400E235A3 /* NavigationBackArrowLight@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "NavigationBackArrowLight@2x.png"; sourceTree = "<group>"; };
|
||||
D05CC3281B69750D00E235A3 /* InteractiveTransitionGestureRecognizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InteractiveTransitionGestureRecognizer.swift; sourceTree = "<group>"; };
|
||||
D06B76DA20592A97006E9EEA /* LayoutSizes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutSizes.swift; sourceTree = "<group>"; };
|
||||
D06EE8441B7140FF00837186 /* Font.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Font.swift; sourceTree = "<group>"; };
|
||||
D077B8E81F4637040046D27A /* NavigationBarBadge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBarBadge.swift; sourceTree = "<group>"; };
|
||||
D081229C1D19AA1C005F7395 /* ContainerViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContainerViewLayout.swift; sourceTree = "<group>"; };
|
||||
@ -321,6 +325,8 @@
|
||||
D0C85DCF1D1C082E00124894 /* ActionSheetItemGroupsContainerNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetItemGroupsContainerNode.swift; sourceTree = "<group>"; };
|
||||
D0C85DD11D1C08AE00124894 /* ActionSheetItemNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetItemNode.swift; sourceTree = "<group>"; };
|
||||
D0C85DD31D1C1E6A00124894 /* ActionSheetItemGroupNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionSheetItemGroupNode.swift; sourceTree = "<group>"; };
|
||||
D0CA3F892073F7650042D2B6 /* LinkHighlightingNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkHighlightingNode.swift; sourceTree = "<group>"; };
|
||||
D0CA3F8B2073F8240042D2B6 /* TapLongTapOrDoubleTapGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapLongTapOrDoubleTapGestureRecognizer.swift; sourceTree = "<group>"; };
|
||||
D0CB788F1F9822F8004AB79B /* WindowPanRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowPanRecognizer.swift; sourceTree = "<group>"; };
|
||||
D0CD12151CCFEB4E000DE7BC /* ScrollToTopProxyView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollToTopProxyView.swift; sourceTree = "<group>"; };
|
||||
D0CE67911F7DA11700FFB557 /* ActionSheetTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionSheetTheme.swift; sourceTree = "<group>"; };
|
||||
@ -476,6 +482,7 @@
|
||||
D04C468D1F4C97BE00D30FE1 /* PageControlNode.swift */,
|
||||
D0FA08C32048803C00DD23FC /* TextNode.swift */,
|
||||
D0FA08C5204880C900DD23FC /* ImmediateTextNode.swift */,
|
||||
D0CA3F892073F7650042D2B6 /* LinkHighlightingNode.swift */,
|
||||
);
|
||||
name = Nodes;
|
||||
sourceTree = "<group>";
|
||||
@ -522,6 +529,8 @@
|
||||
D05174B21EAA833200A1BF36 /* CASeeThroughTracingLayer.m */,
|
||||
D01847651FFA72E000075256 /* ContainedViewLayoutTransition.swift */,
|
||||
D0FA08C120487A8600DD23FC /* HapticFeedback.swift */,
|
||||
D06B76DA20592A97006E9EEA /* LayoutSizes.swift */,
|
||||
D0CA3F8B2073F8240042D2B6 /* TapLongTapOrDoubleTapGestureRecognizer.swift */,
|
||||
);
|
||||
name = Utils;
|
||||
sourceTree = "<group>";
|
||||
@ -977,6 +986,7 @@
|
||||
D0C2DFCA1CC4431D0044FF83 /* ListViewItem.swift in Sources */,
|
||||
D05CC2A01B69326400E235A3 /* NavigationController.swift in Sources */,
|
||||
D06EE8451B7140FF00837186 /* Font.swift in Sources */,
|
||||
D0CA3F8A2073F7650042D2B6 /* LinkHighlightingNode.swift in Sources */,
|
||||
D0C2DFCB1CC4431D0044FF83 /* ListViewAnimation.swift in Sources */,
|
||||
D0BE93191E8ED71100DCC1E6 /* NativeWindowHostView.swift in Sources */,
|
||||
D05CC3251B695B0700E235A3 /* NavigationBarProxy.m in Sources */,
|
||||
@ -992,6 +1002,7 @@
|
||||
D01E2BDE1D9049620066BF65 /* GridNode.swift in Sources */,
|
||||
D01E2BE01D90498E0066BF65 /* GridNodeScroller.swift in Sources */,
|
||||
D0C2DFD01CC4431D0044FF83 /* ListViewAccessoryItemNode.swift in Sources */,
|
||||
D06B76DB20592A97006E9EEA /* LayoutSizes.swift in Sources */,
|
||||
D0DA44501E4DCBDE005FDCA7 /* AlertContentNode.swift in Sources */,
|
||||
D0D94A171D3814F900740E02 /* UniversalTapRecognizer.swift in Sources */,
|
||||
D00701982029CAD6006B9E34 /* TooltipController.swift in Sources */,
|
||||
@ -1044,6 +1055,7 @@
|
||||
D05CC2EC1B69558A00E235A3 /* RuntimeUtils.m in Sources */,
|
||||
D0E35A031DE473B900BC6096 /* HighlightableButton.swift in Sources */,
|
||||
D0FA08C42048803C00DD23FC /* TextNode.swift in Sources */,
|
||||
D0CA3F8C2073F8240042D2B6 /* TapLongTapOrDoubleTapGestureRecognizer.swift in Sources */,
|
||||
D0CD12161CCFEB4E000DE7BC /* ScrollToTopProxyView.swift in Sources */,
|
||||
D0AA840E1FEBFB72005C6E91 /* ListViewFloatingHeaderNode.swift in Sources */,
|
||||
D03AA4EB202E02B10056C405 /* ListViewReorderingGestureRecognizer.swift in Sources */,
|
||||
@ -1388,6 +1400,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "-DMINIMAL_ASDK";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@ -1429,6 +1442,7 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_CFLAGS = "-DMINIMAL_ASDK";
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
@ -1554,6 +1568,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "-DMINIMAL_ASDK";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@ -1633,6 +1648,7 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_CFLAGS = "-DMINIMAL_ASDK";
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
@ -1712,6 +1728,7 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_CFLAGS = "-DMINIMAL_ASDK";
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<key>Display.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>5</integer>
|
||||
<integer>6</integer>
|
||||
</dict>
|
||||
<key>DisplayMac.xcscheme</key>
|
||||
<dict>
|
||||
@ -17,7 +17,7 @@
|
||||
<key>DisplayTests.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>7</integer>
|
||||
<integer>8</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
|
||||
@ -12,7 +12,7 @@ open class ActionSheetController: ViewController {
|
||||
public init(theme: ActionSheetControllerTheme) {
|
||||
self.theme = theme
|
||||
|
||||
super.init(navigationBarTheme: nil)
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
|
||||
@ -79,8 +79,11 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
var insets = layout.insets(options: [.statusBar])
|
||||
insets.left += layout.safeInsets.left
|
||||
insets.right += layout.safeInsets.right
|
||||
|
||||
let containerWidth = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: layout.safeInsets.left)
|
||||
|
||||
insets.left = floor((layout.size.width - containerWidth) / 2.0)
|
||||
insets.right = insets.left
|
||||
|
||||
self.validLayout = layout
|
||||
|
||||
@ -91,7 +94,7 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.itemGroupsContainerNode.frame = CGRect(origin: CGPoint(x: insets.left + containerInsets.left, y: layout.size.height - insets.bottom - containerInsets.bottom - self.itemGroupsContainerNode.calculatedSize.height), size: self.itemGroupsContainerNode.calculatedSize)
|
||||
self.itemGroupsContainerNode.layout()
|
||||
|
||||
self.updateScrollDimViews(size: layout.size, safeInsets: layout.safeInsets)
|
||||
self.updateScrollDimViews(size: layout.size, insets: insets)
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
@ -141,8 +144,15 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
if let validLayout = self.validLayout {
|
||||
self.updateScrollDimViews(size: validLayout.size, safeInsets: validLayout.safeInsets)
|
||||
if let layout = self.validLayout {
|
||||
var insets = layout.insets(options: [.statusBar])
|
||||
|
||||
let containerWidth = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: layout.safeInsets.left)
|
||||
|
||||
insets.left = floor((layout.size.width - containerWidth) / 2.0)
|
||||
insets.right = insets.left
|
||||
|
||||
self.updateScrollDimViews(size: layout.size, insets: insets)
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,15 +165,15 @@ final class ActionSheetControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func updateScrollDimViews(size: CGSize, safeInsets: UIEdgeInsets) {
|
||||
func updateScrollDimViews(size: CGSize, insets: UIEdgeInsets) {
|
||||
let additionalTopHeight = max(0.0, -self.scrollView.contentOffset.y)
|
||||
let additionalBottomHeight = -min(0.0, -self.scrollView.contentOffset.y)
|
||||
|
||||
self.topDimView.frame = CGRect(x: containerInsets.left, y: -additionalTopHeight, width: size.width - containerInsets.left - containerInsets.right, height: max(0.0, self.itemGroupsContainerNode.frame.minY + additionalTopHeight))
|
||||
self.bottomDimView.frame = CGRect(x: containerInsets.left, y: self.itemGroupsContainerNode.frame.maxY, width: size.width - containerInsets.left - containerInsets.right, height: max(0.0, size.height - self.itemGroupsContainerNode.frame.maxY + additionalBottomHeight))
|
||||
self.topDimView.frame = CGRect(x: containerInsets.left + insets.left, y: -additionalTopHeight, width: size.width - containerInsets.left - containerInsets.right - insets.left - insets.right, height: max(0.0, self.itemGroupsContainerNode.frame.minY + additionalTopHeight))
|
||||
self.bottomDimView.frame = CGRect(x: containerInsets.left + insets.left, y: self.itemGroupsContainerNode.frame.maxY, width: size.width - containerInsets.left - containerInsets.right - insets.left - insets.right, height: max(0.0, size.height - self.itemGroupsContainerNode.frame.maxY + additionalBottomHeight))
|
||||
|
||||
self.leftDimView.frame = CGRect(x: 0.0, y: -additionalTopHeight, width: containerInsets.left + safeInsets.left, height: size.height + additionalTopHeight + additionalBottomHeight)
|
||||
self.rightDimView.frame = CGRect(x: size.width - containerInsets.right, y: -additionalTopHeight, width: containerInsets.right + safeInsets.right, height: size.height + additionalTopHeight + additionalBottomHeight)
|
||||
self.leftDimView.frame = CGRect(x: 0.0, y: -additionalTopHeight, width: containerInsets.left + insets.left, height: size.height + additionalTopHeight + additionalBottomHeight)
|
||||
self.rightDimView.frame = CGRect(x: size.width - containerInsets.right - insets.right, y: -additionalTopHeight, width: containerInsets.right + insets.right, height: size.height + additionalTopHeight + additionalBottomHeight)
|
||||
}
|
||||
|
||||
func setGroups(_ groups: [ActionSheetItemGroup]) {
|
||||
|
||||
@ -33,7 +33,7 @@ open class AlertController: ViewController {
|
||||
self.theme = theme
|
||||
self.contentNode = contentNode
|
||||
|
||||
super.init(navigationBarTheme: nil)
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
}
|
||||
|
||||
@ -9,8 +9,9 @@
|
||||
@property (nonatomic, readonly) bool shouldBeAdjustedToInverseTransform;
|
||||
@property (nonatomic, weak, readonly) id _Nullable userData;
|
||||
@property (nonatomic, readonly) int32_t tracingTag;
|
||||
@property (nonatomic, readonly) int32_t disableChildrenTracingTags;
|
||||
|
||||
- (instancetype _Nonnull)initWithShouldBeAdjustedToInverseTransform:(bool)shouldBeAdjustedToInverseTransform userData:(id _Nullable)userData tracingTag:(int32_t)tracingTag;
|
||||
- (instancetype _Nonnull)initWithShouldBeAdjustedToInverseTransform:(bool)shouldBeAdjustedToInverseTransform userData:(id _Nullable)userData tracingTag:(int32_t)tracingTag disableChildrenTracingTags:(int32_t)disableChildrenTracingTags;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -50,6 +50,9 @@ static void traceLayerSurfaces(int32_t tracingTag, int depth, CALayer * _Nonnull
|
||||
[array addObject:sublayer];
|
||||
hadTraceableSublayers = true;
|
||||
}
|
||||
if (sublayerTraceableInfo.disableChildrenTracingTags & tracingTag) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipIfNoTraceableSublayers || !hadTraceableSublayers) {
|
||||
@ -347,12 +350,13 @@ static void traceLayerSurfaces(int32_t tracingTag, int depth, CALayer * _Nonnull
|
||||
|
||||
@implementation CATracingLayerInfo
|
||||
|
||||
- (instancetype _Nonnull)initWithShouldBeAdjustedToInverseTransform:(bool)shouldBeAdjustedToInverseTransform userData:(id _Nullable)userData tracingTag:(int32_t)tracingTag {
|
||||
- (instancetype _Nonnull)initWithShouldBeAdjustedToInverseTransform:(bool)shouldBeAdjustedToInverseTransform userData:(id _Nullable)userData tracingTag:(int32_t)tracingTag disableChildrenTracingTags:(int32_t)disableChildrenTracingTags {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_shouldBeAdjustedToInverseTransform = shouldBeAdjustedToInverseTransform;
|
||||
_userData = userData;
|
||||
_tracingTag = tracingTag;
|
||||
_disableChildrenTracingTags = disableChildrenTracingTags;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -233,23 +233,39 @@ public extension ContainedViewLayoutTransition {
|
||||
}
|
||||
}
|
||||
|
||||
func animatePositionAdditive(node: ASDisplayNode, offset: CGFloat) {
|
||||
func animatePositionAdditive(node: ASDisplayNode, offset: CGFloat, removeOnCompletion: Bool = true, completion: @escaping (Bool) -> Void) {
|
||||
switch self {
|
||||
case .immediate:
|
||||
break
|
||||
case let .animated(duration, curve):
|
||||
let timingFunction: String
|
||||
switch curve {
|
||||
case .easeInOut:
|
||||
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
|
||||
case .spring:
|
||||
timingFunction = kCAMediaTimingFunctionSpring
|
||||
}
|
||||
node.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
|
||||
case .immediate:
|
||||
break
|
||||
case let .animated(duration, curve):
|
||||
let timingFunction: String
|
||||
switch curve {
|
||||
case .easeInOut:
|
||||
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
|
||||
case .spring:
|
||||
timingFunction = kCAMediaTimingFunctionSpring
|
||||
}
|
||||
node.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
func animatePositionAdditive(node: ASDisplayNode, offset: CGPoint, completion: (() -> Void)? = nil) {
|
||||
func animatePositionAdditive(layer: CALayer, offset: CGFloat, removeOnCompletion: Bool = true, completion: @escaping (Bool) -> Void) {
|
||||
switch self {
|
||||
case .immediate:
|
||||
break
|
||||
case let .animated(duration, curve):
|
||||
let timingFunction: String
|
||||
switch curve {
|
||||
case .easeInOut:
|
||||
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
|
||||
case .spring:
|
||||
timingFunction = kCAMediaTimingFunctionSpring
|
||||
}
|
||||
layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
func animatePositionAdditive(node: ASDisplayNode, offset: CGPoint, removeOnCompletion: Bool = true, completion: (() -> Void)? = nil) {
|
||||
switch self {
|
||||
case .immediate:
|
||||
break
|
||||
@ -261,27 +277,27 @@ public extension ContainedViewLayoutTransition {
|
||||
case .spring:
|
||||
timingFunction = kCAMediaTimingFunctionSpring
|
||||
}
|
||||
node.layer.animatePosition(from: offset, to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true, completion: { _ in
|
||||
node.layer.animatePosition(from: offset, to: CGPoint(), duration: duration, timingFunction: timingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: { _ in
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func animatePositionAdditive(layer: CALayer, offset: CGPoint, completion: (() -> Void)? = nil) {
|
||||
func animatePositionAdditive(layer: CALayer, offset: CGPoint, to toOffset: CGPoint = CGPoint(), removeOnCompletion: Bool = true, completion: (() -> Void)? = nil) {
|
||||
switch self {
|
||||
case .immediate:
|
||||
break
|
||||
case let .animated(duration, curve):
|
||||
let timingFunction: String
|
||||
switch curve {
|
||||
case .easeInOut:
|
||||
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
|
||||
case .spring:
|
||||
timingFunction = kCAMediaTimingFunctionSpring
|
||||
}
|
||||
layer.animatePosition(from: offset, to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true, completion: { _ in
|
||||
completion?()
|
||||
})
|
||||
case .immediate:
|
||||
break
|
||||
case let .animated(duration, curve):
|
||||
let timingFunction: String
|
||||
switch curve {
|
||||
case .easeInOut:
|
||||
timingFunction = kCAMediaTimingFunctionEaseInEaseOut
|
||||
case .spring:
|
||||
timingFunction = kCAMediaTimingFunctionSpring
|
||||
}
|
||||
layer.animatePosition(from: offset, to: toOffset, duration: duration, timingFunction: timingFunction, removeOnCompletion: removeOnCompletion, additive: true, completion: { _ in
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ public final class ContextMenuController: ViewController {
|
||||
self.actions = actions
|
||||
self.catchTapsOutside = catchTapsOutside
|
||||
|
||||
super.init(navigationBarTheme: nil)
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
|
||||
@ -200,7 +200,7 @@ final class ContextMenuNode: ASDisplayNode {
|
||||
}
|
||||
self.arrowOnBottom = arrowOnBottom
|
||||
|
||||
let horizontalOrigin: CGFloat = floor(min(max(8.0, sourceRect.midX - actionsWidth / 2.0), layout.size.width - actionsWidth - 8.0))
|
||||
let horizontalOrigin: CGFloat = floor(min(max(sourceRect.minX + 8.0, sourceRect.midX - actionsWidth / 2.0), layout.size.width - actionsWidth - 8.0))
|
||||
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(x: horizontalOrigin, y: verticalOrigin), size: CGSize(width: actionsWidth, height: 54.0))
|
||||
self.containerNode.relativeArrowPosition = (sourceRect.midX - horizontalOrigin, arrowOnBottom)
|
||||
|
||||
@ -5,11 +5,93 @@ public class ImmediateTextNode: TextNode {
|
||||
public var textAlignment: NSTextAlignment = .natural
|
||||
public var maximumNumberOfLines: Int = 1
|
||||
public var lineSpacing: CGFloat = 0.0
|
||||
public var insets: UIEdgeInsets = UIEdgeInsets()
|
||||
|
||||
private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer?
|
||||
private var linkHighlightingNode: LinkHighlightingNode?
|
||||
|
||||
public var linkHighlightColor: UIColor?
|
||||
|
||||
public var highlightAttributeAction: (([NSAttributedStringKey: Any]) -> NSAttributedStringKey?)? {
|
||||
didSet {
|
||||
if self.isNodeLoaded {
|
||||
self.updateInteractiveActions()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var tapAttributeAction: (([NSAttributedStringKey: Any]) -> Void)?
|
||||
|
||||
public func updateLayout(_ constrainedSize: CGSize) -> CGSize {
|
||||
let makeLayout = TextNode.asyncLayout(self)
|
||||
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: .end, constrainedSize: constrainedSize, alignment: self.textAlignment, lineSpacing: self.lineSpacing, cutout: nil, insets: UIEdgeInsets()))
|
||||
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: .end, constrainedSize: constrainedSize, alignment: self.textAlignment, lineSpacing: self.lineSpacing, cutout: nil, insets: self.insets))
|
||||
let _ = apply()
|
||||
return layout.size
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
self.updateInteractiveActions()
|
||||
}
|
||||
|
||||
private func updateInteractiveActions() {
|
||||
if self.highlightAttributeAction != nil {
|
||||
if self.tapRecognizer == nil {
|
||||
let tapRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapAction(_:)))
|
||||
tapRecognizer.highlight = { [weak self] point in
|
||||
if let strongSelf = self {
|
||||
var rects: [CGRect]?
|
||||
if let point = point {
|
||||
if let (index, attributes) = strongSelf.attributesAtPoint(CGPoint(x: point.x, y: point.y)) {
|
||||
if let selectedAttribute = strongSelf.highlightAttributeAction?(attributes) {
|
||||
rects = strongSelf.attributeRects(name: selectedAttribute.rawValue, at: index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let rects = rects {
|
||||
let linkHighlightingNode: LinkHighlightingNode
|
||||
if let current = strongSelf.linkHighlightingNode {
|
||||
linkHighlightingNode = current
|
||||
} else {
|
||||
linkHighlightingNode = LinkHighlightingNode(color: strongSelf.linkHighlightColor ?? .clear)
|
||||
strongSelf.linkHighlightingNode = linkHighlightingNode
|
||||
strongSelf.addSubnode(linkHighlightingNode)
|
||||
}
|
||||
linkHighlightingNode.frame = strongSelf.bounds
|
||||
linkHighlightingNode.updateRects(rects.map { $0.offsetBy(dx: 0.0, dy: -3.0) })
|
||||
} else if let linkHighlightingNode = strongSelf.linkHighlightingNode {
|
||||
strongSelf.linkHighlightingNode = nil
|
||||
linkHighlightingNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, removeOnCompletion: false, completion: { [weak linkHighlightingNode] _ in
|
||||
linkHighlightingNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
self.view.addGestureRecognizer(tapRecognizer)
|
||||
}
|
||||
} else if let tapRecognizer = self.tapRecognizer {
|
||||
self.tapRecognizer = nil
|
||||
self.view.removeGestureRecognizer(tapRecognizer)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func tapAction(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case .ended:
|
||||
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||
switch gesture {
|
||||
case .tap:
|
||||
if let (_, attributes) = self.attributesAtPoint(CGPoint(x: location.x, y: location.y)) {
|
||||
self.tapAttributeAction?(attributes)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
9
Display/LayoutSizes.swift
Normal file
9
Display/LayoutSizes.swift
Normal file
@ -0,0 +1,9 @@
|
||||
import Foundation
|
||||
|
||||
public func horizontalContainerFillingSizeForLayout(layout: ContainerViewLayout, sideInset: CGFloat) -> CGFloat {
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
return min(layout.size.width, 414.0) - sideInset * 2.0
|
||||
} else {
|
||||
return layout.size.width - sideInset * 2.0
|
||||
}
|
||||
}
|
||||
@ -33,7 +33,7 @@ open class LegacyPresentedController: ViewController {
|
||||
self.legacyController = legacyController
|
||||
self.presentation = presentation
|
||||
|
||||
super.init(navigationBarTheme: nil)
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
/*legacyController.navigation_setDismiss { [weak self] in
|
||||
self?.dismiss()
|
||||
|
||||
237
Display/LinkHighlightingNode.swift
Normal file
237
Display/LinkHighlightingNode.swift
Normal file
@ -0,0 +1,237 @@
|
||||
import Foundation
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
|
||||
private enum CornerType {
|
||||
case topLeft
|
||||
case topRight
|
||||
case bottomLeft
|
||||
case bottomRight
|
||||
}
|
||||
|
||||
private func drawFullCorner(context: CGContext, color: UIColor, at point: CGPoint, type: CornerType, radius: CGFloat) {
|
||||
context.setFillColor(color.cgColor)
|
||||
switch type {
|
||||
case .topLeft:
|
||||
context.clear(CGRect(origin: point, size: CGSize(width: radius, height: radius)))
|
||||
context.fillEllipse(in: CGRect(origin: point, size: CGSize(width: radius * 2.0, height: radius * 2.0)))
|
||||
case .topRight:
|
||||
context.clear(CGRect(origin: CGPoint(x: point.x - radius, y: point.y), size: CGSize(width: radius, height: radius)))
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: point.x - radius * 2.0, y: point.y), size: CGSize(width: radius * 2.0, height: radius * 2.0)))
|
||||
case .bottomLeft:
|
||||
context.clear(CGRect(origin: CGPoint(x: point.x, y: point.y - radius), size: CGSize(width: radius, height: radius)))
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: point.x, y: point.y - radius * 2.0), size: CGSize(width: radius * 2.0, height: radius * 2.0)))
|
||||
case .bottomRight:
|
||||
context.clear(CGRect(origin: CGPoint(x: point.x - radius, y: point.y - radius), size: CGSize(width: radius, height: radius)))
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: point.x - radius * 2.0, y: point.y - radius * 2.0), size: CGSize(width: radius * 2.0, height: radius * 2.0)))
|
||||
}
|
||||
}
|
||||
|
||||
private func drawConnectingCorner(context: CGContext, color: UIColor, at point: CGPoint, type: CornerType, radius: CGFloat) {
|
||||
context.setFillColor(color.cgColor)
|
||||
switch type {
|
||||
case .topLeft:
|
||||
context.fill(CGRect(origin: CGPoint(x: point.x - radius, y: point.y), size: CGSize(width: radius, height: radius)))
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: point.x - radius * 2.0, y: point.y), size: CGSize(width: radius * 2.0, height: radius * 2.0)))
|
||||
case .topRight:
|
||||
context.fill(CGRect(origin: CGPoint(x: point.x, y: point.y), size: CGSize(width: radius, height: radius)))
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: point.x, y: point.y), size: CGSize(width: radius * 2.0, height: radius * 2.0)))
|
||||
case .bottomLeft:
|
||||
context.fill(CGRect(origin: CGPoint(x: point.x - radius, y: point.y - radius), size: CGSize(width: radius, height: radius)))
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: point.x - radius * 2.0, y: point.y - radius * 2.0), size: CGSize(width: radius * 2.0, height: radius * 2.0)))
|
||||
case .bottomRight:
|
||||
context.fill(CGRect(origin: CGPoint(x: point.x, y: point.y - radius), size: CGSize(width: radius, height: radius)))
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: point.x, y: point.y - radius * 2.0), size: CGSize(width: radius * 2.0, height: radius * 2.0)))
|
||||
}
|
||||
}
|
||||
|
||||
private func generateRectsImage(color: UIColor, rects: [CGRect], inset: CGFloat, outerRadius: CGFloat, innerRadius: CGFloat) -> (CGPoint, UIImage?) {
|
||||
if rects.isEmpty {
|
||||
return (CGPoint(), nil)
|
||||
}
|
||||
|
||||
var topLeft = rects[0].origin
|
||||
var bottomRight = CGPoint(x: rects[0].maxX, y: rects[0].maxY)
|
||||
for i in 1 ..< rects.count {
|
||||
topLeft.x = min(topLeft.x, rects[i].origin.x)
|
||||
topLeft.y = min(topLeft.y, rects[i].origin.y)
|
||||
bottomRight.x = max(bottomRight.x, rects[i].maxX)
|
||||
bottomRight.y = max(bottomRight.y, rects[i].maxY)
|
||||
}
|
||||
|
||||
topLeft.x -= inset
|
||||
topLeft.y -= inset
|
||||
bottomRight.x += inset * 2.0
|
||||
bottomRight.y += inset * 2.0
|
||||
|
||||
return (topLeft, generateImage(CGSize(width: bottomRight.x - topLeft.x, height: bottomRight.y - topLeft.y), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
context.setFillColor(color.cgColor)
|
||||
|
||||
context.setBlendMode(.copy)
|
||||
|
||||
for i in 0 ..< rects.count {
|
||||
let rect = rects[i].insetBy(dx: -inset, dy: -inset)
|
||||
context.fill(rect.offsetBy(dx: -topLeft.x, dy: -topLeft.y))
|
||||
}
|
||||
|
||||
for i in 0 ..< rects.count {
|
||||
let rect = rects[i].insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y)
|
||||
|
||||
var previous: CGRect?
|
||||
if i != 0 {
|
||||
previous = rects[i - 1].insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y)
|
||||
}
|
||||
|
||||
var next: CGRect?
|
||||
if i != rects.count - 1 {
|
||||
next = rects[i + 1].insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y)
|
||||
}
|
||||
|
||||
if let previous = previous {
|
||||
if previous.contains(rect.topLeft) {
|
||||
if abs(rect.topLeft.x - previous.minX) >= innerRadius {
|
||||
var radius = innerRadius
|
||||
if let next = next {
|
||||
radius = min(radius, floor((next.minY - previous.maxY) / 2.0))
|
||||
}
|
||||
drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.topLeft.x, y: previous.maxY), type: .topLeft, radius: radius)
|
||||
}
|
||||
} else {
|
||||
drawFullCorner(context: context, color: color, at: rect.topLeft, type: .topLeft, radius: outerRadius)
|
||||
}
|
||||
if previous.contains(rect.topRight.offsetBy(dx: -1.0, dy: 0.0)) {
|
||||
if abs(rect.topRight.x - previous.maxX) >= innerRadius {
|
||||
var radius = innerRadius
|
||||
if let next = next {
|
||||
radius = min(radius, floor((next.minY - previous.maxY) / 2.0))
|
||||
}
|
||||
drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.topRight.x, y: previous.maxY), type: .topRight, radius: radius)
|
||||
}
|
||||
} else {
|
||||
drawFullCorner(context: context, color: color, at: rect.topRight, type: .topRight, radius: outerRadius)
|
||||
}
|
||||
} else {
|
||||
drawFullCorner(context: context, color: color, at: rect.topLeft, type: .topLeft, radius: outerRadius)
|
||||
drawFullCorner(context: context, color: color, at: rect.topRight, type: .topRight, radius: outerRadius)
|
||||
}
|
||||
|
||||
if let next = next {
|
||||
if next.contains(rect.bottomLeft) {
|
||||
if abs(rect.bottomRight.x - next.maxX) >= innerRadius {
|
||||
var radius = innerRadius
|
||||
if let previous = previous {
|
||||
radius = min(radius, floor((next.minY - previous.maxY) / 2.0))
|
||||
}
|
||||
drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.bottomLeft.x, y: next.minY), type: .bottomLeft, radius: radius)
|
||||
}
|
||||
} else {
|
||||
drawFullCorner(context: context, color: color, at: rect.bottomLeft, type: .bottomLeft, radius: outerRadius)
|
||||
}
|
||||
if next.contains(rect.bottomRight.offsetBy(dx: -1.0, dy: 0.0)) {
|
||||
if abs(rect.bottomRight.x - next.maxX) >= innerRadius {
|
||||
var radius = innerRadius
|
||||
if let previous = previous {
|
||||
radius = min(radius, floor((next.minY - previous.maxY) / 2.0))
|
||||
}
|
||||
drawConnectingCorner(context: context, color: color, at: CGPoint(x: rect.bottomRight.x, y: next.minY), type: .bottomRight, radius: radius)
|
||||
}
|
||||
} else {
|
||||
drawFullCorner(context: context, color: color, at: rect.bottomRight, type: .bottomRight, radius: outerRadius)
|
||||
}
|
||||
} else {
|
||||
drawFullCorner(context: context, color: color, at: rect.bottomLeft, type: .bottomLeft, radius: outerRadius)
|
||||
drawFullCorner(context: context, color: color, at: rect.bottomRight, type: .bottomRight, radius: outerRadius)
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
}
|
||||
|
||||
public final class LinkHighlightingNode: ASDisplayNode {
|
||||
private var rects: [CGRect] = []
|
||||
private let imageNode: ASImageNode
|
||||
|
||||
public var innerRadius: CGFloat = 4.0
|
||||
public var outerRadius: CGFloat = 4.0
|
||||
public var inset: CGFloat = 2.0
|
||||
|
||||
private var _color: UIColor
|
||||
public var color: UIColor {
|
||||
get {
|
||||
return _color
|
||||
} set(value) {
|
||||
self._color = value
|
||||
if !self.rects.isEmpty {
|
||||
self.updateImage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public init(color: UIColor) {
|
||||
self._color = color
|
||||
|
||||
self.imageNode = ASImageNode()
|
||||
self.imageNode.isLayerBacked = true
|
||||
self.imageNode.displaysAsynchronously = false
|
||||
self.imageNode.displayWithoutProcessing = true
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.imageNode)
|
||||
}
|
||||
|
||||
public func updateRects(_ rects: [CGRect]) {
|
||||
if self.rects != rects {
|
||||
self.rects = rects
|
||||
|
||||
self.updateImage()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateImage() {
|
||||
if rects.isEmpty {
|
||||
self.imageNode.image = nil
|
||||
}
|
||||
let (offset, image) = generateRectsImage(color: self.color, rects: self.rects, inset: self.inset, outerRadius: self.outerRadius, innerRadius: self.innerRadius)
|
||||
|
||||
if let image = image {
|
||||
self.imageNode.image = image
|
||||
self.imageNode.frame = CGRect(origin: offset, size: image.size)
|
||||
}
|
||||
}
|
||||
|
||||
public func asyncLayout() -> (UIColor, [CGRect], CGFloat, CGFloat, CGFloat) -> () -> Void {
|
||||
let currentRects = self.rects
|
||||
let currentColor = self._color
|
||||
let currentInnerRadius = self.innerRadius
|
||||
let currentOuterRadius = self.outerRadius
|
||||
let currentInset = self.inset
|
||||
|
||||
return { [weak self] color, rects, innerRadius, outerRadius, inset in
|
||||
var updatedImage: (CGPoint, UIImage?)?
|
||||
if currentRects != rects || !currentColor.isEqual(color) || currentInnerRadius != innerRadius || currentOuterRadius != outerRadius || currentInset != inset {
|
||||
updatedImage = generateRectsImage(color: color, rects: rects, inset: inset, outerRadius: outerRadius, innerRadius: innerRadius)
|
||||
}
|
||||
|
||||
return {
|
||||
if let strongSelf = self {
|
||||
strongSelf._color = color
|
||||
strongSelf.rects = rects
|
||||
strongSelf.innerRadius = innerRadius
|
||||
strongSelf.outerRadius = outerRadius
|
||||
strongSelf.inset = inset
|
||||
|
||||
if let (offset, maybeImage) = updatedImage, let image = maybeImage {
|
||||
strongSelf.imageNode.image = image
|
||||
strongSelf.imageNode.frame = CGRect(origin: offset, size: image.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -951,7 +951,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
var lowestOverlayNode: ListViewItemNode?
|
||||
|
||||
for itemNode in self.itemNodes {
|
||||
if itemNode.isHighligtedInOverlay {
|
||||
if itemNode.isHighlightedInOverlay {
|
||||
lowestOverlayNode = itemNode
|
||||
itemNode.view.superview?.bringSubview(toFront: itemNode.view)
|
||||
}
|
||||
@ -2472,7 +2472,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
self.updateScroller(transition: headerNodesTransition.0)
|
||||
|
||||
if let topItemOverscrollBackground = self.topItemOverscrollBackground {
|
||||
headerNodesTransition.0.animatePositionAdditive(node: topItemOverscrollBackground, offset: -headerNodesTransition.2)
|
||||
headerNodesTransition.0.animatePositionAdditive(node: topItemOverscrollBackground, offset: CGPoint(x: 0.0, y: -headerNodesTransition.2))
|
||||
}
|
||||
|
||||
self.setNeedsAnimations()
|
||||
@ -2496,7 +2496,7 @@ open class ListView: ASDisplayNode, UIScrollViewDelegate, UIGestureRecognizerDel
|
||||
self.updateScroller(transition: headerNodesTransition.0)
|
||||
|
||||
if let topItemOverscrollBackground = self.topItemOverscrollBackground {
|
||||
headerNodesTransition.0.animatePositionAdditive(node: topItemOverscrollBackground, offset: -headerNodesTransition.2)
|
||||
headerNodesTransition.0.animatePositionAdditive(node: topItemOverscrollBackground, offset: CGPoint(x: 0.0, y: -headerNodesTransition.2))
|
||||
}
|
||||
|
||||
self.updateVisibleContentOffset()
|
||||
|
||||
@ -72,7 +72,7 @@ open class ListViewItemNode: ASDisplayNode {
|
||||
let rotated: Bool
|
||||
final var index: Int?
|
||||
|
||||
public var isHighligtedInOverlay: Bool = false
|
||||
public var isHighlightedInOverlay: Bool = false
|
||||
|
||||
public private(set) var accessoryItemNode: ListViewAccessoryItemNode?
|
||||
|
||||
|
||||
@ -38,6 +38,26 @@ public final class NavigationBarTheme {
|
||||
}
|
||||
}
|
||||
|
||||
public final class NavigationBarStrings {
|
||||
public let back: String
|
||||
public let close: String
|
||||
|
||||
public init(back: String, close: String) {
|
||||
self.back = back
|
||||
self.close = close
|
||||
}
|
||||
}
|
||||
|
||||
public final class NavigationBarPresentationData {
|
||||
public let theme: NavigationBarTheme
|
||||
public let strings: NavigationBarStrings
|
||||
|
||||
public init(theme: NavigationBarTheme, strings: NavigationBarStrings) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
}
|
||||
}
|
||||
|
||||
private func backArrowImage(color: UIColor) -> UIImage? {
|
||||
var red: CGFloat = 0.0
|
||||
var green: CGFloat = 0.0
|
||||
@ -58,8 +78,30 @@ private func backArrowImage(color: UIColor) -> UIImage? {
|
||||
}
|
||||
}
|
||||
|
||||
enum NavigationPreviousAction: Equatable {
|
||||
case item(UINavigationItem)
|
||||
case close
|
||||
|
||||
static func ==(lhs: NavigationPreviousAction, rhs: NavigationPreviousAction) -> Bool {
|
||||
switch lhs {
|
||||
case let .item(lhsItem):
|
||||
if case let .item(rhsItem) = rhs, lhsItem === rhsItem {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .close:
|
||||
if case .close = rhs {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class NavigationBar: ASDisplayNode {
|
||||
private var theme: NavigationBarTheme
|
||||
private var presentationData: NavigationBarPresentationData
|
||||
|
||||
private var validLayout: (CGSize, CGFloat, CGFloat)?
|
||||
private var requestedLayout: Bool = false
|
||||
@ -201,7 +243,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
private var title: String? {
|
||||
didSet {
|
||||
if let title = self.title {
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: self.theme.primaryTextColor)
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: self.presentationData.theme.primaryTextColor)
|
||||
if self.titleNode.supernode == nil {
|
||||
self.clippingNode.addSubnode(self.titleNode)
|
||||
}
|
||||
@ -234,52 +276,59 @@ open class NavigationBar: ASDisplayNode {
|
||||
var previousItemListenerKey: Int?
|
||||
var previousItemBackListenerKey: Int?
|
||||
|
||||
var _previousItem: UINavigationItem?
|
||||
var previousItem: UINavigationItem? {
|
||||
var _previousItem: NavigationPreviousAction?
|
||||
var previousItem: NavigationPreviousAction? {
|
||||
get {
|
||||
return self._previousItem
|
||||
} set(value) {
|
||||
if let previousValue = self._previousItem {
|
||||
if let previousItemListenerKey = self.previousItemListenerKey {
|
||||
previousValue.removeSetTitleListener(previousItemListenerKey)
|
||||
self.previousItemListenerKey = nil
|
||||
}
|
||||
if let previousItemBackListenerKey = self.previousItemBackListenerKey {
|
||||
previousValue.removeSetBackBarButtonItemListener(previousItemBackListenerKey)
|
||||
self.previousItemBackListenerKey = nil
|
||||
}
|
||||
}
|
||||
self._previousItem = value
|
||||
|
||||
if let previousItem = value {
|
||||
self.previousItemListenerKey = previousItem.addSetTitleListener { [weak self] _, _ in
|
||||
if let strongSelf = self, let previousItem = strongSelf.previousItem {
|
||||
if let backBarButtonItem = previousItem.backBarButtonItem {
|
||||
strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "")
|
||||
} else {
|
||||
strongSelf.backButtonNode.updateManualText(previousItem.title ?? "")
|
||||
}
|
||||
strongSelf.invalidateCalculatedLayout()
|
||||
strongSelf.requestLayout()
|
||||
if self._previousItem != value {
|
||||
if let previousValue = self._previousItem, case let .item(itemValue) = previousValue {
|
||||
if let previousItemListenerKey = self.previousItemListenerKey {
|
||||
itemValue.removeSetTitleListener(previousItemListenerKey)
|
||||
self.previousItemListenerKey = nil
|
||||
}
|
||||
if let previousItemBackListenerKey = self.previousItemBackListenerKey {
|
||||
itemValue.removeSetBackBarButtonItemListener(previousItemBackListenerKey)
|
||||
self.previousItemBackListenerKey = nil
|
||||
}
|
||||
}
|
||||
self._previousItem = value
|
||||
|
||||
self.previousItemBackListenerKey = previousItem.addSetBackBarButtonItemListener { [weak self] _, _, _ in
|
||||
if let strongSelf = self, let previousItem = strongSelf.previousItem {
|
||||
if let backBarButtonItem = previousItem.backBarButtonItem {
|
||||
strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "")
|
||||
} else {
|
||||
strongSelf.backButtonNode.updateManualText(previousItem.title ?? "")
|
||||
}
|
||||
strongSelf.invalidateCalculatedLayout()
|
||||
strongSelf.requestLayout()
|
||||
if let previousItem = value {
|
||||
switch previousItem {
|
||||
case let .item(itemValue):
|
||||
self.previousItemListenerKey = itemValue.addSetTitleListener { [weak self] _, _ in
|
||||
if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem {
|
||||
if let backBarButtonItem = itemValue.backBarButtonItem {
|
||||
strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "")
|
||||
} else {
|
||||
strongSelf.backButtonNode.updateManualText(itemValue.title ?? "")
|
||||
}
|
||||
strongSelf.invalidateCalculatedLayout()
|
||||
strongSelf.requestLayout()
|
||||
}
|
||||
}
|
||||
|
||||
self.previousItemBackListenerKey = itemValue.addSetBackBarButtonItemListener { [weak self] _, _, _ in
|
||||
if let strongSelf = self, let previousItem = strongSelf.previousItem, case let .item(itemValue) = previousItem {
|
||||
if let backBarButtonItem = itemValue.backBarButtonItem {
|
||||
strongSelf.backButtonNode.updateManualText(backBarButtonItem.title ?? "")
|
||||
} else {
|
||||
strongSelf.backButtonNode.updateManualText(itemValue.title ?? "")
|
||||
}
|
||||
strongSelf.invalidateCalculatedLayout()
|
||||
strongSelf.requestLayout()
|
||||
}
|
||||
}
|
||||
case .close:
|
||||
break
|
||||
}
|
||||
}
|
||||
self.updateLeftButton(animated: false)
|
||||
|
||||
self.invalidateCalculatedLayout()
|
||||
self.requestLayout()
|
||||
}
|
||||
self.updateLeftButton(animated: false)
|
||||
|
||||
self.invalidateCalculatedLayout()
|
||||
self.requestLayout()
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,7 +345,14 @@ open class NavigationBar: ASDisplayNode {
|
||||
|
||||
private func updateLeftButton(animated: Bool) {
|
||||
if let item = self.item {
|
||||
var needsLeftButton = false
|
||||
if let leftBarButtonItem = item.leftBarButtonItem, !leftBarButtonItem.backButtonAppearance {
|
||||
needsLeftButton = true
|
||||
} else if let previousItem = self.previousItem, case .close = previousItem {
|
||||
needsLeftButton = true
|
||||
}
|
||||
|
||||
if needsLeftButton {
|
||||
if animated {
|
||||
if self.leftButtonNode.view.superview != nil {
|
||||
if let snapshotView = self.leftButtonNode.view.snapshotContentTree() {
|
||||
@ -343,7 +399,11 @@ open class NavigationBar: ASDisplayNode {
|
||||
self.backButtonArrow.removeFromSupernode()
|
||||
self.badgeNode.removeFromSupernode()
|
||||
|
||||
self.leftButtonNode.updateItems([leftBarButtonItem])
|
||||
if let leftBarButtonItem = item.leftBarButtonItem {
|
||||
self.leftButtonNode.updateItems([leftBarButtonItem])
|
||||
} else {
|
||||
self.leftButtonNode.updateItems([UIBarButtonItem(title: self.presentationData.strings.close, style: .plain, target: nil, action: nil)])
|
||||
}
|
||||
|
||||
if self.leftButtonNode.supernode == nil {
|
||||
self.clippingNode.addSubnode(self.leftButtonNode)
|
||||
@ -370,10 +430,15 @@ open class NavigationBar: ASDisplayNode {
|
||||
if let leftBarButtonItem = item.leftBarButtonItem, leftBarButtonItem.backButtonAppearance {
|
||||
backTitle = leftBarButtonItem.title
|
||||
} else if let previousItem = self.previousItem {
|
||||
if let backBarButtonItem = previousItem.backBarButtonItem {
|
||||
backTitle = backBarButtonItem.title ?? "Back"
|
||||
} else {
|
||||
backTitle = previousItem.title ?? "Back"
|
||||
switch previousItem {
|
||||
case let .item(itemValue):
|
||||
if let backBarButtonItem = itemValue.backBarButtonItem {
|
||||
backTitle = backBarButtonItem.title ?? self.presentationData.strings.back
|
||||
} else {
|
||||
backTitle = itemValue.title ?? self.presentationData.strings.back
|
||||
}
|
||||
case .close:
|
||||
backTitle = nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,7 +548,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
if let value = value {
|
||||
switch value.role {
|
||||
case .top:
|
||||
if let transitionTitleNode = value.navigationBar?.makeTransitionTitleNode(foregroundColor: self.theme.primaryTextColor) {
|
||||
if let transitionTitleNode = value.navigationBar?.makeTransitionTitleNode(foregroundColor: self.presentationData.theme.primaryTextColor) {
|
||||
self.transitionTitleNode = transitionTitleNode
|
||||
if self.leftButtonNode.supernode != nil {
|
||||
self.clippingNode.insertSubnode(transitionTitleNode, belowSubnode: self.leftButtonNode)
|
||||
@ -494,11 +559,11 @@ open class NavigationBar: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
case .bottom:
|
||||
if let transitionBackButtonNode = value.navigationBar?.makeTransitionBackButtonNode(accentColor: self.theme.buttonColor) {
|
||||
if let transitionBackButtonNode = value.navigationBar?.makeTransitionBackButtonNode(accentColor: self.presentationData.theme.buttonColor) {
|
||||
self.transitionBackButtonNode = transitionBackButtonNode
|
||||
self.clippingNode.addSubnode(transitionBackButtonNode)
|
||||
}
|
||||
if let transitionBackArrowNode = value.navigationBar?.makeTransitionBackArrowNode(accentColor: self.theme.buttonColor) {
|
||||
if let transitionBackArrowNode = value.navigationBar?.makeTransitionBackArrowNode(accentColor: self.presentationData.theme.buttonColor) {
|
||||
self.transitionBackArrowNode = transitionBackArrowNode
|
||||
self.clippingNode.addSubnode(transitionBackArrowNode)
|
||||
}
|
||||
@ -520,13 +585,13 @@ open class NavigationBar: ASDisplayNode {
|
||||
private var transitionBackArrowNode: ASDisplayNode?
|
||||
private var transitionBadgeNode: ASDisplayNode?
|
||||
|
||||
public init(theme: NavigationBarTheme) {
|
||||
self.theme = theme
|
||||
public init(presentationData: NavigationBarPresentationData) {
|
||||
self.presentationData = presentationData
|
||||
self.stripeNode = ASDisplayNode()
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
self.backButtonNode = NavigationButtonNode()
|
||||
self.badgeNode = NavigationBarBadgeNode(fillColor: theme.badgeBackgroundColor, strokeColor: theme.badgeStrokeColor, textColor: theme.badgeTextColor)
|
||||
self.badgeNode = NavigationBarBadgeNode(fillColor: self.presentationData.theme.badgeBackgroundColor, strokeColor: self.presentationData.theme.badgeStrokeColor, textColor: self.presentationData.theme.badgeTextColor)
|
||||
self.badgeNode.isUserInteractionEnabled = false
|
||||
self.badgeNode.isHidden = true
|
||||
self.backButtonArrow = ASImageNode()
|
||||
@ -538,20 +603,20 @@ open class NavigationBar: ASDisplayNode {
|
||||
self.clippingNode = ASDisplayNode()
|
||||
self.clippingNode.clipsToBounds = true
|
||||
|
||||
self.backButtonNode.color = self.theme.buttonColor
|
||||
self.leftButtonNode.color = self.theme.buttonColor
|
||||
self.rightButtonNode.color = self.theme.buttonColor
|
||||
self.backButtonArrow.image = backArrowImage(color: self.theme.buttonColor)
|
||||
self.backButtonNode.color = self.presentationData.theme.buttonColor
|
||||
self.leftButtonNode.color = self.presentationData.theme.buttonColor
|
||||
self.rightButtonNode.color = self.presentationData.theme.buttonColor
|
||||
self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor)
|
||||
if let title = self.title {
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: self.theme.primaryTextColor)
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: self.presentationData.theme.primaryTextColor)
|
||||
}
|
||||
self.stripeNode.backgroundColor = self.theme.separatorColor
|
||||
self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.clippingNode)
|
||||
|
||||
self.backgroundColor = self.theme.backgroundColor
|
||||
self.backgroundColor = self.presentationData.theme.backgroundColor
|
||||
|
||||
self.stripeNode.isLayerBacked = true
|
||||
self.stripeNode.displaysAsynchronously = false
|
||||
@ -580,8 +645,10 @@ open class NavigationBar: ASDisplayNode {
|
||||
self.leftButtonNode.pressed = { [weak self] index in
|
||||
if let item = self?.item {
|
||||
if index == 0 {
|
||||
if let leftBarButtonItem = item.leftBarButtonItem {
|
||||
leftBarButtonItem.performActionOnTarget()
|
||||
if let leftBarButtonItem = item.leftBarButtonItem {
|
||||
leftBarButtonItem.performActionOnTarget()
|
||||
} else if let previousItem = self?.previousItem, case .close = previousItem {
|
||||
self?.backPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -600,22 +667,22 @@ open class NavigationBar: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public func updateTheme(_ theme: NavigationBarTheme) {
|
||||
if theme !== self.theme {
|
||||
self.theme = theme
|
||||
public func updatePresentationData(_ presentationData: NavigationBarPresentationData) {
|
||||
if presentationData.theme !== self.presentationData.theme || presentationData.strings !== self.presentationData.strings {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.backgroundColor = self.theme.backgroundColor
|
||||
self.backgroundColor = self.presentationData.theme.backgroundColor
|
||||
|
||||
self.backButtonNode.color = self.theme.buttonColor
|
||||
self.leftButtonNode.color = self.theme.buttonColor
|
||||
self.rightButtonNode.color = self.theme.buttonColor
|
||||
self.backButtonArrow.image = backArrowImage(color: self.theme.buttonColor)
|
||||
self.backButtonNode.color = self.presentationData.theme.buttonColor
|
||||
self.leftButtonNode.color = self.presentationData.theme.buttonColor
|
||||
self.rightButtonNode.color = self.presentationData.theme.buttonColor
|
||||
self.backButtonArrow.image = backArrowImage(color: self.presentationData.theme.buttonColor)
|
||||
if let title = self.title {
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: self.theme.primaryTextColor)
|
||||
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.semibold(17.0), textColor: self.presentationData.theme.primaryTextColor)
|
||||
}
|
||||
self.stripeNode.backgroundColor = self.theme.separatorColor
|
||||
self.stripeNode.backgroundColor = self.presentationData.theme.separatorColor
|
||||
|
||||
self.badgeNode.updateTheme(fillColor: theme.badgeBackgroundColor, strokeColor: theme.badgeStrokeColor, textColor: theme.badgeTextColor)
|
||||
self.badgeNode.updateTheme(fillColor: self.presentationData.theme.badgeBackgroundColor, strokeColor: self.presentationData.theme.badgeStrokeColor, textColor: self.presentationData.theme.badgeTextColor)
|
||||
}
|
||||
}
|
||||
|
||||
@ -649,11 +716,11 @@ open class NavigationBar: ASDisplayNode {
|
||||
let nominalHeight: CGFloat = self.collapsed ? 32.0 : 44.0
|
||||
let contentVerticalOrigin = size.height - nominalHeight
|
||||
|
||||
var leftTitleInset: CGFloat = leftInset + 4.0
|
||||
var rightTitleInset: CGFloat = rightInset + 4.0
|
||||
var leftTitleInset: CGFloat = leftInset + 1.0
|
||||
var rightTitleInset: CGFloat = rightInset + 1.0
|
||||
if self.backButtonNode.supernode != nil {
|
||||
let backButtonSize = self.backButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight))
|
||||
leftTitleInset += backButtonSize.width + backButtonInset + 4.0 + 4.0
|
||||
leftTitleInset += backButtonSize.width + backButtonInset + 1.0
|
||||
|
||||
let topHitTestSlop = (nominalHeight - backButtonSize.height) * 0.5
|
||||
self.backButtonNode.hitTestSlop = UIEdgeInsetsMake(-topHitTestSlop, -27.0, -topHitTestSlop, -8.0)
|
||||
@ -698,7 +765,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
}
|
||||
} else if self.leftButtonNode.supernode != nil {
|
||||
let leftButtonSize = self.leftButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight))
|
||||
leftTitleInset += leftButtonSize.width + leftButtonInset + 8.0 + 8.0
|
||||
leftTitleInset += leftButtonSize.width + leftButtonInset + 1.0
|
||||
|
||||
self.leftButtonNode.alpha = 1.0
|
||||
self.leftButtonNode.frame = CGRect(origin: CGPoint(x: leftButtonInset, y: contentVerticalOrigin + floor((nominalHeight - leftButtonSize.height) / 2.0)), size: leftButtonSize)
|
||||
@ -710,7 +777,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
|
||||
if self.rightButtonNode.supernode != nil {
|
||||
let rightButtonSize = self.rightButtonNode.updateLayout(constrainedSize: (CGSize(width: size.width, height: nominalHeight)))
|
||||
rightTitleInset += rightButtonSize.width + leftButtonInset + 8.0 + 8.0
|
||||
rightTitleInset += rightButtonSize.width + leftButtonInset + 1.0
|
||||
self.rightButtonNode.alpha = 1.0
|
||||
self.rightButtonNode.frame = CGRect(origin: CGPoint(x: size.width - leftButtonInset - rightButtonSize.width, y: contentVerticalOrigin + floor((nominalHeight - rightButtonSize.height) / 2.0)), size: rightButtonSize)
|
||||
}
|
||||
@ -783,7 +850,14 @@ open class NavigationBar: ASDisplayNode {
|
||||
|
||||
if let titleView = self.titleView {
|
||||
let titleSize = CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight)
|
||||
titleView.frame = CGRect(origin: CGPoint(x: leftTitleInset, y: contentVerticalOrigin), size: titleSize)
|
||||
let titleFrame = CGRect(origin: CGPoint(x: leftTitleInset, y: contentVerticalOrigin), size: titleSize)
|
||||
titleView.frame = titleFrame
|
||||
|
||||
if let titleView = titleView as? NavigationBarTitleView {
|
||||
let titleWidth = size.width - leftTitleInset - rightTitleInset
|
||||
|
||||
titleView.updateLayout(size: titleFrame.size, clearBounds: CGRect(origin: CGPoint(x: leftTitleInset - titleFrame.minX, y: 0.0), size: CGSize(width: titleWidth, height: titleFrame.height)), transition: transition)
|
||||
}
|
||||
|
||||
if let transitionState = self.transitionState, let otherNavigationBar = transitionState.navigationBar {
|
||||
let progress = transitionState.progress
|
||||
@ -861,7 +935,7 @@ open class NavigationBar: ASDisplayNode {
|
||||
|
||||
private func makeTransitionBadgeNode() -> ASDisplayNode? {
|
||||
if self.badgeNode.supernode != nil && !self.badgeNode.isHidden {
|
||||
let node = NavigationBarBadgeNode(fillColor: self.theme.badgeBackgroundColor, strokeColor: self.theme.badgeStrokeColor, textColor: self.theme.badgeTextColor)
|
||||
let node = NavigationBarBadgeNode(fillColor: self.presentationData.theme.badgeBackgroundColor, strokeColor: self.presentationData.theme.badgeStrokeColor, textColor: self.presentationData.theme.badgeTextColor)
|
||||
node.text = self.badgeNode.text
|
||||
let nodeSize = node.measure(CGSize(width: 200.0, height: 100.0))
|
||||
node.frame = CGRect(origin: CGPoint(), size: nodeSize)
|
||||
|
||||
@ -3,4 +3,6 @@ import UIKit
|
||||
|
||||
public protocol NavigationBarTitleView {
|
||||
func animateLayoutTransition()
|
||||
|
||||
func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition)
|
||||
}
|
||||
|
||||
@ -3,9 +3,48 @@ import UIKit
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
|
||||
private class NavigationControllerView: UIView {
|
||||
public final class NavigationControllerTheme {
|
||||
public let navigationBar: NavigationBarTheme
|
||||
public let emptyAreaColor: UIColor
|
||||
public let emptyDetailIcon: UIImage?
|
||||
|
||||
public init(navigationBar: NavigationBarTheme, emptyAreaColor: UIColor, emptyDetailIcon: UIImage?) {
|
||||
self.navigationBar = navigationBar
|
||||
self.emptyAreaColor = emptyAreaColor
|
||||
self.emptyDetailIcon = emptyDetailIcon
|
||||
}
|
||||
}
|
||||
|
||||
private final class NavigationControllerContainerView: UIView {
|
||||
override class var layerClass: AnyClass {
|
||||
return CATracingLayer.self
|
||||
}
|
||||
}
|
||||
|
||||
private final class NavigationControllerView: UIView {
|
||||
var inTransition = false
|
||||
|
||||
let sharedStatusBar: StatusBar
|
||||
let containerView: NavigationControllerContainerView
|
||||
let separatorView: UIView
|
||||
var navigationBackgroundView: UIView?
|
||||
var navigationSeparatorView: UIView?
|
||||
var emptyDetailView: UIImageView?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.containerView = NavigationControllerContainerView()
|
||||
self.separatorView = UIView()
|
||||
self.sharedStatusBar = StatusBar()
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.containerView)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override class var layerClass: AnyClass {
|
||||
return CATracingLayer.self
|
||||
}
|
||||
@ -18,9 +57,40 @@ private class NavigationControllerView: UIView {
|
||||
}
|
||||
}
|
||||
|
||||
private enum ControllerTransition {
|
||||
case none
|
||||
case appearance
|
||||
}
|
||||
|
||||
private final class ControllerRecord {
|
||||
let controller: UIViewController
|
||||
var transition: ControllerTransition = .none
|
||||
|
||||
init(controller: UIViewController) {
|
||||
self.controller = controller
|
||||
}
|
||||
}
|
||||
|
||||
private enum ControllerLayoutConfiguration {
|
||||
case single
|
||||
case masterDetail
|
||||
}
|
||||
|
||||
public enum NavigationControllerMode {
|
||||
case single
|
||||
case automaticMasterDetail
|
||||
}
|
||||
|
||||
open class NavigationController: UINavigationController, ContainableController, UIGestureRecognizerDelegate {
|
||||
private let mode: NavigationControllerMode
|
||||
private var theme: NavigationControllerTheme
|
||||
|
||||
public private(set) weak var overlayPresentingController: ViewController?
|
||||
|
||||
private var controllerView: NavigationControllerView {
|
||||
return self.view as! NavigationControllerView
|
||||
}
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private var navigationTransitionCoordinator: NavigationTransitionCoordinator?
|
||||
@ -28,35 +98,33 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
private var currentPushDisposable = MetaDisposable()
|
||||
private var currentPresentDisposable = MetaDisposable()
|
||||
|
||||
private var statusBarChangeObserver: AnyObject?
|
||||
|
||||
//private var layout: NavigationControllerLayout?
|
||||
//private var pendingLayout: (NavigationControllerLayout, Double, Bool)?
|
||||
|
||||
private var _presentedViewController: UIViewController?
|
||||
open override var presentedViewController: UIViewController? {
|
||||
return self._presentedViewController
|
||||
}
|
||||
|
||||
private var _viewControllers: [UIViewController] = []
|
||||
private var _viewControllers: [ControllerRecord] = []
|
||||
open override var viewControllers: [UIViewController] {
|
||||
get {
|
||||
return self._viewControllers
|
||||
return self._viewControllers.map { $0.controller }
|
||||
} set(value) {
|
||||
self.setViewControllers(_viewControllers, animated: false)
|
||||
self.setViewControllers(value, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
open override var topViewController: UIViewController? {
|
||||
return self._viewControllers.last
|
||||
return self._viewControllers.last?.controller
|
||||
}
|
||||
|
||||
public init() {
|
||||
public init(mode: NavigationControllerMode, theme: NavigationControllerTheme) {
|
||||
self.mode = mode
|
||||
self.theme = theme
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
||||
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
|
||||
preconditionFailure()
|
||||
}
|
||||
|
||||
public required init(coder aDecoder: NSCoder) {
|
||||
@ -68,6 +136,384 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
self.currentPresentDisposable.dispose()
|
||||
}
|
||||
|
||||
public func updateTheme(_ theme: NavigationControllerTheme) {
|
||||
self.theme = theme
|
||||
if self.isViewLoaded {
|
||||
self.controllerView.backgroundColor = theme.emptyAreaColor
|
||||
self.controllerView.separatorView.backgroundColor = theme.navigationBar.separatorColor
|
||||
self.controllerView.navigationBackgroundView?.backgroundColor = theme.navigationBar.backgroundColor
|
||||
self.controllerView.navigationSeparatorView?.backgroundColor = theme.navigationBar.separatorColor
|
||||
if let emptyDetailView = self.controllerView.emptyDetailView {
|
||||
emptyDetailView.image = theme.emptyDetailIcon
|
||||
if let image = theme.emptyDetailIcon {
|
||||
emptyDetailView.frame = CGRect(origin: CGPoint(x: floor((self.controllerView.containerView.bounds.size.width - image.size.width) / 2.0), y: floor((self.controllerView.containerView.bounds.size.height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var previouslyLaidOutMasterController: UIViewController?
|
||||
private var previouslyLaidOutTopController: UIViewController?
|
||||
|
||||
private func layoutConfiguration(for layout: ContainerViewLayout) -> ControllerLayoutConfiguration {
|
||||
switch self.mode {
|
||||
case .single:
|
||||
return .single
|
||||
case .automaticMasterDetail:
|
||||
if case .regular = layout.metrics.widthClass, case .regular = layout.metrics.heightClass {
|
||||
if layout.size.width > 690.0 {
|
||||
return .masterDetail
|
||||
}
|
||||
}
|
||||
return .single
|
||||
}
|
||||
}
|
||||
|
||||
private func layoutDataForConfiguration(_ layoutConfiguration: ControllerLayoutConfiguration, layout: ContainerViewLayout, index: Int) -> (CGRect, ContainerViewLayout) {
|
||||
switch layoutConfiguration {
|
||||
case .masterDetail:
|
||||
let masterWidth: CGFloat = max(320.0, floor(layout.size.width / 3.0))
|
||||
let detailWidth: CGFloat = layout.size.width - masterWidth
|
||||
if index == 0 {
|
||||
return (CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: masterWidth, height: layout.size.height)), ContainerViewLayout(size: CGSize(width: masterWidth, height: layout.size.height), metrics: layout.metrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging))
|
||||
} else {
|
||||
let detailFrame = CGRect(origin: CGPoint(x: masterWidth, y: 0.0), size: CGSize(width: detailWidth, height: layout.size.height))
|
||||
return (CGRect(origin: CGPoint(), size: detailFrame.size), ContainerViewLayout(size: CGSize(width: detailWidth, height: layout.size.height), metrics: LayoutMetrics(widthClass: .regular, heightClass: .regular), intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging))
|
||||
}
|
||||
case .single:
|
||||
return (CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: layout.size.height)), ContainerViewLayout(size: CGSize(width: layout.size.width, height: layout.size.height), metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging))
|
||||
}
|
||||
}
|
||||
|
||||
private func updateControllerLayouts(previousControllers: [ControllerRecord], layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
var firstControllerFrameAndLayout: (CGRect, ContainerViewLayout)?
|
||||
let lastControllerFrameAndLayout: (CGRect, ContainerViewLayout)
|
||||
|
||||
let layoutConfiguration = self.layoutConfiguration(for: layout)
|
||||
|
||||
switch layoutConfiguration {
|
||||
case .masterDetail:
|
||||
self.viewControllers.first?.view.clipsToBounds = true
|
||||
self.controllerView.containerView.clipsToBounds = true
|
||||
let masterData = layoutDataForConfiguration(layoutConfiguration, layout: layout, index: 0)
|
||||
firstControllerFrameAndLayout = masterData
|
||||
lastControllerFrameAndLayout = layoutDataForConfiguration(layoutConfiguration, layout: layout, index: 1)
|
||||
if self.controllerView.separatorView.superview == nil {
|
||||
self.controllerView.addSubview(self.controllerView.separatorView)
|
||||
}
|
||||
|
||||
let navigationBackgroundFrame = CGRect(origin: CGPoint(x: masterData.0.maxX, y: 0.0), size: CGSize(width: lastControllerFrameAndLayout.0.width, height: (layout.statusBarHeight ?? 0.0) + 44.0))
|
||||
|
||||
if let navigationBackgroundView = self.controllerView.navigationBackgroundView, let navigationSeparatorView = self.controllerView.navigationSeparatorView, let emptyDetailView = self.controllerView.emptyDetailView {
|
||||
transition.updateFrame(view: navigationBackgroundView, frame: navigationBackgroundFrame)
|
||||
transition.updateFrame(view: navigationSeparatorView, frame: CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel)))
|
||||
if let image = emptyDetailView.image {
|
||||
transition.updateFrame(view: emptyDetailView, frame: CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size))
|
||||
}
|
||||
} else {
|
||||
let navigationBackgroundView = UIView()
|
||||
navigationBackgroundView.backgroundColor = self.theme.navigationBar.backgroundColor
|
||||
let navigationSeparatorView = UIView()
|
||||
navigationSeparatorView.backgroundColor = self.theme.navigationBar.separatorColor
|
||||
let emptyDetailView = UIImageView()
|
||||
emptyDetailView.image = self.theme.emptyDetailIcon
|
||||
emptyDetailView.alpha = 0.0
|
||||
|
||||
self.controllerView.navigationBackgroundView = navigationBackgroundView
|
||||
self.controllerView.navigationSeparatorView = navigationSeparatorView
|
||||
self.controllerView.emptyDetailView = emptyDetailView
|
||||
|
||||
self.controllerView.insertSubview(navigationBackgroundView, at: 0)
|
||||
self.controllerView.insertSubview(navigationSeparatorView, at: 1)
|
||||
self.controllerView.insertSubview(emptyDetailView, at: 2)
|
||||
|
||||
navigationBackgroundView.frame = navigationBackgroundFrame
|
||||
navigationSeparatorView.frame = CGRect(origin: CGPoint(x: navigationBackgroundFrame.minX, y: navigationBackgroundFrame.maxY), size: CGSize(width: navigationBackgroundFrame.width, height: UIScreenPixel))
|
||||
|
||||
transition.animatePositionAdditive(layer: navigationBackgroundView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0))
|
||||
transition.animatePositionAdditive(layer: navigationSeparatorView.layer, offset: CGPoint(x: navigationBackgroundFrame.width, y: 0.0))
|
||||
|
||||
if let image = emptyDetailView.image {
|
||||
emptyDetailView.frame = CGRect(origin: CGPoint(x: masterData.0.maxX + floor((lastControllerFrameAndLayout.0.size.width - image.size.width) / 2.0), y: floor((lastControllerFrameAndLayout.0.size.height - image.size.height) / 2.0)), size: image.size)
|
||||
}
|
||||
|
||||
transition.updateAlpha(layer: emptyDetailView.layer, alpha: 1.0)
|
||||
}
|
||||
transition.updateFrame(view: self.controllerView.separatorView, frame: CGRect(origin: CGPoint(x: masterData.0.maxX, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height)))
|
||||
case .single:
|
||||
self.viewControllers.first?.view.clipsToBounds = false
|
||||
if let navigationBackgroundView = self.controllerView.navigationBackgroundView, let navigationSeparatorView = self.controllerView.navigationSeparatorView {
|
||||
self.controllerView.navigationBackgroundView = nil
|
||||
self.controllerView.navigationSeparatorView = nil
|
||||
|
||||
transition.updatePosition(layer: navigationBackgroundView.layer, position: CGPoint(x: layout.size.width + navigationBackgroundView.bounds.size.width / 2.0, y: navigationBackgroundView.center.y), completion: { [weak navigationBackgroundView] _ in
|
||||
navigationBackgroundView?.removeFromSuperview()
|
||||
})
|
||||
transition.updatePosition(layer: navigationSeparatorView.layer, position: CGPoint(x: layout.size.width + navigationSeparatorView.bounds.size.width / 2.0, y: navigationSeparatorView.center.y), completion: { [weak navigationSeparatorView] _ in
|
||||
navigationSeparatorView?.removeFromSuperview()
|
||||
})
|
||||
if let emptyDetailView = self.controllerView.emptyDetailView {
|
||||
self.controllerView.emptyDetailView = nil
|
||||
transition.updateAlpha(layer: emptyDetailView.layer, alpha: 0.0, completion: { [weak emptyDetailView] _ in
|
||||
emptyDetailView?.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
self.controllerView.containerView.clipsToBounds = false
|
||||
lastControllerFrameAndLayout = layoutDataForConfiguration(layoutConfiguration, layout: layout, index: 1)
|
||||
transition.updateFrame(view: self.controllerView.separatorView, frame: CGRect(origin: CGPoint(x: -UIScreenPixel, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height)), completion: { [weak self] completed in
|
||||
if let strongSelf = self, completed {
|
||||
strongSelf.controllerView.separatorView.removeFromSuperview()
|
||||
}
|
||||
})
|
||||
}
|
||||
transition.updateFrame(view: self.controllerView.containerView, frame: CGRect(origin: CGPoint(x: firstControllerFrameAndLayout?.0.maxX ?? 0.0, y: 0.0), size: lastControllerFrameAndLayout.0.size))
|
||||
|
||||
switch layoutConfiguration {
|
||||
case .single:
|
||||
if self.controllerView.sharedStatusBar.view.superview != nil {
|
||||
self.controllerView.sharedStatusBar.removeFromSupernode()
|
||||
self.controllerView.containerView.layer.setTraceableInfo(nil)
|
||||
}
|
||||
case .masterDetail:
|
||||
if self.controllerView.sharedStatusBar.view.superview == nil {
|
||||
self.controllerView.addSubnode(self.controllerView.sharedStatusBar)
|
||||
self.controllerView.containerView.layer.setTraceableInfo(CATracingLayerInfo(shouldBeAdjustedToInverseTransform: true, userData: self, tracingTag: 0, disableChildrenTracingTags: WindowTracingTags.statusBar | WindowTracingTags.keyboard))
|
||||
}
|
||||
}
|
||||
|
||||
if let _ = layout.statusBarHeight {
|
||||
self.controllerView.sharedStatusBar.frame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: 40.0))
|
||||
}
|
||||
|
||||
var controllersAndFrames: [(Bool, ControllerRecord, ContainerViewLayout)] = []
|
||||
for i in 0 ..< self._viewControllers.count {
|
||||
if let controller = self._viewControllers[i].controller as? ViewController {
|
||||
if i == 0 {
|
||||
controller.navigationBar?.previousItem = nil
|
||||
} else if case .masterDetail = layoutConfiguration, i == 1 {
|
||||
controller.navigationBar?.previousItem = .close
|
||||
} else {
|
||||
controller.navigationBar?.previousItem = .item(viewControllers[i - 1].navigationItem)
|
||||
}
|
||||
}
|
||||
viewControllers[i].navigation_setNavigationController(self)
|
||||
|
||||
if i == 0, let (_, layout) = firstControllerFrameAndLayout {
|
||||
controllersAndFrames.append((true, self._viewControllers[i], layout))
|
||||
} else if i == self._viewControllers.count - 1 {
|
||||
controllersAndFrames.append((false, self._viewControllers[i], lastControllerFrameAndLayout.1))
|
||||
}
|
||||
}
|
||||
|
||||
var masterController: UIViewController?
|
||||
var appearingMasterController: ControllerRecord?
|
||||
var appearingDetailController: ControllerRecord?
|
||||
|
||||
for (isMaster, record, layout) in controllersAndFrames {
|
||||
let frame: CGRect
|
||||
if isMaster, let firstControllerFrameAndLayout = firstControllerFrameAndLayout {
|
||||
masterController = record.controller
|
||||
frame = firstControllerFrameAndLayout.0
|
||||
} else {
|
||||
frame = lastControllerFrameAndLayout.0
|
||||
}
|
||||
let isAppearing = record.controller.view.superview == nil
|
||||
(record.controller as? ViewController)?.containerLayoutUpdated(layout, transition: isAppearing ? .immediate : transition)
|
||||
if isAppearing {
|
||||
if isMaster {
|
||||
appearingMasterController = record
|
||||
} else {
|
||||
appearingDetailController = record
|
||||
}
|
||||
} else if record.controller.view.superview !== (isMaster ? self.controllerView : self.controllerView.containerView) {
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
if isMaster {
|
||||
self.controllerView.insertSubview(record.controller.view, at: 0)
|
||||
} else {
|
||||
self.controllerView.containerView.addSubview(record.controller.view)
|
||||
}
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
}
|
||||
if !isAppearing {
|
||||
var isPartOfTransition = false
|
||||
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator {
|
||||
if navigationTransitionCoordinator.topView == record.controller.view || navigationTransitionCoordinator.bottomView == record.controller.view {
|
||||
isPartOfTransition = true
|
||||
}
|
||||
}
|
||||
if !isPartOfTransition {
|
||||
transition.updateFrame(view: record.controller.view, frame: frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var animatedAppearingDetailController = false
|
||||
|
||||
if let previousController = self.previouslyLaidOutTopController, !controllersAndFrames.contains(where: { $0.1.controller === previousController }), previousController.view.superview != nil {
|
||||
if transition.isAnimated, let record = appearingDetailController {
|
||||
animatedAppearingDetailController = true
|
||||
|
||||
previousController.viewWillDisappear(true)
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
self.controllerView.containerView.addSubview(record.controller.view)
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
|
||||
if let _ = previousControllers.index(where: { $0.controller === record.controller }) {
|
||||
//previousControllers[index].transition = .appearance
|
||||
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.controllerView.containerView, topView: previousController.view, topNavigationBar: (previousController as? ViewController)?.navigationBar, bottomView: record.controller.view, bottomNavigationBar: (record.controller as? ViewController)?.navigationBar)
|
||||
self.navigationTransitionCoordinator = navigationTransitionCoordinator
|
||||
|
||||
self.controllerView.inTransition = true
|
||||
navigationTransitionCoordinator.animateCompletion(0.0, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.navigationTransitionCoordinator = nil
|
||||
strongSelf.controllerView.inTransition = false
|
||||
|
||||
record.controller.viewDidAppear(true)
|
||||
|
||||
previousController.setIgnoreAppearanceMethodInvocations(true)
|
||||
previousController.view.removeFromSuperview()
|
||||
previousController.setIgnoreAppearanceMethodInvocations(false)
|
||||
previousController.viewDidDisappear(true)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if let index = self._viewControllers.index(where: { $0.controller === previousController }) {
|
||||
self._viewControllers[index].transition = .appearance
|
||||
}
|
||||
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Push, container: self.controllerView.containerView, topView: record.controller.view, topNavigationBar: (record.controller as? ViewController)?.navigationBar, bottomView: previousController.view, bottomNavigationBar: (previousController as? ViewController)?.navigationBar)
|
||||
self.navigationTransitionCoordinator = navigationTransitionCoordinator
|
||||
|
||||
self.controllerView.inTransition = true
|
||||
navigationTransitionCoordinator.animateCompletion(0.0, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if let index = strongSelf._viewControllers.index(where: { $0.controller === previousController }) {
|
||||
strongSelf._viewControllers[index].transition = .none
|
||||
}
|
||||
strongSelf.navigationTransitionCoordinator = nil
|
||||
strongSelf.controllerView.inTransition = false
|
||||
|
||||
record.controller.viewDidAppear(true)
|
||||
|
||||
previousController.setIgnoreAppearanceMethodInvocations(true)
|
||||
previousController.view.removeFromSuperview()
|
||||
previousController.setIgnoreAppearanceMethodInvocations(false)
|
||||
previousController.viewDidDisappear(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
previousController.viewWillDisappear(false)
|
||||
previousController.view.removeFromSuperview()
|
||||
previousController.viewDidDisappear(false)
|
||||
}
|
||||
}
|
||||
|
||||
if !animatedAppearingDetailController, let record = appearingDetailController {
|
||||
record.controller.viewWillAppear(false)
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
self.controllerView.containerView.addSubview(record.controller.view)
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
record.controller.viewDidAppear(false)
|
||||
if let controller = record.controller as? ViewController {
|
||||
controller.displayNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
}
|
||||
}
|
||||
|
||||
if let record = appearingMasterController, let firstControllerFrameAndLayout = firstControllerFrameAndLayout {
|
||||
record.controller.viewWillAppear(false)
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
self.controllerView.insertSubview(record.controller.view, belowSubview: self.controllerView.containerView)
|
||||
record.controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
record.controller.viewDidAppear(false)
|
||||
if let controller = record.controller as? ViewController {
|
||||
controller.displayNode.recursivelyEnsureDisplaySynchronously(true)
|
||||
}
|
||||
|
||||
record.controller.view.frame = firstControllerFrameAndLayout.0
|
||||
record.controller.viewDidAppear(transition.isAnimated)
|
||||
transition.animatePositionAdditive(layer: record.controller.view.layer, offset: CGPoint(x: -firstControllerFrameAndLayout.0.width, y: 0.0))
|
||||
}
|
||||
|
||||
for record in self._viewControllers {
|
||||
let controller = record.controller
|
||||
if case .none = record.transition, !controllersAndFrames.contains(where: { $0.1.controller === controller }) {
|
||||
if controller === self.previouslyLaidOutMasterController {
|
||||
controller.viewWillDisappear(true)
|
||||
record.transition = .appearance
|
||||
transition.animatePositionAdditive(layer: controller.view.layer, offset: CGPoint(), to: CGPoint(x: -controller.view.bounds.size.width, y: 0.0), removeOnCompletion: false, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
controller.view.removeFromSuperview()
|
||||
controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
controller.viewDidDisappear(true)
|
||||
controller.view.layer.removeAllAnimations()
|
||||
for r in strongSelf._viewControllers {
|
||||
if r.controller === controller {
|
||||
r.transition = .none
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if controller.isViewLoaded && controller.view.superview != nil {
|
||||
var isPartOfTransition = false
|
||||
if let navigationTransitionCoordinator = self.navigationTransitionCoordinator {
|
||||
if navigationTransitionCoordinator.topView == controller.view || navigationTransitionCoordinator.bottomView == controller.view {
|
||||
isPartOfTransition = true
|
||||
}
|
||||
}
|
||||
|
||||
if !isPartOfTransition {
|
||||
controller.viewWillDisappear(false)
|
||||
controller.setIgnoreAppearanceMethodInvocations(true)
|
||||
controller.view.removeFromSuperview()
|
||||
controller.setIgnoreAppearanceMethodInvocations(false)
|
||||
controller.viewDidDisappear(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for previous in previousControllers {
|
||||
var isFound = false
|
||||
inner: for current in self._viewControllers {
|
||||
if previous.controller === current.controller {
|
||||
isFound = true
|
||||
break inner
|
||||
}
|
||||
}
|
||||
if !isFound {
|
||||
(previous.controller as? ViewController)?.navigationStackConfigurationUpdated(next: [])
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0 ..< self._viewControllers.count {
|
||||
var currentNext: UIViewController? = (i == (self._viewControllers.count - 1)) ? nil : self._viewControllers[i + 1].controller
|
||||
if case .single = layoutConfiguration {
|
||||
currentNext = nil
|
||||
}
|
||||
|
||||
var previousNext: UIViewController?
|
||||
inner: for j in 0 ..< previousControllers.count {
|
||||
if previousControllers[j].controller === self._viewControllers[i].controller {
|
||||
previousNext = (j == (previousControllers.count - 1)) ? nil : previousControllers[j + 1].controller
|
||||
break inner
|
||||
}
|
||||
}
|
||||
|
||||
if currentNext !== previousNext {
|
||||
let next = currentNext as? ViewController
|
||||
(self._viewControllers[i].controller as? ViewController)?.navigationStackConfigurationUpdated(next: next == nil ? [] : [next!])
|
||||
}
|
||||
}
|
||||
|
||||
self.previouslyLaidOutMasterController = masterController
|
||||
self.previouslyLaidOutTopController = self._viewControllers.last?.controller
|
||||
}
|
||||
|
||||
public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
if !self.isViewLoaded {
|
||||
self.loadView()
|
||||
@ -75,17 +521,11 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
self.validLayout = layout
|
||||
transition.updateFrame(view: self.view, frame: CGRect(origin: self.view.frame.origin, size: layout.size))
|
||||
|
||||
let containedLayout = ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging)
|
||||
|
||||
if let topViewController = self.topViewController {
|
||||
if let topViewController = topViewController as? ContainableController {
|
||||
topViewController.containerLayoutUpdated(containedLayout, transition: transition)
|
||||
} else {
|
||||
transition.updateFrame(view: topViewController.view, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height))
|
||||
}
|
||||
}
|
||||
self.updateControllerLayouts(previousControllers: self._viewControllers, layout: layout, transition: transition)
|
||||
|
||||
if let presentedViewController = self.presentedViewController {
|
||||
let containedLayout = ContainerViewLayout(size: layout.size, metrics: layout.metrics, intrinsicInsets: layout.intrinsicInsets, safeInsets: layout.safeInsets, statusBarHeight: layout.statusBarHeight, inputHeight: layout.inputHeight, standardInputHeight: layout.standardInputHeight, inputHeightIsInteractivellyChanging: layout.inputHeightIsInteractivellyChanging)
|
||||
|
||||
if let presentedViewController = presentedViewController as? ContainableController {
|
||||
presentedViewController.containerLayoutUpdated(containedLayout, transition: transition)
|
||||
} else {
|
||||
@ -103,6 +543,9 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
self.view.clipsToBounds = true
|
||||
self.view.autoresizingMask = []
|
||||
|
||||
self.controllerView.backgroundColor = self.theme.emptyAreaColor
|
||||
self.controllerView.separatorView.backgroundColor = theme.navigationBar.separatorColor
|
||||
|
||||
if #available(iOSApplicationExtension 11.0, *) {
|
||||
self.navigationBar.prefersLargeTitles = false
|
||||
}
|
||||
@ -122,7 +565,26 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
switch recognizer.state {
|
||||
case UIGestureRecognizerState.began:
|
||||
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
guard let layout = self.validLayout else {
|
||||
return
|
||||
}
|
||||
guard self.navigationTransitionCoordinator == nil else {
|
||||
return
|
||||
}
|
||||
let beginGesture: Bool
|
||||
switch self.layoutConfiguration(for: layout) {
|
||||
case .masterDetail:
|
||||
let location = recognizer.location(in: self.controllerView.containerView)
|
||||
if self.controllerView.containerView.bounds.contains(location) {
|
||||
beginGesture = self._viewControllers.count >= 3
|
||||
} else {
|
||||
beginGesture = false
|
||||
}
|
||||
case .single:
|
||||
beginGesture = self._viewControllers.count >= 2
|
||||
}
|
||||
|
||||
if beginGesture {
|
||||
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
|
||||
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
|
||||
|
||||
@ -131,7 +593,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
bottomController.viewWillAppear(true)
|
||||
let bottomView = bottomController.view!
|
||||
|
||||
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.view, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar)
|
||||
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.controllerView.containerView, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar)
|
||||
self.navigationTransitionCoordinator = navigationTransitionCoordinator
|
||||
}
|
||||
case UIGestureRecognizerState.changed:
|
||||
@ -147,9 +609,8 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
(self.view as! NavigationControllerView).inTransition = true
|
||||
navigationTransitionCoordinator.animateCompletion(velocity, completion: {
|
||||
(self.view as! NavigationControllerView).inTransition = false
|
||||
self.navigationTransitionCoordinator = nil
|
||||
|
||||
//self._navigationBar.endInteractivePopProgress()
|
||||
self.navigationTransitionCoordinator = nil
|
||||
|
||||
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
|
||||
@ -165,8 +626,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
bottomController.viewDidAppear(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
|
||||
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
|
||||
@ -180,8 +640,6 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
(self.view as! NavigationControllerView).inTransition = false
|
||||
self.navigationTransitionCoordinator = nil
|
||||
|
||||
//self._navigationBar.endInteractivePopProgress()
|
||||
|
||||
if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil {
|
||||
let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController
|
||||
let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController
|
||||
@ -226,11 +684,15 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
self.view.endEditing(true)
|
||||
}
|
||||
if let validLayout = self.validLayout {
|
||||
let appliedLayout = validLayout.withUpdatedInputHeight(controller.hasActiveInput ? validLayout.inputHeight : nil)
|
||||
let (_, controllerLayout) = self.layoutDataForConfiguration(self.layoutConfiguration(for: validLayout), layout: validLayout, index: self.viewControllers.count)
|
||||
|
||||
let appliedLayout = controllerLayout.withUpdatedInputHeight(controller.hasActiveInput ? controllerLayout.inputHeight : nil)
|
||||
controller.containerLayoutUpdated(appliedLayout, transition: .immediate)
|
||||
self.currentPushDisposable.set((controller.ready.get() |> take(1)).start(next: { [weak self] _ in
|
||||
if let strongSelf = self, let validLayout = strongSelf.validLayout {
|
||||
let containerLayout = validLayout.withUpdatedInputHeight(controller.hasActiveInput ? validLayout.inputHeight : nil)
|
||||
let (_, controllerLayout) = strongSelf.layoutDataForConfiguration(strongSelf.layoutConfiguration(for: validLayout), layout: validLayout, index: strongSelf.viewControllers.count)
|
||||
|
||||
let containerLayout = controllerLayout.withUpdatedInputHeight(controller.hasActiveInput ? controllerLayout.inputHeight : nil)
|
||||
if containerLayout != appliedLayout {
|
||||
controller.containerLayoutUpdated(containerLayout, transition: .immediate)
|
||||
}
|
||||
@ -253,7 +715,8 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
public func replaceTopController(_ controller: ViewController, animated: Bool, ready: ValuePromise<Bool>? = nil) {
|
||||
self.view.endEditing(true)
|
||||
if let validLayout = self.validLayout {
|
||||
controller.containerLayoutUpdated(validLayout, transition: .immediate)
|
||||
let (_, controllerLayout) = self.layoutDataForConfiguration(self.layoutConfiguration(for: validLayout), layout: validLayout, index: self.viewControllers.count)
|
||||
controller.containerLayoutUpdated(controllerLayout, transition: .immediate)
|
||||
}
|
||||
self.currentPushDisposable.set((controller.ready.get() |> take(1)).start(next: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
@ -269,7 +732,8 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
public func replaceAllButRootController(_ controller: ViewController, animated: Bool, ready: ValuePromise<Bool>? = nil) {
|
||||
self.view.endEditing(true)
|
||||
if let validLayout = self.validLayout {
|
||||
controller.containerLayoutUpdated(validLayout, transition: .immediate)
|
||||
let (_, controllerLayout) = self.layoutDataForConfiguration(self.layoutConfiguration(for: validLayout), layout: validLayout, index: self.viewControllers.count)
|
||||
controller.containerLayoutUpdated(controllerLayout, transition: .immediate)
|
||||
}
|
||||
self.currentPushDisposable.set((controller.ready.get() |> take(1)).start(next: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
@ -324,132 +788,24 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
}
|
||||
|
||||
open override func setViewControllers(_ viewControllers: [UIViewController], animated: Bool) {
|
||||
var resultControllers: [ControllerRecord] = []
|
||||
for controller in viewControllers {
|
||||
controller.navigation_setNavigationController(self)
|
||||
}
|
||||
|
||||
if viewControllers.count > 0 {
|
||||
let topViewController = viewControllers[viewControllers.count - 1] as UIViewController
|
||||
|
||||
if let controller = topViewController as? ContainableController {
|
||||
if let validLayout = self.validLayout {
|
||||
var layoutToApply = validLayout
|
||||
var hasActiveInput = false
|
||||
if let controller = controller as? ViewController {
|
||||
hasActiveInput = controller.hasActiveInput
|
||||
}
|
||||
if !hasActiveInput {
|
||||
layoutToApply = layoutToApply.withUpdatedInputHeight(nil)
|
||||
}
|
||||
controller.containerLayoutUpdated(layoutToApply, transition: .immediate)
|
||||
var found = false
|
||||
inner: for current in self._viewControllers {
|
||||
if current.controller === controller {
|
||||
resultControllers.append(current)
|
||||
found = true
|
||||
break inner
|
||||
}
|
||||
} else {
|
||||
topViewController.view.frame = CGRect(origin: CGPoint(), size: self.view.bounds.size)
|
||||
}
|
||||
if !found {
|
||||
resultControllers.append(ControllerRecord(controller: controller))
|
||||
}
|
||||
}
|
||||
|
||||
if animated && self.viewControllers.count != 0 && viewControllers.count != 0 && self.viewControllers.last! !== viewControllers.last! {
|
||||
if self.viewControllers.contains(where: { $0 === viewControllers.last }) {
|
||||
let bottomController = viewControllers.last! as UIViewController
|
||||
let topController = self.viewControllers.last! as UIViewController
|
||||
|
||||
if let bottomController = bottomController as? ViewController {
|
||||
if viewControllers.count >= 2 {
|
||||
bottomController.navigationBar?.previousItem = viewControllers[viewControllers.count - 2].navigationItem
|
||||
} else {
|
||||
bottomController.navigationBar?.previousItem = nil
|
||||
}
|
||||
}
|
||||
|
||||
bottomController.viewWillAppear(true)
|
||||
let bottomView = bottomController.view!
|
||||
topController.viewWillDisappear(true)
|
||||
let topView = topController.view!
|
||||
|
||||
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.view, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar)
|
||||
self.navigationTransitionCoordinator = navigationTransitionCoordinator
|
||||
|
||||
(self.view as! NavigationControllerView).inTransition = true
|
||||
navigationTransitionCoordinator.animateCompletion(0.0, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
(strongSelf.view as! NavigationControllerView).inTransition = false
|
||||
strongSelf.navigationTransitionCoordinator = nil
|
||||
|
||||
topController.setIgnoreAppearanceMethodInvocations(true)
|
||||
bottomController.setIgnoreAppearanceMethodInvocations(true)
|
||||
strongSelf.setViewControllers(viewControllers, animated: false)
|
||||
topController.setIgnoreAppearanceMethodInvocations(false)
|
||||
bottomController.setIgnoreAppearanceMethodInvocations(false)
|
||||
|
||||
topController.viewDidDisappear(true)
|
||||
bottomController.viewDidAppear(true)
|
||||
|
||||
topView.removeFromSuperview()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let topController = viewControllers.last! as UIViewController
|
||||
let bottomController = self.viewControllers.last! as UIViewController
|
||||
|
||||
if let topController = topController as? ViewController {
|
||||
topController.navigationBar?.previousItem = bottomController.navigationItem
|
||||
}
|
||||
|
||||
bottomController.viewWillDisappear(true)
|
||||
let bottomView = bottomController.view!
|
||||
topController.viewWillAppear(true)
|
||||
let topView = topController.view!
|
||||
|
||||
let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Push, container: self.view, topView: topView, topNavigationBar: (topController as? ViewController)?.navigationBar, bottomView: bottomView, bottomNavigationBar: (bottomController as? ViewController)?.navigationBar)
|
||||
self.navigationTransitionCoordinator = navigationTransitionCoordinator
|
||||
|
||||
topView.isUserInteractionEnabled = false
|
||||
|
||||
(self.view as! NavigationControllerView).inTransition = true
|
||||
navigationTransitionCoordinator.animateCompletion(0.0, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
(strongSelf.view as! NavigationControllerView).inTransition = false
|
||||
strongSelf.navigationTransitionCoordinator = nil
|
||||
|
||||
topController.setIgnoreAppearanceMethodInvocations(true)
|
||||
bottomController.setIgnoreAppearanceMethodInvocations(true)
|
||||
strongSelf.setViewControllers(viewControllers, animated: false)
|
||||
topController.setIgnoreAppearanceMethodInvocations(false)
|
||||
bottomController.setIgnoreAppearanceMethodInvocations(false)
|
||||
|
||||
topController.view.isUserInteractionEnabled = true
|
||||
|
||||
bottomController.viewDidDisappear(true)
|
||||
topController.viewDidAppear(true)
|
||||
|
||||
bottomView.removeFromSuperview()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if let topController = self.viewControllers.last , topController.isViewLoaded {
|
||||
topController.navigation_setNavigationController(nil)
|
||||
topController.viewWillDisappear(false)
|
||||
topController.view.removeFromSuperview()
|
||||
topController.viewDidDisappear(false)
|
||||
}
|
||||
|
||||
self._viewControllers = viewControllers
|
||||
|
||||
if let topController = viewControllers.last {
|
||||
if let topController = topController as? ViewController {
|
||||
if viewControllers.count >= 2 {
|
||||
topController.navigationBar?.previousItem = viewControllers[viewControllers.count - 2].navigationItem
|
||||
} else {
|
||||
topController.navigationBar?.previousItem = nil
|
||||
}
|
||||
}
|
||||
|
||||
topController.navigation_setNavigationController(self)
|
||||
topController.viewWillAppear(false)
|
||||
self.view.addSubview(topController.view)
|
||||
topController.viewDidAppear(false)
|
||||
}
|
||||
let previousControllers = self._viewControllers
|
||||
self._viewControllers = resultControllers
|
||||
if let layout = self.validLayout {
|
||||
self.updateControllerLayouts(previousControllers: previousControllers, layout: layout, transition: animated ? .animated(duration: 0.5, curve: .spring) : .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -527,7 +883,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if let panRecognizer = otherGestureRecognizer as? UIPanGestureRecognizer {
|
||||
if let _ = otherGestureRecognizer as? UIPanGestureRecognizer {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@ -27,9 +27,9 @@ class NavigationTransitionCoordinator {
|
||||
|
||||
private let container: UIView
|
||||
private let transition: NavigationTransition
|
||||
private let topView: UIView
|
||||
let topView: UIView
|
||||
private let viewSuperview: UIView?
|
||||
private let bottomView: UIView
|
||||
let bottomView: UIView
|
||||
private let topNavigationBar: NavigationBar?
|
||||
private let bottomNavigationBar: NavigationBar?
|
||||
private let dimView: UIView
|
||||
@ -181,7 +181,7 @@ class NavigationTransitionCoordinator {
|
||||
self.animatingCompletion = true
|
||||
let distance = (1.0 - self.progress) * self.container.bounds.size.width
|
||||
let f = {
|
||||
switch self.transition {
|
||||
/*switch self.transition {
|
||||
case .Push:
|
||||
if let viewSuperview = self.viewSuperview {
|
||||
viewSuperview.addSubview(self.bottomView)
|
||||
@ -194,7 +194,7 @@ class NavigationTransitionCoordinator {
|
||||
} else {
|
||||
self.topView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
self.dimView.removeFromSuperview()
|
||||
self.shadowView.removeFromSuperview()
|
||||
|
||||
@ -35,7 +35,7 @@ public final class PeekController: ViewController {
|
||||
self.content = content
|
||||
self.sourceNode = sourceNode
|
||||
|
||||
super.init(navigationBarTheme: nil)
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
|
||||
@ -90,29 +90,11 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
}
|
||||
|
||||
private func longTapTimerFired() {
|
||||
guard let _ = self.tapLocation, let (sourceNode, content) = self.candidateContent else {
|
||||
guard let tapLocation = self.tapLocation else {
|
||||
return
|
||||
}
|
||||
|
||||
self.state = .began
|
||||
|
||||
if let presentedController = self.present(content, sourceNode) {
|
||||
self.menuActivation = content.menuActivation()
|
||||
self.presentedController = presentedController
|
||||
|
||||
switch content.menuActivation() {
|
||||
case .drag:
|
||||
break
|
||||
case .press:
|
||||
if #available(iOSApplicationExtension 9.0, *) {
|
||||
if presentedController.traitCollection.forceTouchCapability != .available {
|
||||
self.startPressTimer()
|
||||
}
|
||||
} else {
|
||||
self.startPressTimer()
|
||||
}
|
||||
}
|
||||
}
|
||||
self.checkCandidateContent(at: tapLocation)
|
||||
}
|
||||
|
||||
private func pressTimerFired() {
|
||||
@ -136,27 +118,8 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
self.candidateContent = nil
|
||||
self.state = .failed
|
||||
} else {
|
||||
if let contentSignal = self.contentAtPoint(tapLocation) {
|
||||
self.candidateContentDisposable.set((contentSignal |> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
if let strongSelf = self {
|
||||
switch strongSelf.state {
|
||||
case .possible, .changed:
|
||||
if let (sourceNode, content) = result {
|
||||
strongSelf.tapLocation = tapLocation
|
||||
strongSelf.candidateContent = (sourceNode, content)
|
||||
strongSelf.menuActivation = content.menuActivation()
|
||||
strongSelf.startLongTapTimer()
|
||||
} else {
|
||||
strongSelf.state = .failed
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
self.state = .failed
|
||||
}
|
||||
self.tapLocation = tapLocation
|
||||
self.startLongTapTimer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -178,6 +141,8 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
|
||||
self.tapLocation = nil
|
||||
self.candidateContent = nil
|
||||
self.longTapTimer?.invalidate()
|
||||
self.pressTimer?.invalidate()
|
||||
self.state = .failed
|
||||
}
|
||||
}
|
||||
@ -199,9 +164,9 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesMoved(touches, with: event)
|
||||
|
||||
if let touch = touches.first, let initialTapLocation = self.tapLocation, let menuActivation = self.menuActivation {
|
||||
if let touch = touches.first, let initialTapLocation = self.tapLocation {
|
||||
let touchLocation = touch.location(in: self.view)
|
||||
if let presentedController = self.presentedController {
|
||||
if let menuActivation = self.menuActivation, let presentedController = self.presentedController {
|
||||
switch menuActivation {
|
||||
case .drag:
|
||||
var offset = touchLocation.y - initialTapLocation.y
|
||||
@ -257,17 +222,39 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
|
||||
if let strongSelf = self {
|
||||
switch strongSelf.state {
|
||||
case .possible, .changed:
|
||||
if let (sourceNode, content) = result, let currentContent = strongSelf.candidateContent, !currentContent.1.isEqual(to: content) {
|
||||
strongSelf.tapLocation = touchLocation
|
||||
strongSelf.candidateContent = (sourceNode, content)
|
||||
strongSelf.menuActivation = content.menuActivation()
|
||||
if let presentedController = strongSelf.presentedController, presentedController.isNodeLoaded {
|
||||
presentedController.sourceNode = {
|
||||
return sourceNode
|
||||
if let (sourceNode, content) = result {
|
||||
if let currentContent = strongSelf.candidateContent {
|
||||
if !currentContent.1.isEqual(to: content) {
|
||||
strongSelf.tapLocation = touchLocation
|
||||
strongSelf.candidateContent = (sourceNode, content)
|
||||
strongSelf.menuActivation = content.menuActivation()
|
||||
if let presentedController = strongSelf.presentedController, presentedController.isNodeLoaded {
|
||||
presentedController.sourceNode = {
|
||||
return sourceNode
|
||||
}
|
||||
(presentedController.displayNode as? PeekControllerNode)?.updateContent(content: content)
|
||||
}
|
||||
}
|
||||
(presentedController.displayNode as? PeekControllerNode)?.updateContent(content: content)
|
||||
} else {
|
||||
strongSelf.startLongTapTimer()
|
||||
if let presentedController = strongSelf.present(content, sourceNode) {
|
||||
strongSelf.candidateContent = (sourceNode, content)
|
||||
strongSelf.menuActivation = content.menuActivation()
|
||||
strongSelf.presentedController = presentedController
|
||||
strongSelf.state = .began
|
||||
|
||||
switch content.menuActivation() {
|
||||
case .drag:
|
||||
break
|
||||
case .press:
|
||||
if #available(iOSApplicationExtension 9.0, *) {
|
||||
if presentedController.traitCollection.forceTouchCapability != .available {
|
||||
strongSelf.startPressTimer()
|
||||
}
|
||||
} else {
|
||||
strongSelf.startPressTimer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if strongSelf.presentedController == nil {
|
||||
strongSelf.state = .failed
|
||||
|
||||
@ -113,7 +113,7 @@ public final class StatusBar: ASDisplayNode {
|
||||
self.addSubnode(self.offsetNode)
|
||||
self.addSubnode(self.inCallBackgroundNode)
|
||||
|
||||
self.layer.setTraceableInfo(CATracingLayerInfo(shouldBeAdjustedToInverseTransform: true, userData: self, tracingTag: WindowTracingTags.statusBar))
|
||||
self.layer.setTraceableInfo(CATracingLayerInfo(shouldBeAdjustedToInverseTransform: true, userData: self, tracingTag: WindowTracingTags.statusBar, disableChildrenTracingTags: 0))
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
|
||||
@ -64,10 +64,10 @@ open class TabBarController: ViewController {
|
||||
|
||||
private var theme: TabBarControllerTheme
|
||||
|
||||
public init(navigationBarTheme: NavigationBarTheme, theme: TabBarControllerTheme) {
|
||||
public init(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) {
|
||||
self.theme = theme
|
||||
|
||||
super.init(navigationBarTheme: navigationBarTheme)
|
||||
super.init(navigationBarPresentationData: navigationBarPresentationData)
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
@ -78,8 +78,8 @@ open class TabBarController: ViewController {
|
||||
self.pendingControllerDisposable.dispose()
|
||||
}
|
||||
|
||||
public func updateTheme(navigationBarTheme: NavigationBarTheme, theme: TabBarControllerTheme) {
|
||||
self.navigationBar?.updateTheme(navigationBarTheme)
|
||||
public func updateTheme(navigationBarPresentationData: NavigationBarPresentationData, theme: TabBarControllerTheme) {
|
||||
self.navigationBar?.updatePresentationData(navigationBarPresentationData)
|
||||
if self.theme !== theme {
|
||||
self.theme = theme
|
||||
if self.isNodeLoaded {
|
||||
@ -192,6 +192,13 @@ open class TabBarController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
override open func navigationStackConfigurationUpdated(next: [ViewController]) {
|
||||
super.navigationStackConfigurationUpdated(next: next)
|
||||
for controller in self.controllers {
|
||||
controller.navigationStackConfigurationUpdated(next: next)
|
||||
}
|
||||
}
|
||||
|
||||
override open func viewDidAppear(_ animated: Bool) {
|
||||
if let currentController = self.currentController {
|
||||
currentController.viewDidAppear(animated)
|
||||
|
||||
257
Display/TapLongTapOrDoubleTapGestureRecognizer.swift
Normal file
257
Display/TapLongTapOrDoubleTapGestureRecognizer.swift
Normal file
@ -0,0 +1,257 @@
|
||||
import Foundation
|
||||
import UIKit.UIGestureRecognizerSubclass
|
||||
import Display
|
||||
|
||||
private class TapLongTapOrDoubleTapGestureRecognizerTimerTarget: NSObject {
|
||||
weak var target: TapLongTapOrDoubleTapGestureRecognizer?
|
||||
|
||||
init(target: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
self.target = target
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc func longTapEvent() {
|
||||
self.target?.longTapEvent()
|
||||
}
|
||||
|
||||
@objc func tapEvent() {
|
||||
self.target?.tapEvent()
|
||||
}
|
||||
|
||||
@objc func holdEvent() {
|
||||
self.target?.holdEvent()
|
||||
}
|
||||
}
|
||||
|
||||
enum TapLongTapOrDoubleTapGesture {
|
||||
case tap
|
||||
case doubleTap
|
||||
case longTap
|
||||
case hold
|
||||
}
|
||||
|
||||
enum TapLongTapOrDoubleTapGestureRecognizerAction {
|
||||
case waitForDoubleTap
|
||||
case waitForSingleTap
|
||||
case waitForHold(timeout: Double, acceptTap: Bool)
|
||||
case fail
|
||||
}
|
||||
|
||||
public final class TapLongTapOrDoubleTapGestureRecognizer: UIGestureRecognizer, UIGestureRecognizerDelegate {
|
||||
private var touchLocationAndTimestamp: (CGPoint, Double)?
|
||||
private var touchCount: Int = 0
|
||||
private var tapCount: Int = 0
|
||||
|
||||
private var timer: Foundation.Timer?
|
||||
private(set) var lastRecognizedGestureAndLocation: (TapLongTapOrDoubleTapGesture, CGPoint)?
|
||||
|
||||
var tapActionAtPoint: ((CGPoint) -> TapLongTapOrDoubleTapGestureRecognizerAction)?
|
||||
var highlight: ((CGPoint?) -> Void)?
|
||||
|
||||
var hapticFeedback: HapticFeedback?
|
||||
|
||||
private var highlightPoint: CGPoint?
|
||||
|
||||
override public init(target: Any?, action: Selector?) {
|
||||
super.init(target: target, action: action)
|
||||
|
||||
self.delegate = self
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if otherGestureRecognizer is UIPanGestureRecognizer {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override public func reset() {
|
||||
self.timer?.invalidate()
|
||||
self.timer = nil
|
||||
self.touchLocationAndTimestamp = nil
|
||||
self.tapCount = 0
|
||||
self.touchCount = 0
|
||||
self.hapticFeedback = nil
|
||||
|
||||
if self.highlightPoint != nil {
|
||||
self.highlightPoint = nil
|
||||
self.highlight?(nil)
|
||||
}
|
||||
|
||||
super.reset()
|
||||
}
|
||||
|
||||
fileprivate func longTapEvent() {
|
||||
self.timer?.invalidate()
|
||||
self.timer = nil
|
||||
if let (location, _) = self.touchLocationAndTimestamp {
|
||||
self.lastRecognizedGestureAndLocation = (.longTap, location)
|
||||
} else {
|
||||
self.lastRecognizedGestureAndLocation = nil
|
||||
}
|
||||
self.state = .ended
|
||||
}
|
||||
|
||||
fileprivate func tapEvent() {
|
||||
self.timer?.invalidate()
|
||||
self.timer = nil
|
||||
if let (location, _) = self.touchLocationAndTimestamp {
|
||||
self.lastRecognizedGestureAndLocation = (.tap, location)
|
||||
} else {
|
||||
self.lastRecognizedGestureAndLocation = nil
|
||||
}
|
||||
self.state = .ended
|
||||
}
|
||||
|
||||
fileprivate func holdEvent() {
|
||||
self.timer?.invalidate()
|
||||
self.timer = nil
|
||||
if let (location, _) = self.touchLocationAndTimestamp {
|
||||
self.hapticFeedback?.tap()
|
||||
self.lastRecognizedGestureAndLocation = (.hold, location)
|
||||
} else {
|
||||
self.lastRecognizedGestureAndLocation = nil
|
||||
}
|
||||
self.state = .began
|
||||
}
|
||||
|
||||
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
self.lastRecognizedGestureAndLocation = nil
|
||||
|
||||
super.touchesBegan(touches, with: event)
|
||||
|
||||
self.touchCount += touches.count
|
||||
|
||||
if let touch = touches.first {
|
||||
let touchLocation = touch.location(in: self.view)
|
||||
|
||||
if self.highlightPoint != touchLocation {
|
||||
self.highlightPoint = touchLocation
|
||||
self.highlight?(touchLocation)
|
||||
}
|
||||
|
||||
if let hitResult = self.view?.hitTest(touch.location(in: self.view), with: event), let _ = hitResult as? UIButton {
|
||||
self.state = .failed
|
||||
return
|
||||
}
|
||||
|
||||
self.tapCount += 1
|
||||
if self.tapCount == 2 && self.touchCount == 1 {
|
||||
self.timer?.invalidate()
|
||||
self.timer = nil
|
||||
self.lastRecognizedGestureAndLocation = (.doubleTap, self.location(in: self.view))
|
||||
self.state = .ended
|
||||
} else {
|
||||
let touchLocationAndTimestamp = (touch.location(in: self.view), CACurrentMediaTime())
|
||||
self.touchLocationAndTimestamp = touchLocationAndTimestamp
|
||||
|
||||
var tapAction: TapLongTapOrDoubleTapGestureRecognizerAction = .waitForDoubleTap
|
||||
if let tapActionAtPoint = self.tapActionAtPoint {
|
||||
tapAction = tapActionAtPoint(touchLocationAndTimestamp.0)
|
||||
}
|
||||
|
||||
switch tapAction {
|
||||
case .waitForSingleTap, .waitForDoubleTap:
|
||||
self.timer?.invalidate()
|
||||
let timer = Timer(timeInterval: 0.3, target: TapLongTapOrDoubleTapGestureRecognizerTimerTarget(target: self), selector: #selector(TapLongTapOrDoubleTapGestureRecognizerTimerTarget.longTapEvent), userInfo: nil, repeats: false)
|
||||
self.timer = timer
|
||||
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
|
||||
case let .waitForHold(timeout, _):
|
||||
self.hapticFeedback = HapticFeedback()
|
||||
self.hapticFeedback?.prepareTap()
|
||||
let timer = Timer(timeInterval: timeout, target: TapLongTapOrDoubleTapGestureRecognizerTimerTarget(target: self), selector: #selector(TapLongTapOrDoubleTapGestureRecognizerTimerTarget.holdEvent), userInfo: nil, repeats: false)
|
||||
self.timer = timer
|
||||
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
|
||||
case .fail:
|
||||
self.state = .failed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesMoved(touches, with: event)
|
||||
|
||||
guard let touch = touches.first else {
|
||||
return
|
||||
}
|
||||
|
||||
if let (gesture, _) = self.lastRecognizedGestureAndLocation, case .hold = gesture {
|
||||
let location = touch.location(in: self.view)
|
||||
self.lastRecognizedGestureAndLocation = (.hold, location)
|
||||
self.state = .changed
|
||||
return
|
||||
}
|
||||
|
||||
if let touch = touches.first, let (touchLocation, _) = self.touchLocationAndTimestamp {
|
||||
let location = touch.location(in: self.view)
|
||||
let distance = CGPoint(x: location.x - touchLocation.x, y: location.y - touchLocation.y)
|
||||
if distance.x * distance.x + distance.y * distance.y > 4.0 {
|
||||
self.state = .cancelled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesEnded(touches, with: event)
|
||||
|
||||
self.touchCount -= touches.count
|
||||
|
||||
if self.highlightPoint != nil {
|
||||
self.highlightPoint = nil
|
||||
self.highlight?(nil)
|
||||
}
|
||||
|
||||
self.timer?.invalidate()
|
||||
|
||||
if let (gesture, location) = self.lastRecognizedGestureAndLocation, case .hold = gesture {
|
||||
self.lastRecognizedGestureAndLocation = (.hold, location)
|
||||
self.state = .ended
|
||||
return
|
||||
}
|
||||
|
||||
if self.tapCount == 1 {
|
||||
var tapAction: TapLongTapOrDoubleTapGestureRecognizerAction = .waitForDoubleTap
|
||||
if let tapActionAtPoint = self.tapActionAtPoint, let (touchLocation, _) = self.touchLocationAndTimestamp {
|
||||
tapAction = tapActionAtPoint(touchLocation)
|
||||
}
|
||||
|
||||
switch tapAction {
|
||||
case .waitForSingleTap:
|
||||
if let (touchLocation, _) = self.touchLocationAndTimestamp {
|
||||
self.lastRecognizedGestureAndLocation = (.tap, touchLocation)
|
||||
}
|
||||
self.state = .ended
|
||||
case .waitForDoubleTap:
|
||||
self.state = .began
|
||||
let timer = Timer(timeInterval: 0.2, target: TapLongTapOrDoubleTapGestureRecognizerTimerTarget(target: self), selector: #selector(TapLongTapOrDoubleTapGestureRecognizerTimerTarget.tapEvent), userInfo: nil, repeats: false)
|
||||
self.timer = timer
|
||||
RunLoop.main.add(timer, forMode: RunLoopMode.commonModes)
|
||||
case let .waitForHold(_, acceptTap):
|
||||
if let (touchLocation, _) = self.touchLocationAndTimestamp, acceptTap {
|
||||
if self.state != .began {
|
||||
self.lastRecognizedGestureAndLocation = (.tap, touchLocation)
|
||||
self.state = .began
|
||||
}
|
||||
}
|
||||
self.state = .ended
|
||||
case .fail:
|
||||
self.state = .failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
|
||||
super.touchesCancelled(touches, with: event)
|
||||
|
||||
self.touchCount -= touches.count
|
||||
|
||||
if self.highlightPoint != nil {
|
||||
self.highlightPoint = nil
|
||||
self.highlight?(nil)
|
||||
}
|
||||
|
||||
self.state = .cancelled
|
||||
}
|
||||
}
|
||||
@ -41,7 +41,7 @@ public final class TooltipController: ViewController {
|
||||
self.text = text
|
||||
self.timeout = timeout
|
||||
|
||||
super.init(navigationBarTheme: nil)
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
@ -54,10 +54,7 @@ public final class TooltipController: ViewController {
|
||||
|
||||
open override func loadDisplayNode() {
|
||||
self.displayNode = TooltipControllerNode(text: self.text, dismiss: { [weak self] in
|
||||
self?.dismissed?()
|
||||
self?.controllerNode.animateOut { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false)
|
||||
}
|
||||
self?.dismiss()
|
||||
})
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
@ -111,4 +108,12 @@ public final class TooltipController: ViewController {
|
||||
timeoutTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.dismissed?()
|
||||
self.controllerNode.animateOut { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false)
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,10 +149,10 @@ open class ViewControllerPresentationArguments {
|
||||
}
|
||||
}
|
||||
|
||||
public init(navigationBarTheme: NavigationBarTheme?) {
|
||||
public init(navigationBarPresentationData: NavigationBarPresentationData?) {
|
||||
self.statusBar = StatusBar()
|
||||
if let navigationBarTheme = navigationBarTheme {
|
||||
self.navigationBar = NavigationBar(theme: navigationBarTheme)
|
||||
if let navigationBarPresentationData = navigationBarPresentationData {
|
||||
self.navigationBar = NavigationBar(presentationData: navigationBarPresentationData)
|
||||
} else {
|
||||
self.navigationBar = nil
|
||||
}
|
||||
@ -218,6 +218,9 @@ open class ViewControllerPresentationArguments {
|
||||
}
|
||||
}
|
||||
|
||||
open func navigationStackConfigurationUpdated(next: [ViewController]) {
|
||||
}
|
||||
|
||||
open override func loadView() {
|
||||
self.view = self.displayNode.view
|
||||
if let navigationBar = self.navigationBar {
|
||||
@ -237,7 +240,7 @@ open class ViewControllerPresentationArguments {
|
||||
|
||||
open func displayNodeDidLoad() {
|
||||
if let layer = self.displayNode.layer as? CATracingLayer {
|
||||
layer.setTraceableInfo(CATracingLayerInfo(shouldBeAdjustedToInverseTransform: false, userData: self.displayNode.layer, tracingTag: WindowTracingTags.keyboard))
|
||||
layer.setTraceableInfo(CATracingLayerInfo(shouldBeAdjustedToInverseTransform: false, userData: self.displayNode.layer, tracingTag: WindowTracingTags.keyboard, disableChildrenTracingTags: 0))
|
||||
}
|
||||
self.updateScrollToTopView()
|
||||
}
|
||||
@ -248,9 +251,9 @@ open class ViewControllerPresentationArguments {
|
||||
}
|
||||
}
|
||||
|
||||
public func setDisplayNavigationBar(_ displayNavigtionBar: Bool, transition: ContainedViewLayoutTransition = .immediate) {
|
||||
if displayNavigtionBar != self.displayNavigationBar {
|
||||
self.displayNavigationBar = displayNavigtionBar
|
||||
public func setDisplayNavigationBar(_ displayNavigationBar: Bool, transition: ContainedViewLayoutTransition = .immediate) {
|
||||
if displayNavigationBar != self.displayNavigationBar {
|
||||
self.displayNavigationBar = displayNavigationBar
|
||||
if let parent = self.parent as? TabBarController {
|
||||
if parent.currentController === self {
|
||||
parent.displayNavigationBar = displayNavigationBar
|
||||
@ -346,4 +349,15 @@ open class ViewControllerPresentationArguments {
|
||||
super.unregisterForPreviewing(withContext: previewing)
|
||||
}
|
||||
}
|
||||
|
||||
public final func navigationNextSibling() -> UIViewController? {
|
||||
if let navigationController = self.navigationController as? NavigationController {
|
||||
if let index = navigationController.viewControllers.index(where: { $0 === self }) {
|
||||
if index != navigationController.viewControllers.count - 1 {
|
||||
return navigationController.viewControllers[index + 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,8 +296,8 @@ public final class WindowHostView {
|
||||
}
|
||||
|
||||
public struct WindowTracingTags {
|
||||
public static let statusBar: Int32 = 0
|
||||
public static let keyboard: Int32 = 1
|
||||
public static let statusBar: Int32 = 1 << 0
|
||||
public static let keyboard: Int32 = 1 << 1
|
||||
}
|
||||
|
||||
public protocol WindowHost {
|
||||
@ -309,7 +309,11 @@ public protocol WindowHost {
|
||||
}
|
||||
|
||||
private func layoutMetricsForScreenSize(_ size: CGSize) -> LayoutMetrics {
|
||||
return LayoutMetrics(widthClass: .compact, heightClass: .compact)
|
||||
if size.width > 690.0 && size.height > 690.0 {
|
||||
return LayoutMetrics(widthClass: .regular, heightClass: .regular)
|
||||
} else {
|
||||
return LayoutMetrics(widthClass: .compact, heightClass: .compact)
|
||||
}
|
||||
}
|
||||
|
||||
private func safeInsetsForScreenSize(_ size: CGSize) -> UIEdgeInsets {
|
||||
@ -465,14 +469,14 @@ public class Window1 {
|
||||
|
||||
let screenHeight: CGFloat
|
||||
|
||||
if true || !UIScreen.main.bounds.width.isEqual(to: strongSelf.windowLayout.size.width) {
|
||||
if keyboardFrame.width.isEqual(to: UIScreen.main.bounds.width) {
|
||||
if keyboardFrame.width.isEqual(to: UIScreen.main.bounds.width) {
|
||||
if abs(strongSelf.windowLayout.size.height - UIScreen.main.bounds.height) > 41.0 {
|
||||
screenHeight = UIScreen.main.bounds.height
|
||||
} else {
|
||||
screenHeight = UIScreen.main.bounds.width
|
||||
screenHeight = strongSelf.windowLayout.size.height
|
||||
}
|
||||
} else {
|
||||
screenHeight = UIScreen.main.bounds.height
|
||||
screenHeight = UIScreen.main.bounds.width
|
||||
}
|
||||
|
||||
let keyboardHeight = max(0.0, screenHeight - keyboardFrame.minY)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user