You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UIHostingController遮挡底层视图触摸事件的解决方法

问题:SwiftUI叠加视图触摸事件透传失败,底层按钮无法响应

场景与问题

我有一段简化后的代码,包含以下组件:

  • ContentView1:带有按钮和计数标签的SwiftUI视图
  • ContentView2:顶部为空白、底部显示文本的SwiftUI视图
  • ViewController:将上述两个视图分别嵌入独立UIHostingController,并将ContentView2叠加在ContentView1之上的标准UIKit视图控制器

当前核心问题:ContentView1中的按钮无法响应触摸,上层ContentView2的UIHostingController会拦截所有触摸事件,无法传递到底层视图。

已考量的限制

  • 已设置hosting2.view.backgroundColor = .clear,ContentView1可见但按钮仍无法点击
  • 不能设置hosting2.view.isUserInteractionEnabled = false,因为需要与ContentView2进行交互
  • 无法重构为单个SwiftUI视图的ZStack结构,这是生产代码的简化版本,调整成本极高

曾尝试的无效方案

  • 实现继承自UIView的自定义PassthroughView,重写point(inside:with:)方法
  • 创建PassthroughHostingController并在loadView中设置自定义透传视图,但该方案导致SwiftUI视图无法加载

解决方案:正确实现触摸透传的HostingController

1. 自定义触摸透传视图PassthroughView

该视图仅在SwiftUI内容存在的区域拦截触摸,空白区域自动将事件透传到底层:

class PassthroughView: UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let hitView = super.hitTest(point, with: event)
        // 若触摸点落在空白区域(仅命中自身),返回nil让事件透传
        return hitView == self ? nil : hitView
    }
}

2. 创建适配的PassthroughHostingController

关键是要保证SwiftUI视图能正确挂载到自定义透传视图上,需在loadView中先初始化自定义视图,再调用父类方法:

class PassthroughHostingController<Content: View>: UIHostingController<Content> {
    override func loadView() {
        self.view = PassthroughView()
        super.loadView() // 必须调用,确保SwiftUI内容渲染到自定义视图
    }
}

3. 在ViewController中替换使用自定义HostingController

将加载ContentView2的UIHostingController替换为PassthroughHostingController:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 底层ContentView1
        let hosting1 = UIHostingController(rootView: ContentView1())
        addChild(hosting1)
        view.addSubview(hosting1.view)
        hosting1.view.frame = view.bounds
        hosting1.didMove(toParent: self)
        
        // 上层ContentView2:使用透传HostingController
        let hosting2 = PassthroughHostingController(rootView: ContentView2())
        addChild(hosting2)
        view.addSubview(hosting2.view)
        hosting2.view.frame = view.bounds
        hosting2.view.backgroundColor = .clear
        hosting2.didMove(toParent: self)
    }
}

原理说明

  • PassthroughView通过hitTest方法判断触摸目标:如果触摸点仅命中视图本身(即SwiftUI未渲染内容的空白区),则返回nil,让事件继续向下传递给底层的ContentView1;如果命中了SwiftUI的子视图(比如ContentView2的文本区域),则正常返回该子视图,保证ContentView2的交互功能正常。
  • 在PassthroughHostingController的loadView中先设置自定义视图再调用父类方法,确保SwiftUI的视图层级正确挂载,解决了之前视图无法加载的问题。

内容的提问来源于stack exchange,提问作者Matt Beaney

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 09:25:16