WenchaoD / FSCalendar

A fully customizable iOS calendar library, compatible with Objective-C and Swift
MIT License
10.6k stars 1.94k forks source link

How to customise cell in FSCalendar? #1281

Open shin1412-zz opened 3 years ago

shin1412-zz commented 3 years ago

I am trying to achieve Date Range selection using FSCalendar, and i am achieving that but the view i want that is not proper . so can anyone help for that? I want it rounded as below. My Current Output: Simulator Screen Shot - iPhone 8 - 2021-01-18 at 14 17 51

Desired output: Screen Shot 2021-01-18 at 14 18 48 ViewController:

    func initCalendar() {
        calendar.dataSource = self
        calendar.delegate = self
        calendar.allowsMultipleSelection = true
        calendar.firstWeekday = 2
        calendar.backgroundColor = .clear
        calendar.appearance.eventSelectionColor = UIColor.white
        calendar.appearance.eventOffset = CGPoint(x: 0, y: 0)
        calendar.placeholderType = .none
        calendar.scope = .month
        calendar.appearance.headerTitleColor = Defined.reuColor
        calendar.appearance.weekdayTextColor = Defined.reuColor
        //calendar.appearance.
        calendar.today = nil // Hide the today circle
        calendar.register(DateCalendarCell.self, forCellReuseIdentifier: "cell")
        calendar.clipsToBounds = false // Remove top/bottom line
        calendar.swipeToChooseGesture.isEnabled = true // Swipe-To-Choose
        self.calendar.accessibilityIdentifier = "calendar"
    }
extension AddRequestViewController: FSCalendarDelegate, FSCalendarDelegateAppearance, FSCalendarDataSource {
    func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell {
        let cell = calendar.dequeueReusableCell(withIdentifier: "cell", for: date, at: position)
        return cell
    }

    func calendar(_ calendar: FSCalendar, willDisplay cell: FSCalendarCell, for date: Date, at position: FSCalendarMonthPosition) {
        self.configure(cell: cell, for: date, at: position)
    }

    func calendar(_ calendar: FSCalendar, titleFor date: Date) -> String? {
        return nil
    }

    func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? {
        return Defined.reuColor
    }

    // MARK: - FSCalendarDelegate

    func calendar(_ calendar: FSCalendar, boundingRectWillChange bounds: CGRect, animated: Bool) {
        self.calendar.frame.size.height = bounds.height

    }

    func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool {
        return monthPosition == .current
    }

    func calendar(_ calendar: FSCalendar, shouldDeselect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool {
        return monthPosition == .current
    }

    func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {

        if calendar.selectedDates.count == 3 {
            for date in calendar.selectedDates {
                calendar.deselect(date)
            }
            calendar.select(date)
        }

        if calendar.selectedDates.count == 1 {
            let date = calendar.selectedDates[0]
            self.startDate = date.dateTimeRequest
            self.startDateForView = date.fullDateString
            self.lblDateRange.text = "\(startDateForView) > \(startDateForView)"

            self.joinPickedDateAndTime()
        }

        if calendar.selectedDates.count == 2 {
            var first = calendar.selectedDates[0]
            var second = calendar.selectedDates[1]
            if second <= first {
                let temp = first
                first = second
                second = temp
            }
            self.startDate = first.dateTimeRequest
            self.endDate = second.dateTimeRequest
            self.startDateForView = first.fullDateString
            self.endDateForView = second.fullDateString
            self.lblDateRange.text = "\(self.startDateForView) > \(self.endDateForView)"

            self.joinPickedDateAndTime()
        }
        self.configureVisibleCells()
    }

    func calendar(_ calendar: FSCalendar, didDeselect date: Date, at monthPosition: FSCalendarMonthPosition) {
        self.configureVisibleCells()
    }

    func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, eventOffsetFor date: Date) -> CGPoint {
        return CGPoint(x: 0, y: 0)
    }

    // MARK: - Private functions

    private func configureVisibleCells() {
        calendar.visibleCells().forEach { (cell) in
            let date = calendar.date(for: cell)
            let position = calendar.monthPosition(for: cell)
            self.configure(cell: cell, for: date!, at: position)
        }
    }

    private func configure(cell: FSCalendarCell, for date: Date, at position: FSCalendarMonthPosition) {
        guard let diyCell = (cell as? DateCalendarCell) else {return}
        diyCell.circleImageView.isHidden = true
        if position == .current {
            var selectionType = SelectionType.none
            if calendar.selectedDates.count == 2 {
                var first = calendar.selectedDates[0]
                var second = calendar.selectedDates[1]
                if second <= first {
                    let temp = first
                    first = second
                    second = temp
                }
                if date == first {
                    selectionType = .leftBorder
                } else if date == second {
                    selectionType = .rightBorder
                } else if date >= first && date <= second {
                    selectionType = .middle
                }
            } else {
                if calendar.selectedDates.contains(date) {
                    if calendar.selectedDates.count == 1 {
                        selectionType = .single
                    } else {
                        selectionType = .none
                    }
                } else {
                    selectionType = .none
                }
            }
            diyCell.selectionLayer.isHidden = false
            diyCell.selectionType = selectionType
        } else {
            diyCell.selectionLayer.isHidden = true
            diyCell.titleLabel.textColor = Defined.reuColor
        }
    }
}

DIYCalendarCell.swift

enum SelectionType : Int {
    case none
    case single
    case leftBorder
    case middle
    case rightBorder
}

class DateCalendarCell: FSCalendarCell {

    weak var circleImageView: UIImageView!
    weak var selectionLayer: CAShapeLayer!

    fileprivate var lightBlue: UIColor = Defined.lightBlue
    fileprivate var darkBlue: UIColor = Defined.filterColor

    var selectionType: SelectionType = .none {
        didSet {
            setNeedsLayout()
        }
    }

    required init!(coder aDecoder: NSCoder!) {
        fatalError("init(coder:) has not been implemented")
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        let circleImageView = UIImageView(image: UIImage(named: "ic_today_wrapper")!)
        self.contentView.insertSubview(circleImageView, at: 0)
        self.circleImageView = circleImageView

        let selectionLayer = CAShapeLayer()
        selectionLayer.fillColor = darkBlue.cgColor
        selectionLayer.actions = ["hidden": NSNull()]
        self.contentView.layer.insertSublayer(selectionLayer, at: 0)
        self.selectionLayer = selectionLayer

        self.shapeLayer.isHidden = true
        self.titleLabel.textColor = Defined.reuColor
        let view = UIView(frame: self.bounds)
        view.backgroundColor = .white
        self.backgroundView = view

    }

    override func layoutSubviews() {
        super.layoutSubviews()
        let width = self.contentView.bounds.height
        let distance = (self.contentView.bounds.width - width) / 2
        let frame = CGRect(x: self.contentView.bounds.minX + distance, y: self.contentView.bounds.minY, width: width, height: width)

        self.circleImageView.frame = frame
        self.eventIndicator.isHidden = true
        self.backgroundView?.frame = self.bounds.insetBy(dx: 0, dy: 0)
        self.selectionLayer.frame = self.contentView.bounds
        self.selectionLayer.fillColor = UIColor.white.cgColor
        self.titleLabel.textColor = Defined.reuColor
        selectionLayer.sublayers?.removeAll()
        if selectionType == .middle {
            self.selectionLayer.fillColor = lightBlue.cgColor
            self.titleLabel.textColor = darkBlue
            self.selectionLayer.path = UIBezierPath(rect: CGRect(origin: CGPoint(x: self.selectionLayer.bounds.minX, y: self.selectionLayer.bounds.minY + 6), size: CGSize(width: self.selectionLayer.frame.width, height: self.selectionLayer.frame.height - 12))).cgPath
        } else if selectionType == .rightBorder {
            let layer = CAShapeLayer()
            let path = UIBezierPath()
            path.move(to: CGPoint(x: 0, y: self.selectionLayer.frame.maxY / 2))
            path.addLine(to: CGPoint(x: 10, y: 2))
            path.addLine(to: CGPoint(x: self.selectionLayer.frame.maxX - 10, y: 2))
            path.addLine(to: CGPoint(x: self.selectionLayer.frame.maxX - 10, y: self.selectionLayer.frame.maxY - 2))
            path.addLine(to: CGPoint(x: 10, y: self.selectionLayer.frame.maxY - 2))
            path.close()
            layer.path = path.cgPath
            layer.fillColor = darkBlue.cgColor
            layer.borderWidth = 1
            self.selectionLayer.insertSublayer(layer, at: 0)
            self.selectionLayer.fillColor = lightBlue.cgColor
            self.titleLabel.textColor = .white
            self.selectionLayer.lineCap = .round
            self.selectionLayer.lineWidth = 3
            self.selectionLayer.path = UIBezierPath(rect: CGRect(origin: CGPoint(x: self.selectionLayer.bounds.minX, y: self.selectionLayer.bounds.minY + 6), size: CGSize(width: self.selectionLayer.frame.width - 10, height: self.selectionLayer.frame.height - 12))).cgPath
        } else if selectionType == .leftBorder {
            let layer = CAShapeLayer()
            let path = UIBezierPath()
            path.move(to: CGPoint(x: 10, y: 2))
            path.addLine(to: CGPoint(x: self.selectionLayer.frame.maxX - 10, y: 2))
            path.addLine(to: CGPoint(x: self.selectionLayer.frame.maxX, y: self.selectionLayer.frame.maxY / 2))
            path.addLine(to: CGPoint(x: self.selectionLayer.frame.maxX - 10, y: self.selectionLayer.frame.maxY - 2))
            path.addLine(to: CGPoint(x: 10, y: self.selectionLayer.frame.maxY - 2))
            path.close()
            layer.path = path.cgPath
            layer.fillColor = darkBlue.cgColor
            layer.borderWidth = 1
            self.selectionLayer.insertSublayer(layer, at: 0)
            self.selectionLayer.fillColor = lightBlue.cgColor
            self.titleLabel.textColor = .white
            self.selectionLayer.lineCap = .round
            self.selectionLayer.lineWidth = 3
            self.selectionLayer.path = UIBezierPath(rect: CGRect(origin: CGPoint(x: self.selectionLayer.bounds.minX + 10, y: self.selectionLayer.bounds.minY + 6), size: CGSize(width: self.selectionLayer.frame.width - 10, height: self.selectionLayer.frame.height - 12))).cgPath
        } else if selectionType == .single {
            self.selectionLayer.fillColor = darkBlue.cgColor
            self.titleLabel.textColor = lightBlue
            let diameter: CGFloat = min(self.selectionLayer.frame.height, self.selectionLayer.frame.width)
            self.selectionLayer.path = UIBezierPath(ovalIn: CGRect(x: self.contentView.frame.width / 2 - diameter / 2, y: self.contentView.frame.height / 2 - diameter / 2, width: diameter, height: diameter)).cgPath
        } else {
            self.selectionLayer.fillColor = UIColor.white.cgColor
            self.titleLabel.textColor = Defined.reuColor
            let diameter: CGFloat = min(self.selectionLayer.frame.height, self.selectionLayer.frame.width)
            self.selectionLayer.path = UIBezierPath(ovalIn: CGRect(x: self.contentView.frame.width / 2 - diameter / 2, y: self.contentView.frame.height / 2 - diameter / 2, width: diameter, height: diameter)).cgPath
            //change from rectangle to circle as background flicks from circle to rectangle
        }

    }

    override func configureAppearance() {
        super.configureAppearance()
        self.eventIndicator.isHidden = true
        // Override the build-in appearance configuration
        if self.isPlaceholder {
            self.titleLabel.textColor = UIColor.lightGray
        } else {
            self.titleLabel.textColor = Defined.reuColor
        }
    }

}
quanshday commented 3 years ago

bác cho tui xin sources với đc k?

ThaiHien1610 commented 5 months ago

bác cho tui xin sources với đc k?

Chắc là không rồi :D