web-dev-qa-db-ja.com

タブバーをタップして、UITableViewControllerの上部にスクロールします

現在のNavigation ControllerのTab Barアイコンをタップすると、ユーザーはすでにルートビューに戻りますが、スクロールダウンした場合、もう一度タップすると一番上にスクロールします(ステータスバーをタップしたのと同じ効果)。どうすればいいですか?

良い例は、Instagramのフィードです。下にスクロールしてから、タブバーのホームアイコンをタップして、トップに戻ります。

一番上に戻るスクロールは簡単ですが、Tab Bar Controllerに接続することは私が行き詰まっていることです。

29

UITabBarControllerDelegateメソッドを実装するtabBarController:didSelectViewController:ユーザーがタブを選択したときに通知されます。このメソッドは、同じタブが既に選択されている場合でも、同じタブボタンが再度タップされたときにも呼び出されます。

このdelegateを実装する適切な場所は、おそらくAppDelegateです。または、Tab Bar Controllerを論理的に「所有する」オブジェクト。

View Controllerで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;
}
35
DrummerB

スイフト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」ステートメントで正しいインデックスを選択してください。印刷機能を追加したので、再確認できます。

20
jsanabria

このビュー階層を使用していました。

UITabBarController > UINavigationController > UIViewController


UITabBarControllerUIViewControllerへの参照を得ました

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

次に、UITabBarControllerカスタムクラスでこれを呼び出しました

func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {

    // Allows scrolling to top on second tab bar click
    if (viewController.isKindOfClass(CustomNavigationBarClass) && tabBarController.selectedIndex == 0) {
        if (viewControllerRef!.canScrollToTop) {
            viewControllerRef!.scrollToTop()
        }
    }
}

結果はInstagramとTwitterのフィードと同じです:)

5
Michael

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
}

ここにいくつかの注意事項があります。「didSelect」の代わりに「shouldSelect」。後者はviewController local varが既に変更されていることを意味するため、後者が移行後に行われるためです。 2.スクロール(またはそうでない)アクションに関するナビゲーションのView Controllerの情報を取得するために、コントローラーを変更する前にイベントを処理する必要があります。

説明::現在のビューが実際にリスト/テーブルビューコントローラーである場合、上にスクロールします。ナビゲーションが進んで同じタブバーをタップした場合、望ましいアクションは1ステップだけをポップすること(デフォルトの機能)であり、一番上までスクロールしないことです。ナビゲーションが高度な意味を持たない場合、まだテーブル/リストコントローラーにいますそのときのみもう一度タップしたときに上にスクロールします。 (Facebookはユーザーのプロフィールから「フィード」をタップするときに同じことを行います。トップにスクロールせずにフィードに戻るだけです。

4
Jimi
 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。

4

shouldSelectではなくdidSelectを使用できます。これにより、以前のView Controllerを追跡するための外部変数が不要になります。

- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
    if ([viewController isEqual:self] && [tabBarController.selectedViewController isEqual:viewController]) {
        // Do custom stuff here
    }
    return YES;
}
3
James Kuang

この実装では、静的変数は不要および以前のView Controllerの状態

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

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;
}
1
Niklas

プロジェクトで自由に再利用できるプラグアンドプレイUITabBarControllerを実装しました。最上部へのスクロール機能を有効にするには、サブクラスのみを使用する必要があります。

ストーリーボードでもすぐに使えるはずです。

コード:

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

編集(2016年8月9日):

デフォルトのリリース構成(アーカイブ)でコンパイルしようとすると、コンパイラーは再帰関数でキャプチャーされた多数のクロージャーを作成する可能性を許可しないため、コンパイルされません。クロージャーを使用せずに「scrollsToTop」をtrueに設定して最初に見つかったUIScrollViewを返すようにコードを変更しました。

1
D6mi

scrollRectToVisibleメソッドはsetContentOffsetよりも優れていることがわかりました。

Swift:

デリゲートからタブバーをクリックすると、次のようになります。

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)
} 
0
Malloc