Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tap UITabBarItem To Scroll To Top Like Instagram and Twitter

I'm having issues making this feature work and i would like to get some help.

My hierarchy is TabBarController -> Navigation Controller -> TableViewController

What i want is if you are on current tab and you scrolled down you will be able to tap the current View's UITabBarItem and you will be scrolled back to the top,Like Instagram and Twitter does for example.

I have tried many things right here :

Older Question

but sadly non of the answers did the job for me.

I would really appreciate any help about this manner , Thank you in advance!

Here is my TableView`controller's Code :

import UIKit

class BarsViewController: UITableViewController,UISearchResultsUpdating,UISearchBarDelegate,UISearchDisplayDelegate,UITabBarControllerDelegate{

//TableView Data & non related stuff....

override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        self.searchController.searchBar.resignFirstResponder()
        self.searchController.searchBar.endEditing(true)
    }

 func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        let tabBarIndex = tabBarController.selectedIndex
        if tabBarIndex == 0 {

            let indexPath = IndexPath(row: 0, section: 0)
            let navigVC = viewController as? UINavigationController
            let finalVC = navigVC?.viewControllers[0] as? BarsViewController
            finalVC?.tableView.scrollToRow(at: indexPath as IndexPath, at: .top, animated: true)

        } 
    }

}

TabBarController.Swift Code ( Code doesn't work ) :

import UIKit

class TabBarController: UITabBarController,UITabBarControllerDelegate {

       override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    self.delegate = self
}


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
        guard let viewControllers = viewControllers else { return false }
        if viewController == viewControllers[selectedIndex] {
            if let nav = viewController as? UINavigationController {
                guard let topController = nav.viewControllers.last else { return true }
                if !topController.isScrolledToTop {
                    topController.scrollToTop()
                    return false
                } else {
                    nav.popViewController(animated: true)
                }
                return true
            }
        }

        return true
    }

}
extension UIViewController {
    func scrollToTop() {
        func scrollToTop(view: UIView?) {
            guard let view = view else { return }

            switch view {
            case let scrollView as UIScrollView:
                if scrollView.scrollsToTop == true {
                    scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: true)
                    return
                }
            default:
                break
            }

            for subView in view.subviews {
                scrollToTop(view: subView)
            }
        }

        scrollToTop(view: view)
    }

    var isScrolledToTop: Bool {
        for subView in view.subviews {
            if let scrollView = subView as? UIScrollView {
                return (scrollView.contentOffset.y == 0)
            }
        }
        return true
    }

}
like image 297
Newbie Questions Avatar asked Dec 23 '22 18:12

Newbie Questions


1 Answers

Here you go, this should work:

func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
    guard let viewControllers = viewControllers else { return false }
    if viewController == viewControllers[selectedIndex] {
        if let nav = viewController as? ZBNavigationController {
            guard let topController = nav.viewControllers.last else { return true }
            if !topController.isScrolledToTop {
                topController.scrollToTop()
                return false
            } else {
                nav.popViewController(animated: true)
            }
            return true
        }
    }

    return true
}

and then...

extension UIViewController {
    func scrollToTop() {
        func scrollToTop(view: UIView?) {
            guard let view = view else { return }

            switch view {
            case let scrollView as UIScrollView:
                if scrollView.scrollsToTop == true {
                    scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.contentInset.top), animated: true)
                    return
                }
            default:
                break
            }

            for subView in view.subviews {
                scrollToTop(view: subView)
            }
        }

        scrollToTop(view: view)
    }

    // Changed this

    var isScrolledToTop: Bool {
        if self is UITableViewController {
            return (self as! UITableViewController).tableView.contentOffset.y == 0
        }
        for subView in view.subviews {
            if let scrollView = subView as? UIScrollView {
                return (scrollView.contentOffset.y == 0)
            }
        }
        return true
    }
}

There's a bit extra in this function so that if the UIViewController is already at the top it will pop to the previous controller

like image 161
jackchmbrln Avatar answered Feb 23 '23 00:02

jackchmbrln