Optimizing Mobile App UI Rendering Speed

Recently, a client faced an issue: on iPhone 13, the app showed 58–60 FPS on most screens, but one screen with a custom UICollectionViewLayout consistently dropped to 42–45 FPS while scrolling. We launched Instruments and discovered that 14ms out of 16 per frame were spent on layoutAttributesForElem

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
Optimizing Mobile App UI Rendering Speed
Complex
~3-5 days

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    597

Recently, a client faced an issue: on iPhone 13, the app showed 58–60 FPS on most screens, but one screen with a custom UICollectionViewLayout consistently dropped to 42–45 FPS while scrolling. We launched Instruments and discovered that 14ms out of 16 per frame were spent on layoutAttributesForElementsInRect — the method recalculated all cell positions without caching. Classic: UI rendering doesn't slow down "in general"; it slows down at a specific place for a specific reason. After implementing a simple cache, FPS returned to 60, and the client got a smooth interface in one day. UI rendering speed optimization requires a systematic approach.

Rendering stutters are among the most insidious problems because they are immediately visible to the user but take a long time to diagnose. The FPS metric says "bad", but exactly where needs to be dug into. We work with data, not guesses.

Why does the main thread block rendering?

The golden rule is 16 ms per frame (60 FPS) or 8 ms (120 Hz on Pro devices). Anything that runs on the main thread beyond this blocks rendering. Typical culprits:

On iOS: synchronous CoreData work via viewContext directly in cellForItemAt, decoding UIImage without preparingForDisplay(), NSAttributedString with size calculation in sizeForItemAt without caching.

On Android: blocking I/O in onBindViewHolder, Bitmap.decodeResource() on the main thread, heavy Drawable animations via AnimationDrawable on budget devices with Mali GPU.

A special case is the measure/layout pass. On Android Jetpack Compose, a ConstraintLayout inside LazyColumn with deep nesting triggers two full measure passes per cell. On a complex list with 50+ items, this is noticeable even on a Pixel 7.

How to diagnose rendering problems?

We start diagnostics by profiling on a real device. On iOS we use Xcode Instruments (Core Animation template), on Android — GPU Inspector or Android Studio Profiler. For Flutter — flutter run --profile with DevTools Performance overlay. We collect a baseline: average FPS, number of janky frames, frame time distribution. If the average FPS is below 55, we look for blocking points.

Why is the main thread the main enemy of smoothness? – UI rendering speed optimization

Every operation on the main thread beyond 16 ms (or 8 ms for 120 Hz) causes a frame drop. For example, calling sd_setImageWithURL: without the SDWebImageAvoidAutoSetImage flag consumes 8 ms on the main thread — we saw this on a project where FPS dropped from 55 to 47. Replacing it with the correct option restored smoothness.

What is overdraw and how to find it?

Overdraw is when a single pixel is drawn multiple times per frame. On Android, enable it via "Developer Options → Show GPU Overdraw": blue = 1×, green = 2×, pink = 3×, red = 4×+. A red screen on a budget Xiaomi with Adreno 610 guarantees jank.

A common cause is nested ViewGroups with opaque backgrounds, where each layer draws its background on top of the previous one. On iOS, the equivalent is a CALayer with opaque = false where transparency is not needed, or shouldRasterize without explicit rasterizationScale.

UI rendering speed optimization: step-by-step plan

What is included in the work

  • Audit with a report: profiling screenshots, list of problematic spots, baseline metrics.
  • Code changes with comments for developers.
  • Recommendations on architecture and tools for long-term FPS maintenance.
  • Monitoring — integration of Firebase Performance or a custom FPS monitor.
  • Guarantee to achieve the target FPS within 30 days after delivery.

How we do it: a specific case

A typical iOS project scenario: a client complains about "lag in the feed." We open Time Profiler, record 5 seconds of scrolling. In the call tree, we immediately see: [SDWebImage sd_setImageWithURL:] consumes 8 ms on the main thread because someone removed options:SDWebImageAvoidAutoSetImage and images are applied synchronously after loading. One flag — and FPS went from 47 to 59.

On Android, there was a case with RecyclerView + DiffUtil: the developer called submitList() from ViewModel, but DiffUtil worked on the main thread (used ListAdapter without AsyncListDiffer). On a list of 200 items, the diff took ~18 ms. We moved the diff computation to a background thread via AsyncListDiffer — the problem disappeared. AsyncListDiffer is 3 times faster than synchronous DiffUtil on lists of 500 items.

Specific tools and techniques

iOS:

  • CADisplayLink + custom FPS monitor in debug builds for continuous monitoring
  • UIView.setNeedsLayout() vs UIView.layoutIfNeeded() — understanding the difference is critical during animations
  • drawRect: is almost always replaced with CALayer sublayers — Core Animation renders them on the GPU without CPU involvement
  • UIGraphicsImageRenderer instead of the outdated UIGraphicsBeginImageContextWithOptions for offscreen rendering
  • Prefetching via UICollectionViewDataSourcePrefetching — decode images before the cell appears on screen

Android / Compose:

  • Modifier.graphicsLayer {} for hardware-accelerated transformations instead of software ones
  • remember {} and derivedStateOf {} — prevent unnecessary recompositions
  • key() in LazyColumn — without it, Compose cannot reuse nodes when the list changes
  • Bitmap.Config.RGB_565 instead of ARGB_8888 where alpha is not needed — half the GPU memory

Flutter:

  • RepaintBoundary around widgets that frequently repaint independently
  • const constructors — widget is not recreated on parent rebuild
  • flutter run --profile + DevTools → Performance overlay — essential before release

Case from our practice: 120 Hz on iPad Pro

Our client made a custom animation via UIViewPropertyAnimator with preferredFrameRateRange. The animation ran at 60 FPS instead of 120. It turned out that one CALayer had shouldRasterize = true without an explicit rasterizationScale = UIScreen.main.scale * 2. Core Animation limited the entire subtree to 60 FPS due to the mismatch in rasterization scale. After the fix, the animation ran at 120 FPS with a noticeable difference in feel. The Core Animation Programming Guide recommends always explicitly setting rasterizationScale for rasterized layers.

Comparison of profiling tools

Tool Platform Typical usage Complexity
Xcode Instruments (Core Animation) iOS FPS measurement, detecting overdraw and main thread blocking Medium
Android GPU Inspector Android Frame tracing, GPU load analysis High
Android Studio Profiler (Rendering) Android Quick frame time view and recommendations Low
Flutter DevTools Performance Flutter FPS overlay and timeline with rebuilds Low

Work stages

  1. Audit — record sessions in Instruments / Android Profiler, collect baseline FPS, janky frames, frame time.
  2. Analysis — identify bottlenecks: main thread blocking, overdraw, unnecessary layout passes.
  3. Fixes — iteratively, with measurements after each change.
  4. Regression run — verify that the fix hasn't broken adjacent screens.
  5. Monitoring — integrate Firebase Performance or a custom FPS monitor for production tracking.

We estimate the scope after the audit — sometimes the problem is solved in a day, sometimes a custom layout needs rewriting. We have 8+ years of experience in mobile development and have completed over 50 UI optimization projects. Our clients save up to 40% of the budget on improvements compared to full code rewrites.

Timeframe estimates

Point fix (one screen, clear cause): 1–3 days. System audit and optimization of several screens: 1–3 weeks. If the problem is in architectural decisions (incorrect use of main thread throughout the app): allocate 3–6 weeks with phased migration.

Get a consultation for your project — we will estimate the scope and offer the optimal solution. Contact us to start the audit.