안녕하세요 콩리또 입니다.🌯
Swift 왕초보로써 제가 코딩하면서 어려웠던 점을 포스팅하고 있는데요
오늘은 TextField 유효성 검사 기능을 구현한 부분에 대해 말씀드리겠습니다!
모든 포스팅 말투는 편한 말투로 작성합니다!
피드백은 댓글로 부탁드려요!
감사합니다🥰
🤔 배경


TableVIew에 행을 추가할때, 중복되는 이름은 추가할 수 없도록 동일한 텍스트에 대한 유효성 검사를 구현하고 싶었음
근데 따로 버튼을 누르지않고, 중복 텍스트가 입력됐을 때 바로 TextField 밑에 해당 문구가 보이도록 만들고 싶어
인터넷을 뒤지던 중, 요 블로그(https://velog.io/@sso0022/iOS-TextField-글자수-제한하기-한글 )를 보고
'오호 NotificationCenter를 사용하면 되겠군..!' 하고 사용해봄

🤔 1차 오류성 검사 제작 과정
①AddNewCategory 뷰컨
카테고리명을 입력하는 ViewController의 viewDidLoad()에 → NotificationCenter addObserver 선언
- selector : 실행할 @objc 함수를 지정
- name : 감시하고 싶은 UI 타입 및 어떤 감시법?을 쓸건지 지정
- object : 내가 감시하고싶은 텍스트필드를 지정
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
render()
// 텍스트필드 입력갑을 옵저버하는 부분
NotificationCenter.default.addObserver(self, selector: #selector(checkTextValidation(_:)), name: UITextField.textDidChangeNotification, object: categoryTextField)
}
② @objc 함수에 유효성 로직 구현
- 함수 파라미터에 (_ notification: Notification)라고 작성
- ①에 정의한 object를 해당 타입으로 설정
- contains는 배열의 값을 검색해줌 (값 ⭕️ : true / 값 ❌ : false를 반환)
//텍스트 필드를 감시해서 동일 문자열 입력 시 유효성검사를 띄우려고함..
@objc private func checkTextValidation(_ notification: Notification) {
if let textField = notification.object as? UITextField {
if let text = textField.text {
//categories의 1번 인덱스의 categoryName과 텍스트필드 입력값이 일치하면 유효성검사
//1번 인텍스 categoryName = "아이돌"
if categories[1].categoryName.contains(text) == true {
validationLabel.text = "Category name already exists."
validationLabel.textColor = .red
} else {
validationLabel.text = "Good name😎"
validationLabel.textColor = .blue
}
}
}
}
😨 1차 결과화면

오 쉣!!!!!!!!!😱😱😱😱😱
array에 .contains를 주니 한 글자씩 찾아줌..😭
나는 “아이돌” 단어일때 유효성검사 문구를 띄우고 싶었는데
결과 화면을 보면 “아” / “아이” / “아이돌” → 이 3가지 상황에 반응을 함;;;;;;;
찾아보니 array에 .contains를 주면 해당하는 한글자 한글자씩 다 돌아서 시간도 오래걸리고 효율이 좋지않다고함..
🤓 중간 리서치

'다 포기하고 그냥 버튼을 눌렀을때 유효성 검사를 하게 할까..?'라고 고민 하던 중
도서관에서 Swift 쌉고수 켐선생님을 만나 조언을 구함
🐶 켐선생님의 개꿀 조언 🍯
- contains는 한글자씩 다 찾아줌 → array에서 값을 통째로 찾아주는 다른 메소드 찾기
- categories > 전체 Category > CategoryName를 잡으려면 .map를 사용하기
// 이렇게 하면 카테고리의 모든 카테고리네임을 잡을 수 있다고함
categories.map { $0.categoryName }
이걸 바탕으로 구글링을 하니
- 일단 array에서 값을 통째로 찾아주는 다른 메소드는 없는 것 같음..ㅠㅠ🥲
- Set에서 contains를 사용하면 문자열을 하나씩 찾아주지 않고 통째로 찾아준다고 함
- 그리고 Array에서 contains를 사용했을때보다 시간효율성이 매우 높다고 함
- Map을 이용하면 데이터 변형 할 수 있고 더 가독성 높아짐
아무튼 이 정보를 가지고 2차 코딩 들어감
참고 블로그
Set : https://stackoverflow.com/questions/34161786/reduce-array-to-set-in-swift
Map : https://shark-sea.kr/entry/Swift-고차함수-Map-Filter-Reduce-알아보기
🤔 2차 오류성 검사 제작 과정
① 전체 CategoryName를 Set으로 선언
// contains에서 전체 문자열을 통째로 찾기 위해 array를 Set로 선언
let nameSet = Set(categories.map {$0.categoryName})
② 유효성 검사 로직 수정
그리고 그 Set에 contains를 사용해 값을 찾고 중복되면 이미 있다는 문구를 내보냄
@objc private func checkTextValidation(_ notification: Notification) {
if let textField = notification.object as? UITextField {
if let text = textField.text {
//categories의 categoryName을 set으로 모아놓은 배열을 만듦
let nameSet = Set(categories.map {$0.categoryName})
//위에서 선언한 Set에 contains(text)를 줘서 중복되면 유효성검사 및 SAVE 버튼 비활성화
if nameSet.contains(text) == true {
validationLabel.text = "Category name already exists 🥲"
validationLabel.textColor = .red
navigationItem.rightBarButtonItem?.isEnabled = false
} else if text.isEmpty {
validationLabel.text = ""
} else {
validationLabel.text = "Good name 😎"
validationLabel.textColor = .blue
}
}
}
}

2차 결과화면!!!
VERY GOOD!!🥰🥰🥰🥰🥰
array에서 set로 바꾸니 한글자씩 인식하던게 통째로 인식됨!!!!!!
오늘의 결론
모르는게 있으면 포기하지말고 개발자를 찾아가자!
🖥 전체 코드
혹시 상세한 코드가 궁금하실 수 있어서 전체코드 올려놓겠습니다!
import UIKit
class AddNewCategory: UIViewController {
// MARK: - Properties
lazy var categoryTextField: UITextField = {
let categoryTextField = UITextField()
categoryTextField.placeholder = "Enter up to 10 characters"
categoryTextField.delegate = self
categoryTextField.clearButtonMode = .whileEditing
categoryTextField.borderStyle = .none
return categoryTextField
}()
lazy var validationLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 16, weight: .regular)
return label
}()
var categoryNameDelegate: CategoryNameProtocol?
// 더미데이터 입니다. 데이터가 들어오면 삭제할 예정입니다.
let word1 = KWord(name: "비티짱1", isFavorite: true, isOriginal: true, description: "바티바티", relatedWords: ["바티짱2", "바티짱3"])
let word2 = KWord(name: "비티짱2", isFavorite: false, isOriginal: true, description: "짱", relatedWords: ["바티짱1", "바티짱3"])
let word3 = KWord(name: "비티짱3", isFavorite: true, isOriginal: true, description: "ㅋㅋㅋㅋ", usages: [Usage(korean: "지난 주 뮤직쇼 봤어?", english: "music show you see?"), Usage(korean: "지난 치티치티치티?", english: "티clclslsl?")], relatedWords: ["바티장1", "바티짱2"])
let word4 = KWord(name: "아오나1", isFavorite: true, isOriginal: false)
let word5 = KWord(name: "zz", isFavorite: false, isOriginal: true, description: "zzzz")
let word6 = KWord(name: "gg1", isFavorite: false, isOriginal: false, description: "zz")
lazy var categories: [Category] = [Category(categoryName: "💜BTS💜", count: "8 Words", kwords: [word1, word2, word3]),Category(categoryName: "아이돌", count: "8 Words", kwords: [word4, word5]), Category(categoryName: "소녀시대", count: "8 Words", kwords: [word6])]
// MARK: - Lifecycle
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
underLine()
}
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
render()
// 텍스트필드 입력갑을 옵저버하는 부분
NotificationCenter.default.addObserver(self, selector: #selector(checkTextValidation(_:)), name: UITextField.textDidChangeNotification, object: categoryTextField)
}
private func configureUI() {
view.backgroundColor = UIColor.white
self.navigationItem.title = "Category Name"
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationItem.rightBarButtonItem =
UIBarButtonItem.init(title: "Save", style: .done, target: self, action: #selector(addNewCategory(_:)))
navigationItem.leftBarButtonItem = UIBarButtonItem.init(title: "Cancel", style: .done, target: self, action: #selector(dismissModal(_:)))
}
private func render() {
view.addSubview(categoryTextField)
categoryTextField.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.safeAreaLayoutGuide.leftAnchor, right: view.safeAreaLayoutGuide.rightAnchor, paddingTop: 20, paddingLeft: 16, paddingRight: 16)
view.addSubview(validationLabel)
validationLabel.anchor(top: categoryTextField.bottomAnchor, left: view.safeAreaLayoutGuide.leftAnchor, right: view.safeAreaLayoutGuide.rightAnchor, paddingTop: 20, paddingLeft: 16, paddingRight: 16)
}
func underLine() {
let border = CALayer()
border.frame = CGRect(x: 0, y: categoryTextField.frame.size.height+10, width: categoryTextField.frame.width, height: 1)
border.borderWidth = 1
border.backgroundColor = UIColor.gray.cgColor
categoryTextField.layer.addSublayer(border)
}
@objc private func checkTextValidation(_ notification: Notification) {
if let textField = notification.object as? UITextField {
if let text = textField.text {
//categories의 categoryName을 set으로 모아놓은 배열을 만듦
let nameSet = Set(categories.map {$0.categoryName})
//위에서 선언한 Set에 contains(text)를 줘서 중복되면 유효성검사를 반환하게 만듦
if nameSet.contains(text) == true {
validationLabel.text = "Category name already exists 🥲"
validationLabel.textColor = .red
navigationItem.rightBarButtonItem?.isEnabled = false
} else if text.isEmpty {
validationLabel.text = ""
} else {
validationLabel.text = "Good name 😎"
validationLabel.textColor = .blue
}
}
}
}
@objc private func addNewCategory(_ sender: Any) {
if let text = categoryTextField.text {
categoryNameDelegate?.categoryNameSend(name: text)
}
dismiss(animated: true, completion: nil)
}
@objc private func dismissModal(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
}
extension AddNewCategory: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if let char = string.cString(using: String.Encoding.utf8) {
let isBackSpace = strcmp(char, "\\b")
if isBackSpace == -92 {
return true
}
}
guard categoryTextField.text!.count < 10 else { return false }
return true
}
}
protocol CategoryNameProtocol {
func categoryNameSend(name: String)
}
'🍎 IOS > UIKit' 카테고리의 다른 글
| [Swift/UIKIt] UITableView에 대해 알아보자! ① (1) | 2022.09.19 |
|---|---|
| [iOS/UIKIt] UItableViewcell 간 간격 띄우기 (Section header 이용) (1) | 2022.09.16 |
| [iOS/UIKit] Simulator 실행시 보이는 StoryBoard 파일 변경하는 법 (2) | 2022.06.12 |