本文主要讲述常见的View和Modifiers的认识和使用
主要内容:
- 常用View
- 常用Modifiers
1. 介绍
SwiftUI通过View视图搭建界面,使用Modifiers修饰器来修饰视图。系统提供了大量的视图和修饰器,并且还可以让我们自定义修饰器。
既可以手动写,也可以直接拖出到代码区或者预览区。这三种方式的结果都是一样的。
示意图:
2、Text
显示一行或多行的只读文本视图,可以类似于OC中的label
//1、Text
Text("我是一个Text,**Markdown语法加粗了**").foregroundColor(.red)
- 可以使用modifiers进行修饰视图。
- 也可以使用Markdown语法进行修饰
3、Label
Label用户界面项的标准标签,包括图片和标题
//2、Label
VStack {
//用户界面项的标准标签,由带有标题的图标组成。
//labelStyle设置label样式
Label("Lightning", systemImage: "bolt.fill")
Label("Lightning", systemImage: "bolt.fill")
.labelStyle(.iconOnly)
Label("Lightning", systemImage: "bolt.fill")
.labelStyle(.titleOnly)
//自定义label样式
Label("Lightning", systemImage: "bolt.fill")
// .labelStyle(.makeBody(RedBorderedLabelStyle))
//多label统一样式
VStack {
Label("Rain", systemImage: "cloud.rain")
Label("Snow", systemImage: "snow")
Label("Sun", systemImage: "sun.max")
}
.labelStyle(.iconOnly)
//组合标签
Label {
Text("wenyi")
.font(.body)
.foregroundColor(.primary)
Text("ya")
.font(.subheadline)
.foregroundColor(.secondary)
} icon: {
Circle()
.fill(.orange)
.frame(width: 44, height: 44, alignment: .center)
.overlay(Text("圆")).foregroundColor(.white)
}
}
说明:
- 正常情况包含了图标和标题
- 也可以设置labelStyle,设置为只显示图标或标题
- labelStyle也可以直接设置到外部视图上,可以作用到内部所有的label上
- 我们还可以给一个label自由组合文本和图标。
- 上文中自定义一个Label,包含两个文本和一个图标
- 还可以自定义labelStyle
4、Button
//Button,文字和响应
Button {
print("button点击响应")
} label: {
Text("我是按钮")
}
说明:
- 按钮可以添加action进行响应,进行打印。
- 还有设置label,label上设置图标和Text。
- 这里仅设置了Text。
5、Link
通过提供目标URL和标题来创建链接
//Link
Link(destination: URL(string:"https://www.baidu.com/")!) {
Text("Link")
}
Link("View Our Terms of Service",
destination: URL(string: "https://www.example.com/TOS.html")!)
//设置OpenURLAction
Link("Visit Our Site", destination: URL(string: "https://www.example.com")!)
.environment(\.openURL, OpenURLAction { url in
print("Open \(url)")
return .handled
})
说明:
- destination参数用来设置URL
- 还需要设置标题用来描述URL
- 通过设置OpenURLAction覆盖默认的URL打开的方式
6、Image
//直接拿Assets中图片
Image("wy")
.resizable()
.aspectRatio(contentMode: .fit)
// UIImage.init(named: "wy.png")
//加载网络图片
// AsyncImage.
说明:
- 直接使用Image就可以拿到图片了,这里只能使用Assets中的图片
- 如果想要使用文件路径下的图片,需要使用UIImage
- 网络图片使用AsyncImage,注意要设置占位图片或占位文字,网络图片有可能会失败
7、TimelineView
根据时间表更新的视图
//定义
struct TimelineView<Schedule, Content> where Schedule : TimelineSchedule
//TimelineView
TimelineView(.periodic(from: Date.now, by: 1.0)) {
context in
Text(context.date.description).font(.title)
}
说明:
- 两个参数,一个是计划表,一个是执行的内容
- 在这里时间表是从当前时间,每1秒开始执行一次
- 执行的内容就是打印当前时间
- 因此知道个视图可以做定时器使用
- 可以传入三种参数
- .everyMinute 每分钟
- .periodic(from: , by: )从开始时间开始,多久开始更新
- .explicit(更新次数)
8、Canvas
绘制图形
//Canvas
Canvas { context, size in
context.stroke(Path(ellipseIn: CGRect(origin: .zero, size: size)),with: .color(.blue), lineWidth: 3)
}.frame(width: 100, height: 50, alignment: .center).border(.red,width: 2)
说明:
- Canvas就是一个画布。我们可以对画布进行绘制丰富和动态的2D图形
- Canvas将GraphicsContext传递给用于执行即时模式绘图操作的闭包
- 通过闭包进行绘制,传入两个参数,一个是绘制的内容,一个是绘制的大小
- Canvas还传递一个CGSize值,您可以使用它来定制您绘制的内容
使用画布在SwiftUI视图中绘制丰富和动态的2D图形。画布将GraphicsContext传递给用于执行即时模式绘图操作的闭包。画布还传递一个CGSize值,您可以使用它来定制您绘制的内容。例如,你可以使用上下文的stroke(_:with:lineWidth:)命令来绘制一个Path实例:
9、TextEditor
显示可编辑文本界面的控件。相当于UITextView
//TextEditor
TextEditor(text:
.constant("Placeholder"))
.frame(width: 100, height: 30, alignment: .center)
说明:
- constant可以设置默认值
- frame可以设置TextEditor的宽高,以及对齐方式。
10、TextField
文本输入框
//TextField,预览无法操作
TextField("首字母默认大写", text: $str).frame(width: 100, height: 56, alignment: .center)
.textInputAutocapitalization(.never)
//自动纠错
.disableAutocorrection(true)
// .border(.red, width: 1)
// .cornerRadius(20)
.overlay{
RoundedRectangle(cornerRadius: 20)
.stroke(.red, lineWidth: 10)
.padding(-10)
}
.onSubmit {
print("我点击了!")
}
...
.contentShape(Rectangle())//追加热区设置
.onTapGesture {
print("tap")
//热区
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
说明:
- 可以添加.onSubmit来捕获提交事件
- 点击空白区域收起键盘
- 设置自动大小写的场景,比如首字母大写,全部大写,全不大写等等
- 自动纠错默认为false就是会自动纠错
- 直接设置.border设置圆角,只会设置外部的,不会设置内部的,因此需要手动绘制一个图像进行覆盖
- 注意“热区”,只要有内容的区域都属于热区(即使这里是空白的),热区在这里不属于空白区域
- 因此需要追加热区设置.contentShape(Rectangle())
11、ColorPicker
颜色选择器(UIKit没有)
@State var textColor = Color.red
//ColorPicker
//supportsOpacity是否设置透明度
ColorPicker("picker", selection: $textColor, supportsOpacity: false).font(.largeTitle).foregroundColor(textColor)
说明:
- 设置Picker的标题,还有设置一个选取器selection
- 这里拿到的$textColor就是不断刷新的颜色
- supportsOpacity是设置是否设置透明度,其实还可以设置Pciker的很多东西
12、Picker
选择器
@State var selectedCity = city.xian
//Picker
Picker(selection: $selectedCity, label: Text("Picker").frame(width: 50, height: 10, alignment: .center)) {
Text("taiyuan").tag(city.taiyuan)
Text("xian").tag(city.xian)
Text("datong").tag(city.datong)
}.border(.orange)
Text("this city is : \(selectedCity.rawValue)")
说明:
- $selectedCity就是获取的值
- 设置多种选择项
13、toolbar
//设置工具栏
NavigationView {
Text("Hello World!").navigationTitle("navigation")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Edit") {}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("back") {}
}
}
}
说明:
- 设置TollbarItem项是每个按钮
- 有很多样式可以选择
- 不仅可以用于导航栏,可以用在任意的工具栏中
14、ProgressView
进度条视图
//ProgressView
// ProgressView(value: /*@START_MENU_TOKEN@*/0.5/*@END_MENU_TOKEN@*/)
ProgressView(value: progress, total: 10, label: {
Text("WY")
}, currentValueLabel: {
Text("start")
})
.progressViewStyle(.circular)
Button("加1") {
progress += 1
}
ProgressView().progressViewStyle(.linear)
}
说明:
- 进度条颜色的设置.tint(.red)
- view的颜色的设置.background(.green)
- 进度条值的设置,默认是0-1,我们可以也可以设置总数
- 空进度条的样式,可以直接设置成ProgressView(),此时是加载图,如果需要设置空进度条,就.progressViewStyle(.linear)
- .progressViewStyle()就可以设置进度条的样式
15、Slider
滑动块
@State private var isEditing = false
Slider(
value: $speed,
in: 0...100,
step: 5
) {
Text("Speed")
} minimumValueLabel: {
Text("0")
} maximumValueLabel: {
Text("100")
} onEditingChanged: { editing in
isEditing = editing
}
Text("\(speed)")
.foregroundColor(isEditing ? .red : .blue)
说明:
- 滑动时动态获取到的值是value
- in是设置滑动范围
- step是可以设置整个范围多少等分(可以设置自己的精确度)
- onEidtingChanged是滑动时的响应操作
- 还可以设置最大范围和最小范围显示
16、Togle
切换按钮,相当于UISwitch
Toggle(isOn: $vibrateOnRing) {
Text("Vibrate on Ring")
}
Toggle("Vibrate on Ring", isOn: $vibrateOnRing)
- Toggle就是切换按钮
- 这两种写法都可以,很简单,最后拿到vibrateOnRing就可以
17、Stepper
当您希望用户在增加或减少值时进行粒度控制时,请使用Stepper控件
//Stepper
Stepper {
//左侧文本
Text("Value: \(value) Color: \(colors[value].description)")
} onIncrement: {//加操作
incrementStep()
} onDecrement: {//减操作
decrementStep()
}
.padding(5)
.background(colors[value])//设置颜色
//设置范围,且默认加减
Stepper(value: $value,
in: range,
step: step) {
Text("Current: \(value) in \(range.description) " +
"stepping by \(step)")
}
.padding(10)
- 可以给Stepper设置点击的效果:
- 默认效果就是进行数值的加减计算,此时需要设置数值的总数和步长
- 如果自定义效果,就需要设置onIncrement和onDecrement来自定义响应
18、Gradient
图形渐变
代码:
//图形渐变
//角渐变
AngularGradient(gradient: Gradient(colors: [Color.red, Color.blue,.purple,.red]), center: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
//椭圆
EllipticalGradient(colors:/*@START_MENU_TOKEN@*/[Color.blue, Color.green]/*@END_MENU_TOKEN@*/, center: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/, startRadiusFraction: /*@START_MENU_TOKEN@*/0.0/*@END_MENU_TOKEN@*/, endRadiusFraction: /*@START_MENU_TOKEN@*/0.5/*@END_MENU_TOKEN@*/)
//线性
LinearGradient(gradient: /*@START_MENU_TOKEN@*/Gradient(colors: [Color.red, Color.blue])/*@END_MENU_TOKEN@*/, startPoint: /*@START_MENU_TOKEN@*/.leading/*@END_MENU_TOKEN@*/, endPoint: /*@START_MENU_TOKEN@*/.trailing/*@END_MENU_TOKEN@*/)
//辐射渐变
RadialGradient(gradient: /*@START_MENU_TOKEN@*/Gradient(colors: [Color.red, Color.blue])/*@END_MENU_TOKEN@*/, center: .bottomLeading, startRadius: 100, endRadius: 120)
19、searchable
//创建一个model项
struct ItemModel: Identifiable {
var id = UUID()
var name: String
var detailView: DetailView
}
//创建一个详情View
struct DetailView: View, Identifiable {
var id = UUID()
var detail: String
@State var text = ""
var body: some View {
VStack (alignment: .leading){
Text(detail).font(.largeTitle).foregroundColor(.gray).bold()
.searchable(text: $text){
Text("大同").searchCompletion("大同")
Text("太原").searchCompletion("太原")
Text("太原").searchCompletion("太原")
Text("太原").searchCompletion("太原")
Text("太原").searchCompletion("太原")
}
Spacer()
}
}
}
//定义一个数组
let datas: [ItemModel] = [
ItemModel(name: "太原", detailView: DetailView(detail: "山西省会")),
ItemModel(name: "西安", detailView: DetailView(detail: "陕西省会")),
ItemModel(name: "银川", detailView: DetailView(detail: "宁夏省会")),
ItemModel(name: "西宁", detailView: DetailView(detail: "青海省会")),
ItemModel(name: "呼和浩特", detailView: DetailView(detail: "内蒙省会")),
ItemModel(name: "郑州", detailView: DetailView(detail: "河南省会"))
]
//创建一个viewModel,提供了数组项
//并且还有一个filtedItems用来过滤每一项
class ViewModel: ObservableObject {
@Published var allItems: [ItemModel] = datas
@Published var searchedItem: String = ""
var filtedItems: [ItemModel] {
searchedItem.isEmpty ? allItems : allItems.filter({ str in
str.name.lowercased().contains(searchedItem.lowercased())
})
}
}
struct ContentView: View {
@ObservedObject var vm = ViewModel()
var body: some View {
NavigationView {
List {
ForEach(vm.filtedItems) {item in
NavigationLink(item.name, destination: item.detailView)
}
}
.navigationTitle(Text("搜索页面"))
.searchable(text: $vm.searchedItem, prompt: "输入您想要搜索的省会名称")
}
}
}
说明:
- 如果给List添加.searchable,必须配合NavigationView
- 给Text添加搜索框,可以给搜索框添加可选值,还可以给这些值设置响应
20、List
列表,就是OC中的TableView,是对UITableView的包装
List{
ForEach(todos, id:\.name){ (todo) in
Text("wenyi")
}
}
说明:
- List里面设置每一项cell
- 通过ForEach进行循环设置。
- 每个cell显示一个文本"wenyi".
21、TabView
Tab的View,就是之前的tabbar
//TabView
TabView() {
Text("Tab Content 1").tabItem {
Image(systemName: "person")
Text("Tab Label 1")
}.tag(1).badge(Text("news"))
Text("Tab Content 2").tabItem {
Text("Tab Label 2")
}.tag(2)
}
说明:
- 这里设置了两项,每项包括自己的文本内容,以及item自身的内容
- tabItem可以设置图片和文字。
- 还可以设置标记.badge
设置动画效果:
TabView() {
Text("Tab Content 1").tabItem {
Image(systemName: "person")
Text("Tab Label 1")
}.tag(1).badge(Text("news"))
Text("Tab Content 2").tabItem {
Text("Tab Label 2")
}.tag(2)
}.tabViewStyle(.page).background(.orange)
说明:
- item只有两个点
- 此时也可以取消白点(indexDispalyMode: .never)
- 这里的点如果想要设置图片,也是可以的
22、OnOpenURL
用来跳转页面,通过其他APP进行跳转到当前页面
//OnOpenURL
struct ContentView: View {
@State var show = true
@State var tabSelection = 1
var body: some View {
TabView(selection: $tabSelection) {
Text("Tab Content 1").tabItem {
Image(systemName: "person")
Text("Tab Label 1")
}.tag(1).badge(Text("news"))
Text("Tab Content 2").tabItem {
Image(systemName: "person")
Text("Tab Label 2")
}.tag(2)
}.onOpenURL { url in
switch url.host {
case "tab1":
tabSelection = 1
case "tab2":
tabSelection = 2
default:
show.toggle()
}
}
.sheet(isPresented: $show) {
Text("URL参数错误")
}
}
}
设置scheme
结果:
说明:
- 给TabView增加一个跳转连接,可以从外部应用跳转到当前页面
- 需要在info中设置一下scheme
- 如果想要删除URL type中的项,可以在Info.plist中删除,而且重新打开XCode
- .sheet用来弹出文本视图的弹框进行参数错误提醒
23、interactiveDismissDisab
禁止手势滑动关闭界面,必须加在按钮的后面
Button("Open Sheet") {
show.toggle()
}
.sheet(isPresented: $show) {
Button("Close") {
show.toggle()
}
.interactiveDismissDisabled()
}
说明:
- 给按钮设置禁止手势滑动关闭界面。此时就只能通过返回按钮返回。不能左滑返回了。
24、contextMenu
文本菜单
struct ContentView: View {
@State var backgroundColor = Color.red
@State var isShow = true
var body: some View {
Text("Hello world~").bold().font(.largeTitle).foregroundColor(.white).background(backgroundColor)
.contextMenu (isShow ? ContextMenu{
Button("Red") {
backgroundColor = .red
}
Button("Green") {
backgroundColor = .green
}
Button("Blue") {
backgroundColor = .blue
}
Button {
backgroundColor = .yellow
} label: {
Label("yellow", systemImage: "scribble")
}
} : nil )
}
}
说明:
- 给一个view增加菜单选项
- 也可以设置是否显示菜单
- 案例中菜单选项有多个按钮,点击按钮后可以改变背景颜色。
- 背景颜色设置到文本视图中,所以通过点击菜单选项就可以改变文本视图的背景颜色。
25、Menu
菜单视图,可以嵌套
Menu("Menu") {
Text("Item1")
Text("Item2")
Text("Item3")
Button("Red") {
backgroundColor = .red
}
Button("Green") {
backgroundColor = .green
}
Button("Blue"){
backgroundColor = .blue
}
Button("Yellow"){
backgroundColor = .yellow
}
Menu("Menu") {
Text("Item1")
Text("Item2")
Text("Item3")
Button("Red") {
backgroundColor = .red
}
Menu("Menu") {
Text("Item1")
Text("Item2")
Text("Item3")
Button("Red") {
backgroundColor = .red
}
}
}
}
说明:
- Menu来实现菜单视图,可以设置标题
- 菜单中可以放文本视图和按钮视图
- 并还可以再嵌套菜单视图,实现多层级菜单的选择。
26、Form
表单视图
Form和List在使用和现象是没有区别的,底层也是一样的,都是对UItableView的封装,可以看做是分组的List
用于分组数据输入的控件的容器,如在设置或检查器中。
Form {
Text("item1")
Text("item2")
Text("item3")
}
说明:
- 案例中显示三个视图文本
27、ScrollView
ScrollViewReader { proxy in
Button("gotoBottom"){
proxy.scrollTo(90)
}
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .center, spacing: 10) {
ForEach(0..<100) {
Text("cell \($0)").font(.title)
}
.frame(maxWidth:.infinity)
}
}
}
说明:
- 设置方向,可以选择垂直还是水平方向滑动
- 设置是否显示Indicators
- 通过代理增加按钮跳转某个cell,此时需要使用ScrollViewReader来实现。
- 比如这里点击按钮后,可以直接跳转到底90个cell。
28、Alert
//Alert
Button("show Alert") {
show.toggle()
}
.alert(isPresented: $show) {
Alert.init(title: Text("title"), message: Text("message"), dismissButton: .cancel())
}
Button("show Dialog") {
show.toggle()
}.confirmationDialog("dialog", isPresented: $show) {
Button("btn1") {}
Button("btn2") {}
Button("btn3") {}
}
}
说明:
- 这里可以设置多种按钮类型
- 可以设置点击事件的响应
- 还可以设置按钮列表,进行选择点击