Comportamento estranho do uitableview no iOS11. Células rolam para cima com animação push de navegação

116

Migrei recentemente alguns códigos para o novo iOS 11 beta 5 SDK.

Agora recebo um comportamento muito confuso de UITableView. O tableview em si não é tão chique. Tenho células personalizadas, mas na maioria das vezes são apenas para a altura.

Quando empurro meu controlador de visualização com tableview, obtenho uma animação adicional em que as células "rolam para cima" (ou possivelmente todo o quadro tableview é alterado) e para baixo ao longo da animação de navegação push / pop. Por favor, veja gif:

tableview ondulado

Eu crio manualmente tableviewno loadViewmétodo e configuro as restrições de layout automático para serem iguais à visão inicial, final, superior e inferior da supervisão do tableview. O superview é a visão raiz do controlador de visão.

Visualizar o código de envio do controlador é muito padrão: self.navigationController?.pushViewController(notifVC, animated: true)

O mesmo código fornece comportamento normal no iOS 10.

Você poderia me indicar o que está errado?

EDIT: Eu fiz um controlador tableview muito simples e posso reproduzir o mesmo comportamento lá. 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)
    }
}

EDIT 2: Consegui restringir o problema à minha personalização do UINavigationBar. Tenho uma personalização como esta:

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

onde createFilledImagecria uma imagem quadrada com determinado tamanho e cor.

Se eu comentar esta linha, recupero o comportamento normal.

Eu apreciaria qualquer opinião sobre este assunto.

iur
fonte
Pode não ser um problema com a personalização da barra de navegação. Eu estava tendo o mesmo problema (a resposta aceita resolveu isso) sem qualquer personalização. Acho que pode ser um problema com a maneira como o iOS lida com a tableview quando ela é criada manualmente como uma subvisualização, em vez de usar UITableViewController.
Mark Leonard,
2
Estou vendo esse comportamento apenas quando definido navigationBar.isTranslucentcomo false, caso contrário, funciona bem.
b_ray
5
Este parece ser um bug no iOS11 GM, por favor, modifique esse relatório de bug para que este problema receba alguma atenção da Apple: openradar.appspot.com/34465226
b_ray
1
Esse problema parece ter sido corrigido no iOS 11.2 beta. Eu não configuraria contentInsetAdjustmentBehavior como nunca porque isso quebra as visualizações de rolagem do iPhone X por não fornecer preenchimento na parte inferior da tela. A parte inferior da visualização do conteúdo fica sob o "botão" inicial do iPhone X.
batu

Respostas:

150

Isso se deve à nova propriedade UIScrollView's (UITableView é uma subclasse de UIScrollview)contentInsetAdjustmentBehavior , que é definida como .automaticpor padrão.

Você pode substituir esse comportamento com o seguinte snippet no viewDidLoad de qualquer controlador afetado:

    tableView.contentInsetAdjustmentBehavior = .never

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

Maggy Hillen
fonte
2
este comentário sobre a definição de UIScrollViewContentInsetAdjustmentBehavior.automatic diz: "... para compatibilidade com versões anteriores também ajustará o contentInset superior e inferior quando a visualização de rolagem for propriedade de um controlador de visualização com automaticAdjustsScrollViewInsets = YES dentro de um controlador de navegação, independentemente de a rolagem a visualização é rolável ". Minha teoria é que o contentInset da barra de navegação é afetado pela configuração da imagem de fundo, que é então ajustada dinamicamente.
Maggy Hillen
8
Você também pode fazer isso com o storyboard. Inspetor de tamanho -> Inserções de conteúdo -> Definir 'Nunca'.
Woongbi Kim
4
Se o seu conteúdo se estende para trás da barra de guias, a desativação tableView.contentInsetAdjustmentBehaviorquebraria as inserções.
kean
4
Além disso, simplesmente desabilitar isso colocará o indicador de rolagem atrás do (dispositivo superior) no iPhone X em paisagem. O objetivo desse comportamento é ajustar a área de conteúdo de scrollviews para que fiquem visíveis em telas que não são retangulares. Acho que só podemos ver isso atualmente no iPhone X Sim.
PhoneyDeveloper
8
Esse problema foi causado por um bug no iOS 11, em que os safeAreaInsets da visualização do controlador de visualização foram configurados incorretamente durante a transição de navegação, o que deve ser corrigido no iOS 11.2. Definir contentInsetAdjustmentBehaviorcomo .nevernão é uma ótima solução alternativa porque provavelmente terá outros efeitos colaterais indesejáveis. Se você usar uma solução alternativa, certifique-se de removê-la das versões iOS> = 11.2.
smileyborg
23

Além da resposta de Maggy

OBJETIVO-C

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

Esse problema foi causado por um bug no iOS 11 em que a safeAreaInsetsvisualização do controlador de visualização foi configurada incorretamente durante a transição de navegação, o que deve ser corrigido no iOS 11.2. Definir contentInsetAdjustmentBehaviorcomo .nevernão é uma ótima solução alternativa porque provavelmente terá outros efeitos colaterais indesejáveis. Se você usar uma solução alternativa, certifique-se de removê-la para as versões iOS> = 11.2

- mencionado por smileyborg (Engenheiro de Software da Apple)

Lal Krishna
fonte
6

Você pode editar esse comportamento de uma vez em todo o aplicativo usando NSProxy em, por exemplo, didFinishLaunchingWithOptions:

if (@available(iOS 11.0, *)) {
      [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} 
BigDanceMouse
fonte
1
Isso vai quebrar o controlador do sistema, como UIImagePickerController
galway
6

Veja como eu consegui corrigir esse problema e ainda permitir que o iOS 11 defina os insets automaticamente . Estou usando UITableViewController.

  • Selecione "Estender bordas sob as barras superiores" e "Estender bordas sob as barras opacas" em seu controlador de visualização no storyboard (ou programaticamente ). As inserções da área de segurança impedirão que sua visão vá para baixo da barra superior.
  • Verifique o botão "Insets to Safe Area" na visualização da mesa em seu storyboard. (ou tableView.insetsContentViewsToSafeArea = true) - Pode não ser necessário, mas foi o que fiz.
  • Defina o comportamento de ajuste de inserção de conteúdo como "Eixos roláveis" (ou tableView.contentInsetAdjustmentBehavior = .scrollableAxes) - .alwaystambém pode funcionar, mas não testei.

Outra coisa a tentar se tudo mais falhar:

viewSafeAreaInsetsDidChange UIViewControllerMétodo de substituição para obter a visualização da tabela para forçar a configuração das inserções da visualização de rolagem para as inserções da área segura. Isso ocorre em conjunto com a configuração 'Nunca' na resposta de Maggy.

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

Nota: self.tableViewe self.viewdeve ser a mesma coisa paraUITableViewController

Papai Noel
fonte
Tentei quase tudo neste tópico e funcionou para mim. TableViewVC incorporado em TabBarVC. Obrigado!
Josh Wolff
3

Isso parece mais um bug do que um comportamento pretendido. Acontece quando a barra de navegação não é translúcida ou quando a imagem de fundo é definida.

Se você apenas definir contentInsetAdjustmentBehavior como .never, as inserções de conteúdo não serão definidas corretamente no iPhone X, por exemplo, o conteúdo irá para a área inferior, sob as barras de rolagem.

É necessário fazer duas coisas:
1. evitar a animação do scrollView no push / pop
2. manter o comportamento .automático porque é necessário para o iPhone X. Sem isso, por exemplo, no retrato, o conteúdo irá abaixo da barra de rolagem inferior.

Nova solução simples: no XIB: Basta adicionar um novo UIView no topo de sua visualização principal com o topo, à esquerda e à direita para supervisualizar e a altura definida como 0. Você não precisa conectá-lo a outras subvisualizações nem nada.

Solução antiga:

Nota: Se você estiver usando UIScrollView no modo paisagem, ele ainda não define inserções horizontais corretamente (outro bug?), Então você deve fixar o início / fim de scrollView em safeAreaInsets em IB.

Nota 2: A solução abaixo também tem o problema de que se tableView for rolado para a parte inferior e você empurrar o controlador e voltar, ele não estará mais na 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
fonte
O que é parentView?
PhoneyDeveloper
A visualização principal do seu controlador de visualização, atualizei a resposta. THX.
El Horrible,
Quando estou usando UITableViewController, tableView é a visualização do controlador de visualização. Além disso, as inserções devem ser diferentes quando o dispositivo é girado. Eu quero o ajuste. Só não gosto da animação quando o tableView aparece pela primeira vez.
PhoneyDeveloper 02 de
No seu caso, como UITableView é a visualização do controlador, ele deve ter safeAreaInsets.bottom = 34 (retrato), então você pode apenas definir tableView.contentInset = tableView.safeAreaInsets.
El Horrible
Isso não funciona para mim. Em viewDidLoad, todas as inserções são zero. Se eu tentar usar esse código em viewWillLayoutSubviews, o indicador de rolagem será inserido, mas o próprio tableView poderá rolar horizontalmente. Se eu apenas olhar as inserções em viewWillLayoutSubviews sem desabilitar o ajuste, o bottomjustedContentInset torna-se 21 e isso é tudo que parece mudar.
PhoneyDeveloper
3

Posso reproduzir o bug do iOS 11.1, mas parece que o bug foi corrigido desde o iOS 11.2. Veja http://openradar.appspot.com/34465226

Linda
fonte
sim, corrigido no iOS 11.2
Richie Hyatt
2

Certifique-se, juntamente com o código acima, de adicionar código adicional conforme segue. Resolveu o problema

override func viewDidLayoutSubviews() { 
     super.viewDidLayoutSubviews() 
     tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) // print(thisUISBTV.adjustedContentInset) 
}
Basavaraj Kalaghatagi
fonte
Só isso já resolveu meu problema !! Obrigado. Perdi muitas horas neste assunto!
Murat Yasar
2

Além disso, se você usar a barra de guias, a inserção de conteúdo inferior da visualização da coleção será zero. Para isso, coloque o código abaixo em viewDidAppear:

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

No meu caso, funcionou (coloque em viewDidLoad):

self.navigationController.navigationBar.translucent = YES;
nemissm
fonte
1

Removendo espaço extra na parte superior collectionViewoutableView

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

Acima do código collectionViewou tableViewsob a barra de navegação.
O código abaixo impede que a visualização da coleção vá para a navegação

    self.edgesForExtendedLayout = UIRectEdge.bottom

mas adoro usar a lógica e o código abaixo para o UICollectionView

Os valores de borda interna são aplicados a um retângulo para reduzir ou expandir a área representada por esse retângulo. Normalmente, as inserções de borda são usadas durante o layout da vista para modificar o quadro da vista. Valores positivos fazem com que o quadro seja reduzido (ou reduzido) na quantidade especificada. Valores negativos fazem com que o quadro seja iniciado (ou expandido) pelo valor especificado.

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

A melhor maneira de UICollectionView

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

Excluir este código funciona para mim

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

Eu estava usando UISearchController com resultsControllers personalizados que tem visualização de tabela. Empurrar o novo controlador no controlador de resultados fez com que tableview fosse para pesquisa.

O código listado acima resolveu totalmente o problema

Vitalii Shvetsov
fonte