Come si regola il punto di ancoraggio di un CALayer quando si utilizza il layout automatico?


161

Nota : le cose sono andate avanti da quando è stata posta questa domanda; vedi qui per una buona panoramica recente.


Prima del layout automatico, è possibile modificare il punto di ancoraggio del livello di una vista senza spostare la vista memorizzando la cornice, impostando il punto di ancoraggio e ripristinando la cornice.

In un mondo di layout automatico, non impostiamo più i frame, ma i vincoli non sembrano all'altezza del compito di adattare la posizione di una vista al punto desiderato. È possibile modificare i vincoli per riposizionare la vista, ma in caso di rotazione o altri eventi di ridimensionamento, questi diventano nuovamente non validi.

La seguente brillante idea non funziona in quanto crea un "Accoppiamento non valido di attributi di layout (sinistra e larghezza)":

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]];

La mia intenzione qui era di impostare il bordo sinistro di layerView, la vista con il punto di ancoraggio regolato, a metà della sua larghezza più 20 (la distanza che voglio inserire dal bordo sinistro della superview).

È possibile cambiare il punto di ancoraggio, senza cambiare la posizione di una vista, in una vista strutturata con layout automatico? Devo usare valori hardcoded e modificare il vincolo su ogni rotazione? Spero di no.

Devo cambiare il punto di ancoraggio in modo che quando applico una trasformazione alla vista, ottengo l'effetto visivo corretto.


Dato proprio quello che hai qui sembra che finirai con layout ambigui comunque anche se il codice sopra funziona. Come fa a layerViewsapere la sua larghezza? Sta attaccando il suo lato destro a qualcos'altro?
John Estropia,

Questo è coperto da //Size-related constraints that work fine: la larghezza e l'altezza della vista di livello sono derivate da quella della superview.
jrturton,

Risposte:


361

[MODIFICA: Avviso: l'intera discussione che ne seguirà sarà probabilmente superata o almeno fortemente mitigata da iOS 8, il che potrebbe non commettere più l'errore di innescare il layout nel momento in cui viene applicata una trasformazione della vista.]

Autolayout vs. Visualizza trasformazioni

Il trasferimento automatico non funziona affatto bene con le trasformazioni della vista. Il motivo, per quanto posso discernere, è che non dovresti fare confusione con il frame di una vista che ha una trasformazione (diversa dalla trasformazione dell'identità predefinita), ma è esattamente ciò che fa il payout automatico. Il modo in cui funziona il autolayout è che in layoutSubviewsfase di esecuzione viene eseguito il superamento di tutti i vincoli e l'impostazione dei frame di tutte le viste di conseguenza.

In altre parole, i vincoli non sono magici; sono solo un elenco di cose da fare. layoutSubviewsè dove viene eseguita la lista delle cose da fare. E lo fa impostando i frame.

Non posso fare a meno di considerare questo come un bug. Se applico questa trasformazione a una vista:

v.transform = CGAffineTransformMakeScale(0.5,0.5);

Mi aspetto di vedere la vista apparire con il suo centro nello stesso posto di prima e a metà della dimensione. Ma a seconda dei suoi vincoli, potrebbe non essere quello che vedo affatto.

[In realtà, qui c'è una seconda sorpresa: l'applicazione di una trasformazione a una vista attiva immediatamente il layout. Questo mi sembra un altro bug. O forse è il cuore del primo bug. Quello che mi aspetterei è di riuscire a cavarmela con una trasformazione almeno fino al momento del layout, ad esempio il dispositivo viene ruotato, così come posso cavarmela con un'animazione del frame fino al tempo del layout. Ma in effetti il ​​tempo di layout è immediato, il che sembra sbagliato.]

Soluzione 1: nessun vincolo

Una soluzione attuale è, se ho intenzione di applicare una trasformazione semipermanente a una vista (e non semplicemente agitarla temporaneamente in qualche modo), per rimuovere tutti i vincoli che la riguardano. Sfortunatamente questo in genere fa scomparire la vista dallo schermo, poiché il trasferimento automatico ha ancora luogo e ora non ci sono vincoli per dirci dove posizionare la vista. Quindi oltre a rimuovere i vincoli, ho impostato la vista translatesAutoresizingMaskIntoConstraintssu SÌ. La vista ora funziona alla vecchia maniera, senza essere influenzata dall'autolayout. ( Ovviamente è influenzato dal completamento automatico, ma i vincoli impliciti della maschera di ridimensionamento automatico fanno sì che il suo comportamento sia esattamente come prima del completamento automatico.)

Soluzione 2: utilizzare solo vincoli appropriati

Se questo sembra un po 'drastico, un'altra soluzione è impostare i vincoli affinché funzionino correttamente con una trasformazione prevista. Se una vista viene dimensionata esclusivamente in base alla larghezza e all'altezza fisse interne e posizionata esclusivamente al centro, ad esempio, la trasformazione della scala funzionerà come mi aspetto. In questo codice, rimuovo i vincoli esistenti su una subview ( otherView) e li sostituisco con quei quattro vincoli, dandogli una larghezza e un'altezza fisse e bloccandoli puramente al centro. Dopodiché, la mia trasformazione di scala funziona:

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]];

Il risultato è che se non ci sono vincoli che influenzano il frame di una vista, il layout automatico non toccherà il frame della vista - che è proprio quello che stai cercando quando è coinvolta una trasformazione.

Soluzione 3: utilizzare una vista secondaria

Il problema con entrambe le soluzioni di cui sopra è che perdiamo i vantaggi dei vincoli per posizionare la nostra visione. Quindi, ecco una soluzione che risolve questo. Inizia con una vista invisibile il cui compito è esclusivamente quello di fungere da host e utilizzare i vincoli per posizionarlo. Al suo interno, inserisci la vista reale come sottoview. Utilizzare i vincoli per posizionare la sottoview nella vista host, ma limitare tali vincoli ai vincoli che non reagiranno quando applicheremo una trasformazione.

Ecco un'illustrazione:

inserisci qui la descrizione dell'immagine

La vista bianca è vista host; dovresti far finta che sia trasparente e quindi invisibile. La vista rossa è la sua vista secondaria, posizionata fissando il suo centro al centro della vista host. Ora possiamo ridimensionare e ruotare la vista rossa attorno al suo centro senza alcun problema, e in effetti l'illustrazione mostra che l'abbiamo fatto:

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

E intanto i vincoli sulla vista host lo mantengono nel posto giusto mentre ruotiamo il dispositivo.

Soluzione 4: utilizzare invece le trasformazioni di livello

Invece di visualizzare le trasformazioni, utilizzare le trasformazioni di livello che non attivano il layout e quindi non causano conflitti immediati con i vincoli.

Ad esempio, questa semplice animazione di visualizzazione "pulsata" potrebbe interrompersi con il completamento automatico:

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

Anche se alla fine non vi è stato alcun cambiamento nelle dimensioni della vista, è sufficiente impostarne il transformlayout e i vincoli possono far saltare la vista. (Ti sembra un bug o cosa?) Ma se facciamo la stessa cosa con Core Animation (usando un CABasicAnimation e applicando l'animazione al livello della vista), il layout non accade e funziona bene:

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];

3
La trasformazione non è ciò che mi sta causando il problema, basta impostare il nuovo punto di ancoraggio. Proverò a rimuovere i vincoli e provare la maschera di ridimensionamento automatico.
jrturton,

1
Non capisco qualcosa qui. Soluzione 4: quando una trasformazione viene applicata a un livello di supporto che influisce anche sul trasferimento automatico, poiché la proprietà di trasformazione della vista si riferisce alla trasformazione del livello di supporto
Luca Bartoletti,

1
@Andy al contrario, su iOS 8 il problema è completamente sparito e la mia intera risposta non è necessaria
matt

1
@Sunkas Ho risposto al tuo problema.
Ben Sinclair,

2
Su iOS 8, la trasformazione dei livelli attiva anche il layout automatico
CarmeloS

41

Ho avuto un Isuue simile e ho appena sentito Back dal team Autolayout di Apple. Suggeriscono di utilizzare l'approccio Vista contenitore suggerito da matt, ma creano una sottoclasse di UIView per sovrascrivere layoutSubview e applicare il codice di layout personalizzato lì - Funziona come un fascino

Il file di intestazione è simile al modo in cui è possibile collegare la sottoview scelta direttamente da Interface Builder

#import <UIKit/UIKit.h>

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

e il file m applica il codice speciale in questo modo:

#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

Come puoi vedere, afferra il punto centrale della vista quando viene chiamato per la prima volta e riutilizza la posizione in ulteriori chiamate per posizionare la vista di conseguenza. Questo sovrascrive il codice di Autolayout in quel senso, che ha luogo dopo [super layoutSubviews]; che contiene il codice di autolayout.

In questo modo non è più necessario evitare il layout automatico, ma è possibile creare il proprio layout automatico quando i comportamenti predefiniti non sono più appropriati. Ovviamente puoi applicare cose molto più complicate di quelle contenute in quell'Esempio ma questo era tutto ciò di cui avevo bisogno poiché la mia App può usare solo la modalità Ritratto.


5
grazie per aver postato questo. Da quando ho scritto la mia risposta, ho sicuramente capito il valore dell'override layoutSubviews. Vorrei solo aggiungere, però, che Apple qui è semplicemente facendo voi fate quello che penso che avrebbero dovuto fare (nella loro attuazione Autolayout) per tutto il tempo.
opaco

Hai ragione, ma immagino che non sia realmente correlato alla questione stessa. Dal momento che hanno fornito il codice sopra, ne sono felice!
sensslen,

4
È triste e assurdo che gli stessi team di autolayout raccomandino di creare una sottoclasse di container personalizzata solo per aggirare il modo in cui l'autolayout interrompe il comportamento intuitivo e di lunga durata nel sistema di visualizzazione.
Algal,

8

Trovo un modo semplice. E funziona su iOS 8 e iOS 9.

Come regolare anchorPoint quando si utilizza il layout basato su frame:

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

Quando si regola l'ancoraggio della vista con il layout automatico, si fa la stessa cosa ma in modo vincolato. Quando anchorPoint passa da (0,5, 0,5) a (1, 0,5), il layerView si sposterà a sinistra con una distanza della metà della lunghezza della larghezza della vista, quindi è necessario compensare questo.

Non capisco il vincolo nella domanda, quindi supponiamo che si aggiunga un vincolo centerX relativo a superView centerX con una costante: layerView.centerX = superView.centerX + costante

layerView.layer.anchorPoint = CGPoint(1, 0.5)
let centerXConstraint = .....
centerXConstraint.constant = centerXConstraint.constant + layerView.bounds.size.width/2

Bicchiere di birra migliore per te amico mio. Questo è un ottimo modo per aggirare: D
Błażej,

3

Se stai usando il layout automatico, allora non vedo come l'impostazione manuale servirà a lungo termine perché alla fine il layout automatico ostruirà il valore di posizione che hai impostato quando calcola il suo layout.

Piuttosto, è necessario modificare gli stessi vincoli di layout per compensare le modifiche prodotte impostando anchorPoint. La seguente funzione lo fa per le viste non trasformate.

/**
  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];
}

Devo ammettere che probabilmente non è tutto ciò che speravi, dal momento che di solito l'unica ragione per cui vuoi modificare anchorPoint è impostare una trasformazione. Ciò richiederebbe una funzione più complessa che aggiorna i vincoli di layout per riflettere tutte le modifiche al frame che potrebbero essere causate dalla proprietà di trasformazione stessa. Questo è complicato perché le trasformazioni possono fare molto al frame. Una trasformazione in scala o rotazione renderebbe il telaio più grande, quindi avremmo bisogno di aggiornare eventuali vincoli di larghezza o altezza, ecc.

Se stai usando la trasformazione solo per un'animazione temporanea, allora quanto sopra potrebbe essere sufficiente poiché non credo che il layout automatico impedirà all'animazione in volo di presentare immagini che rappresentano violazioni puramente transitorie dei vincoli.


Sfortunatamente sto usando le trasformazioni. L'impostazione della posizione va bene perché il metodo viene chiamato ogni volta che la vista è strutturata, quindi non viene ostruita. Sto pensando di ignorare il layoutRect potrebbe essere una soluzione migliore (menzionata in una delle tue precedenti risposte) ma non l'ho ancora provato.
jrturton,

A proposito, ecco un banco di prova che ho usato. Sarebbe bello se qualcuno lo scoprisse
algal

Questa dovrebbe essere la risposta accettata. La risposta di @ matt non riguarda nemmeno il punto di ancoraggio.
Iulian Onofrei,

3

tl: dr: è possibile creare uno sbocco per uno dei vincoli in modo che possa essere rimosso e aggiunto di nuovo.


Ho creato un nuovo progetto e ho aggiunto una vista con una dimensione fissa al centro. I vincoli sono mostrati nell'immagine seguente.

I vincoli per il più piccolo esempio a cui potrei pensare.

Successivamente ho aggiunto uno sbocco per la vista che sta per ruotare e per il vincolo di allineamento x centrale.

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

Più avanti viewDidAppearcalcolerò il nuovo punto di ancoraggio

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;

Quindi rimuovo il vincolo per cui ho uno sbocco, ne creo uno nuovo che è sfalsato e lo aggiungo di nuovo. Dopodiché dico alla vista con il vincolo modificato che è necessario aggiornare i vincoli.

[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];

Alla fine aggiungo solo l'animazione di rotazione alla vista rotante.

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"];

Lo strato rotante sembra rimanere centrato (cosa che dovrebbe) anche quando si ruota il dispositivo o altrimenti causa l'aggiornamento dei vincoli. Il nuovo vincolo e il punto di ancoraggio modificato si annullano visivamente.


1
Funzionerebbe per la mia situazione specifica, non sembra che sia possibile una buona soluzione generale.
jrturton,

1

La mia attuale soluzione è quella di regolare manualmente la posizione del livello in viewDidLayoutSubviews. Questo codice può essere utilizzato anche layoutSubviewsper una sottoclasse della vista, ma nel mio caso la mia vista è una vista di livello superiore all'interno di un controller di vista, quindi ciò significa che non ho dovuto creare una sottoclasse UIView.

Sembra uno sforzo eccessivo, quindi altre risposte sono le benvenute.

-(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;
        }

    }
}

1

Ispirato alla risposta di Matt, ho deciso di provare un approccio diverso. È possibile utilizzare una vista contenitore, con i vincoli applicati in modo appropriato. La vista con il punto di ancoraggio modificato può quindi essere posizionata nella vista contenitore, usando maschere di ridimensionamento automatico e impostazione esplicita della cornice, proprio come ai vecchi tempi.

Funziona a meraviglia, comunque per la mia situazione. Le viste sono impostate qui in 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;
}

Non importa che i frame per la vista rossa siano zero a questo punto, a causa della maschera di ridimensionamento automatico sulla vista verde.

Ho aggiunto una trasformazione di rotazione su un metodo di azione e questo è stato il risultato:

inserisci qui la descrizione dell'immagine

Sembra che si sia perso durante la rotazione del dispositivo, quindi l'ho aggiunto al metodo 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];

}

Mi chiedo se nel tuo caso non sarebbe stato più semplice lasciare view.layersolo e fare tutto il tuo lavoro in un sottostrato di view.layer. In altre parole, la vista sarebbe solo un host, e tutti i disegni e il sottostrato si trasformeranno e così via sarebbero a un livello inferiore, non influenzati dai vincoli.
matt

1
D'accordo, ispirato alla tua soluzione di subview, l'ho aggiunto al mio saggio! Grazie per avermi permesso di giocare nella tua sandbox ...
matt

0

Penso che tu stia sconfiggendo lo scopo di autolayout con quel metodo. Hai detto che la larghezza e il bordo destro dipendono dalla superview, quindi perché non aggiungere semplicemente dei vincoli lungo quella linea di pensiero?

Perdi il paradigma anchorPoint / Transform e prova:

[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

Il NSLayoutAttributeRightvincolo significa esattamente come anchorPoint = CGPointMake(1.0, 0.5), e il NSLayoutAttributeWidthvincolo è approssimativamente equivalente al codice precedente NSLayoutAttributeLeft.


Grazie per la risposta, ma non posso "perdere il punto di ancoraggio / trasformare il paradigma". Devo applicare una trasformazione e regolare il punto di ancoraggio per effettuare la trasformazione corretta.
jrturton,

Per spiegare ulteriormente, in questo caso specifico la vista trasformata a volte dovrà piegarsi nello schermo ruotando lungo l'asse Y sul suo margine destro. Quindi, il punto di ancoraggio deve essere spostato. Sto anche cercando una soluzione generale.
jrturton,

Ovviamente puoi ancora mantenere il anchorPoint, il punto è che non dovresti usarlo per le misurazioni. Il sistema di autolayout UIView dovrebbe essere indipendente dalle trasformazioni di CALayer. Quindi UIView: layout, CALayer: aspetto / animazioni
John Estropia,

1
Dovrebbe essere, ma non lo è. Hai davvero provato questo? Cambiando il punto di ancoraggio si cambia la posizione del livello dopo che il layout automatico ha fatto il suo lavoro. La tua soluzione non funziona con un punto di ancoraggio modificato.
jrturton,

Sì, posso utilizzare diversi punti di ancoraggio per le mie viste disposte con vincoli. La tua risposta viewDidLayoutSubviewsdovrebbe risolverlo; positionva sempre d'accordo anchorPoint. La mia risposta mostra semplicemente come definire il vincolo per la trasformazione dell'identità.
John Estropia,

0

Questa domanda e le risposte mi hanno ispirato a risolvere i miei problemi con il layout automatico e il ridimensionamento, ma con le visualizzazioni di scorrimento. Ho creato un esempio della mia soluzione su github:

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

Questo è un esempio di UIScrollView con paging personalizzato completamente realizzato in AutoLayout ed è scalabile (CATransform3DMakeScale) con una pressione prolungata e toccare per ingrandire. Compatibile con iOS 6 e 7.


0

È un argomento importante e non ho letto tutti i commenti ma ho riscontrato lo stesso problema.

Ho avuto una visione da XIB con autolayout. E volevo aggiornare la sua proprietà di trasformazione. Incorporare la vista in una vista contenitore non risolve il mio problema perché il layout automatico stava agendo in modo strano sulla vista contenitore. Ecco perché ho appena aggiunto la seconda vista contenitore per contenere la vista contenitore che contiene la mia vista e stavo applicando le trasformazioni su di essa.


0

tl; dr Supponiamo che tu abbia cambiato il punto di ancoraggio in (0, 0). Il punto di ancoraggio è ora in alto a sinistra. Ogni volta che vedi il centro parole nel layout automatico, dovresti pensare in alto a sinistra .

Quando si regola anchorPoint, è sufficiente modificare la semantica di AutoLayout. Il layout automatico non interferirà con il tuo anchorPoint né viceversa. Se non lo capisci, avrai un brutto momento .


Esempio:

Figura A. Nessuna modifica del punto di ancoraggio

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

Figura B. Punto di ancoraggio modificato in alto a sinistra

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

La Figura A e la Figura B sembrano esattamente uguali. Niente è cambiato. È cambiata solo la definizione di quale centro si riferisce.


Va bene e va bene, ma non risponde alla domanda su cosa fare quando i tuoi vincoli non sono correlati al centro.
jrturton,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.