Pertanyaan Quartz 2D drawRect method (iPhone)


Saya punya 4 berbeda iPhone / Kakao / Inti Animasi / Objective-C buku di depan saya, bersama dengan banyak kode contoh dari web. Namun entah bagaimana saya masih merasa seperti saya kehilangan beberapa pemahaman mendasar tentang cara kerja gambar 2D kuarsa.

Aku s drawRect() dimaksudkan untuk sekadar menjadi pengait untuk mengeksekusi kode gambar Anda? Atau apakah metode ini seharusnya juga menggambarkan daerah yang "rusak", dan perlu dicat ulang? Bisakah saya menggambar barang saya satu kali dan kemudian "menempel", atau saya harus mengecat ulang seluruh adegan kapan saja melalui drawRect()? Objek Graphics2D Java bekerja dengan cara ini - Anda harus menggambar seluruh "gambar" Anda setiap kali cat () dipanggil, jadi Anda harus siap untuk membangunnya kembali setiap saat (atau menyimpannya).

Bagaimana Anda akan menerapkan program menggambar sederhana? Apakah Anda harus "mengingat" setiap baris / titik / goresan yang diambil oleh pengguna, dan mengulanginya setiap kali drawRect() disebut? Bagaimana dengan rendering "offscreen"; dapatkah Anda melakukan semua gambar dan kemudian menelepon [self setNeedsDisplay] agar tulisan Anda memerah ke layar?

Katakanlah sebagai tanggapan atas sentuhan pengguna, saya ingin meletakkan "X" di layar di mana dia menyentuh. X harus tetap ada, dan setiap sentuhan baru menghasilkan X lain. Apakah saya perlu mengingat semua koordinat sentuhan ini dan kemudian menggambar semuanya di drawRect() ?

EDIT:

Kecuali saya salah mengerti, jawaban joconor dan Hector Ramos di bawah ini saling bertentangan. Dan itu adalah demonstrasi yang bagus tentang kebingungan saya tentang hal ini. :-)


32
2018-04-04 02:15


asal


Jawaban:


Beberapa kebingungan antara berbagai referensi Cocoa berasal dari pengenalan tampilan yang didukung lapisan di Leopard. Pada iPhone, semua UIViews didukung lapisan, di mana dalam tampilan Leopard perlu secara manual mengaktifkan lapisan-backing.

Untuk tampilan yang didukung lapisan, konten diambil sekali menggunakan apa pun yang Anda berikan drawRect(), tetapi kemudian disangga ke lapisan. Lapisan bertindak seperti tekstur persegi panjang, jadi ketika Anda memindahkan tampilan yang didukung layer atau menutupnya, tidak perlu redraw, tekstur baru saja dipindahkan ke lokasi itu melalui GPU. Kecuali Anda mengatur needsDisplayOnBoundsChange property untuk YES untuk lapisan, mengubah ukuran lapisan (atau tampilan yang mengandung) hanya akan skala konten. Ini dapat menyebabkan grafik buram dalam tampilan atau lapisan Anda, jadi Anda mungkin ingin memaksakan penarikan ulang dalam kasus ini. setNeedsDisplay akan memicu penggambaran ulang manual konten tampilan atau lapisan, dan penghentian selanjutnya konten tersebut di lapisan.

Untuk kinerja optimal, disarankan agar Anda tidak sering melakukan panggilan drawRect, Karena gambar Quartz dan recaching dalam lapisan adalah operasi yang mahal. Sebaiknya coba lakukan animasi menggunakan lapisan terpisah yang bisa Anda gerakkan atau skala.

Referensi berbasis Kakao yang Anda lihat yang berhubungan dengan desktop mungkin menganggap tampilan non-layer-backed, yang memanggil drawRect: setiap kali tampilan perlu diperbarui, apakah itu dari pergerakan, penskalaan, atau memiliki bagian dari tampilan dikaburkan. Seperti yang saya katakan, semua UIViews didukung lapisan, jadi ini tidak terjadi pada iPhone.

Yang mengatakan, untuk aplikasi gambar Anda, salah satu cara untuk melakukannya adalah dengan memelihara array objek yang diambil dan memanggil drawRect: setiap kali pengguna menambahkan sesuatu yang baru, iterasi atas masing-masing objek yang sebelumnya diambil secara berurutan. Saya mungkin menyarankan pendekatan alternatif di mana Anda membuat UIView atau CALayer baru untuk setiap operasi gambar. Isi dari operasi menggambar (garis, busur, X, dll.) Akan ditarik oleh tampilan individu atau lapisan. Dengan begitu, Anda tidak perlu menggambar ulang semuanya dengan sentuhan baru, dan Anda mungkin bisa melakukan beberapa pengeditan gaya vektor yang rapi dengan menggerakkan masing-masing elemen yang digambar di sekitar secara independen dari yang lain. Untuk gambar yang kompleks, mungkin ada sedikit pengimbangan memori dalam hal ini, tapi saya yakin itu akan memiliki kinerja menggambar yang jauh lebih baik (penggunaan CPU minimal dan kedipan).


38
2018-04-04 15:11



drawRect () akan menggambar ke buffer offscreen. Anda tidak perlu menggambar ulang setiap kali wilayah "rusak", karena OS iPhone menangani penanganan pelapisan. Anda cukup menulis sekali ke buffer, dan biarkan OS menangani sisanya. Ini tidak seperti lingkungan pemrograman lain di mana Anda perlu terus menggambar ulang setiap kali ada sesuatu yang melewati tampilan Anda.


4
2018-04-04 04:26



Selalu siap untuk menggambar area yang sesuai dengan pandangan Anda saat itu drawRect: disebut.

Meskipun sistem dapat menyangga pandangan Anda, yang hanya akan dihindari drawRect: dari dipanggil. Jika karena alasan tertentu, sistem harus membatalkan buffer, Anda drawRect: metode dapat dipanggil lagi. Juga, drawRect: akan dipanggil untuk area berbeda dari pandangan Anda karena terlihat sebagai hasil dari pengguliran dan operasi lain yang memengaruhi visibilitas area tampilan Anda.


2
2018-04-04 05:14



Apakah drawRect () dimaksudkan untuk sekadar menjadi pengait untuk mengeksekusi kode gambar Anda?

Ini dimaksudkan untuk menggambar ulang wilayah (rect) yang dilewatkan kepada Anda, menggunakan tumpukan konteks grafis saat ini. Tidak lagi.

Atau apakah metode ini seharusnya juga menggambarkan daerah yang "rusak", dan perlu dicat ulang?

Tidak. Jika wilayah kotor tidak tumpang tindih, Anda mungkin menerima banyak permintaan dari drawRect: dengan berbagai ransum yang diberikan kepada Anda. Rektal tidak valid menggunakan setNeedsDisplayInRect:. Jika hanya sebagian dari permukaan pandangan Anda yang harus digambar ulang, maka Anda akan diminta untuk menggambar bagian tersebut ketika waktunya untuk menggambar.

Bisakah saya menggambar barang saya satu kali dan kemudian "menempel", atau saya harus mengecat ulang seluruh adegan kapan saja melalui drawRect ()?

Itu tidak 'menempel'. Rect menjadi tidak berlaku selama eksekusi aplikasi Anda, dan Anda diminta untuk menggambar kembali reka-rambu tersebut ketika sistem tampilan perlu memperbarui layar. Anda hanya mengecat ulang rektum yang diminta.

Dalam beberapa kasus, implementasi sederhana mungkin (agak malas) membatalkan seluruh rektata tampilan setiap kali sebagian tidak valid. Itu biasanya buruk karena biasanya membutuhkan lebih banyak gambar daripada yang diperlukan, dan sangat boros ketika pandangan tidak buram.

Objek Graphics2D Java bekerja dengan cara ini - Anda harus menggambar seluruh "gambar" Anda setiap kali cat () dipanggil, jadi Anda harus siap untuk membangunnya kembali setiap saat (atau menyimpannya).

Tidak demikian halnya dengan AppKit atau UIKit.

Bagaimana Anda akan menerapkan program menggambar sederhana? Apakah Anda harus "mengingat" setiap baris / titik / goresan yang diambil oleh pengguna, dan mereplikasi bahwa setiap kali drawRect () dipanggil?

Anda harus mengingat konteks (misalnya setiap baris / titik / langkah) yang diperlukan untuk menggambar tampilan Anda. Anda hanya perlu menggambar wilayah yang diminta. Secara teknis, sistem grafis tidak akan mengeluh jika Anda menggambar di luar rektum itu, tetapi itu bisa menyebabkan artefak.

Untuk perenderan yang kompleks, mungkin lebih mudah atau lebih efisien untuk menggambar ke buffer eksternal (misalnya bitmap), kemudian menggunakan representasi bitmap prarender untuk mendapatkan hal-hal di layar saat di drawrect:. (lihat juga jawaban Brad untuk lapisan)

Bagaimana dengan rendering "offscreen"; dapatkah Anda melakukan semua gambar Anda dan kemudian memanggil [self setNeedsDisplay] agar tulisan Anda memerah ke layar?

Ya, Anda bisa melakukannya. Secara khusus, Anda akan render ke buffer eksternal (misalnya bitmap), ketika Anda selesai rendering ke bitmap, membatalkan rect yang ingin Anda gambar, kemudian menggambar ke layar menggunakan data dalam bitmap ketika drawRect: disebut.

Katakanlah sebagai tanggapan atas sentuhan pengguna, saya ingin meletakkan "X" di layar di mana dia menyentuh. X harus tetap ada, dan setiap sentuhan baru menghasilkan X lain. Apakah saya perlu mengingat semua koordinat sentuhan ini dan kemudian menggambar semuanya dalam drawRect ()?

Anda punya beberapa opsi. Proposal Anda adalah satu (dengan asumsi Anda menggambar dalam rect yang diberikan kepada Anda). Yang lainnya adalah membuat tampilan 'X', dan cukup ingat poin-poin yang diperlukan untuk merekonstruksi tampilan jika Anda membutuhkan X tersebut untuk bertahan di seluruh peluncuran. Dalam banyak kasus, Anda dapat dengan mudah membagi masalah kompleks menjadi beberapa lapisan (permainan 2D sederhana):

  • 1) Gambar latar belakang dengan cakrawala.
  • 2) Beberapa hal di latar depan yang tidak sering berubah.
  • 3) Karakter pemain yang digunakan pengguna untuk menavigasi game.

Jadi, sebagian besar masalah dapat dengan mudah dibagi sehingga Anda tidak harus membuat semuanya sepanjang waktu. Ini mengurangi kerumitan dan meningkatkan kinerja jika dilakukan dengan baik. Jika dilakukan dengan buruk, itu bisa menjadi beberapa lebih buruk.


2
2017-10-21 20:14