点击当前导航控制器的标签栏图标已经将用户返回到根视图,但是如果它们向下滚动,如果再次点击它我希望它滚动到顶部(与点击状态相同的效果酒吧)。我该怎么做?
一个很好的例子是Instagram的Feed,向下滚动然后点击标签栏中的主页图标以滚动回到顶部。
滚动到顶部很容易,但将它连接到标签栏控制器是我所坚持的。
答案 0 :(得分:34)
实施UITabBarControllerDelegate
方法tabBarController:didSelectViewController:
,以便在用户选择标签时收到通知。当再次点击相同的选项卡按钮时,也会调用此方法,即使该选项卡已被选中。
实施此delegate
的好地方可能是您的AppDelegate
。或者逻辑上拥有的对象"标签栏控制器。
我会声明并实现一个方法,可以在视图控制器上调用以滚动UICollectionView
。
- (void)tabBarController:(UITabBarController *)tabBarController
didSelectViewController:(UIViewController *)viewController
{
static UIViewController *previousController = nil;
if (previousController == viewController) {
// the same tab was tapped a second time
if ([viewController respondsToSelector:@selector(scrollToTop)]) {
[viewController scrollToTop];
}
}
previousController = viewController;
}
答案 1 :(得分:20)
SWIFT 3
这里有..
首先在类中实现UITabBarControllerDelegate
并确保在viewDidLoad中设置委托
class DesignStoryStreamVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UITabBarControllerDelegate {
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarController?.delegate = self
collectionView.delegate = self
collectionView.dataSource = self
}
}
接下来,将此委托函数放在您的类中的某个位置。
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let tabBarIndex = tabBarController.selectedIndex
print(tabBarIndex)
if tabBarIndex == 0 {
self.collectionView.setContentOffset(CGPoint.zero, animated: true)
}
}
确保在“if”语句中选择正确的索引。我包含了打印功能,因此您可以仔细检查。
答案 2 :(得分:5)
我正在使用此View层次结构。
rbindlist
我在[{1}}
中引用了UITabBarController > UINavigationController > UIViewController
UITabBarController
然后我创建了一个在正确的时间调用的UIViewController
,以及一个允许滚动到顶部的方法
tabBarControllerRef = self.tabBarController as! CustomTabBarClass
tabBarControllerRef!.navigationControllerRef = self.navigationController as! CustomNavigationBarClass
tabBarControllerRef!.viewControllerRef = self
然后在我的Bool
自定义类我调用了这个
var canScrollToTop:Bool = true
// Called when the view becomes available
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
canScrollToTop = true
}
// Called when the view becomes unavailable
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
canScrollToTop = false
}
// Scrolls to top nicely
func scrollToTop() {
self.collectionView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
结果与Instagram和Twitter的Feed相同:)
答案 3 :(得分:4)
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: self.view)
}
}
这是我在Swift 3中的答案。它使用辅助函数进行递归调用,并在调用时自动滚动到顶部。在嵌入UITabBarController中嵌入的UINavigationController的UICollectionViewController上测试
答案 4 :(得分:4)
Swift 3方法::
//MARK: Properties
var previousController: UIViewController?
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if self.previousController == viewController || self.previousController == nil {
// the same tab was tapped a second time
let nav = viewController as! UINavigationController
// if in first level of navigation (table view) then and only then scroll to top
if nav.viewControllers.count < 2 {
let tableCont = nav.topViewController as! UITableViewController
tableCont.tableView.setContentOffset(CGPoint(x: 0.0, y: -tableCont.tableView.contentInset.top), animated: true)
}
}
self.previousController = viewController;
return true
}
这里有几点注意事项:: “shouldSelect”而不是“didSelect”,因为后者是在转换后发生的,这意味着viewController本地var已经改变。 2.我们需要在更改控制器之前处理事件,以便获得有关滚动(或不)动作的导航视图控制器的信息。
说明::如果当前视图实际上是List / Table视图控制器,我们想要滚动到顶部。如果导航已经提前并且我们点击相同的标签栏,则所需的操作将只是弹出一步(默认功能)而不是滚动到顶部。如果导航没有提前意味着我们仍然在表/列表控制器然后只有我们想要再次点击时滚动到顶部。 (Facebook从用户的个人资料中点击“Feed”时也会这样做。它只会返回到Feed而不会滚动到顶部。
答案 5 :(得分:3)
您可以使用shouldSelect
而非didSelect
,这将省略外部变量以跟踪前一个视图控制器的需要。
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
if ([viewController isEqual:self] && [tabBarController.selectedViewController isEqual:viewController]) {
// Do custom stuff here
}
return YES;
}
答案 6 :(得分:1)
我有一个嵌入在导航控制器中的集合视图,在Swift中可行。
var previousController: UIViewController?
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
if previousController == viewController {
if let navVC = viewController as? UINavigationController, vc = navVC.viewControllers.first as? UICollectionViewController {
vc.collectionView?.setContentOffset(CGPointZero, animated: true)
}
}
previousController = viewController;
}
答案 7 :(得分:1)
我已经实现了一个插件&amp;玩你可以在你的项目中自由重用的UITabBarController。要启用滚动到顶部功能,您只需使用子类,不需要其他任何内容。
也应与Storyboard一起开箱即用。
代码:
/// A UITabBarController subclass that allows "scroll-to-top" gestures via tapping
/// tab bar items. You enable the functionality by simply subclassing.
class ScrollToTopTabBarController: UITabBarController, UITabBarControllerDelegate {
/// Determines whether the scrolling capability's enabled.
var scrollEnabled: Bool = true
private var previousIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
/*
Always call "super" if you're overriding this method in your subclass.
*/
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
guard scrollEnabled else {
return
}
guard let index = viewControllers?.indexOf(viewController) else {
return
}
if index == previousIndex {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { [weak self] () in
guard let scrollView = self?.iterateThroughSubviews(self?.view) else {
return
}
dispatch_async(dispatch_get_main_queue(), {
scrollView.setContentOffset(CGPointZero, animated: true)
})
})
}
previousIndex = index
}
/*
Iterates through the view hierarchy in an attempt to locate a UIScrollView with "scrollsToTop" enabled.
Since the functionality relies on "scrollsToTop", it plugs easily into existing architectures - you can
control the behaviour by modifying "scrollsToTop" on your UIScrollViews.
*/
private func iterateThroughSubviews(parentView: UIView?) -> UIScrollView? {
guard let view = parentView else {
return nil
}
for subview in view.subviews {
if let scrollView = subview as? UIScrollView where scrollView.scrollsToTop == true {
return scrollView
}
if let scrollView = iterateThroughSubviews(subview) {
return scrollView
}
}
return nil
}
}
编辑(09.08.2016):
尝试使用默认的Release配置(归档)进行编译后,编译器不允许创建在递归函数中捕获的大量闭包,因此无法编译。更改了代码以返回第一个找到的UIScrollView,其中“scrollsToTop”设置为true而不使用闭包。
答案 8 :(得分:1)
在此实现中,您不需要静态变量和以前的视图控制器状态
如果您在UINavigationController中使用UITableViewController,则可以实现协议和功能:
protocol ScrollableToTop {
func scrollToTop()
}
extension UIScrollView {
func scrollToTop(_ animated: Bool) {
var topContentOffset: CGPoint
if #available(iOS 11.0, *) {
topContentOffset = CGPoint(x: -safeAreaInsets.left, y: -safeAreaInsets.top)
} else {
topContentOffset = CGPoint(x: -contentInset.left, y: -contentInset.top)
}
setContentOffset(topContentOffset, animated: animated)
}
}
然后在您的UITableViewController中:
class MyTableViewController: UITableViewController: ScrollableToTop {
func scrollToTop() {
if isViewLoaded {
tableView.scrollToTop(true)
}
}
}
然后在UITabBarControllerDelegate中:
extension MyTabBarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
guard tabBarController.selectedViewController === viewController else { return true }
guard let navigationController = viewController as? UINavigationController else {
assertionFailure()
return true
}
guard
navigationController.viewControllers.count <= 1,
let destinationViewController = navigationController.viewControllers.first as? ScrollableToTop
else {
return true
}
destinationViewController.scrollToTop()
return false
}
}
答案 9 :(得分:0)
我发现scrollRectToVisible
方法比setContentOffset
效果更好。
<强>夫特:强>
从代理中点击标签栏上的点击后,如下所示:
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
if (viewController.isKindOfClass(SomeControllerClass) && tabBarController.selectedIndex == 0)
{
viewController.scrollToTop()
}
}
现在控制器内的scrollToTop
功能:
func scrollToTop()
{
self.tableView.scrollRectToVisible(CGRectMake(0,0,CGRectGetWidth(self.tableView.frame), CGRectGetHeight(self.tableView.frame)), animated: true)
}
答案 10 :(得分:0)
快捷键5::无需在UITabBarController
中存储属性。
在 MyTabBarController.swift 中,实现tabBarController(_:shouldSelect)
以检测用户何时重新选择选项卡栏项:
protocol TabBarReselectHandling {
func handleReselect()
}
class MyTabBarController: UITabBarController, UITabBarControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
}
func tabBarController(
_ tabBarController: UITabBarController,
shouldSelect viewController: UIViewController
) -> Bool {
if tabBarController.selectedViewController === viewController,
let handler = viewController as? TabBarReselectHandling {
// NOTE: viewController in line above might be a UINavigationController,
// in which case you need to access its contents
handler.handleReselect()
}
return true
}
}
在 MyTableViewController.swift 中,通过将表视图滚动到顶部来处理重新选择:
class MyTableViewController: UITableViewController, TabBarReselectHandling {
func handleReselect() {
tableView?.setContentOffset(.zero, animated: true)
}
}
现在,您只需实施TabBarReselectHandling
就可以轻松地将其扩展到其他标签。
答案 11 :(得分:0)
我尝试了@jsanabria提供的解决方案。这在固定的tableview上效果很好,但是对我的无限滚动tableview无效。在加载新的滚动数据之后,它仅在表视图的一半位置出现。
Swift 5.0 +
self.tableView.scrollToRow(at: IndexPath.init(row: 0, section: 0), at: UITableView.ScrollPosition(rawValue: 0)!, animated: true)
答案 12 :(得分:0)
weak static var previousController: UIViewController?
MainTabBarViewController.previousController = viewControllers?[0]
extension MainTabBarViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if MainTabBarViewController.previousController == viewController {
/// here comes your code
}
MainTabBarViewController.previousController = viewController
}
}