165 lines
4.9 KiB
Swift
165 lines
4.9 KiB
Swift
//
|
|
// TimelineGapCollectionViewCell.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 11/16/22.
|
|
// Copyright © 2022 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
class TimelineGapCollectionViewCell: UICollectionViewCell {
|
|
|
|
private(set) var direction = TimelineGapDirection.above
|
|
|
|
private let indicator = UIActivityIndicatorView(style: .medium)
|
|
private let chevronView = AnimatingChevronView()
|
|
|
|
override var isHighlighted: Bool {
|
|
didSet {
|
|
backgroundColor = isHighlighted ? .systemFill : .systemGroupedBackground
|
|
}
|
|
}
|
|
|
|
var showsIndicator: Bool = false {
|
|
didSet {
|
|
if showsIndicator {
|
|
indicator.isHidden = false
|
|
indicator.startAnimating()
|
|
} else {
|
|
indicator.isHidden = true
|
|
indicator.stopAnimating()
|
|
}
|
|
}
|
|
}
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
|
|
backgroundColor = .systemGroupedBackground
|
|
|
|
indicator.isHidden = true
|
|
indicator.color = .tintColor
|
|
|
|
let label = UILabel()
|
|
label.text = "Load more"
|
|
label.font = .preferredFont(forTextStyle: .headline)
|
|
label.textColor = .tintColor
|
|
|
|
chevronView.update(direction: .above)
|
|
|
|
let stack = UIStackView(arrangedSubviews: [
|
|
label,
|
|
chevronView,
|
|
])
|
|
stack.axis = .horizontal
|
|
stack.spacing = 8
|
|
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
contentView.addSubview(stack)
|
|
|
|
indicator.translatesAutoresizingMaskIntoConstraints = false
|
|
contentView.addSubview(indicator)
|
|
|
|
NSLayoutConstraint.activate([
|
|
stack.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
|
|
stack.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
|
indicator.trailingAnchor.constraint(equalTo: stack.leadingAnchor, constant: -8),
|
|
indicator.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
|
contentView.heightAnchor.constraint(equalToConstant: 44),
|
|
])
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func didMoveToSuperview() {
|
|
super.didMoveToSuperview()
|
|
update()
|
|
}
|
|
|
|
func update() {
|
|
guard let superview = superview as? UICollectionView else {
|
|
return
|
|
}
|
|
let yInParent = frame.minY - superview.contentOffset.y
|
|
let centerInParent = yInParent + bounds.height / 2
|
|
let centerOfParent = superview.frame.height / 2
|
|
|
|
let newDirection: TimelineGapDirection
|
|
if centerInParent > centerOfParent {
|
|
newDirection = .above
|
|
} else {
|
|
newDirection = .below
|
|
}
|
|
|
|
if newDirection != direction {
|
|
direction = newDirection
|
|
chevronView.update(direction: newDirection)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private class AnimatingChevronView: UIView {
|
|
|
|
override class var layerClass: AnyClass { CAShapeLayer.self }
|
|
var shapeLayer: CAShapeLayer { layer as! CAShapeLayer }
|
|
|
|
override var intrinsicContentSize: CGSize { CGSize(width: 20, height: 25) }
|
|
|
|
var animator: UIViewPropertyAnimator?
|
|
|
|
let upPath: CGPath = {
|
|
let path = UIBezierPath()
|
|
let width: CGFloat = 20
|
|
let height: CGFloat = 25
|
|
path.move(to: CGPoint(x: 0, y: height / 2))
|
|
path.addLine(to: CGPoint(x: width / 2, y: height / 5))
|
|
path.addLine(to: CGPoint(x: width, y: height / 2))
|
|
return path.cgPath
|
|
}()
|
|
let downPath: CGPath = {
|
|
let path = UIBezierPath()
|
|
let width: CGFloat = 20
|
|
let height: CGFloat = 25
|
|
path.move(to: CGPoint(x: 0, y: height / 2))
|
|
path.addLine(to: CGPoint(x: width / 2, y: 4 * height / 5))
|
|
path.addLine(to: CGPoint(x: width, y: height / 2))
|
|
return path.cgPath
|
|
}()
|
|
|
|
init() {
|
|
super.init(frame: .zero)
|
|
shapeLayer.fillColor = nil
|
|
shapeLayer.strokeColor = tintColor.cgColor
|
|
shapeLayer.lineCap = .round
|
|
shapeLayer.lineJoin = .round
|
|
shapeLayer.lineWidth = 3
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func tintColorDidChange() {
|
|
super.tintColorDidChange()
|
|
shapeLayer.strokeColor = tintColor.cgColor
|
|
}
|
|
|
|
func update(direction: TimelineGapDirection) {
|
|
if animator?.isRunning == true {
|
|
animator!.stopAnimation(true)
|
|
}
|
|
animator = UIViewPropertyAnimator(duration: 0.1, curve: .linear) {
|
|
if direction == .below {
|
|
self.shapeLayer.path = self.upPath
|
|
} else {
|
|
self.shapeLayer.path = self.downPath
|
|
}
|
|
}
|
|
animator!.startAnimation()
|
|
}
|
|
|
|
}
|