- 코드베이스로 계산기 구현 과제 제출이 끝났고, 과제 해설 영상이 공개되었다.
- 해설 영상의 코드와 내가 만든 코드를 보면서 배운 점과 고쳐야 될 점을 생각하며 정리했다.

UILabel 객체 만들기UILabel 객체만 선언 후 메소드에서 속성을 정의| 분류 | 내가 만든 코드 | 해설 코드 |
|---|---|---|
| UILabel 객체 만들기 | - 인스턴스를 만들고 메소드에 정리 | - 인스턴스를 만들때 속성도 같이 정의 |
| 메소드 사용 | - 한개의 메소드에 모든 코드 작성 | - 각 기능별로 메소드를 분리 (View 관련 메소드, label 제약 관련 메소드) |
| 평가 | - 코드가 단순함 | - 코드 가독성이 좋음 |
class ViewController: UIViewController {
let stackView = UIStackView() // 스택 뷰
func configureUI() {
// 스택뷰 속성 정의
stackView.axis = .horizontal
stackView.backgroundColor = .black
stackView.spacing = 10
stackView.distribution = .fillEqually
// 제약 설정
stackView.snp.makeConstraints {
$0.height.equalTo(80)
$0.top.equalTo(label.snp.bottom).offset(30)
$0.centerX.equalTo(label.snp.centerX)
}
}func makeHorizontalStackView(_ views: [UIView]) -> UIStackView {
let stackView = UIStackView(arrangedSubviews: views)
stackView.axis = .horizontal
stackView.spacing = 10
stackView.distribution = .fillEqually
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}func configureUI() {
.
.
.
stackView.snp.makeConstraints {
$0.height.equalTo(80)
$0.top.equalTo(label.snp.bottom).offset(30)
$0.centerX.equalTo(label.snp.centerX)
}
.
.
.
}func setupStackView() {
let buttons = createButtons(titles: ["7", "8", "9", "+"])
let horizontalStackView = makeHorizontalStackView(buttons)
view.addSubview(horizontalStackView)
horizontalStackView.snp.makeConstraints {
$0.top.equalTo(label.snp.bottom).offset(60)
$0.height.equalTo(80)
}
}class ViewController: UIViewController {
.
.
.
let button7 = UIButton()
let button8 = UIButton()
let button9 = UIButton()
let buttonPlus = UIButton()
.
.
.
func configureUI() {
.
.
.
// 7 버튼 속성 정의
button7.setTitle("7", for: .normal)
button7.titleLabel?.font = .boldSystemFont(ofSize: 30)
button7.backgroundColor = UIColor(red: 58/255,
green: 58/255,
blue: 58/255,
alpha: 1.0)
button7.layer.cornerRadius = 40
// 8 버튼 속성 정의
button8.setTitle("8", for: .normal)
button8.titleLabel?.font = .boldSystemFont(ofSize: 30)
button8.backgroundColor = UIColor(red: 58/255,
green: 58/255,
blue: 58/255,
alpha: 1.0)
button8.layer.cornerRadius = 40
.
.
.
[button7, button8, button9, buttonPlus]
.forEach{ stackView.addArrangedSubview($0) }
button7.snp.makeConstraints {
$0.width.height.equalTo(80)
}
button8.snp.makeConstraints {
$0.width.height.equalTo(80)
}
.
.
.
}
}func createButtons(titles: [String]) -> [UIButton] {
var buttons: [UIButton] = []
for title in titles {
let button = UIButton()
button.setTitle(title, for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 30)
button.backgroundColor = .gray
button.translatesAutoresizingMaskIntoConstraints = false
buttons.append(button)
}
buttons.forEach {
$0.snp.makeConstraints {
$0.height.width.equalTo(80)
}
}
return buttons
}class ViewController: UIViewController {
.
.
.
let verticalStackView = UIStackView()
func configureUI() {
.
.
.
// 스택뷰 생성 메소드를 이용하여 스택뷰 생성
// 첫째 줄 스택뷰
let stackView = makeHorizontalStackView(buttonTitle: ["7", "8", "9", "+"])
// 첫째 줄 스택뷰 제약 조건 추가
stackView.snp.makeConstraints {
$0.height.equalTo(80)
}
// 둘째 줄 스택뷰
let secondStackView = makeHorizontalStackView(buttonTitle: ["4", "5", "6", "-"])
// 둘째 줄 스택뷰 제약 조건 추가
secondStackView.snp.makeConstraints {
$0.height.equalTo(80)
}
// 셋째 줄 스택뷰
let thirdStackView = makeHorizontalStackView(buttonTitle: ["1", "2", "3", "*"])
// 셋째 줄 스택뷰 제약 조건 추가
thirdStackView.snp.makeConstraints {
$0.height.equalTo(80)
}
// 네째 줄 스택뷰
let fourthStackView = makeHorizontalStackView(buttonTitle: ["AC", "0", "=", "/"])
// 네째 줄 스택뷰 제약 조건 추가
fourthStackView.snp.makeConstraints {
$0.height.equalTo(80)
}
// 수직 스택뷰 속성 정의
verticalStackView.axis = .vertical
verticalStackView.backgroundColor = .black
verticalStackView.spacing = 10
verticalStackView.distribution = .fillEqually
view.addSubview(verticalStackView) // 뷰 위에 스택뷰 추가
// 수직 스택뷰 제약 조건 추가
verticalStackView.snp.makeConstraints {
$0.width.equalTo(350)
$0.top.equalTo(label.snp.bottom).offset(60)
$0.centerX.equalTo(label.snp.centerX)
}
// 수직 스택뷰 안에 수평 스택뷰 4개 추가
[stackView, secondStackView, thirdStackView, fourthStackView]
.forEach { verticalStackView.addArrangedSubview($0) }
}
}func setupStackView() {
let row1 = createButtons(titles: ["7", "8", "9", "+"])
let row2 = createButtons(titles: ["4", "5", "6", "-"])
let row3 = createButtons(titles: ["1", "2", "3", "*"])
let row4 = createButtons(titles: ["AC", "0", "=", "/"])
let horizontalStackView1 = makeHorizontalStackView(row1)
let horizontalStackView2 = makeHorizontalStackView(row2)
let horizontalStackView3 = makeHorizontalStackView(row3)
let horizontalStackView4 = makeHorizontalStackView(row4)
let verticalStackView = makeVerticalStackView([horizontalStackView1, horizontalStackView2, horizontalStackView3, horizontalStackView4])
view.addSubview(verticalStackView)
verticalStackView.snp.makeConstraints {
$0.top.equalTo(label.snp.bottom).offset(60)
$0.height.equalTo(350)
$0.centerX.equalToSuperview()
}
}나는 orange 라는 배열에 연산자를 담아서, 조건문으로 버튼 제목이 해당 배열 요소가 포함되어있으면 색을 바꿔주게 구현했다.
이 로직을 구현하느냐고 얼마나 많은 오류를 경험했는지...
func makeButton(title: String) -> UIButton {
let button = UIButton()
/*
버튼 이름이 특정 문자와 일치하면
버튼 색을 오랜지 색으로 변경한다.
*/
let orange = ["+", "-", "*", "/", "AC", "="]
for i in orange {
if title.contains(i) {
button.backgroundColor = .orange
break
} else {
button.backgroundColor = 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.layer.cornerRadius = 40
button.snp.makeConstraints { $0.width.height.equalTo(80)}
return button
}
buttonData 변수에 문자열과 색상 정보를 담는 튜플의 배열을 담아서 연산자의 버튼 색을 변경했다.func setupStackView() {
let buttonData: [[(String, UIColor)]] = [
[("7", .darkGray),("8", .darkGray),("9", .darkGray),("+", .orange)],
[("4", .darkGray),("5", .darkGray),("6", .darkGray),("-", .orange)],
[("1", .darkGray),("2", .darkGray),("3", .darkGray),("*", .orange)],
[("AC", .orange),("0", .darkGray),("=", .orange),("/", .orange)]
]
let stackViews = buttonData.map {
makeHorizontalStackView(buttonInfo: $0)
}
let verticalStackView = makeVerticalStackView(stackViews)
view.addSubview(verticalStackView)
verticalStackView.snp.makeConstraints {
$0.top.equalTo(label.snp.bottom).offset(60)
$0.height.equalTo(350)
$0.centerX.equalToSuperview()
}
}
func makeButton(title: String, backgroundColor: UIColor) -> UIButton {
let button = UIButton()
button.setTitle(title, for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 30)
button.backgroundColor = backgroundColor
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
button.snp.makeConstraints {
$0.height.width.equalTo(80)
}
return button
}
- Lv.5 는 버튼 속성 한개만 추가하면 되는 것으로 넘어가도록 하겠다.
addTarget을 사용하여 버튼 이벤트 구현buttonTapped 메소드를 만들어 최근에 탭된 버튼의 제목을 가져와 label.text의 값이 되게 함.if-else문을 사용하여, 0 일때 숫자가 탭되면 0을 지우고 숫자가 입력되게 처리@objc func buttonTapped(button: UIButton) {
guard let tappedButtonTitle = button.currentTitle else { return }
if label.text == "0" {
label.text = tappedButtonTitle
} else {
label.text! += tappedButtonTitle
}
}신기하게 버튼 이벤트를 구현하는 메소드와 label.text을 변경하는 메소드 두개를 이용하여 구현했다.
그리고 처음보는 hasPrefix라는 속성을 사용하여, 0 이 앞에 오지 못하게 예외처리한 부분이 신선했다.
@objc func buttonTapped(sender: UIButton) {
guard let buttonText = sender.currentTitle else { return }
if buttonText == "AC" {
} else if buttonText == "=" {
} else {
if currentText == "0" {
currentText = buttonText
} else {
currentText += buttonText
}
}
updateLabel()
}
func updateLabel() {
// 필요 시, 0 을 제거
if currentText.hasPrefix("0") && currentText.count > 1 {
currentText = String(currentText.dropFirst())
}
label.text = currentText
}
배운점
hasPrefix
String(문자열)이 지정된 접두사로 시작하는지 여부를 Boolean 값으로 반환합니다.선언 (Declaration)
func hasPrefix(_ prefix: String) -> Bool매개변수 (Parameters)
prefix
- 이 문자열에 대해 테스트 할 수 있는 접두사이다.
리턴 타입 (Return Value)
- 테스트할 문자열이
prefix에 넣은 접두사로 시작하면true를 반환하고, 그렇지 않으면false를 반환한다.
동일한 코드를 추가하여 구현되어 생략한다.
if buttonText == "AC" { currentText = "0" }
switch-case 문으로 버튼의 title 에 따라 조건에 맞는 로직을 처리하게 구현@objc func buttonTapped(button: UIButton) {
guard let tappedButtonTitle = button.currentTitle else { return }
// 누른 버튼에 따른 로직 구현
switch tappedButtonTitle {
case "AC":
label.text = "0"
return
case "=":
// 예외처리: 계산식이 연산자에서 끝났으면 아무동작 하지 않게 처리
if label.text?.last == "/" || label.text?.last == "*" || label.text?.last == "-" || label.text?.last == "+" {
return
} else {
let result = calculateModel.calculate(expression:label.text!) // 연산 수행
label.text = String(result!) // 결과 출력
}
case "0":
// 예외처리: "0" 만 있을 때 "0"을 추가 할 수 없게 처리
if label.text == "0" {
return
} else {
label.text! += tappedButtonTitle // 예외처리 상황이 아니면 계산식에 "0" 추가
}
case "/","*","-","+":
// 예외처리: 마지막 계산식에 연산자가 있으면, 더 이상 연산자를 사용 못하게 처리
if label.text?.last == "/" || label.text?.last == "*" || label.text?.last == "-" || label.text?.last == "+"
{
return
}
// 예외처리: 첫 계산식이 0과 연산자가 될 수 없게 처리
if label.text == "0" || label.text == "/" || label.text == "*" || label.text == "-" || label.text == "+" {
return
} else {
label.text! += tappedButtonTitle // 예외처리 상황이 아니면 계산식에 추가
}
default:
// 예외처리: 첫 계산식이 0이 될 수 없게 처리
print(label.text!.suffix(2))
// 연산자 뒤에 "0" 오면 "0" 을 제거
if ["+0", "-0", "*0", "/0"].contains(label.text!.suffix(2)) {
label.text!.removeLast()
}
if label.text == "0" {
label.text! = tappedButtonTitle // "0" 을 없애고 숫자를 추가
} else {
label.text! += tappedButtonTitle // 예외처리 상황이 아니면 계산식에 추가
}
}
}NSExpression을 사용해서 계산식을 처리하는 부분만 추가되었다.@objc func buttonTapped(sender: UIButton) {
guard let buttonText = sender.currentTitle else { return }
if buttonText == "AC" {
currentText = "0"
} else if buttonText == "=" {
currentText = String(calculate(expression: currentText) ?? 0)
} else {
if currentText == "0" {
currentText = buttonText
} else {
currentText += buttonText
}
}
updateLabel()
}extention을 사용하여, 코드를 분리하는 법도 배워서 코드의 가독성을 높히는 법을 배웠다.