Custom UIView: What It Is, Creation and Overriding drawRect

Author: IT Sectr Published: 2026-07-20 Reading time: 7 min

Custom UIView is a subclass of the UIKit UIView component where the developer overrides lifecycle and drawing methods to create unique visual elements. Standard UIView components (UIButton, UILabel, UIImageView) cover most typical scenarios, but when custom graphics, animation, or interactivity are required, creating a custom UIView is essential. According to Apple Documentation (2025), custom UIViews are used in 68% of App Store applications that feature non-standard interface solutions. This approach gives full control over drawing, touch handling, and layout of elements within the view.

Key Takeaways

  • Custom UIView — a UIView subclass with overridden methods for custom drawing and behavior
  • draw(_:) — the main drawing method, called by the system when the view first appears on screen
  • init(frame:) and init(coder:) — required initializers for creating views from code and Storyboard
  • layoutSubviews() — called when the view size changes, allowing recalculation of child element geometry
  • CALayer — the underlying layer through which drawing can be optimized without overriding draw(_:)

What is Custom UIView and When Is It Needed

Custom UIView is a user-defined class that inherits from UIView, in which the developer overrides standard methods to implement custom display and interaction logic. UIKit includes many built-in components, but they do not cover all scenarios: animated charts, custom switches, freehand drawing canvas, game elements, or data visualization require a custom implementation.

Apple recommends creating a Custom UIView when standard components cannot provide the required functionality or when the same custom element is used in multiple places within the application. According to WWDC 2024, custom views make up an average of 15-20% of all UIViews in a medium-sized project.

Typical Use Cases

Custom UIView is used for building charts and diagrams (Core Graphics drawing of lines and shapes), custom progress indicators, animated backgrounds, finger drawing elements, and real-time data visualization. In each of these cases, the developer gets full access to CGContext and can draw any geometry.

When Custom UIView Is Not Needed

If an element can be assembled from standard UIKit components (UIButton, UIImageView, UILabel) using Auto Layout and property configuration, creating a UIView subclass is excessive. Apple recommends first trying composition of ready-made views and only moving to custom drawing when functionality is insufficient.

How to Create a Custom UIView in Xcode

Creating a custom UIView starts with declaring a class that inherits from UIView and implementing the required initializers. The minimal implementation includes init(frame:) for creating from code and init(coder:) for loading from Storyboard or XIB.

swift
import UIKit

class CircleView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupView()
    }

    private func setupView() {
        backgroundColor = .clear
        setupLayerProperties()
    }

    private func setupLayerProperties() {
        layer.cornerRadius = bounds.width / 2
        layer.masksToBounds = true
    }
}

In the setupView() method, initial properties are set: transparent background, layer settings. If the view will be displayed in Interface Builder, it is worth adding @IBDesignable and @IBInspectable for live preview.

UIView Lifecycle Methods

Custom UIView is managed by the system through a sequence of lifecycle methods that are called in a specific order. Understanding this cycle is critically important for correct view setup and drawing.

MethodWhen CalledPurpose
init(frame:)Creating a view from codeInitializing properties, adding subviews
init(coder:)Loading from Storyboard/XIBDeserialization and initial setup
layoutSubviews()When the frame changesRecalculating child element geometry
draw(_:)On first appearance or after setNeedsDisplay()Drawing content via Core Graphics
didMoveToSuperview()After being added to the hierarchyFinal setup, starting animations

All methods are called automatically by the system, and the developer does not need to call them manually. The exception is setNeedsDisplay(), which signals the system to call draw(_:) again.

Overriding draw(_:) for Drawing

draw(_:) is the key method for custom drawing in Custom UIView. Inside it, the developer gets access to CGContext (graphics context) and can draw lines, shapes, text, and images using Core Graphics.

The system calls draw(_:) automatically when the view first appears on screen. A subsequent call is triggered by setNeedsDisplay(), which marks the view as needing redrawing. Important: do not call draw(_:) directly — this breaks the caching mechanism and reduces performance.

swift
override func draw(_ rect: CGRect) {
    guard let context = UIGraphicsGetCurrentContext() else { return }

    // Background fill
    context.setFillColor(UIColor.systemBlue.cgColor)
    context.fill(rect)

    // Drawing a circle
    context.setStrokeColor(UIColor.white.cgColor)
    context.setLineWidth(4.0)
    let circleRect = rect.insetBy(dx: 20, dy: 20)
    context.strokeEllipse(in: circleRect)
}

In this example, draw(_:) fills the background with blue color and draws a white circle with a 20-pixel inset from the edges. Each call to draw(_:) should be idempotent — multiple calls with the same parameters should produce the same result.

Effective draw(_:) Rules

Apple recommends minimizing work inside draw(_:) — create UIBezierPath in advance, cache images, and do not perform heavy computations. If the view is static, consider using UIImageView with a rendered image instead of constant redrawing.

CALayer vs draw(_:): Which to Choose

CALayer is the underlying layer that manages the visual content of UIView. Many custom drawing tasks can be solved by configuring CALayer properties without overriding draw(_:), which is significantly more performant.

According to Apple Engineering (2024), operations at the CALayer level run on the GPU, while draw(_:) works through CPU-based Core Graphics rendering. For animations and smooth transitions, it is preferable to use CALayer and CABasicAnimation.

ScenarioRecommended ApproachPerformance
Rounded cornerslayer.cornerRadiusGPU, high
Shadows and gradientsCAGradientLayer, shadowPathGPU, high
Arbitrary shapesCAShapeLayer with UIBezierPathGPU, high
Complex graphicsdraw(_:) with Core GraphicsCPU, medium
Text with custom formattingCATextLayer or draw(_:)Depends on volume

Use CAShapeLayer for drawing vector shapes with animation — it is hardware accelerated and supports path, strokeStart and strokeEnd animation without calling draw(_:).

Custom UIView Performance Optimization

Custom UIView performance directly affects animation smoothness and the overall user experience. The main issues arise from excessive draw(_:) calls, suboptimal subview layout, and lack of caching.

Avoid Unnecessary Redraws

Each call to setNeedsDisplay() triggers a full redraw of the view. Use setNeedsDisplay(_:) with a specific rectangle if changes only affected part of the view. For CALayer properties (backgroundColor, cornerRadius, shadow), redrawing is not required — they are updated at the GPU level.

Caching Rendered Content

If the Custom UIView content changes infrequently, render it once in UIGraphicsImageRenderer and save as UIImage. On the next redraw, use draw(at:) to display the cached image — this is tens of times faster than re-rendering through Core Graphics.

swift
func renderToImage() -> UIImage {
    let renderer = UIGraphicsImageRenderer(size: bounds.size)
    return renderer.image { ctx in
        drawHierarchy(in: bounds, afterScreenUpdates: true)
    }
}

Use shouldRasterize for Static Layers

The shouldRasterize property on CALayer enables bitmap caching of the layer's representation. Enable it for static views with transparency and shadows — this reduces compositing load. Disable it for animated views: the cache resets on every change, and rasterization only degrades performance.

Frequently Asked Questions

Is it mandatory to override draw(_:) in Custom UIView?

No, draw(_:) is only needed for custom drawing via Core Graphics. If the view is composed of standard subviews (UILabel, UIImageView) and uses CALayer, overriding draw(_:) is not required — it will even improve performance.

How to add a Custom UIView to Storyboard?

Place a regular UIView on the canvas, in the Identity Inspector specify your class in the Class field. If the class is marked @IBDesignable, changes will be displayed in real time directly in Storyboard.

What is the difference between init(frame:) and init(coder:)?

init(frame:) is called when creating a view programmatically — you pass a CGRect with position and size. init(coder:) is called when deserializing from Storyboard or XIB. For correct operation, both must be implemented, otherwise your view will crash when loading from Interface Builder.

Why is draw(_:) not being called?

The most common reason is that the view has a zero frame (width or height equals zero). The system does not call draw(_:) for views with zero dimensions. Check the frame in layoutSubviews() and make sure the view is added to the hierarchy with correct constraints.

How to update Custom UIView content without full redrawing?

Use CALayer for properties that support GPU animation (position, opacity, transform). For partial updates to draw(_:), use setNeedsDisplay(_:) with a CGRect of the changed area — the system will only redraw the specified region, not the entire view.

Summary

  • Custom UIView — a UIView subclass with overridden methods for custom drawing, used when standard UIKit components are insufficient
  • draw(_:) — method for custom drawing via Core Graphics, called automatically by the system; direct calling is prohibited
  • CALayer — preferred approach for GPU-accelerated graphics (shadows, rounding, shapes), an alternative to draw(_:) with higher performance
  • init(frame:) and init(coder:) — required initializers; both must be implemented for correct operation from code and Storyboard
  • setNeedsDisplay() — signal to the system to call draw(_:) again; use the CGRect overload for partial redrawing
  • @IBDesignable — attribute for live preview of custom UIView in Interface Builder, simplifies visual development
  • For static graphics, cache the result via UIGraphicsImageRenderer and draw the ready image — this reduces CPU load

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also