Anpassade vyer är grunden för det unika gränssnittet i alla mobila appar. Anpassade vyer i mobila appar låter dig skapa din egen grafik, animationer och element som inte finns i standardkomponenter. Enligt Apple Developer, 2025 förblir drawRect den primära metoden för anpassad ritning i iOS-mobilutveckling. På Android spelar onDraw med Canvas samma roll, och i Flutter — CustomPainter.
Huvudpunkter
Anpassade vyer är användardefinierade gränssnittskomponenter som åsidosätter standardmetoder för ritning och mätning för att skapa unik grafik. Grafer, animationer, diagram, anpassade chatelement och AR-masker — det här är typiska användningsscenarier för anpassade vyer i mobila appar. Standard UI-komponenter (Button, TextView, Label) täcker 80% av uppgifterna, men de återstående 20% kräver full kontroll över ritning i mobil utveckling. Ritning i mobil utveckling är en färdighet som skiljer en juniorutvecklare från en senior.
Anpassade vyer i mobila appar behövs för unik 2D-grafik, komplexa animationer, diagram och datavisualisering. Om standard UIKit inte tillhandahåller det nödvändiga elementet — skapas en anpassad vy. I mobila appar används anpassade vyer för förloppsindikatorer, anpassade knappar, gradientfyllningar, animerade ikoner och kanvas för fingerritning. Det är viktigt att inte överanvända — för enkla stilar räcker det att konfigurera standardkomponenter via utseende-API:et.
På iOS skapas anpassade vyer genom att ärva UIView och åsidosätta drawRect-metoden. drawRect anropas vid första renderingen och efter varje setNeedsDisplay()-anrop. Inuti drawRect får utvecklaren en CGContext via UIGraphicsGetCurrentContext() — en kanvas för linjer, former, gradienter och text. Core Graphics (Quartz 2D) är en lågnivå CPU-baserad renderingsmotor som ger full kontroll över varje pixel. UIBezierPath är ett bekvämt omslag för att skapa sökvägar med koncis syntax.
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 tillhandahåller CGContext — en kanvas för alla ritoperationer. CGGradient skapar linjära och radiella gradienter med stöd för CGColorSpace (sRGB, Display P3). Transformationer utförs via CGAffineTransform — en 3x3-matris för rotation, skalning och förskjutning. CGContextTranslateCTM, CGContextRotateCTM och CGContextScaleCTM ändrar koordinatsystemet. Core Graphics körs på CPU:n, så komplex grafik kan belasta processorn — använd Metal för GPU-acceleration.
UIBezierPath är ett objektorienterat omslag över CGPath. Metoden addArc(withCenter:radius:startAngle:endAngle:clockwise:) skapar bågar och cirklar, addCurve skapar Bézier-kurvor. Sökvägar stöder union-, snitt- och subtraktionsoperationer via addClip. UIBezierPath är praktiskt för anpassade masker (maskView), klippningssökvägar och komplexa former — stjärnor, hjärtan, anpassade ikoner.
På Android utökar en anpassad vy View med åsidosatt onDraw(Canvas). Canvas innehåller alla ritmetoder i mobil utveckling: drawCircle, drawRect, drawLine, drawText, drawBitmap, drawPath och drawRoundRect. Paint konfigurerar parametrar — färg, linjetjocklek, stil (FILL, STROKE, FILL_AND_STROKE), kantutjämning (isAntiAlias) och textstorlek. onDraw anropas vid varje invalidate() och bör inte innehålla tunga operationer — alla beräkningar cachelagras.
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 på Android är motsvarigheten till UIBezierPath: addCircle, addRect, addOval, lineTo, cubicTo och quadTo. PathMeasure beräknar sökvägslängd och koordinater vid valfri punkt — användbart för att animera objektets rörelse längs en sökväg. För animationer används ValueAnimator med invalidate() i återanropet. Animatorn kan ändra färg, radie, position — vilken egenskap som helst som påverkar ritning. Anpassade vyer på Android stöder även StateListDrawable och animated-vector för animation via XML.
Gradienter på Android ställs in via Shader — LinearGradient, RadialGradient och SweepGradient. Shader tilldelas Paint via paint.setShader(shader). LinearGradient genererar en linjär gradient mellan två punkter med valfria färgpositioner. RadialGradient — radiell från centrum till kanter. SweepGradient — konisk runt en mittpunkt. Shader.TileMode (CLAMP, REPEAT, MIRROR) definierar beteende utanför områdets gränser.
Flutter använder CustomPainter — en klass med metoderna paint(Canvas canvas, Size size) och shouldRepaint. Canvas i Flutter tillhandahåller samma metoder som i Android: drawCircle, drawRect, drawLine, drawPath, drawParagraph och drawImage. Paint i Flutter konfigurerar färg, tjocklek, stil och kantutjämning. Flutter använder inte drawRect eller onDraw — all ritning sker via CustomPainter, inbäddad i CustomPaint-widgeten.
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 avgör om CustomPainter behöver ritas om. Returnera endast true när data som påverkar renderingen ändras — färg, storlek, diagramdata. RepaintBoundary isolerar omritning i ett separat lager — använd för widgetar som sällan ändras. Flutter har inget setNeedsDisplay eller invalidate — omritning utlöses via setState eller ValueNotifier med lyssning på ändringar.
Flutter stöder LinearGradient, RadialGradient och SweepGradient — samma typer som i Android. Gradient skapar en Shader för Paint via createShader(Rect bounds). Transformationer utförs via canvas.rotate, canvas.scale, canvas.translate och canvas.skew. Flutter använder en 4x4-transformationsmatris (Matrix4) för Transform-widgeten. Ritningsanimationer i Flutter görs med AnimationController i kombination med setState för att rita om CustomPainter.
Korrekt mätning av anpassade vyer är grunden för förutsägbart gränssnittsbeteende. På iOS bestäms Vyens storlek via sizeThatFits och intrinsicContentSize. sizeThatFits returnerar den optimala storleken för givna begränsningar. intrinsicContentSize — den naturliga innehållsstorleken utan externa begränsningar. På Android implementeras mätning i onMeasure med MeasureSpec-parametrar (UNSPECIFIED, EXACTLY, AT_MOST). I Flutter överförs storleken till paint(Canvas, Size) och kan beräknas via computeDryLayout eller LayoutBuilder.
intrinsicContentSize returnerar ett CGSize — Vyens naturliga storlek baserat på dess innehåll. setContentHuggingPriority och setContentCompressionResistancePriority styr beteendet vid sträckning och kompression. sizeThatFits anropas av systemet för att beräkna storlek i Auto Layout. För anpassade vyer, åsidosätt intrinsicContentSize om din komponent har en naturlig storlek — till exempel bör en cirkel alltid vara 100x100.
onMeasure accepterar widthMeasureSpec och heightMeasureSpec. MeasureSpec innehåller läge och storlek: UNSPECIFIED (inga begränsningar, Vyn väljer sin egen storlek), EXACTLY (exakt storlek från förälder), AT_MOST (maximal storlek). setMeasuredDimension(width, height) sparar mätresultatet. När data ändras, anropa requestLayout() för omätning. Kapslade ViewGroup kräver åsidosättande av onLayout för att arrangera underordnade element.
| Plattform | Mätmetod | Anrop vid ändring | Retur |
|---|---|---|---|
| iOS | sizeThatFits / intrinsicContentSize | setNeedsLayout / invalidateIntrinsicContentSize | CGSize |
| Android | onMeasure / setMeasuredDimension | requestLayout / invalidate | void (sparar storlek) |
| Flutter | computeDryLayout | setState / LayoutBuilder | Size |
computeDryLayout är en RenderObject-metod för att beräkna storlek utan en fullständig layoutcykel. LayoutBuilder.getSize — ett alternativ för att få tillgängligt utrymme i build-metoden. I Flutter överförs kanvasstorleken till paint som Size och kräver ingen separat mätning — CustomPainter ritar inom de överförda gränserna. För anpassade RenderObject måste du dock åsidosätta performLayout och computeDryLayout.
Vanliga frågor
drawRect är en UIView-metod för anpassad ritning via Core Graphics. Den anropas vid första renderingen av Vyn och efter varje setNeedsDisplay()-anrop. Använd CGContext i drawRect för linjer, former och text.
Skapa en klass som utökar View, åsidosätt onDraw(Canvas canvas) och använd Canvas API: drawCircle, drawRect, drawPath. Paint konfigurerar färg, tjocklek och stil. För att uppdatera, anropa invalidate().
CustomPainter är en Flutter-klass för anpassad ritning på Canvas. Implementera metoderna paint(Canvas, Size) och shouldRepaint. Den placeras via CustomPaint-widgeten. Flutter har ingen drawRect — all ritning sker via CustomPainter.
På iOS använd sizeThatFits och intrinsicContentSize. På Android — onMeasure med MeasureSpec. I Flutter överförs storleken till paint(Canvas, Size) eller beräknas via computeDryLayout. Varje plattform använder sin egen mätmetod.
Anpassade vyer behövs för unik grafik, animationer, diagram och element som inte kan implementeras med standard UIKit eller View. För enkla stilar räcker det att konfigurera standardkomponenter — komplicera inte arkitekturen i onödan.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.