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

SwiftUI中实现常规文本转粗体动画的更优雅方案探讨

实现SwiftUI文本字重平滑过渡(支持自动换行)

问题背景

UI设计要求按钮按下后文本切换为粗体,但当前用两层Text叠加切换透明度的方案动画过渡不够平滑(比如字符错位),且需要支持文本自动换行,因此逐字符处理的方案不可行。希望找到借助TextKit实现浮点字重平滑过渡的方案,允许使用UIKit/Core Animation技术。

当前实现代码:

Text("Brat")
    .fontWeight(.bold)
    .foregroundStyle(Color.black.opacity(isSelected ? 1 : 0))
    .overlay(alignment: .center) {
        Text("Brat")
            .fontWeight(.regular)
            .opacity(isSelected ? 0 : 1)
    }
    .padding()
    .id(model.title)

最优实现思路:封装UIKit UILabel做字重插值动画

核心思路是利用UIKit的UILabel(基于TextKit)支持动态字体调整的特性,通过插值浮点型字重值生成过渡字体,配合UIKit的动画系统实现平滑过渡,同时原生支持文本换行。

具体实现步骤

  1. 封装SwiftUI可复用组件:用UIViewRepresentable将UILabel包装成SwiftUI视图,绑定一个浮点型的字重控制值(比如0对应常规字重,1对应粗体)。
  2. 配置UILabel支持换行:设置numberOfLines = 0让文本自动换行。
  3. 字重插值与动画:在字重值变化时,通过UIFontDescriptor动态生成对应字重的字体,用UIKit的动画API实现字体的平滑切换。

代码示例

import SwiftUI

struct WeightAnimatableText: UIViewRepresentable {
    let text: String
    @Binding var weightProgress: Double // 0 = 常规字重, 1 = 粗体
    let baseFont: UIFont
    
    func makeUIView(context: Context) -> UILabel {
        let label = UILabel()
        label.numberOfLines = 0
        label.text = text
        label.textAlignment = .center // 可根据需求调整对齐方式
        return label
    }
    
    func updateUIView(_ uiView: UILabel, context: Context) {
        // 字重映射:UIFont.Weight的rawValue范围大致是-1(ultraLight)到1(black),这里取0到0.7对应regular到bold
        let targetWeight = UIFont.Weight(rawValue: weightProgress * 0.7)
        let fontDescriptor = baseFont.fontDescriptor.addingAttributes([
            .traits: [UIFontDescriptor.TraitKey.weight: targetWeight]
        ])
        let animatedFont = UIFont(descriptor: fontDescriptor, size: baseFont.pointSize)
        
        // 平滑动画过渡,时长可自定义
        UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseInOut) {
            uiView.font = animatedFont
        }
    }
}

// 使用示例
struct DemoView: View {
    @State private var isSelected = false
    
    var body: some View {
        WeightAnimatableText(
            text: "Brat\n这是一段支持换行的测试文本",
            weightProgress: .constant(isSelected ? 1 : 0),
            baseFont: UIFont.systemFont(ofSize: 18)
        )
        .padding()
        .onTapGesture {
            isSelected.toggle()
        }
    }
}

方案优势

  • 原生支持换行:依赖UILabel的TextKit布局,自动处理文本换行,无需自定义布局逻辑。
  • 平滑无错位:直接过渡字体本身,避免了两层文本叠加导致的字符偏移问题。
  • 灵活控制:通过浮点值可精确控制字重过渡的进度,支持任意中间状态的动画。

注意事项

  • 不同字体的字重rawValue范围可能有差异,比如部分自定义字体的粗体对应值可能不是0.7,需要根据实际使用的字体调整插值乘数,确保过渡到预期的粗体效果。
  • 如果需要更精细的动画控制,可以改用Core Animation的显式动画(比如CABasicAnimation),直接对UILabel的font属性做动画。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:03:23