📦 Lightweight, Swift-y looking code for modern SwiftUI developers
⚙️ Dozens of view modifiers to add expected functionality
💨 Custom, built-in transitions & animations for views
💻 Cross-platform Support for iOS, macOS, watchOS
🧩 Pre-made components that look great in any app
💕 This package works great with and is inspired by SwiftUIX!
🚧 Wiki under construction. Read below to get started!
GitHub release (latest SemVer) GitHub Release Date GitHub issues GitHub pull requests
ShinySwiftUI aims to turn messy Swift + SwiftUI code into cleaner, Swift-ier code. It also aims to provide a library of useful modifiers, components, and extensions to create consistent, good-looking apps.
// 😴 Before HStack { ViewA() ViewB() } // ✨ After ViewA() + ViewB()
// 😴 Before MyView().frame(width: 30.0, height: 30.0) MyView().frame(maxWidth: 40.0, maxHeight: 40.0) // ✨ After MyView().frame(30.0) MyView().frame(max: 40.0)
// 😴 Before MyView().onAppear { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) } // ✨ After MyView().onAppear { hideKeyboard() }
// 😴 Before MyView().overlay(RoundedRectangle(cornerRadius: 5.0).stroke(.red, lineWidth: 2.0)) // ✨ After MyView().roundedBorder(.red, cornerRadius: 5.0, lineWidth: 2.0)
- App Layout
- View Functionality
- Other Features
Most of the above features are cross-platform and are supported on both iOS and macOS.
Add ShinySwiftUI to your project using Swift Package Manager:
https://github.com/Flowductive/shiny-swift-ui
Improve code consistency with CGFloat spacing values:
MyView().padding(.m).cornerRadius(.xs)
These values include: .xxs, .xs, .s, .m, .l. .xl, and .xxl.
You can use a generic stack, or GStack, to position items vertically or horizontally using a Bool input:
GStack(platform == .iOS ? .vertical : .horizontal) { MyViewA() MyViewB() }
A typical use case of GStack is for changing display layout on macOS vs. iOS devices.
Use a ShoveView to quickly push inner content to one side/corner:
// Position MyView right ShoveView(.trailing) { MyView() } // Position MyView top-left ShoveView(.topLeading) { MyView() }
Use fixed-width spacers for consistent spacing:
// Large vertical spacer Spacer.VL // Extra-small vertical spacer Spacer.HXS
Vertical spacer variants include .VXXS, .VXS, .VS, .VM, .VL, .VXL, and .VXXL.
Horizontal spacer variants include .HXXS, .HXS, .HS, .HM, .HL, .HXL, and .HXXL.
You can quickly group views using operators:
// Horizontal stack MyViewA() + MyViewB() // Vertical stack, center-aligned MyViewA() / MyViewB() // Vertical stack, left-aligned MyViewA() /- MyViewB();
Easily set the dimensions of a square frame:
// Sets MyView's frame to width = 30.0, height = 30.0 MyView().frame(30.0)
Stretch the view:
// Stretch horizontally MyViewA().stretchH() // Stretch vertically MyViewB().stretchV() // Stretch in both directions MyViewC().stretch()
Use a @State boolean to refresh a view quickly:
@State var refresh: Bool = false var body { MyView().refreshable(with: refresh) }
Updating the view would require that refresh.toggle() is called.
Set the relative opacity of a view:
MyView().opacity(.half)
You can choose from (in order of opacity) .opaque, .most, .half, .quarter, .almostInvisible, .invisible.
Add a rounded border to any view:
MyViewA().roundedBorder(.green) MyViewB().roundedBorder(.red, cornerRadius: .s, lineWidth: 2.0)
Repeat an action in a specified interval:
MyView().every(3.0) { print("Hello") // Runs every 3 seconds }
Perform an action after a specified delay:
MyView().after(3.0) { print("Hello") // Runs 3 seconds after the view appears }
Add a slick transition to a view using .slickAnimation(value:):
MyViewA().slickAnimation() MyViewB().slickAnimation(value: myVal)
Add a custom built-in animation; i.e. .slickEaseOut, .slickEaseIn, .rampEaseOut, .rampEaseIn, .bounce, .lightBounce, or .page:
MyViewA().animation(.rampEaseOut) MyViewB().animation(.slickEaseOut(duration: 1.0), value: myVal)
Add a custom built-in transition; i.e. .turn, .swipe, .pop:
MyViewA().transition(.turn)
Use the .debug() view modifier to randomly change the background color of the view for debugging:
MyView().debug()
Take a screenshot of a view and save the image to path:
myView.snapshot()
Add a tooltip upon hover to a view:
MyView() .withTooltip(present: $showTooltip) { Text("This is a tooltip!") }
Add a keyboard shortcut, which automatically adds the shortcut tooltip:
MyViewA().shortcut("c", modifiers: [.shift, .command]) MyViewB().shortcut(.defaultAction)
Track the relative position of the mouse pointer within the view:
MyView().trackingMouse { pos in // ... }
Take advantage of color utilities:
// Init color from hex code var color = Color(hex: "#ffffff") // If bindingBool.wrappedValue is true, show the color MyView().foregroundColor(.red.if($bindingBool)) // Get a lighter version of a color lighter = color.ligher(by: 0.3) // Colors also have relative opacities, just like views halfColor = color.opacity(.half)
When importing ShinySwiftUI, colors will also conform to Codable.
Easily add SwiftUI wraps of UIVisualEffectView:
VisualEffectView()