首页 > 解决方案 > 如何在 SwiftUI 中实现自定义日历?

问题描述

我需要使用自定义日历而不是SwiftUI DatePicker. 为此,我pod 'KDCalendar', '~> 1.8.9'Podfile该结构之后添加MyCalendar了以下内容:

import SwiftUI
import KDCalendar

public struct MyCalendar: UIViewRepresentable {

    public func makeUIView(context: Context) -> CalendarView {
        return CalendarView()
    }

    public func updateUIView(_ calendar: CalendarView, context: Context) {
        let date: Date = Date()
        calendar.selectDate(date)
    }
}

当我要在任何堆栈中使用它时,它只显示星期名称和日历的其他部分从视图中省略,并发生以下错误:

在此处输入图像描述

[Assert] negative or zero item sizes are not supported in the flow layout

非常希望您的合作能够解决此问题。

标签: iosswiftxcodeswiftui

解决方案


你也可以试试FSCalendar。有很多可配置选项的地方有点复杂。请找到FSCalendar的最简单实现,如下所示:

用法


import UIKit
import SwiftUI
import FSCalendar

MyCalendar().frame(minWidth: 200, minHeight: 320).padding(.leading, 5)

我的日历


struct MyCalendar: UIViewControllerRepresentable {
    func makeUIViewController(context: UIViewControllerRepresentableContext<MyCalendar>) -> MyCalendarController {
        let calendar: MyCalendarController = .init()
        return calendar
    }

    func updateUIViewController(_ calendar: MyCalendarController, context: UIViewControllerRepresentableContext<MyCalendar>) {
        // MARK: - TODO
    }
}     

我的日历控制器


class MyCalendarController: UIViewController, FSCalendarDelegateAppearance {
    let secondary: UIColor = .parse(0xE0B355)
    let primary  : UIColor = .parse(0x346C7C)
    let tersiary : UIColor = .parse(0xE7EEEF)
    
    fileprivate let formatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter
    }()
    
    fileprivate weak var calendar: FSCalendar!
    
    override func loadView() {
        let width: CGFloat = UIScreen.main.bounds.width - 40
        let frame: CGRect  = .init(x: 0, y: 0, width: width, height: 300)
        let view:  UIView  = .init(frame: frame)
        self.view = view
        
        let calendar: FSCalendar = .init(frame: frame)
        calendar.allowsMultipleSelection = false
        calendar.dataSource = self
        calendar.delegate = self
        
        view.addSubview(calendar)
        self.calendar = calendar
        
        calendar.calendarHeaderView.backgroundColor = self.primary
        calendar.calendarWeekdayView.backgroundColor = self.primary
        calendar.appearance.headerTitleColor = self.tersiary
        calendar.appearance.weekdayTextColor = self.tersiary
        
        calendar.appearance.eventSelectionColor = self.tersiary
        calendar.appearance.eventDefaultColor = self.primary
        calendar.appearance.eventOffset = CGPoint(x: 0, y: -7)
        
        calendar.appearance.todaySelectionColor = self.primary
        calendar.appearance.selectionColor = self.secondary
        calendar.appearance.todayColor = self.primary
        
        calendar.appearance.titleWeekendColor = self.secondary
        calendar.appearance.titleDefaultColor = self.primary

        calendar.swipeToChooseGesture.isEnabled = true
        let scopeGesture = UIPanGestureRecognizer(target: calendar, action: #selector(calendar.handleScopeGesture(_:)));
        scopeGesture.delegate = self
        calendar.addGestureRecognizer(scopeGesture)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.calendar.scope = .month
        self.calendar.select(Date.init())
        self.calendar.accessibilityIdentifier = "calendar"
    }
}

扩展


extension MyCalendarController: FSCalendarDataSource {
    func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
        debugPrint("did select date \(self.formatter.string(from: date))")
        let selectedDates = calendar.selectedDates.map({self.formatter.string(from: $0)})
        debugPrint("selected dates is \(selectedDates)")
        if monthPosition == .next || monthPosition == .previous {
            calendar.setCurrentPage(date, animated: true)
        }
    }
}

extension MyCalendarController: FSCalendarDelegate {
    func calendar(_ calendar: FSCalendar, boundingRectWillChange bounds: CGRect, animated: Bool) {
        self.calendar.frame.size.height = bounds.height
        self.view.layoutIfNeeded()
    }
}

extension MyCalendarController: UIGestureRecognizerDelegate {
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        debugPrint("UIGestureRecognizer")
        return true
    }
}

extension UIColor {
    static func parse(_ hex: UInt32, alpha: Double = 1.0) -> UIColor {
        let red   = CGFloat((hex & 0xFF0000) >> 16)/256.0
        let green = CGFloat((hex & 0xFF00) >> 8)/256.0
        let blue  = CGFloat(hex & 0xFF)/256.0
        return UIColor(red: red, green: green, blue: blue, alpha: CGFloat(alpha))
    }
}

推荐阅读