前提回顾

在上一章节中,咱们完结了“预备游戏”页面和“游戏列表”页面,并完结了游戏列表的简单交互,在本章中,咱们将持续完结其他的相关内容。

实战编程

页面切换

在整个空气投篮项目中,“预备游戏”页面和“游戏列表”页面的交互逻辑是,翻开App展现“预备游戏”页面,同时唤起Watch端的授权,授权通往后,进入到“游戏列表”页面。

当时Watch端先行忽略,咱们先完结切换切换的逻辑。首要声明一个变量存储切换动作,如下代码所示:

@State var isAffirmInWatch: Bool = false

上述代码中,咱们声明晰一个Bool类型的变量isAffirmInWatch,初始状况位false。

当isAffirmInWatch是否授权状况为true时,咱们进入到gameListView游戏列表页面,若没有授权,则停留在prepareView预备游戏页面。如下代码所示:

if isAffirmInWatch {
	gameListView()
} else {
	prepareView()
	.onTapGesture {
		self.isAffirmInWatch = true
	}
}

上述代码中,为了演示方便,咱们给prepareView预备游戏视图加了一个onTapGesture点击事情,当点击prepareView预备游戏视图时,切换isAffirmInWatch是否授权状况为true,如此在点击时便可进入到gameListView游戏列表视图。

游戏回合视图

接下来,当用户点击游戏列表的游戏项时,需求进入到游戏概况页。

而对于“投篮项目”来说,一般有3~5个回合,在正式游戏开端之前,会展现一个类似“Round1,Ready Go”的游戏回合页面,然后才是正式游戏概况页。

为此,咱们需求创建一个新的SwiftUI页面来承载它,在Xcode左侧视图工具栏中新建一个SwiftUI文件,命名为GameDetailView,如下图所示:

在“游戏回合”视图中,咱们能够看到几个页面元素:游戏回合数(Round1)、游戏规则(5米)、游戏规则阐明(间隔篮筐)、游戏成果(投中:0)。

因为上述的参数会跟着游戏更新内容,因而需求声明其变量进行存储,如下代码所示:

@State var roundNum:Int = 1
@State var distanceNum:Int = 5
@State var gameGoal:Int = 0

上述代码中,咱们声明晰3个变量:roundNum游戏回合数、distanceNum篮筐间隔、gameGoal单回合游戏得分。这儿因为在后续要运用到数值计算,因而声明的变量都是Int类型。

紧接着,咱们来剖析构建页面,如下代码所示:

// 游戏回合
func preStartView() -> some View {
	VStack {
			Spacer()
			VStack(alignment: .center, spacing: 40) {
				Text("Round" + String(roundNum))
				.font(.system(size: 48, design: .rounded))
				.bold()
				.foregroundColor(.white)
				VStack(alignment: .center, spacing: 20) {
					Text(String(distanceNum) + "米")
					.font(.system(size: 24))
					.foregroundColor(.white)
					.bold()
					Text("间隔篮筐")
					.font(.system(size: 17))
					.foregroundColor(.white)
				}
			}
			Spacer()
			Spacer()
			Text("投中:  " + String(gameGoal))
			.font(.system(size: 17))
			.foregroundColor(.white)
		}
}

上述代码中,咱们创建了一个新的视图preStartView

剖析下页面元素,Text回合数文字为首要文字,运用48号圆形字体款式,并设置bold加粗,foregroundColor文字填充色彩为白色,其余文字根本修饰符类型。

这儿科普一个知识点。

因为声明的变量是Int类型,而Text文字需求键入String类型的文本,因而需求将Int类型转化为String类型。SwiftUI对于类型转化能够直接运用类型包裹进行转化,示例:String(roundNum),如此便能够直接将roundNum游戏回合数转化为String类型的参数。

别的,咱们能够运用“+”对字符串进行拼接,组成一个新的字符串,示例:

“I”+”Love”+”You” 成果为 “ILoveYou”

回归正题,文字部分也是运用VStack笔直布局视图进行包裹元素,这儿的编程思想是“由中间向两边散开”,即相间隔较近的元素能够先组合成一个容器,再和外边的容器进行组合,便于设置视图元素之间的距离。

完结好单个preStartView视图后,咱们在Body中展现它,如下代码所示:

ZStack {
	Color(.black).edgesIgnoringSafeArea(.all)
	preStartView()
}

游戏中视图

在游戏回合视图展现后,用户会进入到“游戏中”视图,正式开端参与游戏。如下图所示:

空气投篮游戏的游戏视图很简答,还原在现实生活中的篮筐,由一个计分板和投篮的篮筐组成,而计分板分为两块,分别为个位数计分板和十位数计分板。

咱们首要要导入“篮筐”的图片,相同是在黑色布景下,咱们需求一张SVG格式的矢量图,如下图所示:

回到GameDetailView游戏概况页,咱们来构建游戏中视图的款式,因为需求统计计分板的分数,因而需求声明好变量部分,如下代码所示:

@State var unitsDigit: Int = 0
@State var tensDigit: Int = 0

上述代码中,unitsDigit为计分板个位数,tensDigit为计分板十位数。然后,咱们再构建款式部分,如下代码所示:

// 游戏页面
func playGameView() -> some View {
	VStack(alignment: .center, spacing: 40) {
			HStack(alignment: .center, spacing: 20) {
				Text(String(unitsDigit))
				.font(.system(size: 120))
				.bold()
				.foregroundColor(.white)
				.padding(40)
				.background(Color.gray)
				.cornerRadius(8)
				Text(String(tensDigit))
				.font(.system(size: 120))
				.bold()
				.foregroundColor(.white)
				.padding(40)
				.background(Color.gray)
				.cornerRadius(8)
			}
			Image("ball")
			.resizable()
			.aspectRatio(contentMode: .fit)
			.frame(maxWidth: UIScreen.main.bounds.size.width / 2)
		}
}

上述代码,在playGameView视图中,咱们运用HStack横向布局容器包裹了计分板的款式内容。

关于Text文字布景部分,SwiftUI供给的办法是运用padding撑开间隔,再运用background布景色彩填充撑开的距离,最终再运用cornerRadius设置圆角。

如此,便完成了计分板的款式效果,图片部分这儿就不多说了。

相同,前期什么的Int类型的参数,在Text文字组件中运用需求转化成String字符串类型,方可运用。

此时咱们就完结了2个页面:preStartView游戏回合视图、playGameView游戏中视图。这儿做页面的切换,咱们也能够声明一个参数来进行状况的切换,如下代码所示:

@State var isReady:Bool = false

然后经过声明好的isReady参数进行页面间的切换,如下代码所示:

if isReady {
	playGameView()
} else {
	preStartView()
}

进入&回来

经过两个章节的学习,咱们完结了两个首要的视图:ContentView主页视图、GameDetailView游戏概况视图,共4个页面,接下来,咱们要将这4个页面串起来。

回到ContentView主页视图,咱们盘一盘逻辑,在用户点击游戏项时,将会进入到GameDetailView游戏概况视图,游戏概况页首要会展现回合视图,然后再开端游戏。

了解了根本的交互逻辑后,咱们先完结页面之间的跳转,这儿能够运用根据NavigationView顶部导航栏的跳转方式,如下代码所示:

NavigationView {
	ZStack {
		Color(.black).edgesIgnoringSafeArea(.all)
		if isAffirmInWatch {
					gameListView()
				} else {
					prepareView()
					.onTapGesture {
						self.isAffirmInWatch = true
					}
				}
	}
}

上述代码中,需求运用NavigationView将整个视图包裹起来,然后再在gameListView游戏列表视图中增加跳转办法,如下代码所示:

NavigationLink(destination: GameDetailView()) {
	gameRowView(gameName: "投篮", gameHelpText: "手举球开端游戏", gameImage: "basketball")
}

如此,当咱们点击“投篮”的游戏项时,就会跳转到GameDetailView游戏概况页。

咱们来到GameDetailView游戏概况页,因为当时GameDetailView游戏概况页的isReady参数变量为false,因而GameDetailView游戏概况页会展现preStartView游戏回合视图。

咱们希望的交互是preStartView游戏回合视图在显示2秒后自动到playGameView游戏中视图。

这儿在页面载入时增加多一个办法,如下代码所示:

ZStack {
	Color(.black).edgesIgnoringSafeArea(.all)
	if isReady {
		playGameView()
	} else {
		preStartView()
	}
}
.onAppear(){
	DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
				self.isReady = true
			}
}

上述代码中,咱们在GameDetailView游戏概况页onAppear展现时增加了一个在主线程上的操作,即根据当时时刻2秒钟后,切换isReady状况。

如此,咱们便完成了在用户进入GameDetailView游戏概况页时,先展现preStartView游戏回合视图,再展现preStartView游戏中视图了。

进入操作有了,最终是回来操作。

原有的回来按钮太丑了,咱们能够自定义一个回来按钮,如下代码所示:

// 回来上一页
func backButton() -> some View {
	Button(action: {
		}) {
			Image(systemName: "chevron.left.circle")
			.font(.system(size: 17))
			.foregroundColor(.white)
		}
}

并将它加到GameDetailView游戏概况页视图中,如下代码所示:

ZStack {
	Color(.black).edgesIgnoringSafeArea(.all)
	if isReady {
		playGameView()
	} else {
		preStartView()
	}
}
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: backButton())

回来的操作交互也很简单,咱们能够调用SwiftUI的通用回来办法,如下代码所示:

@Environment(.presentationMode) var mode

最终在点击backButton回来按钮的时分运用回来办法,如下代码所示:

self.mode.wrappedValue.dismiss()

本章预览

完结后,咱们回到ContentView预览下全体项目。

1664534091925-a33785ed-b6bb-42cc-bb18-1822cb65beaa.gif

本章小结

恭喜你,完结了本章的所有内容!

在本章中,咱们构建了游戏概况页的视图,并完结了概况页的两种状况页面,预备开端游戏和游戏中的状况页面,还完成了从主页跳转到概况页、回来主页的全过程。

空气投篮项目iOS端全体的交互内容根本就到这儿了,接下来咱们将持续运用MVVM开发模式调整iOS端的内容,后面还会完结Watch端的页面及其交互。

最终如果条件成熟,咱们将调用Apple供给的各种传感器来完结真实的交互体验。

请坚持期待吧~

版权声明

本文为稀土技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!