Прилагођени прикази су основа јединственог интерфејса сваке мобилне апликације. Прилагођени прикази у мобилним апликацијама омогућавају креирање сопствене графике, анимација и елемената недоступних у стандардним компонентама. Према Apple Developer, 2025, drawRect остаје основни метод прилагођеног цртања у мобилном развоју на iOS-у. На Android-у исту улогу има onDraw са Canvas-ом, а у Flutter-у — CustomPainter.
Главно
Прилагођени прикази су кориснички дефинисане интерфејс компоненте које преписују стандардне методе цртања и мерења за креирање јединствене графике. Графикони, анимације, дијаграми, прилагођени елементи ћаскања и AR маске — типични сценарији примене прилагођених приказа у мобилним апликацијама. Стандардне UI компоненте (Button, TextView, Label) покривају 80% задатака, али преосталих 20% захтева потпуну контролу над цртањем у мобилном развоју. Цртање у мобилном развоју је вештина која разликује junior програмера од senior-а.
Прилагођени прикази у мобилним апликацијама су потребни за јединствену 2D графику, сложене анимације, графиконе и визуелизацију података. Ако стандардни UIKit не пружа потребан елемент — креира се прилагођени приказ. У мобилним апликацијама, прилагођени прикази се користе за траке напретка, прилагођена дугмад, градијентна пуњења, анимиране иконе и платна за цртање прстом. Важно је не злоупотребљавати — за једноставне стилове довољно је подесити стандардне компоненте кроз Appearance API.
На iOS-у, прилагођени прикази се креирају наслеђивањем UIView-а и преписивањем метода drawRect. drawRect се позива при првом рендеровању и након сваког позива setNeedsDisplay(). Унутар drawRect-а, програмер добија CGContext кроз UIGraphicsGetCurrentContext() — платно за линије, облике, градијенте и текст. Core Graphics (Quartz 2D) је нисконivoнски 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-у, па сложена графика може оптеретити процесор — за GPU убрзање користите Metal.
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. године. Саветоваћемо вас и предложити најбоље решење.