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

Swift自定义气泡视图组件:跑步者进度展示实现方案咨询

跑步者进度气泡组件实现方案

问题概述

我需要开发一个以气泡形式展示多名跑步者进度的组件,尝试用Charts库实现但未达到预期效果。当前的Swift实现代码如下:

override func viewDidLoad() {
    super.viewDidLoad()

    self.configureChart(raceChartData: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], raceDistance: 10)
}

func configureChart(raceChartData: [Int], raceDistance: Double) {
    chartView.chartDescription.enabled = false
    chartView.dragEnabled = false
    chartView.pinchZoomEnabled = false
    chartView.xAxis.enabled = true
    //chartView.maxVisibleCount = 0
    chartView.autoScaleMinMaxEnabled = false
    chartView.leftAxis.enabled = false
    chartView.rightAxis.enabled = false
    chartView.legend.enabled = false
    chartView.legend.drawInside = false
    chartView.setScaleEnabled(false)
    let xAxis: XAxis = chartView.xAxis
    xAxis.axisLineColor = #colorLiteral(red: 1, green: 0.4823529412, blue: 0.1450980392, alpha: 1).withAlphaComponent(0.1)
    xAxis.drawAxisLineEnabled = false
    xAxis.drawLabelsEnabled = false
    xAxis.gridColor = #colorLiteral(red: 1, green: 0.4823529412, blue: 0.1450980392, alpha: 1).withAlphaComponent(0.3)
    //xAxis.axisMinimum = 1
    //xAxis.axisMaximum = 10

    var calculatedChartDistance = 0.0

    if raceChartData.count != 0 {
        var chartdistanceArray = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
        if activeRaceFormat == "SPDWRK" {
            calculatedChartDistance = raceDistance
        } else {
            chartView.xAxis.centeredEntries = [0, 0.5, 0.4]
            chartView.xAxis.centerAxisLabelsEnabled = true
            calculatedChartDistance = raceDistance
        }

        for i in 0...9 {
            chartdistanceArray[i] = calculatedChartDistance * Double((i + 1))
        }

        let yVals1 = [
            BubbleChartDataEntry(x: chartdistanceArray[0], y: 0, size: CGFloat(raceChartData[0])),
            BubbleChartDataEntry(x: chartdistanceArray[1], y: 0, size: CGFloat(raceChartData[1])),
            BubbleChartDataEntry(x: chartdistanceArray[2], y: 0, size: CGFloat(raceChartData[2])),
            BubbleChartDataEntry(x: chartdistanceArray[3], y: 0, size: CGFloat(raceChartData[3])),
            BubbleChartDataEntry(x: chartdistanceArray[4], y: 0, size: CGFloat(raceChartData[4])),
            BubbleChartDataEntry(x: chartdistanceArray[5], y: 0, size: CGFloat(raceChartData[5])),
            BubbleChartDataEntry(x: chartdistanceArray[6], y: 0, size: CGFloat(raceChartData[6])),
            BubbleChartDataEntry(x: chartdistanceArray[7], y: 0, size: CGFloat(raceChartData[7])),
            BubbleChartDataEntry(x: chartdistanceArray[8], y: 0, size: CGFloat(raceChartData[8])),
            BubbleChartDataEntry(x: chartdistanceArray[9], y: 0, size: CGFloat(raceChartData[9]))
        ]

        let set1 = BubbleChartDataSet(entries: yVals1)
        set1.drawIconsEnabled = false
        set1.setColor(ChartColorTemplates.colorful()[1], alpha: 0.5)
        set1.drawValuesEnabled = true
        set1.normalizeSizeEnabled = false

        let data = [set1] as BubbleChartData
        data.setDrawValues(true)
        data.setValueFont(UIFont(name: "HelveticaNeue-Light", size: 7)!)
        data.setHighlightCircleWidth(0.5)
        chartView.data = data
    }
}

当前效果

当前效果截图

目标效果

目标效果截图

需求说明:赛程为10KM,带头像的气泡及标线代表用户自身的跑步进度,其他气泡代表对应距离处的跑步者人数,所有数据来自后端Socket。考虑基于UIView从零开发该组件,以下是具体实现思路:


从零开发UIView组件的实现思路

1. 组件结构拆分

  • 进度基线层:绘制横向赛程轴,包含等距刻度线和距离标签(如0KM、2KM...10KM)
  • 普通跑步者气泡层:根据各距离点的人数,绘制对应大小的半透明气泡,气泡内显示人数数值
  • 用户专属标记层:绘制垂直标线+带头像的气泡,突出用户当前进度位置

2. 核心布局计算

  • 坐标映射:将赛程距离(0-10KM)转换为组件内的X坐标,比如组件总宽度为viewWidth,则nKM处的X坐标为 (n / 10) * viewWidth
  • 气泡大小适配:预设最小/最大气泡直径(如12pt-40pt),根据当前人数与最大人数的比例,计算对应气泡直径,确保视觉比例协调
  • 用户位置定位:根据用户当前跑过的距离,计算其在进度轴上的X坐标,垂直标线从基线向上延伸至气泡中心位置

3. 自定义绘制(Core Graphics)

在自定义UIView的draw(_ rect: CGRect)方法中完成所有绘制逻辑:

  • 绘制进度基线:用UIBezierPath绘制横向直线,再添加等距刻度线和距离文本
  • 绘制普通气泡:遍历跑步者数据,在对应X坐标处绘制圆形气泡,填充半透明颜色,再用UILabel或直接绘制文本显示人数
  • 绘制用户标记:先绘制垂直标线,再加载用户头像并裁剪为圆形,放在对应X坐标的上方
  • 注意:提前缓存计算好的坐标、气泡大小等数据,避免在draw方法中做重复计算

4. 实时数据更新(Socket驱动)

  • 监听后端Socket推送的实时数据,包括各距离点人数、用户当前进度
  • 数据更新时,更新组件内的数据源,调用setNeedsDisplay()触发重绘;可选添加气泡大小/位置的过渡动画,提升流畅度

5. 交互与边界处理

  • 可选添加气泡点击交互:给每个气泡添加点击手势,点击时展示该位置的跑步者详情
  • 边界情况处理:人数为0时不绘制对应气泡;用户进度超过10KM时,将位置固定在终点
  • 性能优化:异步加载用户头像,避免阻塞UI绘制;复用文本绘制的UIFont、NSParagraphStyle等对象

内容的提问来源于stack exchange,提问作者Wo_0NDeR ᵀᴹ

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 10:22:04