点击UITabBarItem滚动到顶部像Instagram和Twitter

时间:2017-04-14 13:12:52

标签: ios swift uitableview scrolltop uitabbaritem

我遇到了使这项功能有效的问题,我希望得到一些帮助。

我的层次结构是TabBarController - > Navigation Controller - > TableViewController

我想要的是,如果你在当前标签上并向下滚动,你将能够点击当前视图的UITabBarItem,你将滚动回到顶部,例如Instagram和Twitter。 / p>

我在这里尝试了很多东西:

Older Question

但遗憾的是没有答案为我做了这份工作。

我真的很感激有关这种方式的任何帮助, 提前谢谢!

这是我的TableView`controller的代码:

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代码(代码不起作用):

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
    }

}

5 个答案:

答案 0 :(得分:3)

在这里,这应该有效:

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
}

然后......

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

此功能还有一点额外功能,如果UIViewController已经位于顶部,它将弹出到前一个控制器

答案 1 :(得分:2)

在TabViewController中尝试此代码:

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

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

    } 
}

此外,您的TabViewController应该继承自UITabBarControllerDelegate

enter image description here

最终代码:

import UIKit

class tabViewController: UITabBarController, UITabBarControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
        // Do any additional setup after loading the view.
    }

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

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

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

        } 
    }


}

请记得更改tabBarIndex并在viewDidLoad中设置 self.delegate = self

答案 2 :(得分:1)

您只需使用以下代码创建文件TabBarViewController

class TabBarController: UITabBarController, UITabBarControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

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

然后在您的故事板中,设置自定义类TabBarController,如下所示:

enter image description here

答案 3 :(得分:0)

我只是必须实现这一点并且惊讶地发现它并不像我想象的那么容易。这就是我实现它的方式:

关于我的应用程序设置的一些注意事项

  • MainTabBarcontroller是我们的根视图控制器,我们通常从UISharedApplication访问它。
  • 我们的导航结构是MainTabBarController - >导航控制器 - >可见视图控制器

我发现实现此问题的主要问题是,我只想滚动到顶部,如果我已经在标签栏的第一个屏幕上,但是一旦您尝试从tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController)进行此检查,您就可以了。已经丢失了对您点击之前可见的视图控制器的引用。由于可见视图控制器可能位于另一个选项卡中,因此在切换选项卡时比较视图控制器也是一个问题。

所以我在我的TabBar类中创建了一个属性来保存对previousTopVC的引用,并创建了一个协议来帮助我从当前可见的VC设置这个属性。

protocol TopScreenFindable {
    func setVisibleViewController()
}

extension TopScreenFindable where Self: UIViewController {
    func setVisibleViewController() {
        guard let tabController = UIApplication.shared.keyWindow?.rootViewController as? MainTabBarController else { return }
        tabController.previousTopVC = self
    }
}

然后我调用了这个协议和setVisibleViewController来自View Controllers' viewDidAppear现在有任何屏幕出现时对前一个可见VC的引用。

我为MainTabBarController类创建了一个委托

protocol MainTabBarDelegate: class {
    func firstScreenShouldScrollToTop()
}

然后从UITabBarControllerDelegate方法

调用它
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        guard let navController = viewController as? UINavigationController, let firstVC = navController.viewControllers.first, let previousVC = previousTopController else { return }
        if firstVC == previousVC {
            mainTabBarDelegate?.firstScreenShouldScrollToTop()
        }
    } 

在第一个视图控制器中符合MainTabBarDelegate

extension FirstViewController: MainTabBarDelegate {
    func firstScreenShouldScrollToTop() {
        collectionView.setContentOffset(CGPoint(x: 0, y: collectionView.contentInset.top), animated: true)
    }

我在FirstViewController的viewDidAppear上设置tabBar.mainTabBarDelegate = self,以便每次屏幕显示时都设置委托。如果我在viewDidLoad上这样做,它在切换标签时就不起作用了。

我对自己的做法并不感兴趣。

  • 在我的视图控制器中引用了标签栏,以便它可以将自己设置为其委托。
  • 使应用中的每个相关屏幕都符合TopScreenFindable协议

答案 4 :(得分:0)

很好的例子!我也尝试了一些事情,我想代表我们的这小段代码就是我们所需要的:

extension AppDelegate: UITabBarControllerDelegate {

func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
    // Scroll to top when corresponding view controller was already selected and no other viewcontrollers are pushed
    guard tabBarController.viewControllers?[tabBarController.selectedIndex] == viewController,
          let navigationController = viewController as? UINavigationController, navigationController.viewControllers.count == 1,
          let topViewController = navigationController.viewControllers.first,
          let scrollView = topViewController.view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView else {
        return true
    }

    scrollView.scrollRectToVisible(CGRect(origin: .zero, size: CGSize(width: 1, height: 1)), animated: true)
    return false
}

}