该项目是同济大学软件学院2020年移动应用开发的第二次课程作业。
使用UIKit和SwiftUI分别开发一个计算器应用程序。具体要求如下:
- 使用MVC架构进行计算器开发,代码中需要包括Module、View、Controller的部分
- 使用AutoLayout,使得计算器能够在不同的iPhone和iPad上正常显示
- 计算器不需要支持多步计算(优先级等)
- 使用MVVM架构进行计算器开发
- 计算器能够在不同的iPhone和iPad上正常显示
- 计算器不需要支持多步计算(优先级等)
Xcode 12.0.1 + Swift 5.0
采用MVC架构,Main.storyboard为View部分,CalculatorViewController.swift为Controller,CalculatorBrain为Model。DigitButton.swift中将所有Button设置为圆角,外观更符合iOS原生计算器。
View由三部分组成:
- 黄色矩形框中为DisplayValueOutlet,显示计算结果;
- 红色矩形框中为运算符Button,与之相关联的为performOperation方法;
- 蓝色矩形框中为数字Button,与之相关联的为touchDigit方法。
使用AutoLayout使之在多设备可以正常显示:
设置变量displayValue来更改Outlet的显示:
@IBOutlet weak var displayVlaueOutlet: UILabel!
var displayValue: Double {
get {
return Double(displayVlaueOutlet.text!)!
}
set {
if Double(Int(newValue)) == newValue {
displayVlaueOutlet.text = String(Int(newValue))
}else {
displayVlaueOutlet.text = String(newValue)
}
}
}
对于数字键输入进行判断,如果userIsInTyping为真,说明用户正在输入数字的过程中,所以对数字进行处理,由于类型均为String,所以可以直接相加;如果userIsInTyping为假,则说明用户输入的是**”.“或者首位数字,进行设置userIsInTyping**为真。
@IBAction func touchDigit(_ sender: UIButton) {
let digit = sender.currentTitle!
let textCurrentInDisplay = displayVlaueOutlet.text!
// 如果正在输入中
if userIsInTyping {
if digit == "0" && textCurrentInDisplay == "0" {
displayVlaueOutlet.text = "0"
}
else {
displayVlaueOutlet.text = textCurrentInDisplay + digit
}
}
else {
if digit == "." {
displayVlaueOutlet.text = "0."
}else {
displayVlaueOutlet.text = digit
}
userIsInTyping = true
}
}
对于操作符按键进行处理,调用Model中相关方法处理运算。
@IBAction func performOperation(_ sender: UIButton) {
// 1. 设置操作数
if userIsInTyping {
brain.setOperand(displayValue)
// 设置完操作符之后,需要接受第二个操作数
userIsInTyping = false
}
// 2.执行计算
brain.performOperation(sender.currentTitle!)
// 3.获取结果
if let result = brain.result {
displayValue = result
}
}
Model中定义操作的类型:
private enum Operation {
// 常量操作
case constant(Double)
// 一元操作
case unary((Double) -> Double)
// 二元操作
case binary((Double,Double) -> Double)
// ..equal
case equal
}
定义操作符对应的运算:
private var operations: [String: Operation] = [
"AC": .constant(0), // 清空,直接返回0
"±" : .unary({-$0}),
"%" : .unary({$0 / 100}),
"+" : .binary( + ),
"−" : .binary( - ),
"×" : .binary( * ),
"÷" : .binary( / ),
"=" : .equal,
]
设置操作数:
mutating func setOperand(_ operand: Double) {
self.operand = operand
result = operand
}
等待中的操作数和操作符:
private struct PendingBinaryOperation {
let firstOperand: Double
let operation: (Double, Double) -> Double
func perform(with secondOperand: Double) -> Double {
return operation(firstOperand,secondOperand)
}
}
执行计算:
mutating func performOperation(_ symbol: String) {
let operation = operations[symbol]!
switch operation {
case .constant(let value):
result = value
pendingOperation = nil
case .unary(let function):
if operand != nil {
result = function(operand!)
}
case .binary(let function):
if operand != nil {
pendingOperation = PendingBinaryOperation(firstOperand: operand!, operation: function)
result = nil
}
case .equal:
if pendingOperation != nil && operand != nil {
result = pendingOperation?.perform(with: operand!)
}
}
通过VStack和HStack构建基本的图形:
var body: some View {
VStack(spacing : 20) {
Text(self.data.text)
.frame(maxWidth:.infinity,maxHeight:200,alignment: .trailing)
.padding(.trailing,50)
.font(.system(size: 60))
.foregroundColor(Color("result_fg"))
.opacity(self.data.opacity)
ForEach(0..<calculatorNumber.count) { i in
HStack {
ForEach(0..<calculatorNumber[i].count ) { j in
CustomButton(calculatorNumber[i][j],self.$data)
}
}
}
}
.frame(maxWidth: .infinity,maxHeight: .infinity)
.background(Color("Color_bg"))
.edgesIgnoringSafeArea(.all)
}
进行计算器的运算,对于运算符、数字、特殊符号以及小数的处理
定义了三种不同类型的按钮:
enum CalButtonType {
case number(_ text:String)
case calOperator(_ text:String)
case calSOperator(_ text:String)
}
并对他们的颜色、处理方式进行设置。
prefix operator %
prefix operator -
prefix operator +
prefix func %(right: String) -> CalButtonType {
return .calSOperator(right)
}
prefix func -(right: String) -> CalButtonType {
return .calOperator(right)
}
prefix func +(right: String) -> CalButtonType {
return .number(right)
}
let calculatorNumber:[[CalButtonType]] = [[%"AC",%"±",%"%",-"÷"],
[+"7",+"8",+"9",-"×"],
[+"4",+"5",+"6",-"-"],
[+"1",+"2",+"3",-"+"],
[+"0",+".",-"="],]
iphone 11 Pro Max
旋转适配:
适配各种屏幕大小的iphone(与iphone8 plus对比):
适配iPad:
ipad旋转:
iphone 11 Pro Max
上图还原度还比较高,比较美观,但是要适配横屏以及各尺寸ipad,不得不进行一些修改:将图标设置为圆形,会影响横屏的效果,所以在后续测试取消了圆形。
横屏:
大尺寸iPad Pro:
可以进行加减乘除的连续运算,可以清零,取相反数,百分号运算,可以进行小数运算。
加法:
减法:
乘法:
除法:
小数:
特殊符号: