SwiftUI extensions and components that just make sense.
What is ShinySwiftUI?
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)
// ✨ After
MyView().frame(30.0)
// 😴 Before
MyView().onAppear {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
// ✨ After
MyView().hideKeyboard()
// 😴 Before
MyView().overlay(RoundedRectangle(cornerRadius: 5.0).stroke(.red, lineWidth: 2.0))
// ✨ After
MyView().roundedBorder(.red, cornerRadius: 5.0, lineWidth: 2.0)
Completed Features
- App Layout
- View Functionality
- Other Features
Most of the above features are cross-platform and are supported on both iOS and macOS.
Get Started
Add ShinySwiftUI to your project using Swift Package Manager:
https://github.com/Flowductive/shiny-swift-ui
📐 App Layout Features
Pre-defined spacing values
Improve code consistency with CGFloat
spacing values:
MyView().padding(.m).cornerRadius(.xs)
These values include: .xxs
, .xs
, .s
, .m
, .l
. .xl
, and .xxl
.
Layout using Generic Stack
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.
Layout using Shove View
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()
}
Fixed-width spacers
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
.
⚙️ View Functionality
Operations on views
You can quickly group views using operators:
// Horizontal stack
MyViewA() + MyViewB()
// Vertical stack, center-aligned
MyViewA() / MyViewB()
// Vertical stack, left-aligned
MyViewA() /- MyViewB();
View frame modifiers
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()
View refresh modifier
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.
View styling modifiers
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)
View timing modifiers
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
}
Custom animation/transitions
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)
Debugging view modifier
Use the .debug()
view modifier to randomly change the background color of the view for debugging:
MyView().debug()
Screenshot view method
Take a screenshot of a view and save the image to path:
myView.snapshot()
Hover tooltip modifier (macOS)
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)
View mouse position checking (macOS)
Track the relative position of the mouse pointer within the view:
MyView().trackingMouse { pos in
// ...
}
🎁 Other Features
Color features
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
.
Visual effects
Easily add SwiftUI wraps of UIVisualEffectView
:
VisualEffectView()