Персонализираните изгледи са основата на уникалния интерфейс на всяко мобилно приложение. Персонализираните изгледи в мобилните приложения позволяват създаването на собствена графика, анимации и елементи, недостъпни в стандартните компоненти. Според 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-базиран двигател за рендериране, който дава пълен контрол над всеки пиксел. 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 създава криви на Безие. Пътищата поддържат операции обединение, сечение и изваждане чрез 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, вграден в джаджата 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 изолира прерисуването в отделен слой — използвайте за джаджи, които рядко се променят. 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) за джаджата 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. Поставя се чрез джаджата CustomPaint. Flutter няма drawRect — цялото рисуване става чрез CustomPainter.
На iOS използвайте sizeThatFits и intrinsicContentSize. На Android — onMeasure с MeasureSpec. Във Flutter размерът се предава на paint(Canvas, Size) или се изчислява чрез computeDryLayout. Всяка платформа използва свой собствен метод за измерване.
Персонализираните изгледи са необходими за уникална графика, анимации, графики и елементи, които не могат да бъдат реализирани със стандартен UIKit или View. За прости стилове е достатъчно да конфигурирате стандартните компоненти — не усложнявайте архитектурата без необходимост.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.