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(), 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 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.
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.
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(_:) 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.
| Özellik | setNeedsDisplay() | setNeedsDisplay(_:) |
|---|---|---|
| Parametre | Yok | CGRect — yeniden çizim alanı |
| Yeniden çizim alanı | Tüm görünüm bounds | Yalnızca belirtilen dikdörtgen |
| Performans | Büyük görünümlerde daha düşük | Kısmi değişikliklerde daha yüksek |
| draw(_:)'de rect parametresi | Tüm bounds | Belirtilen dikdörtgen |
| Ne zaman kullanılır | Tüm içeriği değiştirme | Noktasal 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(), 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.
// 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.
Sık setNeedsDisplay() çağrıları, özellikle animasyon sırasında performansı düşürebilir. Ana optimizasyon stratejilerine bakalım.
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.
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.
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.
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
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.
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(), 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(_:), 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.
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
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.
Ayrıca okuyun