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

Swift UI实现单行长文本左右循环滚动动画的问题

解决SwiftUI单行长文本左右循环滚动问题

你当前的代码无法实现回滚的核心原因是:scrollTo(bottomID)是一次性的定位动作,即便给它加上repeatForever(autoreverses: true)的动画,也只是重复执行“滚动到bottomID”这个操作,并不会自动反向滚动回起点。

下面提供两种可靠的实现方案:

方案一:基于ScrollViewReader的无缝滚动

这种方案通过复制文本、配合滚动锚点设置,实现自然的来回滚动效果:

private func scrollViewForLongName(_ name: String) -> some View {
    let startID = "start"
    let endID = "end"
    
    return ScrollViewReader { proxy in
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 20) {
                Text(name)
                    .id(startID)
                Text(name)
                    .id(endID)
            }
        }
        .clipped()
        .onAppear {
            let scrollAnimation = Animation.easeInOut(duration: 3)
                .repeatForever(autoreverses: true)
            
            withAnimation(scrollAnimation) {
                proxy.scrollTo(endID, anchor: .leading)
            }
        }
    }
}

关键改动说明:

  • 复制一份文本:避免滚动到末尾时出现空白跳转,让整个滚动过程看起来连续自然
  • 设置anchor: .leading:确保滚动到第二个文本的开头位置,动画反向时会平滑滚回第一个文本的起点
  • 动画配置:repeatForever(autoreverses: true)会让滚动动作自动来回执行,无需额外逻辑

方案二:基于Offset的轻量滚动

如果只是纯文本的滚动需求,用Offset动画更轻量,无需依赖ScrollView:

@State private var offsetX: CGFloat = 0

private func scrollViewForLongName(_ name: String) -> some View {
    GeometryReader { geo in
        Text(name)
            .lineLimit(1)
            .offset(x: offsetX)
            .onAppear {
                let textWidth = getTextWidth(name, font: .body)
                // 只有文本宽度超过容器时才启动滚动
                guard textWidth > geo.size.width else { return }
                let scrollDistance = textWidth - geo.size.width
                
                withAnimation(Animation.easeInOut(duration: 3).repeatForever(autoreverses: true)) {
                    offsetX = -scrollDistance
                }
            }
    }
    .clipped()
}

// 辅助方法:计算文本的实际宽度
private func getTextWidth(_ text: String, font: Font) -> CGFloat {
    let label = UILabel()
    label.text = text
    label.font = UIFont(font)
    label.sizeToFit()
    return label.frame.width
}

关键逻辑说明:

  • 先计算文本宽度和容器宽度的差值,确定需要滚动的距离
  • 通过offset(x:)动画让文本左右移动,达到滚动效果
  • 加判断逻辑:只有文本超出容器时才启动滚动,避免短文本无效动画

内容的提问来源于stack exchange,提问作者Kevin Roy Lima

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 19:37:27