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

自定义绘制UIButton如何适配高亮、禁用等不同状态的渲染需求

类UIColorWell圆形自定义UIButton实现方案

方案1:多CALayer实现(兼容iOS14+,优先推荐)

  • 无需依赖按钮原生layer属性,新增2个自定义CALayer即可实现外边框、透明间隔、中心彩色的三层结构:
    1. 外边框layer:cornerRadius设为按钮宽度/2,fillColor设为clear,strokeColor设为tintColor,lineWidth为预期的外边框粗细,frame与按钮bounds完全对齐
    2. 中心彩色layer:边长为 按钮宽度 - 2*外边框粗细 - 2*透明间隔宽度,cornerRadius为边长/2,fillColor设为自定义颜色,frame居中于按钮bounds,天然和外边框留出透明空隙
  • 状态适配逻辑:重写tintColorDidChange方法,以及isHighlighted、isEnabled、isSelected的属性观察者,状态变更时直接修改对应layer的颜色、透明度即可,系统自带过渡动画,性能优于重写draw方案。

方案2:SFSymbol手动拼接方案(兼容iOS14+)

  • 不需要iOS15的符号层级渲染能力,手动拼接两层内容即可实现需求:
    1. 先使用UIImage(systemName: "circle")获取系统圆形边框符号,设置为alwaysOriginal渲染模式,用当前tintColor着色,得到外边框图片
    2. 开启图形上下文,先绘制外边框图片,再在中心位置绘制直径更小的自定义颜色圆形,导出为新的合成图片,设为按钮不同状态的image即可

方案3:重写draw方法适配状态实现

核心是监听所有状态变更时机触发重绘,示例代码如下:

class CustomColorWellButton: UIButton {
    // 自定义配置
    var centerColor: UIColor = .systemBlue {
        didSet { setNeedsDisplay() }
    }
    private let borderWidth: CGFloat = 2
    private let gapWidth: CGFloat = 2

    // 监听所有状态变更,触发重绘
    override var isHighlighted: Bool {
        didSet { setNeedsDisplay() }
    }
    override var isEnabled: Bool {
        didSet { setNeedsDisplay() }
    }
    override var isSelected: Bool {
        didSet { setNeedsDisplay() }
    }
    override func tintColorDidChange() {
        super.tintColorDidChange()
        setNeedsDisplay()
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)
        guard let context = UIGraphicsGetCurrentContext() else { return }
        let center = CGPoint(x: bounds.midX, y: bounds.midY)
        let outerRadius = bounds.width / 2 - borderWidth / 2

        // 绘制外边框
        context.setStrokeColor(tintColor.cgColor)
        context.setLineWidth(borderWidth)
        context.addArc(center: center, radius: outerRadius, startAngle: 0, endAngle: .pi * 2, clockwise: false)
        context.strokePath()

        // 根据状态调整中心色透明度
        var finalCenterColor = centerColor
        if !isEnabled {
            finalCenterColor = finalCenterColor.withAlphaComponent(0.3)
        } else if isHighlighted {
            finalCenterColor = finalCenterColor.withAlphaComponent(0.7)
        }

        // 绘制中心彩色圆
        let centerRadius = outerRadius - borderWidth / 2 - gapWidth
        context.setFillColor(finalCenterColor.cgColor)
        context.addArc(center: center, radius: centerRadius, startAngle: 0, endAngle: .pi * 2, clockwise: false)
        context.fillPath()
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 03:27:04