【iOS】—— 仿写计算器项目总结

仿写计算器

在完成这次仿写任务时,和以前所写的项目最大的区别时首次运用到了MVC模式和Masonry界面,并且在计算器使用的过程中运用了很多的算法知识,在整个过程中会出现特别多的error,以及很神奇的错误。

View

先来看界面:
【iOS】—— 仿写计算器项目总结_第1张图片

利用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

在控制计算器输入合理的过程中,我在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

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的时候,会显示如下:
【iOS】—— 仿写计算器项目总结_第2张图片

为了解决这一问题,在代码中输出计算结果:
【iOS】—— 仿写计算器项目总结_第3张图片

其中我用到了doubleValue方法来去掉结果后面多余的0,结果发现,在运行完这一代码后不但没有去掉后面的0,而且在数值发生了改变,最后在测试中发现是单精度和双精度的问题,将doubleValue改成floatValue即可。

你可能感兴趣的:(ios,xcode,objective-c)