[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 layoutSubviews
runtime 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. layoutSubviews
adalah 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 translatesAutoresizingMaskIntoConstraints
ke 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:
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 transform
tata 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];
layerView
mengetahui lebarnya? Apakah itu menempelkan sisi kanannya ke sesuatu yang lain?