Ответ 1
Очевидно, это ошибка, так как Apple, наконец, ответила на мой отчет об ошибке, объявив ее дубликатом.
[Появляется, чтобы быть исправлен в iOS 10!] Итак, что следует применять только к iOS 9...
Я экспериментировал с новой инфраструктурой контактов Apple, и я обнаружил огромную ошибку в одной из трех форм CNContactViewController. Он разрушает окружающий интерфейс, так что ваше приложение становится бесполезным; пользователь застревает.
Чтобы сделать эту ошибку легко увидеть, я разместил пример проекта в https://github.com/mattneub/CNContactViewControllerBug.
Чтобы поэкспериментировать, запустите проект и выполните следующие шаги:
Нажмите кнопку (Неизвестное лицо).
Предоставьте доступ по запросу.
В нашем навигационном интерфейсе отображается частичный контакт (обратите внимание на кнопку "Назад" вверху).
Коснитесь Добавить в существующий контакт. Появится панель выбора контактов.
Нажмите "Отмена". На самом деле это не имеет значения, что вы делаете здесь, но нажатие Cancel является самым простым и самым быстрым способом добраться до ошибки.
Теперь мы возвращаемся к частичному контакту, но интерфейс навигации исчез. У пользователя нет возможности выйти из этого интерфейса. Приложение закрыто.
Чтобы уточнить, вот скриншоты шагов, которые вам нужно предпринять:
Нажмите "Добавить" в "Существующий контакт", чтобы увидеть это:
Нажмите "Отмена", чтобы увидеть это; обратите внимание, что это то же самое, что и первый снимок экрана, но панель навигации исчезла:
Я пробовал много способов обойти эту ошибку, но, похоже, нет способа. Насколько я могу судить, это окно представлено каркасом "вне процесса" и не является частью вашего приложения. Вы не можете избавиться от него.
Так что же вопрос? Я предполагаю, что это: может ли кто-нибудь показать мне способ сделать этот контроллер (в этой форме) доступным? Существует ли обходное решение, которое я не нашел?
EDIT Эта ошибка появилась в 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 Я обнаружил, что этот метод вызывается частным [CNContactViewController isPresentingFullscreen:]
.
Проверяя, является ли self.topViewController
навигационного контроллера типом класса 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];
надеясь, что это поможет
Вас интересует очень частное исправление 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
Это "волшебное" решение, которое позволяет Apple использовать багги-контроллеры XPC. Решает так много проблем как для современных контроллеров CN
, так и для устаревших контроллеров AB
, которые внутренне используют CN
.
Ну, я нашел три способа решить проблему ВРЕМЕННО.
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 (контакт:).
Когда изображение или "общий контакт" были удалены, панель навигации в корневом узле 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 (анимированный:) Я вызываю функцию setnavigationbar (скрытый: анимированный:), чтобы скрыть панель навигации.
Я надеюсь, что Apple скоро это исправит, так как это менее идеальное решение.
Эта проблема может быть легко решена. Подкласс CNContactViewController и метод viewDidAppear сначала вызывают суперкласс, а затем сразу же после этого устанавливают метод leftBarButtonItem с помощью метода действий, который вызывает rejectViewController. Также убедитесь, что вы вставляете этот viewController в контроллер навигации.