iOS计算器

  • 思路解构
  • MVC
  • Masonry布局
  • 设计时的问题解决
  • 代码
    • View
    • Model
    • 计算表达式:

思路解构

要学会写计算器,必须要学会用栈来实现四则运算,同时要实现复杂运算的前提,就增加括号、小数点情况的判断来处理 。

这里提供两篇博客参考:
四则运算
栈的四则运算(带小数点和括号)

然后根据OC的特性,(Objective-C中没有栈这种数据结构)考虑用可变数组来直接进行对栈的模拟,操作更简洁。

当然,也可以直接用C来进行算法部分,OC的环境下运行C完全没有问题。

MVC

这里使用MVC设计模式来对计算器进行设计。
MVC的具体介绍可在这里了解:MVC模式基础

显然对于算法部分一定是放在Model中进行数据处理,计算器显示屏与计算器按钮的布局写在View中,当需要在viewController布局View时,View内的布局方法被调用。ViewController就负责转发请求,处理请求。

Masonry布局

Masonry安装以及基本使用
这次使用了Masonry框架来对计算器的各个按钮元件设置位置,更方便更快捷而且不会因为机型更迭而失效或出现乱位。

 _roundButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];[_roundButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];[self addSubview:_roundButton];//设置按钮圆角_roundButton.layer.cornerRadius = 45;//边框宽度_roundButton.layer.borderWidth = 1;[_roundButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];_roundButton.titleLabel.font = [UIFont systemFontOfSize:45];[_roundButton mas_makeConstraints:^(MASConstraintMaker *make) {make.top.equalTo(@(self.frame.size.height/3 + 100 * (i-1) + 80));make.left.equalTo(@(10 + (j-1) * 100));make.height.equalTo(@90);make.width.equalTo(@90);

实际效果:

设计时的问题解决

  • 计算表达式:用UILabel去充当计算器显示屏,每次按下一个按钮就用传值从View传到Controller,处理传来的值并刷新Label,就可以实现向计算器不断输入数据.
    刷新时使用下面方法:
- (void)reloadInputViews API_AVAILABLE(ios(3.2));
//具体使用:[_mainView.numLabel reloadInputViews];
//_mianView.numLabel是需要刷新的label
  • 计算结果处理:
    计算结果如果是用double类型转换为字符串再放到label上显示,会出现多余0的情况,可以这么处理:
lastString = [NSMutableString stringWithFormat:@"%@", @(testString.floatValue)];

用floatValue属性对testString处理来得到lastString,注意使用这一操作时需要将数据转成字符串testString。

  • 限制输入:对于计算器,人类所能写出来的算法只能计算正确无误的表达式,如果输入字符串的表达式不是符合数学上的规则,就会导致算法无法正常计算,程序崩溃,我们就可以用一些合适的属性作为标志,通过判断来限制输入(也可以通过判断在合适的时机将label.text置为error)。

下面列出了一部分我写计算器时所遵循的限制:

  • 加减乘除:运算符号不能打头不能结尾,不能连续输入运算符,需要输入数字来保证运算符被分隔
  • 小数点:不能打头不能结尾,不能连续输入小数点,不能跟随运算符或者括号出现
  • 括号:不能左右括号数量不匹配,左括号左边不能连运算符号之外的字符,右括号右边不能连运算符号之外的字符。

代码

View

这是布局按钮的完整代码

//布局按钮for (int i = 1; i <= 4; i++) {for (int j = 1; j <=4; j++) {_roundButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];[_roundButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];[self addSubview:_roundButton];//设置按钮圆角_roundButton.layer.cornerRadius = 45;//边框宽度_roundButton.layer.borderWidth = 1;[_roundButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];_roundButton.titleLabel.font = [UIFont systemFontOfSize:45];[_roundButton mas_makeConstraints:^(MASConstraintMaker *make) {make.top.equalTo(@(self.frame.size.height/3 + 100 * (i-1) + 80));make.left.equalTo(@(10 + (j-1) * 100));make.height.equalTo(@90);make.width.equalTo(@90);}];if (i == 1 && j != 4) {_roundButton.backgroundColor = [UIColor colorWithRed:0.608 green:0.608 blue:0.608 alpha:1];[_roundButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];if (j == 1) {[_roundButton setTitle:@"AC" forState:UIControlStateNormal];_roundButton.titleLabel.textColor = [UIColor blackColor];}if (j == 2) {[_roundButton setTitle:@"(" forState:UIControlStateNormal];_roundButton.titleLabel.textColor = [UIColor blackColor];}if (j == 3) {[_roundButton setTitle:@")" forState:UIControlStateNormal];_roundButton.titleLabel.textColor = [UIColor blackColor];}_roundButton.tag = 110 + j;}if (i == 2 && j != 4) {_roundButton.backgroundColor = [UIColor darkGrayColor];if (j == 1) {[_roundButton setTitle:@"7" forState:UIControlStateNormal];}if (j == 2) {[_roundButton setTitle:@"8" forState:UIControlStateNormal];}if (j == 3) {[_roundButton setTitle:@"9" forState:UIControlStateNormal];}_roundButton.tag = 106 + j;}if (i == 3 && j != 4) {_roundButton.backgroundColor = [UIColor darkGrayColor];if (j == 1) {[_roundButton setTitle:@"4" forState:UIControlStateNormal];}if (j == 2) {[_roundButton setTitle:@"5" forState:UIControlStateNormal];}if (j == 3) {[_roundButton setTitle:@"6" forState:UIControlStateNormal];}_roundButton.tag = 103 + j;}if (i == 4 && j != 4) {_roundButton.backgroundColor = [UIColor darkGrayColor];if (j == 1) {[_roundButton setTitle:@"1" forState:UIControlStateNormal];}if (j == 2) {[_roundButton setTitle:@"2" forState:UIControlStateNormal];}if (j == 3) {[_roundButton setTitle:@"3" forState:UIControlStateNormal];}_roundButton.tag = 100 + j;}if (j == 4) {_roundButton.backgroundColor = [UIColor orangeColor];if (i == 1) {[_roundButton setTitle:@"÷" forState:UIControlStateNormal];_roundButton.tag = 114;} else if (i == 2) {[_roundButton setTitle:@"×" forState:UIControlStateNormal];_roundButton.tag = 115;} else if (i == 3) {[_roundButton setTitle:@"-" forState:UIControlStateNormal];_roundButton.tag = 116;} else {[_roundButton setTitle:@"+" forState:UIControlStateNormal];_roundButton.tag = 117;}}}}//最后一行   一个零 一个点 一个等号for (int k = 0; k < 3; k++) {if (k == 0) {_roundButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];[_roundButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];[self addSubview:_roundButton];//设置按钮圆角_roundButton.layer.cornerRadius = 45;//边框宽度_roundButton.layer.borderWidth = 1;[_roundButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];_roundButton.titleLabel.font = [UIFont systemFontOfSize:45];[_roundButton mas_makeConstraints:^(MASConstraintMaker *make) {make.top.equalTo(@(self.frame.size.height/3 + 100 * 4 + 80));make.left.equalTo(@(10 + 0 * 100));make.height.equalTo(@90);make.width.equalTo(@190);}];[_roundButton setTitle:@"0" forState:UIControlStateNormal];_roundButton.backgroundColor = [UIColor darkGrayColor];_roundButton.tag = 100;} else {_roundButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];[_roundButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];[self addSubview:_roundButton];//设置按钮圆角_roundButton.layer.cornerRadius = 45;//边框宽度_roundButton.layer.borderWidth = 1;[_roundButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];_roundButton.titleLabel.font = [UIFont systemFontOfSize:45];[_roundButton mas_makeConstraints:^(MASConstraintMaker *make) {make.top.equalTo(@(self.frame.size.height/3 + 100 * 4 + 80));make.left.equalTo(@(10 + (k+1) * 100));make.height.equalTo(@90);make.width.equalTo(@90);}];if (k == 1) {[_roundButton setTitle:@"." forState:UIControlStateNormal];_roundButton.backgroundColor = [UIColor darkGrayColor];_roundButton.tag = 110;}if (k == 2) {[_roundButton setTitle:@"=" forState:UIControlStateNormal];_roundButton.backgroundColor = [UIColor orangeColor];_roundButton.tag = 118;}}}

Model

#import "CalculatorModel.h"@implementation CalculatorModel- (void) pushNum:(double)num {if (_numArray.count == 50) {NSLog(@"进栈失败");return;}//NSLog(@"%f", num);[_numArray addObject:[NSString stringWithFormat:@"%f", num]];return;
}- (void) pushChar: (char) c {if (_strArray.count == 50) {NSLog(@"进栈失败");return;}//NSLog(@"%c", c);[_strArray addObject:[NSString stringWithFormat:@"%c", c]];return;
}//字符串转数字
- (double) transNum: (NSString*) string {//NSLog(@"%c", [string characterAtIndex:0]);int minus = 0;double result;if ([string characterAtIndex:0] == '-') {minus = 1;}if (minus == 0) {int i = 0;double x = 0;while ([string characterAtIndex:i] >= '0' &&[string characterAtIndex:i] <= '9' &&i < string.length) {x *= 10 ;x += (double) [string characterAtIndex:i] - '0';//NSLog(@"%f", x);++i;}//存在小数点if (i < string.length && [string characterAtIndex:i] == '.') {double m = 0.1;i++;while (i < string.length) {x += ((double) [string characterAtIndex:i] - '0') * m;m *= 0.1;++i;}}result = x;} else {                //int i = 1;double x = 0;while ([string characterAtIndex:i] >= '0' &&[string characterAtIndex:i] <= '9' &&i < string.length) {x *= 10;x += (double)[string characterAtIndex:i] - '0';++i;}//存在小数点if (i < string.length && [string characterAtIndex:i] == '.') {double m = 0.1;++i;while (i < string.length) {x += ((double)[string characterAtIndex:i] - '0') * m;m *= 0.1;++i;}}result = x;}if (minus == 0) {return  result;} else {return  -result;}}- (double) popNum {double temp = [self transNum:_numArray[_numArray.count - 1]];[_numArray removeLastObject];return temp;
}- (char) popChar {char temp = [_strArray[_strArray.count - 1] characterAtIndex:0];[_strArray removeLastObject];return temp;
}//返回数字栈栈顶
- (double) topNum {return [self transNum:_numArray[_numArray.count - 1]];
}//返回符号栈栈顶
- (char) topChar {return [_strArray[_strArray.count - 1] characterAtIndex:0];
}//比较符号优先级
- (NSInteger) compare:(char) str {if (str == '+' || str == '-') {return 1;} else if (str == '*' || str == '/') {return 2;} else {return 0;}
}//计算函数
- (double) counter:(double) first :(double) second :(char) counterStr {double ans = 0.0;if (counterStr == '+') {ans = first + second;} else if (counterStr == '-') {ans = second - first;} else if (counterStr == '*') {ans = first * second;} else {ans = second / first;}return ans;
}- (NSInteger) emptyNum{NSInteger cnt = _numArray.count;[_numArray removeAllObjects];return cnt;
}- (NSInteger) emptyChar {NSInteger cnt = _strArray.count;[_strArray removeAllObjects];return cnt;
}@end

计算表达式:

- (void) calculation {CalculatorModel* Model = [[CalculatorModel alloc]init];Model.numArray = [[NSMutableArray alloc]init];Model.strArray = [[NSMutableArray alloc]init];Model.expression = [NSMutableString stringWithString:_mainView.numLabel.text];NSMutableString* newString = [NSMutableString stringWithString:Model.expression];[newString appendString:@"#"];char* cStr = (char*)[newString UTF8String];/*          NSMutableArray* arrayStack = [[NSMutableArray alloc]init];for (int i = 0; i < Model.expression.length; i++) {if (cStr[i] == '+' || cStr[i] == '-') {if ([arrayStack.lastObject isEqualToString:@"*"] ||[arrayStack.lastObject isEqualToString:@"/"]) {}} else if (cStr[i] == '*' || cStr[i] == '/') {} else if (cStr[i] == ')') {} else {}}*/int index = 0;double x = 0;double numberFirst, numberSecond;char testChar;double lastResult = 0.0;while (cStr[index] != '#') {x = 0;//判断是数字入数字栈if (cStr[index] >= '0' && cStr[index] <= '9') {while (cStr[index] >= '0' && cStr[index] <= '9') {x = x * 10;x = x + cStr[index] - '0';index++;}//判断小数点if (cStr[index] == '.') {double t = 0.1;index++;while (cStr[index] >= '0' && cStr[index] <= '9') {x += (t * (cStr[index] - '0'));t *= 0.1;index++;}}//将得到的数字送进数字栈[Model pushNum:x];continue;}//判断是符号送进符号栈,送符号进栈时确保符号栈空if (Model.strArray.count == 0 && (cStr[index] == '+' || cStr[index] == '-' || cStr[index] == '*' || cStr[index] == '/' || cStr[index] == '(' || cStr[index] == ')')) {[Model pushChar:cStr[index]];index++;continue;}//左括号直接入栈if (cStr[index] == '(') {[Model pushChar:cStr[index]];index++;continue;}//右括号需要循环取出符号栈的符号,同时循环每次取两个数字栈的数字运算后,放回数字栈,直到从符号栈取出左括号if (cStr[index] == ')') {while ( [Model topChar] != '(') {testChar = [Model popChar];numberFirst = [Model popNum];numberSecond = [Model popNum];lastResult = [Model counter:numberFirst :numberSecond :testChar];//计算结果入数字栈[Model pushNum:lastResult];}//左括号出栈[Model popChar];index++;continue;}//入符号栈if ([Model compare:cStr[index]] <= [Model compare:[Model topChar]]) {testChar = [Model popChar];numberFirst = [Model popNum];numberSecond = [Model popNum];lastResult = [Model counter:numberFirst :numberSecond :testChar];[Model pushNum:lastResult];}[Model pushChar:cStr[index]];index++;}//若是符号栈还不为空while (Model.strArray.count > 0) {testChar = [Model popChar];numberFirst = [Model popNum];numberSecond = [Model popNum];lastResult = [Model counter:numberFirst :numberSecond :testChar];[Model pushNum:lastResult];}NSString* testString = [NSString stringWithFormat:@"%f", lastResult];float k = (float) lastResult;int n = (int) k;NSMutableString* lastString;if ((k - n) <= 0.001 && k >= n) {lastString = [NSMutableString stringWithFormat:@"%d", n];} else if((n-k) <= 0.001 && k < n) {lastString = [NSMutableString stringWithFormat:@"%d", n];} else {if (k>=n) {lastString = [NSMutableString stringWithFormat:@"%@", @(testString.floatValue)];} else {lastString = [NSMutableString stringWithFormat:@"%@", @(testString.floatValue)];}}_mainView.numLabel.text = lastString;[_mainView.numLabel reloadInputViews];_ACSign = 0;
}

这次的计算器bug频出,究其原因是在写限制输入的时候没有构思好就写,导致后面bug改完一个,新的就又漏出来,必要处注释缺少。以后的项目会越来越难,必须认真对待,少写bug,改正坏习惯。

【iOS】仿写iOS计算器总结相关推荐

  1. 如何仿写iOS微信打飞机

    仿写打飞机学习总结 一.Android微信打飞机与iOS微信打飞机的区别 1.Android打飞机,同一时间屏幕上不止存在一个子弹,可能有三颗或更多,而iOS上的打飞机任意时间屏幕上至多存在一个子弹. ...

  2. iOS仿写计算器中缀转后缀 表达式求值

    github地址 iOS计算器 准备工作 由于OC没有自带的栈文件,只能链表模拟栈 typedef struct {char data[50];int top; }Stack;//符号栈 //之前把D ...

  3. 【iOS 仿写计算器】

    大概思路 这是第一次使用MVC模式写 ,思路就是先写UI,之后根据UI设置的Button来嵌入之前的算法完成 代码的View布局用到了Masonry布局 View界面 View界面用到了Masonry ...

  4. iOS 仿写项目之微信聊天界面、QQ聊天界面

    消息格式 文本 语音 图片 位置 拍照 视频 名片 通知 红包 表情 Gif 阅后即焚 功能 消息长按与点击 头像长按与点击 消息发送状态 消息重发 消息点击 语音播放与暂停 消息发送状态 微信版 输 ...

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

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

  6. 礼物说仿写项目iOS源码

    礼物说仿写(updating...) 源码下载: http://code.662p.com/view/14507.html api: 礼物说 首页精选 banner2: http://api.liwu ...

  7. iOS 仿看了吗应用、指南针测网速等常用工具、自定义弹出视图框架、图片裁剪、内容扩展等源码...

    iOS精选源码 扩展内容的cell - folding-cell 一个近乎完整的可识别中国身份证信息的Demo 可自动快速... JPImageresizerView 仿微信的图片裁剪 带年月和至今以 ...

  8. [iOS]仿微博视频边下边播之滑动 TableView 自动播放

    注意:框架已经迭代到2.0版本,我重新架构了整个框架,API 也得到了更好的设计,我为 2.0 版本的实现写了一篇文章 [iOS]如何重新架构 JPVideoPlayer ?.此文中的实现思路仍然是一 ...

  9. [iOS]仿微博视频边下边播之滑动TableView自动播放

    Tips:这次的内容分为两篇文章讲述 01.[iOS]仿微博视频边下边播之封装播放器 讲述如何封装一个实现了边下边播并且缓存的视频播放器. 02.[iOS]仿微博视频边下边播之滑动TableView自 ...

最新文章

  1. Spring Security源码分析八:Spring Security 退出
  2. 渗透知识-sqlmap
  3. adaboost算法java_Adaboost 算法实例解析
  4. 科技感的动态设计方法-2
  5. 二层交换机、三层交换机和路由器的基本工作原理和三者之间的主要区别
  6. 如何使用Cmder替换cmd
  7. go get 的不再src目录中_Go 每日一库之 sqlc:根据 sql 生成代码
  8. C++ class中的静态(static)成员
  9. 首页 系统相关 正文 小米手机远程和电脑互相连接及控制的方法
  10. 机器视觉的典型应用及领域分析
  11. 程序员眼中的“鼠标宏”
  12. .rpt 文件怎麽打開?Crystal Reports 打開.rpt失敗 ?
  13. 五种常用的绩效考核工具对比
  14. android 状态栏为白色的时候图标不显示的解决方案
  15. 洛谷1146 硬币翻转
  16. QEMU 简介(一)
  17. Excel数据分析案例三——预测销量
  18. jsp的内置对象有哪些,四大作用域有哪些??
  19. StarUML3.0的破解方法
  20. 【原创】遥感影像法分析河流演变

热门文章

  1. 十种深度学习算法要点及代码解析(转)
  2. 如何使用logcat和getevent查看遥控器的ir key,linux key和android key值
  3. 鸿蒙系统有没有红外遥控,红外遥控与蓝牙遥控的区别
  4. Linux scp远程文件/目录传输 用ps和grep命令寻找僵尸进程
  5. Unity3D-计算帧率FPS
  6. 小程序 formid 生成_微信电子计次卡生成小程序
  7. 微信小程序推广方式,不再千遍一律,看看这种方式是否适合你
  8. 【算法学习笔记】67.状态压缩 DP SJTU OJ 1383 畅畅的牙签袋
  9. html页面中访问外站资源的时候协议的问题
  10. 怎么样防止服务器被入侵