前言
这次仿写计算器是第一次使用MVC设计模式写一个小demo,对于MVC设计模式可见这篇博客 【iOS】MVC设计模式。也是第一次使用Masonry
库进行view的页面布局,关于Masonry
的使用可见这篇博客【iOS】Masonry的简单使用。其次,关于计算器中对字符串处理、计算等逻辑问题主要用到了栈的思想,关于c语言中四则运算可见这篇博客 用栈实现四则运算。虽然是用Objective-C
写,但是在OC中可以调用许多方法处理,可以更方便。
使用Masonry在View中布局
- 关于整齐排列的16个按键可以使用循环创建
button
,只需使用不同tag值在后面调用指定的button
,最下面的三个按键就可以分别单独创建button
- 在使用
Masonry
约束时,注意不可约束条件过多造成冲突,我是使用了bottom
、left
、width
、height
四个条件来进行约束的,width
和height
因为每个按键形状大小都一样所以都是固定值,而bottom
和left
要根据每一行每一列进行计算
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
_baseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_baseButton.titleLabel.font = [UIFont systemFontOfSize:42];
_baseButton.layer.cornerRadius = SIZE / 2;
[_baseButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];
_baseButton.tag = 204 + j + i * 4;
[self addSubview:_baseButton];
[_baseButton mas_makeConstraints:^(MASConstraintMaker* make) {
make.bottom.equalTo(self).offset(-(90 + (SIZE + 15) * (i + 1)));
make.left.equalTo(self).offset(15 + (SIZE + 15) * j);
make.width.equalTo(@SIZE);
make.height.equalTo(@SIZE);
}];
if (j < 3) {
if (i < 3) {
[_baseButton setBackgroundColor:[UIColor colorWithWhite:0.15 alpha:1]];
[_baseButton setTitle:[NSString stringWithFormat:@"%d", i * 3 + j + 1] forState:UIControlStateNormal];
[_baseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
} else {
[_baseButton setBackgroundColor:[UIColor lightGrayColor]];
[_baseButton setTitle:grayArray[j] forState:UIControlStateNormal];
[_baseButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
}
} else {
[_baseButton setBackgroundColor:[UIColor colorWithRed:0.9 green:0.58 blue:0 alpha:1]];
[_baseButton setTitle:orangeArray[i] forState:UIControlStateNormal];
[_baseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
}
}
}
效果如下:
判错处理
- 在按键的同时进行判定,如果表达式有误则赋给一个
flag
属性为1,最后在点击 “=” 时再进行判定,如果flag==1
则表达式有误,屏幕上就会显示错误提示,不再进行计算。 - 对除法式子,分母为0会报错;括号匹配不当会报错;运算符相连输入会报错等等
- (BOOL) isSymbolJudgeFlag:(NSString*)first :(NSString*)second {
int firstFlag = 0;
int secondFlag = 0;
if ([first isEqualToString:@"+"] || [first isEqualToString:@"-"] || [first isEqualToString:@"×"] || [first isEqualToString:@"÷"]) {
firstFlag = 1;
}
if ([second isEqualToString:@"+"] || [second isEqualToString:@"-"] || [second isEqualToString:@"×"] || [second isEqualToString:@"÷"]) {
secondFlag = 1;
}
if ([first isEqualToString:@"÷"] && [second isEqualToString:@"0"]) {
return true;
}
if ([first isEqualToString:@"("] && [second isEqualToString:@")"]) {
return true;
}
if ([first isEqualToString:@"("] && ([second isEqualToString:@"+"] || [second isEqualToString:@"-"] || [second isEqualToString:@"×"] || [second isEqualToString:@"÷"])) {
return true;
}
if ([second isEqualToString:@"("] && (![first isEqualToString:@"+"] && ![first isEqualToString:@"-"] && ![first isEqualToString:@"×"] && ![first isEqualToString:@"÷"] && ![first isEqualToString:@""])) {
return true;
}
if (firstFlag == 1 && secondFlag == 1) {
return true;
} else {
return false;
}
}
数据处理
- 根据MVC设计模式的要求,数据处理基本都在Model里完成
- 将输入的表达式
NSString
类型传到model
中,循环遍历每个字符进行运算数栈和运算符栈的存储,此处的栈使用NSMutable
数组。 - 将NSString类型转换成double类型使用
double temp = [_tempNumberString doubleValue]
方法 - 删除结果多余的零时使用下面这个方法函数,传进的参数是
NSString
类型,返回的也是NSString
类型
- (NSString*)removeFloatAllZeroByString:(NSString *)testNumber {
NSString * outNumber = [NSString stringWithFormat:@"%@",@(testNumber.floatValue)];
return outNumber;
}
- 使用栈的具体操作如下
- (NSString*)getAnswer {
_numberStack = [[NSMutableArray alloc] init];
_symbolStack = [[NSMutableArray alloc] init];
for (int i = 0; i < _printString.length;) {
char tempChar = [_printString characterAtIndex:i];
NSString *tempString =[[NSString alloc] initWithFormat:@"%c",tempChar];
if (![self isSymbol:tempString]) {
_tempNumberString = [[NSString alloc] init];
while (![self isSymbol:tempString]) {
_tempNumberString = [_tempNumberString stringByAppendingString:tempString];
i++;
char tempChar = [_printString characterAtIndex:i];
tempString =[[NSString alloc] initWithFormat:@"%c",tempChar];
}
double temp = [_tempNumberString doubleValue];
[_numberStack addObject:[NSNumber numberWithDouble:temp]];
} else {
if ([[_symbolStack lastObject] isEqualToString:@"("]) {
if ([_printString characterAtIndex:i] == ')') {
[_symbolStack removeLastObject];
} else {
char tempChar = [_printString characterAtIndex:i];
NSString *tempString =[[NSString alloc] initWithFormat:@"%c",tempChar];
[_symbolStack addObject:tempString];
}
i++;
} else {
char tempChar = [_printString characterAtIndex:i];
NSString *tempString =[[NSString alloc] initWithFormat:@"%c",tempChar];
if (![self precede:[_symbolStack lastObject] :tempString]) {
[_symbolStack addObject:tempString];
i++;
} else {
NSNumber* bTemp = [_numberStack lastObject];
double b = [bTemp doubleValue];
[_numberStack removeLastObject];
NSNumber* aTemp = [_numberStack lastObject];
double a = [aTemp doubleValue];
[_numberStack removeLastObject];
NSString* s = [_symbolStack lastObject];
[_symbolStack removeLastObject];
double temp = [self calculation:a :b :s];
[_numberStack addObject:[NSNumber numberWithDouble:temp]];
if ([_printString characterAtIndex:i] == '#' && _symbolStack.count == 0) {
i++;
}
}
}
}
}
double answerNumber = [[_numberStack lastObject] doubleValue];
_answer = [self switchDoubleToNSStringWithoutZero:answerNumber];
_answer = [self removeFloatAllZeroByString:_answer];
return _answer;
}