Swift 계산기 만들기 (UIKit-Coadbase)

2024. 6. 28. 16:50
반응형

 

 

import UIKit


class ViewController: UIViewController {
    var display = UILabel() // 결과를 표시할 라벨
    var verticalStackView = UIStackView() // 버튼을 배치할 수직 스택뷰
    
    // 버튼에 표시할 숫자 및 연산자 배열
    let buttons = [["7", "8", "9", "+"], ["4", "5", "6", "-"], ["1", "2", "3", "x"], ["AC", "0", "=", "÷"]]

    // 뷰가 로드될 때 호출되는 메서드
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .black
        
        setupTextLabel() // 텍스트 라벨 설정
        setupVerticalStackView() // 수직 스택뷰 설정
    }

    // 텍스트 라벨을 설정하는 메서드
    private func setupTextLabel() {
        display.text = "0" // 초기 텍스트 설정
        display.textColor = .white // 텍스트 색상 설정
        display.font = UIFont.boldSystemFont(ofSize: 60) // 텍스트 폰트 설정
        display.textAlignment = .right // 텍스트 정렬 설정
        view.addSubview(display) // 라벨을 뷰에 추가
        
        // 오토레이아웃 제약조건 설정
        display.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            display.heightAnchor.constraint(equalToConstant: 100),
            display.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30),
            display.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30),
            display.topAnchor.constraint(equalTo: view.topAnchor, constant: 200)
        ])
    }

    // 수직 스택뷰를 설정하는 메서드
    private func setupVerticalStackView() {
        verticalStackView.axis = .vertical // 수직 방향 설정
        verticalStackView.backgroundColor = .black // 배경 색상 설정
        verticalStackView.spacing = 10 // 스택뷰 사이의 간격 설정
        verticalStackView.distribution = .fillEqually // 스택뷰 요소 간 균등 배치
        view.addSubview(verticalStackView) // 수직 스택뷰를 뷰에 추가
        
        // 오토레이아웃 제약조건 설정
        verticalStackView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            verticalStackView.widthAnchor.constraint(equalToConstant: 350),
            verticalStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            verticalStackView.topAnchor.constraint(equalTo: display.bottomAnchor, constant: 60)
        ])

        // 버튼 배열을 이용해 수평 스택뷰 생성 및 추가
        for row in buttons {
            let horizontalStackView = createHorizontalStackView(row)
            verticalStackView.addArrangedSubview(horizontalStackView)
            
            // 오토레이아웃 제약조건 설정
            horizontalStackView.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                horizontalStackView.heightAnchor.constraint(equalToConstant: 80),
                horizontalStackView.widthAnchor.constraint(equalToConstant: 350),
                horizontalStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
            ])
        }
    }

    // 수평 스택뷰를 생성하는 메서드
    private func createHorizontalStackView(_ buttons: [String]) -> UIStackView {
        let stackView = UIStackView() // 수평 스택뷰 생성
        stackView.axis = .horizontal // 수평 방향 설정
        stackView.backgroundColor = .black // 배경 색상 설정
        stackView.spacing = 10 // 스택뷰 요소 간 간격 설정
        stackView.distribution = .fillEqually // 스택뷰 요소 간 균등 배치
        for buttonText in buttons {
            let button = createButton(buttonText) // 버튼 생성
            stackView.addArrangedSubview(button) // 버튼을 수평 스택뷰에 추가
        }
        return stackView
    }

    // 버튼을 생성하는 메서드
    private func createButton(_ text: String) -> UIButton {
        let button = UIButton() // 버튼 생성
        button.setTitle(text, for: .normal) // 버튼 타이틀 설정
        button.titleLabel?.font = .boldSystemFont(ofSize: 30) // 버튼 폰트 설정
        if Int(text) != nil {
            button.backgroundColor = UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0) // 숫자 버튼 배경색 설정
            button.addTarget(self, action: #selector(didTapNumberButton(_:)), for: .touchUpInside) // 숫자 버튼 액션 설정
        } else {
            button.backgroundColor = .orange // 연산자 버튼 배경색 설정
            button.addTarget(self, action: #selector(didTapOperateButton(_:)), for: .touchUpInside) // 연산자 버튼 액션 설정
        }
        button.layer.cornerRadius = 40 // 버튼 모서리 둥글게 설정
        return button
    }

    // 숫자 버튼이 눌렸을 때 호출되는 메서드
    @objc func didTapNumberButton(_ sender: UIButton) {
        if let currentText = display.text, let newText = sender.titleLabel?.text {
            // 현재 텍스트가 "0"이면 새로운 숫자로 대체
            if currentText == "0" {
                display.text = newText
            } else {
                // 그렇지 않으면 기존 텍스트에 새로운 숫자를 추가
                display.text = removeLeadingZero(currentText + newText)
            }
        }
    }

    // 연산자 버튼이 눌렸을 때 호출되는 메서드
    @objc func didTapOperateButton(_ sender: UIButton) {
        if let buttonText = sender.titleLabel?.text {
            switch buttonText {
            case "AC":
                // "AC" 버튼을 눌렀을 때 텍스트를 "0"으로 설정
                display.text = "0"
            case "=":
                // "=" 버튼을 눌렀을 때 계산 수행
                if let expression = display.text, let result = calculate(expression: expression) {
                    display.text = String(result)
                }
            default:
                // 기타 연산자 버튼을 눌렀을 때 텍스트에 연산자 추가
                if let currentText = display.text {
                    display.text = currentText + buttonText
                }
            }
        }
    }

    // 입력된 숫자에서 선행 0을 제거하는 메서드
    private func removeLeadingZero(_ input: String) -> String {
        var result = input
        if result.hasPrefix("0") && result.count > 1 {
            result.removeFirst()
        }
        return result
    }

    // 주어진 식(expression)을 계산하는 메서드
    private func calculate(expression: String) -> Int? {
        
        // 곱셈과 나눗셈 기호를 NSExpression이 인식할 수 있는 형식으로 변환
        let expressionWithOperators = expression.replacingOccurrences(of: "x", with: "*").replacingOccurrences(of: "÷", with: "/")
        
        // NSExpression을 사용하여 식을 계산
        let exp = NSExpression(format: expressionWithOperators)
        if let result = exp.expressionValue(with: nil, context: nil) as? Double {
            // 결과를 정수로 반환
            return Int(result)
        }
        return nil
    }
}
반응형