Οι προσαρμοσμένες Προβολές αποτελούν τη βάση της μοναδικής διεπαφής κάθε εφαρμογής κινητού. Οι προσαρμοσμένες Προβολές σε εφαρμογές κινητών σας επιτρέπουν να δημιουργείτε τα δικά σας γραφικά, κινούμενα σχέδια και στοιχεία που δεν είναι διαθέσιμα σε τυπικά εξαρτήματα. Σύμφωνα με το Apple Developer, 2025, το drawRect παραμένει η κύρια μέθοδος προσαρμοσμένης σχεδίασης στην κινητή ανάπτυξη iOS. Στο Android, το onDraw με το Canvas παίζει τον ίδιο ρόλο, και στο Flutter — το CustomPainter.
Βασικά Σημεία
Οι προσαρμοσμένες Προβολές είναι εξαρτήματα διεπαφής που ορίζονται από τον χρήστη και παρακάμπτουν τις τυπικές μεθόδους σχεδίασης και μέτρησης για να δημιουργήσουν μοναδικά γραφικά. Γραφήματα, κινούμενα σχέδια, διαγράμματα, προσαρμοσμένα στοιχεία συνομιλίας και μάσκες AR — αυτά είναι τυπικά σενάρια χρήσης προσαρμοσμένων Προβολών σε εφαρμογές κινητών. Τα τυπικά εξαρτήματα UI (Button, TextView, Label) καλύπτουν το 80% των εργασιών, αλλά το υπόλοιπο 20% απαιτεί πλήρη έλεγχο της σχεδίασης στην κινητή ανάπτυξη. Η σχεδίαση στην κινητή ανάπτυξη είναι μια δεξιότητα που διακρίνει έναν junior προγραμματιστή από έναν senior.
Οι προσαρμοσμένες Προβολές σε εφαρμογές κινητών είναι απαραίτητες για μοναδικά 2D γραφικά, σύνθετες κινούμενες εικόνες, γραφήματα και οπτικοποίηση δεδομένων. Εάν το τυπικό UIKit δεν παρέχει το απαιτούμενο στοιχείο — δημιουργείται μια προσαρμοσμένη Προβολή. Σε εφαρμογές κινητών, οι προσαρμοσμένες Προβολές χρησιμοποιούνται για γραμμές προόδου, προσαρμοσμένα κουμπιά, γεμίσματα κλίσης, κινούμενα εικονίδια και καμβάδες σχεδίασης με το δάχτυλο. Είναι σημαντικό να μην γίνεται κατάχρηση — για απλά στυλ, αρκεί η διαμόρφωση των τυπικών εξαρτημάτων μέσω του API εμφάνισης.
Στο iOS, οι προσαρμοσμένες Προβολές δημιουργούνται με κληρονομικότητα από το UIView και παράκαμψη της μεθόδου drawRect. Το drawRect καλείται στην πρώτη απόδοση και μετά από κάθε κλήση setNeedsDisplay(). Μέσα στο drawRect, ο προγραμματιστής λαμβάνει ένα CGContext μέσω του UIGraphicsGetCurrentContext() — έναν καμβά για γραμμές, σχήματα, κλίσεις και κείμενο. Το Core Graphics (Quartz 2D) είναι μια μηχανή απόδοσης χαμηλού επιπέδου βασισμένη σε CPU που παρέχει πλήρη έλεγχο σε κάθε pixel. Το UIBezierPath είναι ένα βολικό περιτύλιγμα για τη δημιουργία διαδρομών με συνοπτική σύνταξη.
class CircleView: UIView {
private var fillColor: UIColor = .systemBlue
override func drawRect(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.setFillColor(fillColor.cgColor)
ctx.addEllipse(in: bounds.insetBy(dx: 4, dy: 4))
ctx.drawPath(using: .fill)
}
func updateColor(_ newColor: UIColor) {
fillColor = newColor
setNeedsDisplay()
}
}
Το Core Graphics παρέχει το CGContext — έναν καμβά για όλες τις λειτουργίες σχεδίασης. Το CGGradient δημιουργεί γραμμικές και ακτινικές κλίσεις με υποστήριξη CGColorSpace (sRGB, Display P3). Οι μετασχηματισμοί εκτελούνται μέσω του CGAffineTransform — ενός πίνακα 3x3 για περιστροφή, κλιμάκωση και μετατόπιση. Τα CGContextTranslateCTM, CGContextRotateCTM και CGContextScaleCTM τροποποιούν το σύστημα συντεταγμένων. Το Core Graphics λειτουργεί στην CPU, επομένως τα σύνθετα γραφικά μπορούν να φορτώσουν τον επεξεργαστή — χρησιμοποιήστε Metal για επιτάχυνση GPU.
Το UIBezierPath είναι ένα αντικειμενοστραφές περιτύλιγμα πάνω από το CGPath. Η μέθοδος addArc(withCenter:radius:startAngle:endAngle:clockwise:) δημιουργεί τόξα και κύκλους, η addCurve δημιουργεί καμπύλες Bézier. Οι διαδρομές υποστηρίζουν λειτουργίες ένωσης, τομής και αφαίρεσης μέσω addClip. Το UIBezierPath είναι βολικό για προσαρμοσμένες μάσκες (maskView), διαδρομές αποκοπής και σύνθετα σχήματα — αστέρια, καρδιές, προσαρμοσμένα εικονίδια.
Στο Android, μια προσαρμοσμένη Προβολή επεκτείνει το View με παράκαμψη του onDraw(Canvas). Το Canvas περιέχει όλες τις μεθόδους σχεδίασης στην κινητή ανάπτυξη: drawCircle, drawRect, drawLine, drawText, drawBitmap, drawPath και drawRoundRect. Το Paint διαμορφώνει παραμέτρους — χρώμα, πάχος γραμμής, στυλ (FILL, STROKE, FILL_AND_STROKE), εξομάλυνση (isAntiAlias) και μέγεθος κειμένου. Το onDraw καλείται σε κάθε invalidate() και δεν πρέπει να περιέχει βαριές λειτουργίες — όλοι οι υπολογισμοί αποθηκεύονται στην προσωρινή μνήμη.
class CustomCircleView(context: Context, attrs: AttributeSet?)
: View(context, attrs) {
private val paint = Paint().apply {
style = Paint.Style.FILL
color = Color.BLUE
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
val radius = minOf(cx, cy) - 4f
canvas.drawCircle(cx, cy, radius, paint)
}
}
Το Path στο Android είναι το αντίστοιχο του UIBezierPath: addCircle, addRect, addOval, lineTo, cubicTo και quadTo. Το PathMeasure υπολογίζει το μήκος διαδρομής και συντεταγμένες σε οποιοδήποτε σημείο — χρήσιμο για κινούμενα σχέδια κίνησης ενός αντικειμένου κατά μήκος μιας διαδρομής. Για κινούμενα σχέδια, χρησιμοποιείται το ValueAnimator με invalidate() στην επιστροφή κλήσης. Ο κινητήρας μπορεί να αλλάξει χρώμα, ακτίνα, θέση — οποιαδήποτε ιδιότητα επηρεάζει τη σχεδίαση. Οι προσαρμοσμένες Προβολές στο Android υποστηρίζουν επίσης StateListDrawable και animated-vector για κινούμενα σχέδια μέσω XML.
Οι κλίσεις στο Android ορίζονται μέσω Shader — LinearGradient, RadialGradient και SweepGradient. Το Shader εκχωρείται στο Paint μέσω paint.setShader(shader). Το LinearGradient δημιουργεί μια γραμμική κλίση μεταξύ δύο σημείων με προαιρετικές θέσεις χρωμάτων. RadialGradient — ακτινική από το κέντρο προς τις άκρες. SweepGradient — κωνική γύρω από ένα κεντρικό σημείο. Το Shader.TileMode (CLAMP, REPEAT, MIRROR) καθορίζει τη συμπεριφορά πέρα από τα όρια της περιοχής.
Το Flutter χρησιμοποιεί το CustomPainter — μια κλάση με μεθόδους paint(Canvas canvas, Size size) και shouldRepaint. Το Canvas στο Flutter παρέχει τις ίδιες μεθόδους όπως στο Android: drawCircle, drawRect, drawLine, drawPath, drawParagraph και drawImage. Το Paint στο Flutter διαμορφώνει χρώμα, πάχος, στυλ και εξομάλυνση. Το Flutter δεν χρησιμοποιεί drawRect ή onDraw — όλη η σχεδίαση γίνεται μέσω CustomPainter, ενσωματωμένου στο widget CustomPaint.
class CirclePainter extends CustomPainter {
final Color color;
CirclePainter({required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.fill
..isAntiAlias = true;
final radius = size.shortestSide / 2 - 4;
canvas.drawCircle(size.center(Offset.zero), radius, paint);
}
@override
bool shouldRepaint(CirclePainter oldDelegate) =>
oldDelegate.color != color;
}
Το shouldRepaint καθορίζει εάν το CustomPainter χρειάζεται να σχεδιαστεί ξανά. Επιστρέψτε true μόνο όταν αλλάζουν δεδομένα που επηρεάζουν την απόδοση — χρώμα, μέγεθος, δεδομένα γραφήματος. Το RepaintBoundary απομονώνει την εκ νέου σχεδίαση σε ξεχωριστό επίπεδο — χρησιμοποιήστε το για widget που αλλάζουν σπάνια. Το Flutter δεν έχει setNeedsDisplay ή invalidate — η εκ νέου σχεδίαση ενεργοποιείται μέσω setState ή ValueNotifier με ακρόαση αλλαγών.
Το Flutter υποστηρίζει LinearGradient, RadialGradient και SweepGradient — τους ίδιους τύπους όπως στο Android. Το Gradient δημιουργεί ένα Shader για το Paint μέσω createShader(Rect bounds). Οι μετασχηματισμοί εκτελούνται μέσω canvas.rotate, canvas.scale, canvas.translate και canvas.skew. Το Flutter χρησιμοποιεί έναν πίνακα μετασχηματισμού 4x4 (Matrix4) για το widget Transform. Τα κινούμενα σχέδια σχεδίασης στο Flutter γίνονται με το AnimationController σε συνδυασμό με το setState για εκ νέου σχεδίαση του CustomPainter.
Η σωστή μέτρηση των προσαρμοσμένων Προβολών είναι η βάση της προβλέψιμης συμπεριφοράς διεπαφής. Στο iOS, το μέγεθος της Προβολής καθορίζεται μέσω sizeThatFits και intrinsicContentSize. Το sizeThatFits επιστρέφει το βέλτιστο μέγεθος για δεδομένους περιορισμούς. Το intrinsicContentSize — το φυσικό μέγεθος περιεχομένου χωρίς εξωτερικούς περιορισμούς. Στο Android, η μέτρηση υλοποιείται στο onMeasure με παραμέτρους MeasureSpec (UNSPECIFIED, EXACTLY, AT_MOST). Στο Flutter, το μέγεθος μεταβιβάζεται στο paint(Canvas, Size) και μπορεί να υπολογιστεί μέσω computeDryLayout ή LayoutBuilder.
Το intrinsicContentSize επιστρέφει ένα CGSize — το φυσικό μέγεθος της Προβολής βάσει του περιεχομένου της. Οι setContentHuggingPriority και setContentCompressionResistancePriority ελέγχουν τη συμπεριφορά κατά το τέντωμα και τη συμπίεση. Το sizeThatFits καλείται από το σύστημα για τον υπολογισμό μεγέθους στο Auto Layout. Για προσαρμοσμένες Προβολές, παρακάμψτε το intrinsicContentSize εάν το εξάρτημά σας έχει φυσικό μέγεθος — για παράδειγμα, ένας κύκλος πρέπει πάντα να είναι 100x100.
Το onMeasure δέχεται widthMeasureSpec και heightMeasureSpec. Το MeasureSpec περιέχει λειτουργία και μέγεθος: UNSPECIFIED (χωρίς περιορισμούς, η Προβολή επιλέγει το δικό της μέγεθος), EXACTLY (ακριβές μέγεθος από τον γονέα), AT_MOST (μέγιστο μέγεθος). Το setMeasuredDimension(width, height) αποθηκεύει το αποτέλεσμα μέτρησης. Όταν αλλάζουν δεδομένα, καλέστε το requestLayout() για επαναμέτρηση. Τα ένθετα ViewGroup απαιτούν παράκαμψη του onLayout για τη διάταξη των θυγατρικών στοιχείων.
| Πλατφόρμα | Μέθοδος μέτρησης | Κλήση κατά αλλαγή | Επιστροφή |
|---|---|---|---|
| iOS | sizeThatFits / intrinsicContentSize | setNeedsLayout / invalidateIntrinsicContentSize | CGSize |
| Android | onMeasure / setMeasuredDimension | requestLayout / invalidate | void (αποθηκεύει μέγεθος) |
| Flutter | computeDryLayout | setState / LayoutBuilder | Size |
Το computeDryLayout είναι μια μέθοδος RenderObject για τον υπολογισμό μεγέθους χωρίς πλήρη κύκλο διάταξης. LayoutBuilder.getSize — μια εναλλακτική για τη λήψη διαθέσιμου χώρου στη μέθοδο build. Στο Flutter, το μέγεθος του καμβά μεταβιβάζεται στο paint ως Size και δεν απαιτεί ξεχωριστή μέτρηση — το CustomPainter σχεδιάζει εντός των μεταβιβασμένων ορίων. Ωστόσο, για προσαρμοσμένα RenderObject, πρέπει να παρακάμψετε τα performLayout και computeDryLayout.
Συχνές Ερωτήσεις
drawRect είναι μια μέθοδος UIView για προσαρμοσμένη σχεδίαση μέσω Core Graphics. Καλείται στην πρώτη απόδοση της Προβολής και μετά από κάθε κλήση setNeedsDisplay(). Χρησιμοποιήστε το CGContext μέσα στο drawRect για γραμμές, σχήματα και κείμενο.
Δημιουργήστε μια κλάση που επεκτείνει το View, παρακάμψτε το onDraw(Canvas canvas) και χρησιμοποιήστε το Canvas API: drawCircle, drawRect, drawPath. Το Paint διαμορφώνει χρώμα, πάχος και στυλ. Για ενημέρωση, καλέστε το invalidate().
CustomPainter είναι μια κλάση Flutter για προσαρμοσμένη σχεδίαση στο Canvas. Υλοποιήστε τις μεθόδους paint(Canvas, Size) και shouldRepaint. Τοποθετείται μέσω του widget CustomPaint. Το Flutter δεν έχει drawRect — όλη η σχεδίαση γίνεται μέσω CustomPainter.
Στο iOS χρησιμοποιήστε τα sizeThatFits και intrinsicContentSize. Στο Android — το onMeasure με MeasureSpec. Στο Flutter, το μέγεθος μεταβιβάζεται στο paint(Canvas, Size) ή υπολογίζεται μέσω computeDryLayout. Κάθε πλατφόρμα χρησιμοποιεί τη δική της μέθοδο μέτρησης.
Οι προσαρμοσμένες Προβολές είναι απαραίτητες για μοναδικά γραφικά, κινούμενα σχέδια, γραφήματα και στοιχεία που δεν μπορούν να υλοποιηθούν με τυπικό UIKit ή View. Για απλά στυλ, αρκεί η διαμόρφωση των τυπικών εξαρτημάτων — μην περιπλέκετε την αρχιτεκτονική χωρίς λόγο.
Περίληψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.