<aside>
앱 개발 입문 과제 : 계산기 만들기 TIL 링크 (아래 클릭) iOS 앱 개발 입문 과제 : 계산기 만들기 TIL
</aside>
<aside>
계산기 버튼을 수직으로 정렬하고 싶어서 [[UIButton]]
배열을 만들어 수평 스택뷰로 묶고, 그걸 수직 스택뷰로 쌓았다. 코드를 완성하고 너무 뿌듯했는데… 실행 결과는 전혀 예상 밖이었다. 버튼이 네 줄로 나란히 정렬되기는커녕, 한 줄로 길게 뻗어버렸다. Debug View Hierarchy를 켜보니, 스택뷰 구조가 완전히 잘못 잡혀 있었고, 버튼들이 줄도 없이 그대로 세워져 있는 상태였다. 대환장 파티가 열렸지만, 한편으론 아름다운 느낌도 들었다.. ㅋ
이런 구조를 만들고 싶었다.
UIStackView (vertical)
├─ UIStackView (horizontal)
│ ├─ "7" "8" "9" "+"
├─ UIStackView (horizontal)
│ ├─ "4" "5" "6" "-"
그래서 2차원 배열로 버튼을 만들고, 그걸 for문으로 풀어서 수평 → 수직으로 붙이면 된다고 생각했다.
var arrButton: [[UIButton]] = [[]] // 이 라인이 문제
처음부터 빈 배열 [[]]
을 선언하면서, 버튼을 생성한 후에도 append
를 계속해서 버튼을 수평 스택뷰에 넣으니, 의도보다 하나 더 많은 행이 생겼고 각 버튼마다 별도의 스택뷰가 만들어지면서 엉망진창이 된 것.
for arrStackView in numberPad {
for stackView in arrStackView {
verticalStackView.addArrangedSubview(stackView)
}
view.addSubview(verticalStackView) // 반복문 안에서 계속 addSubview 실행됨
}
여기서 verticalStackView
를 매 행마다 뷰에 추가한 것도 문제였다. UIView는 동일한 서브뷰를 여러 번 추가하면 레이아웃이 꼬이고 계층구조가 복제된 것처럼 렌더링될 수 있다.
func makeHorizontalStackView(_ buttonArray: [[UIButton]]) -> [[UIStackView]]
이 함수에서 [[UIStackView]]
를 반환하면서 버튼 하나마다 스택뷰 하나를 만들고 그걸 또 배열에 담아버렸다. 버튼 16개면 스택뷰도 16개 이걸 세로로 하나씩 추가하니 결과는 피노키오처럼 수직으로 늘어진 구조.
var arrButtons: [[UIButton]] = [] // [[]] 아님
func makeHorizontalStackView(_ buttonArray: [[UIButton]]) -> [UIStackView] {
var horizontalStackView: [UIStackView] = []
for row in buttonArray {
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.spacing = 10
stackView.distribution = .fillEqually
for button in row {
stackView.addArrangedSubview(button)
}
horizontalStackView.append(stackView)
}
return horizontalStackView
}
func makeVerticalStackView(_ numberPad: [UIStackView]) {
let verticalStackView = UIStackView()
verticalStackView.axis = .vertical
verticalStackView.spacing = 10
verticalStackView.distribution = .fillEqually
for rowStackView in numberPad {
verticalStackView.addArrangedSubview(rowStackView)
}
view.addSubview(verticalStackView) // 반복문 밖에서 단 한 번만 실행
}
정확한 개념이 잡히지 않은 상태에서 2차원 배열을 처음 사용하다 보니 다양한 문제가 발생 했다. 빈 2차원 배열을 선언하는 것 부터, for문으로 배열을 두번 해체하고 append하는 과정까지 어려움을 많이 겪었다. 어느정도 의도와 방향을 잡고 코드를 작성하긴 했지만, 그 의도를 실행에 옮기는데 필요한 지식이 많이 부족하다는 것을 체감했다. 그래도 이 문제를 통해 2차원 배열과 for문의 활용을 조금 더 깊게 공부할 수 있었다.
</aside>
<aside>
라벨에 숫자 값을 출력하고 싶어서 아래처럼 코드를 작성했다.
resultLabel.text = "\\(resultLabel)" // 스트링 인터폴레이션으로 정수 담기
분명 숫자를 보여주려는 의도였는데, 실행하자 앱 화면엔 예상과 다르게 UILabel:...
같은 이상한 문자열이 출력됐다. 내가 직접 써놓고도 뭐가 문제인지 몇 초간 멍하게 쳐다봤다.
라벨에 resultNumber
라는 정수 값을 넣고 싶었다.
resultLabel.text = "\\(resultNumber)"
변수 이름이 비슷했다.
resultLabel
→ 실제 화면에 표시할 UILabel 객체resultNumber
→ 화면에 표시할 정수 데이터근데 실수로 resultLabel
을 문자열로 감싸서 넣는 실수를 했다.
결과적으로 UILabel
객체 자체를 문자열로 출력한 꼴이 됐다.
resultLabel.text = "\\(resultLabel)"
Swift는 객체를 문자열로 변환하면 내부 정보를 보여주는 형태로 바꾸는데, 그게 바로 <UILabel: 0x...>
처럼 보였던 것. 런타임 에러는 나지 않지만, 의도와 전혀 다른 결과가 나온다.
아래처럼 정확히 의도한 변수인 resultNumber
를 넣어줬다.
resultLabel.text = "\\(resultNumber)"
딱 이 한 줄 바꿨을 뿐인데, 화면에는 내가 원했던 숫자 12345
가 제대로 표시됐다.
변수 이름이 얼마나 명확해야 하는지를 다시 한 번 느낀 사례였다. 비슷한 이름끼리는 항상 의도와 역할이 확실히 다르게 지어줘야 이런 혼동을 줄일 수 있다. 최종 코드까지 완성하면서 종종 동일한 실수를 경험했다. 변수명과 그 역할이 뒤섞이기 시작하면서 오류가 꼬이기도 했다. 다음부터는 변수 이름을 확실히 구분해서 작성해보려고 한다.
</aside>
<aside>
과제 가이드는 이렇게 되어 있었다:
func makeHorizontalStackView(_ views: [UIView]) -> UIStackView
그런데 내 코드에서는 이렇게 바꿔서 작성했다:
private func makeHorizontalStackView(_ buttonArray: [UIButton]) -> UIStackView
사실 처음엔 가이드대로 [UIView]
로 작성을 해보려고 했지만, 내가 의도한 방향은 Button 생성 함수를[UIButton]
타입으로 반환하는 것이었다. 분명 [UIView]
타입으로 파라미터를 받는 방법도 있겠지만, 내가 작성하고자 하는 방향으로 이어가기로 했다.
그리고 처음에 가이드대로 파라미터 이름을 views: [UIView]
로 하니까, 혼동되는 부분이 많았다. 코드 구조상 파라미터로 button을 받아야 하기 때문에 bottonArray
로 이름을 변경했다.
</aside>
<aside>
</aside>
<aside>
</aside>
<aside>
</aside>