当前位置: 首页 > news >正文

[iOS] 计算器仿写

[iOS] 计算器仿写

前言

这个项目主要是锻炼对多种复杂情况的考虑问题,以及使用栈这种数据结构的熟练程度。下面我将以 MVC 模式的角度来去介绍这个项目。

UI 界面

请添加图片描述

上面的 UI 界面都采用了 Masonry 来布局,在这个界面有两个部分组成一个就是 button 按钮一个就是 textField。在这里的 button 按钮我就是通过 for 循环来进行布局的,然后我们还给了 tag 值来做了区分。

下面就是我的 UI 相关的代码。

#import "CaculateView.h"
#import "Masonry.h"
@implementation CaculateView
- (instancetype)initWithFrame:(CGRect)frame {if (self = [super initWithFrame:frame]) {CGFloat SIZE = [UIScreen mainScreen].bounds.size.width / 5;NSArray *grayArray = @[@"AC", @"(", @")"];NSArray *orangeArray = @[@"×", @"÷", @"+", @"-", @"="];for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {_baseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];_baseButton.layer.cornerRadius = SIZE / 2;_baseButton.titleLabel.font = [UIFont systemFontOfSize:42];_baseButton.tag = j + 4 + i * 4;[self addSubview:_baseButton];[_baseButton mas_makeConstraints:^(MASConstraintMaker *make) {make.bottom.equalTo(self).offset(-(75 + (SIZE + 17) * (i + 1)));make.left.equalTo(self).offset(5 + [UIScreen mainScreen].bounds.size.width / 4 * j);make.width.mas_equalTo(SIZE);make.height.mas_offset(SIZE);}];if (j < 3) {if (i < 3) {[_baseButton setBackgroundColor:[UIColor colorWithWhite:0.15 alpha:1]];[_baseButton setTitle:[NSString stringWithFormat:@"%d", j * 1 + i * 3 + 1] forState:UIControlStateNormal];[_baseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];} else {[_baseButton setBackgroundColor:[UIColor lightGrayColor]];[_baseButton setTitle:grayArray[j] forState:UIControlStateNormal];[_baseButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];}} else {[_baseButton setBackgroundColor:[UIColor colorWithRed:0.9 green:0.58 blue:0 alpha:1]];[_baseButton setTitle:orangeArray[i] forState:UIControlStateNormal];[_baseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];}if (j == 0 && i == 3) {_baseButton.titleLabel.font = [UIFont systemFontOfSize:34];}}}_baseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];_baseButton.layer.cornerRadius = SIZE / 2;_baseButton.titleLabel.font = [UIFont systemFontOfSize:42];_baseButton.tag = 20;[self addSubview:_baseButton];[_baseButton mas_makeConstraints:^(MASConstraintMaker *make) {make.bottom.equalTo(self).offset(-SIZE);make.left.equalTo(self).offset(5);make.width.mas_equalTo(2 * SIZE + 20);make.height.mas_equalTo(SIZE);}];[_baseButton setBackgroundColor:[UIColor colorWithWhite:0.15 alpha:1]];[_baseButton setTitle:[NSString stringWithFormat:@"0"] forState:UIControlStateNormal];[_baseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];_baseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];_baseButton.layer.cornerRadius = SIZE / 2;_baseButton.titleLabel.font = [UIFont systemFontOfSize:42];_baseButton.tag = 21;[self addSubview:_baseButton];[_baseButton mas_makeConstraints:^(MASConstraintMaker *make) {make.bottom.equalTo(self).offset(-SIZE);make.left.equalTo(self).offset(5 + [UIScreen mainScreen].bounds.size.width / 4 * 2);make.width.mas_equalTo(SIZE);make.height.mas_equalTo(SIZE);}];[_baseButton setBackgroundColor:[UIColor colorWithWhite:0.15 alpha:1]];[_baseButton setTitle:[NSString stringWithFormat:@"."] forState:UIControlStateNormal];[_baseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];_baseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];_baseButton.layer.cornerRadius = SIZE / 2;_baseButton.titleLabel.font = [UIFont systemFontOfSize:42];_baseButton.tag = 22;[self addSubview:_baseButton];[_baseButton mas_makeConstraints:^(MASConstraintMaker *make) {make.bottom.equalTo(self).offset(-SIZE);make.left.equalTo(self).offset(5 + [UIScreen mainScreen].bounds.size.width / 4 * 3);make.width.mas_equalTo(SIZE);make.height.mas_equalTo(SIZE);}];[_baseButton setBackgroundColor:[UIColor colorWithRed:0.9 green:0.58 blue:0 alpha:1]];[_baseButton setTitle:orangeArray[4] forState:UIControlStateNormal];[_baseButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];self.displayTextField = [[UITextField alloc] init];self.displayTextField.textColor = [UIColor whiteColor];self.displayTextField.textAlignment = NSTextAlignmentRight;self.displayTextField.font = [UIFont systemFontOfSize:88 weight:UIFontWeightLight];self.displayTextField.text = @"0";self.displayTextField.userInteractionEnabled = NO;self.displayTextField.adjustsFontSizeToFitWidth = YES;self.displayTextField.minimumFontSize = 30;[self addSubview:self.displayTextField];[self.displayTextField mas_makeConstraints:^(MASConstraintMaker *make) {make.top.equalTo(self).offset(140);make.left.equalTo(self).offset(12.0);make.right.equalTo(self).offset(-12.0);make.height.mas_equalTo(120);}];}return self;
}@end

Model 数据处理界面

在这里就达到了整个计算器的核心,在这里的第一部分是如何把所谓的字符部分转化成我们所能处理的数据,在这里我专门写了一个函数,把字符串转化成数组,在这里我做了一个缓冲区,如果是数字就会被存放到缓冲区里面知道遇到符号的时候我们才会将数字变成一个整体加入到数组当中,这时我们的数组就是一个合格的中缀表达式,这时我们就会直接开始检测是否有不合规的括号出现或者不合规的标点符号出现,但是因为只是中缀表达是只能判读诸如8( 之类的。

下面就是我的字符串转换成数组的代码

- (NSArray *)tokenize:(NSString *)expression {NSMutableArray *tokens = [NSMutableArray array];NSMutableString *numberBuffer = [NSMutableString string];for (NSUInteger i = 0; i < expression.length; i++) {unichar c = [expression characterAtIndex:i];if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:c] || c == '.') {[numberBuffer appendFormat:@"%C", c];} else {if (numberBuffer.length > 0) {[tokens addObject:[numberBuffer copy]];[numberBuffer setString:@""];}NSString *op = [NSString stringWithFormat:@"%C", c];[tokens addObject:op];}}if (numberBuffer.length > 0) {[tokens addObject:[numberBuffer copy]];}for (NSUInteger i = 0; i < tokens.count; i++) {NSString *token = tokens[i];NSString *next = (i + 1 < tokens.count) ? tokens[i + 1] : nil;if ([self isOperator:token] && next && [self isOperator:next]) {return nil;}if ([self isNumber:token] && [next isEqualToString:@"("]) {return nil;}if ([token isEqualToString:@"("] && next &&([self isOperator:next] || [next isEqualToString:@")"])) {return nil;}if ([self isOperator:token] && [next isEqualToString:@")"]) {return nil;}if (i == tokens.count - 1 && [self isOperator:token]) {return nil;}}return tokens;
}

下面是中缀转后缀的部分,中缀转后缀就是我们需要做一个数组和一个栈,在这里的核心思想就是对符号顺序的一个控制,就比如如果当前词元是左括号 (,直接将它压入运算符栈 stack。如果当前词元是右括号 ),则开始一个循环,不断地从运算符栈 stack 顶部弹出元素,并将其加入到 output 输出队列,直到遇到一个左括号 ( 为止。if ([top isEqualToString:@"("]): 遇到左括号时,将它从栈中丢弃,并跳出循环。if (!foundLeftParen): 如果循环结束了还没找到左括号,说明括号不匹配,表达式非法,返回 nil。如果当前词元是运算符(+, -, ×, ÷),则执行以下逻辑:while (...): 查看运算符栈 stack 顶部的运算符,只要栈顶运算符的优先级大于或等于当前运算符的优先级,就将栈顶运算符弹出并加入到 output 输出队列。[stack addObject:token];: while 循环结束后,将当前这个运算符压入栈中。

下面是相关代码

- (NSArray *)infixToRPN:(NSArray *)tokens {NSMutableArray *output = [NSMutableArray array];NSMutableArray *stack = [NSMutableArray array];NSDictionary *precedence = @{@"+": @1, @"-": @1,@"×": @2, @"÷": @2};for (NSString *token in tokens) {if ([self isNumber:token]) {[output addObject:token];} else if ([token isEqualToString:@"("]) {[stack addObject:token];} else if ([token isEqualToString:@")"]) {BOOL foundLeftParen = NO;while (stack.count > 0) {NSString *top = [stack lastObject];[stack removeLastObject];if ([top isEqualToString:@"("]) {foundLeftParen = YES;break;} else {[output addObject:top];}}if (!foundLeftParen) {return nil;}} else {while (stack.count > 0) {NSString *top = [stack lastObject];if ([precedence objectForKey:top] &&[precedence[token] integerValue] <= [precedence[top] integerValue]) {[output addObject:top];[stack removeLastObject];} else {break;}}[stack addObject:token];}}for (NSString *token in stack) {if ([token isEqualToString:@"("] || [token isEqualToString:@")"]) {return nil;}}while (stack.count > 0) {NSString *top = [stack lastObject];[stack removeLastObject];[output addObject:top];}if (tokens.count == 0) {return nil;}return output;
}

最后就是逆波兰表达式的内容了,逆波兰表达式就是使用栈这一个数据结构来去通过优先级来还原我们的计算顺序。

- (NSDecimalNumber *)evalRPN:(NSArray *)tokens {NSMutableArray *stack = [NSMutableArray array];for (NSString *token in tokens) {if ([self isNumber:token]) {[stack addObject:[NSDecimalNumber decimalNumberWithString:token]];} else {if (stack.count < 2) return nil;NSDecimalNumber *num2 = [stack lastObject];[stack removeLastObject];NSDecimalNumber *num1 = [stack lastObject];[stack removeLastObject];NSDecimalNumber *result = nil;if ([token isEqualToString:@"+"]) {result = [num1 decimalNumberByAdding:num2];} else if ([token isEqualToString:@"-"]) {result = [num1 decimalNumberBySubtracting:num2];} else if ([token isEqualToString:@"×"]) {result = [num1 decimalNumberByMultiplyingBy:num2];} else if ([token isEqualToString:@"÷"]) {if ([num2 isEqualToNumber:@0]) return nil;result = [num1 decimalNumberByDividingBy:num2];}[stack addObject:result];}}return stack.count == 1 ? [stack lastObject] : nil;
}

Controller 界面

这个界面没有什么可以多说的就是单纯的绑定按钮的界面,然后在就是一些按钮的使用就比如 AC 的清零作用还有就是返回 nil 的报错提示。

下面是部分代码

- (void) setupButtonEvents {for (UIView *subview in self.caculateView.subviews) {if ([subview isKindOfClass:[UIButton class]]) {UIButton *btn = (UIButton *)subview;[btn addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];}}
}
- (void)buttonPressed:(UIButton *)sender {NSString *title = sender.currentTitle;if ([title isEqualToString:@"AC"]) {[self.inputString setString:@""];self.caculateView.displayTextField.text = @"0";} else if ([title isEqualToString:@"="]) {NSString *expression = [self.inputString copy];NSDecimalNumber *result = [self.model calculateExpression:expression];if (result) {self.caculateView.displayTextField.text = result.stringValue;[self.inputString setString:result.stringValue];} else {self.caculateView.displayTextField.text = @"错误";[self.inputString setString:@""];}} else {if (self.inputString.length >= 20) {return;}[self.inputString appendString:title];self.caculateView.displayTextField.text = self.inputString;}
}

总结

这个项目的难点就是很多的小细节问题,需要多打磨打磨,还有就是字符串的处理问题。

http://www.dtcms.com/a/516919.html

相关文章:

  • 北京专业网站制作公司家在深圳龙光城
  • Blender学习-基础移动
  • wordpress的文章采集湖北优化网站建设
  • 如何做防水网站电子商务电商网站设计
  • 宁波建网站哪家wordpress调用昵称
  • 深入理解 C++ 红黑树:平衡二叉搜索树的理论精髓​
  • 手机网站建设计中国建筑人才网官网登录
  • rust python 混合编程注意点
  • 做正品的网站申请个人网站有什么用
  • 潍坊网页网站制作怎么做的网站收录快
  • 江象网站建设成都网站建设优化推
  • Elasticsearch从入门到进阶——Elasticsearch部署与使用
  • 嵌入式软件架构--按键消息队列3(测试)
  • 淘宝导购网站模版上海城隍庙简介
  • 怎么建立企业网站免费的软件项目管理方案
  • 工作流activiti(1)
  • 合泰单片机之点亮开发板的所有LED灯
  • 找不到mfc140d.dll文件
  • Dexmal 原力灵机开源 Dexbotic:具身智能的“Transformers“库来了
  • 毕设做网站有什么题目网络规划设计师攻略
  • 【avalonia教程】15Binding的其他属性(2)
  • 企业网站作用平湖手机网站建设
  • 算法leetcode|96. 不同的二叉搜索树(多语言实现)
  • 快速上手ip link命令:查看你的网络接口信息
  • 视频汇聚平台EasyCVR级联播放偶发失败排查:TCP主动模式下的3秒超时响应差
  • 苏州马可波罗网站建设wordpress单页主题制作视频教程
  • html手机网站怎么做清新织梦淘宝客模板淘客网站程序源码
  • 20.2 图像识别技术革命:多模态模型准确率突破87.6%,传统方案效率飙升32%!
  • 深圳网站建设加盟网站 方案
  • ★ Linux ★ 线程概念与控制