Is there a supported way to convert the cached contents of a UIKit snapshot view into a UIImage on physical iOS/iPadOS devices?
A view returned by snapshotView(afterScreenUpdates:) displays correctly on hardware, but drawing that view into a UIGraphicsImageRenderer produces no cached-content pixels. drawHierarchy(in:afterScreenUpdates:) still returns true. The same base experiment succeeds in Simulator.
Feedback Assistant report: FB25336445. A standalone reproducer and measurements are attached there; use the corrected replica-readback-ios-27.0.1-fixed-project.zip attachment.
- Physical iPhone 15 Pro Max: iOS 27.0.1 (24A446).
- Physical iPad mini (6th generation): iPadOS 26.6.1 (23G83). Two independent app launches produced identical measurements.
- Simulator: iPhone 18 Pro / iOS 27.0 and iPhone 15 Pro Max / iOS 26.1.
The app is a standalone UIKit Release build, launched without a debugger or XCTest. It uses an ordinary red UIView and a white UILabel: no WebKit, Liquid Glass, video, secure text fields, third-party libraries, or private API calls.
I have not tested a physical iPhone on the 26 series or iOS 27.0.2. The Simulator and device OS patch versions differ, so this is not a controlled OS-only comparison.
- Display a red 240 x 160 UIView containing a white UILabel; allow it to render.
- Draw the original view into an image as a positive control.
- Call
source.snapshotView(afterScreenUpdates: false), attach the replica to the window at the source frame, wait 500 ms, hide the original, then wait another 500 ms. - Draw the attached replica into transparent and cyan-prefilled image contexts.
- Add an ordinary red 30 x 30 child UIView to the replica, wait 500 ms, and capture again.
- Repeat with replicas from
UIWindow.snapshotViewandUIScreen.snapshotView.
This is the capture operation, called on the main actor after those waits:
func rasterize(_ target: UIView, cyanBackground: Bool) -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = 1
format.opaque = false
return UIGraphicsImageRenderer(size: target.bounds.size, format: format)
.image { context in
if cyanBackground {
UIColor.cyan.setFill()
context.fill(target.bounds)
}
let success = target.drawHierarchy(
in: target.bounds,
afterScreenUpdates: true
)
print("drawHierarchy:", success)
}
}
On both physical devices:
- The original 240 x 160 UIView renders normally: all 38,400 pixels are nontransparent.
- The replica on a transparent target produces zero nontransparent pixels.
- The replica on a cyan target leaves all 38,400 pixels cyan.
- Adding an ordinary child view produces approximately 900 red pixels, but the replica's cached contents are still missing.
- UIView, UIWindow and UIScreen replicas all exhibit the omission, while
drawHierarchyreturnstrue.
In both tested Simulators, the base experiment renders the replica contents and overwrites the cyan background. The child-marker addition was tested on physical devices only.
These counts are measured from the CGImage before PNG encoding. Thus “black output” with an opaque context appears to be missing content, not pixels painted black: transparent and cyan targets retain their original contents.
- Is bitmap readback of these snapshot views supported on physical devices, or are they only intended for on-screen composition?
- If unsupported, is the Simulator behavior expected, and why does
drawHierarchyreturn success despite omitting the visible cached contents? - Is there a supported API to materialize those cached contents as an image, without starting a screen-recording session? Capturing the original view directly is a useful control, but does not answer how to read back an already-created snapshot.
I found this older AVSampleBufferDisplayLayer discussion, which also describes a correctly displayed snapshot view becoming black when converted to an image. My reproducer uses only ordinary UIKit views; I do not know whether the underlying cause is related.