이미지가 있는 picker 버튼을 만들어봅니다.
앞서 만든 버튼과 달리 이미지 애셋이 추가된 버전으로
PeriodPickerBtnView라는 컴포넌트 뷰는 따로 빼서 연결해 주었습니다.
+ TodoCalendarViewController에는 많은 요소들과 func들이 있지만
이 곳에서는 셀을 구성하는 collectionView와 PickerBtn과 관련된 코드만 빼서 기록해 둡니다.
1. TodoCalendarViewController
BaseViewController를 상속받은 클래스로 "할 일 캘린더" 기능을 담당하는 뷰 컨트롤러
→ 사용자에게 일정 관리와 할 일 목록을 월간, 주간, 일간 보기로 제공하는 UI를 관리
BaseViewController
: BaseViewController를 상속받아 기본적인 뷰 컨트롤러 설정 및 공통 기능을 사용 → BaseViewController에서 정의된 configureUI(), configureConstraint(), configureUtil(), configureNotificationCenter() 메서드를 오버라이드하여 다른 팀원들과 일관성있는 메소드로 UI 구성과 제약 조건 설정했다.
class TodoCalendarViewController: BaseViewController {
2. PeriodPickerButtonView 인스턴스
private let periodBtnView = PeriodPickerButtonView()
: PeriodPickerButtonView의 인스턴스를 생성 → 기간을 선택하는 버튼들 포함 (월간, 주간, 일간 3개의 버튼)
private let periodBtnView = PeriodPickerButtonView()
3. periodBtnView.delegate
: PeriodPickerButtonView 델리게이트를 TodoCalendarViewController로 설정 → 버튼이 눌리면 TodoCalendarViewController가 알림을 받게 된다.
periodBtnView.delegate = self
4. addSubview
: PeriodPickerButtonView를 현재 뷰에 추가
view.addSubview(periodBtnView)
5. periodBtnView.snp.makeConstraints
: SnapKit을 사용하여 PeriodPickerButtonView의 위치와 크기를 설정
periodBtnView.snp.makeConstraints
6. extension TodoCalendarViewController
PeriodPickerButtonViewDelegate 프로토콜을 구현하여, PeriodPickerButtonView에서 발생하는 이벤트 처리
extension TodoCalendarViewController : PeriodPickerButtonViewDelegate {
// dailyButton이 눌렸을 때 호출되는 메서드
7. func didTapdailyButton()
: PeriodPickerButtonView의 dailyButton이 눌렸을 때 호출되는 메서드 → DailyViewController 인스턴스를 생성하고 네비게이션 스택에 푸시하여 일간 뷰로 이동
func didTapdailyButton() {
let weeklyVC = DailyViewController() // 일간 뷰컨트롤러 인스턴스 생성
self.navigationController?.pushViewController(weeklyVC, animated: true) // 주간 뷰컨트롤러로 이동
}
}
1-7 코드 ▽
import UIKit
import SnapKit
class TodoCalendarViewController: BaseViewController {
// PeriodPickerButtonView 인스턴스를 생성
private let periodBtnView = PeriodPickerButtonView() // 기간피커
private var collectionView: UICollectionView!
override func viewDidLoad() {
configureCollectionView()
super.viewDidLoad()
periodBtnView.delegate = self // 기간피커의 델리게이트 설정
}
override func configureUI() {
super.configureUI()
collectionView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide.snp.top)
make.leading.equalToSuperview().offset(16)
make.trailing.equalToSuperview().offset(-16)
make.bottom.equalToSuperview()
}
// 기간피커를 뷰에 추가
view.addSubview(periodBtnView)
// 기간피커의 제약 조건을 설정
periodBtnView.snp.makeConstraints { make in
make.width.equalTo(131)
make.left.equalToSuperview().offset(16) // x 좌표 설정
make.top.equalToSuperview().offset(104) // y 좌표 설정
}
}
}
// PeriodPickerButtonViewDelegate 프로토콜을 구현하여 기간피커 버튼이 눌렸을 때의 동작 정의
extension TodoCalendarViewController : PeriodPickerButtonViewDelegate {
// dailyButton이 눌렸을 때 호출되는 메서드
func didTapdailyButton() {
let weeklyVC = DailyViewController() // 일간 뷰컨트롤러 인스턴스 생성
self.navigationController?.pushViewController(weeklyVC, animated: true) // 주간 뷰컨트롤러로 이동
}
}
이제 PeriodPickerBtnView의 UI를 아래 코드를 통해 그려보자.
1. PeriodPickerButtonViewDelegate 프로토콜
didTapdailyButton: PeriodPickerButtonView에서 dailyButton이 눌렸을 때 호출되는 메서드를 정의
// 델리게이트 프로토콜 선언: PeriodPickerButtonView에서 버튼이 탭될 때 호출될 메서드를 정의
protocol PeriodPickerButtonViewDelegate: AnyObject {
func didTapdailyButton()
}
2. PeriodPickerButtonView 클래스
- delegate: 이 뷰의 델리게이트로, 버튼 탭 이벤트를 전달
- button1, button2, button3: "월간", "주간", "일간" 버튼을 정의
- topSeparator, bottomSeparator: 버튼 사이의 구분선을 정의
- buttons: 세 버튼을 배열로 관리
class PeriodPickerButtonView: UIView {
// 델리게이트 변수
// 델리게이트를 통해 버튼 탭 이벤트를 전달
weak var delegate: PeriodPickerButtonViewDelegate?
// 첫 번째 버튼 정의
// "월간" 버튼을 설정
private let button1: UIButton = {
let button = UIButton()
var configuration = UIButton.Configuration.plain()
configuration.title = "월간"
configuration.image = UIImage(systemName: "calendar")
configuration.imagePadding = 44 // 이미지와 텍스트 간의 간격을 넓힘
configuration.imagePlacement = .trailing
configuration.baseBackgroundColor = .clear // 배경 색상을 투명으로 설정
button.configuration = configuration
button.setTitleColor(.challendarBlack60, for: .normal)
button.setTitleColor(.white, for: .selected)
button.tintColor = .challendarBlack60
button.contentHorizontalAlignment = .center
return button
}()
// 두 번째 버튼 정의
// "주간" 버튼을 설정
private let button2: UIButton = {
let button = UIButton()
var configuration = UIButton.Configuration.plain()
configuration.title = "주간"
configuration.image = UIImage(systemName: "calendar")
configuration.imagePadding = 44
configuration.imagePlacement = .trailing
configuration.baseBackgroundColor = .clear // 배경 색상을 투명으로 설정
button.configuration = configuration
button.setTitleColor(.challendarBlack60, for: .normal)
button.setTitleColor(.white, for: .selected)
button.tintColor = .challendarBlack60
button.contentHorizontalAlignment = .center
return button
}()
// 세 번째 버튼 정의
// "일간" 버튼을 설정
private let button3: UIButton = {
let button = UIButton()
var configuration = UIButton.Configuration.plain()
configuration.title = "일간"
configuration.image = UIImage(systemName: "calendar")
configuration.imagePadding = 44
configuration.imagePlacement = .trailing
configuration.baseBackgroundColor = .clear // 배경 색상을 투명으로 설정
button.configuration = configuration
button.setTitleColor(.challendarBlack60, for: .normal)
button.setTitleColor(.white, for: .selected)
button.tintColor = .challendarBlack60
button.contentHorizontalAlignment = .center
return button
}()
// 상단 구분선
private let topSeparator: UIView = {
let view = UIView()
view.backgroundColor = .challendarBlack60
return view
}()
// 하단 구분선
private let bottomSeparator: UIView = {
let view = UIView()
view.backgroundColor = .challendarBlack60
return view
}()
// 버튼 배열
// 버튼들을 배열로 관리
private var buttons: [UIButton] {
return [button1, button2, button3]
}
3. init(frame:) 초기화 메서드
뷰를 초기화하고 configureUI(), configureConstraint(), configureUtil()을 호출
// 초기화 메서드
// 뷰의 초기 설정
override init(frame: CGRect) {
super.init(frame: frame)
configureUI()
configureConstraint()
configureUtil()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
4. configureUI()
뷰에 버튼과 구분선을 추가하고, 뷰의 기본 속성을 설정
// UI 설정 메서드
// 뷰에 UI 요소들을 추가하고 기본 속성을 설정
private func configureUI() {
addSubview(button1)
addSubview(button2)
addSubview(button3)
addSubview(topSeparator)
addSubview(bottomSeparator)
layer.cornerRadius = 12
clipsToBounds = true
backgroundColor = .black // 챌린지100으로 추후 수정
}
5. configureConstraint()
SnapKit을 사용하여 UI 요소의 제약 조건을 설정
// 제약 조건 설정 메서드
// SnapKit을 사용하여 UI 요소들의 제약 조건을 설정
private func configureConstraint() {
button1.snp.makeConstraints { make in
make.top.leading.trailing.equalToSuperview()
make.height.equalTo(44)
}
button2.snp.makeConstraints { make in
make.top.equalTo(button1.snp.bottom)
make.leading.trailing.equalToSuperview()
make.height.equalTo(44)
}
button3.snp.makeConstraints { make in
make.top.equalTo(button2.snp.bottom)
make.leading.trailing.bottom.equalToSuperview()
make.height.equalTo(44)
}
topSeparator.snp.makeConstraints { make in
make.top.equalTo(button2.snp.top)
make.leading.trailing.equalToSuperview()
make.height.equalTo(0.2) // 얇은 실선의 높이
}
bottomSeparator.snp.makeConstraints { make in
make.top.equalTo(button2.snp.bottom)
make.leading.trailing.equalToSuperview()
make.height.equalTo(0.2) // 얇은 실선의 높이
}
}
6. configureUtil()
setupActions()를 호출하여 버튼 액션을 설정
// 유틸리티 설정 메서드
// 액션 설정 등의 추가 설정을 담당
private func configureUtil() {
setupActions()
}
7. setupActions()
각 버튼에 대한 액션을 설정
// 버튼 액션 설정 메서드
// 버튼들에 대한 액션을 설정
private func setupActions() {
button1.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
button2.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
button3.addTarget(self, action: #selector(dailyButtonTapped), for: .touchUpInside)
}
8. dailyButtonTapped()
델리게이트를 통해 dailyButton 탭 이벤트를 전달
// dailyButton 탭 메서드
// 델리게이트를 통해 dailyButton 탭 이벤트를 전달
@objc private func dailyButtonTapped() {
delegate?.didTapdailyButton()
}
9. buttonTapped(_:)
탭된 버튼을 선택 상태로 만들고 다른 버튼들은 선택 해제 + 선택된 버튼의 색상을 변경
// 버튼 탭 메서드
// 버튼이 탭되었을 때의 동작을 정의
@objc private func buttonTapped(_ sender: UIButton) {
buttons.forEach {
$0.isSelected = false
$0.tintColor = .challendarBlack60 // 기본 색상으로 되돌림
}
sender.isSelected = true
sender.tintColor = .white // 선택된 버튼 색상 변경
}
}
1-9 코드 ▽
//
// PeriodPickerButtonView.swift
// Challendar
//
// Created by 채나연 on 6/5/24.
//
import UIKit
import SnapKit
// 델리게이트 프로토콜 선언: PeriodPickerButtonView에서 버튼이 탭될 때 호출될 메서드를 정의
protocol PeriodPickerButtonViewDelegate: AnyObject {
func didTapdailyButton()
}
class PeriodPickerButtonView: UIView {
// 델리게이트 변수
// 델리게이트를 통해 버튼 탭 이벤트를 전달
weak var delegate: PeriodPickerButtonViewDelegate?
// 첫 번째 버튼 정의
// "월간" 버튼을 설정
private let button1: UIButton = {
let button = UIButton()
var configuration = UIButton.Configuration.plain()
configuration.title = "월간"
configuration.image = UIImage(systemName: "calendar")
configuration.imagePadding = 44 // 이미지와 텍스트 간의 간격을 넓힘
configuration.imagePlacement = .trailing
configuration.baseBackgroundColor = .clear // 배경 색상을 투명으로 설정
button.configuration = configuration
button.setTitleColor(.challendarBlack60, for: .normal)
button.setTitleColor(.white, for: .selected)
button.tintColor = .challendarBlack60
button.contentHorizontalAlignment = .center
return button
}()
// 두 번째 버튼 정의
// "주간" 버튼을 설정
private let button2: UIButton = {
let button = UIButton()
var configuration = UIButton.Configuration.plain()
configuration.title = "주간"
configuration.image = UIImage(systemName: "calendar")
configuration.imagePadding = 44
configuration.imagePlacement = .trailing
configuration.baseBackgroundColor = .clear // 배경 색상을 투명으로 설정
button.configuration = configuration
button.setTitleColor(.challendarBlack60, for: .normal)
button.setTitleColor(.white, for: .selected)
button.tintColor = .challendarBlack60
button.contentHorizontalAlignment = .center
return button
}()
// 세 번째 버튼 정의
// "일간" 버튼을 설정
private let button3: UIButton = {
let button = UIButton()
var configuration = UIButton.Configuration.plain()
configuration.title = "일간"
configuration.image = UIImage(systemName: "calendar")
configuration.imagePadding = 44
configuration.imagePlacement = .trailing
configuration.baseBackgroundColor = .clear // 배경 색상을 투명으로 설정
button.configuration = configuration
button.setTitleColor(.challendarBlack60, for: .normal)
button.setTitleColor(.white, for: .selected)
button.tintColor = .challendarBlack60
button.contentHorizontalAlignment = .center
return button
}()
// 상단 구분선
private let topSeparator: UIView = {
let view = UIView()
view.backgroundColor = .challendarBlack60
return view
}()
// 하단 구분선
private let bottomSeparator: UIView = {
let view = UIView()
view.backgroundColor = .challendarBlack60
return view
}()
// 버튼 배열
// 버튼들을 배열로 관리
private var buttons: [UIButton] {
return [button1, button2, button3]
}
// 초기화 메서드
// 뷰의 초기 설정
override init(frame: CGRect) {
super.init(frame: frame)
configureUI()
configureConstraint()
configureUtil()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// UI 설정 메서드
// 뷰에 UI 요소들을 추가하고 기본 속성을 설정
private func configureUI() {
addSubview(button1)
addSubview(button2)
addSubview(button3)
addSubview(topSeparator)
addSubview(bottomSeparator)
layer.cornerRadius = 12
clipsToBounds = true
backgroundColor = .black // 챌린지100으로 추후 수정
}
// 제약 조건 설정 메서드
// SnapKit을 사용하여 UI 요소들의 제약 조건을 설정
private func configureConstraint() {
button1.snp.makeConstraints { make in
make.top.leading.trailing.equalToSuperview()
make.height.equalTo(44)
}
button2.snp.makeConstraints { make in
make.top.equalTo(button1.snp.bottom)
make.leading.trailing.equalToSuperview()
make.height.equalTo(44)
}
button3.snp.makeConstraints { make in
make.top.equalTo(button2.snp.bottom)
make.leading.trailing.bottom.equalToSuperview()
make.height.equalTo(44)
}
topSeparator.snp.makeConstraints { make in
make.top.equalTo(button2.snp.top)
make.leading.trailing.equalToSuperview()
make.height.equalTo(0.2) // 얇은 실선의 높이
}
bottomSeparator.snp.makeConstraints { make in
make.top.equalTo(button2.snp.bottom)
make.leading.trailing.equalToSuperview()
make.height.equalTo(0.2) // 얇은 실선의 높이
}
}
// 유틸리티 설정 메서드
// 액션 설정 등의 추가 설정을 담당
private func configureUtil() {
setupActions()
}
// 버튼 액션 설정 메서드
// 버튼들에 대한 액션을 설정
private func setupActions() {
button1.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
button2.addTarget(self, action: #selector(buttonTapped(_:)), for: .touchUpInside)
button3.addTarget(self, action: #selector(dailyButtonTapped), for: .touchUpInside)
}
// dailyButton 탭 메서드
// 델리게이트를 통해 dailyButton 탭 이벤트를 전달
@objc private func dailyButtonTapped() {
delegate?.didTapdailyButton()
}
// 버튼 탭 메서드
// 버튼이 탭되었을 때의 동작을 정의
@objc private func buttonTapped(_ sender: UIButton) {
buttons.forEach {
$0.isSelected = false
$0.tintColor = .challendarBlack60 // 기본 색상으로 되돌림
}
sender.isSelected = true
sender.tintColor = .white // 선택된 버튼 색상 변경
}
}
'iOS 앱 개발자 프로젝트' 카테고리의 다른 글
[iOS] picker 버튼을 dropdown UIView와 연결하기 (0) | 2024.06.08 |
---|---|
[iOS] picker 버튼을 UIView로 만들기 (UIButton → UIView) (0) | 2024.06.08 |
[iOS] 옵션 선택을 위한 picker 버튼 만들기 (0) | 2024.06.05 |
[iOS] YZCenterFlowLayout 이해하기 (0) | 2024.06.04 |
[iOS] UICollectionView에서 제네릭으로 코드 수정하기 (0) | 2024.06.03 |