Open-source project
fxm90/GradientLoadingBar avatar
fxm90/GradientLoadingBar

GradientLoadingBar: a gradient loading indicator for SwiftUI and UIKit

⌛️A customizable animated gradient loading bar.

893 stars63 forksSwiftMIT

At a glance

What is it?
GradientLoadingBar is a small Swift framework that draws an animated gradient bar at the top of the screen, in four forms: a key window controller, a notch-wrapping variant, a plain UIView and a SwiftUI View. Version 4.0.0 raises the floor to Swift 6.2, Xcode 26 and iOS 26, and older branches exist for older platforms.
Who is it for?
Adopt GradientLoadingBar if you want a distinctive top-of-screen loading indicator in an iOS app and you are on iOS 26 with Swift 6.2, since the current release is built for that toolchain. Do not adopt it if your deployment target is older, where you would have to use the 3.x or 2.x branches instead, and do not adopt it for anything other than a cosmetic indicator, because it has no cancel, no progress reporting and no accessibility behaviour described.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Swift, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem: a spinner that tells the user nothing new

The system loading indicators on iOS are fine and completely anonymous. A grey spinner, or the small bar at the top of the screen, communicates one thing: something is happening. It says nothing about progress, it draws no attention, and in a large app it is easy to miss entirely.

GradientLoadingBar is a customizable animated gradient loading bar, and it exists to be noticed. The design is a thin strip of colour that sweeps along the top of the screen, and the project says it was inspired by an iOS 7 progress bar animation posted on CodePen. It is a small piece of polish rather than a piece of infrastructure, and reading it that way is important: it is a drawing plus a fade, and it is about four types and a handful of properties.

The audience is iOS developers who want an app to feel finished, particularly on the first launch or during a slow network call where a spinner is easy to ignore. The framework supports both SwiftUI and UIKit in full, which is unusual for a component of this size and means it can be dropped into either side of an app that is mid-migration between the two.

The project is MIT licensed, the repository is not archived, and the last push was on 2026-10-08, so it is being worked on. The single published release is 4.0.0 from 2026-01-31, and there is a CHANGELOG.md in the repository root.

The bar is also a good example of a component whose value is entirely in its defaults. Almost nobody configures a loading indicator, so the default colours, the default height of three points and the default fade timings are the product.

Four types, and how they relate to each other

The framework provides four types, and the list is short enough to hold in your head, which is the sign of a well-scoped component.

GradientLoadingBar is a controller class that manages the visibility of a GradientActivityIndicatorView attached to the current key window. NotchGradientLoadingBar is a subclass of GradientLoadingBar that wraps the GradientActivityIndicatorView around the iPhone notch. GradientActivityIndicatorView is a UIView that contains the gradient with the animation, and GradientLoadingBarView is a SwiftUI View that contains the gradient with the animation.

The structure is the interesting part. The drawing and the animation live in the view types, so the gradient is identical whether you use UIKit or SwiftUI, and the controller type is a thin layer that puts that view into the key window and manages whether it is visible. The notch variant inherits that behaviour and changes only the geometry.

That means the choice is not framework first but placement first. If you want the bar at the top of the screen, you use the controller. If you want it inside a navigation bar, a button or a custom container, you take the UIView and add it yourself, setting constraints and driving visibility with fadeIn and fadeOut. If you are in SwiftUI, you use the View directly.

The NotchGradientLoadingBar deserves its own note. It wraps the indicator around the iPhone notch, and on devices without a notch it behaves identically to GradientLoadingBar, so it is safe to use without a device check.

The controller API: one shared instance, two fade calls

For most use cases the documentation recommends the shared instance, GradientLoadingBar.shared, and the usage pattern is two calls around your network request:

swift
final class UserProfileViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()

    GradientLoadingBar.shared.fadeIn()

    userProfileService.fetchUserProfile { _ in
      // ...
      // Be sure to call this from the main actor!
      GradientLoadingBar.shared.fadeOut()
    }
  }
}

The comment in the example is the part to notice. The completion handler of a network call is not on the main actor, and the documentation insists that the fadeOut call is made from the main actor, which in Swift 6 with strict concurrency means the call site has to be annotated or hopped.

The two methods are fadeIn(duration: TimeInterval = 0.33, completion: ((Bool) -> Void)? = nil) and fadeOut(duration: TimeInterval = 0.66, completion: ((Bool) -> Void)? = nil). The asymmetric defaults are a small design choice that matters in practice: appearing should be quick, disappearing can afford to be slower.

The shared instance can be overridden to customize the loading bar globally, which is how you set your brand colours once at app launch rather than at every call site.

Configuration: height, safe area, colours and animation speed

The configuration surface is small, and each option answers a question you would otherwise have to guess at.

swift
let gradientLoadingBar = GradientLoadingBar(
  height: 5.0,
  isRelativeToSafeArea: false,
)

gradientLoadingBar.gradientColors = [.systemIndigo, .systemPurple, .systemPink]
gradientLoadingBar.progressAnimationDuration = 4.5

height is a CGFloat and defaults to 3.0 points, so the default bar is deliberately thin. isRelativeToSafeArea is a Bool that defaults to true, and it decides whether the bar sits inside the safe area or runs to the top edge of the screen, under the status bar and the notch. The documentation includes a screenshot comparison of the two, because the difference is a visual judgement that a parameter name does not convey.

The properties are gradientColors, which is an array of UIColor and defines the gradient, and progressAnimationDuration, a TimeInterval that controls how fast the gradient animates from left to right. The duration of 4.5 seconds in the example is slow, which suggests the author intends a calm, continuous sweep rather than an urgent pulse.

Because the colours are plain UIColor values, dynamic colours are available, and using system colours such as systemIndigo, systemPurple and systemPink as in the example means the bar follows the system appearance for free. A team that ships a dark and a light palette gets both without writing a branch.

The raw view: putting the gradient inside your own layout

If a top-of-screen bar is not what you need, the framework also gives you the view itself. GradientActivityIndicatorView is a UIView subclass that can be added to other views, such as a UINavigationBar, a UIButton or a custom container.

The usage pattern the documentation describes is to store an instance as a property, set up constraints, and control the visibility with fadeIn and fadeOut:

swift
final class UserProfileViewController: UIViewController {

  private let gradientActivityIndicatorView = GradientActivityIndicatorView()

  // ...

  override func viewDidLoad() {
    super.viewDidLoad()

    gradientActivityIndicatorView.translatesAutoresizingMaskIntoConstraints = false
    view.add

The example is cut off in the published documentation at that point, which is a fair indicator of how much detail is available: enough to show the pattern, not enough to copy the constraint code wholesale.

This route is the one to pick when the loading state belongs to a component rather than to the app. A button that is busy while it submits a form, or a card that loads its own content, can each own an indicator view without a global controller and without a shared instance that fights over the key window.

The global controller and the local view are the two ends of a scale, and the shared instance is at the global end. That is worth deciding deliberately, because two parts of an app showing a loading bar at once will fight.

Installing with Swift Package Manager, and the version matrix

The framework is distributed through Swift Package Manager, and the documentation gives both routes. In Xcode you open the project, go to File, then Add Packages, and enter the package URL:

swift
import GradientLoadingBar

For a manually managed manifest, the dependency block is:

swift
dependencies: [
  .package(
  url: "https://github.com/fxm90/GradientLoadingBar",
  from: "4.0.0"
  )
]

and the target configuration references the product:

swift
.product(
  name: "GradientLoadingBar",
  package: "GradientLoadingBar"
)

The requirements for the current release are Swift 6.2, Xcode 26 and iOS 26.0 or later. The compatibility notes give the older options: use version 3.x.x for iOS below 26.0 and for CocoaPods or Carthage support, and 2.x.x for iOS below 13.0.

Those numbers matter more than they look. A library that requires the current Xcode and the current iOS is convenient for a new project and awkward for a maintained one with a wider deployment target, so the compatibility table is the first thing to read. The repository topics also name cocoapods, so the package has been distributed that way historically, which is what the 3.x branch is for.

Maintenance, the toolchain and what this component cannot do

The repository is a well-kept Swift package. The top-level entries are the usual set for a small framework: a Package.swift, a Package.resolved, a Sources/ directory, Tests/, an Assets/ directory, an Example/ directory with an Xcode project, a Makefile, a .swift-version file, a .swiftformat configuration and a .swiftlint.yml, plus a CHANGELOG.md.

The Makefile is more than a convenience wrapper, because it encodes the project's quality process. The format target runs SwiftFormat over the sources, the format-check target runs it in lint mode and is marked as intended for CI, and the lint target runs SwiftLint in strict mode over the whole project. Each of those targets depends on a verification target that checks the tool is installed and fails with a clear message if it is not, which is a nicer failure mode than a missing command in a CI log. There is a test target that runs the suite with xcodebuild, and the repository carries a continuous integration badge and a code coverage badge.

The limits of the component are worth stating plainly, because it is a small library and the boundaries are easy to walk into. It shows an indefinite animation. There is no progress reporting, no cancel, and no failure state, so a caller that wants to distinguish a slow request from a failed one has to handle that itself. It is attached to the key window in the controller form, which means it is process-wide rather than scoped to a screen. And the documentation does not describe any accessibility behaviour for the indicator, so if your app supports VoiceOver you should check what it announces and whether it announces anything at all.

The verdict is simple. It is a well-made, well-tested piece of visual polish with a permissive licence, a sensible API and an honest compatibility matrix, and it earns its place in an app that wants to look considered rather than a place in the architecture of one.

Editorial conclusion

Adopt GradientLoadingBar if you want a distinctive top-of-screen loading indicator in an iOS app and you are on iOS 26 with Swift 6.2, since the current release is built for that toolchain. Do not adopt it if your deployment target is older, where you would have to use the 3.x or 2.x branches instead, and do not adopt it for anything other than a cosmetic indicator, because it has no cancel, no progress reporting and no accessibility behaviour described. Before you add the package, check which version branch matches your minimum iOS version, and if you use Swift concurrency, remember that the fadeOut call has to happen on the main actor.

Frequently asked questions

How do I add GradientLoadingBar to an iOS project?

It is distributed with Swift Package Manager. In Xcode use File then Add Packages and enter the package URL, or add a .package entry for https://github.com/fxm90/GradientLoadingBar with from: "4.0.0" in your Package.swift, then reference the GradientLoadingBar product and import GradientLoadingBar.

Which versions of GradientLoadingBar work on older iOS versions?

The current 4.0.0 release requires Swift 6.2, Xcode 26 and iOS 26.0 or later. For iOS below 26.0, and for CocoaPods or Carthage support, use the 3.x.x version, and for iOS below 13.0 use the 2.x.x version.

How do I show and hide the GradientLoadingBar?

Call fadeIn and fadeOut on GradientLoadingBar.shared for most cases. The shared instance can be overridden to customise the bar globally, and the fadeOut call has to be made from the main actor, which the documentation calls out in the example.

Can I use GradientLoadingBar inside a button or a custom view?

Yes. GradientActivityIndicatorView is a UIView subclass that can be added to a UINavigationBar, a UIButton or a custom container, and you drive it with fadeIn and fadeOut, while GradientLoadingBarView is the SwiftUI equivalent.

What licence is GradientLoadingBar released under?

MIT, with the LICENSE file at the root of the repository. The latest release is 4.0.0 from 2026-01-31 and the last push was on 2026-10-08.

Official sources

  1. fxm90/GradientLoadingBar on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/fxm90-gradientloadingbar.svg)](https://hysenlabs.com/projects/fxm90-gradientloadingbar)

Was this useful?