Comportamiento extraño de uitableview en iOS11. Las celdas se desplazan hacia arriba con la animación push de navegación

116

Recientemente he migrado un código al nuevo SDK de iOS 11 beta 5.

Ahora obtengo un comportamiento muy confuso de UITableView. La vista de tabla en sí no es tan elegante. Tengo celdas personalizadas, pero en su mayor parte es solo por su altura.

Cuando presiono mi controlador de vista con vista de tabla, obtengo una animación adicional donde las celdas "se desplazan hacia arriba" (o posiblemente se cambia todo el marco de vista de tabla) y hacia abajo a lo largo de la animación de navegación push / pop. Por favor ver gif:

vista de mesa ondulada

Creo manualmente tableviewen el loadViewmétodo y configuro restricciones de diseño automático para que sean iguales a la vista inicial, final, superior e inferior de la vista de tabla. La supervista es la vista raíz del controlador de vista.

Ver el código de inserción del controlador es muy estándar: self.navigationController?.pushViewController(notifVC, animated: true)

El mismo código proporciona un comportamiento normal en iOS 10.

¿Podrías indicarme qué está mal?

EDITAR: He hecho un controlador de vista de tabla muy simple y puedo reproducir el mismo comportamiento allí. Código:

class VerySimpleTableViewController : UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    }


    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 4
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = String(indexPath.row)
        cell.accessoryType = .disclosureIndicator

        return cell
    }


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)

        let vc = VerySimpleTableViewController.init(style: .grouped)

        self.navigationController?.pushViewController(vc, animated: true)
    }
}

EDICIÓN 2: Pude limitar el problema a mi personalización de UINavigationBar. Tengo una personalización como esta:

rootNavController.navigationBar.setBackgroundImage(createFilledImage(withColor: .white, size: 1), for: .default)

donde createFilledImagecrea una imagen cuadrada con un tamaño y color determinados.

Si comento esta línea, recupero el comportamiento normal.

Agradecería cualquier comentario sobre este asunto.

yo
fuente
Puede que no sea un problema con la personalización de la barra de navegación. Estaba teniendo el mismo problema (la respuesta aceptada resolvió esto) sin ninguna personalización. Creo que podría ser un problema con la forma en que iOS maneja la vista de tabla cuando se crea manualmente como una subvista, en lugar de usar UITableViewController.
Mark Leonard
2
Estoy viendo este comportamiento sólo cuando me puse navigationBar.isTranslucenta false, si no funciona bien.
b_ray
5
Esto parece ser un error en iOS11 GM, por favor engañe ese informe de error para que este problema llame la
b_ray
1
Este problema parece estar solucionado en iOS 11.2 beta. No establecería contentInsetAdjustmentBehavior en nunca porque rompe las vistas de desplazamiento del iPhone X al no proporcionar relleno en la parte inferior de la pantalla. La parte inferior de la vista de contenido permanece debajo del "botón" de inicio del iPhone X.
batu

Respuestas:

150

Esto se debe a UIScrollView's (UITableView es una subclase de UIScrollview) nueva contentInsetAdjustmentBehaviorpropiedad, que está configurada en .automaticforma predeterminada.

Puede anular este comportamiento con el siguiente fragmento en viewDidLoad de cualquier controlador afectado:

    tableView.contentInsetAdjustmentBehavior = .never

https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior

Maggy Hillen
fuente
2
este comentario sobre la definición de UIScrollViewContentInsetAdjustmentBehavior.automatic dice: "... para compatibilidad con versiones anteriores, también ajustará el contentInset superior e inferior cuando la vista de desplazamiento es propiedad de un controlador de vista con automáticamenteAdjustsScrollViewInsets = YES dentro de un controlador de navegación, independientemente de si el desplazamiento la vista es desplazable ". Mi teoría es que el contentInset de la barra de navegación se ve afectado al configurar la imagen de fondo, que luego se ajusta dinámicamente.
Maggy Hillen
8
También puedes hacer eso con el guión gráfico. Inspector de tamaño -> Inserciones de contenido -> Establecer 'Nunca'.
Woongbi Kim
4
Si su contenido se extiende detrás de la barra de pestañas, la desactivación tableView.contentInsetAdjustmentBehaviorrompería las inserciones.
kean
4
Además, simplemente deshabilitar esto colocará el indicador de desplazamiento detrás del (gizmo superior) en el iPhone X en horizontal. El objetivo de este comportamiento es ajustar el área de contenido de las vistas de desplazamiento para que sean visibles en pantallas que no son rectangulares. Creo que solo podemos ver esto actualmente en el iPhone X Sim.
PhoneyDeveloper
8
Este problema fue causado por un error en iOS 11 en el que los safeAreaInsets de la vista del controlador de vista se establecieron incorrectamente durante la transición de navegación, que debería corregirse en iOS 11.2. Configurar el contentInsetAdjustmentBehaviorto .neverno es una gran solución porque probablemente tendrá otros efectos secundarios indeseables. Si usa una solución alternativa, debe asegurarse de eliminarla para las versiones de iOS> = 11.2.
smileyborg
23

Además de la respuesta de Maggy

C OBJETIVO

if (@available(iOS 11.0, *)) {
    scrollViewForView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}

Este problema fue causado por un error en iOS 11 en el que safeAreaInsetsla vista del controlador de vista se estableció incorrectamente durante la transición de navegación, lo que debería corregirse en iOS 11.2. Configurar el contentInsetAdjustmentBehaviora .neverno es una gran solución porque probablemente tendrá otros efectos secundarios indeseables. Si usa una solución alternativa, debe asegurarse de eliminarla para las versiones de iOS> = 11.2

-mencionado por smileyborg (ingeniero de software de Apple)

Lal Krishna
fuente
6

Puede editar este comportamiento a la vez en toda la aplicación utilizando NSProxy en, por ejemplo, didFinishLaunchingWithOptions:

if (@available(iOS 11.0, *)) {
      [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} 
BigDanceMouse
fuente
1
Esto romperá el controlador del sistema, como UIImagePickerController
galway
6

Así es como logré solucionar este problema al tiempo que permitía que iOS 11 estableciera inserciones automáticamente . Estoy usando UITableViewController.

  • Seleccione "Extender los bordes debajo de las barras superiores" y "Extender los bordes debajo de las barras opacas" en su controlador de vista en el guión gráfico (o mediante programación ). Las inserciones del área segura evitarán que su vista pase por debajo de la barra superior.
  • Marque el botón "Inserciones en área segura" en la vista de su tabla en su guión gráfico. (o tableView.insetsContentViewsToSafeArea = true) - Puede que esto no sea necesario, pero es lo que hice.
  • Establezca el comportamiento de ajuste del recuadro de contenido en "Ejes desplazables" (o tableView.contentInsetAdjustmentBehavior = .scrollableAxes); .alwaystambién podría funcionar, pero no lo probé.

Otra cosa para probar si todo lo demás falla:

viewSafeAreaInsetsDidChange UIViewControllerMétodo de anulación para que la vista de tabla fuerce el establecimiento de las inserciones de la vista de desplazamiento en las inserciones del área segura. Esto está en conjunto con la configuración 'Nunca' en la respuesta de Maggy.

- (void)viewSafeAreaInsetsDidChange {
    [super viewSafeAreaInsetsDidChange];
    self.tableView.contentInset = self.view.safeAreaInsets;
}

Nota: self.tableViewy self.viewdebería ser lo mismo paraUITableViewController

Papá Noel
fuente
Probé casi todo en este hilo y esto funcionó para mí. TableViewVC incrustado en TabBarVC. ¡Gracias!
Josh Wolff
3

Esto parece más un error que un comportamiento previsto. Ocurre cuando la barra de navegación no es translúcida o cuando se establece una imagen de fondo.

Si solo configura contentInsetAdjustmentBehavior en .never, las inserciones de contenido no se configurarán correctamente en el iPhone X, por ejemplo, el contenido iría al área inferior, debajo de las barras de desplazamiento.

Es necesario hacer dos cosas:
1. evitar que scrollView se anime hacia arriba en push / pop
2. mantener el comportamiento .automatic porque es necesario para iPhone X. Sin esto, por ejemplo, en vertical, el contenido irá por debajo de la barra de desplazamiento inferior.

Nueva solución simple: en XIB: simplemente agregue una nueva UIView en la parte superior de su vista principal con la parte superior, anterior y posterior a la supervista y la altura configurada en 0. No tiene que conectarlo a otras subvistas ni nada.

Solución antigua:

Nota: Si está utilizando UIScrollView en modo horizontal, todavía no establece las inserciones horizontales correctamente (¿otro error?), Por lo que debe anclar el avance / final de scrollView a safeAreaInsets en IB.

Nota 2: La solución a continuación también tiene el problema de que si tableView se desplaza hacia la parte inferior, y presiona el controlador y vuelve a aparecer, ya no estará en la parte inferior.

override func viewDidLoad()
{
    super.viewDidLoad()

    // This parts gets rid of animation when pushing
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .never
    }
}

override func viewDidDisappear(_ animated: Bool)
{
    super.viewDidDisappear(animated)
    // This parts gets rid of animation when popping
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .never
    }
}

override func viewDidAppear(_ animated: Bool)
{
    super.viewDidAppear(animated)
    // This parts sets correct behaviour(insets are correct on iPhone X)
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .automatic
    }
}
El Horrible
fuente
¿Qué es parentView?
PhoneyDeveloper
La vista principal de su controlador de vista, he actualizado la respuesta. Gracias.
El Horrible
Cuando estoy usando UITableViewController, tableView es la vista del controlador de vista. Además, las inserciones deben ser diferentes cuando se gira el dispositivo. Quiero el ajuste. Simplemente no me gusta la animación cuando aparece tableView por primera vez.
PhoneyDeveloper
En su caso, dado que UITableView es la vista del controlador, debería tener safeAreaInsets.bottom = 34 (retrato), por lo que podría simplemente establecer tableView.contentInset = tableView.safeAreaInsets.
El Horrible
Eso no me funciona. En viewDidLoad todas las inserciones son cero. Si trato de usar ese código en viewWillLayoutSubviews, el indicador de desplazamiento se inserta, pero la propia tableView puede desplazarse horizontalmente. Si solo miro las inserciones en viewWillLayoutSubviews sin deshabilitar el ajuste, el contenido ajustado inferior se convierte en 21 y eso es todo lo que parece cambiar.
PhoneyDeveloper
2

asegúrese de que junto con el código anterior, agregue código adicional de la siguiente manera. Resolvió el problema

override func viewDidLayoutSubviews() { 
     super.viewDidLayoutSubviews() 
     tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) // print(thisUISBTV.adjustedContentInset) 
}
Basavaraj Kalaghatagi
fuente
¡¡Esto solo resolvió mi problema !! Gracias. ¡Perdí demasiadas horas en este tema!
Murat Yasar
2

Además, si usa la barra de pestañas, la inserción de contenido inferior de la vista de colección será cero. Para esto, ponga el siguiente código en viewDidAppear:

if #available(iOS 11, *) {
    tableView.contentInset = self.collectionView.safeAreaInsets
}
hasankose
fuente
2

En mi caso, esto funcionó (póngalo en viewDidLoad):

self.navigationController.navigationBar.translucent = YES;
nemismo
fuente
1

Eliminar espacio adicional en la parte superior collectionViewotableView

    if #available(iOS 11.0, *) {
        collectionView.contentInsetAdjustmentBehavior  = .never
        //tableView.contentInsetAdjustmentBehavior  = .never
    } else {
        automaticallyAdjustsScrollViewInsets = false
    }

Encima del código collectionViewo tableViewdebajo de la barra de navegación.
El siguiente código evita que la vista de colección vaya debajo de la navegación

    self.edgesForExtendedLayout = UIRectEdge.bottom

pero me encanta usar la lógica y el código siguientes para UICollectionView

Los valores de inserción de borde se aplican a un rectángulo para reducir o expandir el área representada por ese rectángulo. Normalmente, las inserciones de borde se utilizan durante el diseño de la vista para modificar el marco de la vista. Los valores positivos hacen que el marco se inserte (o encoja) en la cantidad especificada. Los valores negativos hacen que el marco se extienda (o se expanda) en la cantidad especificada.

collectionView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
//tableView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)

La mejor forma para UICollectionView

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
}
Nazmul Hasan
fuente
0

Eliminar este código funciona para mí

self.edgesForExtendedLayout = UIRectEdgeNone
Weiminghuaa
fuente
0
  if #available(iOS 11, *) {
        self.edgesForExtendedLayout = UIRectEdge.bottom
  }

Estaba usando UISearchController con resultsControllers personalizados que tienen vista de tabla. La inserción de un nuevo controlador en el controlador de resultados hizo que tableview pasara a buscar.

El código enumerado anteriormente solucionó totalmente el problema

Vitalii Shvetsov
fuente