Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions Calculate/Calculate/CalculatorModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// CalculatorModel.swift
// Calculate
//
// Created by mun on 11/21/24.
//
import Foundation

class CalculatorModel {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

계산 로직을 별도의 객체로 책임분리한 점 좋습니다!
계산 로직을 잘 나타낼 수 있는 클래스 이름을 선택하면 더 좋을 것 같아요~~!


// 계산된 결과 반환
func calculate(expression: String) -> Int? {
guard isValidExpression(expression) else { return nil }

let expression = NSExpression(format: expression)
if let result = expression.expressionValue(with: nil, context: nil) as? Int {
return result
} else {
return nil
}
}

// 수식이 유효한지 확인
private func isValidExpression(_ text: String) -> Bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수식이 유효한지 확인하는 기능을 Bool을 반환하는 함수로 감싸주신 점 굉장히 좋다고 생각해요!

let textArray = Array(text)
var previousChar: String?

// 시작 문자가 "-"을 제외한 다른 연산 기호라면 false
if Int(String(textArray.first!)) == nil && textArray.first != "-" {
return false
}

// 마지막 문자가 연산 기호라면 false
if Int(String(textArray.last!)) == nil {
return false
}

for char in textArray {
let currentChar = String(char)

if Int(currentChar) == nil {
// 연산 기호가 연속으로 나오면 false
if currentChar == previousChar {
return false
}
}
}

return true
}
}
117 changes: 117 additions & 0 deletions Calculate/Calculate/CalculatorView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// CalculatorView.swift
// Calculate
//
// Created by mun on 11/21/24.
//

import UIKit

import SnapKit

class CalculatorView: UIView {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

별도의 뷰로 작성해 주셨군요! 좋습니다~!


var label = UILabel()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이러한 라벨, 스택뷰, 버튼데이터는 internal로 노출되어야 할 이유가 있을까요? (저도 몰라서 여쭤봐요!)

var verticalStackView = UIStackView()

let buttonDatas = [["7", "8", "9", "+"],
["4", "5", "6", "-"],
["1", "2", "3", "*"],
["AC", "0", "=", "/"]]

override init(frame: CGRect) {
super.init(frame: frame)
configureUI()
configureLayout()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// UI 설정
private func configureUI() {
self.backgroundColor = .black
[label, verticalStackView].forEach() {
self.addSubview($0)
}

configureLabel()
configureVerticalStackView()
}

// label 스타일 설정
private func configureLabel() {
label.text = "0"
label.textColor = .white
label.textAlignment = .right
label.font = .boldSystemFont(ofSize: 60)
}

// VerticalStackView 스타일 설정
private func configureVerticalStackView() {
for i in 0..<4 {
let view = makeHorizontalStackView(i)
verticalStackView.addArrangedSubview(view)
}

verticalStackView.axis = .vertical
verticalStackView.backgroundColor = .black
verticalStackView.spacing = 10
verticalStackView.distribution = .fillEqually
}

// horizontalStackView 생성
private func makeHorizontalStackView(_ verIndex: Int) -> UIStackView {
let horizontalStackView = UIStackView()

horizontalStackView.axis = .horizontal
horizontalStackView.backgroundColor = .black
horizontalStackView.spacing = 10
horizontalStackView.distribution = .fillEqually

for horIndex in 0..<4 {
let button = makeButton(verIndex: verIndex, horIndex: horIndex)
horizontalStackView.addArrangedSubview(button)
}

return horizontalStackView
}

// button 생성
private func makeButton(verIndex: Int, horIndex: Int) -> UIButton {
let button = UIButton()
let title = buttonDatas[verIndex][horIndex]
let color = Int(title) == nil ? .orange : UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0)

button.setTitle(title, for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 30)
button.backgroundColor = color
button.layer.cornerRadius = 80 / 2

button.snp.makeConstraints() {
$0.width.height.equalTo(80)
}

return button
}

// button 텍스트 변경
func setLabelText(_ text: String) {
label.text = text
}

// 오토 레이아웃 설정
private func configureLayout() {
label.snp.makeConstraints {
$0.leading.trailing.equalToSuperview().inset(30)
$0.top.equalToSuperview().inset(200)
$0.height.equalTo(100)
}

verticalStackView.snp.makeConstraints {
$0.top.equalTo(label.snp.bottom).offset(60)
$0.centerX.equalToSuperview()
}
}
}
117 changes: 47 additions & 70 deletions Calculate/Calculate/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,79 +9,56 @@ import UIKit
import SnapKit

class ViewController: UIViewController {

let label = UILabel()
let stackView = UIStackView()
let buttons = [UIButton(), UIButton(), UIButton(), UIButton()]
let buttonDatas = ["7", "8", "9", "+"]

override func viewDidLoad() {
super.viewDidLoad()

configureUI()
configureLayout()
}

// UI 설정
private func configureUI() {
view.backgroundColor = .black
configureLabel()
configureStackView()

[label, stackView].forEach{
view.addSubview($0)
}
}

// label 스타일 설정
private func configureLabel() {
label.text = "12345"
label.textColor = .white
label.textAlignment = .right
label.font = .boldSystemFont(ofSize: 60)
}

private func configureStackView() {
stackView.axis = .horizontal
stackView.backgroundColor = .black
stackView.spacing = 10
stackView.distribution = .fillEqually

configureButton()
}

private func configureButton() {
for i in buttons.indices {
buttons[i].setTitle(buttonDatas[i], for: .normal)
buttons[i].titleLabel?.font = .boldSystemFont(ofSize: 30)
buttons[i].backgroundColor = UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0)
stackView.addArrangedSubview(buttons[i])
}

var calculatorModel: CalculatorModel!
var calculatorView: CalculatorView!

override func viewDidLoad() {
super.viewDidLoad()

calculatorModel = CalculatorModel()
calculatorView = CalculatorView(frame: view.bounds)

view.addSubview(calculatorView)

configureActions()
}

// 버튼에 action 연결
private func configureActions() {
for stackView in calculatorView.verticalStackView.arrangedSubviews {
let stackView = stackView as! UIStackView

for button in stackView.arrangedSubviews {
let button = button as! UIButton

button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
}
}

// 레이아웃 설정
private func configureLayout() {
label.snp.makeConstraints {
$0.leading.trailing.equalToSuperview().inset(30)
$0.top.equalToSuperview().inset(200)
$0.height.equalTo(100)
}

stackView.snp.makeConstraints {
$0.height.equalTo(80)
$0.top.equalTo(label.snp.bottom).offset(50)
$0.centerX.equalToSuperview()
}

for button in buttons {
button.snp.makeConstraints {
$0.height.width.equalTo(80)
}
}

}

// 버튼 클릭시 실행
@objc
func buttonTapped(_ sender: UIButton) {
let buttonTitle = sender.currentTitle!

if buttonTitle == "AC" {
calculatorView.setLabelText("0")
} else if buttonTitle == "=" {
if let result = calculatorModel.calculate(expression: calculatorView.label.text!) {
calculatorView.setLabelText("\(result)")
}
} else {
if calculatorView.label.text == "0" {
calculatorView.setLabelText(buttonTitle)
} else {
calculatorView.setLabelText(calculatorView.label.text! + buttonTitle)
}
}
}
}


#Preview("ViewController") {
ViewController()
ViewController()
}
Loading