web-dev-qa-db-ja.com

CNContactViewController forUnknownContactは使用できません。インターフェースが破壊されます

[iOS 10で修正されたようです!]したがって、以下はiOS 9のみに適用されます...


私はAppleの新しい連絡先フレームワークを実験しており、CNContactViewControllerの3つの形式の1つに大きなバグを発見しました。周囲のインターフェースを破壊して、アプリが役に立たなくなるようにします。ユーザーが行き詰まっている。

このバグをわかりやすくするために、サンプルプロジェクトを https://github.com/mattneub/CNContactViewControllerBug に投稿しました。

実験するには、プロジェクトを実行し、次の手順を実行します。

  1. ボタン(不明な人)をタップします。

  2. 必要に応じてアクセスを許可します。

  3. ナビゲーションインターフェースに部分的な連絡先が表示されます(上部の[戻る]ボタンに注意してください)。

  4. [既存の連絡先に追加]をタップします。連絡先ピッカーが表示されます。

  5. [キャンセル]をタップします。実際にここから何をするかは問題ではありませんが、[キャンセル]をタップするのが最も簡単で、バグに到達する最も早い方法です。

  6. これで部分的な接触に戻りましたが、ナビゲーションインターフェースはなくなりました。ユーザーはこのインターフェースから脱出する方法がありません。 アプリはホースです。

わかりやすくするために、実行する必要がある手順のスクリーンショットを次に示します。

enter image description here

これを表示するには、[既存の連絡先に追加]をタップします。

enter image description here

これを表示するには、[キャンセル]をタップします。最初のスクリーンショットと同じであることに注意してください。ただし、ナビゲーションバーはなくなっています

enter image description here

私はこのバグを回避するために多くの方法を試しましたが、方法がないようです。私の知る限り、このウィンドウはフレームワークの「アウトプロセス」によって提示されており、アプリの一部ではありません。それを取り除くことはできません。

それで質問は何ですか?私はこれだと思います:このビューコントローラ(この形式)を使用可能にする方法を誰かに見せてもらえますか?見つからない回避策はありますか?

[〜#〜] edit [〜#〜]このバグはiOS 9.0で発生し、iOS 9.1でも引き続き存在します。コメントの中で、@ SergeySkopusは非推奨のアドレス帳フレームワークに切り替えても役に立たないと報告しています。バグはどこかにある根本的な構造にあります。

29
matt

Appleがついに私のバグレポートに重複と宣言することで対応したので、明らかにこれはバグです。

7
matt

カテゴリを使用してナビゲーションバーを表示または非表示にするために、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の一種であるかどうかを確認することで、ナビゲーションバーを非表示にするかどうかを決定できます。

8
Giammy

「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
Roudger

非常にプライベートな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

これは、アップルによるバグの多いXPCコントローラの使用を元に戻す「魔法の」ソリューションです。最新のCNコントローラーと、内部でABを使用するレガシーCNコントローラーの両方で、非常に多くの問題を解決します。

3
Leo Natan

まあ、私は問題を一時的に解決する3つの方法を見つけました。

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

enter image description here

0
iAugus

これは、私が一人ではなかったことを見てうれしかった問題の1つです。

CNContactViewController(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これが理想的な解決策ではないので、すぐに修正してください。

0
Itergator