UIViewControllerRepresentable和CNContactPickerViewController

时间:2019-07-29 02:53:23

标签: swiftui contactsui

似乎无法创建与CNContactPickerViewController一起使用的UIViewControllerRepresentable。

使用Xcode 11 beta 4,我已经使用其他UIViewController创建了许多其他的UIViewControllerRepresentable,并且效果很好。我尝试更改CNContactPickerViewController的功能以及委托的不同实现。

import SwiftUI
import ContactsUI

// Minimal version
struct LookupContactVCR : UIViewControllerRepresentable {

    func makeUIViewController(context: Context) -> CNContactPickerViewController {
        let contactPickerVC = CNContactPickerViewController()
        contactPickerVC.delegate = context.coordinator
        return contactPickerVC
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator()
    }

    func updateUIViewController(_ uiViewController: CNContactPickerViewController, context: Context) {}

    class Coordinator: NSObject {}
}

extension LookupContactVCR.Coordinator : CNContactPickerDelegate {

    func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
        print("Chose: \(contact.givenName)")
    }
}

#if DEBUG
struct LookupContact_Previews : PreviewProvider {
    static var previews: some View {
        LookupContactVCR()
    }
}
#endif

无错误消息。但是屏幕始终是白色的,什么也没渲染。

3 个答案:

答案 0 :(得分:4)

首先,请为此问题提交Bug Report。 其次,此问题有 2个解决方法

  1. 您可以使用已弃用的ABPeoplePickerNavigationController,但仍然可以使用。
  2. 创建一个在UIViewController上显示CNContactPickerViewController的{​​{1}},并将这个新创建的视图控制器与viewWillAppear一起使用。

1。 ABPeoplePickerNavigationController

SwiftUI

2。 CNContactPickerViewController

EmbeddedContactPickerViewController

import SwiftUI
import AddressBookUI

struct PeoplePicker: UIViewControllerRepresentable {
    typealias UIViewControllerType = ABPeoplePickerNavigationController

    final class Coordinator: NSObject, ABPeoplePickerNavigationControllerDelegate, UINavigationControllerDelegate {
        func peoplePickerNavigationController(_ peoplePicker: ABPeoplePickerNavigationController, didSelectPerson person: ABRecord) {
            <#selected#>
        }

        func peoplePickerNavigationControllerDidCancel(_ peoplePicker: ABPeoplePickerNavigationController) {
            <#cancelled#>
        }
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator()
    }

    func makeUIViewController(context: UIViewControllerRepresentableContext<PeoplePicker>) -> PeoplePicker.UIViewControllerType {
        let result = UIViewControllerType()
        result.delegate = context.coordinator
        return result
    }

    func updateUIViewController(_ uiViewController: PeoplePicker.UIViewControllerType, context: UIViewControllerRepresentableContext<PeoplePicker>) { }

}

EmbeddedContactPicker

import Foundation
import ContactsUI

protocol EmbeddedContactPickerViewControllerDelegate: class {
    func embeddedContactPickerViewControllerDidCancel(_ viewController: EmbeddedContactPickerViewController)
    func embeddedContactPickerViewController(_ viewController: EmbeddedContactPickerViewController, didSelect contact: CNContact)
}

class EmbeddedContactPickerViewController: UIViewController, CNContactPickerDelegate {
    weak var delegate: EmbeddedContactPickerViewControllerDelegate?

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.open(animated: animated)
    }

    private func open(animated: Bool) {
        let viewController = CNContactPickerViewController()
        viewController.delegate = self
        self.present(viewController, animated: false)
    }

    func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
        self.dismiss(animated: false) {
            self.delegate?.embeddedContactPickerViewControllerDidCancel(self)
        }
    }

    func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
        self.dismiss(animated: false) {
            self.delegate?.embeddedContactPickerViewController(self, didSelect: contact)
        }
    }

}

答案 1 :(得分:1)

我所做的只是将它包装在一个 NavigationController 中。也许不像arturigor的回答那么干净,但很容易工作。

func makeUIViewController(context: Context) -> some UIViewController {
    // needs to be wrapper in another controller. Else isn't displayed
    let navController = UINavigationController()
    let controller = CNContactPickerViewController()
    controller.delegate = delegate

    controller.predicateForEnablingContact = enablingPredicate

    navController.present(controller, animated: false, completion: nil)
    return navController
}

关于问题,它应该如何显示。我只是让它有条件地显示为组内的视图

Group {
    Text("Sharing is caring")

    if showContactPicker {
        ContactPicker(contactType: .email)
    }
}

答案 2 :(得分:0)

@youjin 解决方案在带有导航视图的工作表中使用时会出现问题。

例如,首先我展示了一个 .sheet 视图,在我拥有的这个 sheet 视图和 NavigationView 作为子视图中,然后,在所有这些中,我展示了联系人选择器。对于这种情况,当 Contact Picker 关闭时,还要关闭我的工作表视图父级。

我添加了一个 @Environment(\.presentationMode) 变量,并使用 Coordinator 方法将其关闭。在这里查看我的解决方案:

import SwiftUI
import ContactsUI

/**
Presents a CNContactPickerViewController view modally.
- Parameters:
    - showPicker: Binding variable for presenting / dismissing the picker VC
    - onSelectContact: Use this callback for single contact selection
    - onSelectContacts: Use this callback for multiple contact selections
*/
public struct ContactPicker: UIViewControllerRepresentable {
    @Environment(\.presentationMode) var presentationMode
    
    @Binding var showPicker: Bool
    @State private var viewModel = ContactPickerViewModel()
    public var onSelectContact: ((_: CNContact) -> Void)?
    public var onSelectContacts: ((_: [CNContact]) -> Void)?
    public var onCancel: (() -> Void)?
    
    public init(showPicker: Binding<Bool>, onSelectContact: ((_: CNContact) -> Void)? = nil, onSelectContacts: ((_: [CNContact]) -> Void)? = nil, onCancel: (() -> Void)? = nil) {
        self._showPicker = showPicker
        self.onSelectContact = onSelectContact
        self.onSelectContacts = onSelectContacts
        self.onCancel = onCancel
    }
    
    public func makeUIViewController(context: UIViewControllerRepresentableContext<ContactPicker>) -> ContactPicker.UIViewControllerType {
        let dummy = _DummyViewController()
        viewModel.dummy = dummy
        return dummy
    }
    
    public func updateUIViewController(_ uiViewController: _DummyViewController, context: UIViewControllerRepresentableContext<ContactPicker>) {

        guard viewModel.dummy != nil else {
            return
        }
        
        // able to present when
        // 1. no current presented view
        // 2. current presented view is being dismissed
        let ableToPresent = viewModel.dummy.presentedViewController == nil || viewModel.dummy.presentedViewController?.isBeingDismissed == true
        
        // able to dismiss when
        // 1. cncpvc is presented
        let ableToDismiss = viewModel.vc != nil
        
        if showPicker && viewModel.vc == nil && ableToPresent {
            let pickerVC = CNContactPickerViewController()
            pickerVC.delegate = context.coordinator
            viewModel.vc = pickerVC
            viewModel.dummy.present(pickerVC, animated: true)
        } else if !showPicker && ableToDismiss {
//            viewModel.dummy.dismiss(animated: true)
            self.viewModel.vc = nil
        }
    }
    
    public func makeCoordinator() -> Coordinator {
        if self.onSelectContacts != nil {
            return MultipleSelectionCoordinator(self)
        } else {
            return SingleSelectionCoordinator(self)
        }
    }
    
    public final class SingleSelectionCoordinator: NSObject, Coordinator {
        var parent : ContactPicker
        
        init(_ parent: ContactPicker){
            self.parent = parent
        }
        
        public func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
            parent.showPicker = false
            parent.onCancel?()
        }
        
        public func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
            parent.showPicker = false
            parent.onSelectContact?(contact)
        }
    }
    
    public final class MultipleSelectionCoordinator: NSObject, Coordinator {
        var parent : ContactPicker
        
        init(_ parent: ContactPicker){
            self.parent = parent
        }
        
        public func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
            parent.showPicker = false
            parent.onCancel?()
            parent.presentationMode.wrappedValue.dismiss()
        }
        
        public func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
            parent.showPicker = false
            parent.onSelectContacts?(contacts)
            parent.presentationMode.wrappedValue.dismiss()
        }
    }
}

class ContactPickerViewModel {
    var dummy: _DummyViewController!
    var vc: CNContactPickerViewController?
}

public protocol Coordinator: CNContactPickerDelegate {}

public class _DummyViewController: UIViewController {}