计算器仿写总结

计算器仿写总结

前言

计算器的主要难点在于表达式校验**、**中缀转后缀(调度场算法) 和 UI 动态适配 的实现细节。项目采用 MVC 架构,最终效果:支持括号嵌套、连续运算、负数输入、表达式实时预览,并能在数字过长时自动缩小字体并水平滚动

UI布局

HomeView 作为主界面控制器,使用Masonry自动布局库,分为三个区域

顶部表达式预览区:显示当前输入的完整算式,支持水平滚动

底部按钮网格:4 列 × 5 行的标准计算器键盘,包含数字、运算符、括号、功能键

底部按钮我们选择循环创建UIButton,循环完之后单独处理一些按钮,比如变背景色和字号色,每个按钮绑定什么方法

对于底部按键需要注意的就是按钮应该有按动的反馈

objc 复制代码
- (void)buttonTouchDown:(UIButton *)sender {
    [UIView animateWithDuration:0.1 animations:^{
        sender.transform = CGAffineTransformMakeScale(0.92, 0.92);
        sender.alpha = 0.75;
    }];
}

- (void)buttonTouchUp:(UIButton *)sender {
    [UIView animateWithDuration:0.1 animations:^{
        sender.transform = CGAffineTransformIdentity;
        sender.alpha = 1.0;
    }];
}

按下时按钮会缩小到0.92,并且透明度会改变,松开按钮时恢复原样

顶部表达式应该有字体逐渐变小和可以滚动的功能

objc 复制代码
- (void)updateScroll:(UIScrollView *)scroll label:(UILabel *)label text:(NSString *)text {
    label.text = text;
    CGFloat fontSize = 70;
    if (text.length > 8) fontSize = 55;
    if (text.length > 12) fontSize = 45;
    if (text.length > 16) fontSize = 35;
    label.font = [UIFont systemFontOfSize:fontSize];

    NSDictionary *attrs = @{NSFontAttributeName : label.font};
    CGFloat textWidth = [text sizeWithAttributes:attrs].width;
    CGFloat labelWidth = MAX(textWidth, scroll.bounds.size.width - 40);
    label.frame = CGRectMake(20, 0, labelWidth, scroll.bounds.size.height);
    scroll.contentSize = CGSizeMake(labelWidth + 40, scroll.bounds.size.height);
    [scroll setContentOffset:CGPointMake(scroll.contentSize.width - scroll.bounds.size.width, 0) animated:NO];
}

这段代码会随着字数的增加变三次字号,这里主要是为了美观

在这里特意强调一下,我们使用scrollerView加上UIlabel,前者可以用Masonry约束,但是后者得使用frame约束,不然不会到自己想到的地方

这个方法的下半段就是计算字符串宽度,因为每个字符的宽度是不一样的,但你的UIlabel不能简单的n * width来计算宽度,之后用结果决定label的宽度,也决定scroll的宽度,实现可以来回滚动

计算

这里主要采用中缀表达式转后缀表达式来进行计算具体可以看这篇文章《数据结构》:中缀表达式转后缀表达式 + 后缀表达式的计算

转了后缀表达式计算之后还要进行一个表达式校验,拦截非法输入,看起来更像是正常的计算器

objc 复制代码
if (self.isCalculated) {
    if (!inputIsOperator) {
        [self.express setString:@""];
    }
    self.isCalculated = NO;
}

比如说按下等号后如果用户继续按数字,清空旧结果,重新开始计算,如果用户按的是运算符,则保留结果

objc 复制代码
for (NSInteger i = self.express.length - 1; i >= 0; i--) {
    unichar ch = [self.express characterAtIndex:i];
    if (ch == '+' || ch == '-' || ch == 0x00D7 || ch == 0x00F7) {
        break;
    }
    if (ch == '.') {
        return;
    }
}

小数点不能跟在运算符号后面,也不能一个数字里塞两个

我们还要考虑有括号的情况,毕竟括号不是运算符号却决定着运算的优先级,比如左括号一定要有对应的有括号,数字前后不能直接跟括号等

还有一个比较重要的地方就是减号和负号的区别了,我们采用这样的逻辑,如果最后一个字符是 -,且它前面是 + - × ÷ (,或者它本身就是第一个字符,那么这个 - 就是负号

objc 复制代码
if (ch == '-') {
        if (i == 0) {
            isNegativeSign = YES;
        } else {
            unichar prev = [self.express characterAtIndex:i - 1];
            if (prev == '+' || prev == '-' || prev == 0x00D7 || prev == 0x00F7 || prev == '(') {
                isNegativeSign = YES;
            }
        }
    }
    if ((ch >= '0' && ch <= '9') || ch == '.' || isNegativeSign) {
        [number appendFormat:@"%C", ch];
    } else {
        if (number.length > 0) {
            [self.infixArray addObject:[number copy]];
            [number setString:@""];
        }
        NSString *op = [NSString stringWithFormat:@"%C", ch];
        [self.infixArray addObject:op];
    }
}

其他需要注意的方面有数学上就非法的输入,比如除以0的情况,系统默认会给你一个inf,不过有个问题就是inf后如果进行运算,inf并不会参与,所以我们干脆直接让结果为error,开始下一次运算,还有其他不合法的输入都可以让结果为error

总结

这个项目的ui难度并不是很难,但是多种多样的情况需要考虑并去处理

相关推荐
小向是个Der5 小时前
嵌入式进阶——MCU上电后的动作
c语言·单片机·mcu·学习·链接脚本
辣知9 小时前
辣知·化智93 被历史尘封的鬻子
学习
2501_9160074710 小时前
抓包工具功能对比,TraceEagle、Charles、Fiddler、Wireshark、mitmproxy 差别
前端·网络协议·ios·adb·https·fiddler·wireshark
每天题库11 小时前
西藏安全员 A 证备考真题题库 强化重点知识刷题提升通过率
学习·安全·考试·题库·考证
Be for thing11 小时前
PyTorch基础|Tensor张量详解,张量创建、数据类型与常用操作
pytorch·学习·1024程序员节
前进的李工11 小时前
Agent 学习笔记 02:Harness 与工作流怎么理解
笔记·学习·agent·工作流·harness
m4Rk_12 小时前
【论文阅读】Agent记忆机制(114):MemoryTax——当 Agent 能自主搜索历史,专门的记忆系统还划算吗?
论文阅读·人工智能·学习·开源·github
程序员梅雨14 小时前
【从零开始学习数据仓库 | 第一篇】:维度建模理论
大数据·数据仓库·学习·阿里云·数据分析
2501_9151063215 小时前
Windows 上开发 Unity iOS,使用 KXBuild 进行真机调试、性能采样与打包 ipa
windows·vscode·unity·ios·个人开发·swift·敏捷流程
keyipatience15 小时前
Git学习(4)Git分布式优势与实战配置全解析
git·学习