RepoPilotOpen in app →

lexrus/LTMorphingLabel

[EXPERIMENTAL] Graceful morphing effects for UILabel written in Swift.

Mixed

Mixed signals — read the receipts

worst of 4 axes
Use as dependencyMixed

no tests detected; no CI workflows detected

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 2mo ago
  • 20 active contributors
  • MIT licensed
Show 3 more →
  • Concentrated ownership — top contributor handles 74% of recent commits
  • No CI workflows detected
  • No test directory detected
What would change the summary?
  • Use as dependency MixedHealthy if: add a test suite

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Forkable" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Forkable
[![RepoPilot: Forkable](https://repopilot.app/api/badge/lexrus/ltmorphinglabel?axis=fork)](https://repopilot.app/r/lexrus/ltmorphinglabel)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/lexrus/ltmorphinglabel on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: lexrus/LTMorphingLabel

Generated by RepoPilot · 2026-05-10 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/lexrus/LTMorphingLabel shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

WAIT — Mixed signals — read the receipts

  • Last commit 2mo ago
  • 20 active contributors
  • MIT licensed
  • ⚠ Concentrated ownership — top contributor handles 74% of recent commits
  • ⚠ No CI workflows detected
  • ⚠ No test directory detected

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live lexrus/LTMorphingLabel repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/lexrus/LTMorphingLabel.

What it runs against: a local clone of lexrus/LTMorphingLabel — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in lexrus/LTMorphingLabel | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 93 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>lexrus/LTMorphingLabel</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of lexrus/LTMorphingLabel. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/lexrus/LTMorphingLabel.git
#   cd LTMorphingLabel
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of lexrus/LTMorphingLabel and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "lexrus/LTMorphingLabel(\\.git)?\\b" \\
  && ok "origin remote is lexrus/LTMorphingLabel" \\
  || miss "origin remote is not lexrus/LTMorphingLabel (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "LTMorphingLabel/LTMorphingLabel.swift" \\
  && ok "LTMorphingLabel/LTMorphingLabel.swift" \\
  || miss "missing critical file: LTMorphingLabel/LTMorphingLabel.swift"
test -f "LTMorphingLabel/LTCharacterDiffResult.swift" \\
  && ok "LTMorphingLabel/LTCharacterDiffResult.swift" \\
  || miss "missing critical file: LTMorphingLabel/LTCharacterDiffResult.swift"
test -f "LTMorphingLabel/LTStringDiffResult.swift" \\
  && ok "LTMorphingLabel/LTStringDiffResult.swift" \\
  || miss "missing critical file: LTMorphingLabel/LTStringDiffResult.swift"
test -f "LTMorphingLabel/LTMorphingEffect.swift" \\
  && ok "LTMorphingLabel/LTMorphingEffect.swift" \\
  || miss "missing critical file: LTMorphingLabel/LTMorphingEffect.swift"
test -f "LTMorphingLabel/LTCharacterLimbo.swift" \\
  && ok "LTMorphingLabel/LTCharacterLimbo.swift" \\
  || miss "missing critical file: LTMorphingLabel/LTCharacterLimbo.swift"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 93 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~63d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/lexrus/LTMorphingLabel"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

LTMorphingLabel is a UILabel subclass written in Swift that animates text transitions with graceful morphing effects, originally designed to recreate Apple's QuickType animation from iOS 8. It provides six distinct morphing effects (scale, evaporate, fall, pixelate, sparkle, burn) that transform text character-by-character using diffing algorithms and particle systems, making label updates visually engaging rather than instantaneous. Modular effect architecture: LTMorphingLabel.swift is the core UILabel subclass, with six extension files (LTMorphingLabel+Anvil.swift through LTMorphingLabel+Sparkle.swift) each implementing one morphing effect. Character diffing logic lives in LTCharacterDiffResult.swift and LTStringDiffResult.swift. Particle effects use LTEmitterView.swift and CAEmitterLayer (for sparkle). SwiftUI support is isolated in SwiftUI/MorphingText.swift. Support files like LTEasing.swift and LTCharacterLimbo.swift handle animation timing and character lifecycle.

👥Who it's for

iOS/tvOS developers building apps that need polished, eye-catching text animations—particularly designers and engineers working on keyboard apps, messaging interfaces, or any UI where text updates are frequent and deserve visual flair. Both UIKit and SwiftUI developers can use it via the MorphingText wrapper.

🌱Maturity & risk

Experimental but actively maintained. The repo shows 86KB of Swift code with a clean structure, includes linting rules (.swiftlint.yml), supports multiple package managers (CocoaPods, Carthage, SPM), and has example project structure (LTMorphingLabelDemo). However, marked as [EXPERIMENTAL] in the description—suggesting the API or effects may evolve, though it's stable enough for production use given widespread adoption mentioned in the README.

Low risk for stability, moderate risk for long-term maintenance. Single maintainer (lexrus) with no visible recent commit data in file list; no CI/CD pipeline evidence (.github/FUNDING.yml exists but no Actions workflows shown). Dependency surface is small (Ruby/Shell build scripts only, no external SDK dependencies visible). Breaking changes could occur given experimental status, but the effect system is modular so individual effects can evolve independently.

Active areas of work

No recent commit data visible in provided file structure. The repo appears in a stable/maintenance state with complete feature set (six effects, UIKit + SwiftUI support, multi-platform deployment). Focus seems to be on reliability rather than active feature development. CocoaPods and Carthage configs are current.

🚀Get running

Check README for instructions.

Daily commands: UIKit Demo: Open LTMorphingLabelDemo.xcodeproj and run the 'LTMorphingLabelDemo' scheme. SwiftUI: Use MorphingText from LTMorphingLabel/SwiftUI/MorphingText.swift by importing LTMorphingLabel and calling MorphingText("text", morphingEffect: .evaporate). CocoaPods: Add pod 'LTMorphingLabel' to Podfile and pod install. SPM: Add via Xcode → File → Add Packages.

🗺️Map of the codebase

  • LTMorphingLabel/LTMorphingLabel.swift — Core UILabel subclass that orchestrates all morphing animations and text updates; the primary entry point for all consumers.
  • LTMorphingLabel/LTCharacterDiffResult.swift — Computes character-level diffs between old and new text to determine which characters are added, removed, or kept; essential for animation logic.
  • LTMorphingLabel/LTStringDiffResult.swift — High-level string diffing that produces character and morphing region mappings; powers the diff-driven animation flow.
  • LTMorphingLabel/LTMorphingEffect.swift — Base protocol defining the morphing effect interface; all effect implementations (Burn, Fall, Pixelate, etc.) conform to this abstraction.
  • LTMorphingLabel/LTCharacterLimbo.swift — Manages intermediate state and animations for individual characters during morphing transitions; bridges the diff result to visual animation.
  • LTMorphingLabel/LTEmitterView.swift — Handles particle effects (fire, smoke, sparkle) rendered during morphing; critical for visual polish of burn, evaporate, and sparkle effects.
  • LTMorphingLabel/LTEasing.swift — Provides easing functions for animation timing curves; controls the feel and pacing of all morphing transitions.

🛠️How to make changes

Add a New Morphing Effect

  1. Create a new extension file LTMorphingLabel+YourEffect.swift mirroring the structure of LTMorphingLabel+Burn.swift or LTMorphingLabel+Fall.swift. (LTMorphingLabel/LTMorphingLabel+YourEffect.swift)
  2. Implement the LTMorphingEffect protocol with your custom animation logic (update, resetCharacterLimbo methods). (LTMorphingLabel/LTMorphingEffect.swift)
  3. In the main LTMorphingLabel.swift file, add a new case to the morphingEffect enum (e.g., case .yourEffect) and route it in the effect selection switch statement. (LTMorphingLabel/LTMorphingLabel.swift)
  4. If your effect needs particle emissions, extend LTEmitterView.swift with a new emitter configuration method and call it from your effect. (LTMorphingLabel/LTEmitterView.swift)
  5. Test by updating LTDemoViewController.swift to include your new effect in the demo list and verify the animation on device/simulator. (LTMorphingLabelDemo/LTDemoViewController.swift)

Customize Animation Timing & Easing

  1. Open LTEasing.swift and inspect or add new easing functions (e.g., cubic bezier, exponential). (LTMorphingLabel/LTEasing.swift)
  2. In your effect file (e.g., LTMorphingLabel+YourEffect.swift), call the desired easing function when computing character position or opacity deltas. (LTMorphingLabel/LTMorphingLabel+YourEffect.swift)
  3. Adjust the morphingDuration and morphingCharacterDelay properties in LTMorphingLabel.swift to control overall animation speed. (LTMorphingLabel/LTMorphingLabel.swift)

Integrate LTMorphingLabel into a SwiftUI App

  1. Import the MorphingText view from LTMorphingLabel.SwiftUI module. (LTMorphingLabel/SwiftUI/MorphingText.swift)
  2. Use MorphingText(text: .constant("Your Text"), effect: .burn) in your SwiftUI view hierarchy; update the text binding to trigger animations. (LTMorphingLabel/SwiftUI/MorphingText.swift)

🔧Why these technologies

  • Swift & UIKit (UILabel subclass) — Direct control over text rendering and character-level animation via Core Graphics and CADisplayLink for 60fps smooth morphing.
  • CADisplayLink for animation loop — Synchronizes per-frame callbacks to screen refresh rate; enables precise character animation timing and frame-perfect transitions.
  • CAEmitterLayer for particles — GPU-accelerated particle rendering provides visual polish for Burn, Sparkle, and Evaporate effects without CPU overhead.
  • Protocol-based effect system — Decouples individual morphing effects from core label logic; allows new effects to be added without modifying main label class.
  • SwiftUI wrapper (MorphingText) — Bridges UIKit LTMorphingLabel into modern SwiftUI apps via UIViewRepresentable.

⚖️Trade-offs already made

  • Character-level diffing + per-frame CADisplayLink animation

    • Why: Provides fine-grained control over individual character morphing and smooth 60fps animations.
    • Consequence: Higher CPU usage during animation compared to simple text property binding; not ideal for animating very large text strings simultaneously.
  • Protocol-based effects vs. enum-based effects

    • Why: Allows dynamic effect registration and decouples effect logic into separate files.
    • Consequence: Slightly more boilerplate per effect; runtime dispatch overhead is negligible for animation frames.
  • UILabel subclass vs. custom CALayer rendering

    • Why: Easier integration with existing UIKit codebases and standard label APIs (font, color, alignment).
    • Consequence: Some UILabel properties may not play well with custom character animation; text manipulation must go through setText() to trigger morphing.

🚫Non-goals (don't propose these)

  • Does not support attributed strings or per-character styling (font, color changes).
  • Does not support right-to-left or complex Unicode text transformations; primarily optimized for LTR languages.
  • Does not provide animation cancellation or mid-animation reversal; all animations run to completion.
  • Not a real-time text rendering engine; optimized for label-sized text, not large document rendering.
  • Does not handle auto-layout constraints or size transitions; size must be set statically or via frame.

🪤Traps & gotchas

No explicit environment variables or service dependencies—this is pure local computation. However: (1) Particle effects (sparkle, burn) require particle image assets (Fire.png, Smoke.png, etc.) to be bundled; missing or corrupted images will cause silent rendering failures. (2) Animation performance degrades rapidly with very long strings (100+ characters); the diffing + per-character animation creates quadratic complexity. (3) SwiftUI MorphingText wrapper may have state synchronization bugs if parent View updates morphingEffect mid-animation—always call setMorphingEnabled(false) before switching effects. (4) UIKit and SwiftUI versions do not share view state; changes to one require manual synchronization.

🏗️Architecture

💡Concepts to learn

  • Longest Common Subsequence (LCS) / Edit Distance — LTCharacterDiffResult.swift relies on edit distance calculation to determine which characters changed, stayed, or were removed—this is the foundation of the entire morphing system. Understanding this algorithm is crucial for debugging or optimizing diff performance.
  • CABasicAnimation and CAKeyframeAnimation — Core Animation drives all character animations in LTMorphingLabel; each effect chains CAAnimations to move, scale, fade, or rotate characters. Knowing CA timing, groups, and delegates is essential for modifying or creating new effects.
  • CAEmitterLayer — Used by the sparkle and burn effects to emit particle geometry (Fire.png, Smoke.png textures); understanding emitter cells, velocity, and lifetime is needed to customize particle effects.
  • Easing Functions (Cubic Bézier Curves) — LTEasing.swift implements easing functions that control animation velocity curves (linear, ease-in, ease-out, ease-in-out). These directly affect how natural or jarring the morphing feels; tweaking them changes the user experience.
  • Character Limbo / Lifecycle State Machine — LTCharacterLimbo.swift tracks characters through animation states (appear, change, disappear); understanding this state machine is critical when debugging why characters appear/vanish at unexpected times or in wrong positions.
  • UILabel Subclassing and CADisplayLink — LTMorphingLabel subclasses UILabel and likely uses CADisplayLink or CATransaction for frame-accurate animation synchronization; understanding UIView rendering pipeline and display refresh rates is needed to optimize performance.
  • Protocol-Oriented Architecture (Effects Protocol) — LTMorphingEffect.swift defines a protocol that all effects must implement; this pattern enables pluggable effect system. Understanding protocol composition is key to adding new effects without modifying core label code.
  • CBZChoiceChip/CBZChoiceChip — Similar text morphing animation library for iOS, alternative approach to graceful label transitions.
  • exyte/FloatingTextField — Shares the UILabel animation philosophy for floating placeholder text with smooth transitions.
  • darrylhuffman/DHNumberProgressView — Numeric text morphing animations; demonstrates per-character update patterns similar to LTMorphingLabel's diffing approach.
  • lexrus/VPImageCropper — Another project by the same maintainer (lexrus), sharing similar Swift UIKit extension patterns and visual polish philosophy.
  • apple/sample-code-ios — Official Apple QuickType reference implementation (the original inspiration for LTMorphingLabel); foundational for understanding the animation model this library emulates.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for LTCharacterDiffResult and LTStringDiffResult

The repo has minimal test coverage (LTMorphingLabelTests/LTMorphingLabelTests.swift exists but appears basic). The character and string diffing logic is core to all morphing effects and lacks dedicated unit tests. Testing edge cases (empty strings, unicode characters, special characters) would prevent regressions and make the diffing algorithm more maintainable.

  • [ ] Create LTMorphingLabelTests/LTCharacterDiffResultTests.swift with tests for character diffing edge cases (empty, single char, unicode, emojis)
  • [ ] Create LTMorphingLabelTests/LTStringDiffResultTests.swift with tests for string diffing (insertions, deletions, replacements, no-change scenarios)
  • [ ] Add performance benchmarks for large string diffs to ensure effects remain smooth
  • [ ] Update LTMorphingLabelTests/LTMorphingLabelTests.swift to reference new test files

Add GitHub Actions CI workflow for Swift linting and testing across iOS/tvOS targets

The repo has .swiftlint.yml configuration but no automated CI enforcement. There are multiple build targets (iOS and tvOS per tvOS-Info.plist and the dual scheme structure) that should be validated on every PR. This prevents style drift and ensures all platforms remain buildable.

  • [ ] Create .github/workflows/swift-ci.yml with matrix jobs for iOS (multiple Swift versions) and tvOS targets
  • [ ] Include SwiftLint validation step referencing existing .swiftlint.yml
  • [ ] Add build step for LTMorphingLabelDemo.xcodeproj and Package.swift (SPM)
  • [ ] Include unit test execution for LTMorphingLabelTests
  • [ ] Document CI requirements in CONTRIBUTING guide (if it doesn't exist, create it)

Add comprehensive documentation and examples for each morphing effect in LTMorphingLabel+*.swift files

The repo has 6 effect extensions (Anvil, Burn, Evaporate, Fall, Pixelate, Sparkle) but the README only shows brief mentions. Each effect file lacks inline documentation explaining parameters, recommended use cases, and performance characteristics. New contributors need clarity on how to use or extend effects.

  • [ ] Add detailed doc comments to each effect extension (LTMorphingLabel+Burn.swift, +Evaporate.swift, etc.) explaining visual behavior and tunable parameters
  • [ ] Create LTMorphingLabel/Effects_Documentation.md with code examples showing how to apply each effect with varying durations/easing
  • [ ] Add performance notes to LTMorphingEffect.swift base protocol documenting which effects are heaviest (particle-based) vs lightweight
  • [ ] Update README.md SwiftUI section to include a complete MorphingText.swift usage example (currently cut off)

🌿Good first issues

  • Add unit tests for LTStringDiffResult.swift and LTCharacterDiffResult.swift diffing algorithms—the file list shows no test directory, so character diff correctness is untested. Start by writing XCTest cases that verify diff output for common scenarios (adding/removing/swapping characters).
  • Document the LTMorphingEffect protocol more thoroughly with code examples in LTMorphingLabel/LTMorphingEffect.swift—contributors creating new effects need a clear template. Add inline doc comments showing a minimal effect implementation (e.g., scale-only effect with 5 lines of animation logic).
  • Create missing tvOS examples in LTMorphingLabelDemo—tvOS-Info.plist exists but no demo screens targeting tvOS; add a demo ViewController that showcases all effects on tvOS, exposing any UIKit/tvOS incompatibilities.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • bf629bd — Cleanup README and LICENSE (lexrus)
  • 1b504e1 — Update README.md (lexrus)
  • a7b9c80 — Update README.md (lexrus)
  • 88d7be1 — Update README.md (lexrus)
  • 4b80cf8 — Merge pull request #150 from jonathanwuki/master (lexrus)
  • 16b6b26 — Add additional explainer to SwiftUI section (jonathanwuki)
  • 5fe717c — Add UIKit examples, table of contents, and restructure README (jonathanwuki)
  • e922364 — Merge pull request #148 from Co2333/master (lexrus)
  • 84f1176 — clean warnings (Lakr Aream)
  • d631cbb — Merge pull request #146 from elilien/master (lexrus)

🔒Security observations

This is a UI library project with minimal security risk. No critical vulnerabilities detected. The codebase is a straightforward Swift UI morphing label library without backend services, database operations, network requests, or dependency management concerns visible in the provided file structure. Minor issues relate to version control hygiene (user-specific Xcode files being tracked). The project appears well-maintained with proper licensing and configuration files. No injection vulnerabilities, hardcoded secrets, or infrastructure misconfigurations are evident from the static analysis.

  • Low · Xcode Project File in Version Control — LTMorphingLabelDemo.xcodeproj/xcuserdata/. User-specific Xcode workspace state files (.xcuserdata) are tracked in version control. These files can contain sensitive information about the development environment, build settings, and local machine paths. Fix: Add .xcuserdata to .gitignore to prevent tracking user-specific Xcode files and workspace state.
  • Low · Missing FUNDING.yml Configuration — .github/FUNDING.yml. The .github/FUNDING.yml file exists but its contents were not provided. If improperly configured, it could redirect users to malicious funding pages. Fix: Review FUNDING.yml to ensure it only contains legitimate sponsorship information and no suspicious URLs.
  • Low · Incomplete .gitignore Configuration — .gitignore. While a .gitignore file exists, typical sensitive files for Swift projects may not be properly excluded (e.g., .DS_Store, Pods/, *.xcworkspace/xcuserdata). Fix: Ensure .gitignore includes standard Swift/iOS development exclusions: *.xcworkspace/xcuserdata, .DS_Store, Pods/, build/, DerivedData/, and user-specific files.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Mixed signals · lexrus/LTMorphingLabel — RepoPilot