Apple Developer Documentation
developer.apple.com
tag의 공식 문서를 살펴보면 이를 활용하는 viewWithTag라는 메서드가 있다.
메서드의 역할을 읽어보면 특정 값을 가지는 view를 반환하는 역할을 하는 듯 싶다
이를 보고 나는 tag에 나만의 규칙을 세워 그 값을 할당해주면 필요한 view를 수월하게 가져올 수 있겠다라는 생각을 하게 되었다.
적용 시킬 UI는 위의 사진과 같다
3가지의 감정 표현 중에서 하나를 필수로 선택해야 하는 것인데, TableView로 구성할까 그냥 View 3개로 구성할까 고민하다가 후자로 선택하게 되었다.
후자로 선택한 이유는 스크롤이 필요가 없어 cell을 재사용 등의 기능이 필요하지 않으며, 혹시라도 지정한 TableView의 크기와 셀 3개의 크기가 맞지 않으면 불필요하게 스크롤이 진행될 수도 있지 않을까 싶어서였다.
UITableView를 사용한다면 delegate 메서드인 didSelectItemAt을 사용하면 되겠지만, view를 사용할 것이므로 3가지 중 선택한 감정을 인식해 선택/미선택 상태를 바꿔주는 기능을 직접 구현해야 했다.
tag를 통해 선택한 감정이 이미 선택되어 있는 상태인지 점검해 미선택 상태일 경우 선택 상태로 바꿔주며, 선택되어 있던 감정은 미선택 상태로 바꿔주는 기능을 구현해보도록 하겠다
먼저 각 view에 tap gesture를 추가해주고 tap 되었을 때 실행시킬 메서드로 emotionViewDidClicked를 설정해주었다.
@objc func emotionViewDidClicked(_ sender: UITapGestureRecognizer){
guard let selectEmotion = sender.view as? RecordRegisterEmotionSelectView.FirstEmotionView else { return }
if(selectEmotion.tag == 1){
return
}
guard let willDeselectEmotion = mainView.viewWithTag(1) as? RecordRegisterEmotionSelectView.FirstEmotionView else {
selectEmotion.changeSelectState()
return
}
willDeselectEmotion.changeDeselectState()
selectEmotion.changeSelectState()
}
emotionViewDidClicked을 살펴보면 sender를 통해 tap이 일어난 view를 인식한다.
이후 tag 값 검사를 통해 이미 선택이 되어있는 경우 함수를 빠르게 종료 시킨다.
미선택 상태인 경우에는 선택되어 있는 감정을 viewWithTag 메서드를 통해 가져오고 기존 선택된 감정과 새로 선택된 감정의 상태를 change-State 메서드로 바꿔준다.
guard 문을 통해 viewWithTag로 이전에 선택되어 있는 감정을 가져올 때 만약 nil값일 경우, 아직 선택되어있던 감정이 없는 것으로 새로 선택된 감정을 선택 상태로 바꿔주고 함수를 종료시킨다.
상태에 따른 UI 변화 코드는 아래와 같다
func changeDeselectState(){
self.tag = 0
imageBackView.backgroundColor = Color.grey0
titleLabel.textColor = Color.body
}
func changeSelectState(){
self.tag = 1
imageBackView.backgroundColor = Color.mint10
titleLabel.textColor = Color.mint100
}
기능 구현은 이렇게 해주면 된다.
하지만 한 가지 걸리는 부분이 있다. 바로 코드 이해가 어렵다는 점이다.
아래 tag를 사용한 코드만 따로 빼놓은 것을 잠깐 살펴보겠다.
self.tag = 0
self.tag = 1
if(selectEmotion.tag == 1){
return
}
guard let willDeselectEmotion = mainView.viewWithTag(1) as? RecordRegisterEmotionSelectView.FirstEmotionView else {
selectEmotion.changeSelectState()
return
}
나는 0을 선택하지 않은 상태, 1을 선택한 상태로 규칙을 세우고 코드를 작성한 것인데 과연 다른 개발자들이 이 코드를 봤을 때 이것을 단번에 이해할 수 있을까? 절대 아니라고 본다.
코드를 작성한 나도 1달 뒤에 보면 뭐지하고 고민하는 시간을 가질게 뻔한 코드이다.
(tag 값에 대해 주석이 있다면 이해가 바로 될 수도 있겠지만, 주석 작성 또한 좋지 않은 방법이므로… 차라리 리팩토링을.. )
요즘 클린코드라는 책을 읽으면서 네이밍이 코드 이해와 가독성에 주는 영향을 뼈저리게 느끼고 있기 때문에 코드 이해를 높일 수 있도록 리팩토링을 진행해보도록 하겠다.
먼저 값을 할당할 때 select와 deselect라는 이름으로 tag에 값을 할당하고 싶었다.
이를 위해 생각한 첫 번째 방법은 클래스 내부에 상수를 선언해 값을 할당하는 것이다.
하지만 이 방식으로 사용할 경우 이와 비슷한 tag에 select/deselect 값을 넣는 경우 매번 클래스 내부 프로퍼티로 값을 할당해줘야 하는게..
통일성 깨지기도 쉽고 각 파일마다 다르게 관리된다는게 적절치 않은 것 같다는 생각을 했다.
그래서 tag에 사용되는 값들을 관리할 수 있는 구조체가 있으면 좋겠다는 생각을 하게되었다.
struct ViewTag{
static let select = 1
static let deselect = 0
}
@objc func emotionViewDidClicked(_ sender: UITapGestureRecognizer){
guard let selectEmotion = sender.view as? RecordRegisterEmotionSelectView.FirstEmotionView else { return }
if(selectEmotion.tag == ViewTag.select){
return
}
guard let willDeselectEmotion = mainView.viewWithTag(ViewTag.select) as? RecordRegisterEmotionSelectView.FirstEmotionView else {
selectEmotion.changeSelectState()
return
}
willDeselectEmotion.changeDeselectState()
selectEmotion.changeSelectState()
}
func changeDeselectState(){
self.tag = ViewTag.deselect
imageBackView.backgroundColor = Color.grey0
titleLabel.textColor = Color.body
}
func changeSelectState(){
self.tag = ViewTag.select
imageBackView.backgroundColor = Color.mint10
titleLabel.textColor = Color.mint100
}
이전 코드보다는 select, deselect 표현이 더 잘되어서 코드 이해가 좀 더 수월해진 것 같다
개인적으로는 select, deselect로만 값을 할당하고 싶어 typelias 등 고민을 해보았지만 잘 되지 않아서 일단은 이렇게 리팩토링을 마무리했다.
UITextField에 값 할당한 경우 이벤트 감지시키기 (0) | 2023.01.08 |
---|---|
UITableView 상단 space 제거 (0) | 2023.01.03 |
UITextView Placeholder 및 글자 수 카운팅 (0) | 2022.12.19 |
하위View touch event 부모View로 인식/전달 (0) | 2022.11.12 |
UITableView, UITableViewCell transparent (0) | 2022.11.08 |