setNeedsDisplay(): ουσία της μεθόδου και μηχανισμός ανασχεδίασης στο iOS

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-07-20 Χρόνος ανάγνωσης: 6 λεπ

setNeedsDisplay() — είναι μια μέθοδος στιγμιοτύπου UIView που επισημαίνει την προβολή ως χρήζουσα επανασχεδίασης. Σε αντίθεση με την άμεση κλήση draw(_:), το setNeedsDisplay() απλώς ορίζει μια σημαία dirty για την προβολή και επιστρέφει αμέσως τον έλεγχο. Το σύστημα επεξεργάζεται αυτό το αίτημα στον πλησιέστερο κύκλο απόδοσης, επιτρέποντας τον συνδυασμό πολλαπλών κλήσεων σε μία λειτουργία σχεδίασης. Σύμφωνα με το Apple Documentation (2025), η καθυστερημένη ανασχεδίαση μέσω setNeedsDisplay() είναι ο μόνος σωστός τρόπος ενημέρωσης του περιεχομένου μιας προσαρμοσμένης UIView.

Κύρια σημεία

  • setNeedsDisplay() — καθυστερημένο αίτημα ανασχεδίασης UIView, δεν καλεί draw(_:) σύγχρονα
  • dirty flag — εσωτερική σημαία προβολής που ορίζεται από το setNeedsDisplay() και επαναφέρεται μετά το draw(_:)
  • setNeedsDisplay(_:) — υπερφόρτωση με CGRect για μερική ανασχεδίαση μόνο της καθορισμένης περιοχής
  • displayIfNeeded() — υποχρεωτική σύγχρονη ανασχεδίαση, χρησιμοποιείται για άμεση ενημέρωση
  • Κύκλος απόδοσης — το σύστημα επεξεργάζεται dirty προβολές στο τέλος του run loop, συνδυάζοντας πολλαπλά αιτήματα σε ένα πέρασμα

Τι είναι το setNeedsDisplay()

setNeedsDisplay() — είναι μια μέθοδος UIView που σηματοδοτεί στο σύστημα ότι το περιεχόμενο της προβολής είναι παρωχημένο και απαιτεί εκ νέου απόδοση. Η μέθοδος δεν δέχεται παραμέτρους και επιστρέφει void. Μετά την κλήση, η προβολή επισημαίνεται ως dirty, και στον πλησιέστερο κύκλο σχεδίασης το σύστημα καλεί draw(_:) για αυτήν την προβολή.

Το βασικό χαρακτηριστικό είναι η ασυγχρονικότητα. Πολλαπλές διαδοχικές κλήσεις setNeedsDisplay() για την ίδια προβολή δεν θα οδηγήσουν σε πολλαπλές κλήσεις draw(_:) — το σύστημα τις συνδυάζει σε μία λειτουργία. Αυτό είναι κρίσιμο για την απόδοση: εάν μια ιδιότητα προβολής αλλάξει πολλές φορές ανά καρέ, το draw(_:) καλείται μόνο μία φορά.

Πώς λειτουργεί η καθυστερημένη ανασχεδίαση

Ο μηχανισμός καθυστερημένης ανασχεδίασης βασίζεται στον κύκλο απόδοσης UIKit. Όταν καλείται το setNeedsDisplay(), το σύστημα ορίζει την εσωτερική σημαία layer.needsDisplay σε true. Στο τέλος του τρέχοντος run loop, το σύστημα διατρέχει όλα τα dirty επίπεδα και καλεί draw(_:) για το καθένα.

Run loop και βήματα απόδοσης

Το UIKit run loop περιλαμβάνει τη φάση update, κατά την οποία επεξεργάζονται οι dirty προβολές. Σε αυτή τη φάση, καλείται layoutSubviews() εάν χρειάζεται, στη συνέχεια draw(_:) για όλες τις επισημασμένες προβολές. Μετά την ολοκλήρωση της σχεδίασης, το αποτέλεσμα μεταφέρεται στο GPU μέσω Core Animation για σύνθεση.

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

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

        // Φόντο
        ctx.setFillColor(UIColor.lightGray.cgColor)
        ctx.fill(rect)

        // Γραμμή προόδου
        let fillRect = CGRect(x: rect.minX, y: rect.minY,
                              width: rect.width * progress, height: rect.height)
        ctx.setFillColor(UIColor.systemGreen.cgColor)
        ctx.fill(fillRect)
    }
}

Σε αυτό το παράδειγμα, το progressView ενημερώνει την progress, και κάθε setter καλεί αυτόματα το setNeedsDisplay(). Το σύστημα θα περιμένει το τέλος του run loop και θα καλέσει draw(_:) με την τρέχουσα τιμή progress. Εάν η progress αλλάξει τρεις φορές σε ένα καρέ, το draw(_:) θα εκτελεστεί μόνο μία φορά — με την τελευταία τιμή.

setNeedsDisplay() vs setNeedsDisplay(_:)

Η μέθοδος setNeedsDisplay(_:) δέχεται μια παράμετρο rect τύπου CGRect, που περιορίζει την περιοχή ανασχεδίασης. Αυτό επιτρέπει την ανασχεδίαση μόνο του τροποποιημένου τμήματος της προβολής, όχι ολόκληρης. Η διαφορά απόδοσης γίνεται αισθητή όταν εργάζεστε με μεγάλες προβολές όπου οι αλλαγές επηρεάζουν μια μικρή περιοχή.

ΧαρακτηριστικόsetNeedsDisplay()setNeedsDisplay(_:)
ΠαράμετροςΌχιCGRect — περιοχή ανασχεδίασης
Περιοχή ανασχεδίασηςΟλόκληρο το bounds της προβολήςΜόνο το καθορισμένο ορθογώνιο
ΑπόδοσηΧαμηλότερη σε μεγάλες προβολέςΥψηλότερη σε μερικές αλλαγές
Παράμετρος rect στο draw(_:)Ολόκληρο boundsΚαθορισμένο ορθογώνιο
Πότε να χρησιμοποιείταιΑλλαγή ολόκληρου περιεχομένουΣημειακές αλλαγές (δρομέας, highlight)

Για σύνθετα γραφικά, συνιστάται πρώτα η σχεδίαση ολόκληρης της σκηνής σε ένα offscreen περιβάλλον, στη συνέχεια η κλήση setNeedsDisplay(_:) μόνο για την τροποποιημένη περιοχή. Αυτή είναι μια τυπική τεχνική σε εφαρμογές σχεδίασης και επεξεργαστές γραφικών στο iOS.

displayIfNeeded() εναντίον setNeedsDisplay()

displayIfNeeded() — είναι μια σύγχρονη μέθοδος που ξεκινά αμέσως την ανασχεδίαση όλων των dirty προβολών στην ιεραρχία, χωρίς να περιμένει το τέλος του run loop. Σε αντίθεση με το setNeedsDisplay() που απλώς ορίζει μια σημαία, το displayIfNeeded() εξαναγκάζει την άμεση εκτέλεση του draw(_:).

Η Apple συνιστά τη χρήση του displayIfNeeded() μόνο σε ακραίες περιπτώσεις, όταν το αποτέλεσμα σχεδίασης χρειάζεται άμεσα — για παράδειγμα, πριν από τη δημιουργία στιγμιότυπου οθόνης ή κατά τη σύγχρονη δημιουργία εικόνας για UIGraphicsImageRenderer. Σε κανονικές περιπτώσεις, το ασύγχρονο μοντέλο μέσω setNeedsDisplay() προτιμάται επειδή δεν μπλοκάρει το κύριο νήμα.

swift
    // Ασύγχρονο — συνιστάται
someView.setNeedsDisplay()

    // Σύγχρονο — μόνο όταν χρειάζεται
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) κλήθηκε άμεσα

Η κλήση του displayIfNeeded() χωρίς προηγούμενο setNeedsDisplay() δεν θα έχει αποτέλεσμα — η μέθοδος επεξεργάζεται μόνο προβολές που έχουν ήδη επισημανθεί ως dirty. Εάν καμία προβολή δεν απαιτεί ανασχεδίαση, το displayIfNeeded() ολοκληρώνεται χωρίς να καλέσει draw(_:).

Βελτιστοποίηση κλήσεων setNeedsDisplay()

Οι συχνές κλήσεις setNeedsDisplay() μπορούν να μειώσουν την απόδοση, ιδιαίτερα κατά τη διάρκεια κινούμενων σχεδίων. Ας εξετάσουμε τις κύριες στρατηγικές βελτιστοποίησης.

Συνδυασμός αλλαγών μέσω μοντέλου

Αντί να καλείτε setNeedsDisplay() σε κάθε αλλαγή μιας μεμονωμένης ιδιότητας, συσσωρεύστε τις αλλαγές και καλέστε τη μέθοδο μία φορά μετά την εφαρμογή όλων των ενημερώσεων. Για παράδειγμα, κατά την αλλαγή χρώματος, μεγέθους και θέσης ενός στοιχείου — καλέστε setNeedsDisplay() μία φορά μετά τον ορισμό όλων των ιδιοτήτων.

Χρήση CALayer για ιδιότητες που μπορούν να κινηθούν

Εάν μια ιδιότητα μπορεί να κινηθεί μέσω CALayer (backgroundColor, opacity, position, transform), χρησιμοποιήστε Core Animation αντί για draw(_:). Οι ιδιότητες CALayer ενημερώνονται στο GPU χωρίς κλήση draw(_:), παρέχοντας 60 FPS χωρίς φόρτο CPU.

Μαζική ενημέρωση μέσω CATransaction

Για ομαδοποίηση πολλαπλών αλλαγών σε ένα πέρασμα απόδοσης, χρησιμοποιήστε CATransaction. Αυτό επιτρέπει τον συνδυασμό πολλαπλών setNeedsDisplay() σε μία λειτουργία draw(_:) στο τέλος της συναλλαγής.

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

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

CATransaction.commit()

Μην κάνετε κατάχρηση του displayIfNeeded() — η σύγχρονη ανασχεδίαση μπλοκάρει το κύριο νήμα και μπορεί να προκαλέσει πτώση καρέ. Χρησιμοποιήστε το μόνο για λειτουργίες όπου το αποτέλεσμα σχεδίασης είναι κρίσιμο πριν από την ολοκλήρωση του τρέχοντος run loop.

Συχνές Ερωτήσεις

Μπορεί να κληθεί το draw(_:) απευθείας αντί για setNeedsDisplay();

Όχι, η άμεση κλήση draw(_:) απαγορεύεται. Το setNeedsDisplay() επισημαίνει σωστά την προβολή ως dirty, και το σύστημα καλεί draw(_:) στην κατάλληλη στιγμή του κύκλου απόδοσης. Η άμεση κλήση draw(_:) αγνοεί τον μηχανισμό προσωρινής αποθήκευσης και μπορεί να οδηγήσει σε ασυνεπή κατάσταση.

Τι συμβαίνει με πολλαπλές κλήσεις setNeedsDisplay();

Πολλαπλές κλήσεις setNeedsDisplay() για την ίδια προβολή συνδυάζονται από το σύστημα σε μία κλήση draw(_:). Αυτό συμβαίνει επειδή η μέθοδος απλώς ορίζει το dirty flag, το οποίο επαναφέρεται μόνο μετά την πραγματική εκτέλεση του draw(_:) στο τέλος του run loop.

Σε τι διαφέρει το setNeedsDisplay() από το setNeedsLayout();

Το setNeedsDisplay() ζητά ανασχεδίαση του περιεχομένου της προβολής (κλήση draw(_:)). Το setNeedsLayout() ζητά αναδιάταξη των subview (κλήση layoutSubviews()). Το layoutSubviews() μπορεί να οδηγήσει σε αλλαγή μεγέθους, που με τη σειρά της μπορεί να προκαλέσει ανασχεδίαση.

Πότε να χρησιμοποιείτε το setNeedsDisplay(_:);

Το setNeedsDisplay(_:) πρέπει να χρησιμοποιείται σε σημειακές αλλαγές: κίνηση δρομέα, αλλαγή χρώματος επιλεγμένης περιοχής, ενημέρωση τμήματος γραφήματος. Αυτό περιορίζει την περιοχή ανασχεδίασης και αυξάνει την απόδοση σε σύγκριση με την πλήρη ανασχεδίαση ολόκληρης της προβολής.

Η αλλαγή frame καλεί αυτόματα το setNeedsDisplay();

Από προεπιλογή, η αλλαγή frame ή bounds δεν καλεί setNeedsDisplay(). Το σύστημα απλώς μετακινεί την προβολή. Εάν απαιτείται ανασχεδίαση κατά την αλλαγή μεγέθους, ορίστε την ιδιότητα contentMode στην τιμή .redraw — τότε το UIKit θα καλεί αυτόματα setNeedsDisplay() σε κάθε αλλαγή bounds.

Σύνοψη

  • setNeedsDisplay() — ασύγχρονη μέθοδος UIView για αίτημα καθυστερημένης ανασχεδίασης μέσω ορισμού dirty flag
  • setNeedsDisplay(_:) — υπερφόρτωση με CGRect για μερική ανασχεδίαση μόνο της καθορισμένης περιοχής, βελτιστοποιεί την απόδοση
  • displayIfNeeded() — σύγχρονη μέθοδος για άμεση ανασχεδίαση dirty προβολών, χρησιμοποιείται μόνο σε ακραία ανάγκη
  • Run loop — το σύστημα επεξεργάζεται dirty προβολές στο τέλος του τρέχοντος κύκλου, συνδυάζοντας πολλαπλές κλήσεις σε ένα πέρασμα draw(_:)
  • CALayer — για ιδιότητες που μπορούν να κινηθούν (position, opacity) χρησιμοποιήστε Core Animation αντί για draw(_:) — αυτό είναι τάξεις μεγέθους πιο αποδοτικό
  • CATransaction — ομαδοποιεί πολλαπλές αλλαγές σε έναν κύκλο απόδοσης, μειώνοντας τον αριθμό κλήσεων draw(_:)
  • contentMode = .redraw — ενεργοποιεί αυτόματη κλήση setNeedsDisplay() όταν αλλάζει το bounds της προβολής

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης