Mengapa metode draw dari CALayer khusus tidak dipanggil sebelumnya?

Masalah

Saya memicu animasi acak pada pilihan UIViews menggunakan Timer. Semua animasi berfungsi sebagaimana mestinya kecuali animasi yang metode pengundian CALayer dipanggil setelah keluar dari pemilih pengatur waktu.

Deskripsi Lengkap

Demi kejelasan dan penyederhanaan, izinkan saya membuat skema tindakan yang dilakukan.

Siklus

Semua animasi yang saya buat sejauh ini berfungsi sebagaimana mestinya: merupakan kombinasi CABasicAnimations pada subtampilan/sublapisan yang ada atau yang baru ditambahkan ke hierarki tampilan yang dipilih. Mereka dikodekan sebagai ekstensi UIView, sehingga mereka dapat dipanggil pada tampilan apa pun, terlepas dari tampilan atau pengontrol tampilan tempat tampilan tersebut berada. Ya, semuanya berfungsi kecuali satu.

Saya memang telah membuat kelas CALayer khusus yang terdiri dari menggambar pola pada CALayer. Dalam ekstensi kelas khusus ini, terdapat metode untuk menganimasikan pola tersebut (lihat kode selanjutnya). Jadi secara keseluruhan, ketika saya mencapai langkah/metode animate selected view dan menjalankan animasi khusus ini, inilah yang seharusnya terjadi:

  • sebuah metode bernama animatePattern dipanggil
  • metode ini menambahkan kelas CALayer khusus ke tampilan yang dipilih dan kemudian memanggil ekstensi animasi lapisan ini

Masalahnya: jika dengan semua animasi lainnya, semua gambar dilakukan sebelum keluar dari langkah/metode animate selected view, dalam kasus tertentu, metode pengundian kelas CALayer khusus dipanggil setelah keluarnya metode performAnimation, yang selanjutnya menyebabkan animasi terhenti.

Saya harus menambahkan bahwa saya telah mencoba animasi kelas CALayer khusus di taman bermain yang terpisah dan disederhanakan dan berfungsi dengan baik (saya menambahkan lapisan khusus ke UIView dalam metode viewDidLoad UIViewController dan kemudian saya memanggil animasi lapisan dalam metode viewDidAppear UIViewController.)

Kode

  • metode yang dipanggil oleh animate selected view langkah/metode:

    func animatePattern(for duration: TimeInterval = 1) {
    
    let patternLayer        = PatternLayer(effectType: .dark)
    patternLayer.frame      = self.bounds
    self.layer.addSublayer(patternLayer)
    patternLayer.play(for: duration)
    
    }
    

(perhatikan bahwa metode ini dalam ekstensi UIView, oleh karena itu self di sini mewakili UIView tempat animasi dipanggil)

  • Kelas CALayer kustom yang disederhanakan:

    override var bounds: CGRect {
        didSet {
            self.setNeedsDisplay()
        }
    
    
    // Initializers
    override init() {
        super.init()
        self.setNeedsDisplay()
    }
    
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    init(effectType: EffectType){
        super.init()
        // sets various properties
        self.setNeedsDisplay()
    }
    
    
    // Drawing
    override func draw(in ctx: CGContext) {
        super.draw(in: ctx)
        guard self.frame        != CGRect.zero else { return }
    
        self.masksToBounds      = true
    
        // do the drawings
    }
    
  • ekstensi animasi kelas CALayer khusus:

     func play(for duration: Double, removeAfterCompletion: RemoveAfterCompletion = .no) {
    
        guard self.bounds != CGRect.zero else {
            print("Cannot animate nil layer")
            return }
    
        CATransaction.begin()
    
        CATransaction.setCompletionBlock {
    
            if removeAfterCompletion.removeStatus {
                if case RemoveAfterCompletion.yes = removeAfterCompletion { self.fadeOutDuration = 0.0 }
                self.fadeToDisappear(duration: &self.fadeOutDuration)
            }
        }
    
    
        // perform animations
    
        CATransaction.commit()
    }
    

Upaya sejauh ini

Saya telah mencoba untuk memaksa menggambar lapisan dengan memasukkan setNeedsDisplay / setNeedsLayout di berbagai tempat dalam kode tetapi tidak berhasil: men-debug metode draw kelas CALayer khusus terus-menerus dicapai setelah keluarnya metode performAnimation, sementara itu harus dipanggil ketika bingkai lapisan dimodifikasi dalam metode animatePattern. Saya pasti melewatkan sesuatu yang cukup jelas, tetapi saat ini saya sedang berlari berputar-putar dan saya akan menghargai perhatian baru terhadap hal itu.

Terima kasih sebelumnya telah meluangkan waktu untuk mempertimbangkan masalah ini! Terbaik,


person Chris    schedule 11.02.2019    source sumber
comment
Untuk layer draw, bisakah Anda memberikan contoh animasi yang Anda lakukan di mana Anda memiliki //melakukan animasi   -  person agibson007    schedule 12.02.2019


Jawaban (2)


Anda mengganti undian(_ konteks) karena UIView dapat menjadi delegasi CALayer.

 UIView: {

    var patternLayer :  PatternLayer

     func animatePattern(for duration: TimeInterval = 1) {

      patternLayer      = PatternLayer(effectType: .dark)
       patternLayer.frame      = self.bounds
       self.layer.addSublayer(patternLayer)
     }

      func draw(_ layer: CALayer, in ctx: CGContext){
        layer.draw(ctx)
         if  NECESSARY { patternLayer.play(for: duration)}

        }

      }
person E.Coms    schedule 11.02.2019
comment
Terima kasih telah meluangkan waktu untuk menyarankan solusi! Sejauh yang saya mengerti ide Anda, saya mengganti draw(_context:) dalam tampilan yang tunduk pada animasi. Jika ya, solusi tersebut tidak dapat berfungsi karena animasi diterapkan pada pilihan tampilan acak, tidak pada tampilan tertentu. Setiap kali pengatur waktu mencapai waktu tertentu, ini memicu beberapa animasi pada beberapa tampilan, salah satunya adalah patternLayer. - person Chris; 11.02.2019
comment
Itu WAJIB datang dari. Idenya adalah untuk menjamin pemanggilan animasi setelah menggambar lapisan dengan beberapa kondisi. displayIfNeeded() juga bagus di sini. - person E.Coms; 11.02.2019
comment
Sekali lagi terima kasih :) tetapi saya mungkin tidak sepenuhnya memahami ide Anda... Apakah Anda menyarankan untuk menambahkan metode ini dalam ekstensi UIView? Selain itu, animasi ini mungkin bukan animasi yang dipilih untuk dijalankan (ada pilihan tampilan dan animasi yang akan diterapkan secara acak). - person Chris; 11.02.2019
comment
Tidak. Maksud saya, jawaban Anda patternLayer.displayIfNeeded() ada di sini. - person E.Coms; 11.02.2019

Seperti yang sering terjadi, ketika Anda meluangkan waktu untuk menuliskan masalah Anda, ide-ide baru mulai bermunculan. Faktanya, saya ingat bahwa self.setNeedsDisplay() memberi tahu sistem bahwa lapisan tersebut perlu digambar (ulang) tetapi tidak (ulang) digambar sekaligus: ini akan dilakukan pada penyegaran berikutnya. Tampaknya penyegaran terjadi setelah akhir siklus untuk animasi tertentu. Untuk mengatasi masalah ini, pertama-tama saya menambahkan panggilan untuk metode display tepat setelah batas patternLayer ditetapkan, dan berhasil, tetapi dengan komentar @Matt, saya mengubah solusi dengan memanggil metode displayIfNeeded sebagai gantinya. Ini juga berhasil.

func animatePattern(for duration: TimeInterval = 1) {

let patternLayer        = PatternLayer(effectType: .dark)
patternLayer.frame      = self.bounds
self.layer.addSublayer(patternLayer)

// asks the system to draw the layer now
patternLayer.displayIfNeeded()

patternLayer.play(for: duration)

}

Sekali lagi, jika ada yang memberikan solusi/penjelasan lain yang lebih elegan, jangan menahan diri untuk berbagi!

person Chris    schedule 11.02.2019
comment
Kalimat pertama dalam dokumen untuk display: “Jangan panggil metode ini secara langsung.” - person matt; 11.02.2019
comment
Saya tidak memanggil metode tampilan CALayer, tetapi metode tampilan subkelas. Namun saya ingin mendengar ide Anda untuk mengatasi masalah ini :) - person Chris; 11.02.2019
comment
Saya tidak membaca pertanyaan Anda secara mendalam, tetapi jika saya memahaminya dengan benar, cara saya menangani hal semacam ini adalah dengan memberikan penundaan singkat agar runloop dapat diselesaikan. - person matt; 11.02.2019
comment
Saya baru saja memperkenalkan DispatchQueue.main.asyncAfter yang sangat singkat ke dalam rangkaian kejadian. - person matt; 11.02.2019