You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI集成FSCalendar遇代理处理问题,求正确实现方案

Hey there! Let's sort out your FSCalendar wrapping issue in SwiftUI. I see exactly where the confusion is coming from, so let's break this down step by step.

Core Problem Analysis

Right now, you're splitting FSCalendarDelegate logic between two places: your CalendarModule view controller (which sets itself as the calendar's delegate) and the Coordinator in your UIViewControllerRepresentable struct. This creates a conflict—only one delegate can be active at a time, so your Coordinator's delegate methods never actually run. On top of that, you don't have a way to pass the selected date from the UIKit calendar up to your SwiftUI CalendarView to update the bottom details section.

Fix Steps & Refactored Code

Let's restructure your code to clean up the delegate flow and add state synchronization between UIKit and SwiftUI.

1. Refactor CalendarModule to Remove Internal Delegate

We'll make CalendarModule focus only on calendar setup and let external code handle the delegate:

// CalendarModule.swift
import SwiftUI
import FSCalendar

class CalendarModule: UIViewController {
    var calendar = FSCalendar()
    // Add a delegate property to let external code control the calendar's delegate
    weak var calendarDelegate: FSCalendarDelegate? {
        didSet {
            calendar.delegate = calendarDelegate
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initCalendar()
        view.addSubview(calendar)
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        calendar.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.width)
    }
    
    private func initCalendar() {
        calendar.appearance.todayColor = UIColor.systemGreen
        calendar.appearance.selectionColor = UIColor.systemBlue
    }
}

2. Update UIViewControllerRepresentable with Binding & Coordinator

We'll use Binding to sync the selected date between UIKit and SwiftUI, and let the Coordinator handle all delegate logic:

struct CalendarModuleViewController: UIViewControllerRepresentable {
    // Binding to share selected date state with SwiftUI
    @Binding var selectedDate: Date?
    
    typealias UIViewControllerType = CalendarModule
    
    func makeUIViewController(context: Context) -> CalendarModule {
        let viewController = CalendarModule()
        // Assign Coordinator as the calendar's delegate
        viewController.calendarDelegate = context.coordinator
        return viewController
    }
    
    func updateUIViewController(_ uiViewController: CalendarModule, context: Context) {
        // Sync SwiftUI state back to the calendar if needed
        if let selectedDate = selectedDate {
            uiViewController.calendar.select(selectedDate)
        } else {
            uiViewController.calendar.deselect(uiViewController.calendar.selectedDate)
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    final class Coordinator: NSObject, FSCalendarDelegate {
        private var parent: CalendarModuleViewController
        
        init(_ parent: CalendarModuleViewController) {
            self.parent = parent
        }
        
        // Handle date selection and pass it up to SwiftUI via Binding
        func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
            parent.selectedDate = date
        }
        
        // Optional: Handle date deselection
        func calendar(_ calendar: FSCalendar, didDeselect date: Date, at monthPosition: FSCalendarMonthPosition) {
            parent.selectedDate = nil
        }
    }
}

struct CalendarModuleView: View {
    @Binding var selectedDate: Date?
    
    var body: some View {
        CalendarModuleViewController(selectedDate: $selectedDate)
    }
}

3. Update CalendarView to Manage State & Show Details

Now we'll add a state variable to track the selected date, pass it to the calendar module, and update the bottom details section dynamically:

// CalendarView.swift
import SwiftUI

struct CalendarView: View {
    // State to hold the selected date in SwiftUI
    @State private var selectedDate: Date?
    
    var body: some View {
        NavigationView {
            VStack{
                VStack {
                    Spacer()
                    // Pass the state binding to the calendar module
                    CalendarModuleView(selectedDate: $selectedDate)
                    Spacer()
                }
                VStack {
                    Spacer()
                    if let date = selectedDate {
                        Text("Selected Date: \(formattedDate(date))")
                            .font(.headline)
                        Text("Event details for this day will appear here")
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                    } else {
                        Text("Select a date to view details")
                            .font(.headline)
                    }
                    Spacer()
                }
                .frame(height: 150)
                .border(Color.gray.opacity(0.2))
            }
            .navigationBarTitle(Text("Calendar"), displayMode: .inline)
            .navigationBarItems(trailing: NavigationLink(destination: CreateEventView().environmentObject(Event())) {
                Image(systemName: "plus").imageScale(.large)
            }.buttonStyle(DefaultButtonStyle()) )
        }
    }
    
    // Helper to format the date for display
    private func formattedDate(_ date: Date) -> String {
        let formatter = DateFormatter()
        formatter.dateStyle = .long
        formatter.timeStyle = .none
        return formatter.string(from: date)
    }
}

// Placeholder for your existing CreateEventView and Event class
struct CreateEventView: View {
    @EnvironmentObject var event: Event
    var body: some View {
        Text("Create Event")
    }
}

class Event: ObservableObject {
    // Your existing Event implementation
}

struct CalendarView_Previews: PreviewProvider {
    static var previews: some View {
        CalendarView()
    }
}

4. Keep ContentView Unchanged

Your original ContentView doesn't need any updates—it can still render CalendarView as before.

Why This Works

  • Separation of Concerns: CalendarModule handles only calendar setup and layout, while the Coordinator manages delegate logic, making your code easier to maintain.
  • State Sync: The Binding ensures that selecting a date in the UIKit calendar immediately updates the SwiftUI view, and vice versa if you ever need to set a selected date from SwiftUI.
  • Clear Delegate Flow: All calendar events are now handled by the Coordinator, so you won't run into issues with missing delegate method triggers.

内容的提问来源于stack exchange,提问作者Ted Lim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 23:37:33