Bisakah saya menggunakan CALayer untuk mempercepat rendering tampilan?

Saya membuat objek NSView khusus yang memiliki beberapa konten yang sering berubah, dan beberapa lebih jarang berubah. Ternyata, bagian yang jarang berubah akan memakan waktu paling lama untuk digambar. Apa yang ingin saya lakukan adalah merender kedua bagian ini dalam lapisan yang berbeda, sehingga saya dapat memperbarui satu atau yang lain secara terpisah, sehingga membuat pengguna saya tidak mengalami antarmuka pengguna yang lamban.

Bagaimana cara saya melakukan ini? Saya belum menemukan banyak tutorial bagus tentang hal semacam ini, dan tidak ada yang membahas tentang rendering NSBezierPaths pada CALayer. Ada ide?


person mtmurdock    schedule 21.02.2012    source sumber


Jawaban (1)


Firasat Anda benar, ini sebenarnya cara terbaik untuk mengoptimalkan gambar. Saya telah melakukannya sendiri di mana saya memiliki beberapa latar belakang statis besar yang ingin saya hindari menggambar ulang ketika elemen dipindahkan ke atas.

Yang perlu Anda lakukan hanyalah menambahkan CALayer objek untuk setiap item konten dalam tampilan Anda. Untuk menggambar lapisan, Anda harus mengatur tampilan Anda sebagai delegasi untuk setiap lapisan dan kemudian menerapkan metode drawLayer:inContext:.

Dalam metode itu Anda cukup menggambar konten setiap lapisan:

- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx
{
    if(layer == yourBackgroundLayer)
    {   
        //draw your background content in the context
        //you can either use Quartz drawing directly in the CGContextRef,
        //or if you want to use the Cocoa drawing objects you can do this:
        NSGraphicsContext* drawingContext = [NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES];
        NSGraphicsContext* previousContext = [NSGraphicsContext currentContext];
        [NSGraphicsContext setCurrentContext:drawingContext];
        [NSGraphicsContext saveGraphicsState];
        //draw some stuff with NSBezierPath etc
        [NSGraphicsContext restoreGraphicsState];
        [NSGraphicsContext setCurrentContext:previousContext];
    }
    else if (layer == someOtherLayer)
    {
        //draw other layer
    }
    //etc etc
}

Bila Anda ingin memperbarui konten salah satu lapisan, panggil saja [yourLayer setNeedsDisplay]. Ini kemudian akan memanggil metode delegasi di atas untuk menyediakan konten lapisan yang diperbarui.

Perhatikan bahwa secara default, saat Anda mengubah konten lapisan, Core Animation menyediakan transisi fade yang bagus untuk konten baru. Namun, jika Anda menangani gambarnya sendiri, Anda mungkin tidak menginginkan ini, jadi untuk mencegah animasi pudar default saat konten lapisan berubah, Anda juga harus menerapkan metode delegasi actionForLayer:forKey: dan mencegah animasi dengan mengembalikan a tindakan nol:

- (id<CAAction>)actionForLayer:(CALayer*)layer forKey:(NSString*)key 
{
    if(layer == someLayer)
    {
        //we don't want to animate new content in and out
        if([key isEqualToString:@"contents"])
        {
            return (id<CAAction>)[NSNull null];
        }
    }
    //the default action for everything else
    return nil;
}
person Rob Keniger    schedule 21.02.2012
comment
Terima kasih, ini sangat membantu. Namun saya mengalami kesulitan untuk mengaktifkan metode delegasi. Saya menetapkan pandangan saya sebagai delegasi dan saya menambahkan lapisan sebagai sublapisan dari lapisan akar, dan saya memanggil setNeedsDisplay tetapi drawLayer:inContext tidak pernah dipanggil. Ada ide? - person mtmurdock; 21.02.2012
comment
Saya juga mengganti drawRect: apakah itu menyebabkannya tidak memanggil fungsi lainnya? - person mtmurdock; 24.02.2012
comment
Ya. Anda tidak dapat menampilkan gambar drawRect: dan berbasis lapisan dalam satu tampilan. Anda perlu memastikan bahwa Anda juga telah memanggil setWantsLayer:YES pada tampilan tersebut, jika tidak, lapisan tersebut akan diabaikan. - person Rob Keniger; 24.02.2012
comment
kapan kamu menelepon setWantsLayer:? hanya sekali di konstruktor? Atau setiap kali Anda menggambar? - person mtmurdock; 24.02.2012
comment
Oke, saya sudah menampilkannya sekarang. Saya harus menetapkan posisinya, dan batasan agar dapat ditampilkan, namun hasilnya tidak sesuai dengan yang saya harapkan. Semuanya hilang sebagian besar dari tampilan di sudut kanan bawah. Apakah CALayers menggunakan sistem koordinat yang berbeda dari tampilan? Apakah posisi relatif terhadap tampilan pemilik atau jendela? - person mtmurdock; 24.02.2012
comment
Jika tampilan diatur di Interface Builder maka Anda dapat mengaktifkannya di inspektur Animasi Inti untuk tampilan tersebut (centang kotak di sebelah tampilan di bagian Lapisan Animasi Inti). Jika tidak, Anda dapat mengaktifkannya dalam metode initWithFrame: (jika Anda membuat lapisan secara terprogram) atau dalam initWithCoder: jika Anda memuat tampilan dari ujung pena. - person Rob Keniger; 24.02.2012
comment
Saya tidak mengerti. Apa yang saya aktifkan, dan apa fungsinya? - person mtmurdock; 24.02.2012
comment
CALayers menggunakan cara berbeda dalam menentukan koordinat. Lihat dokumen - person Rob Keniger; 24.02.2012
comment
Saya mengacu pada setWantsLayer. - person Rob Keniger; 24.02.2012