Otomatik Düzen kullanılırken CALayer'ın bağlantı noktasını nasıl ayarlarım?


161

Not : Bu soru sorulduğundan beri işler devam ediyor; iyi bir genel bakış için buraya bakın .


Otomatik mizanpajdan önce, çerçeveyi depolayarak, bağlantı noktasını ayarlayarak ve çerçeveyi geri yükleyerek görünümün katmanının bağlantı noktasını değiştirebilirsiniz.

Otomatik mizanpaj dünyasında artık çerçeve ayarlamıyoruz, ancak kısıtlamalar bir görünümün konumunu istediğimiz yere geri döndürme görevine benzemiyor. Görünümünüzü yeniden konumlandırmak için kısıtlamaları hackleyebilirsiniz, ancak döndürme veya diğer yeniden boyutlandırma olaylarında bunlar tekrar geçersiz hale gelir.

Aşağıdaki parlak fikir, "Düzen niteliklerinin (sol ve genişlik) geçersiz eşleştirilmesi" oluşturduğundan çalışmaz:

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

Buradaki amacım layerView, ayarlanan bağlantı noktasına sahip görüntünün sol kenarını genişliğinin artı 20'sinin yarısına (denetimin sol kenarından eklemek istediğim mesafe) ayarlamaktı.

Bağlantı noktasını, görünümün yerini değiştirmeden otomatik mizanpajla düzenlenmiş bir görünümde değiştirmek mümkün müdür? Her kodda sabit kodlanmış değerler kullanmalı ve kısıtlamayı düzenlemem gerekiyor mu? Umarım değil.

Görünüme bir dönüşüm uyguladığımda doğru görsel efekti elde etmek için bağlantı noktasını değiştirmem gerekiyor.


Sadece burada ne var göz önüne alındığında, yukarıdaki kod çalışma olsa bile zaten belirsiz düzenleri ile sona erecek gibi görünüyor. layerViewGenişliğini nasıl biliyor? Sağ tarafını başka bir şeye mi yapıştırıyor?
John Estropia

Bu kapsanır //Size-related constraints that work fine- katman görünümünün genişliği ve yüksekliği denetimin görünümlerinden türetilir.
jrturton

Yanıtlar:


361

[DÜZENLEME: Uyarı: Sonraki tartışmanın tamamı, muhtemelen bir görünüm dönüşümü uygulandığında düzeni tetikleme hatası yapamayan iOS 8 tarafından modası geçmiş veya en azından hafifletilecek.]

Otomatik Yerleşim ve Görüntüleme Dönüşümleri

Otomatik dönüşüm, görünüm dönüşümleriyle hiç iyi oynamıyor. Bunun nedeni, görebildiğim kadarıyla, dönüşümü olan (varsayılan kimlik dönüşümü dışında) bir görünümün çerçevesiyle uğraşmamanızdır - ancak tam olarak otomatik yerleşim budur. Otomatik layoutSubviewsyerleşimin çalışma şekli, çalışma zamanında tüm kısıtlamalardan kaçınması ve tüm görünümlerin çerçevelerini buna göre ayarlamasıdır.

Başka bir deyişle, kısıtlamalar sihir değildir; bunlar sadece yapılacaklar listesidir. layoutSubviewsyapılacaklar listesinin yapıldığı yerdir. Ve bunu çerçeveler ayarlayarak yapar.

Bir hata olarak bu konuda yardımcı olamaz. Bu dönüşümü bir görünüme uygularsam:

v.transform = CGAffineTransformMakeScale(0.5,0.5);

Görünüm merkezi ile önce ve yarısı boyutunda aynı yerde görünmesini bekliyoruz. Ancak kısıtlamalarına bağlı olarak, gördüğüm şey olmayabilir.

[Aslında burada ikinci bir sürpriz daha var: bir görünüme dönüşüm uygulamak derhal düzeni tetikliyor. Bu bana başka bir hata gibi geliyor. Ya da belki de ilk böceğin kalbidir. Beklediğim şey, en azından düzen süresine kadar bir dönüşümden kurtulabilmektir, örneğin cihaz döndürülür - tıpkı düzen süresine kadar bir kare animasyonundan kurtulabildiğim gibi. Ama aslında düzen zamanı hemen doğru, bu sadece yanlış görünüyor.]

Çözüm 1: Kısıtlama Yok

Geçerli bir çözüm, bir görünüme yarı kalıcı bir dönüşüm uygulayacaksam (ve yalnızca bir şekilde geçici olarak sallamıyorsa), onu etkileyen tüm kısıtlamaları kaldırmak için. Ne yazık ki, bu genellikle görünümün ekrandan kaybolmasına neden olur, çünkü otomatik düzenleme hala gerçekleşir ve şimdi bize görüntüyü nereye koyacağımızı söyleyecek herhangi bir kısıtlama yoktur. Kısıtlamaları kaldırmanın yanı sıra, görünümü translatesAutoresizingMaskIntoConstraintsEVET olarak ayarladım . Görünüm artık eski şekilde çalışıyor ve otomatik düzenlemeden etkili bir şekilde etkilenmiyor. (O olduğu besbelli, Otomatik mizanpaj etkilenen, ancak örtük otomatik yeniden boyutlandırma maskesi kısıtlamaları onun davranışı Otomatik mizanpaj eskisi gibi olmasına neden.)

Çözüm 2: Yalnızca Uygun Kısıtlamaları Kullanın

Bu biraz sert görünüyorsa, başka bir çözüm, kısıtlamaları amaçlanan bir dönüşümle doğru çalışacak şekilde ayarlamaktır. Bir görünüm yalnızca dahili sabit genişliği ve yüksekliği ile boyutlandırılırsa ve yalnızca merkezi tarafından konumlandırılırsa, örneğin, ölçek dönüşümüm beklediğim gibi çalışır. Bu kodda, bir alt görünümdeki mevcut kısıtlamaları kaldırıyorum ( otherView) ve bunları sabit bir genişlik ve yükseklik vererek dört merkezi ile değiştiriyorum ve tamamen merkez tarafından sabitledim. Bundan sonra, ölçek dönüşümüm işe yarıyor:

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

Sonuç olarak, bir görünümün çerçevesini etkileyen herhangi bir kısıtlamanız yoksa, otomatik düzenleme görünümün çerçevesine dokunmaz - bu da bir dönüşüm söz konusu olduğunda arkanızda olan şeydir.

Çözüm 3: Bir Alt Görünüm Kullanın

Her iki çözümün de sorunu, görüşümüzü konumlandırmak için kısıtlamaların faydalarını kaybetmemizdir. İşte bunu çözen bir çözüm. İşi yalnızca ana bilgisayar gibi davranmak olan görünmez bir görünümle başlayın ve konumlandırmak için kısıtlamalar kullanın. Bunun içinde, gerçek görünümü bir alt görünüm olarak yerleştirin. Alt görünümü ana bilgisayar görünümünde konumlandırmak için kısıtlamaları kullanın, ancak bu kısıtlamaları, bir dönüşüm uyguladığımızda geri dönmeyecek kısıtlamalarla sınırlandırın.

İşte bir örnek:

resim açıklamasını buraya girin

Beyaz görünüm ana bilgisayar görünümüdür; bunun şeffaf ve dolayısıyla görünmez olduğunu iddia etmeniz gerekiyor. Kırmızı görünüm, merkezini ana bilgisayar görünümünün merkezine sabitleyerek konumlandırılan alt görünümüdür. Şimdi kırmızı görünümü herhangi bir sorun yaşamadan merkezi etrafında ölçeklendirebilir ve döndürebiliriz ve aslında çizim bunu yaptığımızı göstermektedir:

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

Bu arada, ana bilgisayar görünümündeki kısıtlamalar, cihazı döndürürken doğru yerde tutar.

Çözüm 4: Bunun yerine Katman Dönüşümlerini Kullanın

Görünüm dönüşümleri yerine, düzeni tetiklemeyen ve dolayısıyla kısıtlamalarla anında çakışmaya neden olmayan katman dönüşümleri kullanın.

Örneğin, bu basit "zonklama" görüntüleme animasyonu otomatik düzenleme altında iyi kırılabilir:

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

Sonunda, görünümün boyutunda bir değişiklik olmamasına rağmen, yalnızca transformdüzeninin gerçekleşmesine neden olur ve kısıtlamalar, görünümün atlamasını sağlayabilir. (Bu bir hata veya neye benziyor mu?) Ancak Çekirdek Animasyon ile aynı şeyi yaparsak (CABasicAnimation kullanarak ve animasyonu görünümün katmanına uygularsak), düzen gerçekleşmez ve iyi çalışır:

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
Dönüşüm bana soruna neden olan şey değil, sadece yeni bağlantı noktasını ayarlıyor. Kısıtlamaları kaldıracağım ve otomatikleştirme maskesini deneyeceğim.
jrturton

1
Burada bir şey anlamıyorum. Çözüm 4: Dönüştürmenin bir destek katmanına bir dönüşüm uygulandığında, otomatik yerleşimi de etkiler, çünkü görünümün dönüştürme özelliği, destek katmanının dönüşümüne işaret eder
Luca Bartoletti

1
Aksine, iOS 8'de sorun tamamen ortadan kalktı ve tüm cevabım gereksiz
matt

1
@Sunkas Sorununa cevap verdim.
Ben Sinclair

2
İOS

41

Benzer bir Isuue'um vardı ve Apple'daki Autolayout Ekibi'nden geri duydum. Matt'in önerdiği Kapsayıcı Görünümü Yaklaşımını kullanmayı öneriyorlar ancak mizanpajın üzerine yazmak için UIView'in bir Alt Sınıfını oluşturuyorlar.

Üstbilgi Dosyası şöyle görünür, böylece alt görünümünüzü doğrudan Arayüz Oluşturucu'dan bağlayabilirsiniz

#import <UIKit/UIKit.h>

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

ve m Dosyası aşağıdaki gibi özel Kod uygular:

#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

Gördüğünüz gibi, ilk çağrıldığında Görünümün merkez noktasını alır ve Görünümü buna göre yerleştirmek için bu Konumu başka çağrılarda yeniden kullanır. Bu, Autolayout Kodunun üzerine [süper layoutSubviews]; otomatik yerleşim kodunu içerir.

Artık Otomatik Yerleşimden kaçınmaya gerek yoktur, ancak varsayılan Davranışlar artık uygun olmadığında kendi otomatik yerleşiminizi oluşturabilirsiniz. Tabii ki bu Örnekte olduğundan daha karmaşık şeyler uygulayabilirsiniz, ancak Uygulamam sadece Portre Modunu kullanabildiğinden ihtiyacım olan buydu.


5
Bunu gönderdiğiniz için teşekkür ederiz. Cevabımı yazdığım için, kesinlikle geçersiz kılmanın değerini anladım layoutSubviews. Burada Apple sadece yapmakta olduğunu ben sadece, ama, eklersiniz sen bence ne onlar başından beri (autoLayout onların uygulanmasında) yapmalıydım.
matt

Haklısın ama sanırım bu gerçekten Sorunun kendisi ile ilgili değil. Yukarıdaki kodu verdikleri için bundan memnunum!
sensslen

4
Autolayout ekibinin, yalnızca autolayout'un görünüm sistemindeki uzun süredir devam eden ve sezgisel davranışı nasıl bozduğunu çözmek için özel bir konteyner alt sınıfı oluşturmayı önermesi üzücü ve saçmadır.
algal

8

Basit bir yol buluyorum. Ve iOS 8 ve iOS 9'da çalışır.

Çerçeve tabanlı mizanpaj kullanırken anchorPoint'i ayarlamak gibi:

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

Görünümün bağlantı noktasını otomatik mizanpajla ayarladığınızda, aynı şeyi ancak kısıtlamalar şeklinde yaparsınız. AnchorPoint (0,5, 0,5) 'den (1, 0,5)' e değiştiğinde, layerView görünüm genişliğinin yarısı kadar bir mesafede sola hareket edecektir, bu nedenle bunu telafi etmeniz gerekir.

Yani, superView centerX'e göre bir sabit ile bir centerX kısıtlaması eklediğinizi varsayalım: layerView.centerX = superView.centerX + sabiti

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

Senin için en iyi bira bardağı arkadaşım. Bu çok iyi bir çözüm: D
Błażej

3

Otomatik düzeni kullanıyorsanız, konumun uzun vadede manuel olarak nasıl ayarlanacağını göremiyorum, çünkü sonunda otomatik düzen kendi düzenini hesaplarken ayarladığınız konum değerini tıkayacaktır.

Aksine, anchorPoint ayarlanarak üretilen değişiklikleri telafi etmek için düzen kısıtlamalarını değiştirmek gerekir. Aşağıdaki işlev, dönüştürülmemiş görünümler için bunu yapar.

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

AnchorPoint'i değiştirmek istemenizin tek nedeni bir dönüşüm ayarlamak olduğundan, bunun muhtemelen umduğunuz her şey olmadığını itiraf ediyorum. Bu , transform özelliğinin kendisinden kaynaklanabilecek tüm çerçeve değişikliklerini yansıtacak şekilde düzen kısıtlamalarını güncelleyen daha karmaşık bir işlev gerektirir . Bu zor çünkü dönüşümler çerçeveye çok şey yapabilir. Ölçekleme veya döndürme dönüşümü çerçeveyi büyütür, bu nedenle herhangi bir genişlik veya yükseklik kısıtlamasını vb. Güncellememiz gerekir.

Dönüştürmeyi yalnızca geçici bir animasyon için kullanıyorsanız, otomatik düzenlemenin uçuş sırasında animasyonun kısıtlamaların yalnızca geçici ihlallerini temsil eden görüntüler sunmasını engelleyeceğinden, yukarıdaki şey yeterli olabilir.


Ne yazık ki dönüşümleri kullanıyorum. Konumun ayarlanması iyidir, çünkü görünüm her düzenlendiğinde yöntem çağrılır, bu nedenle tıkanmaz. Overriding layoutRect daha iyi bir çözüm olabilir (önceki yanıtlarınızdan birinde belirtilmiştir) ama henüz denemedim.
jrturton

Bu arada, kullandığım bir test ekipmanı. Birisi bunu çözseydi
algal

Bu kabul edilen cevap olmalı. @ matt'in cevabı bağlantı noktasıyla ilgili bile değil.
Iulian Onofrei

3

tl: dr: Kısıtlardan biri için kaldırılıp tekrar eklenebilmesi için bir çıkış oluşturabilirsiniz.


Yeni bir proje oluşturdum ve ortada sabit boyutlu bir görünüm ekledim. Kısıtlamalar aşağıdaki resimde gösterilmiştir.

Düşünebildiğim en küçük örnek için kısıtlamalar.

Sonra dönecek görünüm ve orta x hizalama kısıtı için bir çıkış ekledim.

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

Daha sonra viewDidAppearyeni bağlantı noktasını hesaplıyorum

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;

Sonra bir çıkış için kısıtlama kaldırmak, ofset yeni bir tane oluşturmak ve tekrar ekleyin. Bundan sonra, görünümü değiştirilen kısıtlamayla, kısıtlamaları güncellemesi gerektiğini söylüyorum.

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

Sonunda döndürme animasyonunu dönen görünüme ekliyorum.

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

Dönen katman, cihazı döndürürken veya başka bir şekilde kısıtlamaları güncellemesine neden olsa bile ortalanmış olarak kalmalıdır (ki gerekir). Yeni kısıtlama ve değişen bağlantı noktası görsel olarak birbirini iptal eder.


1
Bu benim özel durumum için işe yarayacaktı, hoş bir genel çözüm mümkün görünmüyor.
jrturton

1

Mevcut çözümüm, katmanın konumunu manuel olarak ayarlamaktır viewDidLayoutSubviews. Bu kod ayrıca layoutSubviewsbir görünüm alt sınıfı için de kullanılabilir , ancak benim durumumda bir görünüm denetleyicisinin içinde üst düzey bir görünümdür, bu nedenle bu bir UIView alt sınıfı yapmak zorunda olmadığım anlamına geliyordu.

Çok fazla çaba gibi görünüyor, bu yüzden diğer cevaplar en açıktır.

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

Mat'ımın cevabına ilham verdim, farklı bir yaklaşım denemeye karar verdim. Kısıtlamaların uygun şekilde uygulandığı bir kapsayıcı görünümü kullanılabilir. Değiştirilmiş bağlantı noktasına sahip görünüm, otomatik eskileştirme maskeleri ve kötü eski günlerde olduğu gibi açık çerçeve ayarı kullanılarak kap görünümü içine yerleştirilebilir.

Zaten benim durumum için bir tedavi çalışıyor. Görünümler burada viewDidLoad içinde ayarlanır:

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

Yeşil görünümdeki otomatik boyutlandırma maskesi nedeniyle kırmızı görünümün karelerinin bu noktada sıfır olması önemli değildir.

Bir eylem yöntemine bir dönüş dönüşümü ekledim ve sonuç buydu:

resim açıklamasını buraya girin

Aygıt döndürme sırasında kendini kaybetmiş gibi görünüyordu, bu yüzden bunu viewDidLayoutSubviews yöntemine ekledim:

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

}

Durumunuzda view.layeryalnız kalmak ve tüm çalışmalarınızı bir alt katmanda yapmak daha kolay olmaz mıydı acaba view.layer. Başka bir deyişle, görünüm yalnızca bir ana bilgisayar olurdu ve tüm çizim ve alt katman dönüşümleri vb.
matt

1
Tamam, alt görünüm çözümünüzden esinlenerek bunu makaleme ekledim! Kum havuzunda oynamama izin verdiğin için teşekkürler ...
matt

0

Bence, bu yöntemle otomatik düzenlemenin amacını yeniyorsunuz. Genişlik ve sağ kenarın denetime bağlı olduğundan bahsettiniz, neden sadece bu düşünce çizgisi boyunca kısıtlamalar eklemesiniz?

AnchorPoint / transform paradigmasını kaybedip şunu deneyin:

[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

NSLayoutAttributeRightAynen böyle zorlama aracının anchorPoint = CGPointMake(1.0, 0.5)ve NSLayoutAttributeWidthkısıt önceki kod adlı denk düşüyor NSLayoutAttributeLeft.


Cevabınız için teşekkürler, ama "bağlantı noktasını kaybedemiyorum / paradigmayı değiştiremiyorum". Bir dönüşüm uygulamam ve doğru dönüşümü yapmak için bağlantı noktasını ayarlamam gerekiyor.
jrturton

Daha ayrıntılı olarak açıklamak gerekirse, bu özel durumda dönüştürülmüş görünümün bazen en sağ kenarındaki Y ekseni boyunca dönerek ekrana katlanması gerekebilir. Bu nedenle, sabitleme noktasının hareket ettirilmesi gerekir. Ayrıca genel bir çözüm arıyorum.
jrturton

Tabii ki hala tutabilirsiniz anchorPoint, benim açımdan ölçümler için kullanmamalısınız. UIView otomatik yerleşim sistemi, CALayer dönüşümlerinden bağımsız olmalıdır. Yani UIView: düzen, CALayer: görünüm / animasyonlar
John Estropia

1
Olmalı, ama değil. Bunu gerçekten denedin mi? Bağlantı noktasının değiştirilmesi, otomatik düzen çalışmasını yaptıktan sonra katmanın konumunu dengeler . Çözümünüz değiştirilmiş bir bağlantı noktasıyla çalışmıyor.
jrturton

Evet, kısıtlamalarla ortaya konan görüşlerim için farklı anchorPoints kullanabilirim. Cevabınız viewDidLayoutSubviewsbunu düzeltmeli; positionhep birlikte olur anchorPoint. Cevabım sadece kimlik dönüşümü için kısıtlamanın nasıl tanımlanacağını gösteriyor.
John Estropia

0

Bu soru ve cevaplar, Autolayout ve ölçeklendirme ile ilgili ancak kaydırma görüntülerinde kendi sorunlarımı çözmeme ilham verdi. Github'da çözümümün bir örneğini oluşturdum:

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

Bu, tamamen AutoLayout'ta yapılan özel sayfalamaya sahip bir UIScrollView örneğidir ve yakınlaştırmak için uzun basma ve dokunma ile ölçeklenebilir (CATransform3DMakeScale). iOS 6 ve 7 uyumlu.


0

Bu büyük bir konu ve tüm yorumları okumadım ama aynı sorunla karşı karşıyaydım.

Autolayout ile XIB bir görüş vardı. Ve onun transform özelliğini güncellemek istedim. Görünümü bir kapsayıcı görünümüne gömmek sorunumu çözmüyor çünkü otomatik yerleşim kapsayıcı görünümünde garip davranıyordu. Bu yüzden, görünümümü içeren ve üzerine dönüşümler uygulayan kapsayıcı görünümünü içermek için sadece ikinci kapsayıcı görünümü ekledim.


0

tl; dr Diyelim ki bağlantı noktasını (0, 0) olarak değiştirdiniz. Bağlantı noktası artık sol üstte. Otomatik düzende kelime merkezini her gördüğünüzde, sol üstte düşünmelisiniz .

AnchorPoint'inizi ayarladığınızda, Otomatik Düzenlemenin anlambilimini değiştirirsiniz. Otomatik düzen, anchorPoint'inize engel olmaz veya tam tersi. Bunu anlamadıysanız, kötü bir zaman geçireceksiniz .


Misal:

Şekil A. Bağlantı noktası değişikliği yok

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

Şekil B. Bağlantı noktası sol üste değiştirildi

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

Şekil A ve Şekil B tam olarak aynı görünüyor. Hiçbirşey değişmedi. Sadece merkezin neyi ifade ettiği tanımı değişti.


Her şey iyi ve güzel ama kısıtlamalarınız merkezle ilgili olmadığında ne yapılacağı sorusuna cevap vermiyor.
jrturton
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.