web-dev-qa-db-fra.com

Comportement étrange de uitableview dans iOS11. Les cellules défilent vers le haut avec la navigation Animation Push

J'ai récemment migré du code vers le nouveau SDK iOS 11 beta 5.

J'ai maintenant un comportement très déroutant de UITableView. La vue de table en elle-même n'est pas aussi sophistiquée. J'ai des cellules personnalisées mais dans la plupart des cas, c'est juste pour leur taille.

Lorsque j'appuie mon contrôleur de vue avec tableview, une animation supplémentaire s'affiche dans laquelle les cellules "défilent" (ou éventuellement le cadre de la vue table est modifiée) et descendent le long de l'animation de navigation Push/pop. S'il vous plaît voir gif:

wavy tableview

Je crée manuellement tableview dans loadView méthode et configure les contraintes de présentation automatique pour qu'elles soient identiques à celles de début, de fin, de haut et de bas de la vue d'ensemble de tableview. La vue d'ensemble est la vue racine du contrôleur de vue.

Le code de diffusion du contrôleur de vue est très standard: self.navigationController?.pushViewController(notifVC, animated: true)

Le même code fournit un comportement normal sur iOS 10.

Pourriez-vous s'il vous plaît me diriger dans la direction de ce qui ne va pas?

EDIT: J'ai créé un contrôleur de tableview très simple et je peux reproduire le même comportement ici. Code:

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: J'ai pu réduire le problème à ma personnalisation de UINavigationBar. J'ai une personnalisation comme celle-ci:

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

createFilledImage crée une image carrée avec une taille et une couleur données.

Si je commente cette ligne, je retrouve un comportement normal.

Je vous serais reconnaissant de toute réflexion à ce sujet.

116
iur

Cela est dû à _UIScrollView's_ (UITableView est une sous-classe de UIScrollview) new contentInsetAdjustmentBehavior Propriété , définie sur .automatic par défaut.

Vous pouvez remplacer ce comportement par l'extrait suivant dans le viewDidLoad de tout contrôleur affecté:

_    tableView.contentInsetAdjustmentBehavior = .never
_

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

147
Maggy Hillen

En plus de la réponse de maggy

OBJECTIF-C

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

Ce problème était dû à un bogue dans iOS 11 où le safeAreaInsets de la vue du contrôleur de vue était mal défini lors de la transition de navigation, ce qui devrait être corrigé dans iOS 11.2. Régler le paramètre contentInsetAdjustmentBehavior sur .never n’est pas une solution de contournement intéressante, car il aura probablement d’autres effets secondaires indésirables. Si vous utilisez une solution de contournement, assurez-vous de la supprimer pour les versions iOS> = 11.2.

- mentionné par smileyborg (ingénieur logiciel chez Apple)

22
Lal Krishna

Vous pouvez modifier ce comportement en une fois dans l'application en utilisant NSProxy dans, par exemple, didFinishLaunchingWithOptions:

if (@available(iOS 11.0, *)) {
      [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} 
6
BigDanceMouse

Voici comment j'ai réussi à résoudre ce problème tout en permettant à iOS 11 de définir les incrustations automatiquement . J'utilise UITableViewController.

  • Sélectionnez "Étendre les arêtes sous les barres du haut" et "Étendre les arêtes sous les barres opaques" dans votre contrôleur de vue dans le storyboard (ou par programme ). Les incrustations dans la zone de sécurité empêcheront votre vue de passer sous la barre supérieure.
  • Vérifiez le bouton "Inserts to Safe Area" sur la vue de votre table dans votre storyboard. (ou tableView.insetsContentViewsToSafeArea = true) - Cela n'est peut-être pas nécessaire, mais c'est ce que j'ai fait.
  • Définissez le comportement de réglage de l’encart de contenu sur "Axes à faire défiler" (ou tableView.contentInsetAdjustmentBehavior = .scrollableAxes) - .always pourrait également fonctionner, mais je n’ai pas effectué de test.

Une autre chose à essayer si tout le reste échoue:

Remplacer viewSafeAreaInsetsDidChangeUIViewController méthode permettant à la vue tabulaire de forcer la définition des incrustations de la vue de défilement sur celles de la zone sécurisée. Ceci est associé au paramètre "Jamais" de la réponse de Maggy.

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

Remarque: self.tableView et self.view devraient être identiques pour UITableViewController.

4
Santa Claus

Cela ressemble plus à un bug que le comportement prévu. Cela se produit lorsque la barre de navigation n'est pas translucide ou lorsque l'image d'arrière-plan est définie.

Si vous définissez simplement contentInsetAdjustmentBehavior sur .never, les encarts de contenu ne seront pas définis correctement sur iPhone X, par exemple. le contenu irait dans la zone inférieure, sous les barres de défilement.

Il faut faire deux choses:
1. empêcher ScrollView animer sur Push/Pop
2. conserver le comportement automatique car il est nécessaire pour iPhone X. Sans cela, par exemple. en portrait, le contenu ira au-dessous de la barre de défilement inférieure.

Nouvelle solution simple: dans XIB: Ajoutez simplement une nouvelle UIView en haut de votre vue principale avec top, lead et filing to superview et height à 0. Vous n'avez pas besoin de le connecter à d'autres sous-vues ou n'importe quoi.

Ancienne solution:

Remarque: Si vous utilisez UIScrollView en mode paysage, il ne définit toujours pas correctement les encarts horizontaux (un autre bogue?). Vous devez donc épingler le début/la fin de scrollView à safeAreaInsets dans IB.

Remarque 2: La solution ci-dessous pose également le problème suivant: si tableView est déplacé vers le bas et que vous appuyez sur le contrôleur et revenez en arrière, il ne sera plus en bas.

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
    }
}
3
El Horrible

De plus, si vous utilisez la barre d'onglets, le contenu inférieur de la vue de collection sera égal à zéro. Pour cela, mettez le code ci-dessous dans viewDidAppear:

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

assurez-vous, avec le code ci-dessus, d'ajouter du code supplémentaire comme suit. Ça a résolu le problème

override func viewDidLayoutSubviews() { 
     super.viewDidLayoutSubviews() 
     tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) // print(thisUISBTV.adjustedContentInset) 
}
2

Je peux reproduire le bogue pour iOS 11.1 mais il semble que le bogue soit corrigé depuis iOS 11.2. Voir http://openradar.appspot.com/34465226

2
Linda

Dans mon cas, cela a fonctionné (mettez-le dans viewDidLoad):

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

Suppression d'espace supplémentaire en haut de collectionView ou tableView

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

Le code ci-dessus collectionView ou tableView passe sous la barre de navigation.
En dessous du code empêche la vue collection de passer sous la navigation

    self.edgesForExtendedLayout = UIRectEdge.bottom

mais j'aime utiliser ci-dessous la logique et le code pour le ICollectionView

Les valeurs d'incrustation des bords sont appliquées à un rectangle pour réduire ou agrandir la zone représentée par ce rectangle. En règle générale, les incrustations Edge sont utilisées lors de la disposition de la vue pour modifier le cadre de la vue. Les valeurs positives entraînent l'insertion (ou la réduction) de la trame de la quantité spécifiée. Les valeurs négatives entraînent le début (ou l'expansion) de l'image de la quantité spécifiée.

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

Le meilleur moyen pour ICollectionView

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

Supprimer ce code fonctionne pour moi

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

J'utilisais UISearchController avec resultsControllers personnalisé qui a vue sous forme de tableau. En poussant le nouveau contrôleur sur le contrôleur de résultats, tableview est passé à la recherche.

Le code énuméré ci-dessus corrige totalement le problème

0
Vitalii Shvetsov