CNContactViewController forUnknownContact 不可用,破坏接口
CNContactViewController forUnknownContact unusable, destroys interface
[似乎已在 iOS 10 中修复!] 因此以下内容仅适用于 iOS 9...
我一直在尝试使用 Apple 的新联系人框架,并且在 CNContactViewController 的三种形式之一中发现了一个巨大的错误。它破坏了周围的界面,使您的应用变得无用;用户卡住了。
为了让这个错误更容易看到,我在 https://github.com/mattneub/CNContactViewControllerBug 上发布了一个示例项目。
要进行实验,运行 项目并执行以下步骤:
点击按钮(未知人物)。
根据要求授予访问权限。
您会在我们的导航界面中看到部分联系人(请注意顶部的后退按钮)。
点击添加到现有联系人。出现联系人选择器。
点击取消。从这里开始做什么实际上并不重要,但点击“取消”是最简单的,也是解决错误的最快方法。
我们回到了部分联系方式,但是导航界面没有了。用户无法从该界面中退出。 该应用程序已关闭。
澄清一下,这里是您需要执行的步骤的屏幕截图:
点击添加到现有联系人以查看:
点击取消即可看到;观察它与第一个屏幕截图相同,但是导航栏不见了:
我已经尝试了很多方法来解决这个错误,但似乎没有办法。据我所知,这个 window 是由框架 "out-of-process" 提供的,不是您应用程序的一部分。你无法摆脱它。
所以问题是什么?我想是这样的:任何人都可以告诉我一种使这个视图控制器(以这种形式)可用的方法吗?有没有我没有找到的解决方法?
编辑 这个错误出现在 iOS 9.0 中并且仍然存在于 iOS 9.1 中。在评论中,@SergeySkopus 报告说切换到已弃用的地址簿框架没有帮助;该错误在某处的底层结构中。
显然这是一个错误,因为 Apple 最终通过声明重复来回应我的错误报告。
我已经使用类别隐藏了用于显示或隐藏导航栏的 UINavigationController 方法:
@interface UINavigationController (contacts)
@end
@implementation UINavigationController (contacts)
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated {
NSLog(@"Hide: %d", hidden);
}
@end
这样CNContactViewController就无法让导航栏消失了。在 NSLog 上设置断点我发现这个方法被 private [CNContactViewController isPresentingFullscreen:]
.
调用了
通过检查导航控制器的 self.topViewController
是否是 class CNContactViewController
你可以决定是否隐藏导航栏。
我发现使 "CNContactViewController forUnknownContact" 可用的唯一方法是放弃导航栏并使用工具栏退出模态视图,如下所示(在 Objective C 中):
CNContactViewController *picker = [CNContactViewController viewControllerForUnknownContact: newContact];
picker.delegate = self;
UINavigationController *newNavigationController = [[UINavigationController alloc] initWithRootViewController:picker];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:@"Close" style:UIBarButtonItemStyleDone target:self action:@selector(YourDismissFunction)];
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
[picker setToolbarItems:[[NSArray alloc] initWithObjects:flexibleSpace, doneButton, flexibleSpace, nil] animated:NO];
newNavigationController.toolbarHidden = NO;
picker.edgesForExtendedLayout = UIRectEdgeNone;
[self presentViewController:newNavigationController animated:YES completion:nil];
希望能帮到你
这个问题很容易解决。 Subclass CNContactViewController 并在 viewDidAppear 方法中首先调用 super class,然后立即使用调用 dismissViewController 的操作方法设置 leftBarButtonItem。还要确保将 viewController 嵌入到导航控制器中。
您对非常私密的 API 修复感兴趣吗?
@implementation CNContactViewController (Debug)
+ (void)load
{
Method m1 = class_getInstanceMethod([CNContactViewController class], NSSelectorFromString(@"".underscore.s.h.o.u.l.d.B.e.O.u.t.O.f.P.r.o.c.e.s.s));
Method m2 = class_getInstanceMethod([CNContactViewController class], @selector(checkStatus));
method_exchangeImplementations(m1, m2);
}
- (BOOL)checkStatus
{
//Leo: Fix bug where in-process contact view controller crashes if there is no access to local contacts.
BOOL result;
if([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusAuthorized)
{
result = NO;
}
else {
result = YES;
}
return result;
}
@end
这是一个 "magic" 解决方案,可以恢复 Apple 对有问题的 XPC 控制器的使用。解决了现代 CN
控制器以及内部使用 CN
控制器的遗留 AB
控制器中的许多问题。
好吧,我找到了三种方法来暂时解决这个问题。
Swift 2.2 版本:
选项 1:摇动设备以显示导航栏或直接关闭
class CustomContactViewController: CNContactViewController {
override func viewDidLoad() {
super.viewDidLoad()
UIApplication.sharedApplication().applicationSupportsShakeToEdit = true
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
becomeFirstResponder()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
resignFirstResponder()
UIApplication.sharedApplication().applicationSupportsShakeToEdit = false
}
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
navigationController?.setNavigationBarHidden(false, animated: true)
// or just dismiss
// dismissViewControllerAnimated(true, completion: nil)
// or pop
// navigationController?.popViewControllerAnimated(true)
}
}
选项 2:设置一个计时器以强制显示导航栏。但是……这也带来了新的问题,联系人头像无法编辑分享。
class CustomContactViewController: CNContactViewController {
var timer: NSTimer?
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(showNavigationBar), userInfo: nil, repeats: true)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
timer?.fire()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
timer?.invalidate()
}
@objc private func showNavigationBar() {
navigationController?.setNavigationBarHidden(false, animated: true)
}
}
选项 3:在最顶部的视图上创建关闭按钮。
class CustomContactViewController: CNContactViewController {
override func viewDidLoad() {
super.viewDidLoad()
configureDismissButton()
}
private func configureDismissButton() {
guard let topView = UIApplication.topMostViewController?.view else { return }
let button = UIButton()
button.setImage(UIImage(named: "close"), forState: .Normal)
button.addTarget(self, action: #selector(dismissViewController), forControlEvents: .TouchUpInside)
topView.addSubview(button)
// just use SnapKit to set AutoLayout
button.snp_makeConstraints { (make) in
make.width.height.equalTo(36)
make.bottom.equalTo(8)
make.left.equalTo(-8)
}
}
@objc private func dismissViewController() {
dismissViewControllerAnimated(true, completion: nil)
}
var topMostViewController: UIViewController? {
var topController = UIApplication.sharedApplication().keyWindow?.rootViewController
while topController?.presentedViewController != nil {
topController = topController?.presentedViewController
}
return topController
}
}
这是我很高兴看到我并不孤单的问题之一。
我在使用 CNContactViewController(contact:) 显示联系人时遇到同样的问题。
当点击图像或 'share contact' 时,根 CNContactViewController 中的导航栏将消失,使用户卡住。从 iOS 9.3.3.
开始,此问题尚未得到修复
我目前的解决方案是使用 uitoolbar。问题是它一直出现在底部,即使全屏显示联系人的图像数据也是如此。
// initialise new contact view controller to display with contact
let contactVC = CNContactViewController(forContact: contact!)
// set view controller delegate
contactVC.delegate = self
// set view controller contact store
contactVC.contactStore = self.store
// enable actions
contactVC.allowsActions = true
// disable editing
contactVC.allowsEditing = false
// add cancel button
let cancelButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: #selector(dismissContactVC(_:)))
// add flexible space
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
// add to toolbar
contactVC.setToolbarItems([flexibleSpace, cancelButton, flexibleSpace], animated: false)
// contact view controller must be embedded in navigation controller
// initialise navigation controller with contact view controller as root
let navigationVC = SubClassNavigationController(rootViewController: contactVC)
// show toolbar
navigationVC.setToolbarHidden(false, animated: false)
// set navigation presentation style
navigationVC.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
// present view controller
self.presentViewController(navigationVC, animated: true, completion: nil)
在此之后,当您首次显示 cncontactviewcontroller 时会出现一个空白导航栏,因此为了删除它,我将 uinavigationcontroller 子类化,并在 viewWillAppear(animated:) 中调用函数 setnavigationbar(hidden: animated:) 来隐藏导航栏。
我希望 Apple 尽快解决这个问题,因为这是一个不太理想的解决方案。
[似乎已在 iOS 10 中修复!] 因此以下内容仅适用于 iOS 9...
我一直在尝试使用 Apple 的新联系人框架,并且在 CNContactViewController 的三种形式之一中发现了一个巨大的错误。它破坏了周围的界面,使您的应用变得无用;用户卡住了。
为了让这个错误更容易看到,我在 https://github.com/mattneub/CNContactViewControllerBug 上发布了一个示例项目。
要进行实验,运行 项目并执行以下步骤:
点击按钮(未知人物)。
根据要求授予访问权限。
您会在我们的导航界面中看到部分联系人(请注意顶部的后退按钮)。
点击添加到现有联系人。出现联系人选择器。
点击取消。从这里开始做什么实际上并不重要,但点击“取消”是最简单的,也是解决错误的最快方法。
我们回到了部分联系方式,但是导航界面没有了。用户无法从该界面中退出。 该应用程序已关闭。
澄清一下,这里是您需要执行的步骤的屏幕截图:
点击添加到现有联系人以查看:
点击取消即可看到;观察它与第一个屏幕截图相同,但是导航栏不见了:
我已经尝试了很多方法来解决这个错误,但似乎没有办法。据我所知,这个 window 是由框架 "out-of-process" 提供的,不是您应用程序的一部分。你无法摆脱它。
所以问题是什么?我想是这样的:任何人都可以告诉我一种使这个视图控制器(以这种形式)可用的方法吗?有没有我没有找到的解决方法?
编辑 这个错误出现在 iOS 9.0 中并且仍然存在于 iOS 9.1 中。在评论中,@SergeySkopus 报告说切换到已弃用的地址簿框架没有帮助;该错误在某处的底层结构中。
显然这是一个错误,因为 Apple 最终通过声明重复来回应我的错误报告。
我已经使用类别隐藏了用于显示或隐藏导航栏的 UINavigationController 方法:
@interface UINavigationController (contacts)
@end
@implementation UINavigationController (contacts)
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated {
NSLog(@"Hide: %d", hidden);
}
@end
这样CNContactViewController就无法让导航栏消失了。在 NSLog 上设置断点我发现这个方法被 private [CNContactViewController isPresentingFullscreen:]
.
通过检查导航控制器的 self.topViewController
是否是 class CNContactViewController
你可以决定是否隐藏导航栏。
我发现使 "CNContactViewController forUnknownContact" 可用的唯一方法是放弃导航栏并使用工具栏退出模态视图,如下所示(在 Objective C 中):
CNContactViewController *picker = [CNContactViewController viewControllerForUnknownContact: newContact];
picker.delegate = self;
UINavigationController *newNavigationController = [[UINavigationController alloc] initWithRootViewController:picker];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:@"Close" style:UIBarButtonItemStyleDone target:self action:@selector(YourDismissFunction)];
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
[picker setToolbarItems:[[NSArray alloc] initWithObjects:flexibleSpace, doneButton, flexibleSpace, nil] animated:NO];
newNavigationController.toolbarHidden = NO;
picker.edgesForExtendedLayout = UIRectEdgeNone;
[self presentViewController:newNavigationController animated:YES completion:nil];
希望能帮到你
这个问题很容易解决。 Subclass CNContactViewController 并在 viewDidAppear 方法中首先调用 super class,然后立即使用调用 dismissViewController 的操作方法设置 leftBarButtonItem。还要确保将 viewController 嵌入到导航控制器中。
您对非常私密的 API 修复感兴趣吗?
@implementation CNContactViewController (Debug)
+ (void)load
{
Method m1 = class_getInstanceMethod([CNContactViewController class], NSSelectorFromString(@"".underscore.s.h.o.u.l.d.B.e.O.u.t.O.f.P.r.o.c.e.s.s));
Method m2 = class_getInstanceMethod([CNContactViewController class], @selector(checkStatus));
method_exchangeImplementations(m1, m2);
}
- (BOOL)checkStatus
{
//Leo: Fix bug where in-process contact view controller crashes if there is no access to local contacts.
BOOL result;
if([CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts] == CNAuthorizationStatusAuthorized)
{
result = NO;
}
else {
result = YES;
}
return result;
}
@end
这是一个 "magic" 解决方案,可以恢复 Apple 对有问题的 XPC 控制器的使用。解决了现代 CN
控制器以及内部使用 CN
控制器的遗留 AB
控制器中的许多问题。
好吧,我找到了三种方法来暂时解决这个问题。
Swift 2.2 版本:
选项 1:摇动设备以显示导航栏或直接关闭
class CustomContactViewController: CNContactViewController {
override func viewDidLoad() {
super.viewDidLoad()
UIApplication.sharedApplication().applicationSupportsShakeToEdit = true
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
becomeFirstResponder()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
resignFirstResponder()
UIApplication.sharedApplication().applicationSupportsShakeToEdit = false
}
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
navigationController?.setNavigationBarHidden(false, animated: true)
// or just dismiss
// dismissViewControllerAnimated(true, completion: nil)
// or pop
// navigationController?.popViewControllerAnimated(true)
}
}
选项 2:设置一个计时器以强制显示导航栏。但是……这也带来了新的问题,联系人头像无法编辑分享。
class CustomContactViewController: CNContactViewController {
var timer: NSTimer?
override func viewDidLoad() {
super.viewDidLoad()
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(showNavigationBar), userInfo: nil, repeats: true)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
timer?.fire()
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
timer?.invalidate()
}
@objc private func showNavigationBar() {
navigationController?.setNavigationBarHidden(false, animated: true)
}
}
选项 3:在最顶部的视图上创建关闭按钮。
class CustomContactViewController: CNContactViewController {
override func viewDidLoad() {
super.viewDidLoad()
configureDismissButton()
}
private func configureDismissButton() {
guard let topView = UIApplication.topMostViewController?.view else { return }
let button = UIButton()
button.setImage(UIImage(named: "close"), forState: .Normal)
button.addTarget(self, action: #selector(dismissViewController), forControlEvents: .TouchUpInside)
topView.addSubview(button)
// just use SnapKit to set AutoLayout
button.snp_makeConstraints { (make) in
make.width.height.equalTo(36)
make.bottom.equalTo(8)
make.left.equalTo(-8)
}
}
@objc private func dismissViewController() {
dismissViewControllerAnimated(true, completion: nil)
}
var topMostViewController: UIViewController? {
var topController = UIApplication.sharedApplication().keyWindow?.rootViewController
while topController?.presentedViewController != nil {
topController = topController?.presentedViewController
}
return topController
}
}
这是我很高兴看到我并不孤单的问题之一。
我在使用 CNContactViewController(contact:) 显示联系人时遇到同样的问题。
当点击图像或 'share contact' 时,根 CNContactViewController 中的导航栏将消失,使用户卡住。从 iOS 9.3.3.
开始,此问题尚未得到修复我目前的解决方案是使用 uitoolbar。问题是它一直出现在底部,即使全屏显示联系人的图像数据也是如此。
// initialise new contact view controller to display with contact
let contactVC = CNContactViewController(forContact: contact!)
// set view controller delegate
contactVC.delegate = self
// set view controller contact store
contactVC.contactStore = self.store
// enable actions
contactVC.allowsActions = true
// disable editing
contactVC.allowsEditing = false
// add cancel button
let cancelButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Cancel, target: self, action: #selector(dismissContactVC(_:)))
// add flexible space
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
// add to toolbar
contactVC.setToolbarItems([flexibleSpace, cancelButton, flexibleSpace], animated: false)
// contact view controller must be embedded in navigation controller
// initialise navigation controller with contact view controller as root
let navigationVC = SubClassNavigationController(rootViewController: contactVC)
// show toolbar
navigationVC.setToolbarHidden(false, animated: false)
// set navigation presentation style
navigationVC.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
// present view controller
self.presentViewController(navigationVC, animated: true, completion: nil)
在此之后,当您首次显示 cncontactviewcontroller 时会出现一个空白导航栏,因此为了删除它,我将 uinavigationcontroller 子类化,并在 viewWillAppear(animated:) 中调用函数 setnavigationbar(hidden: animated:) 来隐藏导航栏。
我希望 Apple 尽快解决这个问题,因为这是一个不太理想的解决方案。