Ch 3. 앱 개발 입문 주차 과제
⎮ Lv. 8
Lv8. 내가 작성한 코드
class ViewController: UIViewController {
var number = "0"
let label = UILabel()
let button = UIButton()
private var titles = [["7", "8", "9", "+"], ["4", "5", "6", "-"], ["1", "2", "3", "*"], ["AC", "0", "=", "/"]]
private var verticalStackView = UIStackView()
private var stackView1 = UIStackView()
private var stackView2 = UIStackView()
private var stackView3 = UIStackView()
private var stackView4 = UIStackView()
override func viewDidLoad() {
super.viewDidLoad()
setLabel()
setUI()
}
private func setLabel() {
view.backgroundColor = .black
label.text = "\(number)"
label.textAlignment = .right
label.textColor = .white
label.font = UIFont.boldSystemFont(ofSize: 60)
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30),
label.topAnchor.constraint(equalTo: view.topAnchor, constant: 200),
label.heightAnchor.constraint(equalToConstant: 100)
])
}
func setUI() {
let setButton1 = setButton(titles[0], #selector(buttonTapped), button)
let setButton2 = setButton(titles[1], #selector(buttonTapped), button)
let setButton3 = setButton(titles[2], #selector(buttonTapped), button)
let setButton4 = setButton(titles[3], #selector(buttonTapped), button)
stackView1 = makeHorizontalStackView(setButton1)
stackView2 = makeHorizontalStackView(setButton2)
stackView3 = makeHorizontalStackView(setButton3)
stackView4 = makeHorizontalStackView(setButton4)
let arrStackView = fourStackView()
verticalStackView = makeVerticalStackView(arrStackView)
}
// 타이틀이 바뀌어 적용되는 버튼을 만들고 배열로 묶어주는 함수
private func setButton(_ titles : [String], _ action: Selector, _ button: UIButton) -> [UIButton] {
var arrButtons: [UIButton] = []
let operate = ["+", "-", "*", "AC", "=", "/"]
for title in titles {
let button = UIButton()
button.addTarget(self, action: action, for: .touchDown)
button.setTitle(title, for: .normal)
// 만약에 title이 operate의 요소를 포함하고 있지 않으면
if !operate.contains(title) {
button.backgroundColor = UIColor(red: 58/255, green: 58/255, blue: 58/255, alpha: 1.0)
} else {
button.backgroundColor = UIColor.orange
}
button.titleLabel?.font = .boldSystemFont(ofSize: 30)
button.layer.cornerRadius = 40
arrButtons.append(button)
}
return arrButtons
}
// 4개의 버튼 배열을 묶어서 1개의 스택뷰로 만들어주는 함수
private func makeHorizontalStackView(_ views: [UIButton]) -> UIStackView {
let horizontalStackView = UIStackView()
horizontalStackView.axis = .horizontal
horizontalStackView.backgroundColor = .black
horizontalStackView.spacing = 10
horizontalStackView.distribution = .fillEqually
horizontalStackView.addArrangedSubview(views[0])
horizontalStackView.addArrangedSubview(views[1])
horizontalStackView.addArrangedSubview(views[2])
horizontalStackView.addArrangedSubview(views[3])
view.addSubview(horizontalStackView)
horizontalStackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
horizontalStackView.heightAnchor.constraint(equalToConstant: 80)
])
return horizontalStackView
}
// 4개의 스택뷰를 묶어서 배열로 리턴하는 함수
private func fourStackView() -> [UIStackView] {
let arrStackView = [stackView1, stackView2, stackView3, stackView4]
return arrStackView
}
// 스택뷰 배열을 받아서 UIStackView로 리턴하는 함수 (여기서 Vertical StackView로 전환)
private func makeVerticalStackView(_ stackViews: [UIStackView]) -> UIStackView {
verticalStackView.axis = .vertical
verticalStackView.backgroundColor = .black
verticalStackView.spacing = 10
verticalStackView.distribution = .fillEqually
verticalStackView.addArrangedSubview(stackViews[0])
verticalStackView.addArrangedSubview(stackViews[1])
verticalStackView.addArrangedSubview(stackViews[2])
verticalStackView.addArrangedSubview(stackViews[3])
view.addSubview(verticalStackView)
verticalStackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
verticalStackView.widthAnchor.constraint(equalToConstant: 350),
verticalStackView.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 60),
verticalStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
return verticalStackView
}
@objc
private func buttonTapped(_ sender: UIButton) {
guard let title = sender.currentTitle else { return }
if title != "=" {
number += title
if number.first == "0" {
number.removeFirst()
}
label.text = "\(number)"
} else {
if let result = calculate(expression: "\(number)") {
label.text = "\(result)"
}
}
}
func calculate(expression: String) -> Int? {
let expression = NSExpression(format: expression)
if let result = expression.expressionValue(with: nil, context: nil) as? Int {
return result
} else {
return nil
}
}
}
앞서 말했듯 이 계산기는 아래의 오류를 가지고 있다.
1. 이전의 이력을 다 가지고 있다는 점
2. 1의 문제로 연산을 이어서 하지 못한다는 점(곱하기 연산을 선택하면 곱하기가 우선으로 실행되어 잘못된 값이 나옴)
아마 하단 코드의 로직이 꼬여서인것 같은데 이를 해결해보고자 한다.
@objc
private func buttonTapped(_ sender: UIButton) {
guard let title = sender.currentTitle else { return }
if title != "=" {
number += title
if number.first == "0" {
number.removeFirst()
}
label.text = "\(number)"
} else {
if let result = calculate(expression: "\(number)") {
label.text = "\(result)"
}
}
}
⎮ 트러블 슈팅
@objc
private func buttonTapped(_ sender: UIButton) {
guard let title = sender.currentTitle else { return }
if title != "=" {
number += title
if number.first == "0" {
number.removeFirst()
}
label.text = "\(number)"
} else {
if let result = calculate(expression: "\(number)") {
label.text = "\(result)"
}
}
}
코드를 뜯어보며 위의 문제가 왜 생겼는지 생각해보자.
먼저, 누른 버튼이 "="이 아니면 number에 title의 값이 추가된다.
1을 누른 뒤 2를 누르면 12가 되는 것이다.
12에서 +를 누르고 34를 누르면 number의 값은 12+34가 된다. (이게 굉장히 중요함)
다음으로 = 버튼을 누르면 else 구문이 실행되어
result에 12+34를 연산한 값이 담긴다.
label.text = "\(result)"로 해두었기 때문에
계산기 화면에는 12+34를 연산한 값인 46이 표시된다.
정리하면 다음과 같은 차이가 발생한다.
number의 값 : 12+34
result의 값 : 46
다시 버튼을 눌러보자.
6버튼을 누르면 number의 값이 12+34였기 때문에
12+34+6이 된다.
이 문제는 쉽게 해결할 수 있다. (물론 나는 여러 시행착오를 겪으며 깨달았지만;)
결과물을 표시하는 값을 통일해주고 해당 값을 사용하게 하면 된다.
switch title {
case "=":
if let result = calculate(expression: number) {
number = "\(result)"
label.text = "\(number)"
}
case "AC":
number = "0"
label.text = "\(number)"
default:
number += title
if number.first == "0" {
number.removeFirst()
}
label.text = "\(number)"
}
이렇게 해서 계산기는 정상 작동을 하게 되었다.
구현의 기쁨도 잠시, 이 계산기의 허점을 다시 발견하게 되었다..... to be continue....
'스파르타코딩 클럽 > 개인과제' 카테고리의 다른 글
19. 스파르타 코딩클럽 [본캠프 - 계산기 앱 : 정수기(9)] (0) | 2025.04.02 |
---|---|
18. 스파르타 코딩클럽 [본캠프 - 계산기 앱 : 정수기(8)] (0) | 2025.04.02 |
16. 스파르타 코딩클럽 [본캠프 - 계산기 앱 : 정수기(6)] (0) | 2025.04.01 |
15. 스파르타 코딩클럽 [본캠프 - 계산기 앱 : 정수기(5)] (0) | 2025.03.31 |
14. 스파르타 코딩클럽 [본캠프 - 계산기 앱 : 정수기(4)] (0) | 2025.03.29 |