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
相关产品推荐
相关产品推荐

