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

如何在Swift中创建可感知触摸位置并返回x、y轴0-1取值的按钮

Swift 双轴坐标感知按钮实现方案

核心实现逻辑

  • 捕获用户触摸在按钮视图范围内的坐标点,将坐标值与按钮自身的宽高做除法运算做归一化处理,得到0~1区间的映射值
  • 触摸事件可通过UIKit的原生触摸响应回调、SwiftUI的手势Modifier两种方式实现
  • 计算过程中需添加边界限制逻辑,保证最终输出值始终固定在0~1范围内,同时避免视图宽高为0时出现除零异常
    注意:默认计算规则以视图左上角为坐标原点,若需要适配左下角为原点的通用坐标系,可将y轴的归一化结果替换为 1 - 原始计算值

UIKit 实现方案

自定义UIControl子类,继承系统触摸事件回调实现坐标采集:

import UIKit

class CoordinateAwareButton: UIControl {
    // 对外暴露的归一化坐标,x、y取值范围均为0~1
    private(set) var normalizedPoint: CGPoint = .zero
    
    override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        super.beginTracking(touch, with: event)
        updateNormalizedPoint(touch: touch)
        sendActions(for: .touchDown)
        return true
    }
    
    override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        super.continueTracking(touch, with: event)
        updateNormalizedPoint(touch: touch)
        sendActions(for: .valueChanged)
        return true
    }
    
    override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
        super.endTracking(touch, with: event)
        guard let touch = touch else { return }
        updateNormalizedPoint(touch: touch)
        sendActions(for: .touchUpInside)
    }
    
    private func updateNormalizedPoint(touch: UITouch) {
        let location = touch.location(in: self)
        let normalizedX = max(0, min(1, location.x / bounds.width))
        let normalizedY = max(0, min(1, location.y / bounds.height))
        normalizedPoint = CGPoint(x: normalizedX, y: normalizedY)
    }
}

使用示例

// 初始化按钮
let trackButton = CoordinateAwareButton(frame: CGRect(x: 60, y: 100, width: 240, height: 240))
trackButton.backgroundColor = .systemBlue
// 添加值变化监听
trackButton.addTarget(self, action: #selector(onButtonValueChanged), for: .valueChanged)
view.addSubview(trackButton)

@objc private func onButtonValueChanged(sender: CoordinateAwareButton) {
    print("当前归一化坐标 x:\(sender.normalizedPoint.x) y:\(sender.normalizedPoint.y)")
}

SwiftUI 实现方案

通过DragGesture结合GeometryReader实现坐标采集:

import SwiftUI

struct CoordinateAwareButton: View {
    @Binding var normalizedPoint: CGPoint
    let tapAction: () -> Void
    
    var body: some View {
        GeometryReader { geometry in
            RoundedRectangle(cornerRadius: 12)
                .fill(.blue)
                .gesture(
                    DragGesture(minimumDistance: 0)
                        .onChanged { value in
                            let loc = value.location
                            let normalizedX = max(0, min(1, loc.x / geometry.size.width))
                            let normalizedY = max(0, min(1, loc.y / geometry.size.height))
                            normalizedPoint = CGPoint(x: normalizedX, y: normalizedY)
                        }
                        .onEnded { _ in
                            tapAction()
                        }
                )
        }
    }
}

使用示例

struct DemoView: View {
    @State private var currentPoint = CGPoint.zero
    
    var body: some View {
        VStack(spacing: 24) {
            Text("当前坐标 x:\(String(format: "%.2f", currentPoint.x)) y:\(String(format: "%.2f", currentPoint.y))")
                .font(.headline)
            CoordinateAwareButton(normalizedPoint: $currentPoint) {
                print("按钮点击结束,最终坐标:\(currentPoint)")
            }
            .frame(width: 240, height: 240)
        }
        .padding()
    }
}

可选优化点

  • 如果需要支持触摸移出按钮范围后依然继续跟踪坐标,可在UIKit的continueTracking方法中修改返回逻辑,或给SwiftUI手势指定全局坐标空间
  • 可根据业务需求添加不同触摸阶段的回调事件,比如触摸开始、触摸取消等场景的自定义响应

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 09:54:04