在完成这次仿写任务时,和以前所写的项目最大的区别时首次运用到了MVC模式和Masonry界面,并且在计算器使用的过程中运用了很多的算法知识,在整个过程中会出现特别多的error,以及很神奇的错误。
利用Masonry布局,保证这个比例在每个手机上都能正确显示,在创建按钮的时候利用循环创建,把0这个大按钮单独拿出来创建,代码如下:
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
self.backgroundColor = [UIColor blackColor];
_firstarray = [NSArray arrayWithObjects:@"0", @"0", @"0", @".", @"=", @"1", @"2", @"3", @"+", @"4", @"5", @"6" , @"-", @"7", @"8", @"9", @"*", @"AC", @"(", @")", @"/", nil];
_label = [[UILabel alloc] init];
_label.backgroundColor = [UIColor blackColor];
_label.textColor = [UIColor whiteColor];
_label.textAlignment = NSTextAlignmentRight;
_label.text = @"0";
_label.font = [UIFont systemFontOfSize:0.15 * Width];
[self addSubview:_label];
[_label mas_makeConstraints:^(MASConstraintMaker* make) {
make.bottom.equalTo(self).offset(-((smallWidth + 13) * (5 + 1)) + 20);
make.left.equalTo(self).offset(13 + (smallWidth + 13) * 0);
make.width.equalTo(@(Width - 26));
make.height.equalTo(@smallWidth);
}];
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 4; j++) {
_smallbutton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_smallbutton.titleLabel.font = [UIFont systemFontOfSize:0.115 * Width];
_smallbutton.tintColor = [UIColor whiteColor];
if (i == 4 && j <= 2) {
_smallbutton.titleLabel.font = [UIFont systemFontOfSize:Width * 0.1];
_smallbutton.tintColor = [UIColor blackColor];
}
_smallbutton.layer.cornerRadius = smallWidth / 2;
[self addSubview:_smallbutton];
if ((i == 0 && j == 0) || (i == 0 && j == 1)) {
continue;
} else {
_smallbutton.tag = i * 4 + j + 1;
[_smallbutton mas_makeConstraints:^(MASConstraintMaker* make) {
make.bottom.equalTo(self).offset(-((smallWidth + 13) * (i + 1)) + 40);
make.left.equalTo(self).offset(13 + (smallWidth + 13) * j);
make.width.equalTo(@smallWidth);
make.height.equalTo(@smallWidth);
}];
}
if (i >= 0 && i < 4 && j < 3) {
[_smallbutton setBackgroundColor:[UIColor colorWithRed:(85.0f / 256.0f) green:(85.0f / 256.0f) blue:(85.0f / 256.0f) alpha:1]];
} else if (j == 3) {
[_smallbutton setBackgroundColor:[UIColor colorWithRed:(239.0f / 256.0f) green:(143.0f / 256.0f) blue:(50.0f / 256.0f) alpha:1]];
} else {
[_smallbutton setBackgroundColor:[UIColor colorWithRed:(170.0f / 256.0f) green:(170.0f / 256.0f) blue:(170.0f / 256.0f) alpha:1]];
}
[_smallbutton setTitle:_firstarray[_smallbutton.tag] forState:UIControlStateNormal];
[_smallbutton addTarget:self action:@selector(buttonreturn:) forControlEvents:UIControlEventTouchUpInside];
}
}
_smallbutton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_smallbutton.layer.cornerRadius = smallWidth / 2;
_smallbutton.tintColor = [UIColor whiteColor];
[self addSubview:_smallbutton];
_smallbutton.tag = 1;
[_smallbutton mas_makeConstraints:^(MASConstraintMaker* make) {
make.bottom.equalTo(self).offset(-((smallWidth + 13) * (0 + 1)) + 40);
make.left.equalTo(self).offset(13 + (smallWidth + 13) * 0);
make.width.equalTo(@(smallWidth * 2 + 13));
make.height.equalTo(@smallWidth);
}];
[_smallbutton setBackgroundColor:[UIColor colorWithRed:(85.0f / 256.0f) green:(85.0f / 256.0f) blue:(85.0f / 256.0f) alpha:1]];
_smallbutton.titleLabel.font = [UIFont systemFontOfSize:0.115 * Width];
[_smallbutton setTitle:_firstarray[_smallbutton.tag] forState:UIControlStateNormal];
[_smallbutton addTarget:self action:@selector(buttonreturn:) forControlEvents:UIControlEventTouchUpInside];
return self;
}
在完成了按钮以及label的初始化后,View的工作就基本完成了,也就是最简单的一步完成,在完成按钮后,就要为按钮添加点击事件,这里一定要注意的是,按钮的点击事件在MVC模式里,是不允许添加在View里的,要将点击事件添加在Controller里。这样的话就一定要给每个button赋tag值,然后将button传值到controller里面,在controller里面完成事件函数。
在控制计算器输入合理的过程中,我在Controller里控制输入的时候就有些限制,还有一些弹出错误放在了Modal里,首先控制小数点在一个数值中只能出现一次,记录小数点状态变了之后,再点击就没有事件,还有防止连续输入符号,和小数点的控制方式类似。
很重要的一点是括号问题,括号只能先左再右,就令一个变量,记录左括号的数量,当输入左括号时,该变量++,输入右括号时,该变量–,当该变量为0时,不能输入右括号。
// 记录小数点状态
int pointFlag = 0;
// 防止连续输入符号
int secondFlag = 0;
// 记录等于号状态
int equalFlag = 0;
// 记录数字后再输入小数点
int numpoint = 0;
//记录左括号数量
int leftbrackets = 0;
- (void)Chubutton:(UIButton *)button {
//NSLog(@"%ld",(long)button.tag);
if (button.tag == 17) { //AC
//[_MainArray removeAllObjects];
_MainString = nil;
_MainString = [[NSMutableString alloc] init];
self.mainview.label.text = @"0";
pointFlag = 0;
secondFlag = 0;
equalFlag = 0;
leftbrackets = 0;
} else if (button.tag == 1) { //0
// [_MainArray addObject:button.titleLabel.text];
[_MainString appendString:button.titleLabel.text];
self.mainview.label.text = _MainString;
secondFlag = 0;
numpoint = 0;
} else if ((button.tag <= 7 && button.tag >= 5) || (button.tag <= 11 && button.tag >= 9) || (button.tag <= 15 && button.tag >= 13)) { //9个数字
//[_MainArray addObject:button.titleLabel.text];
[_MainString appendString:button.titleLabel.text];
self.mainview.label.text = _MainString;
secondFlag = 0;
numpoint = 0;
} else if (button.tag == 3) { //小数点
if (numpoint == 0 && pointFlag == 0) {
//[_MainArray addObject:button.titleLabel.text];
[_MainString appendString:button.titleLabel.text];
self.mainview.label.text = _MainString;
pointFlag = 1;
} else {
}
} else if (button.tag == 20 || button.tag == 8 || button.tag == 12 || button.tag == 16) { //四个运算符
if (secondFlag == 0) {
pointFlag = 0;
secondFlag = 1;
numpoint = 1;
//[_MainArray addObject:button.titleLabel.text];
[_MainString appendString:button.titleLabel.text];
self.mainview.label.text = _MainString;
} else {
}
} else if (button.tag == 4) { //等号
//[_MainArray addObject:button.titleLabel.text];
//[_MainString appendString:button.titleLabel.text];
self.mainview.label.text = _MainString;
[_MainString replaceOccurrencesOfString:@")(" withString:@")*(" options:NSLiteralSearch range:NSMakeRange(0, [_MainString length])];
const char *cstring = [_MainString UTF8String];
NSMutableString *newstring = [NSMutableString stringWithFormat:@"%@", [self.mainmodel transform:cstring]];
NSLog(@"%@",newstring);
if ([newstring isEqualToString:@"error"] || [newstring isEqualToString:@"inf"]) {
newstring = [NSMutableString stringWithFormat:@"error"];
} else {
newstring = [NSMutableString stringWithFormat:@"%@", @(newstring.floatValue)];
}
NSLog(@"%@",newstring);
self.mainview.label.text = newstring;
_MainString = nil;
_MainString = [[NSMutableString alloc] init];
if ([newstring isEqualToString:@"error"] || [newstring isEqualToString:@"inf"]) {
} else {
[_MainString appendString:newstring];
}
pointFlag = 0;
secondFlag = 0;
equalFlag = 0;
leftbrackets = 0;
} else if (button.tag == 18) { //左
// [_MainArray addObject:button.titleLabel.text];
leftbrackets++;
[_MainString appendString:button.titleLabel.text];
self.mainview.label.text = _MainString;
secondFlag = 0;
} else if (button.tag == 19) { //右
//[_MainArray addObject:button.titleLabel.text];
if (leftbrackets != 0) {
[_MainString appendString:button.titleLabel.text];
self.mainview.label.text = _MainString;
leftbrackets--;
} else {
}
}
}
这里用到的方法将oc字符串转为c字符串,便于对每一个字符的遍历。
const char *cstring = [_MainString UTF8String];
Model部分的代码是最难的,在学习之前得先搞懂用栈如何进行四则运算,可以看之前博客:c语言四则运算
除此之外,在计算中会遇到括号,这时候就需要把中缀表达式转化为后缀表达式,然后再进行计算,思想参考大佬博客
但是还需要注意,上述的方法只能进行整数的运算,在写完计算器之后我才意识到这个问题,其实只需要把int类型转化为double类型,然后在读取的时候,遇到小数点,后面的数值✖️0.1的位数次方,然后加进去即可。
在model端也要进行很多controller端没有进行完的判错,其中包括左括号前不能直接接数字,右括号后不能直接接数字,左右括号数量不相等等问题。
代码:
- (double)Count:(double)x :(char)ch :(double)y {
if (ch == '+')
return x + y;
else if(ch == '-')
return x - y;
else if(ch == '*')
return 1.0 * x * y;
else if(ch == '/')
return 1.0 * x / y;
else
return -1;
}
- (NSString *)transform:(const char *)arr {
int i;
if (arr[strlen(arr) - 1] == '+' || arr[strlen(arr) - 1] == '-' || arr[strlen(arr) - 1] == '*' || arr[strlen(arr) - 1] == '/') {
NSString *errorstring = [NSString stringWithFormat:@"error"];
return errorstring;
}
int leftbrackets = 0, rightbrackets = 0;
for (i = 0; i < strlen(arr); i++) {
if ((arr[i] == '(' && (arr[i + 1] == '*' || arr[i + 1] == '/')) || ((arr[i] == ')') && ((arr[i - 1] == '+') || (arr[i - 1] == '-') || (arr[i - 1] == '*') || (arr[i - 1] == '/')))) {
NSString *errorstring = [NSString stringWithFormat:@"error"];
return errorstring;
}
if ((arr[i] == '(' && (arr[i - 1] <= '9' && arr[i - 1] >= '0'))||(arr[i] == ')' && (arr[i + 1] <= '9' && arr[i + 1] >= '0'))) {
NSString *errorstring = [NSString stringWithFormat:@"error"];
return errorstring;
}
if (arr[i] == '(') {
leftbrackets++;
} else if (arr[i] == ')') {
rightbrackets++;
}
}
if (leftbrackets != rightbrackets) {
NSString *errorstring = [NSString stringWithFormat:@"error"];
return errorstring;
}
double x = 0.0;
int flag = 0;
int f = 0;
double numStack[1000];
int numTop = -1;
char symbolStack[100];
int symbolTop = -1;
int dian = 0;
for (i = 0; i < strlen(arr); i++) {
if ((arr[i] >= '0' && arr[i] <= '9') || arr[i] == '.') {
if (arr[i] == '.') {
dian = 1;
continue;
}
if (dian == 0) {
x = x * 10 + (arr[i] - '0');
flag = 1;
} else {
x = x + (arr[i] - '0') * pow(0.1, dian);
//flag = 1;
dian++;
}
} else {
if (flag == 1) {
if (f == 1) {
x = (-1) * x;
f = 0;
}
numStack[++numTop] = x;
//NSLog(@"%lf niubi",x);
x = 0;
flag = 0;
}
if (arr[i] == '(' || arr[i] == '*' || arr[i] == '/') {
if ((arr[i] == '*' || arr[i] == '/') && (symbolStack[symbolTop] == '*' || symbolStack[symbolTop] == '/')) {
numStack[numTop-1] = [self Count:numStack[numTop-1] :symbolStack[symbolTop] :numStack[numTop]];
numTop--;
symbolTop--;
}
symbolStack[++symbolTop] = arr[i];
}
if (arr[i] == ')') {
while (symbolStack[symbolTop] != '(') {
numStack[numTop - 1] = [self Count:numStack[numTop - 1] :symbolStack[symbolTop] :numStack[numTop]];
numTop--;
symbolTop--;
}
if (symbolStack[symbolTop] == '(') {
symbolTop = symbolTop - 1;
}
}
if (arr[i] == '+' || (arr[i] == '-' && arr[i - 1] != '(')) {
if ((arr[i] == '+' || arr[i] == '-') && (symbolStack[symbolTop] == '+' || symbolStack[symbolTop] == '-')) {
numStack[numTop-1] = [self Count:numStack[numTop-1] :symbolStack[symbolTop] :numStack[numTop]];
numTop--;
symbolTop--;
}
if ((symbolStack[symbolTop] != '*' && symbolStack[symbolTop]!='/') || symbolTop == -1) {
symbolStack[++symbolTop] = arr[i];
} else {
numStack[numTop - 1] = [self Count:numStack[numTop-1] :symbolStack[symbolTop] :numStack[numTop]];
NSLog(@"%f %c %f",numStack[numTop-1] ,symbolStack[symbolTop] ,numStack[numTop]);
numTop--;
symbolTop--;
i = i - 1;
}
}
if (arr[i] == '-' && arr[i-1] == '(') {
f = 1;
}
dian = 0;
}
}
if (arr[strlen(arr)-1] >= '0' && arr[strlen(arr)-1] <= '9') {
numStack[++numTop]=x;
}
while (symbolTop != -1) {
numStack[numTop - 1] = [self Count:numStack[numTop-1] :symbolStack[symbolTop] :numStack[numTop]];
//NSLog(@"%f %c %f %d %d",numStack[numTop-1] ,symbolStack[symbolTop] ,numStack[numTop],numTop,symbolTop);
numTop--;
symbolTop--;
}
NSString *string = [NSString stringWithFormat:@"%.6f", numStack[numTop]];
//NSLog(@"%@",string);
return string;
}
在完成项目后遇到了一个问题,当计算结果为9.3或9.2的时候,会显示如下:
其中我用到了doubleValue方法来去掉结果后面多余的0,结果发现,在运行完这一代码后不但没有去掉后面的0,而且在数值发生了改变,最后在测试中发现是单精度和双精度的问题,将doubleValue改成floatValue即可。