setNeedsDisplay(): yöntemin özü ve iOS'ta yeniden çizim mekanizması

Yazar: IT Sectr Yayınlanma: 2026-07-20 Okuma süresi: 6 dk

setNeedsDisplay(), UIView'in bir örnek yöntemidir ve görünümü yeniden çizilmesi gereken olarak işaretler. Doğrudan draw(_:) çağırmaktan farklı olarak, setNeedsDisplay() yalnızca görünüm için dirty bayrağını ayarlar ve kontrolü hemen geri verir. Sistem bu isteği en yakın işleme döngüsünde işler ve birden fazla çağrının tek bir çizim işleminde birleştirilmesine olanak tanır. Apple Dokümantasyonu'na (2025) göre, setNeedsDisplay() aracılığıyla ertelenmiş yeniden çizim, özel bir UIView'in içeriğini güncellemenin tek doğru yoludur.

Önemli Noktalar

  • setNeedsDisplay() — UIView için ertelenmiş yeniden çizim isteği, draw(_:)'i senkron olarak çağırmaz
  • dirty bayrağı — setNeedsDisplay() tarafından ayarlanan ve draw(_:)'den sonra temizlenen dahili görünüm bayrağı
  • setNeedsDisplay(_:) — yalnızca belirtilen bölgenin kısmi yeniden çizimi için CGRect ile aşırı yükleme
  • displayIfNeeded() — zorunlu senkron yeniden çizim, anlık güncellemeler için kullanılır
  • İşleme döngüsü — sistem, run loop sonunda dirty görünümleri işler ve birden fazla isteği tek bir geçişte birleştirir

setNeedsDisplay() nedir

setNeedsDisplay(), görünümün içeriğinin güncelliğini yitirdiğini ve yeniden işlenmesi gerektiğini sisteme bildiren bir UIView yöntemidir. Yöntem parametre almaz ve void döndürür. Çağrıdan sonra görünüm dirty olarak işaretlenir ve en yakın çizim döngüsünde sistem bu görünüm için draw(_:)'i çağırır.

Temel özellik asenkronluktur. Aynı görünüm için art arda birden fazla setNeedsDisplay() çağrısı, birden fazla draw(_:) çağrısına yol açmaz — sistem bunları tek bir işlemde birleştirir. Bu, performans için kritik öneme sahiptir: bir görünüm özelliği kare başına birden çok kez değişirse, draw(_:) yalnızca bir kez çağrılır.

Ertelenmiş yeniden çizim nasıl çalışır

Ertelenmiş yeniden çizim mekanizması UIKit işleme döngüsüne dayanır. setNeedsDisplay() çağrıldığında, sistem dahili layer.needsDisplay bayrağını true olarak ayarlar. Mevcut run loop'un sonunda, sistem tüm dirty katmanları dolaşır ve her biri için draw(_:)'i çağırır.

Run loop ve işleme adımları

UIKit run loop'u, dirty görünümlerin işlendiği bir güncelleme aşaması içerir. Bu aşamada, gerekirse layoutSubviews() çağrılır, ardından işaretlenmiş tüm görünümler için draw(_:) çağrılır. Çizim tamamlandıktan sonra sonuç, birleştirme için Core Animation aracılığıyla GPU'ya iletilir.

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

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

        // Arka plan
        ctx.setFillColor(UIColor.lightGray.cgColor)
        ctx.fill(rect)

        // İlerleme çubuğu
        let fillRect = CGRect(x: rect.minX, y: rect.minY,
                              width: rect.width * progress, height: rect.height)
        ctx.setFillColor(UIColor.systemGreen.cgColor)
        ctx.fill(fillRect)
    }
}

Bu örnekte, progressView progress'i günceller ve her ayarlayıcı otomatik olarak setNeedsDisplay()'i çağırır. Sistem, run loop'un sonunu bekler ve gerçek progress değeriyle draw(_:)'i çağırır. Progress kare başına üç kez değişirse, draw(_:) yalnızca bir kez — son değerle — yürütülür.

setNeedsDisplay() vs setNeedsDisplay(_:)

setNeedsDisplay(_:) yöntemi, CGRect türünde bir rect parametresi alarak yeniden çizim alanını sınırlar. Bu, görünümün tamamı yerine yalnızca değiştirilen kısmının yeniden çizilmesine olanak tanır. Performans farkı, değişikliklerin küçük bir alanı etkilediği büyük görünümlerle çalışırken belirgin hale gelir.

ÖzelliksetNeedsDisplay()setNeedsDisplay(_:)
ParametreYokCGRect — yeniden çizim alanı
Yeniden çizim alanıTüm görünüm boundsYalnızca belirtilen dikdörtgen
PerformansBüyük görünümlerde daha düşükKısmi değişikliklerde daha yüksek
draw(_:)'de rect parametresiTüm boundsBelirtilen dikdörtgen
Ne zaman kullanılırTüm içeriği değiştirmeNoktasal değişiklikler (imleç, vurgulama)

Karmaşık grafikler için, önce tüm sahnenin ekran dışı bir bağlamda çizilmesi ve ardından yalnızca değiştirilen alan için setNeedsDisplay(_:) çağrılması önerilir. Bu, iOS'taki çizim uygulamalarında ve grafik düzenleyicilerde standart bir tekniktir.

displayIfNeeded() karşı setNeedsDisplay()

displayIfNeeded(), run loop'un sonunu beklemeden hiyerarşideki tüm dirty görünümlerin yeniden çizimini hemen başlatan senkron bir yöntemdir. Yalnızca bir bayrak ayarlayan setNeedsDisplay()'in aksine, displayIfNeeded() draw(_:)'in hemen yürütülmesini zorlar.

Apple, displayIfNeeded()'in yalnızca çizim sonucunun hemen gerekli olduğu aşırı durumlarda kullanılmasını önerir — örneğin, ekran görüntüsü almadan önce veya UIGraphicsImageRenderer için senkron görüntü oluşturma sırasında. Normal senaryolarda, ana iş parçacığını engellemediği için setNeedsDisplay() aracılığıyla asenkron model tercih edilir.

swift
    // Eşzamansız — önerilir
someView.setNeedsDisplay()

    // Eşzamanlı — yalnızca gerekli olduğunda
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) hemen çağrıldı

Öncesinde setNeedsDisplay() olmadan displayIfNeeded() çağırmak etkili olmayacaktır — yöntem yalnızca zaten dirty olarak işaretlenmiş görünümleri işler. Hiçbir görünüm yeniden çizim gerektirmiyorsa, displayIfNeeded() draw(_:)'i çağırmadan tamamlanır.

setNeedsDisplay() çağrılarını optimize etme

Sık setNeedsDisplay() çağrıları, özellikle animasyon sırasında performansı düşürebilir. Ana optimizasyon stratejilerine bakalım.

Değişiklikleri model aracılığıyla gruplama

Tek bir özelliğin her değişikliğinde setNeedsDisplay() çağırmak yerine, değişiklikleri biriktirin ve tüm güncellemeleri uyguladıktan sonra yöntemi bir kez çağırın. Örneğin, bir öğenin rengini, boyutunu ve konumunu değiştirirken — tüm özellikleri ayarladıktan sonra setNeedsDisplay()'i bir kez çağırın.

Animasyon yapılabilir özellikler için CALayer kullanımı

Bir özellik CALayer (backgroundColor, opacity, position, transform) aracılığıyla animasyon yapılabiliyorsa, draw(_:) yerine Core Animation kullanın. CALayer özellikleri, draw(_:) çağırmadan GPU'da güncellenir ve CPU yükü olmadan 60 FPS sağlar.

CATransaction aracılığıyla toplu güncelleme

Birden fazla değişikliği tek bir işleme geçişinde gruplamak için CATransaction kullanın. Bu, işlemin sonunda birden fazla setNeedsDisplay() çağrısının tek bir draw(_:) işleminde birleştirilmesine olanak tanır.

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

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

CATransaction.commit()

displayIfNeeded()'i aşırı kullanmayın — senkron yeniden çizim ana iş parçacığını engeller ve kare düşüşlerine neden olabilir. Yalnızca mevcut run loop bitmeden çizim sonucunun kritik olduğu işlemler için kullanın.

Sıkça Sorulan Sorular

setNeedsDisplay() yerine doğrudan draw(_:)'i çağırabilir miyim?

Hayır, doğrudan draw(_:) çağırmak yasaktır. setNeedsDisplay(), görünümü doğru şekilde dirty olarak işaretler ve sistem, işleme döngüsünün uygun anında draw(_:)'i çağırır. Doğrudan draw(_:) çağırmak önbellekleme mekanizmasını yok sayar ve tutarsız bir duruma yol açabilir.

Birden fazla setNeedsDisplay() çağrısı yapılırsa ne olur?

Aynı görünüm için birden fazla setNeedsDisplay() çağrısı, sistem tarafından tek bir draw(_:) çağrısında birleştirilir. Bunun nedeni, yöntemin yalnızca dirty bayrağını ayarlaması ve bayrağın yalnızca run loop sonunda draw(_:)'in fiilen yürütülmesinden sonra temizlenmesidir.

setNeedsDisplay(), setNeedsLayout()'den nasıl farklıdır?

setNeedsDisplay(), görünüm içeriğinin yeniden çizilmesini ister (draw(_:) çağrısı). setNeedsLayout(), alt görünümlerin yeniden düzenlenmesini ister (layoutSubviews() çağrısı). layoutSubviews() boyut değişikliklerine yol açabilir ve bu da yeniden çizimi tetikleyebilir.

setNeedsDisplay(_:) ne zaman kullanılmalıdır?

setNeedsDisplay(_:), noktasal değişiklikler için kullanılmalıdır: imleç hareketi, seçili alanın renginin değişmesi, grafiğin bir bölümünün güncellenmesi. Bu, yeniden çizim alanını sınırlar ve tüm görünümü yeniden çizmeye kıyasla performansı artırır.

Frame değiştirmek otomatik olarak setNeedsDisplay()'i çağırır mı?

Varsayılan olarak, frame veya bounds değiştirmek setNeedsDisplay()'i çağırmaz. Sistem yalnızca görünümü taşır. Boyut değiştiğinde yeniden çizim gerekiyorsa, contentMode özelliğini .redraw olarak ayarlayın — o zaman UIKit, bounds her değiştiğinde otomatik olarak setNeedsDisplay()'i çağıracaktır.

Özet

  • setNeedsDisplay() — dirty bayrağını ayarlayarak ertelenmiş yeniden çizim talep etmek için UIView'in asenkron yöntemi
  • setNeedsDisplay(_:) — yalnızca belirtilen bölgenin kısmi yeniden çizimi için CGRect ile aşırı yükleme, performansı optimize eder
  • displayIfNeeded() — dirty görünümlerin anında yeniden çizimi için senkron yöntem, yalnızca kesinlikle gerekli olduğunda kullanılır
  • Run loop — sistem, mevcut döngünün sonunda dirty görünümleri işler ve birden fazla çağrıyı tek bir draw(_:) geçişinde birleştirir
  • CALayer — animasyon yapılabilir özellikler (position, opacity) için draw(_:) yerine Core Animation kullanın — çok daha verimlidir
  • CATransaction — birden fazla değişikliği tek bir işleme döngüsünde gruplar, draw(_:) çağrı sayısını azaltır
  • contentMode = .redraw — görünümün bounds'ı değiştiğinde otomatik setNeedsDisplay() çağrısını etkinleştirir

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun