【iOS】仿写iOS计算器总结
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计算器总结相关推荐
- 如何仿写iOS微信打飞机
仿写打飞机学习总结 一.Android微信打飞机与iOS微信打飞机的区别 1.Android打飞机,同一时间屏幕上不止存在一个子弹,可能有三颗或更多,而iOS上的打飞机任意时间屏幕上至多存在一个子弹. ...
- iOS仿写计算器中缀转后缀 表达式求值
github地址 iOS计算器 准备工作 由于OC没有自带的栈文件,只能链表模拟栈 typedef struct {char data[50];int top; }Stack;//符号栈 //之前把D ...
- 【iOS 仿写计算器】
大概思路 这是第一次使用MVC模式写 ,思路就是先写UI,之后根据UI设置的Button来嵌入之前的算法完成 代码的View布局用到了Masonry布局 View界面 View界面用到了Masonry ...
- iOS 仿写项目之微信聊天界面、QQ聊天界面
消息格式 文本 语音 图片 位置 拍照 视频 名片 通知 红包 表情 Gif 阅后即焚 功能 消息长按与点击 头像长按与点击 消息发送状态 消息重发 消息点击 语音播放与暂停 消息发送状态 微信版 输 ...
- 【iOS】—— 仿写计算器项目总结
仿写计算器 在完成这次仿写任务时,和以前所写的项目最大的区别时首次运用到了MVC模式和Masonry界面,并且在计算器使用的过程中运用了很多的算法知识,在整个过程中会出现特别多的error,以及很神奇 ...
- 礼物说仿写项目iOS源码
礼物说仿写(updating...) 源码下载: http://code.662p.com/view/14507.html api: 礼物说 首页精选 banner2: http://api.liwu ...
- iOS 仿看了吗应用、指南针测网速等常用工具、自定义弹出视图框架、图片裁剪、内容扩展等源码...
iOS精选源码 扩展内容的cell - folding-cell 一个近乎完整的可识别中国身份证信息的Demo 可自动快速... JPImageresizerView 仿微信的图片裁剪 带年月和至今以 ...
- [iOS]仿微博视频边下边播之滑动 TableView 自动播放
注意:框架已经迭代到2.0版本,我重新架构了整个框架,API 也得到了更好的设计,我为 2.0 版本的实现写了一篇文章 [iOS]如何重新架构 JPVideoPlayer ?.此文中的实现思路仍然是一 ...
- [iOS]仿微博视频边下边播之滑动TableView自动播放
Tips:这次的内容分为两篇文章讲述 01.[iOS]仿微博视频边下边播之封装播放器 讲述如何封装一个实现了边下边播并且缓存的视频播放器. 02.[iOS]仿微博视频边下边播之滑动TableView自 ...
最新文章
- Spring Security源码分析八:Spring Security 退出
- 渗透知识-sqlmap
- adaboost算法java_Adaboost 算法实例解析
- 科技感的动态设计方法-2
- 二层交换机、三层交换机和路由器的基本工作原理和三者之间的主要区别
- 如何使用Cmder替换cmd
- go get 的不再src目录中_Go 每日一库之 sqlc:根据 sql 生成代码
- C++ class中的静态(static)成员
- 首页 系统相关 正文 小米手机远程和电脑互相连接及控制的方法
- 机器视觉的典型应用及领域分析
- 程序员眼中的“鼠标宏”
- .rpt 文件怎麽打開?Crystal Reports 打開.rpt失敗 ?
- 五种常用的绩效考核工具对比
- android 状态栏为白色的时候图标不显示的解决方案
- 洛谷1146 硬币翻转
- QEMU 简介(一)
- Excel数据分析案例三——预测销量
- jsp的内置对象有哪些,四大作用域有哪些??
- StarUML3.0的破解方法
- 【原创】遥感影像法分析河流演变
热门文章
- 十种深度学习算法要点及代码解析(转)
- 如何使用logcat和getevent查看遥控器的ir key,linux key和android key值
- 鸿蒙系统有没有红外遥控,红外遥控与蓝牙遥控的区别
- Linux scp远程文件/目录传输 用ps和grep命令寻找僵尸进程
- Unity3D-计算帧率FPS
- 小程序 formid 生成_微信电子计次卡生成小程序
- 微信小程序推广方式,不再千遍一律,看看这种方式是否适合你
- 【算法学习笔记】67.状态压缩 DP SJTU OJ 1383 畅畅的牙签袋
- html页面中访问外站资源的时候协议的问题
- 怎么样防止服务器被入侵