diff --git "a/1\354\260\250 \353\260\234\355\221\234\353\202\264\354\232\251 (ExpandableCell).md" "b/1\354\260\250 \353\260\234\355\221\234\353\202\264\354\232\251 (ExpandableCell).md"
index 2fbc324..8eb8f9f 100644
--- "a/1\354\260\250 \353\260\234\355\221\234\353\202\264\354\232\251 (ExpandableCell).md"
+++ "b/1\354\260\250 \353\260\234\355\221\234\353\202\264\354\232\251 (ExpandableCell).md"
@@ -1,59 +1,51 @@
# ExpandableCell
-***
```swift
struct ExpandableNames {
+ // 해당 섹션이 확장된 상태인지 아닌지 확인
var isExpanded: Bool
- let country : String
- let cities : [String]
+ let country: String
+ // 도시 이름
+ let cities: [String]
}
```
-* isExpanded: 변수로 셀들이 확장된 상태인지 아닌지 확인
-* cities: 도시들을 저장할 구조체 안의 문자열 배열
```swift
- @objc func handleExpandClose(button: UIButton) {
+// [수정]
+// 해당 메소드는 Objective-C 호환일 필요가 없으므로 `@objc` 어노테이션 제거
+func handleExpandClose(button: UIButton) {
+ let section = button.tag
+ // button.tag 값을 이용해 섹션값 정의 , tableview각 버튼의 tag값 0부터 오름차순임
+ // [수정]
+ // `Bool`에 정의된 `toggle()` 메소드 사용
+ button.isSelected.toggle()
- let section = button.tag
- // button.tag 값을 이용해 섹션값 정의 , tableview각 버튼의 tag값 0부터 오름차순임
- button.isSelected = !button.isSelected
-
- var indexPaths = [IndexPath]()
- for row in twoDimensionalArray[section].cities.indices{
- print(0,row)
- let indexPath = IndexPath(row: row, section: section)
- indexPaths.append(indexPath)
-
- }
-
- let isExpanded = twoDimensionalArray[section].isExpanded
-
- twoDimensionalArray[section].isExpanded = !isExpanded
-
-
- if isExpanded{
- tableView.deleteRows(at: indexPaths, with: .fade)
-
- } else{
- tableView.insertRows(at: indexPaths, with: .fade)
- }
+ // [수정]
+ // 기존 절차적 로직에서 함수형 프로그래밍 적용하여 리팩토링
+ // `indexPaths`가 변수가 아닌 상수가 되면서 이후 코드에서 해당 변수의 값을 훼손시킬 염려가 없어짐
+ let indexPaths = twoDimensionalArray[section].cities.enumerated()
+ .map { offset, _ in IndexPath(row: offset, section: section) }
+ twoDimensionalArray[section].isExpanded.toggle()
+ if isExpanded {
+ tableView.deleteRows(at: indexPaths, with: .fade)
+ } else {
+ tableView.insertRows(at: indexPaths, with: .fade)
}
+}
```
-* Section에 있는 Button을 눌렀을때 Data들이 펼쳐지거나, 닫혀지는 것이므로
-* 각 tableView의 HeaderSection에 있는 Expandable하게 될 Button의 tag 값으로 Data들의 섹션값을 구별한다.
+Section에 있는 Button을 눌렀을때 Data들이 펼쳐지거나, 닫혀지는 것이므로 각 tableView의 HeaderSection에 있는 Expandable하게 될 Button의 tag 값으로 Data들의 섹션값을 구별한다.
```swift
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
-let headerView = UIView.instantiate(CityChooseSection.self)
-headerView.foldButton.tag = section
-
-headerView.configure(with: twoDimensionalArray[section].country)
-headerView.delegate = self
-return headerView
+ let headerView = UIView.instantiate(CityChooseSection.self)
+ // [수정]
+ // 버튼의 태그값을 설정하는 것도 메소드를 통해서 하는 것이 좋아 보임
+ headerView.configure(atSection: section, withTitle: twoDimensionalArray[section].country)
+ headerView.delegate = self
+ return headerView
}
```
-* 위의 코드를 보면 테이블뷰의 헤더 섹션부분은 Xib로 커스텀을 했으며, 이 과정속에서 헤더섹션뷰의 foldButton의 tag값을 section으로
-* 지정해줌으로서 Data들의 값들을 foldButton의 tag값으로 관리가 가능해졌다.
+테이블뷰의 헤더 섹션부분은 Xib로 커스텀을 했으며, 이 과정속에서 헤더섹션뷰의 foldButton의 tag값을 section으로 지정해줌으로서 Data들의 값들을 foldButton의 tag값으로 관리가 가능해졌다.
```swift
protocol CityChooseSectionDelegate: class {
@@ -62,27 +54,49 @@ protocol CityChooseSectionDelegate: class {
class CityChooseSection: UIView {
weak var delegate: CityChooseSectionDelegate?
- @IBOutlet weak var titleLabel: UILabel!
- @IBOutlet weak var foldButton: UIButton!
+ // [수정]
+ // 접근수준을 internal에서 private으로 변경
+ @IBOutlet private weak var titleLabel: UILabel!
+ @IBOutlet private weak var foldButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
foldButton.addTarget(self, action: #selector(touchUpFoldButton(_:)), for: .touchUpInside)
}
- func configure(with title: String) {
+ // [수정]
+ // 메소드를 만들어 View를 초기화하는 것은 프로퍼티의 접근 수준을 낮추고 필요한 기능만 노출하려는 의도임
+ // 현재는 프로퍼티가 외부에 공개되어 있으므로 메소드를 만드는 것이 의미가 없음
+ // 그래서 프로퍼티의 접근 수준을 비공개로 설정하고, View를 초기화하기 위한 값을 매개변수로 설정함
+ func configure(atSection section: Int, withTitle title: String) {
+ foldButton.tag = section
titleLabel.text = title
}
+
@objc private func touchUpFoldButton(_ sender: UIButton) {
delegate?.cityChooseSection(self, didSelectButton: sender)
}
}
```
- * 위의 코드 =Xib의 UIView 클래스 부분의 코드이다.
- * CityChooseSectionDelegate protocol TableView의 해당 Section에 맞는 버튼을 눌렀을때 올바르게 foldButton의 data전달이 되기 위한 delegate pattern이다.
-* awakeFromNib()
- 공식문서의 정의: Prepares the receiver for service after it has been loaded from an Interface Builder archive, or nib file.
-* 간단하게 해석 하자면 nib 파일에서로드 된 후 서비스를 위해 수신자를 준비하는 함수이다. 그래서 이 메소드 안에서 addTarget을 통해 foldButton에게 delegate패턴인 touchUpFoldButton을 지정해준다.
+ * Xib의 UIView 클래스 부분의 코드이다.
+
+ * CityChooseSectionDelegate protocol TableView의 해당 Section에 맞는 버튼을 눌렀을때 올바르게 foldButton의 data전달이 되기 위한 delegate pattern이다.
+
+ > 프로토콜이 델리게이트 패턴인 것은 아님. 델리게이트 패턴을 구현하기 위해 프로토콜을 활용하는 것.
+ >
+ > `CityChooseSectionDelegate` 프로토콜 : `CityChooseSection` 뷰의 책임을 위임하기 위한 Delegate Pattern을 적용하기 위해 정의한 프로토콜. 여기서는 TableView의 해당 Section에 맞는 버튼을 눌렀을 때의 처리를 위임하는 메소드 하나를 정의하였음
+
+* `awakeFromNib()`
+
+ * 공식문서의 정의: Prepares the receiver for service after it has been loaded from an Interface Builder archive, or nib file.
+
+ * 간단하게 해석 하자면 nib 파일에서로드 된 후 서비스를 위해 수신자를 준비하는 함수이다. 그래서 이 메소드 안에서 addTarget을 통해 foldButton에게 delegate패턴인 touchUpFoldButton을 지정해준다.
+
+ > - `addTarget`은 타겟-액션 패턴을 위한 메소드임
+ > - `touchUpFoldButton`은 액션 메소드임
+ > - 액션 메소드가 델리게이트 프로토콜에 정의된 메소드를 호출한다는 식의 문장이 되어야 함
+ >
+ > 그래서 이 메소드에서 `addTarget(_:action:for:)` 메소드를 사용하여 `foldButton`의 터치업 이벤트에 대한 액션(`touchUpFoldButton(_:)`을 정의하고, 액션 메소드가 호출될 때 델리게이트의 `cityChooseSection(_:didSelectButton:)` 메소드를 호출하도록 한다.
```swift
extension CityChooseTableViewController: CityChooseSectionDelegate {
@@ -92,27 +106,27 @@ extension CityChooseTableViewController: CityChooseSectionDelegate {
}
```
* 실질적인 ExpandableCell이 보여지는 CityChooseTableViewController에서 Delegate 패턴을 통해
-cityChooseSection 메소드로 handleExpandClose 메소드를 연결시켜서 작동되게 하는 코드.
+ cityChooseSection 메소드로 handleExpandClose 메소드를 연결시켜서 작동되게 하는 코드.
+
+ > 본문에 메소드를 언급할 때 메소드 시그니처를 밝히는 것이 좋음
+ >
+ > 실질적인 ExpandableCell이 보여지는 CityChooseTableViewController에서 Delegate 패턴을 통해
+ > `cityChooseSection(_:didSelectButton:)` 메소드로 `handleExpandClose(button:)` 메소드를 연결시켜서 작동되게 하는 코드.
+
+* 그리고 지정된 더미데이터의 크기 만큼 순차적으로 indexPath 배열을 만들어준다.
-* 그리고 지정된 더미데이터의 크기 만큼 순차적으로 indexPath 배열을 만들어준다.
-* #### 왜 indexPaths 배열을 만드는가?
-* 뒤에 사용될 deleteRows,insertRows 메소드의 indexPaths 파라미터는 indexPath로 이루어진 배열이므로.
+### 왜 indexPaths 배열을 만드는가?
+
+* 뒤에 사용될 `deleteRows(at:with:)`, `insertRows(at:with:)` 메소드의 indexPaths 파라미터는 indexPath로 이루어진 배열이므로.
```swift
-var indexPaths = [IndexPath]()
- for row in twoDimensionalArray[section].cities.indices{
- print(0,row)
- let indexPath = IndexPath(row: row, section: section)
- indexPaths.append(indexPath)
-
- }
-
- ....
-
-
- if isExpanded{
- tableView.deleteRows(at: indexPaths, with: .fade)
-
- } else{
- tableView.insertRows(at: indexPaths, with: .fade)
- }
+let indexPaths = twoDimensionalArray[section].cities.enumerated()
+ .map { offset, _ in IndexPath(row: offset, section: section) }
+
+...
+
+if isExpanded {
+ tableView.deleteRows(at: indexPaths, with: .fade)
+} else {
+ tableView.insertRows(at: indexPaths, with: .fade)
+}
```