setNeedsDisplay(): esensya ng pamamaraan at mekanismo ng muling pagguhit sa iOS

May-akda: IT Sectr Nai-publish: 2026-07-20 Oras ng pagbabasa: 6 min

setNeedsDisplay() — ito ay isang instance method ng UIView na minamarkahan ang view bilang nangangailangan ng muling pagguhit. Hindi tulad ng direktang pagtawag ng draw(_:), ang setNeedsDisplay() ay nagtatakda lamang ng dirty flag para sa view at agad na ibinabalik ang kontrol. Pinoproseso ng system ang kahilingang ito sa pinakamalapit na render cycle, na nagpapahintulot na pagsamahin ang maraming tawag sa isang operasyon ng pagguhit. Ayon sa Apple Documentation (2025), ang naantalang muling pagguhit sa pamamagitan ng setNeedsDisplay() ay ang tanging tamang paraan ng pag-update ng nilalaman ng custom na UIView.

Mga Pangunahing Punto

  • setNeedsDisplay() — naantalang kahilingan ng muling pagguhit ng UIView, hindi tumatawag ng draw(_:) nang sabay-sabay
  • dirty flag — panloob na flag ng view na itinakda ng setNeedsDisplay() at ni-reset pagkatapos ng draw(_:)
  • setNeedsDisplay(_:) — overloading na may CGRect para sa bahagyang muling pagguhit lamang ng tinukoy na lugar
  • displayIfNeeded() — sapilitang sabay-sabay na muling pagguhit, ginagamit para sa agarang pag-update
  • Cycle ng render — pinoproseso ng system ang mga dirty view sa dulo ng run loop, pinagsasama ang maraming kahilingan sa isang daanan

Ano ang setNeedsDisplay()

setNeedsDisplay() — ay isang UIView method na nagpapahiwatig sa system na ang nilalaman ng view ay luma na at nangangailangan ng muling pag-render. Ang pamamaraan ay hindi tumatanggap ng mga parameter at nagbabalik ng void. Pagkatapos ng tawag, ang view ay minamarkahan bilang dirty, at sa pinakamalapit na cycle ng pagguhit ay tinatawag ng system ang draw(_:) para sa view na ito.

Ang pangunahing tampok ay pagiging asinkrono. Ang maraming sunod-sunod na tawag ng setNeedsDisplay() para sa parehong view ay hindi magreresulta sa maraming tawag ng draw(_:) — pinagsasama sila ng system sa isang operasyon. Ito ay kritikal para sa pagganap: kung ang property ng view ay nagbago ng maraming beses bawat frame, ang draw(_:) ay tinatawag lamang nang isang beses.

Paano gumagana ang naantalang muling pagguhit

Ang mekanismo ng naantalang muling pagguhit ay batay sa render cycle ng UIKit. Kapag tinawag ang setNeedsDisplay(), itinatakda ng system ang panloob na flag layer.needsDisplay sa true. Sa dulo ng kasalukuyang run loop, dumadaan ang system sa lahat ng dirty-layer at tinatawag ang draw(_:) para sa bawat isa.

Run loop at mga hakbang ng render

Ang UIKit run loop ay may kasamang update phase, kung saan pinoproseso ang mga dirty view. Sa phase na ito, tinatawag ang layoutSubviews() kung kinakailangan, pagkatapos ay draw(_:) para sa lahat ng minarkahang view. Pagkatapos ng pagguhit, ang resulta ay ipinapadala sa GPU sa pamamagitan ng Core Animation para sa compositing.

swift
class ProgressView: UIView {
    var progress: CGFloat = 0 {
        didSet {
            setNeedsDisplay()
        }
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)
        guard let ctx = UIGraphicsGetCurrentContext() else { return }

        // Background
        ctx.setFillColor(UIColor.lightGray.cgColor)
        ctx.fill(rect)

        // Progress bar
        let fillRect = CGRect(x: rect.minX, y: rect.minY,
                              width: rect.width * progress, height: rect.height)
        ctx.setFillColor(UIColor.systemGreen.cgColor)
        ctx.fill(fillRect)
    }
}

Sa halimbawang ito, ina-update ng progressView ang progress, at ang bawat setter ay awtomatikong tumatawag ng setNeedsDisplay(). Hintayin ng system ang dulo ng run loop at tatawagin ang draw(_:) na may kasalukuyang halaga ng progress. Kung magbago ang progress nang tatlong beses sa isang frame, ang draw(_:) ay isasagawa lamang nang isang beses — gamit ang huling halaga.

setNeedsDisplay() vs setNeedsDisplay(_:)

Ang pamamaraang setNeedsDisplay(_:) ay tumatanggap ng parameter na rect ng uri ng CGRect, na naglilimita sa lugar ng muling pagguhit. Ito ay nagpapahintulot ng muling pagguhit lamang ng binagong bahagi ng view, hindi ang kabuuan. Ang pagkakaiba sa pagganap ay nagiging kapansin-pansin kapag nagtatrabaho sa malalaking view kung saan ang mga pagbabago ay nakakaapekto sa maliit na lugar.

KatangiansetNeedsDisplay()setNeedsDisplay(_:)
ParameterWalaCGRect — lugar ng muling pagguhit
Lugar ng muling pagguhitBuong bounds ng viewTanging tinukoy na parihaba
PagganapMababa sa malalaking viewMataas sa bahagyang pagbabago
Parameter rect sa draw(_:)Buong boundsTinukoy na parihaba
Kailan gagamitinPagbabago ng buong nilalamanMga puntong pagbabago (cursor, highlight)

Para sa kumplikadong graphics, inirerekomenda na iguhit muna ang buong eksena sa isang offscreen context, pagkatapos ay tawagin ang setNeedsDisplay(_:) para lamang sa binagong lugar. Ito ay isang karaniwang teknik sa mga application ng pagguhit at graphic editor sa iOS.

displayIfNeeded() laban sa setNeedsDisplay()

displayIfNeeded() — ay isang sabay-sabay na pamamaraan na agad na nag-uumpisa ng muling pagguhit ng lahat ng dirty view sa hierarchy, nang hindi naghihintay ng dulo ng run loop. Hindi tulad ng setNeedsDisplay() na nagtatakda lamang ng flag, pinipilit ng displayIfNeeded() ang agarang pagpapatupad ng draw(_:).

Inirerekomenda ng Apple na gamitin lamang ang displayIfNeeded() sa mga matinding kaso, kapag ang resulta ng pagguhit ay kailangan agad — halimbawa, bago gumawa ng screenshot o sa sabay-sabay na pagbuo ng imahe para sa UIGraphicsImageRenderer. Sa karaniwang mga sitwasyon, ang asinkronong modelo sa pamamagitan ng setNeedsDisplay() ay mas gusto dahil hindi nito hinaharangan ang main thread.

swift
    // Asinkrono — inirerekomenda
someView.setNeedsDisplay()

    // Sabay-sabay — kung kinakailangan lamang
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) tinawag agad

Ang pagtawag ng displayIfNeeded() nang walang naunang setNeedsDisplay() ay walang epekto — pinoproseso lamang ng pamamaraan ang mga view na minarkahan na bilang dirty. Kung walang view na nangangailangan ng muling pagguhit, ang displayIfNeeded() ay magtatapos nang hindi tumatawag ng draw(_:).

Pag-optimize ng mga tawag sa setNeedsDisplay()

Ang madalas na tawag ng setNeedsDisplay() ay maaaring magpababa ng pagganap, lalo na sa panahon ng animation. Tingnan natin ang mga pangunahing estratehiya ng pag-optimize.

Pagsasama-sama ng mga pagbabago sa pamamagitan ng modelo

Sa halip na tawagin ang setNeedsDisplay() sa bawat pagbabago ng indibidwal na property, tipunin ang mga pagbabago at tawagin ang pamamaraan nang isang beses pagkatapos ilapat ang lahat ng update. Halimbawa, kapag binabago ang kulay, laki, at posisyon ng elemento — tawagin ang setNeedsDisplay() nang isang beses pagkatapos itakda ang lahat ng property.

Paggamit ng CALayer para sa mga property na maaaring i-animate

Kung ang property ay maaaring i-animate sa pamamagitan ng CALayer (backgroundColor, opacity, position, transform), gamitin ang Core Animation sa halip na draw(_:). Ang mga property ng CALayer ay ina-update sa GPU nang hindi tumatawag ng draw(_:), na nagbibigay ng 60 FPS nang hindi naglo-load ng CPU.

Batch na update sa pamamagitan ng CATransaction

Para sa paggrupo ng maraming pagbabago sa isang daanan ng render, gamitin ang CATransaction. Ito ay nagpapahintulot na pagsamahin ang maraming setNeedsDisplay() sa isang operasyon ng draw(_:) sa dulo ng transaksyon.

swift
CATransaction.begin()
CATransaction.setDisableActions(true)

view1.setNeedsDisplay()
view2.setNeedsDisplay()
view3.setNeedsDisplay()

CATransaction.commit()

Huwag abusuhin ang displayIfNeeded() — hinaharangan ng sabay-sabay na muling pagguhit ang main thread at maaaring maging sanhi ng pagbaba ng frame. Gamitin lamang ito para sa mga operasyon kung saan ang resulta ng pagguhit ay kritikal bago matapos ang kasalukuyang run loop.

Mga Madalas Itanong

Maaari bang tawagin nang direkta ang draw(_:) sa halip na setNeedsDisplay()?

Hindi, ang direktang pagtawag ng draw(_:) ay ipinagbabawal. Tamang minamarkahan ng setNeedsDisplay() ang view bilang dirty, at tinatawag ng system ang draw(_:) sa tamang sandali ng render cycle. Ang direktang pagtawag ng draw(_:) ay binabalewala ang mekanismo ng caching at maaaring humantong sa hindi pare-parehong estado.

Ano ang mangyayari sa maraming tawag ng setNeedsDisplay()?

Maraming tawag ng setNeedsDisplay() para sa parehong view ay pinagsasama ng system sa isang tawag ng draw(_:). Ito ay nangyayari dahil ang pamamaraan ay nagtatakda lamang ng dirty flag, na nire-reset lamang pagkatapos ng aktwal na pagpapatupad ng draw(_:) sa dulo ng run loop.

Ano ang pagkakaiba ng setNeedsDisplay() sa setNeedsLayout()?

setNeedsDisplay() ay humihiling ng muling pagguhit ng nilalaman ng view (tawag ng draw(_:)). setNeedsLayout() ay humihiling ng muling pag-aayos ng mga subview (tawag ng layoutSubviews()). Ang layoutSubviews() ay maaaring humantong sa pagbabago ng laki, na maaaring magdulot ng muling pagguhit.

Kailan gagamitin ang setNeedsDisplay(_:)?

Ang setNeedsDisplay(_:) ay dapat gamitin sa mga puntong pagbabago: paggalaw ng cursor, pagbabago ng kulay ng napiling lugar, pag-update ng bahagi ng graph. Nililimitahan nito ang lugar ng muling pagguhit at pinapataas ang pagganap kumpara sa buong muling pagguhit ng buong view.

Ang pagbabago ba ng frame ay awtomatikong tumatawag ng setNeedsDisplay()?

Bilang default, ang pagbabago ng frame o bounds ay hindi tumatawag ng setNeedsDisplay(). Inililipat lamang ng system ang view. Kung kailangan ang muling pagguhit sa pagbabago ng laki, itakda ang property na contentMode sa halagang .redraw — pagkatapos ay awtomatikong tatawag ang UIKit ng setNeedsDisplay() sa bawat pagbabago ng bounds.

Buod

  • setNeedsDisplay() — asinkronong UIView method para sa paghingi ng naantalang muling pagguhit sa pamamagitan ng pagtatakda ng dirty flag
  • setNeedsDisplay(_:) — overloading na may CGRect para sa bahagyang muling pagguhit lamang ng tinukoy na lugar, nag-o-optimize ng pagganap
  • displayIfNeeded() — sabay-sabay na pamamaraan para sa agarang muling pagguhit ng dirty view, ginagamit lamang kapag lubhang kailangan
  • Run loop — pinoproseso ng system ang mga dirty view sa dulo ng kasalukuyang cycle, pinagsasama ang maraming tawag sa isang daanan ng draw(_:)
  • CALayer — para sa mga property na maaaring i-animate (position, opacity) gamitin ang Core Animation sa halip na draw(_:) — ito ay mas mahusay
  • CATransaction — naggrupo ng maraming pagbabago sa isang render cycle, binabawasan ang bilang ng mga tawag sa draw(_:)
  • contentMode = .redraw — nag-a-activate ng awtomatikong pagtawag ng setNeedsDisplay() kapag nagbago ang bounds ng view

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din