ShinySwiftUI

1.2.0

✨ Write cleaner, conciser, and more consistent SwiftUI code with a suite of pre-made extensions, view modifiers, and components.
Flowductive/shiny-swift-ui

What's New

1.2.0

2022-08-30T01:29:15Z

Thanks for using ShinySwiftUI! 🎉

New Features

EnumPicker

Quickly create a Picker iterating through enum values:

// Customize view for each item
EnumPicker(selected: $selectedFruit, title: "Choose a Fruit") { fruit in
  Text("\(fruit.emoji) \(fruit.name)")
}

// Display enum's rawValue as Text for each item
EnumPicker(selected: $selectedVeggie)

Animations

Added new .slickEaseInOut(duration:) animation.

Specify your slick animation style within the .slickAnimation(...) view modifier:

myView.slickAnimation(.inOut)
myView.slickAnimation(.out, delay: 1.0, duration: 0.25)

Minor Changes

  • Added a new Line shape.
  • Renamed .reverseMask(...) to .inverseMask(...) for clarification.
  • Create an at-maximum square frame using the new .frame(max:) modifier.
  • Improved the performance of the .shortcut(...) view.
  • Added the .paddedDrawingGroup(_:) modifier to create a drawing group without clipping content.
  • The .every(_:perform:) method will now disconnect the timer on view disappear.

Social Preview

SwiftUI extensions and components that just make sense.

📦 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

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
    • 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.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()

Description

  • Swift Tools 5.5.0
View More Packages from this Author

Dependencies

  • None
Last updated: Sat Nov 16 2024 00:06:30 GMT-1000 (Hawaii-Aleutian Standard Time)