如何使基于FSCalendar的UIViewRepresentable适配UIView类型?
实现方案:将SwiftUI的FSCalendar包装转为UIView类型
你当前的CalendarViewRepresentable是SwiftUI的View实现,本身并非UIKit的UIView类型。要让它能作为标准UIView使用,核心是通过UIHostingController完成SwiftUI视图到UIKit视图的桥接,以下是两种可行方案:
方案一:直接在UIKit控制器中包装使用
如果只是需要在某个UIViewController中快速集成,直接用UIHostingController承载你的SwiftUI日历视图即可:
// 在UIViewController的viewDidLoad或其他生命周期方法中 override func viewDidLoad() { super.viewDidLoad() // 创建SwiftUI日历视图 let calendarSwiftUIView = CalendarViewRepresentable(selectedDate: .constant(Date())) // 用UIHostingController包装 let hostingController = UIHostingController(rootView: calendarSwiftUIView) // 添加到当前控制器 addChild(hostingController) view.addSubview(hostingController.view) hostingController.didMove(toParent: self) // 设置布局约束 hostingController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ hostingController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), hostingController.view.heightAnchor.constraint(equalToConstant: 320) // 固定高度或自适应 ]) }
方案二:封装为自定义UIView子类(高复用性)
如果需要在多处UIKit场景中复用,建议封装成一个标准的UIView子类,内部自动完成SwiftUI视图的桥接:
1. 封装自定义UIView
class FSCalendarWrapperView: UIView { // 对外暴露选中日期的属性,同步到内部SwiftUI视图 var selectedDate: Date { didSet { hostingController?.rootView.selectedDate = .constant(selectedDate) } } private var hostingController: UIHostingController<CalendarViewRepresentable>? // 初始化方法 init(selectedDate: Date = Date()) { self.selectedDate = selectedDate super.init(frame: .zero) setupHostingController() } required init?(coder: NSCoder) { self.selectedDate = Date() super.init(coder: coder) setupHostingController() } // 内部初始化UIHostingController private func setupHostingController() { let calendarSwiftUIView = CalendarViewRepresentable(selectedDate: .constant(selectedDate)) let hostingController = UIHostingController(rootView: calendarSwiftUIView) self.hostingController = hostingController // 将hostingController的view添加到当前view addSubview(hostingController.view) hostingController.view.translatesAutoresizingMaskIntoConstraints = false // 约束铺满父视图 NSLayoutConstraint.activate([ hostingController.view.topAnchor.constraint(equalTo: topAnchor), hostingController.view.leadingAnchor.constraint(equalTo: leadingAnchor), hostingController.view.trailingAnchor.constraint(equalTo: trailingAnchor), hostingController.view.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } }
2. 使用自定义UIView
和普通UIView完全一样,直接初始化添加到视图层级:
// 在UIViewController中 let calendarView = FSCalendarWrapperView(selectedDate: Date()) view.addSubview(calendarView) calendarView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ calendarView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), calendarView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), calendarView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), calendarView.heightAnchor.constraint(equalToConstant: 320) ])
原代码的优化建议
你提供的CalendarViewRepresentable存在两处需要修正的问题:
- 不要把FSCalendar作为结构体属性:结构体是值类型,每次状态变化都会重新初始化,应该在
makeUIView方法内创建FSCalendar实例。 - 错误的约束设置:
context.coordinator.topAnchor是无效的,Coordinator是NSObject子类,没有布局锚点,SwiftUI会自动处理UIViewRepresentable视图的布局,无需额外添加该约束。
修正后的CalendarViewRepresentable代码:
struct CalendarViewRepresentable: UIViewRepresentable { @Binding var selectedDate: Date var calendarHeight: NSLayoutConstraint? func updateUIView(_ uiView: FSCalendar, context: Context) { } func makeUIView(context: Context) -> FSCalendar { let calendar = FSCalendar() calendar.delegate = context.coordinator calendar.dataSource = context.coordinator calendar.translatesAutoresizingMaskIntoConstraints = false calendar.setContentHuggingPriority(.required, for: .vertical) calendar.setContentHuggingPriority(.required, for: .horizontal) return calendar } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, FSCalendarDelegate, FSCalendarDataSource { var parent: CalendarViewRepresentable init(_ parent: CalendarViewRepresentable) { self.parent = parent } func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { parent.selectedDate = date } func calendar(_ calendar: FSCalendar, boundingRectWillChange bounds: CGRect, animated: Bool) { parent.calendarHeight?.constant = bounds.height // SwiftUI视图的布局更新无需手动调用layoutIfNeeded,由SwiftUI自动处理 } } }
内容的提问来源于stack exchange,提问作者devOP1
相关产品推荐
相关产品推荐

