From df552edb9a631c08fc24ceaa4e45bddd340010a1 Mon Sep 17 00:00:00 2001 From: EQuimper Date: Thu, 24 Sep 2026 12:36:54 -0400 Subject: [PATCH] fix(ios): size the legacy Metal drawable from the view's own bounds MTKView's automatic drawable resize derives per-axis scale factors once, on its first window attach when the screen's nativeScale differs from the view's contentScaleFactor (Display Zoom, iPhone mini and Plus). It computes them by converting the screen bounds through every ancestor transform, so a rotated ancestor at that moment leaves a non-uniform drawable for the view's lifetime and Fit.Contain squashes the artboard; a zero-scale ancestor yields an invalid drawable (blank since rive-ios 6.2.1, see rive-app/rive-ios#343). Turn autoResizeDrawable off on the legacy RiveView and set drawableSize from its bounds times the window screen's nativeScale on layout and on window changes. --- ios/legacy/RiveReactNativeView.swift | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/ios/legacy/RiveReactNativeView.swift b/ios/legacy/RiveReactNativeView.swift index 324688f4..31477524 100644 --- a/ios/legacy/RiveReactNativeView.swift +++ b/ios/legacy/RiveReactNativeView.swift @@ -1,3 +1,4 @@ +import MetalKit import NitroModules import RiveRuntime import UIKit @@ -230,6 +231,8 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { riveView = baseViewModel?.createRiveView() if let riveView = riveView { + // The drawable is sized by `syncDrawableSize()` instead. + riveView.autoResizeDrawable = false riveView.translatesAutoresizingMaskIntoConstraints = false addSubview(riveView) riveView.stateMachineDelegate = self @@ -239,6 +242,37 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { riveView.topAnchor.constraint(equalTo: topAnchor), riveView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) + setNeedsLayout() + } + } + + override func layoutSubviews() { + super.layoutSubviews() + syncDrawableSize() + } + + override func didMoveToWindow() { + super.didMoveToWindow() + syncDrawableSize() + } + + /// MTKView's automatic resize scales the bounds by per-axis factors it + /// computes once, on its first window attach when `nativeScale != scale` + /// (Display Zoom, mini and Plus iPhones), by converting the screen bounds + /// through every ancestor transform. A rotated ancestor at that moment + /// leaves a non-uniform drawable for the view's lifetime, so `contain` + /// squashes the artboard; a zero-scale ancestor leaves it blank. Sizing + /// from the view's own bounds ignores ancestor transforms. + private func syncDrawableSize() { + guard let riveView = riveView, let screen = window?.screen else { return } + let pointSize = riveView.bounds.size + guard pointSize.width > 0, pointSize.height > 0 else { return } + let size = CGSize( + width: (pointSize.width * screen.nativeScale).rounded(), + height: (pointSize.height * screen.nativeScale).rounded() + ) + if riveView.drawableSize != size { + riveView.drawableSize = size } }