Après Xcode 11.2 : l'application plante à cause de _UITextLayoutView — solution

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Après la mise à jour de Xcode 11.2, l'application plante : comment vaincre _UITextLayoutView

\n\n

Ça vous arrive : vous travaillez tranquillement sur votre projet, tout fonctionne, les tests passent, les utilisateurs sont satisfaits. Et puis — boum ! — vous mettez à jour Xcode vers la version 11.2, vous lancez l'application, et elle plante avec une erreur mystérieuse _UITextLayoutView. Vous reconnaissez ? Si oui, vous n'êtes pas seul. Ce problème a littéralement explosé sur Stack Overflow fin 2019 (des milliers de vues en une semaine).

\n\n

Dans cet article, je ne vais pas seulement vous montrer comment corriger ce bug, mais aussi expliquer pourquoi il se produit, comment le diagnostiquer et, surtout, comment l'éviter à l'avenir. Nous allons tout détailler avec des exemples concrets en Swift. C'est parti !

\n\n

Qu'est-ce que cette bête de _UITextLayoutView ?

\n\n

Commençons par la théorie. _UITextLayoutView est une classe interne (privée) d'UIKit qui gère le rendu du texte dans UITextView. Apple ne la documente pas publiquement, donc quand quelque chose s'est mal passé avec cette classe dans Xcode 11.2, les développeurs se sont retrouvés dans une impasse. L'erreur ressemble à ceci :

\n\n
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[_UITextLayoutView setFrame:]: unrecognized selector sent to instance 0x...'
\n\n

L'expression clé est "unrecognized selector sent to instance". Cela signifie que vous essayez d'appeler la méthode setFrame: sur un objet qui ne la prend pas en charge. Et comme _UITextLayoutView est une classe privée, avant Xcode 11.2, elle se comportait différemment.

\n\n

Qui est à risque ?

\n\n

Le problème n'apparaît pas chez tout le monde, mais seulement dans certaines conditions. Voici un scénario typique :

\n
    \n
  • Vous utilisez UITextView avec le redimensionnement automatique (auto-sizing) activé.
  • \n
  • Vous avez une hiérarchie de vues personnalisée où UITextView est imbriqué dans un UIScrollView ou une UITableViewCell.
  • \n
  • Vous interagissez activement avec le champ de texte : défilement, édition, changement de police.
  • \n
\n\n

Si au moins un point correspond — félicitations, vous êtes dans la zone à risque. Mais ne vous inquiétez pas : il existe une solution, et elle est élégante.

\n\n

Étape 1 : Diagnostic — trouvez le coupable

\n\n

Avant de traiter, assurons-nous que le problème vient bien de _UITextLayoutView. Ouvrez la console Xcode (View → Debug Area → Activate Console) et lancez l'application. Si vous voyez une erreur similaire à celle que j'ai donnée ci-dessus — vous êtes sur la bonne voie.

\n\n

Un signe supplémentaire : le plantage se produit dès la première tentative d'affichage de UITextView ou lors du défilement à l'intérieur. Si l'application plante immédiatement après le chargement — c'est probablement ça.

\n\n

Étape 2 : Solution simple et rapide (pour ceux qui sont pressés)

\n\n

Le correctif le plus populaire sur Stack Overflow est de désactiver le redimensionnement automatique de UITextView. Voici comment faire :

\n\n
// Dans votre viewDidLoad ou méthode de configuration\ntextView.isScrollEnabled = false\ntextView.translatesAutoresizingMaskIntoConstraints = true
\n\n

Mais attention ! Cette solution fonctionne, mais elle casse l'auto-dimensionnement du texte. Si vous avez besoin que UITextView change dynamiquement de hauteur en fonction du contenu — cette option ne conviendra pas. Creusons plus profondément.

\n\n

Étape 3 : Solution avancée — redéfinition de layoutSubviews

\n\n

Apple a publié un correctif dans Xcode 11.2.1, mais si pour une raison quelconque vous êtes resté sur la version 11.2, ou si vous voulez une solution universelle, voici ce qu'il faut faire.

\n\n

Créez une sous-classe de UITextView et redéfinissez la méthode layoutSubviews :

\n\n
import UIKit\n\nclass FixedTextView: UITextView {\n    override func layoutSubviews() {\n        super.layoutSubviews()\n        // Nettoie la couche des sous-vues superflues causées par le bug\n        self.subviews.forEach { subview in\n            if subview.isKind(of: NSClassFromString("_UITextLayoutView") ?? UIView.self) {\n                

Blogs

Book Recommendations