Comment ajuster le point d'ancrage d'un CALayer lorsque la mise en page automatique est utilisée?

161

Remarque : les choses ont évolué depuis que cette question a été posée; voir ici pour un bon aperçu récent.


Avant la mise en page automatique, vous pouviez modifier le point d'ancrage du calque d'une vue sans déplacer la vue en stockant le cadre, en définissant le point d'ancrage et en restaurant le cadre.

Dans un monde de mise en page automatique, nous ne définissons plus de cadres, mais les contraintes ne semblent pas à la hauteur de la tâche d'ajuster la position d'une vue à l'endroit où nous le souhaitons. Vous pouvez pirater les contraintes pour repositionner votre vue, mais lors de la rotation ou d'autres événements de redimensionnement, celles-ci redeviennent invalides.

L'idée brillante suivante ne fonctionne pas car elle crée une "Association incorrecte des attributs de mise en page (gauche et largeur)":

layerView.layer.anchorPoint = CGPointMake(1.0, 0.5);
// Some other size-related constraints here which all work fine...
[self.view addConstraint:
    [NSLayoutConstraint constraintWithItem:layerView
                                 attribute:NSLayoutAttributeLeft
                                 relatedBy:NSLayoutRelationEqual 
                                    toItem:layerView 
                                 attribute:NSLayoutAttributeWidth 
                                multiplier:0.5 
                                  constant:20.0]];

Mon intention ici était de définir le bord gauche de layerView, la vue avec le point d'ancrage ajusté, à la moitié de sa largeur plus 20 (la distance que je veux insérer à partir du bord gauche de la vue supervisée).

Est-il possible de changer le point d'ancrage, sans changer l'emplacement d'une vue, dans une vue mise en page avec mise en page automatique? Dois-je utiliser des valeurs codées en dur et modifier la contrainte à chaque rotation? J'espère que non.

J'ai besoin de changer le point d'ancrage pour que lorsque j'applique une transformation à la vue, j'obtienne l'effet visuel correct.

jrturton
la source
Compte tenu de ce que vous avez ici, il semble que vous allez de toute façon vous retrouver avec des mises en page ambiguës même si le code ci-dessus fonctionne. Comment layerViewconnaît sa largeur? S'en tient-il à autre chose?
John Estropia
Cela est couvert dans le //Size-related constraints that work fine- la largeur et la hauteur de la vue du calque sont dérivées de celles de la super-vue.
jrturton

Réponses:

361

[EDIT: Attention: toute la discussion qui s'ensuivra sera peut-être dépassée ou du moins fortement atténuée par iOS 8, qui ne fera peut-être plus l'erreur de déclencher la mise en page au moment où une transformation de vue est appliquée.]

Mise en page automatique et voir les transformations

La mise en page automatique ne fonctionne pas du tout bien avec les transformations de vue. La raison, pour autant que je sache, est que vous n'êtes pas censé jouer avec le cadre d'une vue qui a une transformation (autre que la transformation d'identité par défaut) - mais c'est exactement ce que fait la mise en page automatique. La façon dont la mise en page automatique fonctionne est que dans layoutSubviewsle runtime vient se précipiter à travers toutes les contraintes et définir les cadres de toutes les vues en conséquence.

En d'autres termes, les contraintes ne sont pas magiques; ils ne sont qu'une liste de choses à faire. layoutSubviewsest l'endroit où la liste de tâches se fait. Et il le fait en définissant des cadres.

Je ne peux pas m'empêcher de considérer cela comme un bug. Si j'applique cette transformation à une vue:

v.transform = CGAffineTransformMakeScale(0.5,0.5);

Je m'attends à voir la vue apparaître avec son centre au même endroit qu'avant et à la moitié de sa taille. Mais selon ses contraintes, ce n'est peut-être pas du tout ce que je vois.

[En fait, il y a une deuxième surprise ici: appliquer une transformation à une vue déclenche immédiatement la mise en page. Cela me semble être un autre bug. Ou peut-être que c'est le cœur du premier bug. Ce à quoi je m'attendrais, c'est de pouvoir m'en tirer avec une transformation au moins jusqu'au moment de la mise en page, par exemple, l'appareil est tourné - tout comme je peux m'en tirer avec une animation d'image jusqu'au moment de la mise en page. Mais en fait, le temps de mise en page est immédiat, ce qui semble tout simplement faux.]

Solution 1: aucune contrainte

Une solution actuelle est, si je vais appliquer une transformation semi-permanente à une vue (et pas simplement la remuer temporairement d'une manière ou d'une autre), pour supprimer toutes les contraintes qui l'affectent. Malheureusement, cela provoque généralement la disparition de la vue de l'écran, car la mise en page automatique a toujours lieu, et il n'y a maintenant aucune contrainte pour nous dire où placer la vue. Donc, en plus de supprimer les contraintes, j'ai défini la vue translatesAutoresizingMaskIntoConstraintssur OUI. La vue fonctionne désormais à l'ancienne, sans être affectée par la mise en page automatique. (Il est évidemment affecté par la mise en page automatique, mais les contraintes implicites de masque de redimensionnement automatique font que son comportement est exactement comme il l'était avant la mise en page automatique.)

Solution 2: utilisez uniquement les contraintes appropriées

Si cela semble un peu drastique, une autre solution consiste à définir les contraintes pour qu'elles fonctionnent correctement avec une transformation prévue. Si une vue est dimensionnée uniquement par sa largeur et sa hauteur internes fixes, et positionnée uniquement par son centre, par exemple, ma transformation d'échelle fonctionnera comme prévu. Dans ce code, je supprime les contraintes existantes sur une sous-vue ( otherView) et les remplace par ces quatre contraintes, en lui donnant une largeur et une hauteur fixes et en l'épinglant uniquement par son centre. Après cela, ma transformation d'échelle fonctionne:

NSMutableArray* cons = [NSMutableArray array];
for (NSLayoutConstraint* con in self.view.constraints)
    if (con.firstItem == self.otherView || con.secondItem == self.otherView)
        [cons addObject:con];

[self.view removeConstraints:cons];
[self.otherView removeConstraints:self.otherView.constraints];
[self.view addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:self.otherView.center.x]];
[self.view addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:self.otherView.center.y]];
[self.otherView addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeWidth relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.width]];
[self.otherView addConstraint:
 [NSLayoutConstraint constraintWithItem:self.otherView attribute:NSLayoutAttributeHeight relatedBy:0 toItem:nil attribute:0 multiplier:1 constant:self.otherView.bounds.size.height]];

Le résultat est que si vous n'avez aucune contrainte qui affecte le cadre d'une vue, la mise en page automatique ne touchera pas le cadre de la vue - ce qui est exactement ce que vous recherchez lorsqu'une transformation est impliquée.

Solution 3: utiliser une sous-vue

Le problème avec les deux solutions ci-dessus est que nous perdons les avantages des contraintes pour positionner notre point de vue. Voici donc une solution qui résout ce problème. Commencez par une vue invisible dont le travail consiste uniquement à agir en tant qu'hôte et utilisez des contraintes pour la positionner. À l'intérieur, mettez la vue réelle sous forme de sous-vue. Utilisez des contraintes pour positionner la sous-vue dans la vue hôte, mais limitez ces contraintes aux contraintes qui ne riposteront pas lorsque nous appliquerons une transformation.

Voici une illustration:

entrez la description de l'image ici

La vue blanche est la vue de l'hôte; vous êtes censé prétendre qu'il est transparent et donc invisible. La vue rouge est sa sous-vue, positionnée en épinglant son centre au centre de la vue hôte. Maintenant, nous pouvons mettre à l'échelle et faire pivoter la vue rouge autour de son centre sans aucun problème, et en effet l'illustration montre que nous l'avons fait:

self.otherView.transform = CGAffineTransformScale(self.otherView.transform, 0.5, 0.5);
self.otherView.transform = CGAffineTransformRotate(self.otherView.transform, M_PI/8.0);

Et pendant ce temps, les contraintes sur la vue de l'hôte le maintiennent au bon endroit lorsque nous faisons pivoter l'appareil.

Solution 4: utilisez plutôt les transformations de couche

Au lieu des transformations de vue, utilisez des transformations de couche, qui ne déclenchent pas la mise en page et ne provoquent donc pas de conflit immédiat avec les contraintes.

Par exemple, cette simple animation de vue "throb" peut bien être interrompue lors de la mise en page automatique:

[UIView animateWithDuration:0.3 delay:0
                    options:UIViewAnimationOptionAutoreverse
                 animations:^{
    v.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
    v.transform = CGAffineTransformIdentity;
}];

Même si à la fin il n'y a pas eu de changement dans la taille de la vue, le simple fait de définir sa transformmise en page provoque la mise en page et les contraintes peuvent faire sauter la vue. (Est-ce que cela ressemble à un bug ou quoi?) Mais si nous faisons la même chose avec Core Animation (en utilisant un CABasicAnimation et en appliquant l'animation au calque de la vue), la mise en page ne se produit pas et cela fonctionne bien:

CABasicAnimation* ba = [CABasicAnimation animationWithKeyPath:@"transform"];
ba.autoreverses = YES;
ba.duration = 0.3;
ba.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1, 1.1, 1)];
[v.layer addAnimation:ba forKey:nil];
mat
la source
3
La transformation n'est pas ce qui me cause le problème, il suffit de définir le nouveau point d'ancrage. Je vais essayer de supprimer les contraintes et d'essayer le masque de redimensionnement automatique.
jrturton
1
Je ne comprends pas quelque chose ici. Solution 4: Lorsqu'une transformation est appliquée à une couche de support, la transformation affecte également la mise en page automatique, car la propriété de transformation de la vue fait référence à la transformation de la couche de support
Luca Bartoletti
1
@Andy au contraire, sur iOS 8, le problème a complètement disparu et toute ma réponse est inutile
matt
1
@Sunkas j'ai répondu dans votre numéro.
Ben Sinclair
2
Sur iOS 8, la transformation de couche déclenche également la disposition automatique
CarmeloS
41

J'ai eu un Isuue similaire et je viens d'entendre Back de l'équipe Autolayout d'Apple. Ils suggèrent d'utiliser l'approche de la vue conteneur suggérée par Matt, mais ils créent une sous-classe d'UIView pour écraser la mise en page Sous-vues et y appliquer un code de mise en page personnalisé - Cela fonctionne comme un charme

Le fichier d'en-tête ressemble à cela afin que vous puissiez lier votre sous-vue de choix directement à partir d'Interface Builder

#import <UIKit/UIKit.h>

@interface BugFixContainerView : UIView
@property(nonatomic,strong) IBOutlet UIImageView *knobImageView;
@end

et le fichier m applique le code spécial comme ça:

#import "BugFixContainerView.h"

@implementation BugFixContainerView
- (void)layoutSubviews
{
    static CGPoint fixCenter = {0};
    [super layoutSubviews];
    if (CGPointEqualToPoint(fixCenter, CGPointZero)) {
        fixCenter = [self.knobImageView center];
    } else {
        self.knobImageView.center = fixCenter;
    }
}
@end

Comme vous pouvez le voir, il saisit le point central de la vue lors de son premier appel et réutilise cette position dans d'autres appels afin de placer la vue en conséquence. Cela écrase le code de mise en page automatique dans ce sens, qu'il a lieu après [super layoutSubviews]; qui contient le code de mise en page automatique.

Ainsi, il n'est plus nécessaire d'éviter la mise en page automatique, mais vous pouvez créer votre propre mise en page automatique lorsque les comportements par défaut ne sont plus appropriés. Bien sûr, vous pouvez appliquer des choses beaucoup plus compliquées que ce qui est dans cet exemple, mais c'était tout ce dont j'avais besoin car mon application ne peut utiliser que le mode portrait.

sensslen
la source
5
Merci d'avoir posté ceci. Depuis que j'ai écrit ma réponse, j'ai définitivement compris la valeur de la priorité layoutSubviews. J'ajouterais simplement, cependant, qu'Apple ici ne fait que vous faire faire ce que je pense qu'ils auraient dû faire (dans leur implémentation d'Autolayout) tout au long.
mat
Vous avez raison, mais je suppose que cela n'est pas vraiment lié au problème lui-même. Puisqu'ils ont fourni le code ci-dessus, j'en suis satisfait!
sensslen
4
Il est triste et absurde que l'équipe de mise en page automatique recommande elle-même de créer une sous-classe de conteneur personnalisée juste pour contourner la façon dont la mise en page automatique rompt le comportement intuitif et de longue date dans le système de vue.
algal
8

Je trouve un moyen simple. Et cela fonctionne sur iOS 8 et iOS 9.

Comme pour ajuster anchorPoint lorsque vous utilisez une mise en page basée sur un cadre:

let oldFrame = layerView.frame
layerView.layer.anchorPoint = newAnchorPoint
layerView.frame = oldFrame

Lorsque vous ajustez l'ancre de la vue avec la mise en page automatique, vous faites la même chose mais de manière contrainte. Lorsque anchorPoint passe de (0,5, 0,5) à (1, 0,5), le layerView se déplacera à gauche avec une distance égale à la moitié de la longueur de la largeur de la vue, vous devez donc compenser cela.

Je ne comprends pas la contrainte de la question, alors supposons que vous ajoutiez une contrainte centerX relative à superView centerX avec une constante: layerView.centerX = superView.centerX + constant

layerView.layer.anchorPoint = CGPoint(1, 0.5)
let centerXConstraint = .....
centerXConstraint.constant = centerXConstraint.constant + layerView.bounds.size.width/2
seedante
la source
Verre de la meilleure bière pour vous mon ami. C'est une solution de contournement si fine: D
Błażej
3

Si vous utilisez la mise en page automatique, je ne vois pas comment la définition manuelle de la position servira à long terme, car la mise en page automatique finira par écraser la valeur de position que vous avez définie lors du calcul de sa propre mise en page.

Il faut plutôt modifier les contraintes de mise en page elles-mêmes pour compenser les changements produits en définissant le point d'ancrage. La fonction suivante fait cela pour les vues non transformées.

/**
  Set the anchorPoint of view without changing is perceived position.

 @param view view whose anchorPoint we will mutate
 @param anchorPoint new anchorPoint of the view in unit coords (e.g., {0.5,1.0})
 @param xConstraint an NSLayoutConstraint whose constant property adjust's view x.center
 @param yConstraint an NSLayoutConstraint whose constant property adjust's view y.center

  As multiple constraints can contribute to determining a view's center, the user of this
 function must specify which constraint they want modified in order to compensate for the
 modification in anchorPoint
 */
void SetViewAnchorPointMotionlesslyUpdatingConstraints(UIView * view,CGPoint anchorPoint,
                                                       NSLayoutConstraint * xConstraint,
                                                       NSLayoutConstraint * yConstraint)
{
  // assert: old and new anchorPoint are in view's unit coords
  CGPoint const oldAnchorPoint = view.layer.anchorPoint;
  CGPoint const newAnchorPoint = anchorPoint;

  // Calculate anchorPoints in view's absolute coords
  CGPoint const oldPoint = CGPointMake(view.bounds.size.width * oldAnchorPoint.x,
                                 view.bounds.size.height * oldAnchorPoint.y);
  CGPoint const newPoint = CGPointMake(view.bounds.size.width * newAnchorPoint.x,
                                 view.bounds.size.height * newAnchorPoint.y);

  // Calculate the delta between the anchorPoints
  CGPoint const delta = CGPointMake(newPoint.x-oldPoint.x, newPoint.y-oldPoint.y);

  // get the x & y constraints constants which were contributing to the current
  // view's position, and whose constant properties we will tweak to adjust its position
  CGFloat const oldXConstraintConstant = xConstraint.constant;
  CGFloat const oldYConstraintConstant = yConstraint.constant;

  // calculate new values for the x & y constraints, from the delta in anchorPoint
  // when autolayout recalculates the layout from the modified constraints,
  // it will set a new view.center that compensates for the affect of the anchorPoint
  CGFloat const newXConstraintConstant = oldXConstraintConstant + delta.x;
  CGFloat const newYConstraintConstant = oldYConstraintConstant + delta.y;

  view.layer.anchorPoint = newAnchorPoint;
  xConstraint.constant = newXConstraintConstant;
  yConstraint.constant = newYConstraintConstant;
  [view setNeedsLayout];
}

J'admets que ce n'est probablement pas tout ce que vous espériez, car généralement, la seule raison pour laquelle vous souhaitez modifier le point d'ancrage est de définir une transformation. Cela nécessiterait une fonction plus complexe qui met à jour les contraintes de mise en page pour refléter tous les modifications de cadre qui pourraient être causées par la propriété transform elle-même. C'est délicat car les transformations peuvent faire beaucoup pour le cadre. Une transformation de mise à l'échelle ou de rotation rendrait le cadre plus grand, nous aurions donc besoin de mettre à jour toutes les contraintes de largeur ou de hauteur, etc.

Si vous n'utilisez la transformation que pour une animation temporaire, alors ce qui précède peut suffire car je ne pense pas que la mise en page automatique empêchera l'animation en vol de présenter des images qui représentent des violations purement transitoires des contraintes.

algue
la source
J'utilise des transformations malheureusement. La définition de la position est correcte car la méthode est appelée chaque fois que la vue est mise en page, afin qu'elle ne soit pas écrasée. Je pense que la substitution de layoutRect pourrait être une meilleure solution (mentionnée dans l'une de vos réponses précédentes) mais je ne l'ai pas encore essayée.
jrturton
Au fait, voici un banc d'essai que j'utilise. Ce serait formidable si quelqu'un découvrait
algal
Cela devrait être la réponse acceptée. La réponse de @ matt ne concerne même pas le point d'ancrage.
Iulian Onofrei
3

tl: dr: Vous pouvez créer une sortie pour l'une des contraintes afin qu'elle puisse être supprimée et ajoutée à nouveau.


J'ai créé un nouveau projet et ajouté une vue avec une taille fixe au centre. Les contraintes sont présentées dans l'image ci-dessous.

Les contraintes pour le plus petit exemple auquel je puisse penser.

Ensuite, j'ai ajouté une sortie pour la vue qui va tourner et pour la contrainte d'alignement centre x.

@property (weak, nonatomic) IBOutlet UIView *rotatingView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *xAlignmentContstraint;

Plus tard, viewDidAppearje calcule le nouveau point d'ancrage

UIView *view = self.rotatingView;

CGPoint rotationPoint = // The point I'm rotating around... (only X differs)
CGPoint anchorPoint = CGPointMake((rotationPoint.x-CGRectGetMinX(view.frame))/CGRectGetWidth(view.frame),
                                  (rotationPoint.y-CGRectGetMinY(view.frame))/CGRectGetHeight(view.bounds));

CGFloat xCenterDifference = rotationPoint.x-CGRectGetMidX(view.frame);

view.layer.anchorPoint = anchorPoint;

Ensuite, je supprime la contrainte pour laquelle j'ai une prise, j'en crée une nouvelle qui est décalée et je la rajoute à nouveau. Après cela, je dis à la vue avec la contrainte modifiée qu'elle doit mettre à jour les contraintes.

[self.view removeConstraint:self.xAlignmentContstraint];
self.xAlignmentContstraint = [NSLayoutConstraint constraintWithItem:self.rotatingView
                                                          attribute:NSLayoutAttributeCenterX
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:self.view
                                                          attribute:NSLayoutAttributeCenterX
                                                         multiplier:1.0
                                                           constant:xDiff];
[self.view addConstraint:self.xAlignmentContstraint];
[self.view needsUpdateConstraints];

Enfin, je viens d'ajouter l'animation de rotation à la vue rotative.

CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotate.toValue = @(-M_PI_2);
rotate.autoreverses = YES;
rotate.repeatCount = INFINITY;
rotate.duration = 1.0;
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 

[view.layer addAnimation:rotate forKey:@"myRotationAnimation"];

Le calque en rotation semble rester centré (ce qu'il devrait) même lors de la rotation de l'appareil ou de la mise à jour des contraintes. La nouvelle contrainte et le point d'ancrage modifié s'annulent visuellement.

David Rönnqvist
la source
1
Cela fonctionnerait pour ma situation spécifique, il ne semble pas qu'une bonne solution générale soit possible.
jrturton
1

Ma solution actuelle consiste à ajuster manuellement la position du calque dans viewDidLayoutSubviews. Ce code pourrait également être utilisé danslayoutSubviews pour une sous-classe de vue, mais dans mon cas, ma vue est une vue de niveau supérieur dans un contrôleur de vue, donc cela signifiait que je n'avais pas à créer une sous-classe UIView.

Cela semble être trop d’efforts, alors d’autres réponses sont les bienvenues.

-(void)viewDidLayoutSubviews
{
    for (UIView *view in self.view.subviews)
    {
        CGPoint anchorPoint = view.layer.anchorPoint;
        // We're only interested in views with a non-standard anchor point
        if (!CGPointEqualToPoint(CGPointMake(0.5, 0.5),anchorPoint))
        {
            CGFloat xDifference = anchorPoint.x - 0.5;
            CGFloat yDifference = anchorPoint.y - 0.5;
            CGPoint currentPosition = view.layer.position;

            // Use transforms if we can, otherwise manually calculate the frame change
            // Assuming a transform is in use since we are changing the anchor point. 
            if (CATransform3DIsAffine(view.layer.transform))
            {
                CGAffineTransform current = CATransform3DGetAffineTransform(view.layer.transform);
                CGAffineTransform invert = CGAffineTransformInvert(current);
                currentPosition = CGPointApplyAffineTransform(currentPosition, invert);
                currentPosition.x += (view.bounds.size.width * xDifference);
                currentPosition.y += (view.bounds.size.height * yDifference);
                currentPosition = CGPointApplyAffineTransform(currentPosition, current);
            }
            else
            {
                CGFloat transformXRatio = view.bounds.size.width / view.frame.size.width;

                if (xDifference < 0)
                    transformXRatio = 1.0/transformXRatio;

                CGFloat transformYRatio = view.bounds.size.height / view.frame.size.height;
                if (yDifference < 0)
                    transformYRatio = 1.0/transformYRatio;

                currentPosition.x += (view.bounds.size.width * xDifference) * transformXRatio;
                currentPosition.y += (view.bounds.size.height * yDifference) * transformYRatio;
            }
            view.layer.position = currentPosition;
        }

    }
}
jrturton
la source
1

Inspiré de la réponse de mon mat, j'ai décidé d'essayer une approche différente. Une vue de conteneur, avec des contraintes appliquées de manière appropriée, peut être utilisée. La vue avec le point d'ancrage modifié peut ensuite être placée dans la vue du conteneur, en utilisant des masques de redimensionnement automatique et des paramètres de cadre explicites, comme dans le mauvais vieux temps.

Cela fonctionne un régal, pour ma situation de toute façon. Les vues sont configurées ici dans viewDidLoad:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    UIView *redView = [UIView new];
    redView.translatesAutoresizingMaskIntoConstraints = NO;
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[redView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(redView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[redView]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(redView)]];
    self.redView = redView;

    UIView *greenView = [UIView new];
    greenView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    greenView.layer.anchorPoint = CGPointMake(1.0, 0.5);
    greenView.frame = redView.bounds;
    greenView.backgroundColor = [UIColor greenColor];
    [redView addSubview:greenView];
    self.greenView = greenView;

    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = 0.005;
    self.redView.layer.sublayerTransform = perspective;
}

Peu importe que les cadres de la vue rouge soient à zéro à ce stade, en raison du masque de redimensionnement automatique sur la vue verte.

J'ai ajouté une transformation de rotation sur une méthode d'action, et voici le résultat:

entrez la description de l'image ici

Il semblait se perdre lors de la rotation de l'appareil, j'ai donc ajouté ceci à la méthode viewDidLayoutSubviews:

-(void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    CATransform3D transform = self.greenView.layer.transform;
    self.greenView.layer.transform = CATransform3DIdentity;
    self.greenView.frame = self.redView.bounds;
    self.greenView.layer.transform = transform;
    [CATransaction commit];

}
jrturton
la source
Je me demande si dans votre cas, il n'aurait pas été plus facile de simplement laisser view.layertranquille et de faire tout votre travail dans une sous-couche de view.layer. En d'autres termes, la vue ne serait qu'un hôte, et toutes les transformations de dessin et de sous-couche, etc., seraient d'un niveau inférieur, non affectées par les contraintes.
mat
1
D'accord, inspiré par votre solution de sous-vue, j'ai ajouté cela à mon essai! Merci de m'avoir laissé jouer dans votre bac à sable ...
mat
0

Je pense que vous allez à l'encontre de l'objectif de la mise en page automatique avec cette méthode. Vous avez mentionné que la largeur et le bord droit dépendent de la vue supervisée, alors pourquoi ne pas simplement ajouter des contraintes le long de cette ligne de pensée?

Perdez le paradigme anchorPoint / transform et essayez:

[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
                             attribute:NSLayoutAttributeRight
                             relatedBy:NSLayoutRelationEqual 
                                toItem:self.view 
                             attribute:NSLayoutAttributeWidth 
                            multiplier:1.0f
                              constant:-somePadding]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:layerView
                             attribute:NSLayoutAttributeWidth
                             relatedBy:NSLayoutRelationEqual 
                                toItem:someViewWeDependTheWidthOn
                             attribute:NSLayoutAttributeWidth 
                            multiplier:0.5f // because you want it to be half of someViewWeDependTheWidthOn
                              constant:-20.0f]]; // your 20pt offset from the left

La NSLayoutAttributeRightcontrainte signifie exactement comme anchorPoint = CGPointMake(1.0, 0.5), et la NSLayoutAttributeWidthcontrainte est à peu près équivalente à votre code précédent NSLayoutAttributeLeft.

John Estropia
la source
Merci pour votre réponse, mais je ne peux pas "perdre le paradigme point d'ancrage / transformation". J'ai besoin d'appliquer une transformation et d'ajuster le point d'ancrage pour effectuer la transformation correcte.
jrturton
Pour expliquer davantage, dans ce cas précis, la vue transformée devra parfois se replier dans l'écran en tournant le long de l'axe Y sur son bord extrême droit. Par conséquent, le point d'ancrage doit être déplacé. Je recherche également une solution générale.
jrturton
Bien sûr, vous pouvez toujours garder le anchorPoint, mon point est que vous ne devriez pas l'utiliser pour les mesures. Le système de mise en page automatique UIView doit être indépendant des transformations CALayer. Donc UIView: mise en page, CALayer: apparence / animations
John Estropia
1
Cela devrait l'être, mais ce n'est pas le cas. Avez-vous réellement essayé cela? La modification du point d'ancrage décale la position du calque une fois que la disposition automatique a terminé son travail. Votre solution ne fonctionne pas avec un point d'ancrage modifié.
jrturton
Oui, je peux utiliser différents points d'ancrage pour mes vues présentées avec des contraintes. Votre réponse viewDidLayoutSubviewsdevrait résoudre ce problème; positionva toujours avec anchorPoint. Ma réponse montre simplement comment définir la contrainte de la transformation d'identité.
John Estropia
0

Cette question et ces réponses m'ont inspiré à résoudre mes propres problèmes avec la mise en page automatique et la mise à l'échelle, mais avec les vues de défilement. J'ai créé un exemple de ma solution sur github:

https://github.com/hansdesmedt/AutoLayout-scrollview-scale

Ceci est un exemple d'un UIScrollView avec une pagination personnalisée entièrement réalisée dans AutoLayout et est évolutif (CATransform3DMakeScale) avec un appui long et appuyez pour zoomer. Compatible iOS 6 et 7.

Hans
la source
0

C'est un gros sujet et je n'ai pas lu tous les commentaires mais j'étais confronté au même problème.

J'avais une vue de XIB avec mise en page automatique. Et je voulais mettre à jour sa propriété de transformation. L'intégration de la vue dans une vue de conteneur ne résout pas mon problème car la mise en page automatique agissait de manière étrange sur la vue de conteneur. C'est pourquoi je viens d'ajouter une deuxième vue de conteneur pour contenir la vue de conteneur qui contient ma vue et y appliquait des transformations.

plamkata__
la source
0

tl; dr Supposons que vous ayez changé le point d'ancrage en (0, 0). Le point d'ancrage est maintenant en haut à gauche. Chaque fois que vous voyez le centre des mots dans la mise en page automatique, vous devriez penser en haut à gauche .

Lorsque vous ajustez votre point d'ancrage, vous changez simplement la sémantique de la mise en page automatique. La mise en page automatique n'interférera pas avec votre point d'ancrage ni vice versa. Si vous ne comprenez pas cela, vous allez passer un mauvais moment .


Exemple:

Figure A. Aucune modification du point d'ancrage

#Before changing anchor point to top-left
view.size == superview.size
view.center == superview.center

Figure B.Le point d'ancrage a été changé en haut à gauche

view.layer.anchorPoint = CGPointMake(0, 0)
view.size == superview.size
view.center == superview.topLeft                <----- L0-0K, center is now top-left

Les figures A et B se ressemblent exactement. Rien n'a changé. Juste la définition de quoi centre se réfère a changé.

seo
la source
C'est bien beau mais ne répond pas à la question de savoir quoi faire lorsque vos contraintes ne sont pas liées au centre.
jrturton