做一个 iOS 计算器 App

做一个 iOS 计算器 App

这是我第一次完整的做一个 iOS App,算是一个简单的 Demo。可以熟悉简单的单视图层级的 Single View App 的开发过程。iOS 自带的计算器实在是难用,所以这个计算器会针对 iOS 自带计算器的一些痛点作出改进。

目标

  • [ ] 逻辑
    • [x] 最基本的四则运算
    • [x] 可随时修改已输入的计算式
    • [x] 实时显示当前算式的计算结果
    • [ ] 可对计算结果进行复制
    • [ ] 可将计算结果转换为大写文字
    • [ ] 科学计算
    • [ ] 每隔三位插入一个分位符
  • [ ] 交互
    • [x] 自适配iOS13深色模式/浅色模式
    • [x] 按钮震动反馈
    • [x] 计算式超长时自动缩小字体
    • [ ] 自适配刘海全面屏与矩形屏
    • [ ] 界面旋转适配
    • [ ] iPad界面适配

逻辑设计

对于一个随手计算四则运算的计算器 App,最重要的并不是有多少复杂的科学计算的功能,而是交互符合直觉。之前做过一个以栈为原理,基于命令行的计算器,详细原理可以查看这篇文章。实际上这次的核心算法还是将中缀表达式通过栈转换为后缀表达式进行求值,但是与命令行交互不同,命令行交互是一次性给定完整的计算式,再输出结果,而触控界面的计算器则是边输入计算式,边求已输入部分的值。另外一个区别就是命令行交互可能会输入不规范的异常的字符,而触控界面则没有这个问题,只有数字与操作符,所以计算式的合法性问题并不是影响程序健壮性的核心问题。

数字按钮的处理

最基本的四则运算只需要 0 - 9 与 “+“、”-“、”ד、“÷”、“=”、小数点等操作,界面设计上与传统的计算器大同小异,不过多赘述,主要最难解决的问题是实时显示当前算式的计算结果。我们以 1+2×3 这个算式为例,输入到 2 以前时,应该显示答案为 1,输入到 3 以前时,应该显示答案为 3,输入完 3 以后,显示答案为 7,如果再输入一个 0,则答案变成 61,如果再把 0 删除,则答案又变回 7。

观察可以发现,只有当按下的按钮为数字的时候,才会对计算式进行计算,所以我们在按钮事件处可以加入判断,判断当前按下的按钮是否为数字,如果为数字,则计算当前计算式的结果。我们可以在表示计算式的变量中加入 didSet 方法,当计算式被改变时,立刻更新计算式栏的显示结果,结果栏同理,也只需要为表示结果的变量加入 didSet 方法。

小数点按钮的处理

我们要注意一点,为了实现足够的便利性,初始状态时,软件的结果栏与计算式栏并非为空,而是为 0。根据符合直觉的逻辑,计算式为 0 时,按下任意数字清除默认的 0 开始计算,但如果按下的为小数点,计算式会在 0 后开始计算。例如在初始状态输入 3,计算式栏应该显示 3,输入 .3,计算式栏应该显示 0.3,并且在任何时候直接输入小数点以及小数点以后的数字都默认视为小数点前有 0,也就是说 1+.2 这个计算式等同于 1+0.2 这个计算式。这两个并非很难处理,只要判断输入是否为数字,如果为数字,则将 0 删去,不为 0 则跟在 0 之后输入,对于小数点的处理在之前基于命令行的计算器中已经实现了部分健壮性,已经可以处理 1+.2 这样的情况,这里不再赘述。

我们考虑以下情况,当用户输入 1+0.2 时,下一刻按下小数点,如何处理?很显然,我们不应该将这个小数点计入计算式。由于小数点是以按钮形式的形态进行交互的,我们在小数点按钮被按下的时候,将按钮的状态置为不可用的状态,当按下运算符按钮时,将小数点按钮的状态重置为可用状态。通过以上方法,我们解决了与小数点有关的所有健壮性问题,现在小数点按钮可以在任何时候被按下而不会影响计算结果。但其实还有可以改进的地方,我们再考虑一个情况,当用户输入 1+0.2 时,此时按照我们处理小数点的逻辑,小数点按钮已经被禁用,此时将计算式删除到 1 的状态,想要将计算式变更为 1.1+0.2,而此时小数点按钮依然处于禁用状态,除非按下重置按钮,否则我们无法输入这个小数点。为了解决这个问题,我们考虑在删除按钮的事件中加入检测条件,如果检测到删除的为小数点,则也将小数点重置为可用状态。

删除按钮的处理

当我们按下删除按钮时,希望得到什么?我们希望计算式的结果依然随着删除动态变化,思考数字按钮的处理方式,我们只有在计算式的数字变动时才需要计算表达式,所以我们只有在删除的为数字时才对计算式进行重新计算,这里涉及一个问题,以 1+2+3 为例,删除 3,此时需要重新计算表达式,但是剩下的 1+2+ 并不是一个合法的计算式,我们再加入一个判断方法,当删除数字后剩下的计算式末尾为计算符时,计算不含末尾计算符的计算式的结果,并将这个结果返回给我们的结果,这里要注意,不能直接使用我们的计算式进行计算,因为我们不能真的把末尾的计算符删去,我们应该创建一个临时计算式,计算出结果后返回给结果值。

操作符按钮的处理

操作符唯一需要注意的地方就是当连续输入操作符时,根据直觉,相当于直接更改当前输入的操作符。以 1+2+ 为例,此时输入-号等价于将计算式修改为 1+2-,我们可以在按钮方法中加入检测,当按下的是操作符按钮并且当前计算式末尾也为计算符时,修改计算式末尾计算符为新输入的计算符。

交互设计

深浅色模式自适配

对于非图片素材,直接使用动态颜色对控件颜色属性进行设置即可,动态颜色的声明如下:

1
2
3
4
5
6
7
let  dynamicColor = UIColor { (trainCollection) -> UIColor in
if trainCollection.userInterfaceStyle == .dark {
return UIColor.white
} else {
return UIColor.black
}
}

在这段代码中,如果当前为深色模式,dynamicColor 变量为 white,如果为浅色模式,dynamicColor 变量为 black。

按钮震动反馈

首先声明一个UIImpactFeedbackGenerator变量,根据苹果的文档,UIImpactFeedbackGenerator是 UIFeedbackGenerator 的子类,创建模拟触觉的物理反馈。总共有五种反馈模式,分别是轻 light、中 medium、重 heavy 和还在 Beta 测试(2019年11月)的柔软 soft 与干脆 rigid 两种,下面这行语句声明了一个 heavy 类型的震动反馈,并使用 prepare 方法与 impactOccurred 方法触发反馈。

1
2
3
var tapic = UIImpactFeedbackGenerator.init(style: UIImpactFeedbackGenerator.FeedbackStyle.heavy)
tapic.prepare()
tapic.impactOccurred()

计算式超长时自动缩小字体

这个只需要将 UILabel 的 adjustsFontSizeToFitWidth 属性设置为 true,就能实现字体大小自动适应宽度。

结果栏与计算式栏的聚焦

当按下等号按钮时,将结果栏的字体放大,颜色加深,计算式栏字体缩小,颜色减淡,凸显结果,与此同时将计算式重置为当前结果,可以在下一次计算式复用当前计算式计算出来的结果。当继续输入计算式时,又将结果栏字体缩小,颜色减淡,计算式栏字体放大,颜色加深,凸显正在输入的计算式。


做一个 iOS 计算器 App
https://wenchanyuan.com/ios_calculator/
作者
蟾圆
发布于
2019年11月13日
许可协议