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

SwiftUI中ScrollView内Spline 3D模型手势冲突解决咨询

问题:ScrollView与Spline 3D模型手势冲突解决方案?

我是Swift和SwiftUI初学者,正在开发一个副业项目。尝试将从Spline Cloud获取的交互式3D模型加载到ScrollView中,但3D模型的手势会与ScrollView手势相互干扰,导致用户无法正常与3D模型交互。我希望将Spline导入的可交互3D模型放在首页顶部,页面其余部分展示用户统计信息。我研究过Exclusive Gestures和Simultaneous Gestures,但似乎都无法解决问题。

我的Home View代码如下:

import SwiftUI
import SplineRuntime

struct HomeView: View {
    var body: some View {
        ScrollView {
            // Header
            HStack {
                Text("Home")
                    .font(.title)
                    .bold()
                    .padding([.leading, .top])
                Spacer()
            }
            
            // 3D Model
            SplineModel()
        }
    }
}

struct SplineModel: View {
    var body: some View {

        // fetching from cloud
        let link = URL(string: "https://build.spline.design/G6QJHoNN2abqhaMbBmGM/scene.splineswift")!

        SplineView(sceneFileURL: link).ignoresSafeArea(.all)
            .frame(height: 300)
            .background(Color.gray)
    }
}

#Preview {
    HomeView()
}

请问使用当前Swift 5 API能否实现该需求?


解决方案

可以实现,核心思路是通过手势优先级控制或动态滚动状态管理,让Spline 3D模型的交互手势优先响应,同时保证ScrollView在非交互场景下正常工作。

方法1:使用.highPriorityGesture提升Spline手势优先级

SplineRuntime的SplineView内部已封装了交互手势,我们可以通过highPriorityGesture让它的手势优先于ScrollView的滚动手势:

修改SplineModel结构体:

struct SplineModel: View {
    var body: some View {
        let link = URL(string: "https://build.spline.design/G6QJHoNN2abqhaMbBmGM/scene.splineswift")!
        
        SplineView(sceneFileURL: link)
            .ignoresSafeArea(.all)
            .frame(height: 300)
            .background(Color.gray)
            // 让拖拽手势优先响应,覆盖ScrollView的滚动
            .highPriorityGesture(DragGesture())
    }
}

如果你的3D模型需要支持捏合、旋转等多手势交互,可以组合多个手势:

.highPriorityGesture(
    DragGesture()
        .simultaneously(with: MagnificationGesture())
        .simultaneously(with: RotationGesture())
)

方法2:动态控制ScrollView的滚动状态

通过监听用户与SplineView的交互状态,动态禁用/启用ScrollView滚动,实现更精确的控制:

  1. 自定义UIScrollView子类:
import UIKit

class CustomScrollView: UIScrollView {
    var isScrollEnabledWhenInteracting = true {
        didSet {
            isScrollEnabled = isScrollEnabledWhenInteracting
        }
    }
}
  1. 创建SwiftUI包装器:
struct CustomScrollViewWrapper<Content: View>: UIViewRepresentable {
    @Binding var isScrollEnabled: Bool
    let content: Content
    
    init(isScrollEnabled: Binding<Bool>, @ViewBuilder content: () -> Content) {
        self._isScrollEnabled = isScrollEnabled
        self.content = content()
    }
    
    func makeUIView(context: Context) -> CustomScrollView {
        let scrollView = CustomScrollView()
        scrollView.isScrollEnabled = isScrollEnabled
        
        let hostingController = UIHostingController(rootView: content)
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(hostingController.view)
        
        NSLayoutConstraint.activate([
            hostingController.view.topAnchor.constraint(equalTo: scrollView.topAnchor),
            hostingController.view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
            hostingController.view.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
            hostingController.view.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
            hostingController.view.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
        ])
        
        return scrollView
    }
    
    func updateUIView(_ uiView: CustomScrollView, context: Context) {
        uiView.isScrollEnabledWhenInteracting = isScrollEnabled
    }
}
  1. 在HomeView中使用并监听交互状态:
struct HomeView: View {
    @State private var isScrollEnabled = true
    
    var body: some View {
        CustomScrollViewWrapper(isScrollEnabled: $isScrollEnabled) {
            HStack {
                Text("Home")
                    .font(.title)
                    .bold()
                    .padding([.leading, .top])
                Spacer()
            }
            
            SplineModel(isScrollEnabled: $isScrollEnabled)
        }
    }
}

struct SplineModel: View {
    @Binding var isScrollEnabled: Bool
    
    var body: some View {
        let link = URL(string: "https://build.spline.design/G6QJHoNN2abqhaMbBmGM/scene.splineswift")!
        
        SplineView(sceneFileURL: link)
            .ignoresSafeArea(.all)
            .frame(height: 300)
            .background(Color.gray)
            // 用户开始拖拽3D模型时禁用滚动,结束后恢复
            .gesture(
                DragGesture(minimumDistance: 0)
                    .onChanged { _ in
                        isScrollEnabled = false
                    }
                    .onEnded { _ in
                        isScrollEnabled = true
                    }
            )
    }
}

注意事项

  • 确保使用的SplineRuntime版本支持手势交互,若版本较旧建议升级。
  • 测试不同手势组合,确保3D模型的所有交互(拖拽、旋转、捏合)都能正常响应,同时ScrollView在非交互区域可正常滚动。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 04:05:14