Ch 3. 앱 개발 입문 주차 과제
- 내가 작성한 코드
// DataModel.swift
import Foundation
class DataModel {
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
}
}
}
// ViewController.swift
import UIKit
class ViewController: UIViewController {
let onlyView = OnlyView()
override func viewDidLoad() {
super.viewDidLoad()
self.view = onlyView
}
}
// OnlyView.swift
import UIKit
class OnlyView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setLabel()
setUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
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()
private func setLabel() {
self.backgroundColor = .black
label.text = "\(number)"
label.textAlignment = .right
label.textColor = .white
label.font = UIFont.boldSystemFont(ofSize: 60)
self.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -30),
label.topAnchor.constraint(equalTo: self.topAnchor, constant: 200),
label.heightAnchor.constraint(equalToConstant: 100)
])
}
func setUI() {
let setButton1 = setButton(titles[0], #selector(buttonTapped))
let setButton2 = setButton(titles[1], #selector(buttonTapped))
let setButton3 = setButton(titles[2], #selector(buttonTapped))
let setButton4 = setButton(titles[3], #selector(buttonTapped))
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) -> [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])
self.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])
self.addSubview(verticalStackView)
verticalStackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
verticalStackView.widthAnchor.constraint(equalToConstant: 350),
verticalStackView.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 60),
verticalStackView.centerXAnchor.constraint(equalTo: self.centerXAnchor)
])
return verticalStackView
}
@objc
private func buttonTapped(_ sender: UIButton) {
guard let title = sender.currentTitle else { return }
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)"
}
}
}
⎮ Alert 추가하기
구현을 마치고 테스트를 해보던 중, "+-", "--" 등으로 연산을 2번 입력할 수 있다는 것을 발견했다.
이 상태로 "=" 버튼을 누르면 Model에 있는 Calculator 함수에서 오류가 발생한다.
이를 해결하기 위해 예외처리를 하고, Alert을 구현하기로 했다.
현재 Model / View / Controller가 나눠져 있는 상황이어서
Alert을 Controller에서 구현하고, View에서 실행해주고자 했다.
(View에 연산 메서드가 있기 때문에)
// Alert 만들어주는 함수
func makeAlert(_ message: String) {
let alert = UIAlertController(title: "경고", message: message, preferredStyle: .alert)
let alertAction = UIAlertAction(title: "확인", style: .default)
alert.addAction(alertAction)
self.present(alert, animated: true)
}
// Alert의 message를 사용할 때 주입 받기 위한 함수
func showAlert(_ message: String) {
makeAlert(message)
}
함수는 위와 같이 구현을 했는데, 어떻게 데이터를 받아올지 막막해졌다.
연산 메서드는 View에 있고, ViewController에 있는 함수를 실행해야한다.
어떻게 해야할까?
이럴 때 사용하는 것이 Delegate 패턴이다.
Swift | Delegate 패턴이 뭔데?
Delegate 패턴 예? 엘리게이터요? 아뇨 델리게이트요!⎮ Delegate가 뭔데?Delegate : (집단의 의사를 대표하는) 대표(자) / 위임하다 / (대표를) 뽑다[선정하다] https://developer.apple.com/documentation/
uddt.tistory.com
⎮ Delegate 패턴 적용하기
Delegate 패턴을 적용하기 위해 protocol을 만들어줬다.
protocol AlertViewDelegate {
func showAlert(_ message: String)
}
먼저 View에서 대리자 공고를 내고 protocol로 타입을 지정해줬다
var delegate: AlertViewDelegate?
대리자 공고를 본 ViewController가 자원을 한다.
이렇게 하면 Delegate가 가지고 있는 Alert 창을 띄울 수 있게 된다.
'스파르타코딩 클럽 > 개인과제' 카테고리의 다른 글
21. 스파르타 코딩클럽 [본캠프 - 계산기 앱 : 정수기 최종] (0) | 2025.04.03 |
---|---|
20. 스파르타 코딩클럽 [본캠프 - 계산기 앱 : 정수기(10)] (0) | 2025.04.03 |
18. 스파르타 코딩클럽 [본캠프 - 계산기 앱 : 정수기(8)] (0) | 2025.04.02 |
17. 스파르타 코딩클럽 [본캠프 - 계산기 앱 : 정수기(7)] (0) | 2025.04.01 |
16. 스파르타 코딩클럽 [본캠프 - 계산기 앱 : 정수기(6)] (0) | 2025.04.01 |