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.
sumber
layerView
mengetahui lebarnya? Apakah itu menempelkan sisi kanannya ke sesuatu yang lain?//Size-related constraints that work fine
- lebar dan tinggi tampilan layer berasal dari superview.Jawaban:
[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:
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: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:
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:
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:sumber
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
dan File m menerapkan Kode khusus seperti itu:
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.
sumber
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.Saya menemukan cara sederhana. Dan itu berfungsi di iOS 8 dan iOS 9.
Suka sesuaikan anchorPoint ketika Anda menggunakan tata letak berbasis bingkai:
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
sumber
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.
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.
sumber
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.
Selanjutnya saya menambahkan outlet untuk tampilan yang akan berputar dan untuk kendala alignment tengah x.
Kemudian
viewDidAppear
saya menghitung titik jangkar baruLalu 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.
Akhirnya saya hanya menambahkan animasi rotasi ke tampilan berputar.
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.
sumber
Solusi saya saat ini adalah menyesuaikan posisi layer secara manual
viewDidLayoutSubviews
. Kode ini juga dapat digunakanlayoutSubviews
untuk 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.
sumber
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:
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:
Tampaknya kehilangan dirinya sendiri selama rotasi perangkat, jadi saya menambahkan ini ke metode viewDidLayoutSubviews:
sumber
view.layer
saja dan melakukan semua pekerjaan Anda dalam sublapisanview.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.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:
The
NSLayoutAttributeRight
berarti kendala persis sepertianchorPoint = CGPointMake(1.0, 0.5)
, danNSLayoutAttributeWidth
kendala kira-kira setara dengan kode Anda sebelumnyaNSLayoutAttributeLeft
.sumber
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 / animasiviewDidLayoutSubviews
harus memperbaikinya;position
selalu sejalan dengananchorPoint
. Jawaban saya hanya menunjukkan bagaimana mendefinisikan batasan untuk transformasi identitas.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.
sumber
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.
sumber
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
Gambar B. Titik jangkar berubah ke kiri atas
Gambar A dan Gambar B terlihat persis sama. Tidak ada yang berubah. Hanya definisi dari pusat apa yang mengacu pada perubahan.
sumber