├── demo.gif ├── .gitignore ├── Package.swift ├── LICENSE ├── README.md └── Sources └── IntelligenceGlow └── IntelligenceGlow.swift /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Livsy90/IntelligenceGlow/HEAD/demo.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | xcuserdata/ 5 | DerivedData/ 6 | .swiftpm/configuration/registries.json 7 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 8 | .netrc 9 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "IntelligenceGlow", 8 | platforms: [ 9 | .iOS(.v15) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries a package produces, making them visible to other packages. 13 | .library( 14 | name: "IntelligenceGlow", 15 | targets: ["IntelligenceGlow"] 16 | ), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package, defining a module or a test suite. 20 | // Targets can depend on other targets in this package and products from dependencies. 21 | .target( 22 | name: "IntelligenceGlow" 23 | ), 24 | 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Artem 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IntelligenceGlow 2 | 3 | 4 | 5 | A lightweight SwiftUI library that brings an **Apple Intelligence–style glowing stroke effect** to any `InsettableShape`. 6 | It uses animated angular gradients, layered blurs, and smooth transitions to recreate the dynamic glow seen in Apple’s design language. 7 | 8 | ## Features 9 | 10 | - ✨ Apply a glowing gradient as **background** or **overlay** 11 | - 🎨 Fully customizable line widths, blur radii, and animation durations 12 | - 🌈 Gradient stops automatically regenerate for a flowing effect 13 | - ♿ Respects **Reduce Motion** accessibility settings 14 | - 🧩 Works with any built-in `InsettableShape` (`Circle`, `Capsule`, `RoundedRectangle`, etc.) 15 | 16 | ## Installation 17 | 18 | ### Swift Package Manager 19 | 20 | In Xcode: 21 | `File > Add Packages > https://github.com/Livsy90/IntelligenceGlow.git` 22 | 23 | ## Usage 24 | 25 | ### Basic Example 26 | 27 | ```swift 28 | import SwiftUI 29 | import IntelligenceGlow 30 | 31 | struct ContentView: View { 32 | var body: some View { 33 | VStack(spacing: 30) { 34 | Text("Glowing Capsule") 35 | .padding(22) 36 | .intelligenceBackground(in: Capsule()) 37 | 38 | Text("Glowing Rectangle") 39 | .padding(22) 40 | .intelligenceOverlay(in: RoundedRectangle(cornerRadius: 22)) 41 | } 42 | } 43 | } 44 | ``` 45 | 46 | ### Customizing the Glow 47 | 48 | You can control line widths, blurs, animation speed, and gradient generation: 49 | 50 | ```swift 51 | Text("Custom Glow") 52 | .padding(24) 53 | .intelligenceOverlay( 54 | in: Capsule(), 55 | lineWidths: [4, 8, 12], 56 | blurs: [0, 6, 12], 57 | updateInterval: 0.5, 58 | animationDurations: [0.6, 0.8, 1.2], 59 | gradientGenerator: { .intelligenceStyle } 60 | ) 61 | ``` 62 | 63 | ## API 64 | 65 | ### View Extensions 66 | 67 | ```swift 68 | func intelligenceBackground(in shape: S, ...) 69 | func intelligenceOverlay(in shape: S, ...) 70 | ``` 71 | 72 | ### Shape Extension 73 | 74 | ```swift 75 | func intelligenceStroke( 76 | lineWidths: [CGFloat] = [6, 9, 11, 15], 77 | blurs: [CGFloat] = [0, 4, 12, 15], 78 | updateInterval: TimeInterval = 0.4, 79 | animationDurations: [TimeInterval] = [0.5, 0.6, 0.8, 1.0], 80 | gradientGenerator: @escaping () -> [Gradient.Stop] = { .intelligenceStyle } 81 | ) -> some View 82 | ``` 83 | 84 | ## Preview 85 | 86 | ```swift 87 | #Preview { 88 | VStack(spacing: 30) { 89 | Text("Some text here") 90 | .padding(22) 91 | .intelligenceBackground(in: .capsule) 92 | 93 | Text("Some text here") 94 | .padding(22) 95 | .intelligenceOverlay(in: .rect(cornerRadius: 22)) 96 | } 97 | } 98 | ``` 99 | -------------------------------------------------------------------------------- /Sources/IntelligenceGlow/IntelligenceGlow.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | public extension View { 4 | /// Applies a glowing angular-gradient stroke as a background using the provided shape. 5 | @MainActor 6 | func intelligenceBackground( 7 | in shape: S, 8 | lineWidths: [CGFloat] = [6, 9, 11, 15], 9 | blurs: [CGFloat] = [0, 4, 12, 15], 10 | updateInterval: TimeInterval = 0.4, 11 | animationDurations: [TimeInterval] = [0.5, 0.6, 0.8, 1.0], 12 | gradientGenerator: @MainActor @Sendable @escaping () -> [Gradient.Stop] = { .intelligenceStyle } 13 | ) -> some View { 14 | background( 15 | shape.intelligenceStroke( 16 | lineWidths: lineWidths, 17 | blurs: blurs, 18 | updateInterval: updateInterval, 19 | animationDurations: animationDurations, 20 | gradientGenerator: gradientGenerator 21 | ) 22 | ) 23 | } 24 | 25 | /// Applies a glowing angular-gradient stroke as an overlay using the provided shape. 26 | @MainActor 27 | func intelligenceOverlay( 28 | in shape: S, 29 | lineWidths: [CGFloat] = [6, 9, 11, 15], 30 | blurs: [CGFloat] = [0, 4, 12, 15], 31 | updateInterval: TimeInterval = 0.4, 32 | animationDurations: [TimeInterval] = [0.5, 0.6, 0.8, 1.0], 33 | gradientGenerator: @MainActor @Sendable @escaping () -> [Gradient.Stop] = { .intelligenceStyle } 34 | ) -> some View { 35 | overlay( 36 | shape.intelligenceStroke( 37 | lineWidths: lineWidths, 38 | blurs: blurs, 39 | updateInterval: updateInterval, 40 | animationDurations: animationDurations, 41 | gradientGenerator: gradientGenerator 42 | ) 43 | ) 44 | } 45 | } 46 | 47 | public extension InsettableShape { 48 | /// Applies an Apple Intelligence–style animated angular-gradient glow stroke to any Shape. 49 | /// - Parameters: 50 | /// - lineWidths: Line widths for each glow layer. 51 | /// - blurs: Blur radius for each corresponding glow layer. 52 | /// - updateInterval: How often to regenerate gradient stops. 53 | /// - animationDurations: Animation duration per layer when gradient changes. 54 | /// - gradientGenerator: Function that returns a new set of `Gradient.Stop` values. 55 | /// - Returns: A view that renders the shape with a glowing gradient stroke. 56 | @MainActor 57 | func intelligenceStroke( 58 | lineWidths: [CGFloat] = [6, 9, 11, 15], 59 | blurs: [CGFloat] = [0, 4, 12, 15], 60 | updateInterval: TimeInterval = 0.4, 61 | animationDurations: [TimeInterval] = [0.5, 0.6, 0.8, 1.0], 62 | gradientGenerator: @MainActor @Sendable @escaping () -> [Gradient.Stop] = { .intelligenceStyle } 63 | ) -> some View { 64 | IntelligenceStrokeView( 65 | shape: self, 66 | lineWidths: lineWidths, 67 | blurs: blurs, 68 | updateInterval: updateInterval, 69 | animationDurations: animationDurations, 70 | gradientGenerator: gradientGenerator 71 | ) 72 | .allowsHitTesting(false) 73 | } 74 | } 75 | 76 | public extension Array where Element == Gradient.Stop { 77 | static var intelligenceStyle: [Gradient.Stop] { 78 | [ 79 | Color(red: 188/255, green: 130/255, blue: 243/255), 80 | Color(red: 245/255, green: 185/255, blue: 234/255), 81 | Color(red: 141/255, green: 159/255, blue: 255/255), 82 | Color(red: 255/255, green: 103/255, blue: 120/255), 83 | Color(red: 255/255, green: 186/255, blue: 113/255), 84 | Color(red: 198/255, green: 134/255, blue: 255/255) 85 | ] 86 | .map { 87 | Gradient.Stop(color: $0, location: Double.random(in: 0...1)) 88 | } 89 | .sorted { 90 | $0.location < $1.location 91 | } 92 | } 93 | } 94 | 95 | // MARK: - Generic glow stroke for any Shape 96 | 97 | private struct IntelligenceStrokeView: View { 98 | let shape: S 99 | let lineWidths: [CGFloat] 100 | let blurs: [CGFloat] 101 | let updateInterval: TimeInterval 102 | let animationDurations: [TimeInterval] 103 | let gradientGenerator: @MainActor @Sendable () -> [Gradient.Stop] 104 | 105 | @Environment(\.accessibilityReduceMotion) private var reduceMotion 106 | @State private var stops: [Gradient.Stop] = .intelligenceStyle 107 | 108 | var body: some View { 109 | let layerCount = min(lineWidths.count, blurs.count, animationDurations.count) 110 | let gradient = AngularGradient( 111 | gradient: Gradient(stops: stops), 112 | center: .center 113 | ) 114 | 115 | ZStack { 116 | ForEach(0..