实战教程·什么年代了还在敲传统木鱼?(一)

项目背景

最近由于疫情闲暇在家,在浏览网络信息的时候发现一款“电子木鱼App”悄悄爆红网络。无需购买实体的木鱼,只需要下载App,通过不断点击App按钮,即可完成“自我救赎”,甚至还有不同的敲击声音可以选择。在饶有兴趣地查阅相关文章后,不仅觉得“有点意思”,难道这是佛教在做数字化转型?

一笑而过后,当静下来却心里有些感慨。

想起儿时也曾想遁入空门不理世事,做一个和尚追求心中的信仰,过简单朴素的生活。而如今在社会谋生,应付复杂人性,每天拖着疲惫的身体时常半夜回家,这真的是我想要的吗?

感慨之后,想想“电子木鱼”这个案例也挺有意思,不如也尝试做下教程,也算是一种另类的解压。

项目分析

首先我们来先行分析“电子木鱼”App的功能点,如下图所示:

电子木鱼主要功能点为点击木鱼本体发出敲击声音,每当敲击一下,在界面上将出现“功德+1”的字样,且顶部计算敲击的次数。再深入些,我们还可以支持自定义提示文字,比如“开心+1”、“财富+1”等等提示文字。

整体项目难道不大,下面我们来一步一步实现它。

项目准备

首先创建一个新的SwiftUI项目,命名为DigitalWoodfish。如下图所示:

在ContentView文件中,选择模拟器预览示例代码效果,如下图所示:

然后,我们需要导入一张“木鱼”的图片作为项目的素材,在Assets文件中,我们拖入一张网络上下载的木鱼图片。

为了更好的呈现效果,建议下载的图片背景颜色为透明,因为笔者使用的是iphone,在新版的iOS中可以提取图片中的元素出来,很容易就获得了一张没有背景颜色的图片,可以使用此方法获得项目素材。

素材整理好后,拖入到Assets文件中,如下图所示:

回到ContentView文件中,我们开始搭建UI样式部分。

敲击主体:电子木鱼

第一步,我们先搭建木鱼的主体展示部分,可以使用Image图片组件,先删除示例代码中VStack中的代码,然后在顶部工具栏右上角选择“+”按钮,在Library中,点击Media栏目,可以看到在Assets导入的所有素材。

点击woodfish素材,拖动到VStack代码中,如下图所示:

拖入完成后,我们就得到了带有woodfish素材的Image组件代码,如下图所示:

由于导入的图片尺寸等原因,素材超出了屏幕边界范围,这时候需要对Image图片进行修饰,依旧是点击顶部工具栏右上角“+”按钮,在Library选择Modifiers修饰符栏目,找到Image的修饰符,选择Image Resizable图片可调整大小修饰符,如下图所示:

将Image Resizable图片可调整大小修饰符加到Image图片上,如此,修饰符便可作用到控件上,如下图所示:

同理为了保证图片尺寸不变形,还需要拖入Aspect Ratio调整长宽比修饰符,设置为fit自适应。最后使用Frame尺寸框架修饰符调整其大小。如此,便得到了一个木鱼主体视图。如下代码所示:

Image("woodfish")
	.resizable()
	.aspectRatio(contentMode: .fit)
	.frame(maxWidth: 180, maxHeight: 180)
复制代码

大家可能注意到了一点,和之前直接使用代码编程的方式不同,本章使用的方式更类似于UI设计方式。是的,在推出SwiftUI之后,苹果这一声明式语言最大的特点就是通过“描述性语言”来“创建一个事物”。

上述的编程方式中,我们类似于日常描述一样,创建了一个图片和描述这个图片:

  1. 添加一个来自素材库的图片;
  2. 这个图片太大超出边界了,希望它的大小是可以调整的;
  3. 调整后尺寸变形了,希望它能保持原来的宽高比;
  4. 还是有点大/小,希望调整它最大是多大;

完成“木鱼”主体后,为了突出木鱼本体,可以将整个页面背景设置为黑色,和上述方式一致,可以在Library拖入Views栏目中的Depth Stack堆栈视图,并将上述的代码剪切到里面,代码如下:

ZStack {
    VStack {
        Image("woodfish")
        .resizable()
        .aspectRatio(contentMode: .fit)
        .frame(maxWidth: 180, maxHeight: 180)

    }
    .padding()
}
复制代码

背景颜色部分,可以在Library的Views栏目拖入Color颜色,拖入到ZStack堆栈视图中,并设置颜色值为.black黑色。如下图所示:

由于视图安全区域的原因,我们可以看到App页面上下都留有一片白色的区域。这时为Color颜色设置修饰符,让其忽略安全区域。

在Library的Modifiers修饰符栏目中选择Edges Ignoring Safe Area忽略安全区域,将该修饰符加到Color上,如下代码所示:

ZStack {
    Color(.black)
        .edgesIgnoringSafeArea(.all)
    VStack {
        Image("woodfish")
            .resizable()
            .aspectRatio(contentMode: .fit)
            .frame(maxWidth: 180, maxHeight: 180)
        
    }
    .padding()
}
复制代码

标题文字:功德值 10086

完成木鱼主体内容后,我们给这个视图加个标题。在Library的Modifiers修饰符栏目中选择Navigation Title导航标题,将其加到VStack纵向视图中,如下图所示:

我们看到并没有出现想要的导航栏标题,这是因为我们并没有创建导航栏视图,导致在主体视图上增加的修饰符无效。

要是用Navigation相关的修饰符,需要做在整体视图外层包裹一层NavigationView导航视图,在Library的Views栏目中拖入Navigation Stack导航堆栈视图,并把ZStack堆栈视图的内容剪切在里面,如下图所示:

特别说明:Navigation Stack导航堆栈视图是SwiftUI在2022年推出在iOS16版本新增的特性,如果是低版本的设备可能会出现不适用的情况,可以使用NavigationView代替。

完成后我们发现还是看不到导航标题的内容,这是因为整体背景由于Color颜色和ZStack堆栈视图将界面颜色填充为黑色,而Navigation Title导航标题文字颜色为默认为黑色,导致颜色不可见。

这时,可以使用修饰符栏目中的Preferred Color Scheme首选配色方案,将首选配色设置为夜间模式,如此SwiftUI将会将标题由原来的黑色自动转为白色,如下代码所示:

NavigationStack{
    ZStack {
        Color(.black)
            .edgesIgnoringSafeArea(.all)
        
        VStack {
            Image("woodfish")
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(maxWidth: 180, maxHeight: 180)
        }
        .padding()
        .navigationTitle("Title")
        .preferredColorScheme(.dark)
    }
}
复制代码

标题的文字部分,需要展示功德总数,并在每次敲击的时候增加总数。除了基础的“功德数”之外,还可以设置“静心值”、“开心值”等,因此标题部分需要动态设置文本值。

声明2个参数作为标题的内容,如下代码所示:

@State var gameType:String = "功德值"
@State var totalNumber:Int = 10086
复制代码

上述代码中,gameType为虔诚求取的内容,类型为String字符串类型,示例:功德值。totalNumber为总数,统计敲击的次数,由于需要每次 敲击的时候进行累加,因此设置为Int数值类型,示例:10086。

声明参数后,设置Navigation Title导航标题文字内容为声明的变量的组合,如下代码所示:

.navigationTitle(gameType+":"+String(totalNumber))
复制代码

由于totalNumber总数为Int类型,而Navigation Title导航标题文字内容需要为String字符串类型的文字,因此在使用字符串拼接的时候,需要将Int类型转换为String类型,SwiftUI可以直接使用类型包裹进行类型转换。

提示文字:功德值+1

下面我们来完成敲击动作,每当我们敲击/点击木鱼的时候,木鱼会显示提示文字,示例:功德值+1。

我们可以先创建文字部分,在Library的Views栏目中拖入Text文字控件,设置文字内容为“功德值+1”,如下代码所示:

Text("功德值+1")
复制代码

提示文字部分,根据文字结构可以拆分为求拜的内容(与gameType保持一致),和敲击时增加的数量。求拜的内容可以直接使用gameType参数,我们还需要声明每次增加的值的参数,如下代码所示:

@State var number:Int = 1
复制代码

声明好参数,需调整Text组件展示的文字的内容,如下代码所示:

Text(gameType+"+"+String(number))
复制代码

交互动画:敲击木鱼

下面来完成敲击木鱼的交互动作,当我们点击木鱼视图时,Navigation Title导航标题文字的“功德值”总数会增加,我们可以给木鱼主体视图添加一个点击动作。

在Library的Modifiers修饰符栏目中选择On Tap Gesture点击手势修饰符,将它加到Image控件上。当每次点击时,让totalNumber总功德值加上每次点击的number的值,如下代码所示:

.onTapGesture {
	totalNumber += number
}
复制代码

其中,totalNumber += number相当于totalNumber = totalNumber+number。

如此,在每次点击木鱼的时候,功德值总数都会增加。

接下来,我们给木鱼点击增加交互动作。电子木鱼点击时,每次点击木鱼都会有一个收缩的动画带来点击效果,且提示文字为每次点击时才出现,而后消失。这都给用户创造一种点击的反馈,而动画正是SwiftUI优秀之处。

对于上述的交互动作,我们可以使用Bool运算逻辑,实现敲击木鱼的时候木鱼缩小,缩小后再恢复原状。亦敲击木鱼的时候提示文字呈现,而后消失。

首先声明一个Bool类型的变量,如下代码所示:

@State var isTap:Bool = false
复制代码

然后给Text文字加一个if判断,当isTap为true,即木鱼被点击时才展示Text提示文字,如下代码所示:

if isTap {
	Text(gameType+"+"+String(number))
}
复制代码

然后给木鱼Image图片增加缩放的修饰符,在Library的Modifiers修饰符栏目中选择Scale Effect缩放修饰符,添加到Image控件的Frame修饰符之后,如下代码所示:

.scaleEffect(isTap ? 0.99 : 1.0)
复制代码

Scale Effect缩放修饰符根据isTap的状态,展示缩放比例,由于isTap默认状态为false,因此当isTap为ture时Scale Effect缩放比例为0.99,为false时则默认为1.0。判断条件逻辑说明如下:

参数 ? 为ture时的状态 : 为false时的状态

最后,我们需要在点击木鱼的时候修改isTap的状态,但修改后还需要自动恢复原来的状态。因此我们可以在切换isTap状态的同时,在主线程执行一个相反的逻辑,以达到恢复的效果,如下代码所示:

if !isTap {
	self.isTap.toggle()
	DispatchQueue.main.asyncAfter(deadline: .now()+0.1) {
		self.isTap.toggle()
	}
}
复制代码

上述代码中,我们给木鱼Image图片控件点击时间中添加了一个逻辑:当isTap没有被点击的时候,切换isTap的状态,表明,木鱼被点击,木鱼被点击切换isTap状态后,在主线程等待0.1秒后,自动切换isTap状态,恢复到木鱼未敲击时的状态。

项目效果预览

在Xcode右侧的预览窗口尝试点击木鱼看看效果,如下图所示:

iShot_2022-11-09_18.21.18.gif

项目小结

这是一款很有趣的App,玩法简单但不失乐趣,仿佛间感受到自身被净化了。其实想了想,这款App之所以能够火起来,可能是现代打工人对自己的自嘲吧,通过创造一些没什么用的,来让这个世界更有趣一些。

以往的项目可能用的最多的是直接编程的模式,记住基本的控件的使用,记住这些控件能使用的修饰符,记住控件能使用的交互动画。似乎,作为一个程序员不老老实实写代码就不算程序员。

SwiftUI提供了一种新型的开发模式,这种声明式语法就好像我们在“告诉”计算机,我们要什么东西,以及它应该长成什么样。因此,这次的案例尝试从SwiftUI本身出发,以“拖拽组件”的方式呈现一种编程方式,希望大家也能有所收获。

另外,电子木鱼App还没有完善,下一章节,我们将实现敲击时的声音以及更改敲击木鱼祈祷的内容,请保持期待吧。

版权声明

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

猜你喜欢

转载自juejin.im/post/7163988066070167559