【iOS】——仿写计算器

文章目录


一、实现思路

先搭建好MVC框架,接着在各个模块中实现各自的任务。首先要创建好UI界面,接着根据UI界面的元素来与数据进行互动,其中创建UI界面需要用到Masonry布局。

二、实现方法

在calculationView文件中只涉及到UI界面。

首先创建两个UITextField对象,一个用来显示输入到表达式,一个用来输出运算结果,接着创建一个数组用来存放button的名称,接着循环创建button对象即可。

创建button时需要设置button的tag属性,因为后面需要通过button的tag属性来进行逻辑判断。

objectivec 复制代码
self.textField01 = [[UITextField alloc] init];
self.textField01.backgroundColor = [UIColor blackColor];
self.textField02 = [[UITextField alloc] init];
self.textField02.backgroundColor = [UIColor blackColor];
self.buttonArray = [NSMutableArray arrayWithObjects:@"AC", @"(", @")", @"/", @"1", @"2", @"3", @"+", @"4", @"5", @"6", @"-", @"7", @"8", @"9", @"*",  @"0", @".", @"=", nil];
 for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            self.calculatorButton = [UIButton buttonWithType:UIButtonTypeCustom];
            NSString* buttonString = self.buttonArray[i * 4 + j];
            [self.calculatorButton setTitle:buttonString forState:UIControlStateNormal];
            self.calculatorButton.titleLabel.font = [UIFont systemFontOfSize:43];
            self.calculatorButton.titleLabel.textColor = [UIColor whiteColor];
            self.calculatorButton.backgroundColor = [UIColor grayColor];
            [self.calculatorButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];
            self.calculatorButton.layer.borderWidth = 2.0;
            self.calculatorButton.layer.cornerRadius = ButtonSize / 2;
            self.calculatorButton.layer.masksToBounds = YES;
            self.calculatorButton.tag = 100 + j + i*4;
            if (i == 0 && j < 3) {
                self.calculatorButton.backgroundColor = [UIColor colorWithWhite:0.6 alpha:0.9];
                self.calculatorButton.titleLabel.textColor = [UIColor blackColor];
            }
            if (j == 3) {
                self.calculatorButton.backgroundColor = [UIColor orangeColor];
            }
            [self addSubview:self.calculatorButton];
            [self.calculatorButton mas_makeConstraints:^(MASConstraintMaker *make) {
                make.top.equalTo(self).offset(90 + HEIGHT / 6 + HEIGHT / 8 + (ButtonSize + SideSize) * i);
                make.left.equalTo(self).offset(SideSize + (ButtonSize + SideSize) * j);
                make.width.equalTo(@ButtonSize);
                make.height.equalTo(@ButtonSize);
            }];
        }
    }

还需要在当前文件触发button的事件函数,因为要通过button的事件函数将当前button传值到CalculationViewController文件中。

我使用的协议传值进行

定义协议及协议方法

objectivec 复制代码
@protocol ButttonDelegate <NSObject>

- (void)returnButton:(UIButton*)button;

@end

定义代理对象

objectivec 复制代码
@property (nonatomic, weak)id<ButttonDelegate>delegate;

button事件函数触发代理方法

objectivec 复制代码
- (void)pressButton:(UIButton*)button {
    [self.delegate returnButton:button];
}

在CalculationViewController中定义协议方法

objectivec 复制代码
- (void)returnButton:(UIButton*)button;

在CalculationViewController中设置代理对象

objectivec 复制代码
 self.calculationView.delegate = self;

在CalculationViewController中实现协议方法

objectivec 复制代码
- (void)returnButton:(UIButton*)button {
- NSString* buttonStr = button.titleLabel.text;
    NSInteger buttonTag = button.tag;
    if (buttonTag == 100) {
        self.calculationView.textField01.text = @"";
        self.calculationView.textField02.text = @"";
        self.yunsuanStr = [[NSMutableString alloc] init];
        self.jieguoStr = [[NSString alloc] init];
    } else if (buttonTag == 118) {
    	//四则运算
    }
	//
}

对于四则运算,我使用中缀表达式转后缀表达式,再利用后缀表达式进行计算。中缀转后缀定义一个符号栈,一个结果栈。

objectivec 复制代码
		char resultStack[100];
		int resultTop = -1;
		char operationStack[100];
		int operationTop = -1;
 		NSString* ocStr =  self.yunsuanStr;
        const char* strs = [ocStr UTF8String];
        char str[1000] ;
        strcpy(str, strs) ;
        int length = (int)strlen(str);

后缀进行四则运算时定义一个新栈。

objectivec 复制代码
			double Stack[100];
            int StackTop = -1;
              char* token = strtok(resultStack, " ");
            while (token != NULL) {
                if (strcmp(token, "+") == 0) {
                    double a = Stack[StackTop--];
                    double b = Stack[StackTop--];
                    double c = b + a;
                    Stack[++StackTop] = c;
                }
                else if (strcmp(token, "-") == 0) {
                    double a = Stack[StackTop--];
                    double b = Stack[StackTop--];
                    double c = b - a;
                    Stack[++StackTop] = c;
                }
                else if (strcmp(token, "*") == 0) {
                    double a = Stack[StackTop--];
                    double b = Stack[StackTop--];
                    double c = b * a;
                    Stack[++StackTop] = c;
                }
                else if (strcmp(token, "/") == 0) {
                    double a = Stack[StackTop--];
                    double b = Stack[StackTop--];
                    double c = b / a;
                    Stack[++StackTop] = c;
                }
                else if (strcmp(token, "!") == 0) {
                    double c = Stack[StackTop--];
                    c = -c;
                    Stack[++StackTop] = c;
                }
                else {
                    double num = atof(token);
                    Stack[++StackTop] = num;
                }

                token = strtok(NULL, " ");
            }
            double result = Stack[StackTop--];

这里使用了atof()函数可以将字符串中的数字识别为double类型的浮点数。使用strtok()函数将结果栈的元素通过空格分割方便进行识别和运算。

对于负数的运算,我是将与"("相邻的"-"转换为"!",并用"!"进行负数转变,其中需要将"!"的优先级设置为最高

objectivec 复制代码
int Compare(char str) {
    if (str == '(' || str == ')') {
        return 0;
    }
    else if (str == '+' || str == '-') {
        return 1;
    }
    else if (str == '*' || str == '/') {
        return 2;
    } else if (str == '!') {
        return 3;
    } else {
        return -1;
    }
}

接着在输入字符串时进行识别

objectivec 复制代码
for (int i = 0; i < length; ++i) {
                if (str[i] == '(' && str[i + 1] == '-') {
                    str[i + 1] = '!';
                }
            }

三、判错处理

对于括号匹配问题,我使用栈来处理,当读到"("则入栈,读到")"则栈顶元素出栈,最后判断栈是否为空即可。

objectivec 复制代码
- (int)kuoHaoMatch:(NSMutableString*)mutableString {
    const char* cstr = [mutableString UTF8String];
    int length = (int)strlen(cstr);
    char stack[100];
    int stackTop = -1;
    for (int i = 0; i < length; i++) {
        if (cstr[i] == '(') {
            stack[++stackTop] = cstr[i];
        }
        if (cstr[i] == ')') {
            stack[stackTop--];
        }
    }
    if (stackTop == -1) {
        return 1;
    }
    return 0;
}

对于符号匹配问题,两个运算符不能相邻,第一个位置不能为运算符(-除外),最后一个位置不能为运算符

objectivec 复制代码
- (int)fuHaoMatch:(NSMutableString*)mutableString {
    const char* cstr = [mutableString UTF8String];
    int length = (int)strlen(cstr);
    if (cstr[0] == '*' || cstr[0] == '/' || cstr[0] == '+') {
        return 0;
    }
    for (int i = 0; i < length; i++) {
        if ((cstr[i] == '*' && cstr[i+1] == '/') || (cstr[i] == '/' && cstr[i+1] == '*')) {
            return 0;
        }
        if ((cstr[i] == '*' && cstr[i+1] == '-') || (cstr[i] == '*' && cstr[i+1] == '+')) {
            return 0;
        }
        if ((cstr[i] == '/' && cstr[i+1] == '-') || (cstr[i] == '/' && cstr[i+1] == '+')) {
            return 0;
        }
        if ((cstr[i] == '+' && cstr[i+1] == '/') || (cstr[i] == '+' && cstr[i+1] == '*')) {
            return 0;
        }
        if ((cstr[i] == '-' && cstr[i+1] == '/') || (cstr[i] == '-' && cstr[i+1] == '*')) {
            return 0;
        }
        if ((cstr[i] == '-' && cstr[i+1] == '-') || (cstr[i] == '-' && cstr[i+1] == '+')) {
            return 0;
        }
        if ((cstr[i] == '+' && cstr[i+1] == '+') || (cstr[i] == '+' && cstr[i+1] == '-')) {
            return 0;
        }
    }
    if (cstr[length - 1] == '+' || cstr[length - 1] == '-' ||cstr[length - 1] == '*' || cstr[length - 1] == '/') {
        return 0;
    }
    return 1;
}

对于数字和括号的相邻问题,数字后不能直接跟左括号,右括号不能直接跟数字

objectivec 复制代码
- (int)numberAndkuoHao:(NSMutableString*)mutableString {
    const char* cstr = [mutableString UTF8String];
    int length = (int)strlen(cstr);
    int flag = 0;
    for (int i = 0; i < length; i++) {
        if ((cstr[i]>= '0' && cstr[i] <= '9') &&cstr[i+1] == '(') {
            flag++;
        }
        if (cstr[i] == ')' && (cstr[i + 1]>= '0' && cstr[i + 1] <= '9')) {
            flag++;
        }
    }
    if (flag == 0) {
        return 1;
    }
    return 0;
}

对于只有括号没有数字的问题,直接遍历整个字符串如果有数字则标志变量加一,最后判断标志变量

objectivec 复制代码
- (int)numberJudge:(NSMutableString*)mutableString {
    const char* cstr = [mutableString UTF8String];
    int length = (int)strlen(cstr);
    int flag = 0;
    for (int i = 0; i < length; i++) {
        if (cstr[i]>= '0' && cstr[i] <= '9') {
            flag++;
        }
    }
    if (flag != 0) {
        return 1;
    }
    return 0;;
}

对于除零问题

objectivec 复制代码
if ([self.jieguoStr isEqualToString:@"nan"]) {
                self.jieguoStr = @"错误";
            }
            if ([self.jieguoStr isEqualToString:@"inf"]) {
                self.jieguoStr = @"错误";
            }

对于小数点异常处理

objectivec 复制代码
int pointFlag = 0;
for (int i = 0; i < resultTop; i++) {
                if (resultStack[i] == '.') {
                    for (int j = i + 1; resultStack[j] != ' '; j++) {
                        if (resultStack[j] == '.') {
                            pointFlag++;
                        }
                    }
                }
            }
             if (pointFlag != 0) {
                self.jieguoStr = @"错误";
                pointFlag = 0;
            }

对于如何删除多余的零

objectivec 复制代码
NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithString: self.jieguoStr];
            NSDecimalNumberHandler *roundHandler = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundUp scale:8 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
            NSDecimalNumber *roundedNumber = [number decimalNumberByRoundingAccordingToBehavior:roundHandler];
            self.jieguoStr = [roundedNumber stringValue];
相关推荐
tangweiguo030519876 小时前
SwiftUI布局完全指南:从入门到精通
ios·swift
T1an-110 小时前
最右IOS岗一面
ios
坏小虎12 小时前
Expo 快速创建 Android/iOS 应用开发指南
android·ios·rn·expo
光影少年13 小时前
Android和iOS原生开发的基础知识对RN开发的重要性,RN打包发布时原生端需要做哪些配置?
android·前端·react native·react.js·ios
北京自在科技13 小时前
Find My 修复定位 BUG,AirTag 安全再升级
ios·findmy·airtag
Digitally14 小时前
如何不用 USB 线将 iPhone 照片传到电脑?
ios·电脑·iphone
Sim14801 天前
iPhone将内置本地大模型,手机端AI实现0 token成本时代来临?
人工智能·ios·智能手机·iphone
Digitally1 天前
如何将 iPad 上的照片传输到 U 盘(4 种解决方案)
ios·ipad
报错小能手1 天前
ios开发方向——swift并发进阶核心 @MainActor 与 DispatchQueue.main 解析
开发语言·ios·swift
LcGero1 天前
Cocos Creator 业务与原生通信详解
android·ios·cocos creator·游戏开发·jsb