ios - los - Toque la barra de pestañas para desplazarse hasta la parte superior de UITableViewController
los safari de este iphone se cargan y asocian con los de icloud (10)
En esta implementación no necesita variable estática y estado de controlador de vista anterior
Si su UITableViewController en UINavigationController puede implementar el protocolo y la función:
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)
}
}
Luego en tu UITableViewController:
class MyTableViewController: UITableViewController: ScrollableToTop {
func scrollToTop() {
if isViewLoaded {
tableView.scrollToTop(true)
}
}
}
Luego en 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
}
}
Al tocar el icono de la barra de pestañas del controlador de navegación actual, el usuario regresa a la vista raíz, pero si se desplazan hacia abajo, si lo tocan nuevamente, quiero que se desplace hacia la parte superior (el mismo efecto que al tocar la barra de estado). ¿Cómo haría esto?
Un buen ejemplo es la información de Instagram, desplácese hacia abajo y luego toque el ícono de inicio en la barra de pestañas para regresar a la parte superior.
El desplazamiento de vuelta a la parte superior es fácil, pero conectarlo al controlador de la barra de pestañas es lo que me atasca.
Encontré que el método scrollRectToVisible
funciona mejor que setContentOffset
.
Rápido:
Después de capturar el clic en la barra de pestañas del delegado, algo como a continuación:
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
if (viewController.isKindOfClass(SomeControllerClass) && tabBarController.selectedIndex == 0)
{
viewController.scrollToTop()
}
}
Ahora para la función scrollToTop
dentro del controlador:
func scrollToTop()
{
self.tableView.scrollRectToVisible(CGRectMake(0,0,CGRectGetWidth(self.tableView.frame), CGRectGetHeight(self.tableView.frame)), animated: true)
}
Enfoque 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
}
Algunas notas aquí: "shouldSelect" en lugar de "didSelect" porque esto último se está produciendo después de la transición, lo que significa que la variable local de viewController ya cambió. 2. Necesitamos manejar el evento antes de cambiar el controlador, para tener la información de los controladores de vista de navegación con respecto a la acción de desplazamiento (o no).
Explicación: queremos desplazarnos hacia arriba, si la vista actual es en realidad un controlador de vista de Lista / Tabla. Si la navegación ha avanzado y tocamos la misma barra de pestañas, la acción deseada sería simplemente abrir un paso (funcionalidad predeterminada) y no desplazarse hacia arriba. Si la navegación no tiene un significado avanzado, todavía estamos en el controlador de tabla / lista y solo entonces queremos desplazarnos hacia arriba al tocar de nuevo. (Lo mismo que hace Facebook al tocar "Feed" en el perfil de un usuario. Solo vuelve a feed sin desplazarse hacia arriba.
Estaba usando esta jerarquía de vistas.
UITabBarController > UINavigationController > UIViewController
UITabBarController
una referencia al UITabBarController
en el UIViewController
tabBarControllerRef = self.tabBarController as! CustomTabBarClass
tabBarControllerRef!.navigationControllerRef = self.navigationController as! CustomNavigationBarClass
tabBarControllerRef!.viewControllerRef = self
Luego creé un Bool
que se llamó en los momentos correctos, y un método que permite desplazarme hacia arriba sin problemas.
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)
}
Luego en mi UITabBarController
Custom Class lo llamé
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()
}
}
}
El resultado es idéntico al de Instagram y al feed de Twitter :)
He implementado un UITabBarController de plug & play que puedes reutilizar libremente en tus proyectos. Para habilitar la funcionalidad de desplazamiento hacia arriba, solo debe usar la subclase, nada más.
Debe trabajar fuera de la caja con Storyboards también.
Código:
/// 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
}
}
Editar (09.08.2016):
Después de intentar compilar con la configuración de lanzamiento predeterminada (archivo), el compilador no permitiría la posibilidad de crear una gran cantidad de cierres que se capturaron en una función recursiva, por lo que no se compilaría. Se cambió el código para devolver el primer UIScrollView encontrado con "scrollsToTop" establecido en true sin usar cierres.
Puede usar shouldSelect
lugar de didSelect
, lo que didSelect
la necesidad de una variable externa para realizar un seguimiento del controlador de vista anterior.
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
if ([viewController isEqual:self] && [tabBarController.selectedViewController isEqual:viewController]) {
// Do custom stuff here
}
return YES;
}
SWIFT 3
Aquí va..
Primero implemente el UITabBarControllerDelegate
en la clase y asegúrese de que el delegado esté configurado en 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
}
}
A continuación, ponga esta función de delegado en algún lugar de su clase.
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let tabBarIndex = tabBarController.selectedIndex
print(tabBarIndex)
if tabBarIndex == 0 {
self.collectionView.setContentOffset(CGPoint.zero, animated: true)
}
}
Asegúrese de seleccionar el índice correcto en la declaración "if". Incluí la función de impresión para que pueda volver a comprobar.
Tengo una vista de colección incrustada en un controlador de navegación, en Swift esto funciona.
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;
}
Implemente el método UITabBarControllerDelegate tabBarController: didSelectViewController: para recibir una notificación cuando el usuario seleccione una pestaña. También se llama a este método cuando se vuelve a tocar el mismo botón de pestaña, incluso si esa pestaña ya está seleccionada.
Un buen lugar para implementar este delegado probablemente sería su AppDelegate
. O el objeto que lógicamente "posee" el controlador de la barra de pestañas.
Yo declararía e implementaría un método que se puede llamar en sus controladores de vista para desplazarse por la vista de colección.
- (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;
}
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)
}
}
Esta es mi respuesta en Swift 3. Utiliza una función auxiliar para llamadas recursivas y se desplaza automáticamente a la parte superior de la llamada. Probado en un UICollectionViewController integrado en un UINavigationController incorporado en un UITabBarController