JTAppleCalendar — возникли проблемы с обновлением автозапуска ячеек только для видимых ячеек

#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 , автоматически отрегулирует размер ячейки календаря при прокрутке между месяцами.