Remove title when pushing EKCalendarChooser to Navigation Stack with SwiftUI - swiftui

I'm working on an app where I want to push the EKCalendarChooser View Controller to the navigation stack with a navigation link. Everything works as expected apart from the fact that I can't get rid of some magic title/label.
I want to hide the title marked with the red rectangle in the image.
I'm using the following code to push the view:
NavigationLink(destination: CalendarChooser(eventStore: self.eventStore)
.edgesIgnoringSafeArea([.top,.bottom])
.navigationTitle("My Navigation Title")) {
Text("Calendar Selection")
}
And this is my UIViewControllerRepresentable
import SwiftUI
import EventKitUI
struct CalendarChooser: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
#Environment(\.presentationMode) var presentationMode
let eventStore: EKEventStore
func makeUIViewController(context: UIViewControllerRepresentableContext<CalendarChooser>) -> UINavigationController {
let chooser = EKCalendarChooser(selectionStyle: .multiple, displayStyle: .allCalendars, entityType: .event, eventStore: eventStore)
chooser.selectedCalendars = Set(eventStore.selectableCalendarsFromSettings)
chooser.delegate = context.coordinator
chooser.showsDoneButton = false
chooser.showsCancelButton = false
return UINavigationController(rootViewController: chooser)
}
func updateUIViewController(_ uiViewController: UINavigationController, context: UIViewControllerRepresentableContext<CalendarChooser>) {
}
class Coordinator: NSObject, UINavigationControllerDelegate, EKCalendarChooserDelegate {
var parent: CalendarChooser
init(_ parent: CalendarChooser) {
self.parent = parent
}
func calendarChooserDidFinish(_ calendarChooser: EKCalendarChooser) {
let selectedCalendarIDs = calendarChooser.selectedCalendars.compactMap { $0.calendarIdentifier }
UserDefaults.savedCalendarIDs = selectedCalendarIDs
NotificationCenter.default.post(name: .calendarSelectionDidChange, object: nil)
parent.presentationMode.wrappedValue.dismiss()
}
func calendarChooserDidCancel(_ calendarChooser: EKCalendarChooser) {
parent.presentationMode.wrappedValue.dismiss()
}
}
}
Note that I'm not even sure that I'm on the right track here and I'm open for any solution.

I think I've found a solution to my own problem. With a small modification
to my UIViewControllerRepresentable the view looks the way I want it to. More specifically to the updateUIViewController function:
func updateUIViewController(_ uiViewController: UINavigationController, context: UIViewControllerRepresentableContext<CalendarChooser>) {
uiViewController.setNavigationBarHidden(true, animated: false) // This line!
}
By doing this I keep the navigation controls and title from the navigation link, which looks like this:

Related

How to add a customized InfoWindow to markers in google-maps swift ui?

i tried to make a view like bellow in SwiftUi without any success Customized info window swift ui
Since this question doesn't have too much detail, I will be going off of some assumptions. First, I am assuming that you are calling the MapView through a UIViewControllerRepresentable.
I am not too familiar with the Google Maps SDK, but this is possible through the GMSMapViewDelegate Methods. After implementing the proper GMSMapViewDelegate method, you can use ZStacks to present the image that you would like to show.
For example:
struct MapView: UIViewControllerRepresentable {
var parentView: ContentView
func makeUIViewController(context: Context) {
let mapView = GMSMapView()
return mapView
}
func updateUIViewController(_ uiViewController: GMSMapView, context: Context) {
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator: NSObject, GMSMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
//Use the proper Google Maps Delegate method to find out if a marker was tapped and then show the image by doing: parent.parentView.isShowingInformationImage = true.
}
}
In your SwiftUI view that you would like to put this MapView in, you can do the following:
struct ContentView: View {
#State var isShowingInformationImage = false
var body: some View {
ZStack {
if isShowingInformationImage {
//Call the View containing the image
}
MapView(parentView: self)
}
}
}

MFMessageComposeViewController + SwiftUI Buggy Behavior

I'm using ViewControllerRepresentable to present a MFMessageComposeViewController so users can send texts from my app.
However, every time the view is presented, it's very buggy - elements randomly disappear, scrolling is off, and the screen flickers. Tested on iOS 14.2 and 14.3.
Here's the code:
import SwiftUI
import MessageUI
struct MessageView: UIViewControllerRepresentable {
var recipient: String
class Coordinator: NSObject, MFMessageComposeViewControllerDelegate {
var completion: () -> Void
init(completion: #escaping ()->Void) {
self.completion = completion
}
// delegate method
func messageComposeViewController(_ controller: MFMessageComposeViewController,
didFinishWith result: MessageComposeResult) {
controller.dismiss(animated: true, completion: nil)
completion()
}
}
func makeCoordinator() -> Coordinator {
return Coordinator() {} // not using completion handler
}
func makeUIViewController(context: Context) -> MFMessageComposeViewController {
let vc = MFMessageComposeViewController()
vc.recipients = [recipient]
vc.messageComposeDelegate = context.coordinator
return vc
}
func updateUIViewController(_ uiViewController: MFMessageComposeViewController, context: Context) {}
typealias UIViewControllerType = MFMessageComposeViewController
}
and my view
struct ContentView: View {
#State private var isShowingMessages = false
#State var result: Result<MFMailComposeResult, Error>? = nil
var body: some View {
VStack {
Button("Show Messages") {
self.isShowingMessages = true
}
.sheet(isPresented: self.$isShowingMessages) {
MessageView(recipient: "+15555555555")
}
.edgesIgnoringSafeArea(.bottom)
}
}
}
Is there something wrong with the way I'm presenting this view? Has anyone else experienced this behavior? Similar behavior happens with MFMailComposeViewController, but it's not as buggy.
5 minutes later, I realized I needed to add this when presenting the sheet:
MessageView(recipient: "+15555555555")
.ignoresSafeArea()
The view looked buggy because it was trying to account for the keyboard safe area and had a hard time doing it.

Customize Mapbox style with layers in SwiftUI

I have an issue to customize a Mapbox view's style, like for instance adding some information on the map if a switch is switched on. Not sure if it's important, but the layers I need to add are MGLSymbolStyleLayer and MGLLineStyleLayer.
Let's start with code for the main view containing a switch representing a state used to customize the map's style, and an UIViewRepresentable for the Mapbox view.
struct Test_MapBox: View {
#State private var styleURL: URL = MGLStyle.outdoorsStyleURL
#State private var switchButton: Bool = false
var body: some View {
ZStack(alignment: .bottom) {
MapView(switchButton: switchButton)
.styleURL(styleURL)
.edgesIgnoringSafeArea(.all)
Toggle(isOn: $switchButton, label: {
Text("Switch")
})
}
}
}
struct MapView: UIViewRepresentable {
var switchButton: Bool
var mapView = MGLMapView(frame: .zero)
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MGLMapView {
mapView.delegate = context.coordinator
return mapView
}
func updateUIView(_ uiView: MGLMapView, context: UIViewRepresentableContext<MapView>) {
print("Style: \(uiView.style)")
print("Update view, switch: \(switchButton)")
}
func makeCoordinator() -> MapView.Coordinator {
Coordinator(self)
}
func styleURL(_ styleURL: URL) -> MapView {
mapView.styleURL = styleURL
return self
}
final class Coordinator: NSObject, MGLMapViewDelegate {
var parent: MapView
init(_ control: MapView) {
self.parent = control
}
func mapViewDidFinishLoadingMap(_ mapView: MGLMapView) {
print("Map loaded, switch: \(parent.switchButton)")
}
func mapView(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
print("Style loaded, switch: \(parent.switchButton)")
}
}
}
The issue is the following: inside the delegate functions, switchButton is never up to date, always false (and I don't understand why)... And in updateUIView(), switchButton is OK, but the style is not yet loaded, so usually you get a nil when accessing it ...
Have you got a solution ?

SwiftUI - CNContactViewController NavigationBar problem

I am trying to implement CNContactViewDelegate to be able to show detail of the CNContact. And apparently, I am the first one to implement it with SwiftUI and getting problems. Anyway, I can see the detail of CNContact with using UIViewControllerRepresentable but I have an issue with the NavigationBar, which there is gap between the Contact's image and StatusBar -because of the NavigationBar and NavigationLink I think- and this gap is not there in the native Contacts app and apparently in this link that implemented the framework in UIKit.
Here is the code;
struct ContactsListView: View {
#ObservedObject var contactsModel: ContactsViewModel
var body: some View {
NavigationView{
List {
//After some ForEach's and Section's
//This view is working.
NavigationLink(destination: ContactDetailView(contact: self.$contactsModel.contacts[sectionIdx].contacts[contactIdx])) {
Text(self.contactsModel.contacts[sectionIdx].contacts[contactIdx].givenName)
}
}
.navigationBarTitle("Contacts")
}
}
}
struct ContactView: UIViewControllerRepresentable {
#Binding var contact: CNContact
func makeCoordinator() -> ContactView.Coordinator {
Coordinator(self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<ContactView>) -> CNContactViewController {
let controller = CNContactViewController(for: contact)
self.navigationBarHidden(true)
return controller
}
func updateUIViewController(_ uiViewController: CNContactViewController, context: UIViewControllerRepresentableContext<ContactView>) {
print(context)
}
class Coordinator: NSObject, CNContactViewControllerDelegate {
var parent: ContactView
init(_ contactDetail: ContactView) {
self.parent = contactDetail
self.parent.navigationBarHidden(true)
}
}
}
In the ContactView, both of those self.navigationBarHidden(true)'s are not working. As an example of the problem here is the native app's screenshot;
And here is the result of my code;
Posted my comment on the solution and then I came to the idea to wrap the contact view controller inside my custom NavigationController. And voila that fixed it!
struct ContactView: UIViewControllerRepresentable {
var contact: CNContact
func makeCoordinator() -> ContactView.Coordinator {
Coordinator(self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<ContactView>) -> NavigationController {
let controller = CNContactViewController(forUnknownContact: contact)
controller.contactStore = CNContactStore()
controller.delegate = context.coordinator
let navigationController = NavigationController(rootViewController: controller)
return navigationController
}
func updateUIViewController(_ uiViewController: NavigationController, context: UIViewControllerRepresentableContext<ContactView>) {
}
class Coordinator: NSObject, CNContactViewControllerDelegate {
var parent: ContactView
init(_ contactDetail: ContactView) {
self.parent = contactDetail
}
func contactViewController(_ viewController: CNContactViewController,
didCompleteWith contact: CNContact?) {
}
func contactViewController(_ viewController: CNContactViewController,
shouldPerformDefaultActionFor property: CNContactProperty) -> Bool {
return true
}
}
}
As the question is got an upvote I thought I can share my half way solution. This solves the gap however during the transition to detail there is a glitch of navigation bar with background color. After the transition it is becoming clear.
struct ContactDetailView: View {
var contact: CNContact
var body: some View {
ZStack {
Color.clear
ContactView(contact: self.contact)
.navigationBarTitle("", displayMode: .inline)
}.edgesIgnoringSafeArea(.top)
}
}
struct ContactView: UIViewControllerRepresentable {
var contact: CNContact
func makeCoordinator() -> ContactView.Coordinator {
Coordinator(self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<ContactView>) -> CNContactViewController {
let controller = CNContactViewController(forUnknownContact: contact)
controller.allowsActions = true
controller.allowsEditing = false
controller.delegate = context.coordinator
return controller
}
func updateUIViewController(_ uiViewController: CNContactViewController, context: UIViewControllerRepresentableContext<ContactView>) {
print("updated")
}
class Coordinator: NSObject, CNContactViewControllerDelegate {
var parent: ContactView
init(_ contactDetail: ContactView) {
self.parent = contactDetail
}
func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
}
func contactViewController(_ viewController: CNContactViewController, shouldPerformDefaultActionFor property: CNContactProperty) -> Bool {
return true
}
}
}

SwiftUI: NavigationLink's Destination initialized whenever its parent is redrawn

So I have a ParentView, which has a NavigationLink, leading to a UIViewControllerRepresentable-conforming PageViewController.
Now that ParentView also has some subscription on some publisher. Whenever that one is fired, not only will the ParentView redraw all its content (which it should), it will also re-initialize the (already presenting) PageViewController.
That leads to stuttering/glitching, because the PageViewController is already presenting and using the controllers that are continually being resetted.
Below is the ParentView and PageViewController (without the Coordinator stuff), both is pretty vanilla. The commented guard line is a hack I tried to prevent it from updating if displayed already. It helps but it's still stuttering on every swipe.
So the question is: How can we prevent the updating of a presented ViewController-wrapped-View when its presenting View is redrawn?
struct ParentView: View {
#Binding var something: Bool
var body: some View {
NavigationLink(destination: PageViewController(controllers: controllers)) {
Text("Push me")
}
}
}
final class PageViewController: UIViewControllerRepresentable {
var controllers: [UIViewController]
private var currentPage = 0
init(controllers: [UIViewController]) {
self.controllers = controllers
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal)
pageViewController.dataSource = context.coordinator
pageViewController.delegate = context.coordinator
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
// I tried this: guard pageViewController.viewControllers!.isEmpty else { return }
pageViewController.setViewControllers(
[controllers[currentPage]], direction: .forward, animated: true)
}
}
If your controllers don't change after being displayed once, you can simply call:
pageViewController.setViewControllers([controllers[currentPage]], direction: .forward, animated: true)
from the makeUIViewController(context:) function instead of the updateUIViewController(:) function.