Skip to content

Canvas & Metal/OpenGL ES vs. Native UI ​

Canvas, Metal/OpenGL ve native UI seçimi; çizim özgürlüğü, performans ve bakım maliyeti arasında verilen karardır. Standart bileşen yeterliyse native UI ile kalmak en ucuz yoldur.

Hızlı Karar ​

DurumTercihDikkat
Standart form/liste UINative UIErişilebilirlik hazır gelir
Özel 2D çizimCanvasInvalidasyon alanı sınırlanmalı
Yüksek performans grafikMetal/OpenGLCihaz ve driver farkı artar
Basit dekoratif animasyonFramework animasyonuCustom renderer gereksizdir

Üretim Kontrol Listesi ​

  • Problem: Native UI hangi ihtiyacı karşılamıyor?
  • Çözüm: Render path, invalidation, touch handling, accessibility ve fallback net mi?
  • Trade-off: Custom çizim özgürlük sağlar; accessibility, test ve bakım maliyeti ekler.
  • Hata durumu: Overdraw, missed invalidation, memory spike, GPU fallback ve accessibility kaybı ele alınmalı.
  • Ölçüm: Frame time, draw calls, GPU memory, touch latency ve crash-free sessions izlenmeli.
  • Güvenlik/maliyet: Custom rendering ekran okuyucu desteğini bozabilir; düşük cihaz test maliyeti artar.

Canvas Tabanlı Özel Çizim ​

Android Canvas API ​

Android Canvas sistemi, 2D grafik çizimi için güçlü ve esnek bir framework sağlar. Donanım hızlandırma ile birlikte yüksek performanslı özel UI bileşenleri oluşturmaya olanak tanır.

Advanced Canvas Techniques ​

kotlin
class AdvancedCanvasView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val path = Path()
    private val matrix = Matrix()
    private val gradientShader: LinearGradient
    private val shadowPaint = Paint()
    
    // Animation properties
    private var animationProgress = 0f
    private val valueAnimator = ValueAnimator.ofFloat(0f, 1f)
    
    init {
        // Gradient shader oluştur
        gradientShader = LinearGradient(
            0f, 0f, 0f, 200f,
            intArrayOf(Color.CYAN, Color.BLUE, Color.MAGENTA),
            floatArrayOf(0f, 0.5f, 1f),
            Shader.TileMode.CLAMP
        )
        
        // Shadow paint ayarla
        shadowPaint.apply {
            color = Color.BLACK
            alpha = 100
            maskFilter = BlurMaskFilter(8f, BlurMaskFilter.Blur.NORMAL)
        }
        
        setupAnimator()
    }
    
    private fun setupAnimator() {
        valueAnimator.apply {
            duration = 2000
            repeatCount = ValueAnimator.INFINITE
            repeatMode = ValueAnimator.REVERSE
            interpolator = AccelerateDecelerateInterpolator()
            
            addUpdateListener { animator ->
                animationProgress = animator.animatedValue as Float
                invalidate()
            }
        }
        valueAnimator.start()
    }
    
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        
        val centerX = width / 2f
        val centerY = height / 2f
        val radius = (Math.min(width, height) / 3f) * (0.8f + 0.2f * animationProgress)
        
        // Background gradient
        drawBackgroundGradient(canvas)
        
        // Shadow layer
        drawShadowLayer(canvas, centerX, centerY + 10f, radius)
        
        // Main shape with complex path
        drawMainShape(canvas, centerX, centerY, radius)
        
        // Overlay effects
        drawOverlayEffects(canvas, centerX, centerY, radius)
    }
    
    private fun drawBackgroundGradient(canvas: Canvas) {
        paint.shader = gradientShader
        canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
        paint.shader = null
    }
    
    private fun drawShadowLayer(canvas: Canvas, x: Float, y: Float, radius: Float) {
        canvas.drawCircle(x, y, radius, shadowPaint)
    }
    
    private fun drawMainShape(canvas: Canvas, centerX: Float, centerY: Float, radius: Float) {
        path.reset()
        
        // Karmaşık path oluştur (yıldız benzeri şekil)
        val points = 8
        val outerRadius = radius
        val innerRadius = radius * 0.6f
        
        for (i in 0 until points * 2) {
            val angle = (i * Math.PI / points).toFloat()
            val currentRadius = if (i % 2 == 0) outerRadius else innerRadius
            val x = centerX + cos(angle) * currentRadius
            val y = centerY + sin(angle) * currentRadius
            
            if (i == 0) {
                path.moveTo(x, y)
            } else {
                path.lineTo(x, y)
            }
        }
        path.close()
        
        // Matrix transformations
        matrix.reset()
        matrix.postRotate(animationProgress * 360f, centerX, centerY)
        matrix.postScale(
            1f + animationProgress * 0.1f,
            1f + animationProgress * 0.1f,
            centerX,
            centerY
        )
        path.transform(matrix)
        
        // Fill with gradient
        paint.apply {
            style = Paint.Style.FILL
            color = Color.WHITE
            alpha = (255 * (0.8f + 0.2f * animationProgress)).toInt()
        }
        canvas.drawPath(path, paint)
        
        // Stroke
        paint.apply {
            style = Paint.Style.STROKE
            strokeWidth = 4f
            color = Color.BLACK
            alpha = 255
        }
        canvas.drawPath(path, paint)
    }
    
    private fun drawOverlayEffects(canvas: Canvas, centerX: Float, centerY: Float, radius: Float) {
        // Particle effect benzeri circles
        paint.style = Paint.Style.FILL
        for (i in 0 until 5) {
            val angle = (animationProgress * 360f + i * 72f) * Math.PI / 180f
            val distance = radius * 1.5f
            val x = centerX + cos(angle) * distance
            val y = centerY + sin(angle) * distance
            val particleRadius = 8f * (1f - animationProgress * 0.3f)
            
            paint.apply {
                color = Color.YELLOW
                alpha = (255 * (1f - animationProgress)).toInt()
            }
            canvas.drawCircle(x.toFloat(), y.toFloat(), particleRadius, paint)
        }
    }
    
    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        valueAnimator.cancel()
    }
}

Performance-Optimized Canvas Drawing ​

kotlin
class OptimizedCanvasRenderer {
    private val pathCache = mutableMapOf<String, Path>()
    private val paintCache = mutableMapOf<String, Paint>()
    private val bitmapCache = LruCache<String, Bitmap>(20)
    
    fun drawOptimizedChart(
        canvas: Canvas,
        dataPoints: List<PointF>,
        bounds: RectF
    ) {
        // Path caching
        val pathKey = "chart_${dataPoints.hashCode()}"
        val path = pathCache.getOrPut(pathKey) {
            createChartPath(dataPoints, bounds)
        }
        
        // Paint caching
        val linePaint = paintCache.getOrPut("line_paint") {
            Paint(Paint.ANTI_ALIAS_FLAG).apply {
                style = Paint.Style.STROKE
                strokeWidth = 4f
                color = Color.BLUE
                strokeCap = Paint.Cap.ROUND
                strokeJoin = Paint.Join.ROUND
            }
        }
        
        val fillPaint = paintCache.getOrPut("fill_paint") {
            Paint(Paint.ANTI_ALIAS_FLAG).apply {
                style = Paint.Style.FILL
                shader = LinearGradient(
                    bounds.left, bounds.top,
                    bounds.left, bounds.bottom,
                    Color.BLUE, Color.TRANSPARENT,
                    Shader.TileMode.CLAMP
                )
                alpha = 100
            }
        }
        
        // Efficient drawing
        canvas.drawPath(path, fillPaint)
        canvas.drawPath(path, linePaint)
    }
    
    private fun createChartPath(dataPoints: List<PointF>, bounds: RectF): Path {
        val path = Path()
        
        if (dataPoints.isEmpty()) return path
        
        // Move to first point
        path.moveTo(dataPoints[0].x, dataPoints[0].y)
        
        // Smooth curve through points (Catmull-Rom spline)
        for (i in 1 until dataPoints.size - 1) {
            val p0 = if (i > 0) dataPoints[i - 1] else dataPoints[i]
            val p1 = dataPoints[i]
            val p2 = dataPoints[i + 1]
            val p3 = if (i < dataPoints.size - 2) dataPoints[i + 2] else dataPoints[i + 1]
            
            // Control points for smooth curve
            val cp1x = p1.x + (p2.x - p0.x) / 6f
            val cp1y = p1.y + (p2.y - p0.y) / 6f
            val cp2x = p2.x - (p3.x - p1.x) / 6f
            val cp2y = p2.y - (p3.y - p1.y) / 6f
            
            path.cubicTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y)
        }
        
        return path
    }
}

iOS Core Graphics ve Custom Drawing ​

Advanced Core Graphics Implementation ​

swift
class AdvancedDrawingView: UIView {
    private var animationProgress: CGFloat = 0
    private var displayLink: CADisplayLink?
    private let particleSystem = ParticleSystem()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupAnimation()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupAnimation()
    }
    
    private func setupAnimation() {
        displayLink = CADisplayLink(target: self, selector: #selector(updateAnimation))
        displayLink?.add(to: .main, forMode: .common)
    }
    
    @objc private func updateAnimation() {
        animationProgress += 0.016 // ~60 FPS
        if animationProgress > 1.0 {
            animationProgress = 0
        }
        
        particleSystem.update(deltaTime: 0.016)
        setNeedsDisplay()
    }
    
    override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        
        // Background gradient
        drawBackgroundGradient(in: context, rect: rect)
        
        // Complex shapes with transforms
        drawAnimatedShapes(in: context, rect: rect)
        
        // Particle system
        particleSystem.render(in: context)
        
        // Text with custom effects
        drawStyledText(in: context, rect: rect)
    }
    
    private func drawBackgroundGradient(in context: CGContext, rect: CGRect) {
        let colors = [UIColor.blue.cgColor, UIColor.purple.cgColor, UIColor.black.cgColor]
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: [0, 0.5, 1])!
        
        context.drawLinearGradient(
            gradient,
            start: CGPoint.zero,
            end: CGPoint(x: rect.width, y: rect.height),
            options: []
        )
    }
    
    private func drawAnimatedShapes(in context: CGContext, rect: CGRect) {
        context.saveGState()
        
        let centerX = rect.width / 2
        let centerY = rect.height / 2
        
        // Apply transforms
        context.translateBy(x: centerX, y: centerY)
        context.rotate(by: animationProgress * 2 * .pi)
        context.scaleBy(x: 1 + animationProgress * 0.2, y: 1 + animationProgress * 0.2)
        
        // Draw complex path
        let path = CGMutablePath()
        let radius: CGFloat = 60
        let points = 6
        
        for i in 0..<points {
            let angle = CGFloat(i) * 2 * .pi / CGFloat(points)
            let x = cos(angle) * radius
            let y = sin(angle) * radius
            
            if i == 0 {
                path.move(to: CGPoint(x: x, y: y))
            } else {
                path.addLine(to: CGPoint(x: x, y: y))
            }
        }
        path.closeSubpath()
        
        // Fill with pattern
        context.setFillColor(UIColor.white.withAlphaComponent(0.8).cgColor)
        context.addPath(path)
        context.fillPath()
        
        // Stroke with shadow
        context.setShadow(offset: CGSize(width: 2, height: 2), blur: 4)
        context.setStrokeColor(UIColor.yellow.cgColor)
        context.setLineWidth(3)
        context.addPath(path)
        context.strokePath()
        
        context.restoreGState()
    }
    
    private func drawStyledText(in context: CGContext, rect: CGRect) {
        let text = "Advanced Graphics"
        let font = UIFont.boldSystemFont(ofSize: 24)
        let attributes: [NSAttributedString.Key: Any] = [
            .font: font,
            .foregroundColor: UIColor.white,
            .strokeColor: UIColor.black,
            .strokeWidth: -2.0
        ]
        
        let attributedString = NSAttributedString(string: text, attributes: attributes)
        let textSize = attributedString.size()
        let textRect = CGRect(
            x: (rect.width - textSize.width) / 2,
            y: rect.height - textSize.height - 20,
            width: textSize.width,
            height: textSize.height
        )
        
        attributedString.draw(in: textRect)
    }
    
    deinit {
        displayLink?.invalidate()
    }
}

// Particle System for advanced effects
class ParticleSystem {
    private var particles: [Particle] = []
    private let maxParticles = 50
    
    init() {
        for _ in 0..<maxParticles {
            particles.append(Particle())
        }
    }
    
    func update(deltaTime: TimeInterval) {
        for particle in particles {
            particle.update(deltaTime: deltaTime)
        }
    }
    
    func render(in context: CGContext) {
        for particle in particles {
            particle.render(in: context)
        }
    }
}

class Particle {
    var position: CGPoint
    var velocity: CGPoint
    var life: CGFloat
    var maxLife: CGFloat
    var color: UIColor
    
    init() {
        position = CGPoint(x: CGFloat.random(in: 0...400), y: CGFloat.random(in: 0...800))
        velocity = CGPoint(x: CGFloat.random(in: -50...50), y: CGFloat.random(in: -100...0))
        life = 1.0
        maxLife = 1.0
        color = UIColor(hue: CGFloat.random(in: 0...1), saturation: 1, brightness: 1, alpha: 1)
    }
    
    func update(deltaTime: TimeInterval) {
        position.x += velocity.x * CGFloat(deltaTime)
        position.y += velocity.y * CGFloat(deltaTime)
        life -= CGFloat(deltaTime)
        
        if life <= 0 {
            // Reset particle
            position = CGPoint(x: CGFloat.random(in: 0...400), y: 800)
            life = maxLife
        }
    }
    
    func render(in context: CGContext) {
        let alpha = life / maxLife
        context.setFillColor(color.withAlphaComponent(alpha).cgColor)
        context.fillEllipse(in: CGRect(x: position.x - 2, y: position.y - 2, width: 4, height: 4))
    }
}

Metal ve OpenGL ES ile Düşük Seviyeli Grafikler ​

Metal Shaders ve Gelişmiş Render İşlemleri ​

swift
import Metal
import MetalKit

class MetalAdvancedRenderer: NSObject, MTKViewDelegate {
    private var device: MTLDevice
    private var commandQueue: MTLCommandQueue
    private var renderPipelineState: MTLRenderPipelineState
    private var computePipelineState: MTLComputePipelineState
    private var vertexBuffer: MTLBuffer
    private var uniformBuffer: MTLBuffer
    
    struct Uniforms {
        var time: Float
        var resolution: simd_float2
        var modelViewProjectionMatrix: simd_float4x4
    }
    
    init?(metalKitView: MTKView) {
        guard let device = MTLCreateSystemDefaultDevice(),
              let commandQueue = device.makeCommandQueue() else {
            return nil
        }
        
        self.device = device
        self.commandQueue = commandQueue
        
        // Vertex data
        let vertices: [Float] = [
            -1.0, -1.0, 0.0, 1.0,  // Bottom-left
             1.0, -1.0, 0.0, 1.0,  // Bottom-right
             1.0,  1.0, 0.0, 1.0,  // Top-right
            -1.0,  1.0, 0.0, 1.0   // Top-left
        ]
        
        vertexBuffer = device.makeBuffer(bytes: vertices, length: vertices.count * MemoryLayout<Float>.size, options: [])!
        uniformBuffer = device.makeBuffer(length: MemoryLayout<Uniforms>.size, options: [])!
        
        super.init()
        
        setupPipelines(metalKitView: metalKitView)
        
        metalKitView.device = device
        metalKitView.delegate = self
        metalKitView.colorPixelFormat = .bgra8Unorm
    }
    
    private func setupPipelines(metalKitView: MTKView) {
        guard let library = device.makeDefaultLibrary() else {
            fatalError("Could not load default library")
        }
        
        // Render pipeline
        let vertexFunction = library.makeFunction(name: "vertexShader")
        let fragmentFunction = library.makeFunction(name: "fragmentShader")
        
        let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
        renderPipelineDescriptor.vertexFunction = vertexFunction
        renderPipelineDescriptor.fragmentFunction = fragmentFunction
        renderPipelineDescriptor.colorAttachments[0].pixelFormat = metalKitView.colorPixelFormat
        
        do {
            renderPipelineState = try device.makeRenderPipelineState(descriptor: renderPipelineDescriptor)
        } catch {
            fatalError("Could not create render pipeline state: \(error)")
        }
        
        // Compute pipeline
        let computeFunction = library.makeFunction(name: "postProcessCompute")!
        do {
            computePipelineState = try device.makeComputePipelineState(function: computeFunction)
        } catch {
            fatalError("Could not create compute pipeline state: \(error)")
        }
    }
    
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
        // Handle size changes
    }
    
    func draw(in view: MTKView) {
        guard let commandBuffer = commandQueue.makeCommandBuffer(),
              let renderPassDescriptor = view.currentRenderPassDescriptor,
              let drawable = view.currentDrawable else {
            return
        }
        
        // Update uniforms
        let uniforms = Uniforms(
            time: Float(CACurrentMediaTime()),
            resolution: simd_float2(Float(view.bounds.width), Float(view.bounds.height)),
            modelViewProjectionMatrix: matrix_identity_float4x4
        )
        
        let uniformBufferPointer = uniformBuffer.contents().bindMemory(to: Uniforms.self, capacity: 1)
        uniformBufferPointer.pointee = uniforms
        
        // Render pass
        let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)!
        renderEncoder.setRenderPipelineState(renderPipelineState)
        renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
        renderEncoder.setVertexBuffer(uniformBuffer, offset: 0, index: 1)
        renderEncoder.setFragmentBuffer(uniformBuffer, offset: 0, index: 0)
        renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
        renderEncoder.endEncoding()
        
        commandBuffer.present(drawable)
        commandBuffer.commit()
    }
}

Metal Shaders (MSL) ​

metal
// Vertex Shader
#include <metal_stdlib>
using namespace metal;

struct Vertex {
    float4 position [[attribute(0)]];
};

struct Uniforms {
    float time;
    float2 resolution;
    float4x4 modelViewProjectionMatrix;
};

struct VertexOut {
    float4 position [[position]];
    float2 uv;
};

vertex VertexOut vertexShader(
    const device Vertex* vertices [[buffer(0)]],
    const device Uniforms& uniforms [[buffer(1)]],
    uint vid [[vertex_id]]
) {
    VertexOut out;
    out.position = uniforms.modelViewProjectionMatrix * vertices[vid].position;
    out.uv = (vertices[vid].position.xy + 1.0) * 0.5;
    return out;
}

// Fragment Shader with procedural graphics
fragment float4 fragmentShader(
    VertexOut in [[stage_in]],
    const device Uniforms& uniforms [[buffer(0)]]
) {
    float2 uv = in.uv;
    float2 center = float2(0.5);
    float2 pos = uv - center;
    
    // Animated parameters
    float time = uniforms.time;
    float aspect = uniforms.resolution.x / uniforms.resolution.y;
    pos.x *= aspect;
    
    // Distance field calculations
    float distance = length(pos);
    float angle = atan2(pos.y, pos.x);
    
    // Animated pattern
    float pattern = sin(distance * 20.0 - time * 2.0) * 0.5 + 0.5;
    float spiral = sin(angle * 5.0 + distance * 10.0 - time) * 0.5 + 0.5;
    
    // Color mixing
    float3 color1 = float3(0.2, 0.7, 1.0);
    float3 color2 = float3(1.0, 0.3, 0.8);
    float3 finalColor = mix(color1, color2, pattern * spiral);
    
    // Vignette effect
    float vignette = 1.0 - smoothstep(0.3, 0.8, distance);
    finalColor *= vignette;
    
    return float4(finalColor, 1.0);
}

// Compute Shader for post-processing
kernel void postProcessCompute(
    texture2d<float, access::read> inputTexture [[texture(0)]],
    texture2d<float, access::write> outputTexture [[texture(1)]],
    const device Uniforms& uniforms [[buffer(0)]],
    uint2 gid [[thread_position_in_grid]]
) {
    if (gid.x >= outputTexture.get_width() || gid.y >= outputTexture.get_height()) {
        return;
    }
    
    float2 textureSize = float2(inputTexture.get_width(), inputTexture.get_height());
    float2 uv = float2(gid) / textureSize;
    
    // Sample neighboring pixels for blur effect
    float4 color = float4(0.0);
    int kernelSize = 3;
    float weight = 1.0 / float(kernelSize * kernelSize);
    
    for (int x = -kernelSize/2; x <= kernelSize/2; x++) {
        for (int y = -kernelSize/2; y <= kernelSize/2; y++) {
            uint2 sampleCoord = uint2(
                clamp(int(gid.x) + x, 0, int(textureSize.x) - 1),
                clamp(int(gid.y) + y, 0, int(textureSize.y) - 1)
            );
            color += inputTexture.read(sampleCoord) * weight;
        }
    }
    
    // Apply color grading
    color.rgb = pow(color.rgb, float3(1.2));
    color.rgb = saturate(color.rgb * 1.1);
    
    outputTexture.write(color, gid);
}

Native UI vs Özel Grafikler Performansı ​

Native UI vs Özel Grafikler Karşılaştırması ​

Performans Özellikleri ​

Performans Metrikleri Karşılaştırması:
┌─────────────────────┬─────────────┬──────────────┬─────────────┐
│ Yön                 │ Native UI   │ Canvas API   │ Metal/GL    │
├─────────────────────┼─────────────┼──────────────┼─────────────┤
│ Kurulum Yükü        │ Düşük       │ Orta         │ Yüksek      │
│ Render Hızı         │ Hızlı       │ Orta         │ Çok Hızlı   │
│ Bellek Kullanımı    │ Optimize    │ Yüksek       │ Değişken    │
│ Esneklik            │ Sınırlı     │ Yüksek       │ Çok Yüksek  │
│ Platform Entegrasyon│ Mükemmel    │ İyi          │ Sınırlı     │
│ Öğrenme Eğrisi      │ Kolay       │ Orta         │ Zor         │
└─────────────────────┴─────────────┴──────────────┴─────────────┘

Android Canvas API Gelişmiş Teknikler ​

Donanım Hızlandırmalı Özel ViewGroup ​

kotlin
class PerformanceCanvasView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        style = Paint.Style.FILL
        color = Color.BLUE
    }
    
    private val path = Path()
    private val matrix = Matrix()
    private val rect = RectF()
    
    // Hardware acceleration optimization
    private var bitmap: Bitmap? = null
    private var bitmapCanvas: Canvas? = null
    private var isDirty = true
    
    init {
        // Enable hardware acceleration
        setLayerType(LAYER_TYPE_HARDWARE, null)
        
        // Optimize drawing
        setWillNotDraw(false)
    }
    
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        
        // Create offscreen bitmap for complex drawings
        bitmap?.recycle()
        bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
        bitmapCanvas = Canvas(bitmap!!)
        isDirty = true
    }
    
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        
        // Only redraw if necessary
        if (isDirty) {
            drawComplexGraphics(bitmapCanvas!!)
            isDirty = false
        }
        
        // Draw cached bitmap
        bitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
        
        // Draw dynamic elements directly
        drawDynamicElements(canvas)
    }
    
    private fun drawComplexGraphics(canvas: Canvas) {
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
        
        // Complex path drawing
        path.reset()
        path.moveTo(50f, 50f)
        path.quadTo(100f, 25f, 150f, 50f)
        path.quadTo(200f, 75f, 250f, 50f)
        
        // Gradient shader
        val gradient = LinearGradient(
            0f, 0f, width.toFloat(), height.toFloat(),
            intArrayOf(Color.RED, Color.GREEN, Color.BLUE),
            null,
            Shader.TileMode.CLAMP
        )
        paint.shader = gradient
        
        canvas.drawPath(path, paint)
        
        // Reset shader
        paint.shader = null
    }
    
    private fun drawDynamicElements(canvas: Canvas) {
        // Draw elements that change frequently
        val time = System.currentTimeMillis()
        val x = (Math.sin(time * 0.001) * 100 + width / 2).toFloat()
        val y = (Math.cos(time * 0.001) * 100 + height / 2).toFloat()
        
        paint.color = Color.YELLOW
        canvas.drawCircle(x, y, 20f, paint)
    }
    
    fun invalidateDrawing() {
        isDirty = true
        invalidate()
    }
    
    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        bitmap?.recycle()
        bitmap = null
        bitmapCanvas = null
    }
}

Advanced Canvas Transformations ​

kotlin
class TransformationCanvas : View {
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val matrix = Matrix()
    private val camera = Camera()
    
    private var rotationX = 0f
    private var rotationY = 0f
    private var rotationZ = 0f
    
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        
        val centerX = width / 2f
        val centerY = height / 2f
        
        // 3D transformation using Camera
        camera.save()
        camera.rotateX(rotationX)
        camera.rotateY(rotationY)
        camera.rotateZ(rotationZ)
        camera.getMatrix(matrix)
        camera.restore()
        
        // Adjust matrix for center rotation
        matrix.preTranslate(-centerX, -centerY)
        matrix.postTranslate(centerX, centerY)
        
        canvas.save()
        canvas.concat(matrix)
        
        // Draw 3D-transformed content
        paint.color = Color.BLUE
        canvas.drawRect(
            centerX - 100f, centerY - 100f,
            centerX + 100f, centerY + 100f,
            paint
        )
        
        canvas.restore()
        
        // Advanced clipping
        drawClippedGraphics(canvas)
    }
    
    private fun drawClippedGraphics(canvas: Canvas) {
        canvas.save()
        
        // Create complex clipping path
        val clipPath = Path().apply {
            addCircle(width / 4f, height / 4f, 100f, Path.Direction.CW)
            addRect(
                width * 3f / 4f - 50f, height / 4f - 50f,
                width * 3f / 4f + 50f, height / 4f + 50f,
                Path.Direction.CW
            )
        }
        
        canvas.clipPath(clipPath)
        
        // Draw gradient background
        val gradient = RadialGradient(
            width / 2f, height / 2f, width / 2f,
            intArrayOf(Color.MAGENTA, Color.CYAN, Color.YELLOW),
            floatArrayOf(0f, 0.5f, 1f),
            Shader.TileMode.CLAMP
        )
        
        paint.shader = gradient
        canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
        paint.shader = null
        
        canvas.restore()
    }
    
    fun setRotation(x: Float, y: Float, z: Float) {
        rotationX = x
        rotationY = y
        rotationZ = z
        invalidate()
    }
}

iOS Core Graphics Gelişmiş Uygulaması ​

Özel Core Graphics Çizimi ​

swift
import UIKit
import CoreGraphics
import QuartzCore

class AdvancedCoreGraphicsView: UIView {
    private var cachedLayer: CALayer?
    private var needsRedraw = true
    
    override class var layerClass: AnyClass {
        return CALayer.self
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        setupOptimizations()
    }
    
    private func setupOptimizations() {
        // Enable rasterization for complex drawings
        layer.shouldRasterize = true
        layer.rasterizationScale = UIScreen.main.scale
        
        // Set drawing delegate
        layer.delegate = self
        layer.setNeedsDisplay()
    }
    
    override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        
        if needsRedraw {
            drawComplexGraphics(in: context, rect: rect)
            needsRedraw = false
        }
        
        drawDynamicContent(in: context, rect: rect)
    }
    
    private func drawComplexGraphics(in context: CGContext, rect: CGRect) {
        // Create gradient
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let colors = [UIColor.red.cgColor, UIColor.blue.cgColor, UIColor.green.cgColor]
        let locations: [CGFloat] = [0.0, 0.5, 1.0]
        
        guard let gradient = CGGradient(colorsSpace: colorSpace,
                                      colors: colors as CFArray,
                                      locations: locations) else { return }
        
        // Draw radial gradient
        context.drawRadialGradient(
            gradient,
            startCenter: CGPoint(x: rect.midX, y: rect.midY),
            startRadius: 0,
            endCenter: CGPoint(x: rect.midX, y: rect.midY),
            endRadius: min(rect.width, rect.height) / 2,
            options: []
        )
        
        // Complex path with Bezier curves
        let path = CGMutablePath()
        path.move(to: CGPoint(x: 50, y: 50))
        path.addQuadCurve(
            to: CGPoint(x: 250, y: 50),
            control: CGPoint(x: 150, y: 20)
        )
        path.addQuadCurve(
            to: CGPoint(x: 250, y: 250),
            control: CGPoint(x: 280, y: 150)
        )
        path.addQuadCurve(
            to: CGPoint(x: 50, y: 250),
            control: CGPoint(x: 150, y: 280)
        )
        path.addQuadCurve(
            to: CGPoint(x: 50, y: 50),
            control: CGPoint(x: 20, y: 150)
        )
        
        context.setFillColor(UIColor.yellow.withAlphaComponent(0.7).cgColor)
        context.addPath(path)
        context.fillPath()
    }
    
    private func drawDynamicContent(in context: CGContext, rect: CGRect) {
        // Animated circle
        let time = CACurrentMediaTime()
        let x = sin(time) * 50 + rect.midX
        let y = cos(time) * 50 + rect.midY
        
        context.setFillColor(UIColor.orange.cgColor)
        context.fillEllipse(in: CGRect(x: x - 15, y: y - 15, width: 30, height: 30))
        
        // Text rendering with custom attributes
        drawCustomText(in: context, at: CGPoint(x: rect.midX, y: rect.maxY - 50))
    }
    
    private func drawCustomText(in context: CGContext, at point: CGPoint) {
        let text = "Advanced Core Graphics"
        let font = CTFontCreateWithName("Helvetica-Bold" as CFString, 20, nil)
        let attributes: [NSAttributedString.Key: Any] = [
            .font: font,
            .foregroundColor: UIColor.white,
            .strokeColor: UIColor.black,
            .strokeWidth: -2.0
        ]
        
        let attributedString = NSAttributedString(string: text, attributes: attributes)
        let line = CTLineCreateWithAttributedString(attributedString)
        
        context.saveGState()
        context.textPosition = point
        CTLineDraw(line, context)
        context.restoreGState()
    }
    
    func invalidateDrawing() {
        needsRedraw = true
        setNeedsDisplay()
    }
}

// MARK: - CALayerDelegate
extension AdvancedCoreGraphicsView: CALayerDelegate {
    func draw(_ layer: CALayer, in ctx: CGContext) {
        // Custom layer drawing
        ctx.setFillColor(UIColor.blue.withAlphaComponent(0.3).cgColor)
        ctx.fill(layer.bounds)
    }
}

Metal Integration with UIKit ​

swift
import MetalKit
import Metal

class MetalUIKitView: UIView {
    private var metalView: MTKView!
    private var device: MTLDevice!
    private var commandQueue: MTLCommandQueue!
    private var pipelineState: MTLRenderPipelineState!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        setupMetal()
    }
    
    private func setupMetal() {
        guard let device = MTLCreateSystemDefaultDevice() else {
            fatalError("Metal not supported")
        }
        
        self.device = device
        self.commandQueue = device.makeCommandQueue()
        
        // Create MTKView
        metalView = MTKView(frame: bounds, device: device)
        metalView.delegate = self
        metalView.preferredFramesPerSecond = 60
        metalView.colorPixelFormat = .bgra8Unorm_srgb
        addSubview(metalView)
        
        // Setup render pipeline
        setupRenderPipeline()
    }
    
    private func setupRenderPipeline() {
        guard let library = device.makeDefaultLibrary() else {
            fatalError("Could not load default library")
        }
        
        let vertexFunction = library.makeFunction(name: "vertex_main")
        let fragmentFunction = library.makeFunction(name: "fragment_main")
        
        let pipelineDescriptor = MTLRenderPipelineDescriptor()
        pipelineDescriptor.vertexFunction = vertexFunction
        pipelineDescriptor.fragmentFunction = fragmentFunction
        pipelineDescriptor.colorAttachments[0].pixelFormat = metalView.colorPixelFormat
        
        do {
            pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)
        } catch {
            fatalError("Could not create render pipeline state: \(error)")
        }
    }
}

// MARK: - MTKViewDelegate
extension MetalUIKitView: MTKViewDelegate {
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
        // Handle size changes
    }
    
    func draw(in view: MTKView) {
        guard let drawable = view.currentDrawable,
              let renderPassDescriptor = view.currentRenderPassDescriptor,
              let commandBuffer = commandQueue.makeCommandBuffer(),
              let renderEncoder = commandBuffer.makeRenderCommandEncoder(
                descriptor: renderPassDescriptor
              ) else {
            return
        }
        
        // Clear background
        renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(
            red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0
        )
        
        // Set render pipeline
        renderEncoder.setRenderPipelineState(pipelineState)
        
        // Draw triangle
        let vertices: [Float] = [
             0.0,  0.5, 0.0, 1.0, 0.0, 0.0, 1.0,  // Top vertex (red)
            -0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0,  // Bottom left (green)
             0.5, -0.5, 0.0, 0.0, 0.0, 1.0, 1.0   // Bottom right (blue)
        ]
        
        renderEncoder.setVertexBytes(vertices, length: vertices.count * MemoryLayout<Float>.size, index: 0)
        renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)
        
        renderEncoder.endEncoding()
        commandBuffer.present(drawable)
        commandBuffer.commit()
    }
}

Performans Ölçümü ve Analizi ​

Canvas vs Native UI Karşılaştırması ​

kotlin
class PerformanceBenchmark {
    private val renderTimes = mutableListOf<Long>()
    private val maxSamples = 100
    
    fun benchmarkCanvasDrawing(canvas: Canvas, iterations: Int): BenchmarkResult {
        renderTimes.clear()
        
        repeat(iterations) {
            val startTime = System.nanoTime()
            
            // Canvas drawing operations
            drawComplexCanvas(canvas)
            
            val endTime = System.nanoTime()
            renderTimes.add(endTime - startTime)
        }
        
        return calculateBenchmarkResult("Canvas Drawing")
    }
    
    fun benchmarkNativeUI(viewGroup: ViewGroup, iterations: Int): BenchmarkResult {
        renderTimes.clear()
        
        repeat(iterations) {
            val startTime = System.nanoTime()
            
            // Native UI operations
            updateNativeViews(viewGroup)
            
            val endTime = System.nanoTime()
            renderTimes.add(endTime - startTime)
        }
        
        return calculateBenchmarkResult("Native UI")
    }
    
    private fun drawComplexCanvas(canvas: Canvas) {
        val paint = Paint(Paint.ANTI_ALIAS_FLAG)
        
        // Draw 100 circles with gradients
        repeat(100) { i ->
            val gradient = RadialGradient(
                (i * 10).toFloat(), (i * 5).toFloat(), 50f,
                Color.RED, Color.BLUE, Shader.TileMode.CLAMP
            )
            paint.shader = gradient
            canvas.drawCircle((i * 10).toFloat(), (i * 5).toFloat(), 20f, paint)
        }
        
        paint.shader = null
    }
    
    private fun updateNativeViews(viewGroup: ViewGroup) {
        // Update 100 native views
        repeat(100) { i ->
            val view = viewGroup.getChildAt(i % viewGroup.childCount)
            view?.apply {
                x = (i * 10).toFloat()
                y = (i * 5).toFloat()
                scaleX = 1.0f + (i % 10) * 0.1f
                scaleY = 1.0f + (i % 10) * 0.1f
            }
        }
    }
    
    private fun calculateBenchmarkResult(name: String): BenchmarkResult {
        val avgTime = renderTimes.average()
        val minTime = renderTimes.minOrNull() ?: 0L
        val maxTime = renderTimes.maxOrNull() ?: 0L
        val fps = 1_000_000_000.0 / avgTime // Convert nanoseconds to FPS
        
        return BenchmarkResult(
            name = name,
            averageTimeNs = avgTime.toLong(),
            minTimeNs = minTime,
            maxTimeNs = maxTime,
            estimatedFPS = fps
        )
    }
    
    data class BenchmarkResult(
        val name: String,
        val averageTimeNs: Long,
        val minTimeNs: Long,
        val maxTimeNs: Long,
        val estimatedFPS: Double
    ) {
        override fun toString(): String {
            return """
                $name Benchmark Results:
                Average Time: ${averageTimeNs / 1_000_000.0} ms
                Min Time: ${minTimeNs / 1_000_000.0} ms
                Max Time: ${maxTimeNs / 1_000_000.0} ms
                Estimated FPS: ${String.format("%.1f", estimatedFPS)}
            """.trimIndent()
        }
    }
}

En İyi Uygulamalar Özeti ​

Canvas API Ne Zaman Kullanılmalı ​

kotlin
/*
✅ Canvas şu durumlarda kullanılmalı:
- Karmaşık özel çizimler
- Parçacık sistemleri
- Oyunlar ve animasyonlar
- Veri görselleştirmeleri
- Özel şekiller ve yollar
- Performans kritik grafikler

❌ Canvas şu durumlarda kullanılmamalı:
- Basit UI düzenleri
- Standart UI bileşenleri
- Metin ağırlıklı arayüzler
- Formlar ve girişler
- Liste/ızgara düzenleri
*/

Native UI Ne Zaman Kullanılmalı ​

kotlin
/*
✅ Native UI şu durumlarda kullanılmalı:
- Standart UI desenleri
- Platform tutarlılığı gerektiğinde
- Erişilebilirlik gereksinimleri
- Form tabanlı arayüzler
- Hızlı geliştirme gerektiğinde
- Platform animasyonları

❌ Native UI şu durumlarda kullanılmamalı:
- Karmaşık özel grafikler
- Oyun benzeri arayüzler
- Ağır animasyon gereksinimleri
- Piksel hassasiyeti gerektiğinde
- Performans kritik render işlemleri
*/

Eren Demir tarafından oluşturulmuştur.