no message

This commit is contained in:
Peter Iakovlev 2018-04-04 11:00:29 +04:00
parent cad4955b71
commit 95614c8121
28 changed files with 1448 additions and 366 deletions

View File

@ -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;

View File

@ -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>

View File

@ -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) {

View File

@ -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]) {

View File

@ -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
}

View File

@ -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

View File

@ -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;
}

View File

@ -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?()
})
}
}

View File

@ -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) {

View File

@ -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)

View File

@ -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
}
}
}

View 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
}
}

View File

@ -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()

View 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)
}
}
}
}
}
}

View File

@ -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()

View File

@ -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?

View File

@ -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)

View File

@ -3,4 +3,6 @@ import UIKit
public protocol NavigationBarTitleView {
func animateLayoutTransition()
func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition)
}

View File

@ -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

View File

@ -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()

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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)

View 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
}
}

View File

@ -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?()
}
}
}

View File

@ -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
}
}

View File

@ -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)