一、常用的Button相关初始化的方法
public struct Button<Label> : View where Label : View {
@preconcurrency public init(action: @escaping @MainActor () -> Void, @ViewBuilder label: () -> Label)
}
extension Button where Label == Text {
@preconcurrency nonisolated public init(_ titleKey: LocalizedStringKey, action: @escaping @MainActor () -> Void)
@preconcurrency nonisolated public init<S>(_ title: S, action: @escaping @MainActor () -> Void) where S : StringProtocol
}
其中 Label 为 Button中的泛型,它遵循了 View 协议,并决定了每个Button实例将使用什么类型的视图进行渲染。 根据不同的Label,我们可以使用不同的初始化参数
1、初始化参数 action 与 label
action参数为一个逃逸闭包,当用户单击或点击按钮时它会执行相关操作。而label参数为一个尾随闭包,我们可以在其中写一些自定义的视图(custom view)。
// 1
Button {
print("按钮被点击")
} label: {
Text("我是一个按钮")
}
// 2
Button(action: {}) {
Text("尾随闭包的方式")
}
// 3
Button {
} label: {
VStack {
Text("我是按钮")
Text("按钮说明")
}.foregroundColor(.red)
}
2、初始化参数 action 与 titleKey/title
LocalizedStringKey 国际化使用,当你的字符串用 LocalizedStringKey 创建或者使用其类型声明时,SwiftUI会根据你当前的语言环境,自动翻译成对应的字符串.
/// KText //会替换 国际化文件中的值
Button(LocalizedStringKey("KText")) {
}
/// 直接显示字符串
Button("直接显示") {
}
3、可选参数 role
@preconcurrency nonisolated public init(role: ButtonRole?, action: @escaping @MainActor () -> Void, @ViewBuilder label: () -> Label)
@preconcurrency nonisolated public init<S>(_ title: S, role: ButtonRole?, action: @escaping @MainActor () -> Void) where S : StringProtocol
}
role的值
public struct ButtonRole : Equatable, Sendable {
/// List {
/// ForEach(items) { item in
/// Text(item.title)
/// .swipeActions {
/// Button(role: .destructive) { delete() } label: {
/// Label("Delete", systemImage: "trash")
/// }
/// }
/// }
/// }
public static let destructive: ButtonRole
public static let cancel: ButtonRole
///
/// ```
/// struct NewContactSheet: View {
/// var body: some View {
/// NavigationStack {
/// NewContactEditor()
/// .toolbar {
/// Button(role: .confirm) {
/// saveChanges()
/// }
/// }
/// }
/// }
/// }
/// ```
@available(iOS 26.0, macOS 26.0, tvOS 26.0, watchOS 26.0, *)
public static let confirm: ButtonRole
/// ```
/// struct NewContactSheet: View {
/// @Environment(\.dismiss) private var dismiss
///
/// var body: some View {
/// NavigationStack {
/// NewContactEditor()
/// .toolbar {
/// Button(role: .close) {
/// dismiss()
/// }
/// }
/// }
/// }
/// }
/// ```
@available(iOS 26.0, macOS 26.0, tvOS 26.0, watchOS 26.0, *)
public static let close: ButtonRole
}
role 主要结合着 .swipeActions NavigationStack 和 .alert 进行使用
4、 初始化参数:configuration
extension Button where Label == PrimitiveButtonStyleConfiguration.Label {
/// Creates a button based on a configuration for a style with a custom
/// appearance and custom interaction behavior.
///
/// Use this initializer within the
/// ``PrimitiveButtonStyle/makeBody(configuration:)`` method of a
/// ``PrimitiveButtonStyle`` to create an instance of the button that you
/// want to style. This is useful for custom button styles that modify the
/// current button style, rather than implementing a brand new style.
///
/// For example, the following style adds a red border around the button,
/// but otherwise preserves the button's current style:
///
/// struct RedBorderedButtonStyle: PrimitiveButtonStyle {
/// func makeBody(configuration: Configuration) -> some View {
/// Button(configuration)
/// .border(Color.red)
/// }
/// }
///
/// - Parameter configuration: A configuration for a style with a custom
/// appearance and custom interaction behavior.
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
nonisolated public init(_ configuration: PrimitiveButtonStyleConfiguration)
}
根据类型的定义可以得知,我们可以传入一个 PrimitiveButtonStyleConfiguration 类型的参数。根据上方的注释,我们相关例子定义一个struct,这个struct需要遵循 PrimitiveButtonStyle 协议,并且我们需要在 .buttonStyle 修饰符中进行使用
struct JLButtonView: View {
struct RedBorderButtonStyle: PrimitiveButtonStyle{
func makeBody(configuration: Configuration) -> some View {
Button(configuration)
.border(Color.red)
}
}
var body: some View {
VStack {
Button("按钮"){
print("按钮被点击了")
}.buttonStyle(RedBorderButtonStyle())
}
}
}
struct JLButtonView: View {
struct RedBorderButtonStyle: PrimitiveButtonStyle{
func makeBody(configuration: Configuration) -> some View {
// 主动触发按钮点击
configuration.trigger()
return configuration.label.border(Color.red)
}
}
var body: some View {
VStack {
Button("按钮"){
print("按钮被点击了")
}.buttonStyle(RedBorderButtonStyle())
}
}
}
二、样式
ButtonStyle 协议好像和PrimitiveButtonStyle 协议差不多。 接着看一下ButtonStyleConfiguration ,可以发现它里面提供的是 isPressed常量,用来判断当前用户是否按下按钮。而之前的PrimitiveButtonStyleConfiguration 提供的是 trigger 方法,可以允许我们主动去触发按钮。
struct JLButtonView2: View {
struct CustomButtonStyle: ButtonStyle{
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding()
.background(.blue)
.cornerRadius(10)
.foregroundColor(.white)
.scaleEffect(configuration.isPressed ? 2: 1)
.animation(.easeOut(duration: 0.2), value: configuration.isPressed)
}
}
var body: some View {
Button("按钮") {
print("被点击了")
}
.buttonStyle(CustomButtonStyle())
}
}
按下按钮执行对应的效果后,再触发 Button 的 action。
1:ButtonStyle 和 PrimitiveButtonStyle的区别
- 想对按钮的样式做一些修改并且主动触发按钮,我们可以使用
PrimitiveButtonStyle。 - 想在按钮被按下时做一些效果交互,我们可以使用
ButtonStyle。
2:使用Modifier添加自定义样式
struct YellowButtonStyle: ViewModifier{
func body(content: Content) -> some View {
content
.padding()
.background(.yellow)
.cornerRadius(10)
.foregroundColor(.white)
}
}
struct JLButtonView3: View {
var body: some View {
Button("按钮", action: {
})
.modifier(YellowButtonStyle())
}
}
进一步封装
struct YellowButtonStyle: ViewModifier{
func body(content: Content) -> some View {
content
.padding()
.background(.yellow)
.cornerRadius(10)
.foregroundColor(.white)
}
}
extension View{
func yellowButtonStyle() -> some View{
modifier(YellowButtonStyle())
}
}
struct JLButtonView3: View {
var body: some View {
Button("按钮", action: {
})
.yellowButtonStyle()
}
}
3:Button的扩展
在 Buttton 中提供了一个 Label 泛型。我们可以利用该泛型进行对应的拓展。比如我们想创建一个图像按钮
struct JLButtonView4: View {
var body: some View {
VStack{
Button(iconName: "camera.shutter.button"){}
}
.font(.largeTitle)
.foregroundColor(.gray)
}
}
extension Button where Label == Image{
init(iconName: String, action: @escaping () -> Void) {
self.init(action: action) {
Image(systemName: iconName)
}
}
}