chore: migrate to new version + fixed several critical bugs

- Migrated project to latest Telegram iOS base (v12.3.2+)
- Fixed circular dependency between GhostModeManager and MiscSettingsManager
- Fixed multiple Bazel build configuration errors (select() default conditions)
- Fixed duplicate type definitions in PeerInfoScreen
- Fixed swiftmodule directory resolution in build scripts
- Added Ghostgram Settings tab in main Settings menu with all 5 features
- Cleared sensitive credentials from config.json (template-only now)
- Excluded bazel-cache from version control
This commit is contained in:
ichmagmaus 812
2026-02-23 23:04:32 +01:00
parent 703e291bcb
commit db53826061
1017 changed files with 62337 additions and 40559 deletions
@@ -13,7 +13,7 @@ public struct ImmediateTextNodeLayoutInfo {
}
}
public class ImmediateTextNode: TextNode {
open class ImmediateTextNode: TextNode {
public var attributedText: NSAttributedString?
public var textAlignment: NSTextAlignment = .natural
public var verticalAlignment: TextVerticalAlignment = .top
@@ -60,7 +60,7 @@ public class ImmediateTextNode: TextNode {
public var trailingLineWidth: CGFloat?
var constrainedSize: CGSize?
public var constrainedSize: CGSize?
public var highlightAttributeAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)? {
didSet {
@@ -94,7 +94,7 @@ public class ImmediateTextNode: TextNode {
return node
}
public func updateLayout(_ constrainedSize: CGSize) -> CGSize {
open func updateLayout(_ constrainedSize: CGSize) -> CGSize {
self.constrainedSize = constrainedSize
let makeLayout = TextNode.asyncLayout(self)
@@ -9,11 +9,7 @@ public class KeyShortcutsController: UIResponder {
private var viewControllerEnumerator: (@escaping (ContainableController) -> Bool) -> Void
public static var isAvailable: Bool {
if #available(iOSApplicationExtension 8.0, iOS 8.0, *), UIDevice.current.userInterfaceIdiom == .pad {
return true
} else {
return false
}
return true
}
public init(enumerator: @escaping (@escaping (ContainableController) -> Bool) -> Void) {
@@ -30,6 +30,232 @@ private func drawFullCorner(context: CGContext, color: UIColor, at point: CGPoin
}
}
private func drawRectsImageContent(size: CGSize, context: CGContext, color: UIColor, rects: [CGRect], inset: CGFloat, outerRadius: CGFloat, innerRadius: CGFloat, stroke: Bool, strokeWidth: CGFloat, useModernPathCalculation: Bool, topLeft: CGPoint) {
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(color.cgColor)
context.setBlendMode(.copy)
if useModernPathCalculation {
if rects.count == 1 {
let path = UIBezierPath(roundedRect: rects[0].offsetBy(dx: -topLeft.x, dy: -topLeft.y), cornerRadius: outerRadius).cgPath
context.addPath(path)
if stroke {
context.setStrokeColor(color.cgColor)
context.setLineWidth(strokeWidth)
context.strokePath()
} else {
context.fillPath()
}
return
}
var combinedRects: [[CGRect]] = []
var currentRects: [CGRect] = []
for rect in rects {
if rect.width.isZero {
if !currentRects.isEmpty {
combinedRects.append(currentRects)
}
currentRects.removeAll()
} else {
currentRects.append(rect)
}
}
if !currentRects.isEmpty {
combinedRects.append(currentRects)
}
for rects in combinedRects {
var rects = rects.map { $0.insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y) }
let minRadius: CGFloat = 2.0
for _ in 0 ..< rects.count * rects.count {
var hadChanges = false
for i in 0 ..< rects.count - 1 {
if rects[i].maxY > rects[i + 1].minY {
let midY = floor((rects[i].maxY + rects[i + 1].minY) * 0.5)
rects[i].size.height = midY - rects[i].minY
rects[i + 1].origin.y = midY
rects[i + 1].size.height = rects[i + 1].maxY - midY
hadChanges = true
}
if rects[i].maxY >= rects[i + 1].minY && rects[i].insetBy(dx: 0.0, dy: 1.0).intersects(rects[i + 1]) {
if abs(rects[i].minX - rects[i + 1].minX) < minRadius {
let commonMinX = min(rects[i].origin.x, rects[i + 1].origin.x)
if rects[i].origin.x != commonMinX {
rects[i].origin.x = commonMinX
hadChanges = true
}
if rects[i + 1].origin.x != commonMinX {
rects[i + 1].origin.x = commonMinX
hadChanges = true
}
}
if abs(rects[i].maxX - rects[i + 1].maxX) < minRadius {
let commonMaxX = max(rects[i].maxX, rects[i + 1].maxX)
if rects[i].maxX != commonMaxX {
rects[i].size.width = commonMaxX - rects[i].minX
hadChanges = true
}
if rects[i + 1].maxX != commonMaxX {
rects[i + 1].size.width = commonMaxX - rects[i + 1].minX
hadChanges = true
}
}
}
}
if !hadChanges {
break
}
}
context.move(to: CGPoint(x: rects[0].midX, y: rects[0].minY))
context.addLine(to: CGPoint(x: rects[0].maxX - outerRadius, y: rects[0].minY))
context.addArc(tangent1End: rects[0].topRight, tangent2End: CGPoint(x: rects[0].maxX, y: rects[0].minY + outerRadius), radius: outerRadius)
context.addLine(to: CGPoint(x: rects[0].maxX, y: rects[0].midY))
for i in 0 ..< rects.count - 1 {
let rect = rects[i]
let next = rects[i + 1]
if rect.maxX == next.maxX {
context.addLine(to: CGPoint(x: next.maxX, y: next.midY))
} else {
let nextRadius = min(outerRadius, floor(abs(rect.maxX - next.maxX) * 0.5))
context.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - nextRadius))
if next.maxX > rect.maxX {
context.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX + nextRadius, y: rect.maxY), radius: nextRadius)
context.addLine(to: CGPoint(x: next.maxX - nextRadius, y: next.minY))
} else {
context.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX - nextRadius, y: rect.maxY), radius: nextRadius)
context.addLine(to: CGPoint(x: next.maxX + nextRadius, y: next.minY))
}
context.addArc(tangent1End: next.topRight, tangent2End: CGPoint(x: next.maxX, y: next.minY + nextRadius), radius: nextRadius)
context.addLine(to: CGPoint(x: next.maxX, y: next.midY))
}
}
let last = rects[rects.count - 1]
context.addLine(to: CGPoint(x: last.maxX, y: last.maxY - outerRadius))
context.addArc(tangent1End: last.bottomRight, tangent2End: CGPoint(x: last.maxX - outerRadius, y: last.maxY), radius: outerRadius)
context.addLine(to: CGPoint(x: last.minX + outerRadius, y: last.maxY))
context.addArc(tangent1End: last.bottomLeft, tangent2End: CGPoint(x: last.minX, y: last.maxY - outerRadius), radius: outerRadius)
for i in (1 ..< rects.count).reversed() {
let rect = rects[i]
let prev = rects[i - 1]
if rect.minX == prev.minX {
context.addLine(to: CGPoint(x: prev.minX, y: prev.midY))
} else {
let prevRadius = min(outerRadius, floor(abs(rect.minX - prev.minX) * 0.5))
context.addLine(to: CGPoint(x: rect.minX, y: rect.minY + prevRadius))
if rect.minX < prev.minX {
context.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.minX + prevRadius, y: rect.minY), radius: prevRadius)
context.addLine(to: CGPoint(x: prev.minX - prevRadius, y: prev.maxY))
} else {
context.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.minX - prevRadius, y: rect.minY), radius: prevRadius)
context.addLine(to: CGPoint(x: prev.minX + prevRadius, y: prev.maxY))
}
context.addArc(tangent1End: prev.bottomLeft, tangent2End: CGPoint(x: prev.minX, y: prev.maxY - prevRadius), radius: prevRadius)
context.addLine(to: CGPoint(x: prev.minX, y: prev.midY))
}
}
context.addLine(to: CGPoint(x: rects[0].minX, y: rects[0].minY + outerRadius))
context.addArc(tangent1End: rects[0].topLeft, tangent2End: CGPoint(x: rects[0].minX + outerRadius, y: rects[0].minY), radius: outerRadius)
context.addLine(to: CGPoint(x: rects[0].midX, y: rects[0].minY))
if stroke {
context.setStrokeColor(color.cgColor)
context.setLineWidth(strokeWidth)
context.strokePath()
} else {
context.fillPath()
}
}
return
}
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)
}
}
}
private func drawConnectingCorner(context: CGContext, color: UIColor, at point: CGPoint, type: CornerType, radius: CGFloat) {
context.setFillColor(color.cgColor)
switch type {
@@ -76,230 +302,9 @@ public func generateRectsImage(color: UIColor, rects: [CGRect], inset: CGFloat,
bottomRight.x += drawingInset * 2.0
bottomRight.y += drawingInset * 2.0
let capturedTopLeft = topLeft
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)
if useModernPathCalculation {
if rects.count == 1 {
let path = UIBezierPath(roundedRect: rects[0].offsetBy(dx: -topLeft.x, dy: -topLeft.y), cornerRadius: outerRadius).cgPath
context.addPath(path)
if stroke {
context.setStrokeColor(color.cgColor)
context.setLineWidth(strokeWidth)
context.strokePath()
} else {
context.fillPath()
}
return
}
var combinedRects: [[CGRect]] = []
var currentRects: [CGRect] = []
for rect in rects {
if rect.width.isZero {
if !currentRects.isEmpty {
combinedRects.append(currentRects)
}
currentRects.removeAll()
} else {
currentRects.append(rect)
}
}
if !currentRects.isEmpty {
combinedRects.append(currentRects)
}
for rects in combinedRects {
var rects = rects.map { $0.insetBy(dx: -inset, dy: -inset).offsetBy(dx: -topLeft.x, dy: -topLeft.y) }
let minRadius: CGFloat = 2.0
for _ in 0 ..< rects.count * rects.count {
var hadChanges = false
for i in 0 ..< rects.count - 1 {
if rects[i].maxY > rects[i + 1].minY {
let midY = floor((rects[i].maxY + rects[i + 1].minY) * 0.5)
rects[i].size.height = midY - rects[i].minY
rects[i + 1].origin.y = midY
rects[i + 1].size.height = rects[i + 1].maxY - midY
hadChanges = true
}
if rects[i].maxY >= rects[i + 1].minY && rects[i].insetBy(dx: 0.0, dy: 1.0).intersects(rects[i + 1]) {
if abs(rects[i].minX - rects[i + 1].minX) < minRadius {
let commonMinX = min(rects[i].origin.x, rects[i + 1].origin.x)
if rects[i].origin.x != commonMinX {
rects[i].origin.x = commonMinX
hadChanges = true
}
if rects[i + 1].origin.x != commonMinX {
rects[i + 1].origin.x = commonMinX
hadChanges = true
}
}
if abs(rects[i].maxX - rects[i + 1].maxX) < minRadius {
let commonMaxX = max(rects[i].maxX, rects[i + 1].maxX)
if rects[i].maxX != commonMaxX {
rects[i].size.width = commonMaxX - rects[i].minX
hadChanges = true
}
if rects[i + 1].maxX != commonMaxX {
rects[i + 1].size.width = commonMaxX - rects[i + 1].minX
hadChanges = true
}
}
}
}
if !hadChanges {
break
}
}
context.move(to: CGPoint(x: rects[0].midX, y: rects[0].minY))
context.addLine(to: CGPoint(x: rects[0].maxX - outerRadius, y: rects[0].minY))
context.addArc(tangent1End: rects[0].topRight, tangent2End: CGPoint(x: rects[0].maxX, y: rects[0].minY + outerRadius), radius: outerRadius)
context.addLine(to: CGPoint(x: rects[0].maxX, y: rects[0].midY))
for i in 0 ..< rects.count - 1 {
let rect = rects[i]
let next = rects[i + 1]
if rect.maxX == next.maxX {
context.addLine(to: CGPoint(x: next.maxX, y: next.midY))
} else {
let nextRadius = min(outerRadius, floor(abs(rect.maxX - next.maxX) * 0.5))
context.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - nextRadius))
if next.maxX > rect.maxX {
context.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX + nextRadius, y: rect.maxY), radius: nextRadius)
context.addLine(to: CGPoint(x: next.maxX - nextRadius, y: next.minY))
} else {
context.addArc(tangent1End: CGPoint(x: rect.maxX, y: rect.maxY), tangent2End: CGPoint(x: rect.maxX - nextRadius, y: rect.maxY), radius: nextRadius)
context.addLine(to: CGPoint(x: next.maxX + nextRadius, y: next.minY))
}
context.addArc(tangent1End: next.topRight, tangent2End: CGPoint(x: next.maxX, y: next.minY + nextRadius), radius: nextRadius)
context.addLine(to: CGPoint(x: next.maxX, y: next.midY))
}
}
let last = rects[rects.count - 1]
context.addLine(to: CGPoint(x: last.maxX, y: last.maxY - outerRadius))
context.addArc(tangent1End: last.bottomRight, tangent2End: CGPoint(x: last.maxX - outerRadius, y: last.maxY), radius: outerRadius)
context.addLine(to: CGPoint(x: last.minX + outerRadius, y: last.maxY))
context.addArc(tangent1End: last.bottomLeft, tangent2End: CGPoint(x: last.minX, y: last.maxY - outerRadius), radius: outerRadius)
for i in (1 ..< rects.count).reversed() {
let rect = rects[i]
let prev = rects[i - 1]
if rect.minX == prev.minX {
context.addLine(to: CGPoint(x: prev.minX, y: prev.midY))
} else {
let prevRadius = min(outerRadius, floor(abs(rect.minX - prev.minX) * 0.5))
context.addLine(to: CGPoint(x: rect.minX, y: rect.minY + prevRadius))
if rect.minX < prev.minX {
context.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.minX + prevRadius, y: rect.minY), radius: prevRadius)
context.addLine(to: CGPoint(x: prev.minX - prevRadius, y: prev.maxY))
} else {
context.addArc(tangent1End: CGPoint(x: rect.minX, y: rect.minY), tangent2End: CGPoint(x: rect.minX - prevRadius, y: rect.minY), radius: prevRadius)
context.addLine(to: CGPoint(x: prev.minX + prevRadius, y: prev.maxY))
}
context.addArc(tangent1End: prev.bottomLeft, tangent2End: CGPoint(x: prev.minX, y: prev.maxY - prevRadius), radius: prevRadius)
context.addLine(to: CGPoint(x: prev.minX, y: prev.midY))
}
}
context.addLine(to: CGPoint(x: rects[0].minX, y: rects[0].minY + outerRadius))
context.addArc(tangent1End: rects[0].topLeft, tangent2End: CGPoint(x: rects[0].minX + outerRadius, y: rects[0].minY), radius: outerRadius)
context.addLine(to: CGPoint(x: rects[0].midX, y: rects[0].minY))
if stroke {
context.setStrokeColor(color.cgColor)
context.setLineWidth(strokeWidth)
context.strokePath()
} else {
context.fillPath()
}
}
return
}
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)
}
}
drawRectsImageContent(size: size, context: context, color: color, rects: rects, inset: inset, outerRadius: outerRadius, innerRadius: innerRadius, stroke: stroke, strokeWidth: strokeWidth, useModernPathCalculation: useModernPathCalculation, topLeft: capturedTopLeft)
}))
}
+20 -61
View File
@@ -210,7 +210,6 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
private final var displayLink: CADisplayLink!
private final var needsAnimations = false
public final var dynamicBounceEnabled = true
public final var rotated = false
public final var experimentalSnapScrollToItem = false
public final var useMainQueueTransactions = false
@@ -265,6 +264,9 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
public final var addContentOffset: ((CGFloat, ListViewItemNode?) -> Void)?
public final var shouldStopScrolling: ((CGFloat) -> Bool)?
public final var onContentsUpdated: ((ContainedViewLayoutTransition) -> Void)?
public private(set) final var edgeEffectExtension: CGFloat = 0.0
public final var onEdgeEffectExtensionUpdated: ((ContainedViewLayoutTransition) -> Void)?
public final var updateScrollingIndicator: ((ScrollingIndicatorState?, ContainedViewLayoutTransition) -> Void)?
@@ -1044,41 +1046,10 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
self.enqueueUpdateVisibleItems(synchronous: false)
}
var useScrollDynamics = false
let anchor: CGFloat
if self.isTracking {
anchor = self.touchesPosition.y
} else if deltaY < 0.0 {
anchor = self.visibleSize.height
} else {
anchor = 0.0
}
self.didScrollWithOffset?(deltaY, .immediate, nil, self.isTrackingOrDecelerating)
for itemNode in self.itemNodes {
itemNode.updateFrame(itemNode.frame.offsetBy(dx: 0.0, dy: -deltaY), within: self.visibleSize)
if self.dynamicBounceEnabled && itemNode.wantsScrollDynamics {
useScrollDynamics = true
var distance: CGFloat
let itemFrame = itemNode.apparentFrame
if anchor < itemFrame.origin.y {
distance = abs(itemFrame.origin.y - anchor)
} else if anchor > itemFrame.origin.y + itemFrame.size.height {
distance = abs(anchor - (itemFrame.origin.y + itemFrame.size.height))
} else {
distance = 0.0
}
let factor: CGFloat = max(0.08, abs(distance) / self.visibleSize.height)
let resistance: CGFloat = testSpringFreeResistance
itemNode.addScrollingOffset(deltaY * factor * resistance)
}
}
if !self.snapToBounds(snapTopItem: false, stackFromBottom: self.stackFromBottom, insetDeltaOffsetFix: 0.0).offset.isZero {
@@ -1088,38 +1059,10 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
self.updateItemHeaders(leftInset: self.insets.left, rightInset: self.insets.right, synchronousLoad: false)
for (_, headerNode) in self.itemHeaderNodes {
if self.dynamicBounceEnabled && headerNode.wantsScrollDynamics {
useScrollDynamics = true
var distance: CGFloat
let itemFrame = headerNode.frame
if anchor < itemFrame.origin.y {
distance = abs(itemFrame.origin.y - anchor)
} else if anchor > itemFrame.origin.y + itemFrame.size.height {
distance = abs(anchor - (itemFrame.origin.y + itemFrame.size.height))
} else {
distance = 0.0
}
let factor: CGFloat = max(0.08, abs(distance) / self.visibleSize.height)
let resistance: CGFloat = testSpringFreeResistance
headerNode.addScrollingOffset(deltaY * factor * resistance)
}
}
if useScrollDynamics {
self.setNeedsAnimations()
}
self.updateVisibleContentOffset()
self.updateVisibleItemRange()
self.updateItemNodesVisibilities(onlyPositive: false)
self.onContentsUpdated?(.immediate)
//CATransaction.commit()
}
private func calculateAdditionalTopInverseInset() -> CGFloat {
@@ -3914,6 +3857,8 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
let flashing = self.headerItemsAreFlashing()
var maxEdgeEffectExtension: CGFloat = 0.0
func addHeader(id: VisibleHeaderNodeId, upperBound: CGFloat, upperIndex: Int, upperBoundEdge: CGFloat, lowerBound: CGFloat, lowerIndex: Int, item: ListViewItemHeader, hasValidNodes: Bool) {
let itemHeaderHeight: CGFloat = item.height
@@ -3928,7 +3873,11 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
switch item.stickDirection {
case .top:
naturalY = lowerBound
headerFrame = CGRect(origin: CGPoint(x: 0.0, y: min(max(upperDisplayBound, upperBound), lowerBound - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight))
if item.isSticky {
headerFrame = CGRect(origin: CGPoint(x: 0.0, y: min(max(upperDisplayBound, upperBound), lowerBound - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight))
} else {
headerFrame = CGRect(origin: CGPoint(x: 0.0, y: min(upperBound, lowerBound - itemHeaderHeight)), size: CGSize(width: self.visibleSize.width, height: itemHeaderHeight))
}
stickLocationDistance = headerFrame.minY - upperBound
stickLocationDistanceFactor = max(0.0, min(1.0, stickLocationDistance / itemHeaderHeight))
case .topEdge:
@@ -4096,6 +4045,11 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
}
headerNode.updateStickDistanceFactor(stickLocationDistanceFactor, distance: stickLocationDistance, transition: .immediate)
}
if headerNode.contributesToEdgeEffect && stickLocationDistance > 0.0 {
maxEdgeEffectExtension = max(maxEdgeEffectExtension, upperDisplayBound + headerFrame.height + 8.0)
}
headerNode.offsetByHeaderNodeId = offsetByHeaderNodeId
headerNode.naturalOriginY = naturalY
var maxIntersectionHeight: (CGFloat, Int)?
@@ -4228,6 +4182,11 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
}
}
}
if self.edgeEffectExtension != maxEdgeEffectExtension {
self.edgeEffectExtension = maxEdgeEffectExtension
self.onEdgeEffectExtensionUpdated?(transition.0)
}
}
private func updateItemNodesVisibilities(onlyPositive: Bool) {
@@ -12,6 +12,7 @@ public protocol ListViewItemHeader: AnyObject {
var id: ListViewItemNode.HeaderId { get }
var stackingId: ListViewItemNode.HeaderId? { get }
var stickDirection: ListViewItemHeaderStickDirection { get }
var isSticky: Bool { get }
var height: CGFloat { get }
var stickOverInsets: Bool { get }
@@ -21,14 +22,19 @@ public protocol ListViewItemHeader: AnyObject {
func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?)
}
public extension ListViewItemHeader {
var isSticky: Bool {
return true
}
}
open class ListViewItemHeaderNode: ASDisplayNode {
private final var spring: ListViewItemSpring?
let wantsScrollDynamics: Bool
let isRotated: Bool
final private(set) var internalStickLocationDistanceFactor: CGFloat = 0.0
final var internalStickLocationDistance: CGFloat = 0.0
private var isFlashingOnScrolling = false
weak var attachedToItemNode: ListViewItemNode?
public var contributesToEdgeEffect: Bool = false
var offsetByHeaderNodeId: ListViewItemNode.HeaderId?
var naturalOriginY: CGFloat?
@@ -53,12 +59,8 @@ open class ListViewItemHeaderNode: ASDisplayNode {
return self.alpha
}
public init(layerBacked: Bool = false, dynamicBounce: Bool = false, isRotated: Bool = false, seeThrough: Bool = false) {
self.wantsScrollDynamics = dynamicBounce
public init(layerBacked: Bool = false, isRotated: Bool = false, seeThrough: Bool = false) {
self.isRotated = isRotated
if dynamicBounce {
self.spring = ListViewItemSpring(stiffness: -280.0, damping: -24.0, mass: 0.85)
}
super.init()
@@ -69,54 +71,10 @@ open class ListViewItemHeaderNode: ASDisplayNode {
}
final func addScrollingOffset(_ scrollingOffset: CGFloat) {
if self.spring != nil && internalStickLocationDistanceFactor.isZero {
let bounds = self.bounds
self.bounds = CGRect(origin: CGPoint(x: 0.0, y: bounds.origin.y + scrollingOffset), size: bounds.size)
}
}
public func animate(_ timestamp: Double) -> Bool {
var continueAnimations = false
if let _ = self.spring {
let bounds = self.bounds
var offset = bounds.origin.y
let currentOffset = offset
let frictionConstant: CGFloat = testSpringFriction
let springConstant: CGFloat = testSpringConstant
let time: CGFloat = 1.0 / 60.0
// friction force = velocity * friction constant
let frictionForce = self.spring!.velocity * frictionConstant
// spring force = (target point - current position) * spring constant
let springForce = -currentOffset * springConstant
// force = spring force - friction force
let force = springForce - frictionForce
// velocity = current velocity + force * time / mass
self.spring!.velocity = self.spring!.velocity + force * time
// position = current position + velocity * time
offset = currentOffset + self.spring!.velocity * time
offset = offset.isNaN ? 0.0 : offset
let epsilon: CGFloat = 0.1
if abs(offset) < epsilon && abs(self.spring!.velocity) < epsilon {
offset = 0.0
self.spring!.velocity = 0.0
} else {
continueAnimations = true
}
if abs(offset) > 250.0 {
offset = offset < 0.0 ? -250.0 : 250.0
}
self.bounds = CGRect(origin: CGPoint(x: 0.0, y: offset), size: bounds.size)
}
return continueAnimations
return false
}
open func animateRemoved(duration: Double) {
@@ -129,7 +129,6 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
return nil
}
private final var spring: ListViewItemSpring?
private final var animations: [(String, ListViewAnimation)] = []
private final var pendingControlledTransitions: [ControlledTransition] = []
private final var controlledTransitions: [ControlledTransitionContext] = []
@@ -142,8 +141,6 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
open func attachedHeaderNodesUpdated() {
}
final let wantsScrollDynamics: Bool
open var preferredAnimationCurve: (CGFloat) -> CGFloat {
return listViewAnimationCurveSystem
}
@@ -236,12 +233,7 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
return .complete()
}
public init(layerBacked: Bool, dynamicBounce: Bool = true, rotated: Bool = false, seeThrough: Bool = false) {
if dynamicBounce {
self.spring = ListViewItemSpring(stiffness: -280.0, damping: -24.0, mass: 0.85)
}
self.wantsScrollDynamics = dynamicBounce
public init(layerBacked: Bool, rotated: Bool = false, seeThrough: Bool = false) {
self.rotated = rotated
super.init()
@@ -338,56 +330,14 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
}
final func addScrollingOffset(_ scrollingOffset: CGFloat) {
if self.spring != nil {
self.contentOffset += scrollingOffset
}
}
func initializeDynamicsFromSibling(_ itemView: ListViewItemNode, additionalOffset: CGFloat) {
if let itemViewSpring = itemView.spring {
self.contentOffset = itemView.contentOffset + additionalOffset
self.spring?.velocity = itemViewSpring.velocity
}
}
public func animate(timestamp: Double, invertOffsetDirection: inout Bool) -> Bool {
var continueAnimations = false
if let _ = self.spring {
var offset = self.contentOffset
let frictionConstant: CGFloat = testSpringFriction
let springConstant: CGFloat = testSpringConstant
let time: CGFloat = 1.0 / 60.0
// friction force = velocity * friction constant
let frictionForce = self.spring!.velocity * frictionConstant
// spring force = (target point - current position) * spring constant
let springForce = -self.contentOffset * springConstant
// force = spring force - friction force
let force = springForce - frictionForce
// velocity = current velocity + force * time / mass
self.spring!.velocity = self.spring!.velocity + force * time
// position = current position + velocity * time
offset = self.contentOffset + self.spring!.velocity * time
offset = offset.isNaN ? 0.0 : offset
let epsilon: CGFloat = 0.1
if abs(offset) < epsilon && abs(self.spring!.velocity) < epsilon {
offset = 0.0
self.spring!.velocity = 0.0
} else {
continueAnimations = true
}
if abs(offset) > 250.0 {
offset = offset < 0.0 ? -250.0 : 250.0
}
self.contentOffset = offset
}
var i = 0
var animationCount = self.animations.count
while i < animationCount {
@@ -109,7 +109,7 @@ public final class NavigationModalFrame: ASDisplayNode {
let contentScale = (layout.size.width - sideInset * 2.0) / layout.size.width
let bottomInset: CGFloat = layout.size.height - contentScale * layout.size.height - topInset
let cornerRadius: CGFloat = 28.0
let cornerRadius: CGFloat = 38.0
let initialCornerRadius: CGFloat
if !layout.safeInsets.top.isZero {
initialCornerRadius = layout.deviceMetrics.screenCornerRadius
@@ -0,0 +1,332 @@
import Foundation
import UIKit
import AsyncDisplayKit
private var sharedIsReduceTransparencyEnabled = UIAccessibility.isReduceTransparencyEnabled
public final class NavigationBackgroundNode: ASDisplayNode {
private var _color: UIColor
public var color: UIColor {
return self._color
}
private var enableBlur: Bool
private var enableSaturation: Bool
private var customBlurRadius: CGFloat?
public var effectView: UIVisualEffectView?
private let backgroundNode: ASDisplayNode
public var backgroundView: UIView {
return self.backgroundNode.view
}
private var validLayout: (CGSize, CGFloat)?
public var backgroundCornerRadius: CGFloat {
if let (_, cornerRadius) = self.validLayout {
return cornerRadius
} else {
return 0.0
}
}
public init(color: UIColor, enableBlur: Bool = true, enableSaturation: Bool = true, customBlurRadius: CGFloat? = nil) {
self._color = .clear
self.enableBlur = enableBlur
self.enableSaturation = enableSaturation
self.customBlurRadius = customBlurRadius
self.backgroundNode = ASDisplayNode()
super.init()
self.addSubnode(self.backgroundNode)
self.updateColor(color: color, transition: .immediate)
}
public override func didLoad() {
super.didLoad()
if self.scheduledUpdate {
self.scheduledUpdate = false
self.updateBackgroundBlur(forceKeepBlur: false)
}
}
private var scheduledUpdate = false
private func updateBackgroundBlur(forceKeepBlur: Bool) {
guard self.isNodeLoaded else {
self.scheduledUpdate = true
return
}
if self.enableBlur && !sharedIsReduceTransparencyEnabled && ((self._color.alpha > .ulpOfOne && self._color.alpha < 0.95) || forceKeepBlur) {
if self.effectView == nil {
let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
for subview in effectView.subviews {
if subview.description.contains("VisualEffectSubview") {
subview.isHidden = true
}
}
if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters {
sublayer.backgroundColor = nil
sublayer.isOpaque = false
var allowedKeys: [String] = [
"gaussianBlur"
]
if self.enableSaturation {
allowedKeys.append("colorSaturate")
}
sublayer.filters = filters.filter { filter in
guard let filter = filter as? NSObject else {
return true
}
let filterName = String(describing: filter)
if !allowedKeys.contains(filterName) {
return false
}
if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" {
filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius")
}
return true
}
}
if let (size, cornerRadius) = self.validLayout {
effectView.frame = CGRect(origin: CGPoint(), size: size)
ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius)
effectView.clipsToBounds = !cornerRadius.isZero
}
self.effectView = effectView
self.view.insertSubview(effectView, at: 0)
}
} else if let effectView = self.effectView {
self.effectView = nil
effectView.removeFromSuperview()
}
}
public func updateColor(color: UIColor, enableBlur: Bool? = nil, enableSaturation: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) {
let effectiveEnableBlur = enableBlur ?? self.enableBlur
let effectiveEnableSaturation = enableSaturation ?? self.enableSaturation
if self._color.isEqual(color) && self.enableBlur == effectiveEnableBlur && self.enableSaturation == effectiveEnableSaturation {
return
}
self._color = color
self.enableBlur = effectiveEnableBlur
self.enableSaturation = effectiveEnableSaturation
if sharedIsReduceTransparencyEnabled {
transition.updateBackgroundColor(node: self.backgroundNode, color: self._color.withAlphaComponent(1.0))
} else {
transition.updateBackgroundColor(node: self.backgroundNode, color: self._color)
}
self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur)
}
public func update(size: CGSize, cornerRadius: CGFloat = 0.0, transition: ContainedViewLayoutTransition, beginWithCurrentState: Bool = true) {
self.validLayout = (size, cornerRadius)
let contentFrame = CGRect(origin: CGPoint(), size: size)
transition.updateFrame(node: self.backgroundNode, frame: contentFrame, beginWithCurrentState: true)
if let effectView = self.effectView, effectView.frame != contentFrame {
transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true)
if let sublayers = effectView.layer.sublayers {
for sublayer in sublayers {
transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true)
}
}
}
transition.updateCornerRadius(node: self.backgroundNode, cornerRadius: cornerRadius)
if let effectView = self.effectView {
transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius)
effectView.clipsToBounds = !cornerRadius.isZero
}
}
public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) {
self.validLayout = (size, cornerRadius)
let contentFrame = CGRect(origin: CGPoint(), size: size)
animator.updateFrame(layer: self.backgroundNode.layer, frame: contentFrame, completion: nil)
if let effectView = self.effectView, effectView.frame != contentFrame {
animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil)
if let sublayers = effectView.layer.sublayers {
for sublayer in sublayers {
animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil)
}
}
}
animator.updateCornerRadius(layer: self.backgroundNode.layer, cornerRadius: cornerRadius, completion: nil)
if let effectView = self.effectView {
animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil)
effectView.clipsToBounds = !cornerRadius.isZero
}
}
}
open class BlurredBackgroundView: UIView {
private var _color: UIColor?
private var enableBlur: Bool
private var customBlurRadius: CGFloat?
public private(set) var effectView: UIVisualEffectView?
private let backgroundView: UIView
private var validLayout: (CGSize, CGFloat)?
public var backgroundCornerRadius: CGFloat {
if let (_, cornerRadius) = self.validLayout {
return cornerRadius
} else {
return 0.0
}
}
public init(color: UIColor?, enableBlur: Bool = true, customBlurRadius: CGFloat? = nil) {
self._color = nil
self.enableBlur = enableBlur
self.customBlurRadius = customBlurRadius
self.backgroundView = UIView()
super.init(frame: CGRect())
self.addSubview(self.backgroundView)
if let color = color {
self.updateColor(color: color, transition: .immediate)
}
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func updateBackgroundBlur(forceKeepBlur: Bool) {
if let color = self._color, self.enableBlur && !sharedIsReduceTransparencyEnabled && ((color.alpha > .ulpOfOne && color.alpha < 0.95) || forceKeepBlur) {
if self.effectView == nil {
let effectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
for subview in effectView.subviews {
if subview.description.contains("VisualEffectSubview") {
subview.isHidden = true
}
}
if let sublayer = effectView.layer.sublayers?[0], let filters = sublayer.filters {
sublayer.backgroundColor = nil
sublayer.isOpaque = false
//sublayer.setValue(true as NSNumber, forKey: "allowsInPlaceFiltering")
let allowedKeys: [String] = [
"colorSaturate",
"gaussianBlur"
]
sublayer.filters = filters.filter { filter in
guard let filter = filter as? NSObject else {
return true
}
let filterName = String(describing: filter)
if !allowedKeys.contains(filterName) {
return false
}
if let customBlurRadius = self.customBlurRadius, filterName == "gaussianBlur" {
filter.setValue(customBlurRadius as NSNumber, forKey: "inputRadius")
}
return true
}
}
if let (size, cornerRadius) = self.validLayout {
effectView.frame = CGRect(origin: CGPoint(), size: size)
ContainedViewLayoutTransition.immediate.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius)
effectView.clipsToBounds = !cornerRadius.isZero
}
self.effectView = effectView
self.insertSubview(effectView, at: 0)
}
} else if let effectView = self.effectView {
self.effectView = nil
effectView.removeFromSuperview()
}
}
public func updateColor(color: UIColor, enableBlur: Bool? = nil, forceKeepBlur: Bool = false, transition: ContainedViewLayoutTransition) {
let effectiveEnableBlur = enableBlur ?? self.enableBlur
if self._color == color && self.enableBlur == effectiveEnableBlur {
return
}
self._color = color
self.enableBlur = effectiveEnableBlur
if sharedIsReduceTransparencyEnabled {
transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color.withAlphaComponent(1.0))
} else {
transition.updateBackgroundColor(layer: self.backgroundView.layer, color: color)
}
self.updateBackgroundBlur(forceKeepBlur: forceKeepBlur)
}
public func update(size: CGSize, cornerRadius: CGFloat = 0.0, maskedCorners: CACornerMask = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner], transition: ContainedViewLayoutTransition) {
self.validLayout = (size, cornerRadius)
let contentFrame = CGRect(origin: CGPoint(), size: size)
transition.updateFrame(view: self.backgroundView, frame: contentFrame, beginWithCurrentState: true)
if let effectView = self.effectView, effectView.frame != contentFrame {
transition.updateFrame(layer: effectView.layer, frame: contentFrame, beginWithCurrentState: true)
if let sublayers = effectView.layer.sublayers {
for sublayer in sublayers {
transition.updateFrame(layer: sublayer, frame: contentFrame, beginWithCurrentState: true)
}
}
}
if #available(iOS 11.0, *) {
self.backgroundView.layer.maskedCorners = maskedCorners
}
transition.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius)
if let effectView = self.effectView {
transition.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius)
effectView.clipsToBounds = !cornerRadius.isZero
if #available(iOS 11.0, *) {
effectView.layer.maskedCorners = maskedCorners
}
}
}
public func update(size: CGSize, cornerRadius: CGFloat = 0.0, animator: ControlledTransitionAnimator) {
self.validLayout = (size, cornerRadius)
let contentFrame = CGRect(origin: CGPoint(), size: size)
animator.updateFrame(layer: self.backgroundView.layer, frame: contentFrame, completion: nil)
if let effectView = self.effectView, effectView.frame != contentFrame {
animator.updateFrame(layer: effectView.layer, frame: contentFrame, completion: nil)
if let sublayers = effectView.layer.sublayers {
for sublayer in sublayers {
animator.updateFrame(layer: sublayer, frame: contentFrame, completion: nil)
}
}
}
animator.updateCornerRadius(layer: self.backgroundView.layer, cornerRadius: cornerRadius, completion: nil)
if let effectView = self.effectView {
animator.updateCornerRadius(layer: effectView.layer, cornerRadius: cornerRadius, completion: nil)
effectView.clipsToBounds = !cornerRadius.isZero
}
}
}
File diff suppressed because it is too large Load Diff
@@ -12,7 +12,7 @@ public final class NavigationBarBadgeNode: ASDisplayNode {
private let font: UIFont = Font.regular(13.0)
var text: String = "" {
public var text: String = "" {
didSet {
self.textNode.attributedText = NSAttributedString(string: self.text, font: self.font, textColor: self.textColor)
self.invalidateCalculatedLayout()
@@ -39,7 +39,7 @@ public final class NavigationBarBadgeNode: ASDisplayNode {
self.addSubnode(self.textNode)
}
func updateTheme(fillColor: UIColor, strokeColor: UIColor, textColor: UIColor) {
public func updateTheme(fillColor: UIColor, strokeColor: UIColor, textColor: UIColor) {
self.fillColor = fillColor
self.strokeColor = strokeColor
self.textColor = textColor
@@ -26,6 +26,7 @@ open class NavigationBarContentNode: ASDisplayNode {
return .replacement
}
open func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) {
open func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize {
return size
}
}
@@ -1,8 +1,10 @@
import Foundation
import UIKit
public protocol NavigationBarTitleView {
public protocol NavigationBarTitleView: UIView {
var requestUpdate: ((ContainedViewLayoutTransition) -> Void)? { get set }
func animateLayoutTransition()
func updateLayout(size: CGSize, clearBounds: CGRect, transition: ContainedViewLayoutTransition) -> CGRect
func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize
}
@@ -201,10 +201,9 @@ final class NavigationTransitionCoordinator {
position = progress
}
transition.animateView {
topNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: bottomNavigationBar, transition: self.transition, role: .top, progress: position)
bottomNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: topNavigationBar, transition: self.transition, role: .bottom, progress: position)
}
let _ = position
let _ = topNavigationBar
let _ = bottomNavigationBar
}
}
@@ -218,15 +217,16 @@ final class NavigationTransitionCoordinator {
position = progress
}
topNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: bottomNavigationBar, transition: self.transition, role: .top, progress: position)
bottomNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: topNavigationBar, transition: self.transition, role: .bottom, progress: position)
let _ = position
let _ = topNavigationBar
let _ = bottomNavigationBar
}
}
func endNavigationBarTransition() {
if let topNavigationBar = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar, self.inlineNavigationBarTransition {
topNavigationBar.transitionState = nil
bottomNavigationBar.transitionState = nil
let _ = topNavigationBar
let _ = bottomNavigationBar
}
}
@@ -0,0 +1,47 @@
import Foundation
import UIKit
import AsyncDisplayKit
open class SparseNode: ASDisplayNode {
override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.alpha.isZero {
return nil
}
for view in self.view.subviews.reversed() {
if let result = view.hitTest(self.view.convert(point, to: view), with: event), result.isUserInteractionEnabled {
return result
}
}
if !self.bounds.inset(by: self.hitTestSlop).contains(point) {
return nil
}
let result = super.hitTest(point, with: event)
if result != self.view {
return result
} else {
return nil
}
}
}
open class SparseContainerView: UIView {
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.alpha.isZero {
return nil
}
for view in self.subviews.reversed() {
if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled {
return result
}
}
let result = super.hitTest(point, with: event)
if result != self {
return result
} else {
return nil
}
}
}
+14 -4
View File
@@ -108,9 +108,13 @@ public extension UIColor {
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
if self.getRed(&red, green: &green, blue: &blue, alpha: nil) {
return (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, green) * 255.0) << 8) | (UInt32(max(0.0, blue) * 255.0))
let r: UInt32 = UInt32(max(0.0, red) * 255.0)
let g: UInt32 = UInt32(max(0.0, green) * 255.0)
let b: UInt32 = UInt32(max(0.0, blue) * 255.0)
return (r << 16) | (g << 8) | b
} else if self.getWhite(&red, alpha: nil) {
return (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, red) * 255.0) << 8) | (UInt32(max(0.0, red) * 255.0))
let w: UInt32 = UInt32(max(0.0, red) * 255.0)
return (w << 16) | (w << 8) | w
} else {
return 0
}
@@ -122,9 +126,15 @@ public extension UIColor {
var blue: CGFloat = 0.0
var alpha: CGFloat = 0.0
if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
return (UInt32(alpha * 255.0) << 24) | (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, green) * 255.0) << 8) | (UInt32(max(0.0, blue) * 255.0))
let a: UInt32 = UInt32(alpha * 255.0)
let r: UInt32 = UInt32(max(0.0, red) * 255.0)
let g: UInt32 = UInt32(max(0.0, green) * 255.0)
let b: UInt32 = UInt32(max(0.0, blue) * 255.0)
return (a << 24) | (r << 16) | (g << 8) | b
} else if self.getWhite(&red, alpha: &alpha) {
return (UInt32(max(0.0, alpha) * 255.0) << 24) | (UInt32(max(0.0, red) * 255.0) << 16) | (UInt32(max(0.0, red) * 255.0) << 8) | (UInt32(max(0.0, red) * 255.0))
let a: UInt32 = UInt32(max(0.0, alpha) * 255.0)
let w: UInt32 = UInt32(max(0.0, red) * 255.0)
return (a << 24) | (w << 16) | (w << 8) | w
} else {
return 0
}
+58 -10
View File
@@ -217,6 +217,18 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
open var navigationBarRequiresEntireLayoutUpdate: Bool {
return true
}
public struct TabBarSearchState: Equatable {
public var isActive: Bool
public init(isActive: Bool) {
self.isActive = isActive
}
}
public private(set) var tabBarSearchState: TabBarSearchState?
public var tabBarSearchStateUpdated: ((ContainedViewLayoutTransition) -> Void)?
public var currentTabBarSearchNode: (() -> ASDisplayNode?)?
private weak var activeInputViewCandidate: UIResponder?
private weak var activeInputView: UIResponder?
@@ -237,16 +249,24 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
open var interactiveNavivationGestureEdgeWidth: InteractiveTransitionGestureRecognizerEdgeWidth? {
return nil
}
open var navigationEdgeEffectExtension: CGFloat {
return 0.0
}
public func updateNavigationEdgeEffectExtension(transition: ContainedViewLayoutTransition) {
if let navigationBar = self.navigationBar {
navigationBar.updateEdgeEffectExtension(value: max(0.0, self.navigationEdgeEffectExtension - navigationBar.frame.maxY), transition: transition)
}
}
open func navigationLayout(layout: ContainerViewLayout) -> NavigationLayout {
let statusBarHeight: CGFloat = layout.statusBarHeight ?? 0.0
var defaultNavigationBarHeight: CGFloat
if self._presentedInModal && self._hasGlassStyle {
defaultNavigationBarHeight = 66.0
} else if self._presentedInModal && layout.orientation == .portrait {
defaultNavigationBarHeight = 56.0
defaultNavigationBarHeight = 68.0
} else {
defaultNavigationBarHeight = 44.0
defaultNavigationBarHeight = 60.0
}
let navigationBarHeight: CGFloat = statusBarHeight + (self.navigationBar?.contentHeight(defaultHeight: defaultNavigationBarHeight) ?? defaultNavigationBarHeight)
@@ -375,7 +395,7 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
public init(navigationBarPresentationData: NavigationBarPresentationData?) {
self.statusBar = StatusBar()
if let navigationBarPresentationData = navigationBarPresentationData {
self.navigationBar = NavigationBar(presentationData: navigationBarPresentationData)
self.navigationBar = defaultNavigationBarImpl!(navigationBarPresentationData)
} else {
self.navigationBar = nil
}
@@ -447,14 +467,29 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
}
if let navigationBar = self.navigationBar {
if let contentNode = navigationBar.contentNode, case .expansion = contentNode.mode, !self.displayNavigationBar {
navigationBarFrame.origin.y -= navigationLayout.defaultContentHeight
navigationBarFrame.size.height += contentNode.height + navigationLayout.defaultContentHeight + statusBarHeight
navigationBarFrame.origin.y -= navigationLayout.defaultContentHeight + statusBarHeight
navigationBarFrame.size.height += contentNode.height + navigationLayout.defaultContentHeight + statusBarHeight * 2.0
if self._presentedInModal && self._hasGlassStyle {
navigationBarFrame.size.height += 8.0
}
}
//navigationBar.backgroundColor = .blue
if let _ = navigationBar.contentNode, let _ = navigationBar.secondaryContentNode, !self.displayNavigationBar {
navigationBarFrame.size.height += navigationBar.secondaryContentHeight
}
navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: navigationLayout.defaultContentHeight, additionalTopHeight: statusBarHeight, additionalContentHeight: self.additionalNavigationBarHeight, additionalBackgroundHeight: additionalBackgroundHeight, additionalCutout: additionalCutout, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, isLandscape: isLandscape, transition: transition)
var additionalTopHeight = statusBarHeight
if !self.displayNavigationBar {
additionalTopHeight -= statusBarHeight
if statusBarHeight != 0.0 {
additionalTopHeight += 6.0
}
}
if self._presentedInModal && self._hasGlassStyle {
additionalTopHeight += 8.0
}
navigationBar.updateLayout(size: navigationBarFrame.size, defaultHeight: navigationLayout.defaultContentHeight, additionalTopHeight: additionalTopHeight, additionalContentHeight: self.additionalNavigationBarHeight, additionalBackgroundHeight: additionalBackgroundHeight, additionalCutout: additionalCutout, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, appearsHidden: !self.displayNavigationBar, isLandscape: isLandscape, transition: transition)
if !transition.isAnimated {
navigationBar.layer.removeAnimation(forKey: "bounds")
navigationBar.layer.removeAnimation(forKey: "position")
@@ -542,14 +577,14 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
UIView.transition(with: navigationBar.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
}, completion: nil)
}
self.navigationBar?.updatePresentationData(presentationData)
self.navigationBar?.updatePresentationData(presentationData, transition: .immediate)
if let parent = self.parent as? TabBarController {
if parent.currentController === self {
if animated, let navigationBar = parent.navigationBar {
UIView.transition(with: navigationBar.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
}, completion: nil)
}
parent.navigationBar?.updatePresentationData(presentationData)
parent.navigationBar?.updatePresentationData(presentationData, transition: .immediate)
}
}
}
@@ -705,9 +740,22 @@ public protocol CustomViewControllerNavigationDataSummary: AnyObject {
open func tabBarDisabledAction() {
}
open func tabBarActivateSearch() {
}
open func tabBarDeactivateSearch() {
}
open func tabBarItemSwipeAction(direction: TabBarItemSwipeDirection) {
}
public func updateTabBarSearchState(_ tabBarSearchState: TabBarSearchState?, transition: ContainedViewLayoutTransition) {
if self.tabBarSearchState != tabBarSearchState {
self.tabBarSearchState = tabBarSearchState
self.tabBarSearchStateUpdated?(transition)
}
}
open func updatePossibleControllerDropContent(content: NavigationControllerDropContent?) {
}