mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Glass
This commit is contained in:
parent
7ed08c0141
commit
4a13c3830a
234 changed files with 1442 additions and 1764 deletions
|
|
@ -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
|
||||
|
||||
|
|
@ -4096,6 +4041,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 +4178,11 @@ open class ListView: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDel
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.edgeEffectExtension != maxEdgeEffectExtension {
|
||||
self.edgeEffectExtension = maxEdgeEffectExtension
|
||||
self.onEdgeEffectExtensionUpdated?(transition.0)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateItemNodesVisibilities(onlyPositive: Bool) {
|
||||
|
|
|
|||
|
|
@ -22,13 +22,12 @@ public protocol ListViewItemHeader: AnyObject {
|
|||
}
|
||||
|
||||
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 +52,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 +64,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 {
|
||||
|
|
|
|||
|
|
@ -190,6 +190,8 @@ public protocol NavigationBar: ASDisplayNode {
|
|||
var requestContainerLayout: ((ContainedViewLayoutTransition) -> Void)? { get set }
|
||||
|
||||
func updateLayout(size: CGSize, defaultHeight: CGFloat, additionalTopHeight: CGFloat, additionalContentHeight: CGFloat, additionalBackgroundHeight: CGFloat, additionalCutout: CGSize?, leftInset: CGFloat, rightInset: CGFloat, appearsHidden: Bool, isLandscape: Bool, transition: ContainedViewLayoutTransition)
|
||||
|
||||
func updateEdgeEffectExtension(value: CGFloat, transition: ContainedViewLayoutTransition)
|
||||
}
|
||||
|
||||
public var defaultNavigationBarImpl: ((NavigationBarPresentationData) -> NavigationBar)?
|
||||
|
|
|
|||
|
|
@ -249,6 +249,16 @@ 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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue