1.预览快捷键
Option-Cmd-P
2.使用 @State 前一定加private,用来指定这个属性仅用于当前页面
3.常量绑定.constant(value)
示例:
TextField("Example placeholder", text: .constant("Hello"))
// slider不能滑动
Slider(value: .constant(0.5))
注意:像这样的常量绑定仅用于测试和演示目的——您不能在运行时更改它们。
4.展示测试视图
示例:
struct User: Identifiable {
let id: String
}
struct ContentView: View {
let users = (1...100).map { number in "User \(number)" }
var body: some View {
NavigationView {
List(users, id: \.self) { user in
NavigationLink(destination: Text("Detail for \(user)")) {
Text(user)
}
}
.navigationTitle("Select a user")
}
}
}
5.container容器超过10个view的限制怎么办?
SwiftUI中的所有容器必须返回不超过10个子view,这在大多数情况下是可以接受的。 但是,如果需要有10个以上的视图,如果需要从实体属性返回多个视图,或者如果需要返回几种不同类型的视图,则应使用如下组:
struct ContentView: View {
var body: some View {
List {
Group {
Text("Row 1")
Text("Row 2")
Text("Row 3")
Text("Row 4")
Text("Row 5")
Text("Row 6")
}
Group {
Text("Row 7")
Text("Row 8")
Text("Row 9")
Text("Row 10")
Text("Row 11")
}
}
}
}
Group是纯粹的逻辑容器——它们不会影响布局。
6.依靠自适应填充padding()
SwiftUI允许让我们精确地控制在视图周围应用多少填充,如下所示:
Text("Row 1")
.padding(10)
虽然总是像这样控制填充以“让事情变得恰到好处”很有诱惑力,但如果使用padding()
修饰符而不使用任何参数,则会得到自适应填充,即自动根据其内容和环境进行调整的填充。
7.合并文本视图Text
在Swift中,字符串可以用+拼接,在SwiftUI中,Text这个类似View的视图也可以拼接
struct ContentView: View {
var body: some View {
Text("Colored ")
.foregroundColor(.red)
+
Text("SwifUI ")
.foregroundColor(.green)
+
Text("Text")
.foregroundColor(.blue)
}
}
8.依赖隐式VStack容器
struct User: Identifiable {
let id = UUID()
let username = "Anonymous"
}
struct ContentView: View {
let users = [User(), User(), User()]
var body: some View {
List(users) { user in
VStack{ // 默认是VStack
Image("apple")
.resizable()
.frame(width: 40, height: 40)
Text(user.username)
}
}
}
}
如果不写默认是VStack,所以以上代码可以省略VStack容器
List(users) { user in
Image("apple")
.resizable()
.frame(width: 40, height: 40)
Text(user.username)
}
特别是List中的item是单个视图,经常是省略容器VStack
List(users) { user in
// 省略了VStack,使用隐藏的容器VStack
Text(user.username)
}
注意:系统之前是默认HStack,最新已经改为VStack
9.把action事件从View中独立出来
示例:
Button("Show on Map", action: showOnMap)
func showOnMap() {
showingMap = true
}
参考: https://www.hackingwithswift.com/articles/226/5-steps-to-better-swiftui-views
其实这是个有意思的问题,在Obj-C中,action都是独立出来的,很多人给Button添加Block,让button的action可以写在闭包里,以此实现代码的高聚合.
于是在Swift代码里,action都是控件的closure,现在国外的大佬又建议把action单独出来,写成方法,以此提高代码的阅读性,呵呵~
我认为各有优势,没必要一刀切,全部用closure,或全部用function都是没必要的,当closure里面的代码比较简单,行数不多,那么就用closure,如果closure里面代码量多,逻辑复杂,那就抽出成一个单独的方法.
10.使用extension进行模块化,比如把View Variables抽到一个extension中
struct GameView: View {
var body: some View {
...
}
}
// MARK: - View Variables
extension GameView {
// 测试用,非View变量
private var testStr: String { "hello" }
private var gradientBackground: some View {
LinearGradient(
gradient: Gradient(colors: gradientBackgroundColors),
startPoint: .top,
endPoint: .bottom
)
}
private var optionButtons: some View {
ForEach(GameItem.allCases, id: \.self) { gameItem in
GameOptionButton(
gameItem: gameItem,
opposingGameItem: self.game.currentItem,
currentSelection: self.$pendingChoice
)
}
}
}
在SwiftUI中,不仅可以给extension添加计算属性,而且还可以添加存储属性
11.在数组中查询index, 数据中建议加id,这样能保证item的唯一性
struct Task: Equatable, Hashable, Codable, Identifiable {
let id: UUID
var title: String
var isDone: Bool
init(title: String, isDone: Bool) {
self.id = UUID()
self.title = title
self.isDone = isDone
}
}
// 查询
guard let index = self.userData.tasks.firstIndex(where: { $0.id == self.task.id }) else { return }
guard let index = self.userData.tasks.firstIndex(of: self.task) else { return }
12.使用ForEach遍历数组中指定item
TabView {
// 遍历0到4
ForEach(fruits[0...4], id: \.id) { item in
CardView(data: item)
}
}
如果数组中有太多元素,只想使用其中几个item,可以使用[0…n],但需要注意数组的item必须大于n+1
13.顶部导航栏右侧编辑按钮,可以使用系统的EditButton
var body: some View {
NavigationView {
List {
// 第一组
Section { // 无header
ForEach(order.items) { item in
HStack {
Text(item.name)
Spacer()
Text("$\(item.price)")
}
} // 点击删除item
.onDelete(perform: deleteItems)
}
// 第二组
Section {
NavigationLink(
destination: CheckoutView()) {
Text("Place Order")
}
}
.disabled(order.items.isEmpty)
}
.navigationTitle("Order")
.listStyle(.insetGrouped)
.toolbar {
EditButton()
}
}
}
// 删除item
func deleteItems(at offsets: IndexSet) {
order.items.remove(atOffsets: offsets)
}