Bagaimana cara menyesuaikan titik jangkar CALayer, ketika Tata Letak Otomatis sedang digunakan?

161

Catatan : Banyak hal telah berubah sejak pertanyaan ini diajukan; lihat di sini untuk ikhtisar terkini yang bagus.


Sebelum tata letak otomatis, Anda dapat mengubah titik jangkar dari lapisan tampilan tanpa memindahkan tampilan dengan menyimpan bingkai, mengatur titik jangkar, dan mengembalikan bingkai.

Dalam dunia tata letak otomatis, kami tidak mengatur bingkai lagi, tetapi kendala tampaknya tidak sampai pada tugas menyesuaikan posisi tampilan kembali ke tempat yang kita inginkan. Anda dapat meretas batasan untuk memposisikan ulang tampilan Anda, tetapi pada rotasi atau peristiwa pengubahan ukuran lainnya, ini menjadi tidak valid lagi.

Ide cemerlang berikut tidak berfungsi karena membuat "Pasangan atribut tata letak tidak valid (kiri dan lebar)":

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

Tujuan saya di sini adalah untuk mengatur tepi kiri layerView , tampilan dengan titik jangkar yang disesuaikan, menjadi setengah dari lebarnya ditambah 20 (jarak yang saya inginkan inset dari tepi kiri superview).

Apakah mungkin untuk mengubah titik jangkar, tanpa mengubah lokasi tampilan, dalam tampilan yang ditata dengan tata letak otomatis? Apakah saya perlu menggunakan nilai-nilai hardcoded dan mengedit kendala pada setiap rotasi? Saya harap tidak.

Saya perlu mengubah titik jangkar sehingga ketika saya menerapkan transformasi pada tampilan, saya mendapatkan efek visual yang benar.

jrturton
sumber
Mengingat apa yang Anda miliki di sini, sepertinya Anda akan berakhir dengan tata letak yang ambigu, meskipun kode di atas berfungsi. Bagaimana cara layerViewmengetahui lebarnya? Apakah itu menempelkan sisi kanannya ke sesuatu yang lain?
John Estropia
Itu tercakup dalam //Size-related constraints that work fine- lebar dan tinggi tampilan layer berasal dari superview.
jrturton

Jawaban:

361

[EDIT: Peringatan: Seluruh diskusi berikutnya mungkin akan ketinggalan zaman atau setidaknya sangat dimitigasi oleh iOS 8, yang mungkin tidak lagi membuat kesalahan dengan memicu tata letak pada saat transformasi tampilan diterapkan.]

Autolayout vs. Lihat Transforms

Autolayout sama sekali tidak bermain dengan baik dengan transformasi tampilan. Alasannya, sejauh yang saya bisa membedakan, adalah bahwa Anda tidak boleh dipusingkan dengan kerangka tampilan yang memiliki transformasi (selain dari transformasi identitas default) - tetapi itulah yang dilakukan oleh autolayout. Cara autolayout bekerja adalah bahwa dalam layoutSubviewsruntime datang gagah melalui semua kendala dan mengatur frame dari semua tampilan yang sesuai.

Dengan kata lain, batasannya bukanlah sihir; mereka hanya daftar yang harus dilakukan. layoutSubviewsadalah tempat daftar hal yang harus dilakukan. Dan itu dilakukan dengan mengatur frame.

Saya tidak bisa tidak menganggap ini sebagai bug. Jika saya menerapkan transformasi ini ke tampilan:

v.transform = CGAffineTransformMakeScale(0.5,0.5);

Saya berharap melihat tampilan muncul dengan pusatnya di tempat yang sama seperti sebelum dan setengah ukuran. Tetapi tergantung pada batasannya, itu mungkin bukan yang saya lihat sama sekali.

[Sebenarnya, ada kejutan kedua di sini: menerapkan transformasi ke tampilan memicu tata letak segera. Bagi saya ini adalah bug lain. Atau mungkin itu adalah jantung dari bug pertama. Apa yang saya harapkan adalah bisa lolos dengan transformasi setidaknya sampai waktu tata letak, mis. Perangkat diputar - sama seperti saya bisa pergi dengan animasi bingkai sampai waktu tata letak. Tetapi sebenarnya tata letak waktu langsung, yang tampaknya hanya salah.]

Solusi 1: Tidak Ada Kendala

Salah satu solusi saat ini adalah, jika saya akan menerapkan transformasi semipermanen ke tampilan (dan bukan hanya menggoyangkannya sementara waktu entah bagaimana), untuk menghapus semua kendala yang mempengaruhinya. Sayangnya ini biasanya menyebabkan tampilan menghilang dari layar, karena pelunasan otomatis masih terjadi, dan sekarang tidak ada kendala untuk memberi tahu kami di mana harus meletakkan tampilan. Jadi selain menghilangkan kendala, saya mengatur tampilan translatesAutoresizingMaskIntoConstraintske YES. Tampilan sekarang berfungsi dengan cara lama, secara efektif tidak terpengaruh oleh autolayout. (Hal ini dipengaruhi oleh AutoLayout, jelas, tapi kendala topeng autoresizing implisit menyebabkan perilakunya menjadi seperti itu sebelumnya AutoLayout.)

Solusi 2: Gunakan Hanya Kendala yang Tepat

Jika itu terlihat agak drastis, solusi lain adalah mengatur batasan untuk bekerja dengan benar dengan transformasi yang diinginkan. Jika tampilan diukur murni oleh lebar dan tinggi tetap internal, dan diposisikan murni oleh pusatnya, misalnya, transformasi skala saya akan berfungsi seperti yang saya harapkan. Dalam kode ini, saya menghapus batasan yang ada pada subview ( otherView) dan menggantinya dengan empat kendala tersebut, memberinya lebar dan tinggi tetap dan menyematkannya murni di tengahnya. Setelah itu, transformasi skala saya berfungsi:

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

Hasilnya adalah jika Anda tidak memiliki kendala yang memengaruhi bingkai tampilan, autolayout tidak akan menyentuh bingkai tampilan - yang persis seperti yang Anda cari ketika transformasi dilakukan.

Solusi 3: Gunakan Subview

Masalah dengan kedua solusi di atas adalah bahwa kita kehilangan manfaat dari kendala untuk memposisikan pandangan kita. Jadi, inilah solusi yang memecahkannya. Mulailah dengan pandangan yang tidak terlihat yang tugasnya hanya bertindak sebagai tuan rumah, dan gunakan batasan untuk memposisikannya. Di dalamnya, letakkan pandangan nyata sebagai subview. Gunakan kendala untuk memposisikan subview dalam tampilan host, tetapi batasi kendala tersebut untuk kendala yang tidak akan melawan ketika kita menerapkan transformasi.

Ini sebuah ilustrasi:

masukkan deskripsi gambar di sini

Tampilan putih adalah tampilan host; Anda seharusnya berpura-pura transparan dan karenanya tidak terlihat. Tampilan merah adalah subview-nya, diposisikan dengan menyematkan pusatnya ke pusat tampilan host. Sekarang kita dapat skala dan memutar tampilan merah di sekitar pusatnya tanpa masalah, dan memang ilustrasi menunjukkan bahwa kita telah melakukannya:

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

Dan sementara itu, batasan pada tampilan host menyimpannya di tempat yang tepat saat kami memutar perangkat.

Solusi 4: Gunakan Transform Layer Sebagai gantinya

Alih-alih melihat transformasi, gunakan transformasi lapisan, yang tidak memicu tata letak dan dengan demikian tidak menyebabkan konflik langsung dengan kendala.

Misalnya, animasi tampilan "berdenyut" sederhana ini mungkin pecah di bawah autolayout:

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

Meskipun pada akhirnya tidak ada perubahan dalam ukuran tampilan, hanya pengaturan transformtata letak penyebabnya terjadi, dan kendala dapat membuat tampilan melompat-lompat. (Apakah ini terasa seperti bug atau apa?) Tetapi jika kita melakukan hal yang sama dengan Core Animation (menggunakan CABasicAnimation dan menerapkan animasi ke lapisan tampilan), tata letak tidak terjadi, dan berfungsi dengan baik:

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];
matt
sumber
3
Transformasi bukanlah penyebab masalah saya, hanya pengaturan titik jangkar baru. Saya akan mencoba menghilangkan kendala dan mencoba masker autoresizing.
jrturton
1
Saya tidak mengerti sesuatu di sini. Solusi4: Ketika sebuah transformasi diterapkan ke lapisan dukungan yang mengubah mempengaruhi autolayout juga, karena properti transformasi tampilan mengacu pada transformasi lapisan dukungan
Luca Bartoletti
1
@Andy sebaliknya, di iOS 8 masalahnya benar-benar hilang dan seluruh jawaban saya tidak perlu
matt
1
@ Sungkas saya jawab dalam masalah Anda.
Ben Sinclair
2
Di iOS 8, layer transform juga memicu tata letak otomatis
CarmeloS
41

Saya memiliki Isu serupa dan baru saja mendengar Kembali dari Tim Autolayout di Apple. Mereka Menyarankan untuk menggunakan Pendekatan Tampilan Wadah yang disarankan tetapi mereka membuat Subclass dari UIView untuk menimpa tata letakSubview dan terapkan Kode tata letak khusus di sana - Ini berfungsi seperti pesona

File Header terlihat seperti itu sehingga Anda dapat menautkan subview pilihan Anda langsung dari Interface Builder

#import <UIKit/UIKit.h>

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

dan File m menerapkan Kode khusus seperti itu:

#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

Seperti yang dapat Anda lihat, ia meraih titik tengah Tampilan saat pertama kali dipanggil dan menggunakan kembali Posisi itu di panggilan selanjutnya untuk menempatkan Tampilan tersebut. Ini menimpa Kode Autolayout dalam arti itu, yang terjadi setelah [super layoutSubviews]; yang berisi Kode autolayout.

Seperti itu, tidak perlu lagi menghindari Autolayout, tetapi Anda dapat membuat autolayout sendiri ketika Perilaku default tidak lagi sesuai. Tentu saja Anda dapat menerapkan hal-hal yang jauh lebih rumit daripada apa yang ada dalam Contoh itu tetapi ini yang saya butuhkan karena Aplikasi saya hanya dapat menggunakan Mode Potret.

sensslen
sumber
5
Terima kasih telah memposting ini. Karena saya menulis jawaban saya, saya pasti memahami nilai overriding layoutSubviews. Saya hanya akan menambahkan, bahwa Apple di sini hanya membuat Anda melakukan apa yang saya pikir seharusnya mereka lakukan (dalam penerapan Autolayout) selama ini.
matt
Anda benar tetapi ini saya kira tidak benar-benar terkait dengan Masalah itu sendiri. Karena Mereka memberikan Kode di atas, saya senang dengan itu!
sensslen
4
Sangat menyedihkan dan absurd bahwa tim autolayout sendiri merekomendasikan untuk membuat subkelas penampung khusus hanya untuk mengatasi bagaimana autolayout memecah perilaku yang telah lama dan intuitif dalam sistem tampilan.
algal
8

Saya menemukan cara sederhana. Dan itu berfungsi di iOS 8 dan iOS 9.

Suka sesuaikan anchorPoint ketika Anda menggunakan tata letak berbasis bingkai:

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

Saat Anda menyesuaikan jangkar tampilan dengan tata letak otomatis, Anda melakukan hal yang sama tetapi dengan kendala. Ketika anchorPoint berubah dari (0,5, 0,5) menjadi (1, 0,5), layerView akan bergerak ke kiri dengan jarak sejauh setengah panjang lebar tampilan, jadi Anda harus mengimbanginya.

Saya tidak mengerti kendala dalam pertanyaan. Jadi, asumsikan bahwa Anda menambahkan kendala centerX relatif ke superView centerX dengan konstanta: layerView.centerX = superView.centerX + konstanta

layerView.layer.anchorPoint = CGPoint(1, 0.5)
let centerXConstraint = .....
centerXConstraint.constant = centerXConstraint.constant + layerView.bounds.size.width/2
seedante
sumber
Segelas bir terbaik untukmu temanku. Ini adalah solusi yang sangat baik: D
Błażej
3

Jika Anda menggunakan tata letak otomatis, maka saya tidak melihat bagaimana pengaturan posisi secara manual akan berfungsi dalam jangka panjang karena pada akhirnya tata letak otomatis akan merusak nilai posisi yang Anda tetapkan saat menghitung tata letaknya sendiri.

Sebaliknya, yang diperlukan adalah memodifikasi sendiri batasan tata letak untuk mengimbangi perubahan yang dihasilkan dengan mengatur anchorPoint. Fungsi berikut melakukan itu untuk tampilan yang tidak diubah.

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

Saya akui ini mungkin bukan semua yang Anda harapkan, karena biasanya satu-satunya alasan Anda ingin memodifikasi anchorPoint adalah untuk mengatur transformasi. Itu akan membutuhkan fungsi yang lebih kompleks yang memperbarui batasan tata letak untuk mencerminkan semua perubahan bingkai yang bisa disebabkan oleh properti transformasi itu sendiri. Ini rumit karena transformasi dapat melakukan banyak hal untuk frame. Penskalaan atau rotasi akan membuat bingkai lebih besar, jadi kita perlu memperbarui batasan lebar atau tinggi, dll.

Jika Anda hanya menggunakan transformasi untuk animasi sementara, maka apa yang di atas mungkin sudah cukup karena saya tidak percaya tata letak otomatis akan mencegah animasi dalam penerbangan dari menampilkan gambar yang murni merupakan pelanggaran sementara dari kendala.

ganggang
sumber
Saya menggunakan transformasi sayangnya. Pengaturan posisi baik-baik saja karena metode ini dipanggil setiap kali tampilan diletakkan, sehingga tidak berantakan. Saya sedang berpikir mengganti layoutRect mungkin menjadi solusi yang lebih baik (disebutkan dalam salah satu jawaban Anda sebelumnya) tetapi saya belum mencobanya.
jrturton
Ngomong-ngomong, inilah rig uji yang telah saya gunakan. Akan lebih bagus jika seseorang menemukan ini: github.com/algal/AutolayoutAnchorPointTestRig
algal
Ini harus menjadi jawaban yang diterima. @ Matt jawaban bahkan bukan tentang titik jangkar.
Iulian Onofrei
3

tl: dr: Anda dapat membuat saluran keluar untuk salah satu kendala sehingga dapat dilepas dan ditambahkan kembali.


Saya membuat proyek baru dan menambahkan tampilan dengan ukuran tetap di tengah. Kendala ditunjukkan pada gambar di bawah ini.

Hambatan untuk contoh terkecil yang bisa saya pikirkan.

Selanjutnya saya menambahkan outlet untuk tampilan yang akan berputar dan untuk kendala alignment tengah x.

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

Kemudian viewDidAppearsaya menghitung titik jangkar baru

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;

Lalu saya menghapus batasan yang saya punya outlet, buat yang baru diimbangi dan tambahkan kembali. Setelah itu saya memberi tahu tampilan dengan batasan yang diubah yang perlu diperbarui kendala.

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

Akhirnya saya hanya menambahkan animasi rotasi ke tampilan berputar.

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

Lapisan yang berputar sepertinya tetap berada di tengah (yang seharusnya) bahkan ketika memutar perangkat atau menyebabkannya memperbarui kendala. Batasan baru dan titik jangkar yang diubah secara visual membatalkan satu sama lain.

David Rönnqvist
sumber
1
Ini akan bekerja untuk situasi spesifik saya, sepertinya solusi umum yang baik tidak mungkin.
jrturton
1

Solusi saya saat ini adalah menyesuaikan posisi layer secara manual viewDidLayoutSubviews. Kode ini juga dapat digunakan layoutSubviewsuntuk subkelas tampilan, tetapi dalam kasus saya, tampilan saya adalah tampilan tingkat atas di dalam pengontrol tampilan, jadi ini berarti saya tidak perlu membuat subkelas UIView.

Sepertinya terlalu banyak usaha sehingga jawaban lain dipersilahkan.

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

Mengilhami jawaban matt saya, saya memutuskan untuk mencoba pendekatan yang berbeda. Tampilan wadah, dengan kendala diterapkan dengan tepat, dapat digunakan. Tampilan dengan titik jangkar yang dimodifikasi kemudian dapat ditempatkan dalam tampilan wadah, menggunakan masker autoresizing dan pengaturan bingkai eksplisit seperti di masa lalu yang buruk.

Itu berhasil, untuk situasi saya. Tampilan diatur di sini di 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;
}

Tidak masalah bahwa frame untuk tampilan merah adalah nol pada titik ini, karena topeng autoresizing pada tampilan hijau.

Saya menambahkan transformasi rotasi pada metode tindakan, dan ini hasilnya:

masukkan deskripsi gambar di sini

Tampaknya kehilangan dirinya sendiri selama rotasi perangkat, jadi saya menambahkan ini ke metode 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
sumber
Saya ingin tahu apakah dalam kasus Anda tidak akan lebih mudah untuk membiarkan view.layersaja dan melakukan semua pekerjaan Anda dalam sublapisan view.layer. Dengan kata lain, tampilan hanya akan menjadi tuan rumah, dan semua transformasi menggambar dan sublapisan dan seterusnya akan menjadi level bawah, tidak terpengaruh oleh kendala.
matt
1
Oke, terinspirasi oleh solusi subview Anda, saya telah menambahkannya ke esai saya! Terima kasih telah mengizinkan saya bermain di kotak pasir Anda ...
matt
0

Saya pikir Anda mengalahkan tujuan autolayout dengan metode itu. Anda memang menyebutkan bahwa lebar dan tepi kanan tergantung pada superview, jadi mengapa tidak menambahkan kendala di sepanjang garis pemikiran itu?

Kalah paradigma anchorPoint / transform dan coba:

[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

The NSLayoutAttributeRightberarti kendala persis seperti anchorPoint = CGPointMake(1.0, 0.5), dan NSLayoutAttributeWidthkendala kira-kira setara dengan kode Anda sebelumnya NSLayoutAttributeLeft.

John Estropia
sumber
Terima kasih atas jawaban Anda, tetapi saya tidak dapat "kehilangan titik jangkar / mengubah paradigma". Saya perlu menerapkan transformasi, dan menyesuaikan titik jangkar untuk membuat transformasi yang benar.
jrturton
Untuk menjelaskan lebih lanjut, dalam kasus khusus ini tampilan yang ditransformasikan kadang-kadang perlu dilipat ke layar dengan memutar sepanjang sumbu Y di ujung kanannya. Oleh karena itu, titik jangkar harus dipindahkan. Saya juga mencari solusi umum.
jrturton
Tentu saja Anda masih bisa menyimpannya anchorPoint, poin saya adalah Anda tidak boleh menggunakannya untuk pengukuran. Sistem pembayaran otomatis UIView harus independen dari transformasi CALayer. Jadi UIView: tata letak, CALayer: penampilan / animasi
John Estropia
1
Seharusnya, tetapi tidak. Sudahkah Anda mencoba ini? Mengubah titik jangkar mengimbangi posisi lapisan setelah tata letak otomatis melakukan tugasnya. Solusi Anda tidak berfungsi dengan titik jangkar yang dimodifikasi.
jrturton
Ya, saya bisa menggunakan anchorPoints berbeda untuk pandangan saya yang ditata dengan kendala. Jawaban Anda di viewDidLayoutSubviewsharus memperbaikinya; positionselalu sejalan dengan anchorPoint. Jawaban saya hanya menunjukkan bagaimana mendefinisikan batasan untuk transformasi identitas.
John Estropia
0

Pertanyaan dan jawaban ini mengilhami saya untuk menyelesaikan masalah saya sendiri dengan Autolayout dan penskalaan, tetapi dengan tampilan gulir. Saya membuat contoh solusi saya di github:

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

Ini adalah contoh dari UIScrollView dengan halaman kustom yang sepenuhnya dibuat dalam AutoLayout dan dapat diskalakan (CATransform3DMakeScale) dengan tekan lama dan ketuk untuk memperbesar. iOS 6 dan 7 kompatibel.

Hans
sumber
0

Ini adalah topik besar dan saya belum membaca semua komentar tetapi menghadapi masalah yang sama.

Saya memiliki pandangan dari XIB dengan autolayout. Dan saya ingin memperbarui properti transform-nya. Menanamkan tampilan ke tampilan wadah tidak menyelesaikan masalah saya karena autolayout bertingkah aneh pada tampilan wadah. Itu sebabnya saya baru saja menambahkan tampilan wadah kedua untuk memuat tampilan wadah yang berisi tampilan saya dan menerapkan transformasi padanya.

plamkata__
sumber
0

tl; dr Katakanlah Anda mengubah titik jangkar ke (0, 0). Titik jangkar sekarang kiri atas. Setiap kali Anda melihat pusat kata di tata letak otomatis, Anda harus berpikir kiri atas .

Ketika Anda menyesuaikan anchorPoint Anda, Anda hanya mengubah semantik AutoLayout. Tata letak otomatis tidak akan mengganggu anchorPoint Anda atau sebaliknya. Jika Anda tidak mengerti ini, Anda akan mengalami waktu yang buruk .


Contoh:

Gambar A. Tidak ada modifikasi anchor point

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

Gambar B. Titik jangkar berubah ke kiri atas

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

Gambar A dan Gambar B terlihat persis sama. Tidak ada yang berubah. Hanya definisi dari pusat apa yang mengacu pada perubahan.

seo
sumber
Itu semua baik dan bagus tetapi tidak menjawab pertanyaan tentang apa yang harus dilakukan ketika kendala Anda tidak terkait dengan pusat.
jrturton