📦 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!
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()