flowductive/shiny-swift-ui
***
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)
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)Completed Features
- App Layout
- Pre-defined spacing values β - Layout using Generic Stack β - Layout using Shove View β - Fixed-width spacers β
- View Functionality
- Operations on views β - View frame modifiers β - View refresh modifier β - View styling modifiers β - View timing modifiers β - Custom animation/transitions β - Debugging view modifier β - Screenshot view method β - Hover tooltip modifier (macOS) β - View mouse position checking (macOS) β
- Other Features
- Image modifiers - Color features β - Quick dividers - Visual effects β - Pre-made buttons - Action item highlight modifier
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.HXSVertical 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()Package Metadata
Repository: flowductive/shiny-swift-ui
Default branch: main
README: README.md