draw(_:) και drawRect — τι είναι, κλήση και παράκαμψη

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

draw(_:) / drawRect — είναι η μέθοδος της κλάσης UIView (στη Swift) και το ανάλογό της στην Objective-C (drawRect:), που είναι υπεύθυνη για την απόδοση του περιεχομένου του view χρησιμοποιώντας το Core Graphics. Το σύστημα καλεί αυτόματα αυτή τη μέθοδο κατά την πρώτη εμφάνιση του view στην οθόνη και μετά την κλήση της setNeedsDisplay(). Σύμφωνα με το Apple Documentation (2025), η draw(_:) είναι η μόνη μέθοδος όπου ο προγραμματιστής έχει πρόσβαση στο γραφικό περιβάλλον της τρέχουσας οθόνης για προσαρμοσμένη σχεδίαση. Η παράκαμψη της draw(_:) δίνει πλήρη έλεγχο πάνω στην εμφάνιση του component — από απλά γεωμετρικά σχήματα μέχρι σύνθετα κινούμενα γραφικά.

Κύρια σημεία

  • draw(_:) — μέθοδος UIView για προσαρμοσμένη σχεδίαση μέσω Core Graphics, δεν καλείται απευθείας από τον προγραμματιστή
  • drawRect: — το ισοδύναμο της draw(_:) στην Objective-C, συντακτικά διαφορετικό, λειτουργικά ταυτόσημο
  • CGContext — γραφικό περιβάλλον διαθέσιμο εντός της draw(_:) για όλες τις λειτουργίες σχεδίασης
  • setNeedsDisplay() — ο μόνος σωστός τρόπος να ζητηθεί εκ νέου κλήση της draw(_:) από τον προγραμματιστή
  • UIGraphicsGetCurrentContext() — συνάρτηση λήψης του τρέχοντος περιβάλλοντος εντός της draw(_:), υποχρεωτικό βήμα πριν από τη σχεδίαση

Τι είναι το draw(_:) / drawRect

draw(_:) — είναι μια μέθοδος στιγμιοτύπου UIView που το UIKit καλεί για την απόδοση του περιεχομένου του view. Εντός αυτής της μεθόδου, ο προγραμματιστής αποκτά πρόσβαση στο γραφικό περιβάλλον CGContext και χρησιμοποιεί το API του Core Graphics για να σχεδιάσει γραμμές, γεμίσματα, κείμενο και εικόνες. Η drawRect: στην Objective-C εκτελεί την ίδια λειτουργία, αλλά με διαφορετική σύνταξη: η μοναδική παράμετρος είναι ένα CGRect που καθορίζει την περιοχή επανασχεδίασης.

Σύμφωνα με το Apple Engineering (2024), η draw(_:) λειτουργεί μέσω CPU-rendering Bitmap Graphics Context, που παρέχει μέγιστη ευελιξία, αλλά απαιτεί περισσότερους πόρους σε σύγκριση με το CALayer. Η απόφαση χρήσης της draw(_:) λαμβάνεται με βάση την πολυπλοκότητα των γραφικών και τις απαιτήσεις απόδοσης.

Υπογραφή της μεθόδου σε Swift και Objective-C

Στη Swift, η μέθοδος δηλώνεται ως override func draw(_ rect: CGRect), όπου rect είναι το ορθογώνιο που πρέπει να επανασχεδιαστεί. Στην Objective-C, η υπογραφή είναι - (void)drawRect:(CGRect)rect. Η παράμετρος rect μπορεί να είναι μικρότερη από τα bounds του view σε μερική επανασχεδίαση μέσω της setNeedsDisplay(_:).

objective-c
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextFillRect(context, rect);
}

Πότε το σύστημα καλεί την draw(_:)

Το σύστημα καλεί αυτόματα την draw(_:) σε αυστηρά καθορισμένα σενάρια. Η κατανόηση αυτών των ενεργοποιητών βοηθά στην αποφυγή περιττών επανασχεδιάσεων και στη βελτιστοποίηση της απόδοσης του view. Παρακάτω παρατίθενται όλες οι περιπτώσεις αυτόματης κλήσης της μεθόδου.

  • Πρώτη απόδοση — όταν το view προστίθεται για πρώτη φορά στην ιεραρχία και γίνεται ορατό στην οθόνη
  • setNeedsDisplay() — μετά την κλήση αυτής της μεθόδου από το σύστημα στον πλησιέστερο κύκλο σχεδίασης
  • setNeedsDisplay(_:) — το ίδιο, αλλά με καθορισμό του συγκεκριμένου ορθογωνίου για επανασχεδίαση
  • contentMode — κατά την αλλαγή bounds, εάν το contentMode απαιτεί επανασχεδίαση (π.χ. .redraw)
  • setNeedsLayout() — σε ορισμένες περιπτώσεις μετά την εκ νέου διάταξη των subview μπορεί να απαιτηθεί επανασχεδίαση

Η τεκμηρίωση της Apple προειδοποιεί: μην καλείτε ποτέ την draw(_:) απευθείας. Το σύστημα αποφασίζει μόνο του πότε θα εκτελέσει τη σχεδίαση και η άμεση κλήση διαταράσσει τον εσωτερικό μηχανισμό προσωρινής αποθήκευσης. Για να ζητήσετε επανασχεδίαση, χρησιμοποιείτε πάντα την setNeedsDisplay() ή την setNeedsDisplay(_:).

Πώς να παρακάμψετε την draw(_:) στη Swift

Η παράκαμψη της draw(_:) στη Swift ξεκινά με τη λήψη του γραφικού περιβάλλοντος και τις επακόλουθες κλήσεις Core Graphics. Συνιστάται η δημιουργία ξεχωριστών μεθόδων για λογικά τμήματα σχεδίασης — αυτό βελτιώνει την αναγνωσιμότητα και τη δυνατότητα δοκιμής του κώδικα.

swift
override func draw(_ rect: CGRect) {
    super.draw(rect)

    guard let context = UIGraphicsGetCurrentContext() else { return }

    // Παράμετροι γραμμής
    context.setStrokeColor(UIColor.darkGray.cgColor)
    context.setLineWidth(2.0)

    // Σχεδίαση τριγώνου
    context.move(to: CGPoint(x: rect.midX, y: rect.minY + 10))
    context.addLine(to: CGPoint(x: rect.maxX - 10, y: rect.maxY - 10))
    context.addLine(to: CGPoint(x: rect.minX + 10, y: rect.maxY - 10))
    context.closePath()
    context.strokePath()
}

Σε αυτό το παράδειγμα, η draw(_:) σχεδιάζει ένα τρίγωνο με σκούρο γκρι περίγραμμα πάχους 2 pixel. Η κλήση super.draw(rect) στην αρχή της μεθόδου συνιστάται από την Apple για τη διατήρηση της γονικής λογικής σχεδίασης, αν και η προεπιλεγμένη υλοποίηση της draw(_:) στο UIView είναι κενή.

Κανόνας ταυτοδυναμίας της draw(_:)

Κάθε κλήση της draw(_:) πρέπει να δίνει ταυτόσημο αποτέλεσμα με τα ίδια δεδομένα εισόδου. Αυτό επιτρέπει στο σύστημα να αποθηκεύει προσωρινά το αποτέλεσμα και να μην καλεί ξανά την draw(_:) εάν το περιεχόμενο του view δεν έχει αλλάξει. Μην χρησιμοποιείτε εντός της draw(_:) τυχαίες τιμές, χρόνο συστήματος ή αιτήματα δικτύου.

drawRect: στην Objective-C και διαφορά με την draw(_:)

drawRect: — ιστορικά η πρώτη έκδοση της μεθόδου, που εμφανίστηκε στο iOS 2.0 μαζί με την Objective-C. Στη Swift, η μέθοδος μετονομάστηκε σε draw(_:) με χρήση της εξωτερικής παραμέτρου _. Λειτουργικά οι μέθοδοι είναι ταυτόσημες: και οι δύο λαμβάνουν ένα CGRect της περιοχής επανασχεδίασης και χρησιμοποιούν την UIGraphicsGetCurrentContext() για πρόσβαση στο γραφικό περιβάλλον.

Χαρακτηριστικόdraw(_:) (Swift)drawRect: (Objective-C)
Υπογραφήoverride func draw(_ rect: CGRect)- (void)drawRect:(CGRect)rect
Κλήση supersuper.draw(rect)[super drawRect:rect]
ΠεριβάλλονUIGraphicsGetCurrentContext()UIGraphicsGetCurrentContext()
Παράμετρος rectrect: CGRectCGRect rect
ΑπόδοσηΤαυτόσημηΤαυτόσημη

Κατά τη μετεγκατάσταση έργου από Objective-C σε Swift, η μετονομασία της μεθόδου είναι μία από τις πρώτες εργασίες. Το Xcode παρέχει αυτόματο μετατροπέα, αλλά η drawRect: απαιτεί χειροκίνητη ενημέρωση σε draw(_:). Σύμφωνα με την Apple (2024), η έκδοση Swift draw(_:) προτιμάται για νέα έργα.

Βελτιστοποίηση σχεδίασης στην draw(_:)

Η draw(_:) εκτελείται στην CPU και μια μη βέλτιστη υλοποίηση μπορεί να προκαλέσει πτώση καρέ και χαμηλή απόδοση. Η Apple συνιστά αρκετές δοκιμασμένες προσεγγίσεις για την επιτάχυνση της σχεδίασης.

Ελαχιστοποιήστε τον αριθμό των λειτουργιών σχεδίασης

Κάθε λειτουργία Core Graphics (move(to:), addLine(to:), strokePath) έχει επιβάρυνση. Ομαδοποιήστε τις λειτουργίες και χρησιμοποιήστε CGPath για σύνθετα σχήματα — η διαδρομή δημιουργείται μία φορά και επαναχρησιμοποιείται σε κάθε κλήση της draw(_:).

Χρησιμοποιήστε UIBezierPath για διανυσματικά αντικείμενα

UIBezierPath — είναι ένα περιτύλιγμα Objective-C γύρω από το CGPath που παρέχει ένα απλό API για τη δημιουργία σχημάτων. Δημιουργήστε το UIBezierPath εκ των προτέρων (π.χ. στον αρχικοποιητή) και απλώς καλέστε την fill() ή την stroke() εντός της draw(_:).

swift
private let starPath: UIBezierPath = {
    let path = UIBezierPath()
    // Δημιουργία σχήματος αστεριού
    path.move(to: CGPoint(x: 50, y: 0))
    for i in 1...5 {
        let angle = CGFloat(i) * 4 * CGFloat.pi / 5
        path.addLine(to: CGPoint(x: 50 + 40 * cos(angle),
                               y: 50 + 40 * sin(angle)))
    }
    path.close()
    return path
}()

override func draw(_ rect: CGRect) {
    UIColor.systemYellow.setFill()
    starPath.fill()
}

Συνήθη λάθη κατά την εργασία με την draw(_:)

Οι προγραμματιστές συχνά κάνουν τα ίδια λάθη κατά την παράκαμψη της draw(_:). Η γνώση αυτών των μοτίβων βοηθά στην αποφυγή σφαλμάτων και μείωσης της απόδοσης. Ας εξετάσουμε τα πιο συνηθισμένα προβλήματα και τις λύσεις τους.

  • Άμεση κλήση της draw(_:) — μην καλείτε ποτέ την draw(_:) απευθείας. Χρησιμοποιήστε την setNeedsDisplay() για να ζητήσετε επανασχεδίαση. Η άμεση κλήση καταστρέφει την προσωρινή αποθήκευση και μπορεί να οδηγήσει σε εσφαλμένη εμφάνιση.
  • Βαριά υπολογισμοί εντός της draw(_:) — η draw(_:) πρέπει να είναι όσο το δυνατόν ελαφρύτερη. Δημιουργήστε UIBezierPath, εικόνες και άλλα βαριά αντικείμενα εκτός της μεθόδου, αρχικοποιώντας τα μία φορά.
  • Δημιουργία αντικειμένων εντός της draw(_:) — οι κατασκευές UIColor, UIFont και UIGraphicsImageRenderer εντός της draw(_:) δημιουργούν πρόσθετη επιβάρυνση μνήμης. Μεταφέρετε τη δημιουργία αντικειμένων στις ιδιότητες της κλάσης.
  • Αγνόηση της παραμέτρου rect — το rect υποδεικνύει την περιοχή που χρειάζεται επανασχεδίαση. Η σχεδίαση εκτός rect απορρίπτεται από το σύστημα, αλλά καταναλώνει πόρους. Ελέγξτε την τομή με το rect πριν από τη σχεδίαση.
  • Απουσία super.draw(rect) — αν και η υλοποίηση του UIView είναι κενή, η Apple συνιστά την κλήση της super.draw(rect) για συμβατότητα με μελλοντικές αλλαγές του UIKit.

Ακολουθώντας αυτούς τους κανόνες, θα εξασφαλίσετε σταθερή και γρήγορη σχεδίαση προσαρμοσμένων views σε οποιοδήποτε έργο iOS. Καταγράψτε το προφίλ της draw(_:) μέσω του Instruments (Core Animation) για να δείτε τον πραγματικό χρόνο εκτέλεσης και τα σημεία συμφόρησης.

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

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

Όχι, η άμεση κλήση της draw(_:) απαγορεύεται από την τεκμηρίωση της Apple. Το σύστημα διαχειρίζεται μόνο του τον κύκλο σχεδίασης. Για να ζητήσετε επανασχεδίαση, χρησιμοποιήστε την setNeedsDisplay(), η οποία σημειώνει σωστά το view ως χρήζον ενημέρωσης στον πλησιέστερο κύκλο απόδοσης.

Σε τι διαφέρει η drawRect: από την draw(_:);

Λειτουργικά αυτές οι μέθοδοι είναι ταυτόσημες. Η drawRect: χρησιμοποιείται στην Objective-C, η draw(_:) — στη Swift. Και οι δύο λαμβάνουν ένα CGRect της περιοχής επανασχεδίασης και χρησιμοποιούν το ίδιο περιβάλλον Core Graphics μέσω της UIGraphicsGetCurrentContext().

Γιατί δεν καλείται η draw(_:) για κενό UIView;

Η Apple βελτιστοποιεί την απόδοση: εάν το UIView δεν έχει παρακάμψει την draw(_:), το σύστημα δεν δημιουργεί περιβάλλον bitmap για αυτό. Αυτό εξοικονομεί μνήμη. Εάν υπάρχει παράκαμψη αλλά η μέθοδος δεν καλείται — ελέγξτε ότι το πλαίσιο του view δεν είναι μηδενικό και το view είναι ορατό.

Πόσο συχνά το σύστημα καλεί την draw(_:);

Μόνο όταν χρειάζεται: πρώτη απόδοση, μετά την setNeedsDisplay(), κατά την αλλαγή bounds με contentMode = .redraw. Σε στατική κατάσταση, η draw(_:) δεν καλείται ξανά, εξοικονομώντας πόρους CPU και μπαταρίας.

Πρέπει να καλούμε την super.draw(rect) στη Swift;

Η Apple συνιστά την κλήση της super.draw(rect) στην αρχή της παρακαμφθείσας μεθόδου. Αν και η τρέχουσα υλοποίηση του UIView είναι κενή, η κλήση super εξασφαλίζει συμβατότητα με μελλοντικές εκδόσεις του UIKit και αποτελεί καλή πρακτική.

Σύνοψη

  • draw(_:) / drawRect — η κύρια μέθοδος σχεδίασης UIView σε Swift και Objective-C, καλείται αυτόματα από το σύστημα
  • CGContext — το γραφικό περιβάλλον Core Graphics που είναι διαθέσιμο στην draw(_:) μέσω της UIGraphicsGetCurrentContext()
  • setNeedsDisplay() — ο σωστός τρόπος να ζητηθεί επανασχεδίαση· η άμεση κλήση της draw(_:) απαγορεύεται
  • Ταυτοδυναμία — η draw(_:) πρέπει να δίνει το ίδιο αποτέλεσμα με τα ίδια δεδομένα εισόδου για σωστή προσωρινή αποθήκευση
  • UIBezierPath — δημιουργείτε διαδρομές εκτός της draw(_:) για να ελαχιστοποιήσετε το φορτίο CPU σε κάθε κλήση
  • Παράμετρος rect — περιέχει την περιοχή επανασχεδίασης· χρησιμοποιήστε intersection για βελτιστοποίηση — μην σχεδιάζετε εκτός των ορίων της
  • Καταγραφή προφίλ — ελέγξτε την απόδοση της draw(_:) μέσω του Instruments Core Animation για τον εντοπισμό αργών λειτουργιών

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

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

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

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