仿写iOS的计算器,使用了MVC模式和masonry库。
绘制界面
- 建立一个button的循环,统一设置属性,添加约束。
- 将括号按钮的tag设成100+,运算符按钮的tag设成200+,数字按钮的tag设成300+,用于之后分类。
- 设置不同按钮的颜色。
- 在按钮上方添加一个UITextField,关掉它的用户交互,并打开自调整宽度。
- 单独建立AC,小数点,等于号的按钮。
下面是计算器的界面:
Calculate_View.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface Calculate_View : UIView
@property (nonatomic, strong) NSMutableArray* butArray;
@property (nonatomic, strong) UITextField* Caltextfiled;
@property (nonatomic, strong) UIButton* ACBut;
@property (nonatomic, strong) UIButton* pointBut;
@property (nonatomic, strong) UIButton* equalBut;
- (void) addUI;
@end
Calculate_View.m
#import "Calculate_View.h"
#import "Masonry.h"
#define Maxsize 85
#define Width 375
@implementation Calculate_View
- (void) addUI {
NSArray* arr1 = [NSArray arrayWithObjects: @"AC", @"(", @")", @"/", @"*", @"-", @"+", @"=", nil];
self.backgroundColor = UIColor.blackColor;
self.butArray = [[NSMutableArray alloc] init];
self.Caltextfiled = [[UITextField alloc] init];
self.Caltextfiled.backgroundColor = UIColor.blackColor;
self.Caltextfiled.textColor = UIColor.whiteColor;
self.Caltextfiled.textAlignment = NSTextAlignmentRight;
self.Caltextfiled.font = [UIFont systemFontOfSize:60];
self.Caltextfiled.userInteractionEnabled = NO;
self.Caltextfiled.adjustsFontSizeToFitWidth = YES;
//self.Caltextfiled.text = @"0";
[self addSubview:self.Caltextfiled];
[self.Caltextfiled mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(self).offset(-600);
make.width.equalTo(@Width);
}];
for (int i = 0; i< 4; i++) {
for (int j = 0;j < 4; j++) {
UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.layer.cornerRadius = Maxsize/2;
button.titleLabel.font = [UIFont systemFontOfSize:42];
[self addSubview:button];
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(self).offset(-(70 + (Maxsize + 15)* (i + 1)));
make.left.equalTo(self).offset (3 + (Maxsize + 15)*j);
make.size.equalTo (@Maxsize);
}];
if (i == 3 && j < 3) {
button.backgroundColor = UIColor.grayColor;
[button setTitle:arr1[i+j-3] forState:UIControlStateNormal];
[button setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
button.tag = 100 + j;
if (j == 0) {
_ACBut = button;
} else {
[self.butArray addObject:button];
}
} else if (j == 3) {
button.backgroundColor = UIColor.orangeColor;
[button setTitle:arr1[i+j] forState:UIControlStateNormal];
[button setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
button.tag = 200 + i;
[self.butArray addObject:button];
} else {
button.backgroundColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1];
NSString* str = [NSString stringWithFormat:@"%d", j + 3 * i + 1];
[button setTitle:str forState:UIControlStateNormal];
[button setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
button.tag = 300 + i * 3 + j;
[self.butArray addObject:button];
}
}
}
for (int i = 0; i < 3; i++) {
UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.titleLabel.font = [UIFont systemFontOfSize:42];
button.layer.cornerRadius = Maxsize/2;
[button setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
[self addSubview:button];
if (i == 0) {
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(self).offset(-70);
make.left.equalTo(self).offset(0);
make.width.equalTo(@(Maxsize * 2 + 15));
make.height.equalTo(@Maxsize);
}];
[button setTitle:@"0" forState:UIControlStateNormal];
button.backgroundColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1];
button.tag = 310;
[self.butArray addObject:button];
} else {
[button mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.equalTo(self).offset(-70);
make.left.equalTo(self).offset(200 + (Maxsize + 15) * (i - 1));
make.size.equalTo(@Maxsize);
}];
if (i == 1) {
[button setTitle:@"." forState:UIControlStateNormal];
button.backgroundColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1];
button.tag = 311;
self.pointBut = button;
} else {
[button setTitle:@"=" forState:UIControlStateNormal];
button.backgroundColor = UIColor.orangeColor;
button.tag = 400;
self.equalBut = button;
}
}
}
}
@end
添加事件
- MVC模式要求我们在ViewController里实现并添加事件。
- 在ViewController中单独为小数点,AC,等于号添加点击事件。
[self.mView.ACBut addTarget:self action:@selector(pressAC:) forControlEvents:UIControlEventTouchUpInside];
[self.mView.equalBut addTarget:self action:@selector(pressequal:) forControlEvents:UIControlEventTouchUpInside];
[self.mView.pointBut addTarget:self action:@selector(presspoint:) forControlEvents:UIControlEventTouchUpInside];
- 为便利存储在数组中的按钮,为它们添加点击事件。
for (UIButton* button in self.mView.butArray) {
[button addTarget:self action:@selector(press:) forControlEvents:UIControlEventTouchUpInside
];
}
- 设置一个总事件按钮,根据按钮的tag值,对不同的按钮添加事件。
- (void) press: (UIButton*) button {
if (button.tag/100 == 1) {
[self pressbrac:button];
}
if (button.tag/100 == 2) {
[self pressoper:button];
}
if (button.tag/100 == 3) {
[self pressnum: button];
}
}
- 实现这些事件。
输入判错
对于用户的一些错误的输入进行判别,例如:小数点数量问题,括号数量以及匹配问题等不符合计算要求的输入方式。
for (i = 0; i < len; i++) {
if (t[i] == '(') {
count3++;
}
if (t[i] == ')') {
count3--;
}
if (count3 < 0) {
self.mView.Caltextfiled.text = fail;
return;
}
if (t[i] == '/' && t[i+1] == '0' && t[i+2] != '.') {
self.mView.Caltextfiled.text = fail;
return;
}
if (t[i] == '.') {
self.count2++;
}
if ((t[i] == '+' || t[i] == '-' || t[i] == '*'|| t[i] == '/') && (self.count2 == 1 || self.count2 == 0 )){
self.count2 = 0;
}
if (self.count2 > 1) {
self.mView.Caltextfiled.text = fail;
return;
}
if ((t[i] == '+' || t[i] == '-' || t[i] == '*'|| t[i] == '/') && self.count2 > 1) { //数字格式错误
//NSLog(@"%d", self.count2);
self.mView.Caltextfiled.text = fail;
return;
}
if ((t[i] == '+' || t[i] == '-' || t[i] == '*'|| t[i] == '/') && (t[i+1] == '+' || t[i+1] == '-' || t[i+1] == '*' || t[i+1] == '/')) {
self.mView.Caltextfiled.text = fail;
return;
}
if ((t[i] == '+' || t[i] == '-' || t[i] == '*'|| t[i] == '/') && (t[i+1] <'(' || t[i+1] > '9')) {
self.mView.Caltextfiled.text = fail;
return;
}
if (t[i] == '(' && t[i+1] == ')') { //直接左右括号
self.mView.Caltextfiled.text = fail;
return;
}
if (t[i] == '(' && (t[i+1] == '+' || t[i+1] == '-' || t[i+1] == '*'|| t[i+1] == '/') && t[i+2] == ')') {
self.mView.Caltextfiled.text = fail;
return;
}
if (self.count1 != 0 && t[i] == ')') {
self.mView.Caltextfiled.text = fail;
return;
}
if ((t[i] > '0' && t[i] <= '9') && t[i+1] == '(') {
self.mView.Caltextfiled.text = fail;
return;
}
}
ViewController全部代码:
ViewController.h
#import <UIKit/UIKit.h>
#import "Calculate_View.h"
#import "Calculate_Model.h"
@interface ViewController : UIViewController
@property (nonatomic, strong) Calculate_View* mView;
@property (nonatomic, strong) Calculate_Model* mModel;
@property int count1;
@property int count2;
@end
ViewController.m
#import "ViewController.h"
#import "Calculate_View.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.mView = [[Calculate_View alloc] initWithFrame:self.view.bounds];
[self.mView addUI];
[self.view addSubview:self.mView];
self.count1 = 0;
self.count2 = 0;
[self.mView.ACBut addTarget:self action:@selector(pressAC:) forControlEvents:UIControlEventTouchUpInside];
[self.mView.equalBut addTarget:self action:@selector(pressequal:) forControlEvents:UIControlEventTouchUpInside];
[self.mView.pointBut addTarget:self action:@selector(presspoint:) forControlEvents:UIControlEventTouchUpInside];
for (UIButton* button in self.mView.butArray) {
[button addTarget:self action:@selector(press:) forControlEvents:UIControlEventTouchUpInside
];
}
}
- (void) press: (UIButton*) button {
if (button.tag/100 == 1) {
[self pressbrac:button];
}
if (button.tag/100 == 2) {
[self pressoper:button];
}
if (button.tag/100 == 3) {
[self pressnum: button];
}
}
//括号的处理
- (void) pressbrac: (UIButton*) button {
NSString* old = self.mView.Caltextfiled.text;
NSString* new1 = button.titleLabel.text;
if ([button.titleLabel.text isEqualToString:@"("]) {
_count1++;
} else {
_count1--;
}
NSLog(@"%d", _count1);
NSString* new2 = [NSString stringWithFormat:@"%@%@", old,new1];
self.mView.Caltextfiled.text = new2;
}
//运算符号的处理
- (void) pressoper: (UIButton*) button {
if (button.tag > 201) {
[self pressbut1:button];
} else {
[self pressbut2: button];
}
}
- (void) pressbut1: (UIButton*) button {//加减法
NSString* old = self.mView.Caltextfiled.text;
NSString* new1 = button.titleLabel.text;
NSString* new2 = [NSString stringWithFormat:@"%@%@", old,new1];
self.mView.Caltextfiled.text = new2;
}
- (void) pressbut2: (UIButton*) button {//乘除法
NSString* old = self.mView.Caltextfiled.text;
NSString* new1 = button.titleLabel.text;
NSString* new2 = [NSString stringWithFormat:@"%@%@", old,new1];
self.mView.Caltextfiled.text = new2;
}
//数字的处理
- (void) pressnum: (UIButton*) button {
NSString* old = self.mView.Caltextfiled.text;
NSString* new1 = button.titleLabel.text;
NSString* new2 = [NSString stringWithFormat:@"%@%@", old,new1];
self.mView.Caltextfiled.text = new2;
NSLog(@"%@", self.mView.Caltextfiled.text);
}
- (void) pressAC: (UIButton*) button {
self.mView.Caltextfiled.text = nil;
self.count1 = 0;
self.count2 = 0;
}
- (void) presspoint: (UIButton*) button {
NSString* old = self.mView.Caltextfiled.text;
NSString* new1 = button.titleLabel.text;
NSString* new2 = [NSString stringWithFormat:@"%@%@", old,new1];
self.mView.Caltextfiled.text = new2;
}
//等于号部分
- (void) pressequal: (UIButton*) button {
NSString* fail = @"错误";
const char* q = [self.mView.Caltextfiled.text UTF8String];
char* t = strdup(q);
NSLog(@"%s", t);
int i = 0;
int len = (int) strlen (t);
//括号数量
if (self.count1 != 0) {
self.mView.Caltextfiled.text = fail;
printf("100");
return;
}
int count3 = 0;
for (i = 0; i < len; i++) {
if (t[i] == '(') {
count3++;
}
if (t[i] == ')') {
count3--;
}
if (count3 < 0) {
self.mView.Caltextfiled.text = fail;
return;
}
if (t[i] == '/' && t[i+1] == '0' && t[i+2] != '.') {
self.mView.Caltextfiled.text = fail;
printf("200");
return;
}
if (t[i] == '.') {
self.count2++;
}
if ((t[i] == '+' || t[i] == '-' || t[i] == '*'|| t[i] == '/') && (self.count2 == 1 || self.count2 == 0 )){
self.count2 = 0;
}
if (self.count2 > 1) {
self.mView.Caltextfiled.text = fail;
printf("300");
return;
}
if ((t[i] == '+' || t[i] == '-' || t[i] == '*'|| t[i] == '/') && self.count2 > 1) { //数字格式错误
//NSLog(@"%d", self.count2);
self.mView.Caltextfiled.text = fail;
printf("400");
return;
}
if ((t[i] == '+' || t[i] == '-' || t[i] == '*'|| t[i] == '/') && (t[i+1] == '+' || t[i+1] == '-' || t[i+1] == '*' || t[i+1] == '/')) {
self.mView.Caltextfiled.text = fail;
printf("500");
return;
}
if ((t[i] == '+' || t[i] == '-' || t[i] == '*'|| t[i] == '/') && (t[i+1] <'(' || t[i+1] > '9')) {
self.mView.Caltextfiled.text = fail;
printf("600");
return;
}
if (t[i] == '(' && t[i+1] == ')') { //直接左右括号
self.mView.Caltextfiled.text = fail;
printf("700");
return;
}
if (t[i] == '(' && (t[i+1] == '+' || t[i+1] == '-' || t[i+1] == '*'|| t[i+1] == '/') && t[i+2] == ')') {
self.mView.Caltextfiled.text = fail;
return;
}
if (self.count1 != 0 && t[i] == ')') {
self.mView.Caltextfiled.text = fail;
printf("800");
return;
}
if ((t[i] > '0' && t[i] <= '9') && t[i+1] == '(') {
self.mView.Caltextfiled.text = fail;
printf("900");
return;
}
}
if (t[0] == '-') {
t[0] = '!';
}
self.mView.Caltextfiled.text = [NSString stringWithUTF8String:t];
self.mModel = [[Calculate_Model alloc] init];
self.mModel.str1 = self.mView.Caltextfiled.text;
self.mView.Caltextfiled.adjustsFontSizeToFitWidth = YES;
[self.mModel Calculation];
self.mView.Caltextfiled.text = self.mModel.ptf;
}
@end
运算
采取的是数组模拟一个栈的思想,将正确的数学计算表达式转化为逆波兰表达式,再通过计算逆波兰表达式得出计算的结果。
Calculate_Model.h
#import <Foundation/Foundation.h>
#import"Calculate_View.h"
NS_ASSUME_NONNULL_BEGIN
@interface Calculate_Model : NSObject
@property (nonatomic, strong) NSString* str1;
@property char* str;
@property (nonatomic, strong) NSString* ptf;
@property (nonatomic, strong) Calculate_View* view;
- (void) Calculation;
@end
NS_ASSUME_NONNULL_END
Calculate_Model.m
#import "Calculate_Model.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
@implementation Calculate_Model
int Compare (char ch) {
if (ch == '(' || ch == ')') {
return 0;
} else if (ch == '+' || ch == '-') {
return 1;
} else if (ch == '*' || ch == '/' || ch == '!') {
return 2;
} else if (ch == '.') {
return 3;
} else {
return -1;
}
}
- (void) Calculation {
//将NSString的对象a中的字符串转化为C语言中的字符串。
const char* Cstring = [self.str1 UTF8String];
//使用了strdup函数来复制Cstring字符串到另一个内存区域。
self.str = strdup(Cstring);
char stack1[1000];
int Top1 = -1;
char charstack[1000];
int charTop = -1;
int i = 0;
int len = (int) strlen (self.str);
while (i < len) {
if (_str[i] >= '0' && _str[i] <= '9') {
stack1[++Top1] = _str[i++];
if (_str[i] < '0' || _str[i] > '9') {
stack1[++Top1] = ' ';
}
} else if (_str[i] == '(') {
charstack[++charTop] = _str[i++];
} else if (_str[i] == ')') {
while (charstack[charTop] != '(') {
stack1[++Top1] = charstack[charTop--];
stack1[++Top1] = ' ';
}
if (stack1[Top1-1] == '-') {
stack1[Top1-1] = '!';
}
charTop--;
i++;
} else {
if (Compare(_str[i]) > Compare(charstack[charTop]) || charTop == -1) {
charstack[++charTop] = _str[i++];
} else {
while (Compare(charstack[charTop]) >= Compare(_str[i]) && charTop != -1) {
stack1[++Top1] = charstack[charTop--];
stack1[++Top1] = ' ';
}
charstack[++charTop] = _str[i++];
}
}
}
while (charTop != -1) {
stack1[++Top1] = charstack[charTop--];
stack1[++Top1] = ' ';
}
stack1[++Top1] = '\0';
printf("%s", stack1);
printf("\n");
double realstack[1000];
int realtop = -1;//初始化一个栈来存储数字
i = 0;
double x = 0;
while (i < Top1) {
if (stack1[i] >= '0' && stack1[i] <= '9') {
while (stack1[i] >= '0' && stack1[i] <= '9') {
x = x * 10 + stack1[i] - '0';
i++;//获取多位数
}
realstack[++realtop] = x;
x = 0;
i++;
} else if (stack1[i] == '+') {
double b = realstack[realtop--];
double a = realstack[realtop--];
double c = a + b;
realstack[++realtop] = c;
i = i+2;
} else if (stack1[i] == '-') {
double b = realstack[realtop--];
double a = realstack[realtop--];
double c = a - b;
realstack[++realtop] = c;
i = i+2;
} else if (stack1[i] == '*') {
double b = realstack[realtop--];
double a = realstack[realtop--];
double c = a * b;
realstack[++realtop] = c;
i = i+2;
} else if (stack1[i] == '/') {
double b = realstack[realtop--];
double a = realstack[realtop--];
double c = a / b;
realstack[++realtop] = c;
i = i+2;
} else if (stack1[i] == '!') {
double b = realstack[realtop--] ;
double c = -b ;
realstack[++realtop] = c;
i = i+2;
} else {
double b = realstack[realtop--];
double a = realstack[realtop--];
int m = 0;
int n = b;
while (n > 0) {
n /= 10;
m++;
}
double c = a + b* pow(10, -m);
realstack[++realtop] = c;
i = i+2;
}
}
printf("%lf ",realstack[0]);
double num = realstack[0];
int num1 = num;
int n = 0;
while (num1 - num > 0.01 || num - num1 > 0.01) {
num = num*10;
num1 = num;
n++;
if (n == 8) {
break;
}
}
self.ptf = [NSString stringWithFormat:@"%.*f", n, realstack[0]];
}
@end
** 注:在计算的时候我对表达式中的负号进行了一次判断,如果表示的是加减法,就直接进行运算,如果负号表示的是这个数为负数,就把负号改成!,并且在计算中加上关于!的运算。这样就可以区分符号是减号还是负号了。**