Подтвердить что ты не робот

CNContactViewController forUnknownContact неприменимо, разрушает интерфейс

[Появляется, чтобы быть исправлен в iOS 10!] Итак, что следует применять только к iOS 9...


Я экспериментировал с новой инфраструктурой контактов Apple, и я обнаружил огромную ошибку в одной из трех форм CNContactViewController. Он разрушает окружающий интерфейс, так что ваше приложение становится бесполезным; пользователь застревает.

Чтобы сделать эту ошибку легко увидеть, я разместил пример проекта в https://github.com/mattneub/CNContactViewControllerBug.

Чтобы поэкспериментировать, запустите проект и выполните следующие шаги:

  • Нажмите кнопку (Неизвестное лицо).

  • Предоставьте доступ по запросу.

  • В нашем навигационном интерфейсе отображается частичный контакт (обратите внимание на кнопку "Назад" вверху).

  • Коснитесь Добавить в существующий контакт. Появится панель выбора контактов.

  • Нажмите "Отмена". На самом деле это не имеет значения, что вы делаете здесь, но нажатие Cancel является самым простым и самым быстрым способом добраться до ошибки.

  • Теперь мы возвращаемся к частичному контакту, но интерфейс навигации исчез. У пользователя нет возможности выйти из этого интерфейса. Приложение закрыто.

Чтобы уточнить, вот скриншоты шагов, которые вам нужно предпринять:

введите описание изображения здесь

Нажмите "Добавить" в "Существующий контакт", чтобы увидеть это:

введите описание изображения здесь

Нажмите "Отмена", чтобы увидеть это; обратите внимание, что это то же самое, что и первый снимок экрана, но панель навигации исчезла:

введите описание изображения здесь

Я пробовал много способов обойти эту ошибку, но, похоже, нет способа. Насколько я могу судить, это окно представлено каркасом "вне процесса" и не является частью вашего приложения. Вы не можете избавиться от него.

Так что же вопрос? Я предполагаю, что это: может ли кто-нибудь показать мне способ сделать этот контроллер (в этой форме) доступным? Существует ли обходное решение, которое я не нашел?

EDIT Эта ошибка появилась в iOS 9.0 и все еще присутствует в iOS 9.1. В комментарии @SergeySkopus сообщает, что переход на устаревшую структуру адресной книги не помогает; ошибка находится в базовой структуре где-то.

4b9b3361

Ответ 1

Очевидно, это ошибка, так как Apple, наконец, ответила на мой отчет об ошибке, объявив ее дубликатом.

Ответ 2

Я скрыл метод 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, вы можете решить, скрывать или нет панель навигации.

Ответ 3

Единственным способом, с помощью которого я могу использовать "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];

надеясь, что это поможет

Ответ 4

Вас интересует очень частное исправление 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.

Ответ 5

Ну, я нашел три способа решить проблему ВРЕМЕННО.

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

введите описание изображения здесь

Ответ 6

Это одна из тех проблем, которые я был рад видеть, что я не одинок.

У меня такая же проблема при отображении контакта с помощью 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 скоро это исправит, так как это менее идеальное решение.

Ответ 7

Эта проблема может быть легко решена. Подкласс CNContactViewController и метод viewDidAppear сначала вызывают суперкласс, а затем сразу же после этого устанавливают метод leftBarButtonItem с помощью метода действий, который вызывает rejectViewController. Также убедитесь, что вы вставляете этот viewController в контроллер навигации.