Fixed GridNode scrolling animations

This commit is contained in:
Ilya Laktyushin
2019-07-10 03:45:46 +02:00
parent 238ed806bc
commit 5675e81f8b
6 changed files with 31 additions and 19 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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