Compose beautiful preference panels.
- Simple but powerful syntax (powered by result builders).
- Create nested pages and groups.
- Fully searchable.
- Native integration with SwiftUI and AppStorage.
- Comes with pre-made components: Toggle, Button, Slider, etc…
- Style components with native SwiftUI modifiers.
- Show and hide components dynamically.
- Add your own custom SwiftUI views.
- Works on iOS and macOS.
Installation
Setting is available via the Swift Package Manager. Requires iOS 15+ or macOS Monterey and up.
https://github.com/aheze/Setting
Usage
import Setting
import SwiftUI
struct PlaygroundView: View {
/// Setting supports `@State`, `@AppStorage`, `@Published`, and more!
@AppStorage("isOn") var isOn = true
var body: some View {
/// Start things off with `SettingStack`.
SettingStack {
/// This is the main settings page.
SettingPage(title: "Playground") {
/// Use groups to group components together.
SettingGroup(header: "Main Group") {
/// Use any of the pre-made components...
SettingToggle(title: "This value is persisted!", isOn: $isOn)
/// ...or define your own ones!
SettingCustomView {
Image("Logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 160)
.padding(20)
}
/// Nest `SettingPage` inside other `SettingPage`s!
SettingPage(title: "Advanced Settings") {
SettingText(title: "I show up on the next page!")
}
}
}
}
}
}
Examples
View more examples in the example app.
struct PlaygroundView: View {
var body: some View {
SettingStack {
SettingPage(title: "Playground") {
SettingGroup {
SettingText(title: "Hello!")
}
}
}
}
}
|
|
SettingStack {
SettingPage(title: "Playground") {
SettingGroup {
SettingText(title: "Hello!")
}
SettingGroup {
SettingPage(title: "First Page") {}
.previewIcon("star")
SettingPage(title: "Second Page") {}
.previewIcon("sparkles")
SettingPage(title: "Third Page") {}
.previewIcon("leaf.fill")
}
}
}
|
|
struct PlaygroundView: View {
@AppStorage("isOn") var isOn = true
@AppStorage("value") var value = Double(5)
var body: some View {
SettingStack {
SettingPage(title: "Playground") {
SettingGroup {
SettingToggle(title: "On", isOn: $isOn)
}
SettingGroup(header: "Slider") {
SettingSlider(
value: $value,
range: 0 ... 10
)
}
}
}
}
}
|
|
struct PlaygroundView: View {
@AppStorage("index") var index = 0
var body: some View {
SettingStack {
SettingPage(title: "Playground") {
SettingGroup {
SettingPicker(
title: "Picker",
choices: ["A", "B", "C", "D"],
selectedIndex: $index
)
SettingPicker(
title: "Picker with menu",
choices: ["A", "B", "C", "D"],
selectedIndex: $index,
choicesConfiguration: .init(
pickerDisplayMode: .menu
)
)
}
}
}
}
}
|
|
SettingStack {
SettingPage(title: "Playground") {
SettingCustomView {
Color.blue
.opacity(0.1)
.cornerRadius(12)
.overlay {
Text("Put anything here!")
.foregroundColor(.blue)
.font(.title.bold())
}
.frame(height: 150)
.padding(.horizontal, 16)
}
}
}
|
|
Notes
- If multiple components have the same title, use the
id
parameter to make sure everything gets rendered correctly.
SettingText(id: "Announcement 1", title: "Hello!")
SettingText(id: "Announcement 2", title: "Hello!")
- Setting comes with
if-else
support!
SettingToggle(title: "Turn on", isOn: $isOn)
if isOn {
SettingText("I'm turned on!")
}
- Wrap components in
SettingCustomView
to style them.
SettingCustomView {
SettingText(title: "I'm bold!")
.bold()
}
struct PlaygroundView: View {
@StateObject var settingViewModel = SettingViewModel()
var body: some View {
SettingStack(settingViewModel: settingViewModel) {
SettingPage(title: "Playground") {
SettingGroup {
SettingText(title: "Welcome to Setting!")
}
}
} customNoResultsView: {
VStack(spacing: 20) {
Image(systemName: "xmark")
.font(.largeTitle)
Text("No results for '\(settingViewModel.searchText)'")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}
|
|
Community
Author |
Contributing |
Need Help? |
Setting is made by aheze. |
All contributions are welcome. Just fork the repo, then make a pull request. |
Open an issue or join the Discord server. You can also ping me on Twitter. |