作为一名刚接触SwiftUI的开发者,在运用 Button 时还是遇到了不少的困惑。网上绝大部分教程都仅仅浅浅地说了下用法,授人以鱼不如授人以渔,let’s go~

1.Xcode运用(如现已熟练,请越过)

在Xcode中,咱们能够按住 command + shift + L 组合键,来调用出一个窗口,在这个窗口下,咱们能够很方便的查找咱们所需的一些 Controls,比方Button,Text等,如图所示:

SwiftUI之Button精讲
在右侧位置,能够看到Button的一些相关说明,划到底部,咱们能够拜访它的官方文档地址

SwiftUI之Button精讲

在Xcode中,你能够经过拖拽的方式来快速生成一个Button组件,亦或许直接在代码中输入Button字样,也能够得到相关的编译提示,如图所示:

SwiftUI之Button精讲

接着咱们按住 command + 鼠标左键,按回车去到Button的界说文件中

SwiftUI之Button精讲

2. Button的参数

在界说文件中,将Button相关的先折叠起来,如图所示

SwiftUI之Button精讲

其间 Label 为 Button中的泛型,它遵从了 View 协议,并决议了每个Button实例将运用什么类型的视图进行渲染。 依据不同的Label,咱们能够运用不同的初始化参数。

初始化参数(-):action与label

SwiftUI之Button精讲
依据类型的界说能够知道,action参数为一个逃逸闭包,当用户单击或点击按钮时它会履行相关操作。而label参数为一个跟随闭包,咱们能够在其间写一些自界说的视图(custom view)。所以关于这种初始化,咱们能够写出以下代码:

// 正常写法
Button(action: {}, label: {
 Text("按钮")
})
// 因为参数label是个跟随闭包,则能够省掉关键字,用大括号直接翻开
Button(action:{}){
   Text("按钮")
}
// 因为label参数的回来类型是泛型Label,而泛型Label又遵从了View协议,所以咱们能在其间写相关自界说的视图
Button(action:{}){
    VStack{
        Text("按钮")
        Text("描述文字")
    }
    .foregroundColor(.red)
}

初始化参数(二): titleKey/title,action

SwiftUI之Button精讲

依据类型的界说能够知道,在第一个参数中,咱们能够传 LocalizedStringKey 或许是一般的 String 字符串,action 参数则同上。

什么是 LocalizedStringKey

关于LocalizedStringKey官方文档是这么解说的:The key used to look up an entry in a strings file or strings dictionary file.

意思便是,当你的字符串用 LocalizedStringKey 创立或许运用其类型声明时,SwiftUI会依据你当时的言语环境,自动翻译成对应的字符串。比方:

let hello:LocalizedStringKey = "Hello"
let today = LocalizedStringKey("Today")
var body: some View {
    VStack{
        Text(hello) //在中文环境下,将会变成字符串 "你好"
        Text(today) //在中文环境下,将会变成字符串 "今日"
    }
    .font(.largeTitle)
}

接下来咱们看 preview 的作用

SwiftUI之Button精讲

额…这不是没啥改变吗?在这儿,咱们需求做一些装备。咱们先点击根项目,然后添加对应的中文简体言语,如图所示:

SwiftUI之Button精讲

接着新建一个Strings File文件,运用其默许命名即可。

SwiftUI之Button精讲
接着点击右侧检查器位置的 Localize 按钮,在弹出的弹窗中,点击Localize

SwiftUI之Button精讲

同样在检查器 Localzation 中,挑选中文简体,如图所示:

SwiftUI之Button精讲

接着咱们在 Localizable 文件夹中的两个文件内,写入对应的翻译。

SwiftUI之Button精讲

Tips:笔者查了一圈下来,大部分的做法是在Preview中加上 .environment(\.locale, .init(identifier: "zh-Hans")),这样能够预览到言语的改变。但在笔者的Xcode中,这不管用,可能是我Xcode的版别(14.2)过高了。这儿咱们挑选 command + R 运行模拟器来看作用。注意,你需求在模拟器的体系中,切换手机的体系言语,如图所示:

SwiftUI之Button精讲

然后翻开咱们构建后的App应用,就能够看到作用了。

SwiftUI之Button精讲

咱们在本来代码的基础上,再添加一些:

let str = "Hello"
var body: some View {
    VStack{
        Text(hello) //在中文环境下,将会变成字符串 "你好"
        Text(today) //在中文环境下,将会变成字符串 "今日"
        Text("Hello") // 新增
        Text(str) // 新增
    }
    .font(.largeTitle)
}

猜猜看,它的成果是什么?

SwiftUI之Button精讲

在 SwiftUI 中,Button、Text等视图,在传入文本参数时会优先进行言语本地化。假如你不想将文本翻译成本地化言语,能够像上方相同,将Text中的文本值抽出来,提前用let声明即可。

———————————分割线————————————

了解完 LocalizedStringKey 后,是不是第二种 Button 的传值方式便心领神会啦~咱们写出以下的代码:

let str = "Hello"
var body: some View {
    VStack{
        Button("按钮", action: {})
        Button("按钮"){}
    }
    .font(.largeTitle)
}

在后续的界说文件中,也便是ios15.0,咱们看到了 SwiftUI 关于这两种初始化,加多了一个可选参数 role,如图所示

SwiftUI之Button精讲

role 传参中,有两种 ButtonRole 供你挑选。

  1. destructive:用于删去用户数据或履行不可逆操作的按钮。
  2. cancel: 用于撤销操作按钮。

说时迟那时快,相信有朋友现已快速写出以下代码试试水了

VStack{
 Button("按钮",role: .cancel){}
 Button("按钮",role: .destructive){}
}
.font(.largeTitle)

SwiftUI之Button精讲

SwiftUI之Button精讲
啊这…这不就按钮改个颜色吗,这也能独自抽个role参数? 其实这两种 role 首要结合着 .swipeActions.alert 进行运用,如下所示:

struct ButtonView: View {
    @State private var isPresented = false
    var body: some View {
        VStack{
            List{
                Text("测试滑动")
                    .swipeActions {
                        Button("一般按钮", action: {})
                        Button("撤销按钮", role: .cancel, action: {})
                        Button("删去按钮", role: .destructive, action: {})
                    }
            }
            Button("Show Alert", action: {
                isPresented = true
            })
            .alert("Alert", isPresented: $isPresented) {
                Button("撤销按钮", role: .cancel, action: {})
                Button("一般按钮", action: {})
                Button("删去按钮", role: .destructive, action: {})
            }
        }
    }
}

感兴趣的朋友能够看看详细的款式表现~因为今日咱们首要讲Button,就不对其他的进行拓宽了(等会讲不完了)。

初始化参数(三):configuration

SwiftUI之Button精讲
依据类型的界说能够得知,咱们能够传入一个 PrimitiveButtonStyleConfiguration 类型的参数。依据上方的注释,咱们相关例子界说一个struct,这个struct需求遵从 PrimitiveButtonStyle 协议,并且咱们需求在 .buttonStyle 修饰符中进行运用,如图所示:

SwiftUI之Button精讲

ok话不多说,咱们先照猫画虎,把这个struct按照例子先实现一下

struct ButtonView: View {
    struct RedBorderedButtonStyle: PrimitiveButtonStyle {
        func makeBody(configuration: Configuration) -> some View {
                // 这儿便是Button接收configuration的情况啦
                Button(configuration)
                 .border(Color.red)
            }
     }
    var body: some View {
        VStack{
            Button("按钮"){}
                .buttonStyle(RedBorderedButtonStyle())
        }
        .font(.largeTitle)
    }
}

经过预览后的UI咱们能够看到,它为当时的按钮加了个赤色边框。oh~不会绕了一圈,仅仅加了个赤色边框罢了吧? 别着急,咱们还没运用 configuration 这个 struct 里边的内容呢。这儿的Configuration实践上便是 PrimitiveButtonStyleConfiguration,咱们来看看它这儿面有啥。

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public struct PrimitiveButtonStyleConfiguration {
    public struct Label : View {.
        public typealias Body = Never
    }
    @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
    public let role: ButtonRole?
    /// A view that describes the effect of calling the button's action.
    public let label: PrimitiveButtonStyleConfiguration.Label
    /// Performs the button's action.
    public func trigger()
}

role咱们在上面现已讲过了,这儿不再多讲。label 表明的是咱们当时触发这个按钮后,需求展现出来的视图方式。 trigger 办法表明你能够经过调用 configuration.trigger() 的方式来自动触发按钮。咱们先写出以下的代码:

struct ButtonView: View {
    struct RedBorderedButtonStyle: PrimitiveButtonStyle {
        func makeBody(configuration: Configuration) -> some View {
            // 自动触发按钮
            configuration.trigger()
            return configuration.label.border(.red)
//            configuration.label.onTapGesture {
//                configuration.trigger()
//            }
        }
     }
    var body: some View {
        VStack{
            Button("按钮"){
                print("按钮被触发了~")
            }
            .buttonStyle(RedBorderedButtonStyle())
        }
        .font(.largeTitle)
    }
}

预览UI如图所示:

SwiftUI之Button精讲
尽管咱们从视图的展现方式来看是相同的作用,但不同的是,咱们自动触发了按钮。也便是说它在展示出来的时分,按钮就现已被触发了,咱们能够看控制台的输出。

SwiftUI之Button精讲

Tips: Xcode到高版别后,输出只能在 Simulator 中检查,所以command + R 以检查相关输出。

3. Button的款式与交互

3-1: ButtonStyle

在阅览Button文档的过程中,想必你注意到了这样一段话:

You can also create custom styles. To add a custom appearance with standard interaction behavior, create a style that conforms to theButtonStyleprotocol. To customize both appearance and interaction behavior, create a style that conforms to thePrimitiveButtonStyleprotocol. Custom styles can also read the button’s role and use it to adjust the button’s appearance.

官方言语讲得便是很官方,咱们还是来看看 ButtonStyle 的相关界说吧。

SwiftUI之Button精讲
咱们能够发现,这个 ButtonStyle 协议如同和PrimitiveButtonStyle 协议差不多。 接着看一下ButtonStyleConfiguration ,能够发现它里边提供的是 isPressed常量,用来判别当时用户是否按下按钮。而之前的PrimitiveButtonStyleConfiguration 提供的是 trigger 办法,能够允许咱们自动去触发按钮。

SwiftUI之Button精讲

接下来咱们经过一个需求来更好的领会 ButtonStyle的作用。比方我想在用户按下按钮时,让按钮添加一些改变。如下所示:

struct ButtonView: 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 {
        VStack{
            Button("按钮"){
                print("按钮被触发了~")
            }
            .buttonStyle(CustomButtonStyle())
        }
        .font(.largeTitle)
    }
}

咱们来看看实践的作用:

SwiftUI之Button精讲

Tips: 能够发现,咱们是在按下按钮履行对应的作用后,再触发 Button 的 action。

3-2:ButtonStyle 和 PrimitiveButtonStyle的差异

在做完以上的工作后,相信你对这两种 Style 的了解现已很透彻了。那么咱们能够总结一下这两种Style的运用场景。

  1. 想对按钮的款式做一些修改并且自动触发按钮,咱们能够运用 PrimitiveButtonStyle
  2. 想在按钮被按下时做一些作用交互,咱们能够运用ButtonStyle

3-3:运用Modifier添加自界说款式

除了以上的办法,咱们还能够创立一个自界说的 Modifier 来修改相关按钮的款式。咱们按下 command + N 创立一个 Styles 文件,并写上以下代码:

import SwiftUI
struct YellowButtonStyle:ViewModifier{
    func body(content: Content) -> some View {
        content
            .padding()
            .background(.yellow)
            .cornerRadius(10)
            .foregroundColor(.white)
    }
}

接着咱们能够在代码中这样去运用:

Button("黄色按钮"){}.modifier(YellowButtonStyle())

这样看起来方便了一些,但这个 modifier 看着很不顺眼,能够去掉吗?当然了。咱们能够在Styles文件中,对 YellowButtonStyle进行拓宽,如下:

import SwiftUI
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())
    }
}

然后咱们这样去运用即可:

Button("黄色按钮"){}.yellowButtonStyle()

4.Button的拓宽

上面咱们提到过,在 Buttton 中提供了一个 Label 泛型。咱们能够利用该泛型进行对应的拓宽。比方咱们想创立一个图画按钮,能够这样做:

struct ButtonView: 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)
        }
    }
}

作用如图所示:

SwiftUI之Button精讲

你可能会很猎奇,这个 “camera.shutter.button” 是怎样来的?Apple其实给我提供了一些内置的符号,咱们能够在官网的设计资源中下载对应的软件

SwiftUI之Button精讲

以上便是全部内容了,感谢你坚持读完,欢迎在谈论区进行交流~