draw(_:) / drawRect — είναι η μέθοδος της κλάσης UIView (στη Swift) και το ανάλογό της στην Objective-C (drawRect:), που είναι υπεύθυνη για την απόδοση του περιεχομένου του view χρησιμοποιώντας το Core Graphics. Το σύστημα καλεί αυτόματα αυτή τη μέθοδο κατά την πρώτη εμφάνιση του view στην οθόνη και μετά την κλήση της setNeedsDisplay(). Σύμφωνα με το Apple Documentation (2025), η draw(_:) είναι η μόνη μέθοδος όπου ο προγραμματιστής έχει πρόσβαση στο γραφικό περιβάλλον της τρέχουσας οθόνης για προσαρμοσμένη σχεδίαση. Η παράκαμψη της draw(_:) δίνει πλήρη έλεγχο πάνω στην εμφάνιση του component — από απλά γεωμετρικά σχήματα μέχρι σύνθετα κινούμενα γραφικά.
Κύρια σημεία
draw(_:) — είναι μια μέθοδος στιγμιοτύπου UIView που το UIKit καλεί για την απόδοση του περιεχομένου του view. Εντός αυτής της μεθόδου, ο προγραμματιστής αποκτά πρόσβαση στο γραφικό περιβάλλον CGContext και χρησιμοποιεί το API του Core Graphics για να σχεδιάσει γραμμές, γεμίσματα, κείμενο και εικόνες. Η drawRect: στην Objective-C εκτελεί την ίδια λειτουργία, αλλά με διαφορετική σύνταξη: η μοναδική παράμετρος είναι ένα CGRect που καθορίζει την περιοχή επανασχεδίασης.
Σύμφωνα με το Apple Engineering (2024), η draw(_:) λειτουργεί μέσω CPU-rendering Bitmap Graphics Context, που παρέχει μέγιστη ευελιξία, αλλά απαιτεί περισσότερους πόρους σε σύγκριση με το CALayer. Η απόφαση χρήσης της draw(_:) λαμβάνεται με βάση την πολυπλοκότητα των γραφικών και τις απαιτήσεις απόδοσης.
Στη Swift, η μέθοδος δηλώνεται ως override func draw(_ rect: CGRect), όπου rect είναι το ορθογώνιο που πρέπει να επανασχεδιαστεί. Στην Objective-C, η υπογραφή είναι - (void)drawRect:(CGRect)rect. Η παράμετρος rect μπορεί να είναι μικρότερη από τα bounds του view σε μερική επανασχεδίαση μέσω της setNeedsDisplay(_:).
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
}
Το σύστημα καλεί αυτόματα την draw(_:) σε αυστηρά καθορισμένα σενάρια. Η κατανόηση αυτών των ενεργοποιητών βοηθά στην αποφυγή περιττών επανασχεδιάσεων και στη βελτιστοποίηση της απόδοσης του view. Παρακάτω παρατίθενται όλες οι περιπτώσεις αυτόματης κλήσης της μεθόδου.
Η τεκμηρίωση της Apple προειδοποιεί: μην καλείτε ποτέ την draw(_:) απευθείας. Το σύστημα αποφασίζει μόνο του πότε θα εκτελέσει τη σχεδίαση και η άμεση κλήση διαταράσσει τον εσωτερικό μηχανισμό προσωρινής αποθήκευσης. Για να ζητήσετε επανασχεδίαση, χρησιμοποιείτε πάντα την setNeedsDisplay() ή την setNeedsDisplay(_:).
Η παράκαμψη της draw(_:) στη Swift ξεκινά με τη λήψη του γραφικού περιβάλλοντος και τις επακόλουθες κλήσεις Core Graphics. Συνιστάται η δημιουργία ξεχωριστών μεθόδων για λογικά τμήματα σχεδίασης — αυτό βελτιώνει την αναγνωσιμότητα και τη δυνατότητα δοκιμής του κώδικα.
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(_:) εάν το περιεχόμενο του view δεν έχει αλλάξει. Μην χρησιμοποιείτε εντός της 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 |
| Κλήση super | super.draw(rect) | [super drawRect:rect] |
| Περιβάλλον | UIGraphicsGetCurrentContext() | UIGraphicsGetCurrentContext() |
| Παράμετρος rect | rect: CGRect | CGRect rect |
| Απόδοση | Ταυτόσημη | Ταυτόσημη |
Κατά τη μετεγκατάσταση έργου από Objective-C σε Swift, η μετονομασία της μεθόδου είναι μία από τις πρώτες εργασίες. Το Xcode παρέχει αυτόματο μετατροπέα, αλλά η drawRect: απαιτεί χειροκίνητη ενημέρωση σε draw(_:). Σύμφωνα με την Apple (2024), η έκδοση Swift draw(_:) προτιμάται για νέα έργα.
Η draw(_:) εκτελείται στην CPU και μια μη βέλτιστη υλοποίηση μπορεί να προκαλέσει πτώση καρέ και χαμηλή απόδοση. Η Apple συνιστά αρκετές δοκιμασμένες προσεγγίσεις για την επιτάχυνση της σχεδίασης.
Κάθε λειτουργία Core Graphics (move(to:), addLine(to:), strokePath) έχει επιβάρυνση. Ομαδοποιήστε τις λειτουργίες και χρησιμοποιήστε CGPath για σύνθετα σχήματα — η διαδρομή δημιουργείται μία φορά και επαναχρησιμοποιείται σε κάθε κλήση της draw(_:).
UIBezierPath — είναι ένα περιτύλιγμα Objective-C γύρω από το CGPath που παρέχει ένα απλό API για τη δημιουργία σχημάτων. Δημιουργήστε το UIBezierPath εκ των προτέρων (π.χ. στον αρχικοποιητή) και απλώς καλέστε την fill() ή την stroke() εντός της draw(_:).
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(_:). Η γνώση αυτών των μοτίβων βοηθά στην αποφυγή σφαλμάτων και μείωσης της απόδοσης. Ας εξετάσουμε τα πιο συνηθισμένα προβλήματα και τις λύσεις τους.
Ακολουθώντας αυτούς τους κανόνες, θα εξασφαλίσετε σταθερή και γρήγορη σχεδίαση προσαρμοσμένων views σε οποιοδήποτε έργο iOS. Καταγράψτε το προφίλ της draw(_:) μέσω του Instruments (Core Animation) για να δείτε τον πραγματικό χρόνο εκτέλεσης και τα σημεία συμφόρησης.
Συχνές Ερωτήσεις
Όχι, η άμεση κλήση της draw(_:) απαγορεύεται από την τεκμηρίωση της Apple. Το σύστημα διαχειρίζεται μόνο του τον κύκλο σχεδίασης. Για να ζητήσετε επανασχεδίαση, χρησιμοποιήστε την setNeedsDisplay(), η οποία σημειώνει σωστά το view ως χρήζον ενημέρωσης στον πλησιέστερο κύκλο απόδοσης.
Λειτουργικά αυτές οι μέθοδοι είναι ταυτόσημες. Η drawRect: χρησιμοποιείται στην Objective-C, η draw(_:) — στη Swift. Και οι δύο λαμβάνουν ένα CGRect της περιοχής επανασχεδίασης και χρησιμοποιούν το ίδιο περιβάλλον Core Graphics μέσω της UIGraphicsGetCurrentContext().
Η Apple βελτιστοποιεί την απόδοση: εάν το UIView δεν έχει παρακάμψει την draw(_:), το σύστημα δεν δημιουργεί περιβάλλον bitmap για αυτό. Αυτό εξοικονομεί μνήμη. Εάν υπάρχει παράκαμψη αλλά η μέθοδος δεν καλείται — ελέγξτε ότι το πλαίσιο του view δεν είναι μηδενικό και το view είναι ορατό.
Μόνο όταν χρειάζεται: πρώτη απόδοση, μετά την setNeedsDisplay(), κατά την αλλαγή bounds με contentMode = .redraw. Σε στατική κατάσταση, η draw(_:) δεν καλείται ξανά, εξοικονομώντας πόρους CPU και μπαταρίας.
Η Apple συνιστά την κλήση της super.draw(rect) στην αρχή της παρακαμφθείσας μεθόδου. Αν και η τρέχουσα υλοποίηση του UIView είναι κενή, η κλήση super εξασφαλίζει συμβατότητα με μελλοντικές εκδόσεις του UIKit και αποτελεί καλή πρακτική.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης