Нажмите панель вкладок, чтобы перейти к верхней части UITableViewController
Прикосновение к значку панели вкладок для текущего контроллера навигации уже возвращает пользователя в корневой режим, но если они прокручиваются вниз, если они снова коснутся его, я хочу, чтобы он прокручивался вверх (тот же эффект, что и при нажатии на строку состояния). Как мне это сделать?
Хорошим примером является подача Instagram, прокрутка вниз, а затем нажмите значок дома на панели вкладок, чтобы прокрутить назад.
Прокрутка назад к вершине проста, но подключение ее к контроллеру панели вкладок - вот то, что я застрял.
Ответы
Ответ 1
Реализация UITabBarControllerDelegate
метода tabBarController: didSelectViewController: получать уведомления, когда пользователь выбирает вкладку. Этот метод также вызывается, когда одна и та же кнопка табуляции снова используется, даже если эта вкладка уже выбрана.
Хорошим местом для реализации этого делегата, вероятно, будет ваш AppDelegate
. Или объект, который логически "владеет" контроллером панели вкладок.
Я бы объявил и реализую метод, который можно вызвать на контроллерах вашего вида, чтобы прокрутить представление коллекции.
- (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;
}
Ответ 2
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". Я включил функцию печати, чтобы вы могли дважды проверить.
Ответ 3
Я использовал эту иерархию View.
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)
}
Затем в моем пользовательском классе 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()
}
}
}
Результат идентичен Feed Instagram и Twitter :)
Ответ 4
Вы можете использовать shouldSelect
а не didSelect
, что бы опустить необходимость в том, чтобы внешняя переменная отслеживала предыдущий контроллер представления.
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
if ([viewController isEqual:self] && [tabBarController.selectedViewController isEqual:viewController]) {
// Do custom stuff here
}
return YES;
}
Ответ 5
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. Он использует вспомогательную функцию для рекурсивных вызовов, и она автоматически прокручивается вверх по вызову. Протестировано на UICollectionViewController, встроенном в UINavigationController, встроенном в UITabBarController
Ответ 6
Подход 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 уже изменена. 2. Нам нужно обработать событие перед сменой контроллера, чтобы иметь информацию о диспетчерах навигации, касающуюся действия прокрутки (или нет).
Объяснение :: Мы хотим прокрутить вверх, если текущее представление на самом деле является контроллером представления List/Table. Если продвижение навигации продвинулось, и мы коснемся той же панели вкладок, желаемое действие будет состоять только в том, чтобы просто нажать один шаг (функция по умолчанию), а не прокручивать вверх. Если навигация не имеет никакого значения, мы все еще находимся в контроллере таблицы/списка, и только тогда мы хотим прокручивать вверх, когда снова нажимаем. (То же самое происходит с Facebook, когда вы нажимаете "Feed" из пользовательского профиля. Он возвращается только к фиду без прокрутки вверх.
Ответ 7
У меня есть представление коллекции, встроенное в контроллер навигации, в 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;
}
Ответ 8
Я реализовал подключаемый модуль 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 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, без использования закрытий.
Ответ 9
Я обнаружил, что метод 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)
}