SwiftUI - CNContactViewController NavigationBar problem

592 Views Asked by At

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;

enter image description here

And here is the result of my code;

enter image description here

2

There are 2 best solutions below

1
On BEST ANSWER

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
        }
    }
}
0
On

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
        }
    }
}