UIKit 공부겸 프로젝트를 진행하는 도중에 DatePicker를 커스텀해야 하는 상황과 마주했다.
📌 구현내용
일단 기본 제공되는 DatePicker를 사용할 수는 없었다
그래서 UIButton의 inputView에 pickerView를 넣어서 만들것이다
일반적으로 TextField의 inputView에 pickerView를 사용하던데 나는 TextField가 필요없어서 대장정을 걸어보기로 했다..
📌 UI setting
위에 언급했던 CustomDatePicker에서 box 하나씩을 CustomPickerButton이라고 하겠다.
// CustomPickerButton.swift
public class CustomPickerButton: UIButton {
private var uiView = UIView()
private var label = UILabel()
private var btn = UIImageView()
// UI 속성 설정
private func initAttribute(){
uiView = {
let uiView = UIView()
uiView.layer.borderWidth = 1
uiView.layer.cornerRadius = 10
uiView.layer.borderColor = UIColor.lightGray.cgColor
return uiView
}()
label = {
let label = UILabel()
label.font = .systemFont(ofSize: 14)
label.textColor = .lightGray
return label
}()
btn.image = UIImage(named: "btn-bottom")
btn.sizeToFit()
}
// Autolayout 관련 설정
private func initAutolayout(width: CGFloat){
[uiView, label, btn].forEach {
self.addSubview($0)
}
uiView.snp.makeConstraints {
$0.edges.equalToSuperview()
$0.width.equalTo(width)
$0.height.equalTo(50)
}
label.snp.makeConstraints {
$0.left.equalTo(uiView).offset(34)
$0.centerY.equalToSuperview()
}
btn.snp.makeConstraints {
$0.right.equalTo(uiView).offset(-11)
$0.centerY.equalToSuperview()
}
uiView.isUserInteractionEnabled = false
label.isUserInteractionEnabled = false
btn.isUserInteractionEnabled = false
}
}
이 코드는 해당 view들의 이벤트를 무효화 시킨다.
UIButton의 inputView로 pickerView를 넣어주기 때문에 custom한 UI들은 이벤트 없이 보여지기만 해야하기 때문 !!!
이 설정을 해주지 않으면 UIButton 앞에 uiView, label, btn이 놓여지게 되어 UIButton의 이벤트를 방해한다.
Hierarchy를 보면 이해가 더 잘될 것임...
uiView.isUserInteractionEnabled = false
label.isUserInteractionEnabled = false
btn.isUserInteractionEnabled = false
📌 pickerView setting
앞서 언급한 바와 같이 pickerView는 inputView에 들어갈 예정이다.
class CustomPickerButton : UIButton {
private let pickerView = UIPickerView()
// inputView안에 pickerView 띄움
public override var inputView: UIView? {
return pickerView
}
// inputView의 악세사리 (inputView 상단 취소, 완료)
public override var inputAccessoryView: UIView? {
let toolbar = UIToolbar()
toolbar.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: 40)
let closeButton = UIBarButtonItem(
title: "취소",
style: .plain,
target: self,
action: #selector(didTapClose(_:))
)
let space = UIBarButtonItem(
barButtonSystemItem: .flexibleSpace,
target: nil,
action: nil
)
let doneButton = UIBarButtonItem(
title: "완료",
style: .done,
target: self,
action: #selector(didTapDone(_:))
)
let items = [closeButton, space, doneButton]
toolbar.setItems(items, animated: false)
toolbar.sizeToFit()
return toolbar
}
private func configureView() {
pickerView.delegate = self
pickerView.dataSource = self
self.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
}
}
@objc
extension CustomPickerButton {
/// Close the picker view
private func didTapClose(_ button: UIBarButtonItem) {
resignFirstResponder()
}
private func didTapDone(_ button: UIBarButtonItem) {
resignFirstResponder()
}
//MARK: - Open the picker view
private func didTapButton() {
becomeFirstResponder()
}
}
extension CustomPickerButton : UIPickerViewDataSource, UIPickerViewDelegate {
public func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return data.count
}
public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return data[row]
}
//data 선택시 동작할 event
public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
print("didSelect", data[row])
label.text = data[row]
}
}
코드를 요약하면 대충 이런식으로 구현하겠다는 의미
내가 만드는 customPickerButton은 pickerView에서 선택된 데이터가 UI상의 label에 뜨기 때문에 label.text를 선택된 데이터로 변경해주는 방식으로 구현했다.
public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
print("didSelect", data[row])
label.text = data[row]
}
📌 full code
나는 init할 때 데이터를 삽입해주도록 구현했다.
init에 initAttribute, initAutolayout 그리고 delegate설정과 addTarget을 지정 해주는 configureView까지 호출해주면 된다.
(색상코드 역시 custom한거라 그 부분은 무시해주시면 됩니다 -)
import UIKit
import SnapKit
public class CustomPickerButton: UIButton {
// MARK: - UI setting
private var uiView = UIView()
var label = UILabel()
private var btn = UIImageView()
private func initAttribute(placeholder : String){
uiView = {
let uiView = UIView()
uiView.layer.borderWidth = 1
uiView.layer.cornerRadius = 10
uiView.layer.borderColor = UIColor.lightGray.cgColor
return uiView
}()
label = {
let label = UILabel()
label.font = .systemFont(ofSize: 14)
label.textColor = .lightGray
label.text = placeholder
return label
}()
btn.image = UIImage(named: "btn-bottom")
btn.sizeToFit()
}
private func initAutolayout(width: CGFloat){
[uiView, label, btn].forEach {
self.addSubview($0)
}
uiView.snp.makeConstraints {
$0.edges.equalToSuperview()
$0.width.equalTo(width)
$0.height.equalTo(50)
}
label.snp.makeConstraints {
$0.left.equalTo(uiView).offset(34)
$0.centerY.equalToSuperview()
}
btn.snp.makeConstraints {
$0.right.equalTo(uiView).offset(-11)
$0.centerY.equalToSuperview()
}
uiView.isUserInteractionEnabled = false
label.isUserInteractionEnabled = false
btn.isUserInteractionEnabled = false
}
private let data : [String]
public init(placeholder : String, width: CGFloat, data: [String]){
self.data = data
super.init(frame: .zero)
initAttribute(placeholder: placeholder)
initAutolayout(width: width)
configureView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - PickerView & inputView setting
private let pickerView = UIPickerView()
// inputView안에 pickerView 띄움
public override var inputView: UIView? {
return pickerView
}
// inputView의 악세사리 (inputView 상단 취소, 완료)
public override var inputAccessoryView: UIView? {
let toolbar = UIToolbar()
toolbar.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: 40)
let closeButton = UIBarButtonItem(
title: "취소",
style: .plain,
target: self,
action: #selector(didTapClose(_:))
)
closeButton.tintColor = UIColor(named: "butter")
let space = UIBarButtonItem(
barButtonSystemItem: .flexibleSpace,
target: nil,
action: nil
)
let doneButton = UIBarButtonItem(
title: "완료",
style: .done,
target: self,
action: #selector(didTapDone(_:))
)
doneButton.tintColor = UIColor(named: "butter")
let items = [closeButton, space, doneButton]
toolbar.setItems(items, animated: false)
toolbar.sizeToFit()
return toolbar
}
//이벤트 받는 첫번째 responder로 지정
public override var canBecomeFirstResponder: Bool {
return true
}
private func configureView() {
pickerView.delegate = self
pickerView.dataSource = self
self.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
}
}
// MARK: - Obj-C Methods
@objc
extension CustomPickerButton {
/// Close the picker view
private func didTapClose(_ button: UIBarButtonItem) {
resignFirstResponder()
}
private func didTapDone(_ button: UIBarButtonItem) {
resignFirstResponder()
}
//MARK: - Open the picker view
private func didTapButton() {
print("CustomPickerButton: button Tap")
becomeFirstResponder()
}
}
extension CustomPickerButton : UIPickerViewDataSource, UIPickerViewDelegate {
public func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return data.count
}
public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return data[row]
}
//data 선택시 동작할 event
public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
print("didSelect", data[row])
label.text = data[row]
}
}
여기까지 만들면 반은 성공...!
다음 글은 CustomDatePicker를 만들어 보도록 하겠습니다 !!
'📱 iOS > UIKit' 카테고리의 다른 글
[UIKit] TableView Multiple Cell, multi type json data parsing (0) | 2023.06.27 |
---|---|
[UIKit] CustomDatePicker 만들기 (3/3) (0) | 2023.04.03 |
[UIKit] toast message 만들기 (0) | 2023.02.16 |
[UIKit] CustomDatePicker 만들기 (2/3) (0) | 2023.01.08 |
Passing Data (직접전달방식) (0) | 2022.06.27 |