开发iOS App时存在一个十分常见的场景:点击一个按钮,跳转到下一个页面,即导航或页面跳转。

SwiftUI的标准库中存在NavigationViewNavigationLink等支持导航或页面跳转,即使功能十分强大,然而标准库提供的导航模式非常固定地体现在UI上,缺乏灵活性。

有时我们并不需要太强大的导航功能,只需要点击一个按钮,跳转到下一个页面的基本功能,且能够对UI进行灵活自定义。听起来如此简单的功能,对SwiftUI函数式编程的初学者而言并没有那么简单,尤其是在遵守MVVM编程模型的前提下

一般的,我们需要支持以下功能以实现任意情况下的自定义页面跳转:

1.有两个页面,页面A与页面B

2.有一个ViewModel,为页面A与页面B共享访问

3.页面A有一个ButtonA点击后可跳转至页面B,页面B有一个ButtonB点击后可跳转至页面A

4.遵守MVVM编程模型

为了便于演示,我们将情况特例化,我们需要实现在页面A和页面B各有一个emoji,点击任意emoji都会使得emoji下的count++,从而体现出共享ViewModel

首先定义我们简单的Model与ViewModel,Model中isJump用于记录我们处于的页面,为false时我们处于页面A,为true时我们处于页面B

struct Model {
private(set) var count: Int = 0
private(set) var isJump: Bool = false

mutating func increaseCount() {
count += 1
}

mutating func toggleIsJump() {
isJump.toggle()
}
}
class ViewModel: ObservableObject {
@Published private var model = Model()

// MARK: -Access to the model
var count: Int {
model.count
}

var isJump: Bool {
model.isJump
}

// MARK: -Intent(s)
func increaseCount() {
model.increaseCount()
}

func toggleIsJump() {
model.toggleIsJump()
}
}

接下来定义我们的页面A与页面B,我们要使用SwiftUI中的两种特性:ViewBuilderEnvironmentObject,ViewBuilder让我们轻松实现页面的跳转,而EnvironmentObject让我们能够以一种类似C或C++中指针的方式,使页面A与页面B能够共享ViewModel,代码实现如下:

struct AView: View {
@ObservedObject var viewModel = ViewModel()

var body: some View {
if !viewModel.isJump {
HStack {
VStack {
Text("A")
Text("🦄")
.onTapGesture() {
viewModel.increaseCount()
}
Text(String(viewModel.count))
}
Button("Next") {
viewModel.toggleIsJump()
}
}
.font(.system(size: 30))
} else {
BView()
.environmentObject(viewModel)
}
}
}
struct BView: View {
@EnvironmentObject var viewModel: ViewModel

var body: some View {
HStack {
Button("Back") {
viewModel.toggleIsJump()
}
VStack {
Text("B")
Text("🐶")
.onTapGesture() {
viewModel.increaseCount()
}
Text(String(viewModel.count))
}
}
.font(.system(size: 30))
}
}

最终效果如下: