[Project] 계산기 만들기(UI & 기능구현) ③

JJOOEE__·2024년 6월 26일
0

Today I Learned....

목록 보기
12/19
post-thumbnail
//
//  ViewController.swift
//  Calculator_UI&codeBase
//
//  Created by t2023-m0023 on 6/20/24.
//

import UIKit
import SnapKit

class ViewController: UIViewController {
    
    let label = UILabel()
    let stackview1 = UIStackView()
    let stackview2 = UIStackView()
    let stackview3 = UIStackView()
    let stackview4 = UIStackView()
    let clcStackVeiw = UIStackView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }
    
    private func configureUI() {
        // 배경 컬러지정
        view.backgroundColor = .black
        
        //라벨 설정
        label.text = "0"
        label.textColor = .white
        label.textAlignment = .right
        label.font = UIFont.boldSystemFont(ofSize: 60)
        
        // 라벨을 뷰에 추가하고 제약 조건을 설정
        view.addSubview(label)
        label.snp.makeConstraints {
            $0.leading.equalToSuperview().inset(30)
            $0.trailing.equalToSuperview().inset(30)
            $0.top.equalToSuperview().inset(200)
            $0.height.equalTo(100)
        }
        makeHorizontalStackView()
    }
    
    private func makeHorizontalStackView() {
        // 스택뷰를 4개를 만들어 줘야함
        // 각각의 스택뷰에 버튼타이틀 설정 할 수있는 함수 필요
        
        let stackViews = [stackview1, stackview2, stackview3, stackview4]
        let buttonTitle = [["7", "8", "9", "+"], ["4", "5", "6", "-"], ["1", "2", "3", "*"], ["AC", "0", "=", "/"]]
        // for  반복문 사용 스택 뷰의 속성을 설정
        for (index, stackview) in stackViews.enumerated() {
            setHorizontalStackView(stackview: stackview)
            makeButtonStackView(stackview: stackview, titles: buttonTitle[index])
        }
        
        func setHorizontalStackView(stackview : UIStackView){
            stackview.axis = .horizontal
            stackview.backgroundColor = .black
            stackview.spacing = 10
            stackview.distribution = .fillEqually
            
            view.addSubview(stackview)
            stackview.snp.makeConstraints{
                $0.width.equalTo(350)
                $0.height.equalTo(80)
                $0.centerX.equalToSuperview()
            }
        }
        
        func makeButtonStackView (stackview: UIStackView, titles: [String]){
            for title in titles {
                let button = UIButton()
                //버튼 타이틀이 연산자 operator와 숫자를 숫자로 변환
                //숫자로 변환이 되면 검정
                if let _ = Int(title) {
                    button.backgroundColor = UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0)
                    button.addTarget(self, action: clickNum(:), for: .touchUpInside)
                    //숫자로 변환이 안되면 오렌지
                } else {
                    button.backgroundColor = .orange
                    button.addTarget(self, action: clickOperator(:), for: .touchUpInside)
                }
                button.setTitle(String(title), for: .normal)
                button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 30)
                button.frame.size.height = 80
                button.frame.size.width = 80
                button.layer.cornerRadius = 40
                
                stackview.addArrangedSubview(button)
            }
        }
        makeVerticalstackView()
    }
    
    private func makeVerticalstackView() {
        clcStackVeiw.axis = .vertical
        clcStackVeiw.backgroundColor = .black
        clcStackVeiw.spacing = 10
        clcStackVeiw.distribution = .fillEqually
        
        
        view.addSubview(clcStackVeiw)
        clcStackVeiw.snp.makeConstraints {
            $0.width.equalTo(350)
            $0.top.equalTo(label.snp.bottom).offset(60)
            $0.centerX.equalToSuperview()
        }
        
        //가로 스텍뷰를 세로 스택뷰에 추가해주기
        clcStackVeiw.addArrangedSubview(stackview1)
        clcStackVeiw.addArrangedSubview(stackview2)
        clcStackVeiw.addArrangedSubview(stackview3)
        clcStackVeiw.addArrangedSubview(stackview4)
    }
    
    @objc func clickNum(_ sender: UIButton) {
        print()
    }
    
    @objc func clickOperator(_ sender: UIButton) {
        print()
    }
}

1️⃣ Lv. 6 ~ 8

  • 버튼 동작 시행할 수 있도록 하기
  • 버튼 입력시 수행할 함수 만들기
//
//  ViewController.swift
//  Calculator_UI&codeBase
//
//  Created by t2023-m0023 on 6/20/24.
//

import UIKit
import SnapKit

class ViewController: UIViewController {

    // 화면에 표시될 레이블과 스택뷰
    let label = UILabel()
    let stackview1 = UIStackView()
    let stackview2 = UIStackView()
    let stackview3 = UIStackView()
    let stackview4 = UIStackView()
    let clcStackVeiw = UIStackView()

    // 현재 표시되고 있는 텍스트
    var currentText = "0"

    override func viewDidLoad() {
        super.viewDidLoad()
        // 초기 UI 설정 및 스택뷰 생성
        configureUI()
        makeHorizontalStackView()
        makeVerticalstackView()
    }

    // UI를 설정하는 메서드
    private func configureUI() {
        view.backgroundColor = .black  // 배경 검정색

        label.text = currentText  // 초기 텍스트 설정
        label.textColor = .white  // 텍스트 색상 흰색
        label.textAlignment = .right  // 텍스트 정렬 오른쪽
        label.font = UIFont.boldSystemFont(ofSize: 60)  // 굵은 폰트 60

        // 레이블 추가 , 설정
        view.addSubview(label)
        label.snp.makeConstraints {
            $0.leading.equalToSuperview().inset(30)  // 왼쪽 여백 30
            $0.trailing.equalToSuperview().inset(30)  // 오른쪽 여백 30
            $0.top.equalToSuperview().inset(200)  // 상단여백 200
            $0.height.equalTo(100)  // 높이 100
        }
    }

    // 가로스택뷰 생성 버튼추가 메서드
    private func makeHorizontalStackView() {
        let stackViews = [stackview1, stackview2, stackview3, stackview4]
        let buttonTitle = [["7", "8", "9", "+"], ["4", "5", "6", "-"], ["1", "2", "3", "*"], ["AC", "0", "=", "/"]]

        for (index, stackview) in stackViews.enumerated() {
            setHorizontalStackView(stackview: stackview)  // 가로 스택뷰 속성 설정
            makeButtonStackView(stackview: stackview, titles: buttonTitle[index])  // 버튼을 생성, 스택뷰에 추가
        }
    }

    // 가로 스택뷰 속성 설정 메서드
    private func setHorizontalStackView(stackview: UIStackView) {
        stackview.axis = .horizontal  // 가로 방향 정렬
        stackview.backgroundColor = .black  // 배경색 검은색
        stackview.spacing = 10  // 요소 간의 간격 10 포인트
        stackview.distribution = .fillEqually  // 균등하게 배치

        // 스택뷰 추가하고 제약조건 설정
        view.addSubview(stackview)
        stackview.snp.makeConstraints {
            $0.width.equalTo(350)  // 너비 350
            $0.height.equalTo(80)  // 높이 80
            $0.centerX.equalToSuperview()  // 수평 가운데 정렬
        }
    }

    // 버튼생성, 가로스택뷰 추가하는 함수
    private func makeButtonStackView(stackview: UIStackView, titles: [String]) {
        for title in titles {
            let button = UIButton()

            // 숫자 버튼인지 연산자 버튼인지 확인한후 스타일과 액션 설정
            if let _ = Int(title) {
                button.backgroundColor = UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0)  // 숫자 버튼 배경색
                button.addTarget(self, action: #selector(clickNum(_:)), for: .touchUpInside)  // 숫자 버튼 액션 설정
            } else {
                button.backgroundColor = .orange  // 연산자 버튼 배경색
                button.addTarget(self, action: #selector(clickOperator(_:)), for: .touchUpInside)  // 연산자 버튼 클릭 액션을 추가
            }
            //버튼 텍스트 설정
            button.setTitle(String(title), for: .normal)
            button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 30)  // 버튼 텍스트 굵은폰트 크기 30
            button.frame.size.height = 80  // 버튼 높이 80 포인트
            button.frame.size.width = 80  // 버튼 너비 80 포인트
            button.layer.cornerRadius = 40  // 버튼 모서리 둥글게

            stackview.addArrangedSubview(button)  // 버튼 가로 스택뷰에 추가
        }
    }

    // 세로 스택뷰 생성,  가로 스택뷰 추가
    private func makeVerticalstackView() {
        clcStackVeiw.axis = .vertical  // 세로 방향 정렬
        clcStackVeiw.backgroundColor = .black  // 배경색 검은색
        clcStackVeiw.spacing = 10  // 요소  간격을 10 포인트
        clcStackVeiw.distribution = .fillEqually  // 균등 배치

        // 세로 스택뷰 화면에 추가, 제약조건 설정
        view.addSubview(clcStackVeiw)
        clcStackVeiw.snp.makeConstraints {
            $0.width.equalTo(350)  // 너비 350
            $0.top.equalTo(label.snp.bottom).offset(60)  // 레이블 아래 여백 60
            $0.centerX.equalToSuperview()  // 수평 가운데 정렬
        }

        // 가로 스택뷰들을 세로 스택뷰에 추가
        clcStackVeiw.addArrangedSubview(stackview1)
        clcStackVeiw.addArrangedSubview(stackview2)
        clcStackVeiw.addArrangedSubview(stackview3)
        clcStackVeiw.addArrangedSubview(stackview4)
    }

    // 숫자 버튼 클릭 시 호출되는 메서드
    @objc func clickNum(_ sender: UIButton) {
        guard let clickedButtonText = sender.currentTitle else { return }
        // 현재 텍스트가 "0"일 때 클릭된 버튼이 0이 아닌경우, 0제거 클릭된 버튼 텍스트로 대체
        if currentText == "0" && clickedButtonText != "0" {
            currentText = clickedButtonText
        } else {
            currentText += clickedButtonText
        }
        updateLabel()  // 레이블 업데이트
    }

    // 연산자 버튼 클릭 시 호출되는 메서드
    @objc func clickOperator(_ sender: UIButton) {
        guard let clickedButtonText = sender.currentTitle else { return }

        // 클릭된 연산자에 따라 동작 수행
        switch clickedButtonText {
        case "AC":
            currentText = "0"  // AC 버튼 클릭 시 초기화
        case "=":
            if let result = calculate(expression: currentText) {
                currentText = String(result)  // = 버튼 클릭 시 계산, 결과를 텍스트로 설정
            } else {
                currentText = "ERROR"  // 잘못된 수식, 에러 표시
            }
        default:
            currentText += clickedButtonText  // 기타 연산자, 텍스트에 추가
        }
        updateLabel()  // 레이블을 업데이트
    }

    // 수식을 계산,  결과를 반환하는 메서드
    func calculate(expression: String) -> Int? {
        // 입력한 수식이 비어있는 경우
        guard !expression.isEmpty else {
            print("수식이 비어있습니다.")
            return nil
        }
        // 연산자를 포함한 수식, 정규 표현식 사용하여 검사
        let regex = try! NSRegularExpression(pattern: "^[0-9]+([\\+\\-\\*\\/][0-9]+)*$")
        let range = NSRange(location: 0, length: expression.utf16.count)

        // 정규 표현식에 맞지 않는 경우
        guard regex.firstMatch(in: expression, options: [], range: range) != nil else {
            print("잘못된 수식입니다.")
            return nil
        }

        // NSExpression을 사용해 계산
        let nsExpression = NSExpression(format: expression)

        // 계산 결과를 반환
        if let result = nsExpression.expressionValue(with: nil, context: nil) as? Int {
            return result
        } else {
            print("잘못된 수식입니다.")  // 잘못된 수식일 경우, 메시지를 출력
            return nil
        }
    }
    
    // 레이블의 텍스트를 업데이트
    func updateLabel() {
        label.text = currentText  // 레이블 현재 텍스트를 설정
    }
}

⬜ 코드 작성하며 공부한 내용

🌈 NSExpression

🩷 를 클릭하면 Foundation 프레임워크 클래스를 볼 수 있습니다

  • 데이터 처리나 계산이 필요한 애플리케이션 개발에서 유용하게 사용
  • 수식을 표현하고 계산하는 클래스
expressionValue(with: , context:)
  • expressionValue 주어진 수식을 평가하여 결과 값을 반환하는 메서드

1. 기본 사용법

let expression = NSExpression(format: "2 + 3")
if let result = expression.expressionValue(with: nil, context: nil) as? NSNumber {
    print("Result:", result.intValue) // 출력: Result: 5
}
  • 첫 번째 매개변수(object)
    • 일반적으로 nil을 사용
    • 수식에서 사용할 수 있는 추가적인 객체를 전달할 때 사용
  • 두 번째 매개변수(context)
    • 수식 평가에 필요한 컨텍스트 정보를 제공하는 데 사용,
    • 주로 변수 값이나 함수 등을 포함

2. 변수와 함께 사용하는 경우

let variableName = "x"
let variableValue = 10
let expression = NSExpression(format: "\(variableName) * 2")
let context = ["x": variableValue]
if let result = expression.expressionValue(with: nil, context: context) as? NSNumber {
    print("Result:", result.intValue) // 출력: Result: 20
}
  • 변수 x를 포함하는 수식을 평가
  • context 매개변수에 변수의 값을 딕셔너리 형태로 전달

3. 함수를 포함하는 경우

let expression = NSExpression(format: "sqrt(25)")
if let result = expression.expressionValue(with: nil, context: nil) as? NSNumber {
    print("Result:", result.doubleValue) // 출력: Result: 5.0
}
  • sqrt 함수를 사용하여 제곱근을 계산
  • 함수를 포함하는 수식은 수학적 또는 사용자 정의 함수를 호출하여 복잡한 계산을 수행

주석을 간결하고 자세히 달기

👉 필요하다고 생각한 이유 :

  • 코드만 봤을 때 어떤 코드인지 한 눈에 알아보기가 어려웠음
  • 제약조건 코드와 버튼, UI별 디자인 코드들이 많아 수정할 때 용이하게 하기 위함

👉 수정

profile
개발이 어려운 나를 위한... 개발노트

0개의 댓글