前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Swift封装 - 计算器开发

Swift封装 - 计算器开发

作者头像
Dwyane
发布2018-05-22 17:50:56
1.2K0
发布2018-05-22 17:50:56
举报
文章被收录于专栏:技术总结技术总结

image.png

前言:

师弟要毕业设计,就敲了swift版的计算器给他参考下。现在把代码放上来,通过这个计算器,可以学习简单的封装:将逻辑与界面分离并提供接口的编程方式,这也是我们学习面向对象的必要点。

基于 xcode 9.0 swift4.0

一、先引用SnapKit框架 SnapKit自己看git引入 利用其来约束组件

二、新建一个继承UIButton类的类文件,命名为DWFuncButton,对其设置字体、颜色、风格代码如下:

代码语言:javascript
复制
class DWFuncButton: UIButton {

    init() {
        super.init(frame: CGRect.zero)
        //为按钮添加边框
        self.layer.borderWidth = 0.5;
        self.layer.borderColor = UIColor(red: 219/255.0, green: 219/255.0, blue: 219/255.0, alpha: 1).cgColor
        //设置字体与字体颜色
        self.setTitleColor(UIColor.orange, for: .normal)
        self.titleLabel?.font = UIFont.systemFont(ofSize: 25)
        self.setTitleColor(UIColor.black, for: .highlighted)
        
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

三、创建一个继承UIView的类,命名为DWBoard,将其用作计算器的操作面板 首先引入SnapKit框架 import SnapKit 先创建一个数组属性,存放操作面板上的所有功能按钮标题

代码语言:javascript
复制
var dataArray = ["0", ".", "%", "="
                , "1", "2", "3", "+"
                , "4", "5", "6", "-"
                , "7", "8", "9", "*"
                 , "AC", "DEL", "^", "/"]

重写父类的构造方法,在其中进行界面的加载操作:

代码语言:javascript
复制
override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
代码语言:javascript
复制
//对界面进行布局
func setupUI() {
    //创建一个变量 用于保存当前布局按钮的上一个按钮
    var frontBtn: DWFuncButton!
    //进行功能按钮的循环创建
    for index in 0..<20 {
        //创建一个功能按钮
        let btn = DWFuncButton()
        self.addSubview(btn)
        //约束
        btn.snp.makeConstraints({ (make) in
            //当按钮每一行的第一个时,将其靠左侧摆放
            if index%4 == 0 {
                make.left.equalTo(0)
            }else { //否则将按钮的左边考上一个右侧进行摆放
                make.left.equalTo(frontBtn.snp.right)
            }
            //当按钮为第一行,将其靠父视图底部摆放
            if index/4 == 0 {
                make.bottom.equalTo(0)
            }else if index%4 == 0 { //当按钮不在第一行且为每行的第一个时,将其底部与上一个按钮的顶部对齐
                make.bottom.equalTo(frontBtn.snp.top)
                //否则将其底部与上一个按钮底部对齐整
            }else {
                make.bottom.equalTo(frontBtn.snp.bottom)
            }
            //约束宽度为父视图宽度的0.25倍
            make.width.equalTo(btn.superview!.snp.width).multipliedBy(0.25)
            //约束高度为父视图宽度的0.2倍
            make.height.equalTo(btn.superview!.snp.height).multipliedBy(0.2)
        })
        
        //设置tag值
        btn.tag = index + 100
        //添加点击事件
        btn.addTarget(self, action: #selector(btnClick(_:)), for: .touchUpInside)
        //设置标题
        btn.setTitle(dataArray[index], for: .normal)
        //对上一个按钮更新保存
        frontBtn = btn
    }
    
}

上面就构建了一个简单的键盘界面,约束代码大家可以看一下,排版为5行4列,布局顺序为从下向上、从左向右依次布局

创建上述代码的点击方法

代码语言:javascript
复制
@objc func btnClick(_ button:DWFuncButton) {
        print(button.currentTitle as Any)
    }

用户在操作面板上进行输入操作,在计算器的显示屏上还需要显示输入的内容,同时,显示屏还兼有计算结果的功能。 首先在DWCalculator工程上新建一个名为DWScreen的类文件,继承自UIView,作为计算器的显示器控件。显示屏分成两部分,一部分用于计算结果,一部分用于显示用户输入的计算过程,所以用两个UILabel来处理。

代码语言:javascript
复制
class DWScreen: UIView {

    var inputLabel:UILabel?
    //用于显示历史记录信息
    var historyLabel:UILabel?
    //用户输入表达式或者计算结果字符串
    var inputString = ""
    //历史表达式字符串
    var historyString = ""
    //所有数字字符 用于进行检测匹配
    let figureArray:Array<Character> = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "."]
    //所有运算功能字符 用于进行检测匹配
    let funcArray = ["+", "-", "*", "/", "^"]
    init() {
        super.init(frame: CGRect.zero)
        inputLabel = UILabel()
        historyLabel = UILabel()
        setupUI()
    }
    
    func setupUI() {
        //设置文字的对其方式为右对齐
        inputLabel?.textAlignment = .right
        historyLabel?.textAlignment = .right
        //设置字体
        inputLabel?.font = UIFont.systemFont(ofSize: 34)
        historyLabel?.font = UIFont.systemFont(ofSize: 30)
        //设置文字颜色
        inputLabel?.textColor = UIColor.orange
        historyLabel?.textColor = UIColor.black
        //设置文字大小根据字数进行适配
        inputLabel?.adjustsFontSizeToFitWidth = true
        inputLabel?.minimumScaleFactor = 0.5  //最小字体为当前字体的一半
        historyLabel?.adjustsFontSizeToFitWidth = true
        historyLabel?.minimumScaleFactor = 0.5
        //设置文字的截断方式
        inputLabel?.lineBreakMode = .byTruncatingHead
        historyLabel?.lineBreakMode = .byTruncatingHead
        //设置文字的行数
        inputLabel?.numberOfLines = 0
        historyLabel?.numberOfLines = 0
        
        self.addSubview(inputLabel!)
        self.addSubview(historyLabel!)
        //进行自动布局
        inputLabel?.snp.makeConstraints({ (make) in
            make.left.equalTo(10)
            make.right.equalTo(-10)
            make.bottom.equalTo(-10)
        make.height.equalTo(inputLabel!.superview!.snp.height).multipliedBy(0.5).offset(-10)
        })
        historyLabel?.snp.makeConstraints({ (make) in
            make.left.equalTo(10)
            make.right.equalTo(-10)
            make.top.equalTo(10)
            make.height.equalTo(inputLabel!.superview!.snp.height).multipliedBy(0.5).offset(-10)
        })
    }
    
    //提供一个输入信息的接口
    func inputContent(content:String) {
        inputString.append(content)
        inputLabel?.text = inputString
    }
    
    //提供一个刷新历史记录的方法
    func refreshHistory() {
        historyString = inputString
        historyLabel?.text = historyString
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

DWBoard类可以接收用户的输入,DWScreen需要获取用户的输入,他们之间的关联是需要通过ViewController类来完成的。使用代理设计模式完成此功能。

在DWBoard.swift添加协议代码

代码语言:javascript
复制
protocol DWBoardButtonInputDelegate {
    func boardButtonClick(content:String)
}

在DWBoard类添加一个代理属性:

代码语言:javascript
复制
var delegate:DWBoardButtonInputDelegate?

修改DWBoard类中的点击事件

代码语言:javascript
复制
@objc func btnClick(_ button:DWFuncButton) {
    if delegate != nil {
        //通过协议方法将值传递出去
        delegate?.boardButtonClick(content: button.currentTitle!)
    }
}

ViewController类也需要将DWBoard类实例和DWScreen类实例作为自己的属性,相互调用

代码语言:javascript
复制
let board = DWBoard()
let screen = DWScreen()

在viewDidLoad()方法添加setupUI(),并且setupUI代码如下

代码语言:javascript
复制
func setupUI() {
    self.view.addSubview(board)
    //设置代理
    board.delegate = self
    board.snp.makeConstraints { (make) in
        make.left.equalTo(0)
        make.right.equalTo(0)
        make.bottom.equalTo(0)
        make.height.equalTo(board.superview!.snp.height).multipliedBy(2/3.0)
    }
    
    self.view.addSubview(screen)
    screen.snp.makeConstraints { (make) in
        make.left.equalTo(0)
        make.right.equalTo(0)
        make.top.equalTo(0)
        make.bottom.equalTo(board.snp.top)
    }
}

viewController需要遵守DWBoardButtonInputDelegate协议:

代码语言:javascript
复制
class ViewController: UIViewController,DWBoardButtonInputDelegate

并且实现协议方法:

代码语言:javascript
复制
func boardButtonClick(content: String) {
    if content == "AC" || content == "DEL" || content == "=" {
        //进行逻辑处理
        screen.refreshHistory()
    }else {
        screen.inputContent(content:content)
    }
}

运行项目,如下图:

界面部分我们已经基本开发完,接下来进行逻辑处理类的封装。

三、计算器计算逻辑: DWScreen类需要继续完善。例如当用户点击清空按钮时,输入的计算表达就应该被清空。当用户点击回退按钮时,上一次输入的字符就应该被清空。在DWScreen类添加如下代码:

代码语言:javascript
复制
//清空显示屏当前输入的信息
func clearContent() {
    inputString = ""
}

//删除显示屏中上次输入的字符
func deleteInput() {
    if inputString.characters.count>0 {
        inputString.remove(at: inputString.index(before: inputString.endIndex))
        inputLabel?.text = inputString
    }
}

在项目中新建一个继承于NSObject的类文件,并命名为DWCalculatorEngine。将其作为计算引擎工具类,代码如下:

代码语言:javascript
复制
class DWCalculatorEngine: NSObject {
    //运算符集合
    let funcArray:CharacterSet = ["+", "-", "*", "/", "^", "%"]
    func calculatEquation(equation:String)->Double {
        //以运算符进行分割获取到所有数字
        let elementArray = equation.components(separatedBy: funcArray)
        //设置一个运算标记游标
        var tip = 0
        //运算结果
        var result:Double = Double(elementArray[0])!
        //遍历计算表达式
        for char in equation.characters {
            switch char {
                //进行加法运算
            case "+":
                tip += 1
                if elementArray.count>tip {
                    result += Double(elementArray[tip])!
                }
                //进行减法运算
            case "-":
                tip += 1
                if elementArray.count>tip {
                    result -= Double(elementArray[tip])!
                }
            case "*":
                tip += 1
                if elementArray.count>tip {
                    result *= Double(elementArray[tip])!
                }
                //进行除法运算
            case "/":
                tip += 1
                if elementArray.count>tip {
                    result /= Double(elementArray[tip])!
                }
                //进行取余运算
            case "%":
                tip += 1
                if elementArray.count>tip {
                    result = Double(Int(result)%Int(elementArray[tip])!)
                }
                //进行指数运算
            case "^":
                tip += 1
                if elementArray.count>tip {
                    let tmp = result
                    for _ in 1..<Int(elementArray[tip])! {
                        result *= tmp
                    }
                }
            default:
                break
            }
        }
        return result
    }
}

在ViewController类中添加两个属性:一个计算工具类:

代码语言:javascript
复制
//计算引擎实例
let calcalator = DWCalculatorEngine()
//这个输入是否需要刷新显示屏
var isNew = false

isNew属性主要作用是标记本次输入是否需要将显示屏已有的内容清除。当用户完成一次计算后,计算结果会显示在显示屏上。此时如果用户继续输入,则进行下一轮的计算,显示屏的上次结果应该被清空。 修改ViewController类中的协议方法

代码语言:javascript
复制
func boardButtonClick(content: String) {
    if content == "AC" || content == "DEL" || content == "=" {
        //进行逻辑处理
        switch content {
        case "AC":
            screen.clearContent()
            screen.refreshHistory()
        case "DEL":
            screen.deleteInput()
        case "=":
            let result = calcalator.calculatEquation(equation: screen.inputString)
            //先刷新历史
            screen.refreshHistory()
            //清除输入的内容
            screen.clearContent()
            //将结果输入
            screen.inputContent(content: String(result))
            isNew = true
        default:
            screen.refreshHistory()
        }
        
    }else {
        if isNew {
            screen.clearContent()
            isNew = false
        }
        screen.inputContent(content:content)
    }
}

大功告成!代码传送门

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.11.10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言:
  • 基于 xcode 9.0 swift4.0
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档