Skip to content

codewithswiftly/FluidKit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FluidKit

Declarative, chainable UIView animations for iOS — clean and simple.

Swift iOS SPM License


What is FluidKit?

FluidKit makes UIView animations easier to read and write. Instead of nesting callbacks inside callbacks, you chain animation steps in a straight line.

// Before — nested and hard to read
UIView.animate(withDuration: 0.3, animations: {
    button.alpha = 0
}, completion: { _ in
    UIView.animate(withDuration: 0.4, animations: {
        button.transform = .identity
    })
})

// After — FluidKit
FluidAnimation()
    .animate(duration: 0.3) { $0.setAlpha(0) }
    .animate(duration: 0.4, curve: .spring) { $0.resetTransform() }
    .run(on: button)

Installation

In Xcode go to File → Add Package Dependencies and paste:

https://github.com/codewithswiftly/FluidKit.git

Or add it to Package.swift:

.package(url: "https://github.com/codewithswiftly/FluidKit.git", from: "1.0.4")

How to Use

1. Chain animations with FluidAnimation

FluidAnimation()
    .animate(duration: 0.3, curve: .easeOut) { view in
        view.setAlpha(0)
        view.setTranslation(y: -20)
    }
    .wait(0.1)
    .animate(duration: 0.5, curve: .spring) { view in
        view.resetTransform()
        view.setAlpha(1)
    }
    .then {
        print("All done!")
    }
    .run(on: myView)

2. One-line animations on any UIView

// Fade in
myLabel.fadeIn()

// Fade out and hide when done
myLabel.fadeOut(hideAfter: true)

// Shake left and right (great for form errors)
emailField.shake()

// Scale up and back down (great for like buttons)
heartButton.pulse(scale: 1.3)

// Slide in from an edge with a spring bounce
cardView.bounceIn(from: .bottom)

3. Preset animations for common patterns

// Stagger a list of views in one by one
FluidPreset.staggerIn(views: [titleLabel, descriptionLabel, startButton])

// Stagger them out
FluidPreset.staggerOut(views: cells) {
    self.dismiss(animated: false)
}

// Shimmer effect on a placeholder while loading
FluidPreset.startShimmer(view: skeletonCard)
// ...after data loads:
FluidPreset.stopShimmer(view: skeletonCard)

// Pulsing loader
FluidPreset.startPulse(view: loadingView)
FluidPreset.stopPulse(view: loadingView)

// Cross-dissolve between two views
FluidPreset.crossDissolve(from: loadingView, to: contentView)

View Modifiers

Use these inside .animate() blocks to change view properties:

Modifier What it does
setScale(_ scale) Scale up or down. 1.0 = normal, 2.0 = double
setTranslation(x:y:) Slide the view by x and y points
setRotation(degrees:) Rotate by degrees (e.g. 45 for 45°)
resetTransform() Reset to original size, position, rotation
setAlpha(_ value) Transparency: 0 = invisible, 1 = visible
setCornerRadius(_ r) Rounded corners
setBackground(_ color) Background color

Animation Curves

Curve Best used for
.easeIn Dismissing/exiting views
.easeOut Entering/appearing views
.easeInOut General purpose (default)
.linear Looping or continuous animations
.spring Interactive elements like buttons and cards

Real-World Example — Login Screen

@MainActor
class LoginViewController: UIViewController {

    @IBOutlet weak var emailField: UITextField!
    @IBOutlet weak var loginButton: UIButton!
    @IBOutlet weak var spinner: UIActivityIndicatorView!

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // Stagger in the form elements
        FluidPreset.staggerIn(views: [emailField, loginButton])
    }

    @IBAction func loginTapped() {
        // Give the button a pressed feel, then fade it out
        FluidAnimation()
            .animate(duration: 0.1, curve: .easeIn) { [weak self] _ in
                self?.loginButton.setScale(0.95)
            }
            .animate(duration: 0.2, curve: .easeOut) { [weak self] _ in
                self?.loginButton.setAlpha(0)
            }
            .then { [weak self] in
                self?.spinner.startAnimating()
            }
            .run(on: loginButton)
    }

    func showError() {
        emailField.shake()
    }
}

Project Structure

FluidKit/
├── Sources/FluidKit/
│   ├── AnimationCurve.swift   # Timing options: easeIn, spring, etc.
│   ├── FluidAnimation.swift   # Chainable animation builder
│   ├── UIView+Fluid.swift     # setScale(), fadeIn(), shake(), etc.
│   ├── FluidPreset.swift      # staggerIn, shimmer, pulse, crossDissolve
│   └── Examples.swift         # Code examples (safe to delete)
└── Tests/FluidKitTests/
    └── FluidKitTests.swift

Requirements

  • iOS 15+
  • Swift 5.9+
  • Xcode 15+
  • Zero dependencies

License

MIT © 2026 Rahul Das Gupta

Show Your Support

If you found this project useful, consider giving it a ⭐️ on GitHub!

About

Declarative, chainable UIView animations for iOS — built with Swift result builders, async/await, and spring physics. A fluent animation DSL for iOS that eliminates nested UIKit closures. Chain and await animations with zero dependencies. Clean UIView animation chains with async/await, spring presets, keyframes, and shimmer all in pure Swift.

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages