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

