BDUIKnit is a collection of SwiftUI custom reusable UI components and extensions packed in a Swift Package. BDUIKnit is completely written in Swift with no dependencies. The package is fully documented with some exceptions of internal objects.
- To collect my personal custom reusable UI components and extensions and put them in one place.
- To create custom reusable UI components and share them.
- To explore, learn new techniques, and share what I learnt building these components & extensions.
To add BDUIKnit to your project:
- Open your project in Xcode
- Go to
File > Swift Packages > Add Package Dependency...
- Search for
BDUIKnit
and follow Xcode's installation dialogs.
BDUIKnit follows MVVM design pattern; therefore, most Views will have their corresponding View Models. View models are either class
or struct
, so use the appropriate @ObservedObject
, @State
, or @Binding
as needed.
New to MVVM? Fear not. Try to read the below codes, if you can make sense of what they are doing, you are ready to use BDUIKnit.
// create a view model that controls the tray view
let trayViewModel = BDButtonTrayViewModel()
trayViewModel.mainItem = createTrayMainItem()
trayViewModel.items = createTrayItems()
trayViewModel.expanded = true
trayViewModel.shouldDisableMainItemWhenExpanded = true
trayViewModel.trayColor = Color(.systemBackground)
trayViewModel.itemActiveColor = Color.accentColor
// pass the view model to the tray view to render
BDButtonTrayView(viewModel: trayViewModel)
// while the tray view is displaying, update the view model
trayViewModel.expanded = false
// the tray view is now collapsed
A tray-like view that is normally pinned to the bottom-trailing of a scene.
Tray item now supports more animations.
Regular Vertical Size Class
Compact Vertical Size Class
Tray Item Animations
Quick Start:
// Add tray view to a view when content ignores safe area
struct SomeView: View {
@State private var trayViewModel = BDButtonTrayViewModel()
var body: some View {
ZStack {
SomeContent()
.edgesIgnoringSafeArea(.all)
BDButtonTrayView(viewModel: trayViewModel)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottomTrailing)
.padding(16)
.onAppear(perform: setupTrayViewModel)
}
}
func setupTrayViewModel() {
// setup tray view model...
}
}
For sample code, see ButtonTrayViewPreview
A text field view intended to be used as a modal presentation sheet when need to get inputs from user.
Quick Start:
For sample code, see ModalTextFieldPreview
A text view intended to be used as a modal presentation sheet when need to get inputs from user.
Quick Start:
For sample code, see ModalTextViewPreview
A property wrapper that stores value in a given store. For example, UserDefaults
.
Quick Start:
// Store username in UserDefaults
@BDPersist(in: .userDefaults, key: "username", default: "")
var username: String
// Add post notification when username changed
static let nUsernameDidChange = Notification.Name("nUsernameDidChange")
@BDPersist(in: .userDefaults, key: "username", default: "", post: nUsernameDidChange)
var username: String
// Use optional value and NSUbiquitousKeyValueStore
// see docs for how to enable ubiquitous store
@BDPersist(in: .ubiquitousStore, key: "highScore", default: nil)
var highScore: Int?
// Use type-safe key
// create an enum
// conform to BDPersistKey
// implement the required prefix property
enum Keys: BDPersistKey {
var prefix: String { "some.prefix." }
case autoplay
case autosave
}
// the key is 'some.prefix.autoplay'
@BDPersist(in: .userDefaults, key: Keys.autoplay, default: true)
var autoplay: Bool
// the key is 'some.prefix.autosave'
@BDPersist(in: .userDefaults, key: Keys.autosave, default: false)
var autosave: Bool
// Use custom store
// conform to BDPersistStorable
// implement required methods
class CustomStore: BDPersistStorable {
// implementations...
}
@BDPersist(in: .custom(CustomStore()), key: "username", default: "")
var username: String
For sample code, see PersistPropertyWrapperPreview
An object used to present sheet. It provide an easy way to store previous dismissed sheet if needed.
Quick Start
// Example code with Enum conforms to BDPresentationSheetItem
struct UserProfileView: View {
// conform to BDPresentationSheetItem or Identifiable
enum Sheet: BDPresentationSheetItem {
case modalTextField
case modalTextView
}
@State private var sheet = BDPresentationItem<Sheet>()
var body: some View {
Form {
Button("Edit Username") {
self.sheet.present(.modalTextField)
}
Button("Edut UserBio") {
self.sheet.current = .modalTextView
}
}
.onAppear(perform: setupOnAppear)
.sheet(item: $sheet.current, onDismiss: sheetDismissed, content: presentationSheet)
}
func setupOnAppear() {
// if need to access sheet.previous on dismissed
sheet.shouldStorePrevious = true
}
func presentationSheet(for sheet: Sheet) -> some View {
switch sheet {
case .modalTextField: return AnyView(...)
case .modalTextView: return AnyView(...)
}
}
func sheetDismissed() {
if sheet.previous == .modalTextField {
// do something
}
}
}
For sample code, see PresentationItemPreview
// Create color from hex
Color(hex: "BDA12A") // a Color
UIColor(hex: "#bda12a") // a UIColor
UIColor(hex: "purple") // fatal error: create color with invalid hex: 'purple'