Radix-UI 是一个开源库,针对快速开发、易于维护和可访问性进行了优化。
由于 Radix-UI 的开发者表示它不适用于移动开发(例如 SwiftUI),而且永远不会适用(参考),所以我决定自己动手,让它可用,因为我非常喜欢它。我这样做的灵感来自 Basics 网站以及 Radix-UI 简约而华丽的设计。
https://github.com/amirsaam/Radix-UI-Swift.git
Dependency Rule
(依赖规则)更改为 Up to Next Minor Version
(高达下一个次版本)Add Package
(添加软件包)按钮由于 RadixUI for Swift 不会具有任何破坏性的 Semantic 次要版本,所以它的版本控制为 Epoch.Major.Minor * 100 + Patch
。如果可能的话,它应该是 Epoch * 100 + Major
,因此,为了获得与 Semantic Versioning 相同的正确行为,选择 Up to Next Minor Version
才是正确的方法。
在你想要使用它的任何地方导入软件包,如下所示:
import RadixUI
你可以使用两种方式使用 Radix Colors(Radix 颜色)
Text("RadixUI-Swift")
.foregroundColor(.crimson9) // Directly
或
@State private var color: RadixAutoColor = .crimson
Text("RadixUI-Swift")
.foregroundColor(color.solid9) // From RadixAutoColor Enum
为了使用你自己的自定义调色板,请前往 Radix Pallete Generator 并创建你的调色板,然后将它们添加到你的 Asset Catalogue(资源目录)中,命名为 MyColor1, MyColor2, MyColor3, MyColor4, MyColor5, ... MyColor12,然后使用 RadixAutoColor 的 .custom
属性。
@State private var color: RadixAutoColor = .custom("myColor")
// Reads all 12 levels of your custom pallete using the color's child variables.
// It will automatically capitalize the first letter for you.
Text("RadixUI-Swift")
.foregroundColor(color.solid9)
重要提示:所有 RadixUI 修饰符和样式都使用 RadixAutoColor
枚举。
在 Image
中使用图标,bundle(资源包)名称完全可自定义,有两种方式:
// Default SwiftUI's Image
Image("github-logo", bundle: .radixUI)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: anySize, height: anySize)
.foregroundColor(.ruby9)
// Built-in Custom Extension
ResizableBundledImage(imageName: "vercel-logo", imageSize: 20, bundle: .radixUI)
.foregroundColor(.ruby9)
对于在 Label
中使用,请使用内置的 extension(扩展),它使用 ResizableBundledImage
。
Label("Vercel", imageName: "vercel-logo", imageSize: 20, bundle: .radixUI)
你可以将任何 Text(文本)转换为 RadixUI 徽章。
Text("New")
.font(.footnote)
.radixBadge(variant: .outline, radius: .none, color: .grass)
// variant: outline, soft, solid, surface
// radius: none, large, full
RadixUI 具有多种 ButtonStyles(按钮样式),如:radixGhost, radixOutline, radixSoft, radixSolid 和 radixSurface。注意 1:RadixUI 按钮在按下后可以具有加载状态,该状态会将按钮标签的图标更改为进度视图,直到 boolean 值再次变为 false
。示例:
@State private var isLoading = false
// Surely you can go for < Button {} label: {} > approach too!
Button(action: functionName) {
// Best for using Label with RadixUI icons
Label(LocalizedStringKey, imageName: String, imageSize: CGFloat, bundle: Bundle)
// or create one
Label {
Text(LocalizedStringKey)
} icon: {
Image(String)
Image(systemName: String)
Image(String, bundle: Bundle?)
}
// or to use Asset Catalog Image and SFSymbols
Label(LocalizedStringKey, image: String)
Label(LocalizedStringKey, systemImage: String)
}
.buttonStyle(
// for having the similarity to RadixUI buttons like scale and opacity and etc;
// does not change anything of the button but adds some effects.
.radixCustom()
// or
.radixGhost(
layout: .leading,
radius: .large,
color: .grass
),
isLoading: $isLoading
// or
.radixOutline(
layout: .leading,
radius: .large,
color: .grass
),
isLoading: $isLoading
// or
.radixSoft(
layout: .leading,
radius: .large,
color: .grass
),
isLoading: $isLoading
// or
.radixSolid(
layout: .leading,
radius: .large,
color: .grass
),
isLoading: $isLoading
// or
.radixSurface(
layout: .leading,
radius: .large,
color: .grass
),
isLoading: .constant(false) // for disabling the loading state change
)
// layout: icon, title, leading, trailing
// radius: none, large, full
注意 2:始终传递一个 Label() 作为按钮的 label(标签),不要只传递 Text 或 Image,如果你只想要其中一个,请使用 layout
变量的 .icon
或 .title
情况。
你可以将任何 Text(文本)转换为 RadixUI 标注。
let infoText = "You will need admin privileges to install and access this application."
let alertText = "Access denied. Please contact the network administrator to view this page."
Text(infoText)
.radixInfoCallout(variant: .surface, color: .grass) // info callout, customizable color, `info-circled` icon
Text(alertText)
.radixAlertCallout(variant: .surface) // alert callout, red color, `exclamation-triangle` icon
// variant: outline, soft, surface
在底层,它只是一个循环的 Toggle(切换),但它只允许同时打开一个切换,需要传递一个选项数组,这些选项的模型符合某些协议。
// the radio option data options should conform to these protocols
struct Option: Identifiable, Equatable {
let id = UUID()
let title: LocalizedStringKey
}
@State private var selectedOption: Option? = nil
private let options = [
Option(title: "Option 1"),
Option(title: "Option 2"),
Option(title: "Option 3")
]
RadixRadioGroup(
options: options, // pass the array of data options
selected: $selectedOption, // bound of selected option
style: (variant: .surface, layout: .trailing, color: .grass)
) {
// what the options should show as label
Text($0.title)
.foregroundStyle(.grass12)
}
// variant: .soft, surface
// layout: .leading, .trailing
为了自定义 SwiftUI 的 Segmented Picker(分段选择器)以匹配 Radix 样式,请在应用程序的 @main
struct 中创建一个 init
来全局应用它,或者在任何你想要的视图中应用这样一个 init
。
struct PickerDemoView: View {
init() {
RadixSegmentedPicker(
color: .grass,
selectedFont: .systemFont(ofSize: 15, weight: .semibold),
notSelectedFont: .systemFont(ofSize: 15, weight: .light)
)
}
@State private var selected: RadixToggleType = .switch
var body: some View {
Picker(String(""), selection: $selected) {
Text("Checkbox").tag(RadixToggleType.checkbox)
Text("Radio").tag(RadixToggleType.radio)
Text("Switch").tag(RadixToggleType.switch)
Text("Toggle").tag(RadixToggleType.toggle)
}
.pickerStyle(.segmented)
.padding()
.frame(width: 400)
}
}
重要提示:它目前在 macOS 中不起作用!
由于 SwiftUI 的 Slider(滑块)不接受像按钮那样的样式,所以 RadixUI for Swift 拥有自己的 RadixSlider View。
@State private var value = 5.0
RadixSlider(value: $value, step: 1, in: 0...10)
.rxSliderStyle(
.radixSoft(
radius: .full,
size: .medium,
color: .grass
)
// or
.radixSurface(
radius: .full,
size: .medium,
color: .grass
)
)
// radius: .none, .large, .full
// size: small, medium, large
RadixUI for Swift TextFieldStyle(文本框样式)就像 ButtonStyle 一样,具有加载状态,该状态会将 iconLabel
转换为 ProgressView,还可以通过传递一个函数来执行一个操作,该函数可以带或不带一个按钮,该按钮将在尾随侧显示 iconButton
,并遵循文本框样式的样式。
@State private var input = ""
@State private var isLoading = false
TextField("Placeholder1", text: $input)
.textFieldStyle(
.radixSurface(
radius: .large,
color: .grass, // optional, default .blue
iconLabel: Image("quote", bundle: .radixUI), // optional
iconButton: Image("arrow-right", bundle: .radixUI), // optional
action: functionName // optional action, does provide .onSubmit by default to textfield
),
isLoading: $isLoading
// or
.radixSoft(
radius: .large,
color: .grass, // optional, default .blue
iconLabel: Image("quote", bundle: .radixUI), // optional image
iconButton: Image("arrow-right", bundle: .radixUI), // optional image
action: functionName // optional action, does provide .onSubmit by default to textfield
),
isLoading: $isLoading
)
// radius: .none, .large, .full
此软件包具有一个具有 RadixUI 风格的 toast(提示框)或应用内通知功能,遵循 Apple Design Guidelines(苹果设计指南)。它由一个 Binding<Bool>
变量触发,可以有一个作为 CTA(行动号召)的操作按钮,或者只是一个 dismiss(关闭)按钮,可以根据 toast 的显示位置向上或向下滑动。
@State private var presentInfoToast = false
@State private var presentActionToast = false
var body: some View {
VStack { // << this is the highest container in the view, toast should applied to it
HStack {
}
}
.radixInfoToast( // this is a info toast, action is dismiss
$presentInfoToast,
variant: .surface,
position: .bottom,
color: .grass,
duration: 3 // 0 == never, any other number auto dismisses the toast after the given number in seconds
) {
Label(
"This is a Radix Info Toast",
imageName: "vercel-logo",
imageSize: 20,
bundle: .module
) // << pass a label to this part just like how explained in button part of this readme
}
.radixActionToast( // this is a action toast, you should define an action for it
$presentActionToast,
variant: .soft,
position: .top,
color: .grass,
duration: 0
) {
presentInfoToast = true // define the action you wnat here or just pass a function
} buttonLabel: {
Label(
"Ignored Text",
imageName: "avatar",
imageSize: 20,
bundle: .radixUI
) // pass a label as before for the button of the action, only shows the icon
} toastLabel: {
Label(
"Button Presents Info Toast",
imageName: "vercel-logo",
imageSize: 20,
bundle: .radixUI
) // pass a label as the toast's main content
}
}
// variant: .soft, .surface
// position: .bottom, .top
RadixUI 有 4 种类型的 toggle(切换)样式,其中一种是 radixRadio
,这部分被跳过了,因为它主要用于 RadixRadioGroup
中,并且将其作为 togglestyle 用于单个项目不是一个好方法。
Toggle(isOn: $toggleBinding) {
ResizableBundledImage( // showed after toggle (checkbox( has been turned on (checked)
imageName: "check",
imageSize: 20,
bundle: .radixUI
)
}
.toggleStyle(
.radixCheckbox(
variant: .surface,
color: .grass
)
)
Toggle(isOn: $toggleBinding) {
Text("Label Text")
.foregroundStyle(radixColor.grass.text2)
}
.toggleStyle(
.radixSwitch( // a switch style, just like swiftui's default with styling of radix ui
variant: .surface,
radius: .full,
color: .grass
)
)
Toggle(isOn: $toggleBinding) {
ResizableBundledImage(
imageName: "font-italic",
imageSize: 20,
bundle: .radixUI
)
}
.toggleStyle(
.radixToggle( // toggle as toggle, dimmer color when off, strong color when on
color: .grass
)
)
// variant: .soft, .surface
// radius: .none, .large, .full
Radix 阴影以 ViewModifier
的形式提供,共有 6 个级别。
AnyView
.radixShadow1()
.radixShadow2()
.radixShadow3()
.radixShadow4()
.radixShadow5()
.radixShadow6()
主要 (Main)
这些方面需要帮助,欢迎任何 PR。
classic
变体的组件添加 classic
变体Amir Mohammadi – @amirsaam – amirsaam [at] me [dot] com
在 MIT 许可证下分发。有关更多信息,请参见 LICENSE
。