iOS--仿写计算器

计算器的大概思路
首先需要满足一些基本要求,即使用MVC模式写代码,这里只有一个界面,所以没有设置多余的控制器 ;且代码的View要用到之前学过的Masonry布局 ;

view
在MVC中view的功能就是获取Ui界面设计的视图对象
UI:
iOS--仿写计算器_第1张图片
因为编译器不小心更新了,虚拟机宽高也变了,所以可能布局看起来有点问题 ;

view 部分大概为一个textfield和多个按钮 ;且在MVC模式下,应该保证在controller里面任然可以访问到view中的视图对象,所以这里的多个按钮我们选择循环创建,但要存入一个数组中去(不然设多个属性代码量太大) ;

  for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 4; j++) {
            NSString* str = self.buttonarray[j + i * 4] ;
            
            
            if (i == 0 && j < 3) {
                
                UIButton* btn = [UIButton buttonWithType:UIButtonTypeCustom] ;
                [btn setTitle:str forState:UIControlStateNormal] ;
                btn.titleLabel.font = [UIFont systemFontOfSize:20] ;
                [self addSubview:btn] ;
                [btn mas_makeConstraints:^(MASConstraintMaker *make) {
                    make.left.mas_equalTo(30 + j * 90) ;
                    make.top.mas_equalTo(320 + i * 90) ;
                    make.width.mas_equalTo(75) ;
                    make.height.mas_equalTo(75) ;
                }];
                btn.layer.cornerRadius = 75 /2 ;
                btn.layer.masksToBounds = YES ;
                btn.layer.borderWidth = 2.0 ;
                [btn setTitleColor:UIColor.whiteColor forState:UIControlStateNormal] ;
                btn.backgroundColor = UIColor.grayColor ;
                btn.tag = j + i * 4 ;
                [self.btnarray addObject:btn] ;
                
                
            } else if (i != 0 && j < 3) {
                if (i == 4 && j == 0) {
                    UIButton* btn = [UIButton buttonWithType:UIButtonTypeCustom] ;
                    [btn setTitle:str forState:UIControlStateNormal] ;
                    btn.titleLabel.font = [UIFont systemFontOfSize:20] ;
                    [self addSubview:btn] ;
                    [btn mas_makeConstraints:^(MASConstraintMaker *make) {
                        make.left.mas_equalTo(30 + j * 90) ;
                        make.top.mas_equalTo(320 + i * 90) ;
                        make.width.mas_equalTo(150) ;
                        make.height.mas_equalTo(75) ;
                    }];
                    btn.layer.cornerRadius = 75/2 ;
                    btn.layer.masksToBounds = YES ;
                    btn.layer.borderWidth = 2.0 ;
                    [btn setTitleColor:UIColor.whiteColor forState:UIControlStateNormal] ;
                    btn.backgroundColor = UIColor.darkGrayColor ;
                    btn.tag = j + i * 4 ;
                    [self.btnarray addObject:btn] ;
                    j++ ;
                } else {
                    UIButton* btn = [UIButton buttonWithType:UIButtonTypeCustom] ;
                    [btn setTitle:str forState:UIControlStateNormal] ;
                    btn.titleLabel.font = [UIFont systemFontOfSize:20] ;
                    [self addSubview:btn] ;
                    [btn mas_makeConstraints:^(MASConstraintMaker *make) {
                        make.left.mas_equalTo(30 + j * 90) ;
                        make.top.mas_equalTo(320 + i * 90) ;
                        make.width.mas_equalTo(75) ;
                        make.height.mas_equalTo(75) ;
                    }];
                    btn.layer.cornerRadius = 75/2 ;
                    btn.layer.masksToBounds = YES ;
                    btn.layer.borderWidth = 2.0 ;
                    [btn setTitleColor:UIColor.whiteColor forState:UIControlStateNormal] ;
                    btn.backgroundColor = UIColor.grayColor ;
                    btn.tag = j + i * 4 ;
                    [self.btnarray addObject:btn] ;
                    
                }
            } else {
                UIButton* btn = [UIButton buttonWithType:UIButtonTypeCustom] ;
                [btn setTitle:str forState:UIControlStateNormal] ;
                btn.titleLabel.font = [UIFont systemFontOfSize:20] ;
                [self addSubview:btn] ;
                [btn mas_makeConstraints:^(MASConstraintMaker *make) {
                    make.left.mas_equalTo(30 + j * 90) ;
                    make.top.mas_equalTo(320 + i * 90) ;
                    make.width.mas_equalTo(75) ;
                    make.height.mas_equalTo(75) ;
                }];
                btn.layer.cornerRadius = 75 / 2 ;
                btn.layer.masksToBounds = YES ;
                btn.layer.borderWidth = 2.0 ;
                [btn setTitleColor:UIColor.whiteColor forState:UIControlStateNormal] ;
                btn.backgroundColor = UIColor.orangeColor ;
                btn.tag = j + i * 4 ;
                [self.btnarray addObject:btn] ;
            }
        }
    }

注意上面按钮的布局我们使用了第三方库masonry为button对象设置限制来实现位置布局,可以比较方便的设置更多功能,比如自适应布局(不过我这里完全没体现就是了) ;

还有就是设置圆按钮的方法 ;

                btn.layer.cornerRadius = 75 /2 ;//设置弧度,圆形即为长宽的一半 ;
                btn.layer.masksToBounds = YES ;
                btn.layer.borderWidth = 2.0 ;//设置边框厚度

Model

@interface calculatorModel : NSObject
@property (nonatomic, strong) NSString* stackstr ;
@property (nonatomic, strong) NSMutableArray* tagarray ;

- (void)initModel ;
@end


@implementation calculatorModel
- (void)initModel {
    self.stackstr = [[NSString alloc] init] ;
    self.tagarray =  [NSMutableArray arrayWithObjects:@"AC", @"(", @")", @"/", @"7", @"8", @"9", @"*", @"4", @"5", @"6", @"-", @"1", @"2", @"3", @"+", @"0", @"NO", @".", @"=", nil] ;
    
}
@end

这里我设置了两个数组,一个用来存储按钮输入的字符串,一个用来存储按钮本身的值,(我看学长有的用tag值来实现传值,但我感觉那样不太符合MVc模式的理念,我认为可以应该将view中的对象能够在controller全部访问,model则用于数据存储) ;

controller
controller中应完成用户交互(点击函数)和数据处理(计算) ;
先看看数据处理
计算的原理:
1.将中缀表达式转化为后缀表达式 ;
2.进行后缀表达式的运算 ;

这两步可以看看我之前的博客,这里就补充一下负数的计算 ;

else if   (str[i] == '(') {
            if (str[i + 1] == '-') {
                str[i + 1] = '!' ;
            }
            stack02[++top02] = str[i++] ;
            //将左括号前的减号改为负号,设置为!

 if (str[0] == '-') {
        str[0] = '!' ;
    }//特殊情况,即开头减号为负号


else if (stack01[i] == '!') {
            double b = numstack[top03--] ;
            double c = -b ;
            numstack[++top03] = c ;
            i = i +2 ;
        } //负号的计算

点击函数

- (void)presstap : (UIButton*) button {
    NSLog(@"%ld", button.tag) ;
    NSInteger index = button.tag ;
    if (index != 0 && index != 19) {
        NSString* str = self.mModel.tagarray[index] ;
        self.mModel.stackstr =  [self.mModel.stackstr stringByAppendingString:str] ;
        self.mView.textfield.text = self.mModel.stackstr ;
//        NSLog(@"%@",self.mModel.stackstr) ;
        
    } else if (index == 0) {
        self.mModel.stackstr = [[NSString alloc] init] ;
        self.mView.textfield.text = self.mModel.stackstr ;
    } else {
        if (self.mModel.stackstr.length != 0 && [self juicerror:self.mModel.stackstr]) {
          
            const char* Cstr = [self.mModel.stackstr UTF8String] ;
            NSLog(@"%s",Cstr) ;
            NSLog(@"%lf",[self calculatepress:Cstr]) ;
            double x = [self calculatepress:Cstr] ;
            NSNumber* number = [NSNumber numberWithDouble:x] ;
            NSNumberFormatter* numberformatter = [[NSNumberFormatter alloc] init] ;
            numberformatter.numberStyle = NSNumberFormatterDecimalStyle ;
            numberformatter.minimumFractionDigits = 0 ;
            numberformatter.maximumFractionDigits = 8 ;
            numberformatter.minimumIntegerDigits = 1 ;
            self.mModel.stackstr = [numberformatter stringFromNumber:number] ;
//            self.mModel.stackstr = [NSString stringWithFormat:@"%lf",x] ;
            self.mView.textfield.text = self.mModel.stackstr ;
            
        } else {
            self.mModel.stackstr = @"error" ;
            self.mView.textfield.text = self.mModel.stackstr ;
        }
    }
    
}

其中这一段代码实现了除去多余无用的0 ;

NSNumber* number = [NSNumber numberWithDouble:x] ;
            NSNumberFormatter* numberformatter = [[NSNumberFormatter alloc] init] ;
            numberformatter.numberStyle = NSNumberFormatterDecimalStyle ;
            numberformatter.minimumFractionDigits = 0 ;
            numberformatter.maximumFractionDigits = 8 ;
            numberformatter.minimumIntegerDigits = 1 ;
            self.mModel.stackstr = [numberformatter stringFromNumber:number] ;

可以去了解一下这类对象的用法 ;

if (self.mModel.stackstr.length != 0 && [self juicerror:self.mModel.stackstr])

这里有输入字符串的判错,这也是计算器最麻烦的一部分 ;

以下是我的判错函数,

在这里插入代码片int ordersort (char a) {
    if (a == '(' || a == ')') {
        return 0;
    } else if (a == '+' || a == '-') {
        return 1;
    } else if (a == '*' || a == '/')  {
               return 2;
    } else if (a == '.') {
        return 4;
    } else if (a == '!') {
        return 3;
    } else {
        return -1;
    }
}//判断优先级大小

int matejuice (const char* s) {//匹配括号
    int i = 0 ;
    int length = (int)strlen(s) ;
    int top01 = -1 ;
    while (i < length) {
        if (s[i] == '(') {
            top01++ ;
        }
        if (s[i] == ')') {
            top01-- ;
        }
        i++ ;
    }
    if (top01 == -1) {
        return 1;
    } else {
        return 0;
    }
}

 int calcul_juice (const char* strs) { //判断运算符和数字是否匹配
     char str[1000] ;
     strcpy(str, strs) ;
    char stack01[1000] ;
    int top01 = -1 ;
    char stack02[1000] ;
    int top02 = -1 ;
    int i = 0;//初始化一个数字栈和一个符号栈
   
    
    int len = (int)strlen(str) ;
     if (str[0] == '-') {
         str[0] = '!' ;
     }
//    int flag01 = 1 ;
//    int flag02 = 1 ;
//    double e = 0 ;
    while (i < len) {//遍历输入的字符串,查找数字和符号
        
        printf("badhyfvjwf %d",top02) ;
        if (str[i] >= '0' && str[i] <=  '9') {
            stack01[++top01] = str[i++] ;//遇到数字就将数字压入数字栈中
            if (str[i] <'0' || str[i] > '9') {
                stack01[++top01] = ' ' ;//在每一个数字和运算符之间添加一个空格使它们分隔开来,不然多位数运算会多个数字混在一起 ;
                
            }
            
        } else if   (str[i] == '(') {
            if (str[i + 1] == '-') {
                str[i + 1] = '!' ;
            }
            stack02[++top02] = str[i++] ;
            
        } else if (str[i] == ')') {
            while (stack02[top02] != '(') {
              
                stack01[++top01] = stack02[top02--] ;
                stack01[++top01] = ' ' ;//左右括号的优先级比较特熟,所以在判断符号优先级级前提前判断左右括号,左括号直接压入符号栈,右括号则对符号栈弹栈,并将弹出的符号压入数字栈,直到栈顶为左括号;
               
                
                
            }
            
           
            
            top02-- ;
            i++ ;
            
        } else {
            if (ordersort(str[i]) > ordersort(stack02[top02]) || top02 == -1) {
                stack02[++top02] = str[i++] ;//遇到四则运算符时,比较符号和栈顶的元素的优先级,优先级比栈顶高直接压入符号栈,否则直接弹出栈顶元素压入数字栈,直到栈顶元素优先级低于。。
                
               
            } else {
                while (ordersort(stack02[top02]) >= ordersort(str[i]) && top02 != -1) {
                    stack01[++top01] = stack02[top02--] ;
                    stack01[++top01] = ' ' ;
                }
                stack02[++top02] = str[i++] ;
                
            }
        }
    }
        while (top02 != -1) {
            stack01[++top01] = stack02[top02--] ;
            stack01[++top01] = ' ' ;
        }//遍历完后弹出所有的符号栈元素压入数字栈
    stack01[++top01] = '\0' ;
     
     //获得后缀表达式
     
     
     
     int top03 = -1 ;//初始化一个栈来存储数字

     i = 0;
     double e = 0 ;
     while (i < top01) {
         if (stack01[i] >= '0' && stack01[i] <= '9') {
             while (stack01[i] >= '0' && stack01[i] <= '9') {
                 e = e* 10 + stack01[i] - '0' ;
                 i++ ;//获取多位数
             }
             top03++ ;
             e = 0 ;
             i++ ;
         } else if (stack01[i] == '!') {
             i = i + 2 ;
         } else {
             top03-- ;
             i = i + 2 ;
         }
              }
    
     
     if (top03 == 0) {
         return 1;
     } else {
         return 0;
     }
     
}

int pots_juice ( char* str) {
    printf("%s",str) ;
    
    int length = (int)strlen(str) ;
    int i = 0;
    int flag = 0 ;
    while (i < length) {
        if (str[i] == '.') {
            flag++ ;
        }
        i++ ;
    }
    if (flag >= 2) {
        return 0;
    } else {
        return 1;
    }
}


int pot_juice (const char* strs) {
    
    char str[100] ;
    strcpy(str, strs) ;
    const char s[6] = "+-*/()" ;
    char* token ;
    token = strtok(str, s) ;
//    printf("%s",token) ;
    
    if (pots_juice(token) == 0) {
        
        return 0;
    }
   
    
    while (token != NULL) {
        
        token = strtok(NULL, s) ;
        if (token == NULL) {
            break;
        }
        if (pots_juice(token) == 0) {
            return 0;
        }
    }
    return 1;
   
}



- (int) juicerror : (NSString*) str { //判错
   
    const char* Cstr = [self.mModel.stackstr UTF8String] ;
    NSLog(@"%s",Cstr) ;
    if (ordersort(Cstr[0]) >= 1 && ordersort(Cstr[0]) <= 4) {//判断第一个字符是不是二元运算符
        if (Cstr[0] != '-') {
            printf("error01") ;
            return 0;
        }
    }
    
    if (Cstr[0] == ')') {
        printf("error02") ;
        return 0;
    }
    
    if ([[NSString stringWithFormat:@"%lf",[self calculatepress:Cstr]] isEqualToString:@"inf"] ) {//除法运算分母不为零(即运算结果不能为无穷)
        printf("error03") ;
        return 0;
    }
    
    if (matejuice(Cstr) == 0) {//括号要匹配
        printf("error04") ;
        return 0;
    }
    
    if (calcul_juice(Cstr) == 0) {
        printf("error05") ;
        return 0;
    }
    
    if (pot_juice(Cstr) == 0) {
        printf("error06") ;
        return 0;
    }
    
    if (lastjuice(Cstr) == 0) {
        return 0;
    }
    
    int i = 0 ;
    while (i < (int)strlen(Cstr) - 1) {
        if (ordersort(Cstr[i]) >= 1 && ordersort(Cstr[i]) <= 4) {
            if (ordersort(Cstr[i + 1]) >= 1 && ordersort(Cstr[i + 1]) <= 4) {
                return 0;
            }
        }
        if (Cstr[i] == '(') {
            if (ordersort(Cstr[i + 1]) >= 1 && ordersort(Cstr[i + 1]) <= 3) {
                return 0;
            }
        }
        
        i++ ;
    }
    
    

    
    
    return 1;
    
}



总结
注意好MvC的各自的功能,
判错是重中之重 ;

你可能感兴趣的:(ios)