React Native封装第三方库:RCTViewResize后父视图未重排问题
React Native封装第三方库时动态调整视图高度后布局不更新问题
我正在开发React Native对第三方库的封装,需展示该库加载的图片。逻辑是先将RCTView子类(ExpoView子类)加入视图层级,启动图片加载,待加载完成后调整视图大小以完整显示图片(无裁剪或留白)。但遇到问题:RCTView子类可成功Resize,但其周围元素未重新布局,导致该视图被下方视图遮挡。
截图显示中间白色的广告图片区域本该更高以完整展示,却被下方黄色视图挡住。
iOS端关键代码
class EquativDisplayView: ExpoView { let container = UIView() var bannerView:SASBannerView? required init(appContext: AppContext? = nil) { super.init(appContext: appContext) clipsToBounds = true addSubview(container) } override func layoutSubviews() { //确保所有视图尺寸一致 container.frame = bounds bannerView?.frame = bounds } func setConfiguration(_ configuration: Configuration) { //加载图片 let bannerView = SASBannerView(frame: CGRect(x: 0, y: 0, width: bounds.width, height: 0)) let adPlacement = SASAdPlacement.init(testAd: SASAdPlacementTest(rawValue: testPlacementId) ?? .bannerMRAID) bannerView.load(with: adPlacement) bannerView.delegate = self //将banner视图添加到当前视图控制器 container.addSubview(bannerView) self.bannerView = bannerView } } extension EquativDisplayView :SASBannerViewDelegate { func bannerView(_ bannerView: SASBannerView, didDownloadAd ad: SASAd) { print("ad did load") let optimalHeight = bannerView.optimalAdViewHeight(forContainer: nil) let newFrame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: optimalHeight) frame = newFrame print("resizing %@", newFrame) setNeedsLayout() superview!.setNeedsLayout() // 这里本以为屏幕布局会更新,但显然漏掉了关键逻辑 } }
React Native端代码
export default function App() { return ( <View style={styles.container}> <View style={{ height: 100 }} /> <Text style={{ flex: 1, backgroundColor: "yellow", width: "100%", textAlign: "center" }}> {EquativDisplay.hello()} </Text> <EquativDisplay.EquativDisplayView style={{ width: "100%", padding: 10, backgroundColor: "green", }} configuration={{ testPlacementId: EquativDisplay.PlacementTest.BannerMRAID, }} /> <Text style={{ flex: 1, backgroundColor: "yellow", width: "100%", textAlign: "center" }}> {EquativDisplay.hello()} </Text> </View> ) }
日志输出
layoutSubviews %@ (0.0, 0.0, 393.0, 20.0) ad did load resizing %@ (0.0, 466.0, 393.0, 61.0) layoutSubviews %@ (0.0, 0.0, 393.0, 61.0)
视图高度已正确设为61,但底部黄色视图未重新调整位置,问题未解决。
解决方案
核心问题:直接修改原生视图frame无法通知React Native的Yoga布局系统同步尺寸变化,导致RN侧组件不会重新布局。
方案1:通过原生视图intrinsic size同步尺寸
修改iOS端代码,让原生视图支持自动计算固有尺寸,并通知布局系统更新:
class EquativDisplayView: ExpoView { let container = UIView() var bannerView:SASBannerView? private var optimalHeight: CGFloat = 0 required init(appContext: AppContext? = nil) { super.init(appContext: appContext) clipsToBounds = true addSubview(container) // 开启垂直方向的自适应优先级 self.setContentHuggingPriority(.required, for: .vertical) self.setContentCompressionResistancePriority(.required, for: .vertical) } // 重写固有尺寸计算逻辑 override var intrinsicContentSize: CGSize { return CGSize(width: bounds.width, height: optimalHeight) } override func layoutSubviews() { container.frame = bounds bannerView?.frame = bounds } func setConfiguration(_ configuration: Configuration) { let bannerView = SASBannerView(frame: CGRect(x: 0, y: 0, width: bounds.width, height: 0)) let adPlacement = SASAdPlacement.init(testAd: SASAdPlacementTest(rawValue: testPlacementId) ?? .bannerMRAID) bannerView.load(with: adPlacement) bannerView.delegate = self container.addSubview(bannerView) self.bannerView = bannerView } } extension EquativDisplayView :SASBannerViewDelegate { func bannerView(_ bannerView: SASBannerView, didDownloadAd ad: SASAd) { print("ad did load") optimalHeight = bannerView.optimalAdViewHeight(forContainer: nil) // 通知系统固有尺寸变化 invalidateIntrinsicContentSize() // 触发父视图布局更新 self.superview?.setNeedsLayout() self.superview?.layoutIfNeeded() } }
RN端保持原有代码,无需设置固定height,让组件自适应原生视图尺寸即可。
方案2:通过事件通知RN端动态修改高度
如果方案1不生效,可通过原生事件通知RN端更新组件高度:
- iOS端发送事件:
extension EquativDisplayView :SASBannerViewDelegate { func bannerView(_ bannerView: SASBannerView, didDownloadAd ad: SASAd) { print("ad did load") let optimalHeight = bannerView.optimalAdViewHeight(forContainer: nil) // 发送尺寸变化事件给RN self.appContext?.sendEvent(withName: "onAdSizeChanged", body: ["height": optimalHeight]) } }
- RN端监听事件并更新状态:
import { useState, useEffect } from 'react'; export default function App() { const [adHeight, setAdHeight] = useState(0); useEffect(() => { const subscription = EquativDisplay.addListener('onAdSizeChanged', (event) => { setAdHeight(event.height); }); return () => subscription.remove(); }, []); return ( <View style={styles.container}> <View style={{ height: 100 }} /> <Text style={{ flex: 1, backgroundColor: "yellow", width: "100%", textAlign: "center" }}> {EquativDisplay.hello()} </Text> <EquativDisplay.EquativDisplayView style={{ width: "100%", padding: 10, backgroundColor: "green", height: adHeight || 'auto', }} configuration={{ testPlacementId: EquativDisplay.PlacementTest.BannerMRAID, }} /> <Text style={{ flex: 1, backgroundColor: "yellow", width: "100%", textAlign: "center" }}> {EquativDisplay.hello()} </Text> </View> ) }
内容的提问来源于stack exchange,提问作者otusweb
相关产品推荐
相关产品推荐

