mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-01-08 05:55:13 +00:00
Fixed GridNode scrolling animations
This commit is contained in:
@@ -6,6 +6,10 @@ public enum ContainedViewLayoutTransitionCurve {
|
||||
case easeInOut
|
||||
case spring
|
||||
case custom(Float, Float, Float, Float)
|
||||
|
||||
public static var slide: ContainedViewLayoutTransitionCurve {
|
||||
return .custom(0.33, 0.52, 0.25, 0.99)
|
||||
}
|
||||
}
|
||||
|
||||
public extension ContainedViewLayoutTransitionCurve {
|
||||
|
||||
@@ -939,12 +939,13 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
|
||||
if let offset = offset {
|
||||
let timingFunction = curve.timingFunction
|
||||
let mediaTimingFunction = curve.mediaTimingFunction
|
||||
|
||||
for (index, itemNode) in self.itemNodes where existingItemIndices.contains(index) {
|
||||
itemNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
|
||||
itemNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction, additive: true)
|
||||
}
|
||||
for (wrappedSection, sectionNode) in self.sectionNodes where existingSections.contains(wrappedSection) {
|
||||
sectionNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, additive: true)
|
||||
sectionNode.layer.animatePosition(from: CGPoint(x: 0.0, y: offset), to: CGPoint(), duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction, additive: true)
|
||||
}
|
||||
|
||||
for index in self.itemNodes.keys {
|
||||
@@ -953,7 +954,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
if let previousFrame = previousItemFrames[WrappedGridItemNode(node: itemNode)] {
|
||||
self.removeItemNodeWithIndex(index, removeNode: false)
|
||||
let position = CGPoint(x: previousFrame.midX, y: previousFrame.midY)
|
||||
itemNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y - boundsOffset), to: CGPoint(x: position.x, y: position.y + contentOffset.y - boundsOffset - offset), duration: duration, timingFunction: timingFunction, removeOnCompletion: false, force: true, completion: { [weak itemNode] _ in
|
||||
itemNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y - boundsOffset), to: CGPoint(x: position.x, y: position.y + contentOffset.y - boundsOffset - offset), duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: false, force: true, completion: { [weak itemNode] _ in
|
||||
itemNode?.removeFromSupernode()
|
||||
})
|
||||
} else {
|
||||
@@ -965,7 +966,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
for itemNode in removedNodes {
|
||||
if let previousFrame = previousItemFrames[WrappedGridItemNode(node: itemNode)] {
|
||||
let position = CGPoint(x: previousFrame.midX, y: previousFrame.midY)
|
||||
itemNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y), to: CGPoint(x: position.x, y: position.y + contentOffset.y - offset), duration: duration, timingFunction: timingFunction, removeOnCompletion: false, force: true, completion: { [weak itemNode] _ in
|
||||
itemNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y), to: CGPoint(x: position.x, y: position.y + contentOffset.y - offset), duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: false, force: true, completion: { [weak itemNode] _ in
|
||||
itemNode?.removeFromSupernode()
|
||||
})
|
||||
} else {
|
||||
@@ -979,7 +980,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
if let previousFrame = previousItemFrames[WrappedGridItemNode(node: sectionNode)] {
|
||||
self.removeSectionNodeWithSection(wrappedSection, removeNode: false)
|
||||
let position = CGPoint(x: previousFrame.midX, y: previousFrame.midY)
|
||||
sectionNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y), to: CGPoint(x: position.x, y: position.y + contentOffset.y - offset), duration: duration, timingFunction: timingFunction, removeOnCompletion: false, force: true, completion: { [weak sectionNode] _ in
|
||||
sectionNode.layer.animatePosition(from: CGPoint(x: position.x, y: position.y + contentOffset.y), to: CGPoint(x: position.x, y: position.y + contentOffset.y - offset), duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction, removeOnCompletion: false, force: true, completion: { [weak sectionNode] _ in
|
||||
sectionNode?.removeFromSupernode()
|
||||
})
|
||||
} else {
|
||||
@@ -1006,6 +1007,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
}
|
||||
} else if let previousItemFrames = previousItemFrames, case let .animated(duration, curve) = itemTransition {
|
||||
let timingFunction = curve.timingFunction
|
||||
let mediaTimingFunction = curve.mediaTimingFunction
|
||||
let contentOffset = self.scrollView.contentOffset
|
||||
|
||||
for index in self.itemNodes.keys {
|
||||
@@ -1021,7 +1023,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
self.removeItemNodeWithIndex(index, removeNode: true)
|
||||
}
|
||||
} else if let previousFrame = previousItemFrames[WrappedGridItemNode(node: itemNode)] {
|
||||
itemNode.layer.animatePosition(from: CGPoint(x: previousFrame.midX, y: previousFrame.midY + contentOffset.y), to: itemNode.layer.position, duration: duration, timingFunction: timingFunction)
|
||||
itemNode.layer.animatePosition(from: CGPoint(x: previousFrame.midX, y: previousFrame.midY + contentOffset.y), to: itemNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction)
|
||||
} else {
|
||||
itemNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.12, timingFunction: kCAMediaTimingFunctionEaseIn)
|
||||
itemNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.5)
|
||||
@@ -1051,7 +1053,7 @@ open class GridNode: GridNodeScroller, UIScrollViewDelegate {
|
||||
self.removeSectionNodeWithSection(wrappedSection, removeNode: true)
|
||||
}
|
||||
} else if let previousFrame = previousItemFrames[WrappedGridItemNode(node: sectionNode)] {
|
||||
sectionNode.layer.animatePosition(from: CGPoint(x: previousFrame.midX, y: previousFrame.midY + contentOffset.y), to: sectionNode.layer.position, duration: duration, timingFunction: timingFunction)
|
||||
sectionNode.layer.animatePosition(from: CGPoint(x: previousFrame.midX, y: previousFrame.midY + contentOffset.y), to: sectionNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction)
|
||||
} else {
|
||||
sectionNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, timingFunction: kCAMediaTimingFunctionEaseIn)
|
||||
}
|
||||
|
||||
@@ -2789,7 +2789,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
reverseAnimation = reverseBasicAnimation
|
||||
} else {
|
||||
let basicAnimation = CABasicAnimation(keyPath: "sublayerTransform")
|
||||
basicAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.33, 0.52, 0.25, 0.99)
|
||||
basicAnimation.timingFunction = ContainedViewLayoutTransitionCurve.slide.mediaTimingFunction
|
||||
basicAnimation.duration = (duration ?? 0.3) * UIView.animationDurationFactor()
|
||||
basicAnimation.fromValue = NSValue(caTransform3D: CATransform3DMakeTranslation(0.0, -offset, 0.0))
|
||||
basicAnimation.toValue = NSValue(caTransform3D: CATransform3DIdentity)
|
||||
@@ -2797,7 +2797,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
basicAnimation.isAdditive = true
|
||||
|
||||
let reverseBasicAnimation = CABasicAnimation(keyPath: "sublayerTransform")
|
||||
reverseBasicAnimation.timingFunction = CAMediaTimingFunction(controlPoints: 0.33, 0.52, 0.25, 0.99)
|
||||
reverseBasicAnimation.timingFunction = ContainedViewLayoutTransitionCurve.slide.mediaTimingFunction
|
||||
reverseBasicAnimation.duration = (duration ?? 0.3) * UIView.animationDurationFactor()
|
||||
reverseBasicAnimation.fromValue = NSValue(caTransform3D: CATransform3DMakeTranslation(0.0, offset, 0.0))
|
||||
reverseBasicAnimation.toValue = NSValue(caTransform3D: CATransform3DIdentity)
|
||||
@@ -2966,7 +2966,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: CAMediaTimingFunction(controlPoints: p1, p2, p3, p4))
|
||||
case .easeInOut:
|
||||
if transition.1 {
|
||||
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.52, 0.25, 0.99))
|
||||
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: ContainedViewLayoutTransitionCurve.slide.mediaTimingFunction)
|
||||
} else {
|
||||
headerNode.layer.animateBoundsOriginYAdditive(from: offset, to: 0.0, duration: duration, mediaTimingFunction: CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut))
|
||||
}
|
||||
|
||||
@@ -78,8 +78,7 @@ class NavigationBarSearchContentNode: NavigationBarContentNode {
|
||||
if abs(newProgress - self.expansionProgress) > 0.0001 && (progress <= 1.0 || self.expansionProgress != 1.0) {
|
||||
self.expansionProgress = newProgress
|
||||
|
||||
let animationCurve: ContainedViewLayoutTransitionCurve = .custom(0.33, 0.52, 0.25, 0.99)
|
||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: animationCurve) : .immediate
|
||||
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.3, curve: ContainedViewLayoutTransitionCurve.slide) : .immediate
|
||||
if let validLayout = self.validLayout, animated {
|
||||
self.updatePlaceholder(self.expansionProgress, size: validLayout.0, leftInset: validLayout.1, rightInset: validLayout.2, transition: transition)
|
||||
}
|
||||
@@ -130,7 +129,7 @@ class NavigationBarSearchContentNode: NavigationBarContentNode {
|
||||
let overscrollProgress = max(0.0, max(0.0, self.expansionProgress - 1.0 + fraction) / fraction - visibleProgress)
|
||||
|
||||
let searchBarNodeLayout = self.placeholderNode.asyncLayout()
|
||||
let (searchBarHeight, searchBarApply) = searchBarNodeLayout(NSAttributedString(string: self.placeholder, font: searchBarFont, textColor: self.theme?.rootController.activeNavigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93)), CGSize(width: baseWidth, height: fieldHeight), visibleProgress, self.theme?.rootController.activeNavigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93), self.theme?.rootController.activeNavigationSearchBar.inputFillColor ?? .clear, self.theme?.rootController.navigationBar.backgroundColor ?? .clear, transition)
|
||||
let (searchBarHeight, searchBarApply) = searchBarNodeLayout(NSAttributedString(string: self.placeholder, font: searchBarFont, textColor: self.theme?.rootController.navigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93)), CGSize(width: baseWidth, height: fieldHeight), visibleProgress, self.theme?.rootController.navigationSearchBar.inputPlaceholderTextColor ?? UIColor(rgb: 0x8e8e93), self.theme?.rootController.navigationSearchBar.inputFillColor ?? .clear, self.theme?.rootController.navigationBar.backgroundColor ?? .clear, transition)
|
||||
searchBarApply()
|
||||
|
||||
let searchBarFrame = CGRect(origin: CGPoint(x: padding + leftInset, y: 8.0 + overscrollProgress * fieldHeight), size: CGSize(width: baseWidth, height: fieldHeight))
|
||||
|
||||
@@ -32,6 +32,10 @@ final class ThemeGridController: ViewController {
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
override var navigationBarRequiresEntireLayoutUpdate: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
init(context: AccountContext) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
@@ -718,7 +718,10 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
let targetProgress: CGFloat
|
||||
|
||||
let duration: Double = 0.3
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: .easeInOut)
|
||||
let curve = ContainedViewLayoutTransitionCurve.slide
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: curve)
|
||||
let timingFunction = curve.timingFunction
|
||||
let mediaTimingFunction = curve.mediaTimingFunction
|
||||
|
||||
if searchNode.expansionProgress < 0.6 {
|
||||
scrollToItem = GridNodeScrollToItem(index: 0, position: .top(navigationBarSearchContentHeight), transition: transition, directionHint: .up, adjustForSection: true, adjustForTopInset: true)
|
||||
@@ -735,11 +738,11 @@ final class ThemeGridControllerNode: ASDisplayNode {
|
||||
|
||||
let offset = (self.gridNode.scrollView.contentOffset.y + self.gridNode.scrollView.contentInset.top) - previousOffset
|
||||
|
||||
self.backgroundNode.layer.animatePosition(from: self.backgroundNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.backgroundNode.layer.position, duration: duration)
|
||||
self.separatorNode.layer.animatePosition(from: self.separatorNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.separatorNode.layer.position, duration: duration)
|
||||
self.colorItemNode.layer.animatePosition(from: self.colorItemNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.colorItemNode.layer.position, duration: duration)
|
||||
self.galleryItemNode.layer.animatePosition(from: self.galleryItemNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.galleryItemNode.layer.position, duration: duration)
|
||||
self.descriptionItemNode.layer.animatePosition(from: self.descriptionItemNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.descriptionItemNode.layer.position, duration: duration)
|
||||
self.backgroundNode.layer.animatePosition(from: self.backgroundNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.backgroundNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction)
|
||||
self.separatorNode.layer.animatePosition(from: self.separatorNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.separatorNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction)
|
||||
self.colorItemNode.layer.animatePosition(from: self.colorItemNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.colorItemNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction)
|
||||
self.galleryItemNode.layer.animatePosition(from: self.galleryItemNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.galleryItemNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction)
|
||||
self.descriptionItemNode.layer.animatePosition(from: self.descriptionItemNode.layer.position.offsetBy(dx: 0.0, dy: offset), to: self.descriptionItemNode.layer.position, duration: duration, timingFunction: timingFunction, mediaTimingFunction: mediaTimingFunction)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user