开发OS X 图形应用界面时有三种实现方式:XIB、Storyboard、SwiftUI。Storyboard基于XIB做了优化,但XIB基本被放弃了,而SwiftUI是苹果公司后来开发的一套编程语言,用来平替Objective-C。虽然现在Swift 6 还是有些不完善的地方,但可以看到此为未来的主流。
- 本系列课主要讲下SwiftUI方面的知识,不会过多涉及Swift基础语言,Swift可以查看笔者的另一个专题。本节做为第一节内容,主要课下应用构建的基础知识。
- 笔者的开发环境为: Xcode Version 16.1 (16B40)、Swift 5,需要注意不同版本的Xcode其界面会有稍许差异,不同版本的Swift语言的API也会有一定的差异。
创建工程
所有的示例笔者会统一使用MacOS APP做为工程类型(因为IOS模拟器太占资源了),无论是OS还是IOS这对学习SwiftUI没有任何影响。
创建工程
一路Next,最后工程结构如下:
- Preview Content:用于存放预览内容,开发者可以利用这些预览内容来快速查看和验证他们的界面设计在不同设备和屏幕尺寸上的表现,从而提高开发效率和界面质量;
- .entitlements:相当于原来的info.plist文件,用于配置工程的一些设置;
- Assets.xcassets:资源文件,主要用于存放图标;
- XxApp.swift:工程启动入口函数,相当于main();
- ContentView.swift:SwiftUI 界面设计源码,在正式程序中可能会有多个这样的UI界面源码文件,工程默认创建的这个文件会被XxApp.swift调用。
工程源码
- 启动文件
import SwiftUI@main // 注意此标的使用
struct _1_base1App: App { //实现App协议var body: some Scene { //图控制器或应用入口点WindowGroup {ContentView() //调用UI实现类}}
}
- Swift UI 视图
import SwiftUIstruct ContentView: View {var body: some View {VStack { //布局Image(systemName: "globe").imageScale(.large).foregroundStyle(.tint)Text("Hello, world!")}.padding()}
}//这行可有可无,主要是用于开发时实时预览用的
#Preview {ContentView()
}
工程运行
在运行前最基本的有几个设置需要确认一下:
- targets:同一工程可设置多个运行目标,可在项目设置的
General
面板中设置; - swift版本:可在
building settings
面板中设置;
新建界面
如果要创建新的UI,可选择
File- New- File from template
,选择SwiftUI
。
默认代码如下:
import SwiftUIstruct SwiftUIView: View {var body: some View {Text("Hello, World!")}
}#Preview {SwiftUIView()
}
Doc文档
- 中文文档: https://gitee.com/guangjie2021/SwiftUICn
- 苹果官方:https://developer.apple.com/documentation
基础组件
这里只介绍几个,详细的可以参考上述文档中的内容。
Text 文字
基础代码如下,下面代码中有一些是.gray
的写法,其是为了简便编程,把位于SwiftUICore中对象的一些名称在调用时省略了。
import SwiftUIstruct SwiftUIText: View {var body: some View {VStack{Text("这是一个swiftui实现的文本label,可以设置一些属性").font(.custom("slideyouran-Regular", size: 23)).foregroundColor(.gray).lineLimit(2).multilineTextAlignment(.center).padding()Text("以下是一些详情信息").font(.system(size: 23))}.padding()}}#Preview {SwiftUIText()
}
- 自定义字体,在上述代码中有一行
.font(.custom("slideyouran-Regular", size: 23))
代码,这种是一种自定义字体的写法:
- 拷贝.ttf字体文件到工程中;
- 配置工程info信息;
- 编码;
工程info配置方法如下图所示,其中item 0 后面的值为字体的名称,这个名称可能会和文件名不一样,需要在系统中安装一下再确定:
Image 图片
图片有三种实现方式:
加载资源库图标
就是Assets库中的图片,在这个视图中可以添加文件夹等信息,但在使用时不需要关注其路径问题,但也意味着名称要唯一。
Image("DemoImg").resizable().scaledToFit().frame(width: 64).cornerRadius(12)
加载系统图标
苹果官方开发一个名为SF Symbols
图标库的软件,这个软件主要用于查找系统图标名称,同时还能自动生成一些代码,供开发时何用。
这些图标相当于文字图标,可以使用内置函数设置颜色,线条等样式。
Image(systemName: "square.and.arrow.up.circle").font(.system(size: 52)).symbolEffect(.bounce).padding(12)
加载网络图片
就是从互联网上下载图片,但除了打开client设置外,还需要注意URL只能是https开头的。
// 这是一种闭包写法:// image in:这是闭包的开始,image 是闭包的参数,代表加载的图像AsyncImage(url: URL(string: imageUrl)) { image inimage // 这个闭包在图像加载完成后被调用.resizable().scaledToFit().frame(width: 280).cornerRadius(16)} placeholder: {
// Text("图片加载中")
// .font(.system(size: 24))
// .foregroundColor(.gray)
// .padding()
// .frame(width: 280,height: 160)
// .cornerRadius(16)ProgressView()}
自定义组件
SwiftUI的自定义组件比较简单,可以先了解下如何实现,后面章节再详细讲解。
自定义组件
比如如下代码,定义了一个自定义的TextDemo组件。同时也应该注意到,这里可以封装成比较复杂的套件。
import SwiftUI// MARK: - 文字组件
struct TextDemo: View { //组件参数var text: Stringvar textSize: CGFloatvar textColor: Color//组件样式var body: some View {Text(text).font(.system(size: textSize)).foregroundColor(textColor)}
}
自定义组件调用
自定义组件调用
import SwiftUIstruct SloganTextView: View {var body: some View {VStack(spacing: 10) {TextDemo(text: "你好,世界。", textSize: 23, textColor: .black)TextDemo(text: "愿日子如熹光,愿你能幸福~", textSize: 17, textColor: .gray)}}
}
预览效果如下:
布局组件
SwiftUI中内置了三种纯布局组件:VStack、HStack、ZStack,基本代码写法如下,可做为一个容器使用。
struct ContentView: View {var body: some View {VStack {Image(systemName: "globe").imageScale(.large).foregroundStyle(.tint)Text("我是启动页")}.padding()}
}
主界面设计
实现一个底图+图标这样的简单设计。
import SwiftUIstruct StartUpUI: View {var body: some View {ZStack(alignment: .bottom) {// 背景图片Image("starup").resizable()//.aspectRatio(contentMode: .fill).edgesIgnoringSafeArea(.all)VStack {Spacer() // 占位空间// 应用图标和应用名称HStack(alignment: .center, spacing: 20) {// 应用图标Image("DemoImg").resizable().aspectRatio(contentMode: .fit).frame(width: 60).cornerRadius(16)// 应用名称Text("世纪佳人").font(.system(size: 32)).foregroundColor(.white).bold()}}}}
}#Preview {StartUpUI()
}
常用方法
组件设置
以下是一种设计,很多的操作都需要以resizable()为前提。
- resizable():设置组件是否可以改变尺寸;
- aspectRatio():在resizable()打开的,指定调整尺寸时的横纵比和固定比;
- edgesIgnoringSafeArea():屏幕安全区设置;
- frame(width: 60):设置组件大小;
- cornerRadius(32):设置圆角
位置设置
- Spacer():占位符,它会把他后面的元素计算完大小后,然后占据其它空间
`VStack {Text("这是顶部的文本")Spacer() // 占据剩余空间,将下面的文本推到底部Text("这是底部的文本")
}`
- .frame():设置VStack的框架大小,下面这种
infinity
是一种自适应参数方式,非固定大小,建议用这种方式设置。
VStack {......
}
.frame(maxWidth: .infinity, maxHeight: .infinity) // 设置VStack的框架大小
- padding() 修饰符可以接受不同的参数来指定内边距的大小,默认为5px
Text("Hello, SwiftUI!").padding(10.0) // 在文本视图周围添加10点的内边距Text("Hello, SwiftUI!").padding(.init(top: 10, leading: 20, bottom: 15, trailing: 25)) // 设置上、左(leading)、下、右(trailing)内边距Text("Hello, SwiftUI!").padding(.medium) // 使用中等大小的内边距
按钮与交互
SwiftUI的按钮实现有点特殊,它由两部分组成:事件+样式。其事件只有点击,但样式可以根据面要自定义,格式如下:
Button(action: {print("Tap") //事件
}) {Text("I'm a Button") //样式
}
按钮点击
struct TextButton: View {var body: some View {Button(action: { //事件print("aaa")}) {Text("立即使用") //样式.font(.system(size: 17)).foregroundColor(.white).padding().frame(width: 180).background(LinearGradient(gradient: Gradient(colors: [Color.blue, Color.green]), startPoint: .leading, endPoint: .trailing)).cornerRadius(16)}}
}
上述的样式部分也可以换成图标,如下面代码
struct ImageBtnView: View {var body: some View {Button(action: {// 点击后的操作}) {Image("WoodenFish").resizable().scaledToFit().frame(width: 160)}}
}
onTapGesture()手势
下面代码中用到了@State
,其为状态缓存,用于MVVM交互使用的。
struct SwiftUIButton: View {@State var finNumber: Int = 1var body: some View {// 布局容器VStack(spacing: 32) {// 文字内容Text("功德+" + String(finNumber)).font(.system(size: 20)).foregroundColor(.black)// 图片按钮Image("WoodenFish").resizable().scaledToFit().frame(width: 160)// 点击操作.onTapGesture {finNumber += 1}}}
}