EKCalendarChooser in SwiftUI

Hello there!

I'm stuck at integrating an EKCalendarChooser in my SwiftUI App.

Well, basically it works, but the Sheet shows no NavigationBar, NavigationBarButtons or ToolbarButtons.


Thats my code, it works perfectly fine with other UIKit integrations (e.g. EKEventEditViewController). But it seems to be different for EKCalendarChooser.

Code Block
import SwiftUI
import EventKitUI
struct eventCalendarChooser: UIViewControllerRepresentable {
    @EnvironmentObject var manager: EventsCalendarManager
  @Binding var selectedCalendars: [EKCalendar]
init(selectedCalendars: Binding<[EKCalendar]>) {
        self._selectedCalendars = selectedCalendars
    }
    typealias UIViewControllerType = EKCalendarChooser
    func makeUIViewController(context: Context) -> EKCalendarChooser {
        let vc = EKCalendarChooser(selectionStyle: .multiple, displayStyle: .allCalendars, entityType: .event, eventStore: eventStore)
        vc.showsCancelButton = true
        vc.showsDoneButton = true
        vc.delegate = context.coordinator
        return vc
    }
    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }
    func updateUIViewController(_ uiViewController: EKCalendarChooser, context: Context) { }
    class Coordinator: NSObject, EKCalendarChooserDelegate, UINavigationControllerDelegate {
        func calendarChooserDidFinish(_ calendarChooser: EKCalendarChooser) {
            self.parent.selectedCalendars = Array(calendarChooser.selectedCalendars)
            calendarChooser.dismiss(animated: true)
        }
        var parent: eventCalendarChooser
        init(_ parent: eventCalendarChooser) {
            self.parent = parent
        }
    }
}

Any help is appreciated, thank you very much in advance!

Accepted Reply

Well, I doubt that anybody will have this issue, as this is kind of a small niche case...

But I found a solution, here we go.

The EKCalendarChooser needs to be wrapped into a UINavigationController. No, not the SwiftUI NavigationView.
How to achieve this?

The whole SwiftUI Wrapper needs to be changed into UINavigationController:

Code Block
typealias UIViewControllerType = UINavigationController


The makeUIViewController function must return a UINavigationController, but it needs to be set to be the rootViewController of the EKCalendarChooser

Code Block
func makeUIViewController(context: Context) -> UINavigationController {
let vc = EKCalendarChooser(selectionStyle: .multiple, displayStyle: .allCalendars,
entityType: .event, eventStore: eventStore)
vc.showsDoneButton = true
vc.showsCancelButton = true
    vc.delegate = context.coordinator
    let nvc = UINavigationController(rootViewController: vc)
    return nvc
}


The Coordinator can still be set with the makeCoordinator function, but the Coordinator class is different:

Code Block
class Coordinator: NSObject, UINavigationControllerDelegate, EKCalendarChooserDelegate {
func calendarChooserDidFinish(_ calendarChooser: EKCalendarChooser) {
/* Do stuff here, for example assign the selectedCalendars to your bindings etc... */
calendarChooser.dismiss(animated: true)
}
func calendarChooserDidCancel(_ calendarChooser: EKCalendarChooser) {
/* Do stuff here */
calendarChooser.dismiss(animated: true)
}
/*
func calendarChooserSelectionDidChange(_ calendarChooser: EKCalendarChooser) {
}
*/
var parent: eventCalendarChooserNavigationView
init(_ parent: eventCalendarChooserNavigationView) {
self.parent = parent
}
}


Note: Weird, but it seems to be necessary that the Coordinator conforms to UINavigationControllerDelegate first and then EKCalendarChooserDelegate, the other way around it will not work. Of course it also needs to conform to NSObject, but this is known.

If anybody needs any further help in this topic, please contact me.

Replies

Well, I doubt that anybody will have this issue, as this is kind of a small niche case...

But I found a solution, here we go.

The EKCalendarChooser needs to be wrapped into a UINavigationController. No, not the SwiftUI NavigationView.
How to achieve this?

The whole SwiftUI Wrapper needs to be changed into UINavigationController:

Code Block
typealias UIViewControllerType = UINavigationController


The makeUIViewController function must return a UINavigationController, but it needs to be set to be the rootViewController of the EKCalendarChooser

Code Block
func makeUIViewController(context: Context) -> UINavigationController {
let vc = EKCalendarChooser(selectionStyle: .multiple, displayStyle: .allCalendars,
entityType: .event, eventStore: eventStore)
vc.showsDoneButton = true
vc.showsCancelButton = true
    vc.delegate = context.coordinator
    let nvc = UINavigationController(rootViewController: vc)
    return nvc
}


The Coordinator can still be set with the makeCoordinator function, but the Coordinator class is different:

Code Block
class Coordinator: NSObject, UINavigationControllerDelegate, EKCalendarChooserDelegate {
func calendarChooserDidFinish(_ calendarChooser: EKCalendarChooser) {
/* Do stuff here, for example assign the selectedCalendars to your bindings etc... */
calendarChooser.dismiss(animated: true)
}
func calendarChooserDidCancel(_ calendarChooser: EKCalendarChooser) {
/* Do stuff here */
calendarChooser.dismiss(animated: true)
}
/*
func calendarChooserSelectionDidChange(_ calendarChooser: EKCalendarChooser) {
}
*/
var parent: eventCalendarChooserNavigationView
init(_ parent: eventCalendarChooserNavigationView) {
self.parent = parent
}
}


Note: Weird, but it seems to be necessary that the Coordinator conforms to UINavigationControllerDelegate first and then EKCalendarChooserDelegate, the other way around it will not work. Of course it also needs to conform to NSObject, but this is known.

If anybody needs any further help in this topic, please contact me.