CALayer tidak diubah ukurannya pada perubahan batas UIView. Mengapa?

101

Saya memiliki UIViewyang memiliki sekitar 8 CALayersublapisan berbeda yang ditambahkan ke lapisannya. Jika saya memodifikasi batas tampilan (animasi), maka tampilan itu sendiri menyusut (saya memeriksanya dengan a backgroundColor), tetapi ukuran sub-lapisan tetap tidak berubah .

Bagaimana mengatasi ini?

Geri Borbás
sumber

Jawaban:

149

Saya menggunakan pendekatan yang sama yang digunakan Solin, tetapi ada kesalahan ketik dalam kode itu. Metodenya harus:

- (void)layoutSubviews {
  [super layoutSubviews];
  // resize your layers based on the view's new bounds
  mylayer.frame = self.bounds;
}

Untuk tujuan saya, saya selalu ingin sublayer menjadi ukuran penuh dari tampilan induk. Letakkan metode itu di kelas tampilan Anda.

Kayu Chadwick
sumber
8
@fishinear apakah kamu yakin? sepertinya Anda sedang mendeskripsikan bingkai. batas harus, secara teoritis, selalu memiliki 0,0 sebagai asalnya.
griotspeak
3
@ griotspeak - sebenarnya bukan itu masalahnya. Lihat deskripsi properti bounds di sini: developer.apple.com/library/ios/#documentation/uikit/reference/… - "Secara default, kotak asal dari bounds disetel ke (0, 0) tetapi Anda bisa ubah nilai ini untuk menampilkan bagian berbeda dari tampilan. "
jdc
Terima kasih banyak, saya telah mempelajari beberapa kasus (tampilan gulir, untuk satu) di mana itu tidak benar tetapi saya lupa tentang komentar ini.
griotspeak
31
Jangan lupa untuk memanggil [super layoutSubviews], karena ini dapat menyebabkan perilaku tak terduga di berbagai kelas UIKit.
Kpmurphy91
2
Ini tidak berfungsi dengan baik untuk saya dengan pengaturan ukuran sendiri UITextView(scrollEnabled = NO) dan tata letak otomatis. Saya memiliki tampilan teks yang disematkan ke tampilan supernya, yang pada gilirannya memiliki sebuah CALayerdi bagian bawahnya sendiri (sebuah perbatasan), dan saya ingin itu memperbarui posisi perbatasan saat ketinggian tampilan teks berubah. Untuk beberapa alasan layoutSubiewsdisebut terlambat (dan posisi lapisan dianimasikan).
iosdude
26

Karena CALayer di iPhone tidak mendukung pengelola tata letak, saya rasa Anda harus membuat lapisan utama tampilan Anda menjadi subkelas CALayer khusus yang Anda timpa layoutSublayersuntuk menyetel bingkai dari semua sub -lapisan . Anda juga harus mengganti +layerClassmetode view untuk mengembalikan kelas subkelas CALayer baru Anda.

Ole Begemann
sumber
Override + layerClass seperti dalam kasus override layer OpenGL? Berpikir begitu. Atur bingkai sublayer? Setel ke apa? Saya harus menghitung semua frame / posisi sublayers secara individual tergantung pada ukuran frame superlayers? Apakah tidak ada solusi seperti "batasan-seperti", atau "tautan ke superlayer"? Ya Tuhan, hari lain di luar kerangka waktu. CGLayers diubah ukurannya, tetapi terlalu lamban, CALayers cukup cepat, tetapi ukurannya tidak diubah. Banyak kejutan. Terima kasih atas jawabannya.
Geri Borbás
3
CALayer di Mac memiliki manajer tata letak untuk ini, tetapi mereka tidak tersedia di iPhone. Jadi ya, Anda harus menghitung sendiri ukuran bingkai tersebut. Pilihan lainnya adalah menggunakan subview dan bukan sublayers. Kemudian Anda dapat mengatur topeng otomatis subviews yang sesuai.
Ole Begemann
2
Ya, UIViews terlalu kelas performance mahal, itulah mengapa saya menggunakan CALayers.
Geri Borbás
Saya mencoba cara yang Anda sarankan. Itu berhasil, dan ternyata tidak. Saya mengubah ukuran tampilan animasi, tetapi sublayer mengubah ukuran dalam animasi yang berbeda. Sialan, apa yang harus dilakukan sekarang? Apa metode untuk menimpa di subkelas CALayer yang dipanggil pada SETIAP (!) Bingkai animasi tampilan? Apakah ada?
Geri Borbás
Baru saja menabrak ini juga. Sepertinya pilihan terbaik adalah subclass CALayer seperti yang dikatakan Ole, tapi sepertinya tidak praktis.
Dimitri
15

Saya menggunakan ini di UIView.

-(void)layoutSublayersOfLayer:(CALayer *)layer
{
    if (layer == self.layer)
    {
        _anySubLayer.frame = layer.bounds;
    }

super.layoutSublayersOfLayer(layer)
}

Bekerja untuk saya.

Runo Sahara
sumber
Ya, ini berhasil untuk saya di iOS 8. Mungkin akan berfungsi di versi sebelumnya. Saya memiliki lapisan latar belakang gradien dan perlu mengubah ukuran lapisan saat perangkat diputar.
EPage_Ed
Berhasil untuk saya, tetapi memang perlu panggilan melalui super
entropi
Inilah jawaban terbaik! Ya, saya telah mencoba layoutSubviews tetapi hanya merusak tata letak UITextField saya seperti leftView dan rightView menghilang dan lebih banyak Teks di UITextField menghilang. Mungkin saya telah menggunakan ekstensi alih-alih warisan UIView, tapi itu sangat bermasalah. INI BERHASIL BESAR! THX
Michał Ziobro
Untuk layer dengan gambar kustom ( CALayerDelegate's draw(_:in:)), saya harus juga memanggil _anySubLayer.setNeedsDisplay(). Kemudian ini berhasil untuk saya.
jedwidz
9

Saya memiliki masalah yang sama. Di lapisan tampilan kustom saya menambahkan dua sub-lapisan lagi. Untuk mengubah ukuran sublayer (setiap kali batas tampilan kustom berubah), saya menerapkan metode inilayoutSubviews tampilan kustom saya; di dalam metode ini saya hanya memperbarui setiap bingkai sub-lapisan agar sesuai dengan batas-batas lapisan subview saya saat ini.

Sesuatu seperti ini:

-(void)layoutSubviews{
   //keep the same origin, just update the width and height
   if(sublayer1!=nil){
      sublayer1.frame = self.layer.bounds;
   }
}
Solin
sumber
16
FYI, Anda tidak memerlukan if (sublayer1! = Nil), karena ObjC mendefinisikan pesan menjadi nil (dalam hal ini, -setFrame :) sebagai no-op return 0.
Andrew Pouliot
7

2017

Jawaban literal untuk pertanyaan ini:

"CALayer tidak diubah ukurannya pada perubahan batas UIView. Mengapa?"

apakah itu lebih baik atau lebih buruk

needsDisplayOnBoundsChange

defaultnya adalah false in CALayer.

larutan,

class CircularGradientViewLayer: CALayer {
    
    override init() {
        
        super.init()
        needsDisplayOnBoundsChange = true
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override open func draw(in ctx: CGContext) {
        go crazy drawing in .bounds
    }
}

Memang, saya mengarahkan Anda ke QA ini

https://stackoverflow.com/a/47760444/294884

yang menjelaskan, apa yang dilakukan oleh contentsScalepengaturan kritis ; Anda biasanya perlu menyetelnya secara sama saat menyetel needsDisplayOnBoundsChange.

Gendut
sumber
5

Versi Swift 3

Di sel Kustom, Tambahkan baris Mengikuti

Deklarasikan dulu

let gradientLayer: CAGradientLayer = CAGradientLayer()

Kemudian tambahkan baris berikut

override func layoutSubviews() {
    gradientLayer.frame = self.YourCustomView.bounds
}
Vinay Krishna Gupta
sumber
0

Seperti yang [Ole] tulis, CALayer tidak mendukung pengubahan ukuran otomatis di iOS. Jadi Anda harus menyesuaikan tata letak secara manual. Pilihan saya adalah menyesuaikan bingkai lapisan di dalam (iOS 7 dan sebelumnya)

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 

atau (pada iOS 8)

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinato
DevGansta
sumber
0

Dalam tampilan kustom Anda, Anda perlu mendeklarasikan variabel untuk lapisan kustom Anda, jangan mendeklarasikan variabel dalam scope init. Dan cukup lakukan sekali, jangan coba setel nilai null dan ulangi

    class CustomView: UIView {
        var customLayer: CALayer = CALayer ()

        override func layoutSubviews () {
            super.layoutSubviews ()
    // jaga biarkan _fillColor = self._fillColor lain {return}
            initializeLayout ()
        }
        private func initializeLayout () { 
            customLayer.removeFromSuperView ()
            customLayer.frame = layer.bounds
                   
            layer.insertSubview (di: 0)
        }
    }
Lê Tấn Thành
sumber