#ios #swift
Вопрос:
Я реализовал два календаря в одном контроллере просмотра:
представление месяца — реализовано с помощью раскадровки, представление недели — реализовано с помощью storybard, но в коде. и у меня возникли проблемы с версией просмотра за неделю. его ячейка(ячейка collectionviewcell с именем «EventListCell») выполнена программным способом, и я зарегистрировал ячейку в представлении коллекции (с именем «eventListMonthView») .
Мне пришлось указать другую конфигурацию и делегировать источник данных для двух календарей, поэтому я использовал IF для разных настроек.
У меня были ограничения на метки в ячейке создания проблемы(EventListCell), у нее две метки, и у нее есть ведущие и верхние ограничения.
но эти ограничения обновляются только для видимых ячеек сначала, когда загружается представление, а не для других ячеек, когда я прокручиваю, чтобы увидеть другие ячейки.
Я считаю, что функция willDisplay имеет к этому отношение, но я не вижу ничего плохого в том, что я здесь сделал… нужна помощь здесь 🙁
введите описание изображения здесь
мой код:
//
// SelectDateCandidateViewController.swift
// teampang
//
// Created by 선민승 on 2021/10/13.
//
import UIKit
import JTAppleCalendar
import RxSwift
import RxCocoa
class SelectDateCandidateViewController: UIViewController, UIGestureRecognizerDelegate {
let selectDateViewModel = SelectDateViewModel()
var disposeBag = DisposeBag()
private let formatter = DateFormatter()
private var firstDate: Date?
private var planBoundary: [String] = []
private let days = ["월", "화", "수", "목", "금", "토", "일"]
private let calendar = Calendar.current
var currentlySelctedDate = Date()
@IBOutlet var timeSettingUIView: UIView!
@IBOutlet var timeSettingLabel: UILabel!
@IBOutlet var timeSettingUIViewHeight: NSLayoutConstraint! //처음엔 0 -> 48
@IBOutlet var eventListMonthView: JTACMonthView!
@IBOutlet var eventListContainerView: UIView!
@IBOutlet var JTACMonthView: JTACMonthView!
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = false
self.navigationController?.navigationBar.barTintColor = .white
self.navigationController?.navigationBar.clipsToBounds = true
}
override func viewDidLoad() {
super.viewDidLoad()
bindUI()
setNavigation()
setIBOutlets()
setCalendar()
}
@objc private func popToPrevious() {
// our custom stuff
self.navigationController?.popViewController(animated: true)
}
private func bindUI() {
selectDateViewModel.input.selectedDates
.subscribe(onNext:{ list in
UIView.animate(withDuration: 0.5,
delay: 0.0,
usingSpringWithDamping: 0.5,
initialSpringVelocity: 0.5,
options: [],
animations: {
self.timeSettingUIViewHeight.constant = list.count == 0 ? 0 : 48
self.timeSettingLabel.isHidden = list.count == 0 ? true : false
self.timeSettingLabel.alpha = list.count == 0 ? 0 : 1
guard let date = self.JTACMonthView.visibleDates().monthDates.first?.date else { return }
self.timeSettingUIView.layoutIfNeeded()
self.view.layoutIfNeeded()
self.JTACMonthView.reloadData(withAnchor: date, completionHandler: nil)
})
})
.disposed(by: disposeBag)
}
private func setCalendar() {
JTACMonthView.scrollToDate(Date(), animateScroll: false) {
self.JTACMonthView.selectDates([Date()])
}
JTACMonthView.cellSize = 45/812*view.bounds.height
JTACMonthView.allowsMultipleSelection = true
JTACMonthView.scrollingMode = .none
eventListMonthView.scrollToDate(Date(), animateScroll: false) {
self.eventListMonthView.selectDates([Date()])
}
eventListMonthView.minimumInteritemSpacing = 0
eventListMonthView.minimumLineSpacing = 0
eventListMonthView.cellSize = 171/812*view.bounds.height
eventListMonthView.scrollDirection = .horizontal
eventListMonthView.scrollingMode = .none
}
private func setIBOutlets() {
timeSettingUIView.backgroundColor = .grey200
timeSettingLabel.setLabel(text: "시간 설정", font: .spoqaMedium(size: 15), color: .darkGrey, kernValue: -0.6)
eventListContainerView.backgroundColor = .grey100
eventListContainerView.layer.masksToBounds = false
eventListContainerView.layer.shadowOffset = CGSize(width: 0,height: -4)
eventListContainerView.layer.shadowRadius = 10
eventListContainerView.layer.shadowOpacity = 1
eventListContainerView.layer.shadowColor = UIColor(red: 0.024, green: 0.247, blue: 0.345, alpha: 0.1).cgColor
// let flowLayout = UICollectionViewFlowLayout()
// flowLayout.collectionView?.frame = CGRect(x: 0, y: 0, width: 171, height: 171)
// flowLayout.scrollDirection = .horizontal
eventListMonthView.register(EventListCell.self, forCellWithReuseIdentifier: EventListCell.identifier)
eventListMonthView.backgroundColor = .clear
// eventListMonthView.collectionViewLayout = flowLayout
// eventListMonthView.showsHorizontalScrollIndicator = false
}
private func setNavigation() {
self.navigationController?.interactivePopGestureRecognizer?.delegate = self
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
self.navigationController?.navigationBar.backgroundColor = .clear
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.font: UIFont.spoqaMedium(size: 16),NSAttributedString.Key.foregroundColor: UIColor.darkGrey, NSAttributedString.Key.kern: -0.64]
title = "날짜 범위 선택"
navigationItem.leftBarButtonItem = UIBarButtonItem (
image: UIImage(named: "Glyph")?.withRenderingMode(.alwaysOriginal),
style: .plain,
target: self,
action: #selector(popToPrevious)
)
navigationItem.rightBarButtonItem = UIBarButtonItem (
image: UIImage(named: "check_checked")?.withRenderingMode(.alwaysOriginal),
style: .plain,
target: self,
action: #selector(popToPrevious)
)
}
}
// MARK: Calendar ConfigureCell
extension SelectDateCandidateViewController {
private func configureCell(_ calendar: JTACMonthView, view: JTACDayCell?, cellState: CellState) {
if calendar == JTACMonthView {
guard let cell = view as? DateCell else { return }
cell.selectedView.backgroundColor = .primaryColor
// 달력 기본 날짜 설정
cell.dateLabel.text = cellState.text
cell.dateLabel.font = .poppinsRegular(size: 12)
cell.dateLabel.textColor = .grey600
// 선택되면 selectedView 칠해지게 처리
handleCellSelected(cell: cell, cellState: cellState)
handleCellVisibility(cell: cell, cellState: cellState)
// planStartDate~endDate 범위 속 날짜만 black 컬러 처리
handleCellTextColor(calendar, cell: cell, cellState: cellState)
} else {
print(cellState)
guard let cell = view as? EventListCell else { return }
let formatter = DateFormatter() // Declare this outside, to avoid instancing this heavy class multiple times.
formatter.dateFormat = "M월 d일"
let time = formatter.string(from: cellState.date)
cell.monthDateLabel.text = time
var day = "월요일"
switch cellState.day {
case .sunday:
day = "일요일"
case .monday:
day = "월요일"
case .tuesday:
day = "화요일"
case .wednesday:
day = "수요일"
case .thursday:
day = "목요일"
case .friday:
day = "금요일"
case .saturday:
day = "토요일"
}
cell.dayLabel.text = day
handleEventListCellVisibility(cell: cell, cellState: cellState)
}
}
func handleCellTextColor(_ calendar: JTACMonthView, cell: DateCell, cellState: CellState) {
if cellState.isSelected {
cell.dateLabel.textColor = .white
}
}
// 선택되면 selectedView 칠해지게 처리
private func handleCellSelected(cell: DateCell, cellState: CellState) {
cell.selectedView.isHidden = !cellState.isSelected
cell.selectedView.setRounded(radius: nil)
cell.selectedView.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMinXMinYCorner]
}
private func handleCellVisibility(cell: DateCell, cellState: CellState){
cell.isHidden = cellState.dateBelongsTo == .thisMonth ? false : true
}
private func handleEventListCellVisibility(cell: EventListCell, cellState: CellState){
cell.isHidden = cellState.dateBelongsTo == .thisMonth ? false : true
}
}
extension SelectDateCandidateViewController: JTACMonthViewDataSource {
func configureCalendar(_ calendar: JTACMonthView) -> ConfigurationParameters {
formatter.dateFormat = "yyyy MM dd"
formatter.timeZone = Calendar.current.timeZone
formatter.locale = Calendar.current.locale
let startDate = formatter.date(from: "1999 01 01")!
let endDate = formatter.date(from: "3000 02 01")!
if calendar == JTACMonthView {
return ConfigurationParameters(startDate: startDate, endDate: endDate)
} else {
print("calendar datasource: (calendar)")
return ConfigurationParameters(startDate: startDate,
endDate: endDate,
numberOfRows: 1,
generateInDates: .forFirstMonthOnly,
generateOutDates: .off,
hasStrictBoundaries: false)
}
}
}
extension SelectDateCandidateViewController: JTACMonthViewDelegate {
func calendar(_ calendar: JTACMonthView, cellForItemAt date: Date, cellState: CellState, indexPath: IndexPath) -> JTACDayCell {
if calendar == JTACMonthView {
let cell = calendar.dequeueReusableJTAppleCell(withReuseIdentifier: "dateCell", for: indexPath) as! DateCell
self.calendar(calendar, willDisplay: cell, forItemAt: date, cellState: cellState, indexPath: indexPath)
return cell
} else {
let cell = calendar.dequeueReusableCell(withReuseIdentifier: "dateCell", for: indexPath) as! EventListCell
self.calendar(calendar, willDisplay: cell, forItemAt: date, cellState: cellState, indexPath: indexPath)
return cell
}
}
func calendar(_ calendar: JTACMonthView, willDisplay cell: JTACDayCell, forItemAt date: Date, cellState: CellState, indexPath: IndexPath) {
if calendar == JTACMonthView {
configureCell(calendar, view: cell, cellState: cellState)
} else {
print(cell)
configureCell(calendar, view: cell, cellState: cellState)
}
}
func calendar(_ calendar: JTACMonthView, didSelectDate date: Date, cell: JTACDayCell?, cellState: CellState, indexPath: IndexPath) {
if calendar == JTACMonthView {
guard let validCell = cell as? DateCell else { return }
validCell.selectedView.isHidden = false
//서버 전달용 포맷
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ko_KR")
formatter.dateFormat = "YYYY-MM-dd"
//라벨 용 포맷
let labelFormatter = DateFormatter()
labelFormatter.locale = Locale(identifier: "ko_KR")
labelFormatter.dateFormat = "YYYY년 M월 d일"
//서버에 전달 될 배열 초기화
// planBoundary.removeAll()
//한번만 눌렀을 때에도 배열에 selectedDate 데이터 들어감
planBoundary.insert(formatter.string(from: calendar.selectedDates.first!), at: 0)
selectDateViewModel.input.selectedDates.onNext(calendar.selectedDates)
//한번만 누른 값 아래 라벨 내용에 업데이트
let selected = calendar.selectedDates.first!
// eventCreateViewModel.input.startDate.accept(selected)
firstDate = date
currentlySelctedDate = date
configureCell(calendar,view: cell, cellState: cellState)
}
}
func calendar(_ calendar: JTACMonthView, didDeselectDate date: Date, cell: JTACDayCell?, cellState: CellState, indexPath: IndexPath) {
if calendar == JTACMonthView {
firstDate = nil
currentlySelctedDate = date
selectDateViewModel.input.selectedDates.onNext(calendar.selectedDates)
configureCell(calendar,view: cell, cellState: cellState)
}
}
func calendar(_ calendar: JTACMonthView, shouldSelectDate date: Date, cell: JTACDayCell?, cellState: CellState, indexPath: IndexPath) -> Bool {
if calendar == JTACMonthView {
return true
} else {
return false
}
}
func calendar(_ calendar: JTACMonthView, shouldDeselectDate date: Date, cell: JTACDayCell?, cellState: CellState, indexPath: IndexPath) -> Bool {
if calendar == JTACMonthView {
return true
} else {
return false
}
}
func calendar(_ calendar: JTACMonthView, headerViewForDateRange range: (start: Date, end: Date), at indexPath: IndexPath) -> JTACMonthReusableView {
if calendar == JTACMonthView {
let formatter = DateFormatter() // Declare this outside, to avoid instancing this heavy class multiple times.
formatter.dateFormat = "yyyy년 M월"
let header = calendar.dequeueReusableJTAppleSupplementaryView(withReuseIdentifier: "DateHeader", for: indexPath) as! DateHeader
header.monthTitle.text = formatter.string(from: range.start)
header.monthTitle.font = .poppinsRegular(size: 14)
header.monthTitle.textColor = .grey600
let days = [header.monday, header.saturday, header.friday, header.thursday, header.wednesday, header.tuesday, header.sunday]
days.forEach {
$0?.font = .poppinsLight(size: 12)
$0?.textColor = .grey400
$0?.textAlignment = .center
}
header.sunday.textColor = UIColor(red: 0.992, green: 0.196, blue: 0.349, alpha: 0.6)
header.saturday.textColor = UIColor(red: 0.992, green: 0.196, blue: 0.349, alpha: 0.6)
return header
} else {
return UICollectionReusableView() as! JTACMonthReusableView
}
}
func calendarSizeForMonths(_ calendar: JTACMonthView?) -> MonthSize? {
if calendar == JTACMonthView {
return MonthSize(defaultSize: 60)
} else {
return nil
}
}
}
extension SelectDateCandidateViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = 171
let height = 164
return CGSize(width: width, height: height)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 20)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
}
для получения дополнительной информации :
https://github.com/patchthecode/JTAppleCalendar/issues/1341
Ответ №1:
Удалить Calendar.cellsize
из кода.
Calendar.minimumInteritemSpacing = 0
, Calendar.minimumLineSpacing = 0
, автоматически отрегулирует размер ячейки календаря при прокрутке между месяцами.