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

SwiftUI中如何阻止DragGesture修改光标或指定其使用的光标?

SwiftUI中为Divider拖拽手势指定自定义光标

SwiftUI的DragGesture默认会在拖拽过程中将光标切换为pointingHand,这确实不符合左右调整尺寸的交互预期。下面提供两种可行的解决方案,替代你当前的临时实现:

方案一:自定义NSViewRepresentable分隔线(彻底控制光标)

通过包装NSView来实现可拖拽的分隔线,完全接管手势和光标逻辑,避免系统默认行为的干扰:

import SwiftUI

struct ResizableDivider: NSViewRepresentable {
    @Binding var width: CGFloat
    let minWidth: CGFloat = 150
    
    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        view.frame.size.height = 1
        view.backgroundColor = .separatorColor
        
        let panGesture = NSPanGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handlePan(_:)))
        view.addGestureRecognizer(panGesture)
        
        return view
    }
    
    func updateNSView(_ nsView: NSView, context: Context) {}
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject {
        let parent: ResizableDivider
        
        init(_ parent: ResizableDivider) {
            self.parent = parent
        }
        
        @objc func handlePan(_ gesture: NSPanGestureRecognizer) {
            let translation = gesture.translation(in: gesture.view)
            
            switch gesture.state {
            case .began:
                NSCursor.resizeLeftRight.push()
            case .changed:
                parent.width = max(parent.minWidth, parent.width + translation.x)
                gesture.setTranslation(.zero, in: gesture.view)
            case .ended, .cancelled:
                NSCursor.pop()
            default: break
            }
        }
    }
}

// 使用示例
struct ContentView: View {
    @State private var width: CGFloat = 200
    
    var body: some View {
        HStack(spacing: 0) {
            ViewA().frame(width: width)
            ResizableDivider(width: $width)
            ViewB().frame(maxWidth: .infinity)
        }
    }
}

方案二:修复原生SwiftUI手势的光标冲突

如果想继续使用原生Divider和DragGesture,可以修正光标管理逻辑,强制覆盖系统默认的pointingHand:

HStack(spacing: 0) {
    ViewA().frame(width: self.width, alignment: .leading)

    Divider()
        .onHover { isHovering in
            isHovering ? NSCursor.resizeLeftRight.push() : NSCursor.pop()
        }
        .gesture(
            DragGesture(minimumDistance: 0)
                .onChanged { value in
                    self.width = max(150, self.width + value.translation.width)
                    // 持续push光标,覆盖系统自动设置的pointingHand
                    NSCursor.resizeLeftRight.push()
                }
                .onEnded { _ in
                    NSCursor.pop()
                }
        )

    ViewB().frame(maxWidth: .infinity, alignment: .trailing)
}

注意:原代码使用VStack可能是笔误,左右调整宽度应该用HStack;同时修正了onHover的光标逻辑,确保hover和拖拽状态下都显示resizeLeftRight。

两种方案对比:

  • 方案一:交互更稳定,无系统行为干扰,适合需要扩展更多自定义逻辑的场景
  • 方案二:代码更简洁,依赖SwiftUI原生组件,但需要通过重复push光标来覆盖系统默认行为

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 00:52:22