This is what I am doing, may not be great but only working solution so far. Happy to take improvements.
- Create TableViewController with contents larger than it's frame.
- On view will appear, Scroll to the end of table view.
- Set TableViewController's frame same as StatusBarFrame and add it to Window's root viewcontroller
- Inside scrollViewShouldScrollToTop handle ScrollToTop event and scroll to the end to get next events.
- On StatusBar frame change, Update TableViewController's frame.
This works works with SceneDelegate as well.
ScrollToTopViewController.swift
class ScrollToTopViewController: UITableViewController {
private let numberOfRow = 2
private let cellIdentifier = "empty"
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsSelection = false
tableView.separatorColor = .clear
tableView.backgroundColor = .clear
tableView.showsVerticalScrollIndicator = false
tableView.showsHorizontalScrollIndicator = false
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
view.autoresizingMask = []
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
scrollToBottom()
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return view.frame.size.height
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfRow
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) ?? UITableViewCell()
cell.backgroundColor = .clear
return cell
}
override func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.scrollToBottom()
}
print("HANDLE SCROLL TO TOP")
return true
}
private func scrollToBottom() {
tableView.scrollToRow(at: IndexPath(row: numberOfRow - 1, section: 0), at: .bottom, animated: false)
}
}
AppDelegate.swift or equivalent SceneDelegate functions.
private let scrollToTopVC = ScrollToTopViewController()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
NotificationCenter.default.addObserver(self,
selector: #selector(updateScrollToTopVC),
name: UIApplication.didChangeStatusBarFrameNotification,
object: nil)
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
updateScrollToTopVC()
}
@objc
private func updateScrollToTopVC() {
guard let viewController = window?.rootViewController else {
return
}
let statusBarFrame: CGRect
if #available(iOS 13.0, *) {
statusBarFrame = UIApplication.shared.keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? .zero
} else {
statusBarFrame = UIApplication.shared.statusBarFrame
}
scrollToTopVC.view.frame = statusBarFrame
viewController.addChild(scrollToTopVC)
viewController.view.addSubview(scrollToTopVC.view)
scrollToTopVC.didMove(toParent: viewController)
}