Contents

nathantannar4/ignition

`Ignition` aims to help make your SwiftUI views feel more interactive. It does this by providing API that makes it easier to run animations.

See Also

Preview

https://github.com/nathantannar4/Ignition/assets/15272998/0d7b97a0-bf3a-4c07-9a00-b237408f49a4

Requirements

  • Deployment target: iOS 13.0, macOS 10.15, tvOS 13.0, or watchOS 6.0
  • Xcode 16.4+

Installation

Xcode Projects

Select File -> Swift Packages -> Add Package Dependency and enter https://github.com/nathantannar4/Ignition.

Swift Package Manager Projects

You can add Ignition as a package dependency in your Package.swift file:

let package = Package(
    //...
    dependencies: [
        .package(url: "https://github.com/nathantannar4/Ignition"),
    ],
    targets: [
        .target(
            name: "YourPackageTarget",
            dependencies: [
                .product(name: "Ignition", package: "Ignition"),
            ],
            //...
        ),
        //...
    ],
    //...
)

Documentation

Detailed documentation is available here.

Effects

Ignition provides 5 built in effects, but you can also make your own by conforming to the ViewEffect protocol.

/// The style for ``ViewEffectModifier``
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public protocol ViewEffect: DynamicProperty {

    associatedtype Body: View
    @MainActor @ViewBuilder func makeBody(configuration: Configuration) -> Body

    typealias Configuration = ViewEffectConfiguration
}

/// The configuration parameters for ``ViewEffect``
@frozen
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public struct ViewEffectConfiguration {

    /// A type-erased content of a ``ViewEffectModifier``
    public struct Content: ViewAlias { }
    public var content: Content

    /// An opaque identifier to the transaction of a triggered ``ViewEffect``
    public struct ID: Hashable { }
    public var id: ID

    public var isActive: Bool
    public var progress: Double
}

Concatenating Effects

Multiple effects can be concatenating together, for example: .scale.concat(.offset)

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension ViewEffect {

    /// A ``ViewEffect`` that concatenates an additional ``ViewEffect``
    public func concat<ConcatenatingEffect: ViewEffect>(
        _ effect: ConcatenatingEffect
    ) -> ConcatenatedEffect<Self, ConcatenatingEffect> where Self: ViewEffect
}

Triggering Effects

Effects can be triggered via the OnChangeViewEffectModifier or the ScheduledViewEffectModifier.

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension View {

    /// Adds a ``ViewEffect`` animation to a view that runs when the value changes
    @_disfavoredOverload
    @inlinable
    public func changeEffect<
        Effect: ViewEffect,
        Value: Equatable
    >(
        _ effect: Effect,
        value: Value,
        animation: Animation,
        isEnabled: Bool = true
    ) -> some View

    /// Adds a ``ViewEffect`` animation to a view that runs on an interval
    @_disfavoredOverload
    @inlinable
    public func scheduledEffect<
        Effect: ViewEffect
    >(
        _ effect: Effect,
        interval: TimeInterval,
        animation: Animation,
        isEnabled: Bool = true
    ) -> some View {
        scheduledEffect(
            effect,
            interval: interval,
            animation: .continuous(animation),
            isEnabled: isEnabled
        )
    }
}

BackgroundEffect

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension ViewEffect {

    /// A ``ViewEffect`` that emits a view in the background
    public static func background<Content: View>(
        alignment: Alignment = .center,
        @ViewBuilder _ content: () -> Content
    ) -> BackgroundEffect<Content> where Self == BackgroundEffect<Content>
}

OverlayEffect

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension ViewEffect {

    /// A ``ViewEffect`` that emits a view in the foreground
    public static func overlay<Content: View>(
        alignment: Alignment = .center,
        @ViewBuilder _ content: () -> Content
    ) -> OverlayEffect<Content> where Self == OverlayEffect<Content>
}

OffsetEffect

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension ViewEffect where Self == OffsetEffect {

    /// A ``ViewEffect`` that moves the view between an offset
    public static var offset: OffsetEffect

    /// A ``ViewEffect`` that moves the view between an offset
    public static func offset(x: CGFloat) -> OffsetEffect

    /// A ``ViewEffect`` that moves the view between an offset
    public static func offset(y: CGFloat) -> OffsetEffect

    /// A ``ViewEffect`` that moves the view between an offset
    public static func offset(offset: CGPoint) -> OffsetEffect
}

RotationEffect

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension ViewEffect where Self == RotationEffect {

    /// A ``ViewEffect`` that rotates the view by an angle
    public static var rotate: RotationEffect

    /// A ``ViewEffect`` that rotates the view by an angle
    public static func rotate(angle: Angle, anchor: UnitPoint = .center) -> RotationEffect
}

ScaleEffect

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension ViewEffect where Self == ScaleEffect {

    /// A ``ViewEffect`` that scales a view between a size
    public static var scale: ScaleEffect

    /// A ``ViewEffect`` that scales a view between a size
    public static func scale(width: CGFloat, anchor: UnitPoint = .center) -> ScaleEffect

    /// A ``ViewEffect`` that scales a view between a size
    public static func scale(height: CGFloat, anchor: UnitPoint = .center) -> ScaleEffect

    /// A ``ViewEffect`` that scales a view between a size
    public static func scale(scale: CGFloat, anchor: UnitPoint = .center) -> ScaleEffect

    /// A ``ViewEffect`` that scales a view between a size
    public static func scale(scale: CGSize, anchor: UnitPoint = .center) -> ScaleEffect
}

ButtonStyle

Want to run an effect when a user presses a button? Try using the .changeEffect button style.

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension PrimitiveButtonStyle {

    /// A ``PrimitiveButtonStyle`` that runs a ``ViewEffect`` when pressed
    public static func changeEffect<Effect: ViewEffect>(
        effect: Effect
    ) -> ChangeEffectButtonStyle<Effect> where Self == ChangeEffectButtonStyle<Effect>
}

Package Metadata

Repository: nathantannar4/ignition

Default branch: main

README: README.md