sladuf
200
sladuf
전체 방문자
오늘
어제
  • 분류 전체보기 (83)
    • 📚 Programming (32)
      • Swift (13)
      • JAVA (2)
      • Python (6)
      • SQL (6)
      • Web (5)
    • 📱 iOS (25)
      • Base (7)
      • SwiftUI (9)
      • UIKit (7)
      • 인강 & 책 (2)
    • 🔗 Algorithm (20)
      • Python (12)
      • Swift (3)
      • Tip (5)
    • 🗂 ETC (6)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 스위프트
  • Swift

최근 댓글

최근 글

티스토리

글쓰기 설정
hELLO · Designed By 정상우.
sladuf

200

[UIKit] CustomDatePicker 만들기 (1/3)
📱 iOS/UIKit

[UIKit] CustomDatePicker 만들기 (1/3)

2023. 1. 8. 18:23

 

 

 

UIKit 공부겸 프로젝트를 진행하는 도중에 DatePicker를 커스텀해야 하는 상황과 마주했다.

 

이런 모양의 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
    '📱 iOS/UIKit' 카테고리의 다른 글
    • [UIKit] CustomDatePicker 만들기 (3/3)
    • [UIKit] toast message 만들기
    • [UIKit] CustomDatePicker 만들기 (2/3)
    • Passing Data (직접전달방식)
    sladuf
    sladuf

    티스토리툴바