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

Swift 4自定义按钮形状开发:人体区域选择APP需求

实现不规则人体部位点击的完整方案

嘿,我之前正好做过类似的人体部位点击需求,结合你提到的贝塞尔路径思路,给你梳理一套完整的实现方案,帮你快速搞定这个功能!

核心思路

利用UIBezierPath定义每个人体部位的不规则形状,然后在自定义View中监听触摸事件,通过路径的contains(_:)方法判断点击是否落在目标区域内,完全避开Xcode默认的矩形按钮限制。

具体实现步骤

1. 准备各部位的贝塞尔路径

首先你需要把人体示意图的每个可点击区域(比如头部、躯干、左右手臂等),分别用UIBezierPath绘制出来:

  • 建议把所有路径存到一个字典里,比如[String: UIBezierPath],键是部位名称(如"head"、"left_arm"),值是对应的路径,后续判断点击时更方便。
  • 绘制路径时一定要对齐你的示意图坐标系!比如如果示意图放在UIImageView里,路径的坐标要基于这个View的bounds来调整,避免点击判断偏移。
  • 复杂部位可以拆分成多个子路径组合,或者用CGPath的裁剪、合并工具调整形状,调试时可以临时把路径描边显示出来,检查是否和示意图贴合。

2. 自定义可点击的人体示意图View

创建一个继承自UIView的子类,把示意图、路径和触摸逻辑整合在一起:

class BodyPartClickableView: UIView {
    // 人体示意图图片
    var bodyImage: UIImage? {
        didSet { setNeedsDisplay() }
    }
    // 存储所有部位的路径
    var bodyPartPaths: [String: UIBezierPath] = [:]
    // 点击回调,传递被点击的部位名称
    var onBodyPartTap: ((String) -> Void)?
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        // 先绘制人体示意图
        bodyImage?.draw(in: bounds)
        
        // 【调试用】可以把路径描红显示,检查是否贴合示意图
        // UIColor.red.setStroke()
        // bodyPartPaths.values.forEach { $0.stroke() }
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touchPoint = touches.first?.location(in: self) else { return }
        
        // 遍历所有路径,判断点击点是否在路径内
        for (partName, path) in bodyPartPaths {
            if path.contains(touchPoint) {
                onBodyPartTap?(partName)
                // 可选:添加点击高亮效果,比如标记选中路径并重绘
                setNeedsDisplay()
                break
            }
        }
    }
}

3. 在ViewController中使用自定义View

把自定义View加到页面上,传入示意图和路径,监听点击事件:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 创建自定义View,设置合适的frame
        let bodyClickView = BodyPartClickableView(frame: CGRect(x: 50, y: 100, width: 300, height: 500))
        // 设置人体示意图
        bodyClickView.bodyImage = UIImage(named: "body_diagram")
        
        // 传入各部位的路径(这里以头部和左手臂为例)
        bodyClickView.bodyPartPaths["head"] = createHeadPath()
        bodyClickView.bodyPartPaths["left_arm"] = createLeftArmPath()
        
        // 监听点击事件
        bodyClickView.onBodyPartTap = { partName in
            print("用户点击了:\(partName)")
            // 这里可以处理后续逻辑,比如弹出部位详情、显示症状选项等
        }
        
        view.addSubview(bodyClickView)
    }
    
    // 示例:创建头部的贝塞尔路径(根据你的实际示意图坐标调整)
    private func createHeadPath() -> UIBezierPath {
        let path = UIBezierPath()
        path.move(to: CGPoint(x: 150, y: 50))
        path.addArc(withCenter: CGPoint(x: 150, y: 80), radius: 30, startAngle: 0, endAngle: .pi * 2, clockwise: true)
        return path
    }
    
    // 同理实现其他部位的路径绘制...
}

4. 优化交互体验

  • 点击高亮:可以在自定义View中添加一个selectedPartName属性,当点击某个部位时记录下来,在draw(_:)方法中给选中的路径填充半透明颜色,让用户有明确的反馈。
  • 路径精度:如果点击判断不够精准,可以调整贝塞尔路径的控制点,或者给路径添加一点容差(比如用CGPath的copy(strokingWithWidth:lineCap:lineJoin:miterLimit:transform:)方法生成一个稍宽的路径来判断点击)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:37:29