This seems to be specific to iOS 13.1, as it works as expected on iOS 13.0 and earlier versions to add a contact in CNContactViewController, if I 'Cancel', the action sheet is overlapping by keyboard. No actions getting performed and keyboard is not dismissing.
NOTE: This bug is now fixed. This question and answer were applicable only to some particular versions of iOS (a limited range of iOS 13 versions).
The user can in fact swipe down to dismiss the keyboard and then tap Cancel and see the action sheet. So this issue is regrettable and definitely a bug (and I have filed a bug report) but not fatal (though, to be sure, the workaround is not trivial for the user to discover).
Fixed in iOS 13.4 Tested in Xcode Simulator
Kudos to @GxocT for the the great workaround! Helped my users immensely.
But I wanted to share my code based on @GxocT solution hoping it will help others in this scenario.
I needed my CNContactViewControllerDelegate
contactViewController(_:didCompleteWith:)
to be called on cancel (as well as done).
Also my code was not in a UIViewController
so there is no self.navigationController
I also dont like using force unwraps when I can help it. I have been bitten in the past so I chained if let
s in the setup
Here's what I did:
Extend CNContactViewController
and place the swizzle function in
there.
In my case in the swizzle function just call theCNContactViewControllerDelegate
delegatecontactViewController(_:didCompleteWith:)
with self
andself.contact
object from the contact controller
In the setup code, make sure the swizzleMethod call to
class_getInstanceMethod
specifies the CNContactViewController
class instead of self
And the Swift code:
class MyClass: CNContactViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.changeImplementation()
}
func changeCancelImplementation() {
let originalSelector = Selector(("editCancel:"))
let swizzledSelector = #selector(CNContactViewController.cancelHack)
if let originalMethod = class_getInstanceMethod(object_getClass(CNContactViewController()), originalSelector),
let swizzledMethod = class_getInstanceMethod(object_getClass(CNContactViewController()), swizzledSelector) {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) {
// dismiss the contacts controller as usual
viewController.dismiss(animated: true, completion: nil)
// do other stuff when your contact is canceled or saved
...
}
}
extension CNContactViewController {
@objc func cancelHack() {
self.delegate?.contactViewController?(self, didCompleteWith: self.contact)
}
}
The keyboard still shows momentarily but drops just after the Contacts controller dismisses.
Lets hope apple fixes this
I couldn't find a way to dismiss keyboard. But at least you can pop ViewController using my method.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
changeImplementation()
}
@IBAction func userPressedButton(_ sender: Any) {
let controller = CNContactViewController(forNewContact: nil)
controller.delegate = self
navigationController?.pushViewController(controller, animated: true)
}
@objc func popController() {
self.navigationController?.popViewController(animated: true)
}
func changeImplementation() {
let originalSelector = Selector("editCancel:")
let swizzledSelector = #selector(self.popController)
if let originalMethod = class_getInstanceMethod(object_getClass(CNContactViewController()), originalSelector),
let swizzledMethod = class_getInstanceMethod(object_getClass(CNContactViewController()), swizzledSelector) {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
PS: You can find additional info on reddit topic: https://www.reddit.com/r/swift/comments/dc9n3a/bug_with_cnviewcontroller_ios_131/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With