计算器的大概思路
首先需要满足一些基本要求,即使用MVC模式写代码,这里只有一个界面,所以没有设置多余的控制器 ;且代码的View要用到之前学过的Masonry布局 ;
view
在MVC中view的功能就是获取Ui界面设计的视图对象
UI:
因为编译器不小心更新了,虚拟机宽高也变了,所以可能布局看起来有点问题 ;
view 部分大概为一个textfield和多个按钮 ;且在MVC模式下,应该保证在controller里面任然可以访问到view中的视图对象,所以这里的多个按钮我们选择循环创建,但要存入一个数组中去(不然设多个属性代码量太大) ;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 4; j++) {
NSString* str = self.buttonarray[j + i * 4] ;
if (i == 0 && j < 3) {
UIButton* btn = [UIButton buttonWithType:UIButtonTypeCustom] ;
[btn setTitle:str forState:UIControlStateNormal] ;
btn.titleLabel.font = [UIFont systemFontOfSize:20] ;
[self addSubview:btn] ;
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(30 + j * 90) ;
make.top.mas_equalTo(320 + i * 90) ;
make.width.mas_equalTo(75) ;
make.height.mas_equalTo(75) ;
}];
btn.layer.cornerRadius = 75 /2 ;
btn.layer.masksToBounds = YES ;
btn.layer.borderWidth = 2.0 ;
[btn setTitleColor:UIColor.whiteColor forState:UIControlStateNormal] ;
btn.backgroundColor = UIColor.grayColor ;
btn.tag = j + i * 4 ;
[self.btnarray addObject:btn] ;
} else if (i != 0 && j < 3) {
if (i == 4 && j == 0) {
UIButton* btn = [UIButton buttonWithType:UIButtonTypeCustom] ;
[btn setTitle:str forState:UIControlStateNormal] ;
btn.titleLabel.font = [UIFont systemFontOfSize:20] ;
[self addSubview:btn] ;
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(30 + j * 90) ;
make.top.mas_equalTo(320 + i * 90) ;
make.width.mas_equalTo(150) ;
make.height.mas_equalTo(75) ;
}];
btn.layer.cornerRadius = 75/2 ;
btn.layer.masksToBounds = YES ;
btn.layer.borderWidth = 2.0 ;
[btn setTitleColor:UIColor.whiteColor forState:UIControlStateNormal] ;
btn.backgroundColor = UIColor.darkGrayColor ;
btn.tag = j + i * 4 ;
[self.btnarray addObject:btn] ;
j++ ;
} else {
UIButton* btn = [UIButton buttonWithType:UIButtonTypeCustom] ;
[btn setTitle:str forState:UIControlStateNormal] ;
btn.titleLabel.font = [UIFont systemFontOfSize:20] ;
[self addSubview:btn] ;
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(30 + j * 90) ;
make.top.mas_equalTo(320 + i * 90) ;
make.width.mas_equalTo(75) ;
make.height.mas_equalTo(75) ;
}];
btn.layer.cornerRadius = 75/2 ;
btn.layer.masksToBounds = YES ;
btn.layer.borderWidth = 2.0 ;
[btn setTitleColor:UIColor.whiteColor forState:UIControlStateNormal] ;
btn.backgroundColor = UIColor.grayColor ;
btn.tag = j + i * 4 ;
[self.btnarray addObject:btn] ;
}
} else {
UIButton* btn = [UIButton buttonWithType:UIButtonTypeCustom] ;
[btn setTitle:str forState:UIControlStateNormal] ;
btn.titleLabel.font = [UIFont systemFontOfSize:20] ;
[self addSubview:btn] ;
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(30 + j * 90) ;
make.top.mas_equalTo(320 + i * 90) ;
make.width.mas_equalTo(75) ;
make.height.mas_equalTo(75) ;
}];
btn.layer.cornerRadius = 75 / 2 ;
btn.layer.masksToBounds = YES ;
btn.layer.borderWidth = 2.0 ;
[btn setTitleColor:UIColor.whiteColor forState:UIControlStateNormal] ;
btn.backgroundColor = UIColor.orangeColor ;
btn.tag = j + i * 4 ;
[self.btnarray addObject:btn] ;
}
}
}
注意上面按钮的布局我们使用了第三方库masonry为button对象设置限制来实现位置布局,可以比较方便的设置更多功能,比如自适应布局(不过我这里完全没体现就是了) ;
还有就是设置圆按钮的方法 ;
btn.layer.cornerRadius = 75 /2 ;//设置弧度,圆形即为长宽的一半 ;
btn.layer.masksToBounds = YES ;
btn.layer.borderWidth = 2.0 ;//设置边框厚度
Model
@interface calculatorModel : NSObject
@property (nonatomic, strong) NSString* stackstr ;
@property (nonatomic, strong) NSMutableArray* tagarray ;
- (void)initModel ;
@end
@implementation calculatorModel
- (void)initModel {
self.stackstr = [[NSString alloc] init] ;
self.tagarray = [NSMutableArray arrayWithObjects:@"AC", @"(", @")", @"/", @"7", @"8", @"9", @"*", @"4", @"5", @"6", @"-", @"1", @"2", @"3", @"+", @"0", @"NO", @".", @"=", nil] ;
}
@end
这里我设置了两个数组,一个用来存储按钮输入的字符串,一个用来存储按钮本身的值,(我看学长有的用tag值来实现传值,但我感觉那样不太符合MVc模式的理念,我认为可以应该将view中的对象能够在controller全部访问,model则用于数据存储) ;
controller
controller中应完成用户交互(点击函数)和数据处理(计算) ;
先看看数据处理
计算的原理:
1.将中缀表达式转化为后缀表达式 ;
2.进行后缀表达式的运算 ;
这两步可以看看我之前的博客,这里就补充一下负数的计算 ;
else if (str[i] == '(') {
if (str[i + 1] == '-') {
str[i + 1] = '!' ;
}
stack02[++top02] = str[i++] ;
//将左括号前的减号改为负号,设置为!
if (str[0] == '-') {
str[0] = '!' ;
}//特殊情况,即开头减号为负号
else if (stack01[i] == '!') {
double b = numstack[top03--] ;
double c = -b ;
numstack[++top03] = c ;
i = i +2 ;
} //负号的计算
点击函数
- (void)presstap : (UIButton*) button {
NSLog(@"%ld", button.tag) ;
NSInteger index = button.tag ;
if (index != 0 && index != 19) {
NSString* str = self.mModel.tagarray[index] ;
self.mModel.stackstr = [self.mModel.stackstr stringByAppendingString:str] ;
self.mView.textfield.text = self.mModel.stackstr ;
// NSLog(@"%@",self.mModel.stackstr) ;
} else if (index == 0) {
self.mModel.stackstr = [[NSString alloc] init] ;
self.mView.textfield.text = self.mModel.stackstr ;
} else {
if (self.mModel.stackstr.length != 0 && [self juicerror:self.mModel.stackstr]) {
const char* Cstr = [self.mModel.stackstr UTF8String] ;
NSLog(@"%s",Cstr) ;
NSLog(@"%lf",[self calculatepress:Cstr]) ;
double x = [self calculatepress:Cstr] ;
NSNumber* number = [NSNumber numberWithDouble:x] ;
NSNumberFormatter* numberformatter = [[NSNumberFormatter alloc] init] ;
numberformatter.numberStyle = NSNumberFormatterDecimalStyle ;
numberformatter.minimumFractionDigits = 0 ;
numberformatter.maximumFractionDigits = 8 ;
numberformatter.minimumIntegerDigits = 1 ;
self.mModel.stackstr = [numberformatter stringFromNumber:number] ;
// self.mModel.stackstr = [NSString stringWithFormat:@"%lf",x] ;
self.mView.textfield.text = self.mModel.stackstr ;
} else {
self.mModel.stackstr = @"error" ;
self.mView.textfield.text = self.mModel.stackstr ;
}
}
}
其中这一段代码实现了除去多余无用的0 ;
NSNumber* number = [NSNumber numberWithDouble:x] ;
NSNumberFormatter* numberformatter = [[NSNumberFormatter alloc] init] ;
numberformatter.numberStyle = NSNumberFormatterDecimalStyle ;
numberformatter.minimumFractionDigits = 0 ;
numberformatter.maximumFractionDigits = 8 ;
numberformatter.minimumIntegerDigits = 1 ;
self.mModel.stackstr = [numberformatter stringFromNumber:number] ;
可以去了解一下这类对象的用法 ;
if (self.mModel.stackstr.length != 0 && [self juicerror:self.mModel.stackstr])
这里有输入字符串的判错,这也是计算器最麻烦的一部分 ;
以下是我的判错函数,
在这里插入代码片int ordersort (char a) {
if (a == '(' || a == ')') {
return 0;
} else if (a == '+' || a == '-') {
return 1;
} else if (a == '*' || a == '/') {
return 2;
} else if (a == '.') {
return 4;
} else if (a == '!') {
return 3;
} else {
return -1;
}
}//判断优先级大小
int matejuice (const char* s) {//匹配括号
int i = 0 ;
int length = (int)strlen(s) ;
int top01 = -1 ;
while (i < length) {
if (s[i] == '(') {
top01++ ;
}
if (s[i] == ')') {
top01-- ;
}
i++ ;
}
if (top01 == -1) {
return 1;
} else {
return 0;
}
}
int calcul_juice (const char* strs) { //判断运算符和数字是否匹配
char str[1000] ;
strcpy(str, strs) ;
char stack01[1000] ;
int top01 = -1 ;
char stack02[1000] ;
int top02 = -1 ;
int i = 0;//初始化一个数字栈和一个符号栈
int len = (int)strlen(str) ;
if (str[0] == '-') {
str[0] = '!' ;
}
// int flag01 = 1 ;
// int flag02 = 1 ;
// double e = 0 ;
while (i < len) {//遍历输入的字符串,查找数字和符号
printf("badhyfvjwf %d",top02) ;
if (str[i] >= '0' && str[i] <= '9') {
stack01[++top01] = str[i++] ;//遇到数字就将数字压入数字栈中
if (str[i] <'0' || str[i] > '9') {
stack01[++top01] = ' ' ;//在每一个数字和运算符之间添加一个空格使它们分隔开来,不然多位数运算会多个数字混在一起 ;
}
} else if (str[i] == '(') {
if (str[i + 1] == '-') {
str[i + 1] = '!' ;
}
stack02[++top02] = str[i++] ;
} else if (str[i] == ')') {
while (stack02[top02] != '(') {
stack01[++top01] = stack02[top02--] ;
stack01[++top01] = ' ' ;//左右括号的优先级比较特熟,所以在判断符号优先级级前提前判断左右括号,左括号直接压入符号栈,右括号则对符号栈弹栈,并将弹出的符号压入数字栈,直到栈顶为左括号;
}
top02-- ;
i++ ;
} else {
if (ordersort(str[i]) > ordersort(stack02[top02]) || top02 == -1) {
stack02[++top02] = str[i++] ;//遇到四则运算符时,比较符号和栈顶的元素的优先级,优先级比栈顶高直接压入符号栈,否则直接弹出栈顶元素压入数字栈,直到栈顶元素优先级低于。。
} else {
while (ordersort(stack02[top02]) >= ordersort(str[i]) && top02 != -1) {
stack01[++top01] = stack02[top02--] ;
stack01[++top01] = ' ' ;
}
stack02[++top02] = str[i++] ;
}
}
}
while (top02 != -1) {
stack01[++top01] = stack02[top02--] ;
stack01[++top01] = ' ' ;
}//遍历完后弹出所有的符号栈元素压入数字栈
stack01[++top01] = '\0' ;
//获得后缀表达式
int top03 = -1 ;//初始化一个栈来存储数字
i = 0;
double e = 0 ;
while (i < top01) {
if (stack01[i] >= '0' && stack01[i] <= '9') {
while (stack01[i] >= '0' && stack01[i] <= '9') {
e = e* 10 + stack01[i] - '0' ;
i++ ;//获取多位数
}
top03++ ;
e = 0 ;
i++ ;
} else if (stack01[i] == '!') {
i = i + 2 ;
} else {
top03-- ;
i = i + 2 ;
}
}
if (top03 == 0) {
return 1;
} else {
return 0;
}
}
int pots_juice ( char* str) {
printf("%s",str) ;
int length = (int)strlen(str) ;
int i = 0;
int flag = 0 ;
while (i < length) {
if (str[i] == '.') {
flag++ ;
}
i++ ;
}
if (flag >= 2) {
return 0;
} else {
return 1;
}
}
int pot_juice (const char* strs) {
char str[100] ;
strcpy(str, strs) ;
const char s[6] = "+-*/()" ;
char* token ;
token = strtok(str, s) ;
// printf("%s",token) ;
if (pots_juice(token) == 0) {
return 0;
}
while (token != NULL) {
token = strtok(NULL, s) ;
if (token == NULL) {
break;
}
if (pots_juice(token) == 0) {
return 0;
}
}
return 1;
}
- (int) juicerror : (NSString*) str { //判错
const char* Cstr = [self.mModel.stackstr UTF8String] ;
NSLog(@"%s",Cstr) ;
if (ordersort(Cstr[0]) >= 1 && ordersort(Cstr[0]) <= 4) {//判断第一个字符是不是二元运算符
if (Cstr[0] != '-') {
printf("error01") ;
return 0;
}
}
if (Cstr[0] == ')') {
printf("error02") ;
return 0;
}
if ([[NSString stringWithFormat:@"%lf",[self calculatepress:Cstr]] isEqualToString:@"inf"] ) {//除法运算分母不为零(即运算结果不能为无穷)
printf("error03") ;
return 0;
}
if (matejuice(Cstr) == 0) {//括号要匹配
printf("error04") ;
return 0;
}
if (calcul_juice(Cstr) == 0) {
printf("error05") ;
return 0;
}
if (pot_juice(Cstr) == 0) {
printf("error06") ;
return 0;
}
if (lastjuice(Cstr) == 0) {
return 0;
}
int i = 0 ;
while (i < (int)strlen(Cstr) - 1) {
if (ordersort(Cstr[i]) >= 1 && ordersort(Cstr[i]) <= 4) {
if (ordersort(Cstr[i + 1]) >= 1 && ordersort(Cstr[i + 1]) <= 4) {
return 0;
}
}
if (Cstr[i] == '(') {
if (ordersort(Cstr[i + 1]) >= 1 && ordersort(Cstr[i + 1]) <= 3) {
return 0;
}
}
i++ ;
}
return 1;
}
总结
注意好MvC的各自的功能,
判错是重中之重 ;