web-dev-qa-db-fra.com

ios10: la largeur/hauteur du cadre de viewDidLoad n'est pas initialisée correctement

Depuis la mise à niveau vers XCode8 GM et ios10, toutes mes vues créées via Interface Builder ne sont correctement initialisées que beaucoup plus tard que prévu. Cela signifie que dans viewDidLoad, cellForRowAtIndexPath, viewWillAppear, etc., la taille de la trame est définie sur {1000,1000} pour chaque vue. À un moment donné, ils semblent corriger, mais c'est beaucoup trop tard. 

Le premier problème rencontré est celui de l'arrondi des angles arrondis:

view.layer.cornerRadius = view.frame.size.width/2

D'autres problèmes se manifestent pour tout ce qui repose sur la taille de la trame pour effectuer des calculs dans le code. 

cellForRowAtIndexPath 

Pour cellForRowAtIndexPath, la taille de l’image échoue lors de l’affichage initial de la table, mais fonctionne ensuite correctement une fois que vous la faites défiler. willDisplayCell: forRowAtIndexPath n'a pas non plus la taille d'image correcte. 

J'ai codé en dur quelques valeurs mais, évidemment, il s'agit d'une très mauvaise pratique du code, ainsi que de nombreux projets.

Existe-t-il un moyen ou un endroit pour obtenir des tailles de cadre correctes?

MODIFIER

J'ai découvert que l'utilisation de la contrainte hauteur/largeur au lieu de la hauteur de la hauteur du cadre est plus fiable. Cela peut ajouter la surcharge de beaucoup de nouveaux IBOutlets pour lier les contraintes hauteur/largeur sur les éléments.

Pour l'instant, j'ai créé une catégorie UIView qui me permet d'accéder aux contraintes hauteur/largeur d'une vue directement sans les IBOutlets. Pour une utilisation minimale, la petite boucle ne devrait pas être un gros problème. Résultats non garantis pour les articles IB sans les contraintes de largeur/hauteur créées à l’évidence. Retourne probablement 0 au mieux pour la constante, ou pire. De plus, si vous n'avez pas de contrainte hauteur/largeur et que votre vue est redimensionnée de manière dynamique en fonction des contraintes de début/fin, cela ne fonctionnera pas.

-viewDidLoad semble avoir une taille d'image correcte, mais entraîne souvent une modification visuelle de l'interface utilisateur si vous apportez des modifications ici.

UIView + WidthHeightConstraints.h

@interface UIView (WidthHeightConstraints)

-(NSLayoutConstraint*)widthConstraint;
-(NSLayoutConstraint*)heightConstraint;
-(NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute;

@end

UIView + WidthHeightConstraints.m

#import "UIView+WidthHeightConstraints.h"

@implementation UIView (WidthHeightConstraints)

-(NSLayoutConstraint*)widthConstraint{
    return [self constraintForAttribute:NSLayoutAttributeWidth];
}
-(NSLayoutConstraint*)heightConstraint {
    return [self constraintForAttribute:NSLayoutAttributeHeight];
}
-(NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute {
    NSLayoutConstraint *targetConstraint = nil;
    for (NSLayoutConstraint *constraint in self.constraints) {
        if (constraint.firstAttribute == attribute) {
            targetConstraint = constraint;
            break;
        }
    }
    return targetConstraint;
}

@end

EDIT 2

La catégorie ci-dessus ne s'est avérée que partiellement efficace. Principalement parce que ios semble ajouter automatiquement quelques doublons de contrainte de hauteur/largeur supplémentaires, de type NSContentSizeLayoutConstraint, dont la taille est en réalité différente de celle de la contrainte normale. La classe NSContentSizeLayoutConstraint est également une classe privée, donc je ne peux pas faire isKindOfClass pour les filtrer. Je n'ai pas encore trouvé de moyen de tester efficacement ces tests. C'est énervant.

40
Miro

Les problèmes les plus courants que vous décrivez apparaissent uniquement dans iOS 10 et peuvent être résolus en ajoutant cette ligne (si nécessaire):

self.view.layoutIfNeeded()

juste au-dessus du code, responsable de la modification de la contrainte, layer.cornerRadius, etc.

OU 

placez votre code associé aux cadres/couches dans la méthode viewDidLayoutSubviews():

override func viewDidLayoutSubviews() {

    super.viewDidLayoutSubviews()
    view.layer.cornerRadius = self.myView.frame.size.width/2
    view.clipsToBounds = true

    ... etc
}
29
pedrouan

Nous avons créé un radar (28342777 (marqué comme duplicata pour 28221021 mais ouvert)) pour le même problème et la réponse obtenue est la suivante:

"Merci d'avoir signalé le problème. Pourrions-nous obtenir plus d'informations sur la vue d'image de profil? Dans Xcode 8, une vue entièrement contrainte et non égarée n'enregistre plus de cadre pour minimiser les différences et prend en charge la mise à jour automatique des cadres dans IB. Au moment de l'exécution , ces vues sont décodées avec une taille d’espace réservé de 1 000 x 1 000, mais sont résolues après la première mise en page. Cette image peut-elle être affectée avant la mise en page initiale et l’attribution de l’image à la vue de l’image après la première mise en page est traitée? nous analysons plus loin, merci! "

À l'heure actuelle, nous leur avons fourni l'exemple de projet. Mes observations:

  • Le problème que nous avions l'habitude de se produire pour les XIB convertis de Xcode 7.x à Xcode 8.x
  • Si nous rompons intentionnellement la contrainte dans XIB, viewDidLoad obtiendra la hauteur et la largeur attendues, et non 1000x1000.
  • Pour nous, c’était un UIImageView sur lequel nous appliquions des couches pour le rendre circulaire et en utilisant masksToBounds. Si nous définissons masksToBounds = NO, tout fonctionnait correctement. 

Bien que Apple affirme qu'il s'agira d'un standard de Xcode 8 selon lequel les vues seront définies sur 1000x1000, le comportement ne semble pas être cohérent. 

J'espère que cela t'aides. 

16
Bhavik Bhagat

J'ai rencontré le même problème et j'essaie de le résoudre sans chance en me référant aux suggestions ci-dessus.

Il semble que ce soit un bug à résoudre pour Apple. Je trouve enfin une solution en changeant pour enregistrer mon document XIB au format Xcode 7.x et mon interface utilisateur à la normale.

Jusqu'à ce qu'Apple publie un correctif, je ne veux pas passer mon temps à le pirater.

 enter image description here  enter image description here

6
allen

Qu'en est-il de faire ceci: 

- (NSLayoutConstraint*)widthConstraint{
    return [self constraintForAttribute:NSLayoutAttributeWidth];
}

- (NSLayoutConstraint*)heightConstraint {
    return [self constraintForAttribute:NSLayoutAttributeHeight];
}

- (NSLayoutConstraint*)constraintForAttribute:(NSLayoutAttribute)attribute {
    NSLayoutConstraint *targetConstraint = nil;
    for (NSLayoutConstraint *constraint in self.constraints) {
        //NSLog(@"constraint: %@", constraint);
        if (![constraint isKindOfClass:NSClassFromString(@"NSContentSizeLayoutConstraint")]) {
            if (constraint.firstAttribute == attribute) {
                targetConstraint = constraint;
                break;
            }
        }
    }
    return targetConstraint;
}
2
942v

J'avais exactement le même problème. J'avais des sous-classes UITableViewCell personnalisées et j'utilisais clipsToBounds = YES et self.iconView.layer.cornerRadius = self.iconView.frame.size.width/2 pour me donner une image circulaire. J'ai essayé d'appeler ma méthode de configuration de cellule à partir de cellForRowAtIndexPath et willDisplayCell et aucune n'a fonctionné.

Voici ce qui fonctionne:
Déplacez votre code de superposition dans la méthode -layoutSubviews de la cellule comme ceci:

-(void)layoutSubviews {
    [super layoutSubviews];
    self.iconView.clipsToBounds = YES;
    self.iconView.layer.cornerRadius = self.iconView.frame.size.width/2;
} 

Après cela, les images devraient se charger correctement et votre code de superposition devrait également fonctionner.

2
TylerJames

Vous ne devez jamais vous fier au moment où une vue est affichée. Si cela a fonctionné pour vous avant, alors par pure chance. Il y a très peu de garanties à ce sujet dans UIKit. Si vous comptez sur quelque chose qui s'adapte à la taille de votre vue, la meilleure chose à faire est de remplacer layoutSubviews dans cette vue et d'ajuster votre contenu à cet endroit.

Même après que votre vue soit entièrement restituée à l'écran, il y a encore tellement de conditions qui peuvent modifier la taille de la vue. Par exemple: barre d'état double hauteur, multitâche sur iPad, rotation de périphérique, pour n'en nommer que quelques-uns. Il n’est donc pas judicieux d’apporter des modifications de mise en page liées aux images à un moment donné.

2
Michael Ochs

Seul Update frame dans votre boîte de dialogue de autolayout .  enter image description here

0
neha mishra