Skip to content

Rotating Words Text

Words that take turns in the same spot, each one pushing the last out of the way.

Rotating Words Text, playing
  • Text
  • iOS 18.0+
  • beginner
Download .swift
Origin
Original work
Found by
rotating words · word swap · push transition · headline · hero text · cycling text · palabras rotatorias · cambiar palabras · transición · titular · texto cíclico · encabezado

Words that take turns in the same spot, each one pushing the last out of the way.

It reserves the width of the longest word, so nothing around it moves. With Reduce Motion on, the words crossfade.

struct RotatingWordsTextDemo: View {
var body: some View {
ZStack {
LinearGradient(colors: [Color(white: 0.12), .black], startPoint: .top, endPoint: .bottom)
.ignoresSafeArea()
VStack(alignment: .leading, spacing: 4) {
Text("Made for")
.foregroundStyle(.white)
RotatingWordsText(words: ["designers", "developers", "dreamers", "everyone"], interval: 1.2)
.foregroundStyle(.linearGradient(colors: [.orange, .pink], startPoint: .leading, endPoint: .trailing))
}
.font(.system(.largeTitle, design: .rounded, weight: .heavy))
.padding(32)
}
}
}
ParameterTypeDefaultDescription
words[String]Required
intervalDouble1.6Seconds each word stays on screen.

Copy this code into your project, or download the file.

RotatingWordsText.swift
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Oscar Canton and SwiftUI Blocks contributors
import SwiftUI
/// Words that take turns in the same spot, each one pushing the last out of the way.
///
/// It reserves the width of the longest word, so nothing around it moves.
/// With Reduce Motion on, the words crossfade.
struct RotatingWordsText: View {
let words: [String]
/// Seconds each word stays on screen.
var interval: Double = 1.6
@State private var index = 0
@Environment(\.accessibilityReduceMotion) private var reduceMotion
private var transition: AnyTransition {
reduceMotion ? .opacity : .push(from: .bottom)
}
var body: some View {
ZStack(alignment: .leading) {
ForEach(words.indices, id: \.self) { position in
Text(words[position]).hidden()
}
if !words.isEmpty {
Text(words[index % words.count])
.id(index)
.transition(transition)
}
}
.clipped()
.accessibilityElement(children: .ignore)
.accessibilityLabel(words.joined(separator: ", "))
.task(id: words) { await rotate() }
}
private func rotate() async {
guard words.count > 1 else { return }
while !Task.isCancelled {
try? await Task.sleep(for: .seconds(interval))
guard !Task.isCancelled else { return }
withAnimation(.snappy(duration: 0.45)) {
index = (index + 1) % words.count
}
}
}
}
// MARK: - Demo
struct RotatingWordsTextDemo: View {
var body: some View {
ZStack {
LinearGradient(colors: [Color(white: 0.12), .black], startPoint: .top, endPoint: .bottom)
.ignoresSafeArea()
VStack(alignment: .leading, spacing: 4) {
Text("Made for")
.foregroundStyle(.white)
RotatingWordsText(words: ["designers", "developers", "dreamers", "everyone"], interval: 1.2)
.foregroundStyle(.linearGradient(colors: [.orange, .pink], startPoint: .leading, endPoint: .trailing))
}
.font(.system(.largeTitle, design: .rounded, weight: .heavy))
.padding(32)
}
}
}
#Preview {
RotatingWordsTextDemo()
}