web-dev-qa-db-fra.com

Décalage incorrect de UIScrollView avec la mise en page automatique

J'ai une configuration de vue assez simple:

Une UIViewController, avec un enfant UIScrollView et une UIImageView dans cette UIScrollView. J'ai défini la UIImageView avec une hauteur suffisante pour sortir de la zone visible (c'est-à-dire supérieure à 1024pt), et j'ai défini la contrainte Bottom space to superview de ma UIImageView valeur positive fixe (20 par exemple).

project layout

L’ensemble de la configuration fonctionne comme prévu, l’image défilant bien dans son parent . Sauf lorsque la vue défile (l’effet est plus visible si vous faites défiler jusqu’au bas de la vue), puis disparaît et réapparaît (vous avez dans une autre vue et est revenue), la valeur de défilement est restaurée, mais le contenu de la vue de défilement est déplacé vers la partie supérieure extérieure de la vue parente.

Ce n’est pas simple à expliquer, je vais essayer de le dessiner: visual representation of previous paragraph

Si vous souhaitez tester/afficher la source (ou le storyboard, je n'ai pas édité une seule ligne de code). J'ai mis une petite démo sur mon github: https://github.com/guillaume-algis/iOSAutoLayoutScrollView

J'ai lu le iOS 6 changelog et l'explication sur ce sujet particulier, et je pense que c'est la bonne implémentation de la deuxième option (mise en page automatique pure), mais dans ce cas, pourquoi la UIScrollView se comporte-t-elle aussi de manière erratique? Est-ce que je manque quelque chose? 

EDIT: il s'agit exactement du même problème que # 12580434 uiscrollview-autolayout-issue . Les réponses ne sont que des solutions de contournement, car quiconque a trouvé un moyen approprié de résoudre ce problème ou s'agit-il d'un bogue iOS?

EDIT 2: une autre solution de contournement a été trouvée. L'utilisateur l'a laissé dans le même état de défilement (ceci est une amélioration par rapport à la réponse acceptée de 12580434 ):

@interface GAViewController ()

@property CGPoint tempContentOffset;

@end


@implementation GAViewController

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    self.tempContentOffset = self.mainScrollView.contentOffset;
    self.scrollView.contentOffset = CGPointZero;
}

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    self.scrollView.contentOffset = self.tempContentOffset;
}

Cela enregistre essentiellement le décalage dans viewWillAppear, le réinitialise à l'origine et restaure la valeur dans viewDidAppear. Le problème semble se produire entre ces deux appels, mais je ne trouve pas son origine.

40
Guillaume Algis

Oui, quelque chose d'étrange s'est produit avec UIScrollView dans un environnement de pur autolayout. Relisant le notes de version du SDK iOS 6.0 pour la vingtième fois, j'ai constaté que:

Notez que vous pouvez faire en sorte qu'une sous-vue de la vue de défilement semble flotter (et non pas défiler) par-dessus l'autre contenu défilant en créant des contraintes entre la vue et une vue extérieure à la sous-arborescence de la vue de défilement, telle que la vue d'ensemble de la vue.

Solution

Connectez votre sous-vue à la vue extérieure. En d'autres termes, à la vue dans laquelle scrollview est incorporé.

Comme IB ne nous permet pas de définir des contraintes entre imageView et une vue située en dehors de la sous-arborescence de la vue de défilement, telle que la super vue de la vue de défilement, je l’ai déjà faite en code.

- (void)viewDidLoad {
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
    [self.view removeConstraints:[self.view constraints]];
    [self.scrollView removeConstraints:[self.scrollView constraints]];
    [self.imageView removeConstraints:[self.imageView constraints]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_imageView(700)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_imageView(1500)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]];
}

Et vau! Ça marche!

22
Mark Kryzhanouski

Le montage n'a pas fonctionné pour moi. Mais cela a fonctionné:

-(void)viewWillDisappear:(BOOL)animated
{
     [super viewWillDisappear:animated];

     self.tempContentOffset = self.scrollView.contentOffset;
     self.scrollView.contentOffset = CGPointZero;
}

- (void)viewDidLayoutSubviews {
     [super viewDidLayoutSubviews];
     self.scrollView.contentOffset = self.tempContentOffset;
}
10
Pion

Pour moi, je suis allé à l'IB a cliqué sur mon contrôleur de vue qui contient la vue de défilement. Puis je suis allé dans Inspecteur d'attributs -> Contrôleur de vue -> Étendre les bords -> Décocher "Sous les barres du haut" et "Sous les barres du bas".

5
Andy

Solution simple trouvée, il suffit de mettre

[self setAutomaticallyAdjustsScrollViewInsets:NO];

dans votre méthode viewDontLoad de ViewControllers

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.

    [self setAutomaticallyAdjustsScrollViewInsets:NO];
}
2
Purushottam Sain

J'ai eu un problème similaire lors de l'utilisation d'un UIScrollView dans un UIViewController avec un espace supplémentaire supérieur non visible dans Storyboard. La solution pour moi était de décocher la case "Ajuster les encarts de la vue de défilement" dans les propriétés du storyboard ViewController: voir réponse Espace supplémentaire (encart) dans la vue de défilement au moment de l'exécution

0
Maxime T