SwiftUI技术探究之常用View&Modifiers(上)

2,165 阅读6分钟

本文主要内容

一.Text & Label
二.Button & Link
三.Image & AsyncImage
四.TimelineView & Canvas
五.TextEditor & TextField 六.ColorPicker 七.Picker 八.ProgressView 九.Slider 十.Toggle & Stepper

一.Text & Label

1.1、Text视图

Text是显示一行或多行只读文本的视图。类似UIKit中的UILabel,是一个结构体:

@frozen struct Text

字体
通过.font方法设定Text字体。SwiftUI提供了众多的系统字体可用:

extension Font {
    public static let largeTitle: Font
    public static let title: Font
    public static let title2: Font
    public static let title3: Font
    public static let headline: Font
    public static let subheadline: Font
    public static let body: Font
    public static let callout: Font
    public static let footnote: Font
    public static let caption: Font
    public static let caption2: Font
}
// 系统字体
Text("系统字体")
    .font(.headline)
// 自定义字体
Text("自定义字体")
    .font(Font.custom("PingFangSC-Regular",fixedSize: 20))

字体颜色
通过foregroundColor设定字体颜色。

Text("前景色")
    .font(.subheadline)
    .foregroundColor(.gray)

字重
通过fontWeight方法设定字重,系统提供的可用字重如下:

@frozen public struct Weight : Hashable {
        public static let ultraLight: Font.Weight
        public static let thin: Font.Weight
        public static let light: Font.Weight
        public static let regular: Font.Weight
        public static let medium: Font.Weight
        public static let semibold: Font.Weight
        public static let bold: Font.Weight
        public static let heavy: Font.Weight
        public static let black: Font.Weight
}
Text("字重")
    .font(.body)
    .fontWeight(.semibold)

粗体、斜体、下划线、删除线
可以给Text设定一些特殊的样式,如粗体、斜体、下划线、删除线等。

Text("粗体")
    .font(.body)
    .bold()
Text("italic斜体")
    .font(.body)
    .italic()
Text("下划线")
    .font(.body)
    .underline()
Text("删除线")
    .font(.body)
    .strikethrough()

字距、字距调整、行间距
Text相比Label要方便得多,UILabel调整字间距或行间距需要借助AttributedString,而Text提供了对应的API。

Text("字间距")
    .tracking(5.0)
    .background(Color.red)
Text("字距调整")
    .kerning(5.0)
    .background(Color.blue)    
Text("行间距/n行间距")
    .lineSpacing(2)
    .background(Color.green) 

最大行数
通过lineLimit设定最大行数。

Text("最大2行\n最大2行\n最大2行")
    .lineLimit(2)

其他设置 其他设置如内间距、基准线偏移等。

Text("内间距")
    .padding(10)
    .background(Color.black)
Text("基准线偏移")
    .baselineOffset(10)
    .background(Color.yellow)

示例

struct TextTestView: View {
    @State private var textContent = "点击之前"
    
    var body: some View {
        VStack(alignment: .center, spacing: 10, content: {
            
            // 1.字体
            Text("系统字体")
                .font(.headline)
            Text("自定义字体")
                .font(Font.custom("PingFangSC-Regular", fixedSize: 20))
                .foregroundColor(.yellow)
            // 2.字体颜色
            Text("前景色")
                .font(.subheadline)
                .foregroundColor(.gray)
            Text("背景色")
                .font(.body)
                .foregroundColor(.red)
                .background(Color.green)
            // 3.字重
            Text("字重")
                .font(.body)
                .fontWeight(.semibold)
            // 4.粗体
            Text("粗体")
                .font(.body)
                .bold()
            // 5.下划线
            Text("下划线")
                .font(.body)
                .underline()
                .foregroundColor(.green)
            // 6.斜体
            Text("italic(只支持英文)")
                .font(.body)
                .italic()
            // 7.删除线
            Text("删除线")
                .font(.body)
                .strikethrough()
                .foregroundColor(.red)
        })
        
        VStack(alignment: .center, spacing: 10, content: {
            
            // 1.字距、字距调整、行间距
            Text("字间距")
                .tracking(5.0)
                .background(Color.red)
            Text("字距调整")
                .kerning(5.0)
                .background(Color.blue) 
            Text("行间距/n行间距")
                .lineSpacing(2)
                .background(Color.green) 
            // 2.最大行数
            Text("最大2行\n最大2行\n最大2行")
                .lineLimit(2) 
            // 3.内间距、基准线偏移
            Text("内间距")
                .padding(10)
                .background(Color.black)
            Text("基准线偏移")
                .baselineOffset(10)
                .background(Color.yellow)  
            // 4.点击手势
            Text(self.textContent)
                .onTapGesture(count: 1, perform: {
                    self.textContent = "点击之后"
                })
        })
    }
}

截屏2022-08-26 11.58.10.png

1.2、Label视图

Label视图是用户界面项的标准标签,由带有标题的图标组成。是一个结构体:

struct Label<Title, Icon> where Title : View, Icon View
  • 用户界面中最常见和最容易识别的组件之一就是图标和标签的组合。如collections、lists、menus of action items、disclosable lists等。通过提供一个标题和图像名称(如SF Symbols集合中的图标),就可以以最简单的形式创建一个标签。
// 示例:标题和图像组合的Label视图
Label("Lightning", systemImage: "bolt.fill")

预览结果
截屏2022-08-25 16.39.11.png

  • 可以通过labelStyle()修饰符设置标签的样式,如titleOnly表示只显示文字,iconOnly表示只显示图标,同时使用时只有第一个样式起作用
示例:`labelStyle()`修饰符
Label("Lightning", systemImage: "bolt.fill")
    .labelStyle(.titleOnly)
    // .labelStyle(.iconOnly)
    // .labelStyle(.titleAndIcon)
  • 可以通过修改现有的样式来创建自定义的标签样式。
// 示例:给标签样式添加红色边框
Struc RedBorderedLabelStyle: LabelStyle {
    func makeBody(configuration: Configuration) -> some View {
        Label(configuration)
        .border(Color.red)
    }
}
  • 编程方式创建标签,自定义标签的文字和图标。
示例:设置本地图片
Label("Rain", image: Image.init("img"))
// 示例:
Label {
    Text("Hello, world!")
    .font(.body)
    .foregroundColor(.primary)
} icon: {
    Circle()
        .stroke(lineWidth: 5)
        .foregroundColor(.red)
        .frame(width: 34, height: 34, alignment: .center)
        .overlay(Text("5"))
}

预览结果
截屏2022-08-25 18.01.27.png

二.Button & Link

2.1、Button

  • Button视图是启动操作的控件。结构体:
struct Button<Label> where Label : View
  • 创建方式有如下3种:
// 方式1
Button("Sign In") {
    Text("Sign In")
}.foregroundColor(.blue).font(.title2)
// 方式2
Button {
    print("action1")
} label: {
    Text("button1")
}
// 方式3
Button(action: {
    print("action2")
}){
    Text("button2")
}

2.2、Link

  • Link是导航到URL的控件。结构体:
struct Link<Label> where Label : View
  • 创建方式有如下2种:
// 方式1
Link(destination: URL.init(string: "www.baidu.com")!) {
    Text("link")
}
// 方式2
Link("test", destination: URL.init(string: "www.baidu.com")!)

三.Image & AsyncImage

3.1、Image

  • Image是显示本地图片的视图。结构体:
@frozen struct Image
  • 创建方式如下:
// 方式1:加载图片(默认Assets中的图片)
Image("baby").resizable().scaledToFit()

// 方式2:加载直接拉进工程中的图片(不建议使用)
Image(uiImage:.init(named: "baby")!)
注意⚠️:此处图片默认加载Assets中的图片。如果其中没有,则无法加载图片,并且打印log“[SwiftUI] No image named 'baby' found in asset catalog for...”,

3.2、AsyncImage

  • AsyncImage是显示网络图片的视图。
  • 可以加载网络图片,并且可以设置一张默认的占位图。
AsyncImage(url: URL.init(string: "https://gp-dev.cdn.bcebos.com/gp-dev/upload/file/source/d642fa9e1210194ccc228947af9283c7.png")) { img in
    img.resizable()
} placeholder: {
    Image("baby").resozable().scaledToFit()
}.frame(width: 120, height: 120)

四.TimelineView & Canvas

4.1、TimelineView

  • TimelineView根据提供的时间表进行更新的视图。结构体:
struct TimelineView<Schedule, Content> where Schedule : TimelineSchedule
  • 按计划的更新View。创建方式有3
// 方式1:每分钟更新
TimelineView(.everyMinute) { context in
    
}
// 方式2:自定义更新计划-开始时间、更新间隔(秒),类似NSTimer
// 时钟
TimelineView(.periodic(from: Date.now, by: 1.0)) { context in
    Text(context.date.description).font(.largeTitle)
}
// 方式3:自定义更新计划-开始时间、更新间隔,类似NSTimer
TimelineView(.explicit(sequence)) { context in
    
}

4.2、Canvas

  • 绘制画布。结构体:
struct Canvas<Symbols> where Symbols : View
// 示例
Canvas { context, size in
    // Drawing code
    context.stroke(Path(ellipseIn: CGRect(origin: .zero, size: size)), with: .color(.orange), lineWidth: 5)
    
}.frame(width: 100, height: 50).border(.blue, width: 3) // 画布尺寸,画布边框线条宽度

预览结果
截屏2022-08-25 20.00.06.png

五.TextEditor & TextField

5.1、TextEditor(相当于UITextView)

  • 可以显示和编辑长文本的视图。结构体:
struct TextEditor
// 示例
// @State用于提示编译器需要刷新UI
@State var string: String = "Placeholder"

TextEditor(text: $string).frame(width: 200, height: 100)

5.2、TextField

  • 可以显示和编辑长文本的视图。相当于UIKit中的UITextField,单行文本输入框。比如登录用户名、密码等。结构体:
struct TextEditor
  • TextField的初始化:
@State private var inputMessage = ""

TextField("inputPlaceHolder", text: $inputMessage).textFieldStyle(.roundedBorder).font(.largeTitle).frame(width: 100, height: 50) // 一般要设置frame
    .onSubmit {
        // 点击键盘的return
        print("点击了return")
    }

六.ColorPicker

  • 颜色选择器。用于从系统颜色选择器UI中选择颜色的控件。
@State private var bgColor = Color.red

ColorPicker("ColorPicker", selection: $customColor, supportsOpacity: false).font(.largeTitle).foregroundColor(bgColor)

截屏2022-08-26 14.47.47.png

七.Picker

  • 选择器。用于从一组互斥值中进行选择的控件。
enum Person: String {
    case zhangsan
    case lisi 
    case wangwu
    case zhouwu
    case zhengwang
}
@State var  selectionPerson = Person.wangwu

Picker(selection: $selectionPerson, label: Text("Picker")) {
    Text("zhangsan").tag(Person.zhangsan)
    Text("lisi").tag(Person.lisi)
    Text("wangwu").tag(Person.wangwu)
    Text("zhouwu").tag(Person.zhouwu)
    Text("zhengwang").tag(Person.zhengwang)
}
// 同步显示选中的Person
Text("selectioned Person:\(selectionedPerson.rawValue)").foregroundColor(.blue)
    .textSelection(.enabled) // 文本长按可拷贝

截屏2022-08-26 15.15.41.png

八.ProgressView

  • 进度条,显示任务完成进度的视图。
@State var progress = 0.5

ProgressView(value: progress, total: 10, label: {
    Text("SwiftUI")
}, currentValueLabel: {
    Text("当前值:\(progress)")
})
    .background(.blue) // 整个进度条背景
    .tint(.red) // 进度颜色
    
Button("增加") {
    progress += 0.5
}
Button("较少") {
    progress -= 0.5
}

截屏2022-08-26 15.53.16.png

  • 当value未赋值且不指定样式时,默认显示加载中的"菊花"样式。同时也可以通过设置progressViewStyle()来改变其样式,linear为进度条,circular为正在加载。
ProgressView()

截屏2022-08-26 15.59.56.png

九.Slider

  • 滑动进度条。一种从有界线性范围的值中选择值的控件。
@State var isSliding = false
@State var speed = 0

// 1.
// in: 滑动范围,step:步频(有in才能设置),onEditingChanged:是否在滑动
Slider(value: $speed, in: 0...100, onEditingChanged: { editing in
    print(editing)
    isSliding = editing
}).tint(.red)


Text("\(speed)").foregroundColor(isSliding ? .red : .gray).bold()

// 2.
Slider(value: $speed, in: 0...100) {

} minimumValueLabel: {
    Text("0")
} maximumValueLabel: {
    Text("\(speed)")
}

截屏2022-08-26 16.20.24.png

注意⚠️:任何容器中有且只能拥有不超过`10`个View

十.Toggle & Stepper

10.1 开关Toggle

  • 开关。在打开和关闭状态之间切换的控件。
@State var toggleStatus = false

// tint:开启时颜色。为了不影响colorMultiply,设置opacity(0)
// colorMultiply:为颜色叠加,三原色RGB
Toggle(isOn: $toggleStatus) {
    Test("Toggle")
}.tint(.blue.opacity(0)).colorMultiply(.black)

10.2 Stepper

  • 步进器。一种执行递增和递减操作的控件,即可以设置步长的步进控件。
@State var value = 0
let colors: [Color] = [.orange, .red, gray, .blue, .green, .purple, .pink]

func stepperAction(isIncrement: Bool) {
    value = value + isIncrement ? 1 : -1
    if value >= colors.count { value = 0 }
    if value < 0 { value = colors.count -  1 }
}

Stepper(value: $value, in: 0...9, step: 1) {
    Text("\(value)").font(.largeTitle)
}

Stepper {
    Text("value:\(value) Color:\(colors[value].description)").foregroundColor(colors[value]).font(Font.system(.title2).bold())   
    } onIncrement: {
        stepperAction(isIncrement: true)
    } onDecrement: {
        stepperAction(isIncrement: false)
    }