前言
这两周的任务是完成仿写计算器,要实现基本的四则运算和括号。这次的四则运算,我采取之前学习的MVC来进行仿写,在本周我完成了view层和model层的工作以及大部分controller层的工作,还剩下一些有关异常数据的处理之类的问题。那么这篇博客,就先来总结一下计算器view层和model层的仿写。
view层
view层主要就是要呈现出计算器的整个界面并实现界面控件之间与数据无关的互动,自然第一步要做的就是对所有控件布局,这里我使用Masonry布局来对布局过程进行简化。关于布局的代码及效果如下:
- (void)initTextfield {
//创建文本框
self.textField = [[UITextField alloc] init];
self.textField.frame = CGRectMake(0, 230, 352, 100);
[self.textField setText:@""];
self.textField.font = [UIFont systemFontOfSize:90];
self.textField.textColor = [UIColor whiteColor];
self.textField.textAlignment = NSTextAlignmentRight;
self.textField.adjustsFontSizeToFitWidth = YES;
self.textField.userInteractionEnabled = NO;
[self addSubview:self.textField];
}
- (void)initButton{
//创建按钮
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.button.layer.cornerRadius = SIZE / 2;
self.button.titleLabel.font = [UIFont systemFontOfSize:42];
self.button.tag = j + i * 4;
[self addSubview:self.button];
[self.button mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(self.mas_top).offset(425 + (SIZE + 20) * i);
make.left.equalTo(self).offset(20 + (SIZE + 20) * j);
make.width.equalTo(@SIZE);
make.height.equalTo(@SIZE);
}];
if (i > 0 && j < 3) {
[self.button setBackgroundColor:[UIColor colorWithRed:35/255.0 green:35/255.0 blue:35/255.0 alpha:1]];
[self.button setTitle:[self.array objectAtIndex:j + i * 3 - 3] forState:UIControlStateNormal];
[self.button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
}
if (i == 0 && j < 3) {
[self.button setBackgroundColor:[UIColor colorWithRed:170/255.0 green:170/255.0 blue:170/255.0 alpha:1]];
[self.button setTitle:[self.otherArray objectAtIndex:j] forState:UIControlStateNormal];
[self.button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
}
if (j == 3) {
[self.button setBackgroundColor:[UIColor colorWithRed:228/255.0 green:148/255.0 blue:0/255.0 alpha:1]];
[self.button setTitle:[self.actArray objectAtIndex:i] forState:UIControlStateNormal];
[self.button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
}
[self.button addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside];
}
}
self.zeroButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.zeroButton.titleLabel.font = [UIFont systemFontOfSize:42];
self.zeroButton.layer.cornerRadius = SIZE/2;
[self addSubview:self.zeroButton];
[self.zeroButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self).offset(20);
make.bottom.equalTo(self.mas_top).offset(425 + (SIZE + 20) * 4);
make.width.equalTo(@(SIZE * 2 + 20));
make.height.equalTo(@SIZE);
}];
[self.zeroButton setTitle:@"0" forState:UIControlStateNormal];
[self.zeroButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self.zeroButton setBackgroundColor:[UIColor colorWithRed:35/255.0 green:35/255.0 blue:35/255.0 alpha:1]];
[self.zeroButton addTarget:self action:@selector(clickZero) forControlEvents:UIControlEventTouchUpInside];
self.pointButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.pointButton.titleLabel.font = [UIFont systemFontOfSize:42];
self.pointButton.layer.cornerRadius = SIZE/2;
[self addSubview:self.pointButton];
[self.pointButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self).offset(20 + (SIZE + 20) * 2);
make.bottom.equalTo(self.mas_top).offset(425 + (SIZE + 20) * 4);
make.width.equalTo(@SIZE);
make.height.equalTo(@SIZE);
}];
[self.pointButton setTitle:@"." forState:UIControlStateNormal];
[self.pointButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self.pointButton setBackgroundColor:[UIColor colorWithRed:35/255.0 green:35/255.0 blue:35/255.0 alpha:1]];
[self.pointButton addTarget:self action:@selector(clickPoint) forControlEvents:UIControlEventTouchUpInside];
self.equalButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.equalButton.titleLabel.font = [UIFont systemFontOfSize:42];
self.equalButton.layer.cornerRadius = SIZE/2;
[self addSubview:self.equalButton];
[self.equalButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self).offset(20 + (SIZE + 20) * 3);
make.bottom.equalTo(self.mas_top).offset(425 + (SIZE + 20) * 4);
make.width.equalTo(@SIZE);
make.height.equalTo(@SIZE);
}];
[self.equalButton setTitle:@"=" forState:UIControlStateNormal];
[self.equalButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self.equalButton setBackgroundColor:[UIColor colorWithRed:228/255.0 green:148/255.0 blue:0/255.0 alpha:1]];
}
这样我们就实现了计算器界面的布局,但此时还只是一个只有控件,没有实现任何功能的界面。我们将与数据无关的功能在这里实现,需要将通过按钮键入的文本实时放入文本框中,再加上AC按钮的功能来实时更新文本框。对算式进行结算这种与数据相关的功能我们放到controller层去实现。
model层
我们在model层来处理数据,需要两个栈来处理我们的算式,确保有括号和四则运算的算式计算顺序的正确,有关计算的过程我们在controller层实现,所以这里我们只进行初始化、入栈、出栈等数据的预处理。另外需要注意的是:计算器里的乘号和除号是中文字符,数据类型是unichar,与加号减号不同,加号减号是char,所以如果使用数组来实现栈的话,每次处理数据之前都要先判断数据的类型,并且还要先对这两个基础类型封装,封装成NSValue类型,这样处理就会非常麻烦,所以我直接使用NSMutableString来实现栈,字符串本身其实就是字符数组。
model层的部分代码如下:
- (instancetype)init {
self = [super init];
self.operand_Stack = [NSMutableArray array];
self.operator_Stack = [[NSMutableString alloc] init];
return self;
}
- (void)pushOperandStack:(double)value {
NSNumber* number = [NSNumber numberWithDouble:value];
[self.operand_Stack addObject:number];
}
- (void)pushOperatorStack:(char)op {
[self.operator_Stack appendFormat:@"%c", op];
}
- (double)popOperandStack {
if ([self.operand_Stack count] < 0) {
NSLog(@"操作数栈为空!");
exit(1);
return 0;
} else {
double value;
value = [[self.operand_Stack lastObject] doubleValue];
[self.operand_Stack removeLastObject];
return value;
}
}
- (char)popOperatorStack {
if ([self.operator_Stack length] == 0) {
NSLog(@"操作符栈为空!");
exit(1);
return 0;
} else {
char ch = [self.operator_Stack characterAtIndex:[self.operator_Stack length] - 1];
[self.operator_Stack deleteCharactersInRange:NSMakeRange([self.operator_Stack length] - 1, 1)];
return ch;
}
}
- (int)precedenceOf:(char)op {
if (op == '+' || op == '-') {
return 1;
}
if (op == '*' || op == '/') {
return 2;
}
return 0;
}
- (double)applyOperator:(double)a And:(double)b Of:(char)op {
switch (op) {
case '+':
return a + b;
break;
case '-':
return a - b;
break;
case '*':
return a * b;
break;
case '/':
return a/b;
break;
default:
NSLog(@"存在非法符号!");
return 0;
break;
}
}
- (void)performOperation {
char op = [self popOperatorStack];
double val2 = [self popOperandStack];
double val1 = [self popOperandStack];
[self pushOperandStack:[self applyOperator:val1 And:val2 Of:op]];
}
总结
这篇博客总结了计算器的view层和model层,现在只需要在controller层实现对算式的结算即可,同样controller层里的算法也是整个计算器最麻烦的,现在已经完成了基本的运算,接下来再增加一些对异常数据的处理,可能会用到消息传值,之后会再有一篇博客总结。