Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 스위프트
- Xcode
- combine
- clean architecture
- map
- uitableview
- swiftUI
- 리팩토링
- Protocol
- UICollectionView
- RxCocoa
- 리펙토링
- UITextView
- uiscrollview
- MVVM
- tableView
- 애니메이션
- 리펙터링
- rxswift
- Refactoring
- Clean Code
- ios
- HIG
- Observable
- collectionview
- 클린 코드
- Human interface guide
- ribs
- swift documentation
- SWIFT
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] UI 컴포넌트 - 검색창 TextField (SearchTextField) 본문
UI 포인트
- leftView, rightView
- padding 설정
- eftViewRect 설정, rightViewRect 설정
- textRect(bounds:): 입력중이 아닌 resignFirstResponder 상태일 경우의 입력된 text 위치
- editingRect(bounds:): 입력중의 텍스트 위치
- placeholderRect(bounds:): placeholder의 위치
- delegate를 통해 특정 타이밍에 leftView, rightView 사라지게 하는 방법
- textFieldDidBeginEditing(:): 포커스를 얻은 경우
- textFieldDidEndEditing(:): 포커스를 잃은 경우
- textField(:shouldChangeCharactersIn:replacementString:): 입력 중 인터렉션 처리 (메소드 개념 참고)
String trim 처리 기본기
- trimmingCharacters(in:): 문자열 앞뒤 공백 제거
let a = " 123 456 \n"
print("before trimmingCharacters: \(a.count) ") // 10
print("after trimmingCharacters: \(a.trimmingCharacters(in: .whitespacesAndNewlines).count) ") // 7
BaseTextField 정의
class BaseTextField: UITextField {
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
fatalError("not implement required init?(coder: NSCoder)")
}
convenience init(isSecureEntry: Bool = false,
keyboardType: UIKeyboardType,
returnKeyType: UIReturnKeyType = .done) {
self.init(frame: .zero)
self.isSecureTextEntry = isSecureEntry
self.keyboardType = keyboardType
self.returnKeyType = returnKeyType
self.autocapitalizationType = .none
}
func configure() {}
func bind() {}
}
SearchTextField 정의
- SearchTextField 클래스 생성
class SearchTextField: BaseTextField {
}
- leftView, rightView에 들어갈 button 정의
private lazy var searchButton: UIButton = {
let button = UIButton()
button.setImage(#imageLiteral(resourceName: "btnSearchRight"), for: .normal)
return button
}()
private lazy var clearButton: UIButton = {
let button = UIButton()
button.setImage(#imageLiteral(resourceName: "btnClearRight"), for: .normal)
button.addTarget(self, action: #selector(didTapClearButton), for: .touchUpInside)
return button
}()
- SearchTextField의 기본 UI 세팅
- 주의할 점: clearButtonMode = .never로 설정해야 rightView에 view를 넣었을때 적용
override func configure() {
super.configure()
delegate = self
borderStyle = .none
textColor = .label
font = .systemFont(ofSize: 16.0, weight: .bold)
attributedPlaceholder = NSAttributedString(string: "검색 창",
attributes: [NSAttributedString.Key.foregroundColor: UIColor.placeholderText])
layer.borderWidth = 1.0
layer.borderColor = UIColor.lightGray.cgColor
layer.cornerRadius = 4.0
clearButtonMode = .never
leftView = searchButton
leftViewMode = .always
rightView = clearButton
rightViewMode = .whileEditing
}
- 커스텀할때 용이한 left/rightViewMode 4가지
- always: 항상 해당 view 표출
- never
- unlessEditing: 입력중이 아닌 경우에만 해당 view 표출 (focus를 받은 경우에도 표출 안되는 것 주의)
- whileEditing: 입력중만 해당 view 표출
- Clear button 탭 인터렉션
- 입력값을 모두 지우고 rightView(클리어 버튼)을 안보이도록 설정
// MARK: - Interaction
@objc
func didTapClearButton() {
text?.removeAll()
rightViewMode = .never
}
- leftView와 rightView 패딩 설정
override func leftViewRect(forBounds bounds: CGRect) -> CGRect {
var padding = super.leftViewRect(forBounds: bounds)
padding.origin.x += 12
return padding
}
override func rightViewRect(forBounds bounds: CGRect) -> CGRect {
var padding = super.rightViewRect(forBounds: bounds)
padding.origin.x -= 12
return padding
}
- resign되었을 때의 text, 입력되는 text, placeholder의 text 패딩 설정
// MARK: - 텍스트 패딩 설정
// editing 모드가 아닌 경우의 inset 값
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: UIEdgeInsets(top: 14.0, left: 38.0, bottom: 14.0, right: 0.0))
}
// editing 모드인 경우의 inset 값
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: UIEdgeInsets(top: 14.0, left: 38.0, bottom: 14.0, right: 36.0))
}
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: UIEdgeInsets(top: 16.0, left: 38.0, bottom: 14.0, right: 36.0))
}
- Delegate 설정
- focus mode는 textFieldDidBeginEditing(:)에서 작성
- resign mode는 textFieldDidEndEditing(:)에서 작성
- 텍스트가 입력되는 상황에서는 textField(:shouldChangeCharactersIn:replacementString:)에서 작성
extension SearchTextField: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
/// Focus mode
/// 입력된 값이 없는 경우만 leftView(검색 이미지), rightView(클리어 버튼)를 보이지 않도록 설정
if text?.isEmpty ?? true {
rightViewMode = .never
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
/// Resign mode
// leftViewMode, rightViewMode를 .never로 설정하여 사라지게끔 하는 작업 가능
}
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
/// newText: 새로 입력된 텍스트
let newText = string.trimmingCharacters(in: .whitespacesAndNewlines)
/// text: 기존에 입력되었던 text
/// predictRange: 입력으로 예상되는 text의 range값 추측 > range값을 알면 기존 문자열에 새로운 문자를 위치에 알맞게 추가 가능
guard let text = textField.text, let predictRange = Range(range, in: text) else { return true }
/// predictedText: 기존에 입력되었던 text에 새로 입력된 newText를 붙여서, 현재까지 입력된 전체 텍스트
let predictedText = text.replacingCharacters(in: predictRange, with: newText)
.trimmingCharacters(in: .whitespacesAndNewlines)
if predictedText.isEmpty {
rightViewMode = .never
} else {
rightViewMode = .whileEditing
}
return true
}
}
* source code: https://github.com/JK0369/SearchTextField
'UI 컴포넌트 (swift)' 카테고리의 다른 글
[iOS - swift] UI 컴포넌트 - ViewPager (뷰 페이저) (CollectionView + PageViewController) (5) | 2021.07.21 |
---|---|
[iOS - swift] UI 컴포넌트 - Pinterest(핀터레스트, 인스타그램) UI 만들기 (CollectionView) (0) | 2021.07.20 |
[iOS - swift] UI 컴포넌트 - GradientView (흐려지는 뷰) (0) | 2021.07.17 |
[iOS - swift] UI 컴포넌트 - 수평 스크롤 뷰 (ScrollView + StackView) (0) | 2021.07.14 |
[iOS - swift] UI 컴포넌트 - Rounded shadow Button (라운드 그림자 버튼) (0) | 2021.07.14 |
Comments