Sample Code
class ViewController: UIViewController {
let dataCount = 10 // this will be replaced by the datasource count.
var currentIndex: Int = 0 {
didSet {
print("aboveButton.isHidden: \(aboveButton.isHidden)")
if currentIndex == 0 {
UIView.animate(withDuration: 1, animations: {
self.aboveButton.alpha = 0
})
} else if currentIndex == dataCount - 1 {
UIView.animate(withDuration: 1, animations: {
self.belowButton.alpha = 0
})
}
else {
UIView.animate(withDuration: 0.5, animations: {
self.aboveButton.alpha = 1
self.belowButton.alpha = 1
})
}
}
}
let tableViewHeight: CGFloat = 300
let buttonSize: CGFloat = 50
lazy var aboveButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "chevron.up"), for: .normal)
button.imageView?.contentMode = .scaleAspectFit
button.contentHorizontalAlignment = .fill
button.contentVerticalAlignment = .fill
button.tintColor = .systemGray
button.addTarget(self, action: #selector(onAboveButtonTapped), for: .touchUpInside)
return button
}()
@objc private func onAboveButtonTapped() {
let index = currentIndex - 1
guard index >= 0 else { return }
let indexPath = IndexPath(row: index, section: 0)
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
currentIndex = index
}
lazy var belowButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "chevron.down"), for: .normal)
button.imageView?.contentMode = .scaleAspectFit
button.contentHorizontalAlignment = .fill
button.contentVerticalAlignment = .fill
button.tintColor = .systemGray
button.addTarget(self, action: #selector(onBelowButtonTapped), for: .touchUpInside)
return button
}()
@objc private func onBelowButtonTapped() {
let index = currentIndex + 1
guard index <= dataCount - 1 else { return }
let indexPath = IndexPath(row: index, section: 0)
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
currentIndex = index
}
lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.separatorColor = .clear
tableView.rowHeight = tableViewHeight
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(tableView)
view.addSubview(aboveButton)
view.addSubview(belowButton)
if currentIndex == 0 {
aboveButton.alpha = 0
}
if currentIndex == dataCount - 1 {
belowButton.alpha = 0
}
tableView.delegate = self
tableView.dataSource = self
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
layout()
}
private func layout() {
tableView.frame = CGRect(x: 0, y: view.bounds.height/2 - tableViewHeight/2, width: view.bounds.width, height: tableViewHeight)
aboveButton.frame = CGRect(x: view.bounds.width/2 - buttonSize/2, y: tableView.frame.minY + 10, width: buttonSize, height: buttonSize)
belowButton.frame = CGRect(x: view.bounds.width/2 - buttonSize/2, y: tableView.frame.minY + tableViewHeight - 10 - buttonSize, width: buttonSize, height: buttonSize)
}
}
extension ViewController: UITableViewDelegate {
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
dataCount
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "\(indexPath.row)"
cell.backgroundColor = .systemGray6
return cell
}
}
extension ViewController: UIScrollViewDelegate {
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var visibleRect = CGRect()
visibleRect.origin = tableView.contentOffset
visibleRect.size = tableView.bounds.size
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
guard let indexPath = tableView.indexPathForRow(at: visiblePoint) else { return }
currentIndex = indexPath.row
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView,
withVelocity velocity: CGPoint,
targetContentOffset: UnsafeMutablePointer<CGPoint>)
{
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.scrollViewDidEndDecelerating(scrollView)
}
}
}