CNContactViewControllerのiOS 13.1のキーボードオーバーレイアクションシート


12

これはiOS 13.1に固有のようです。iOS13.0以前のバージョンではCNContactViewControllerに連絡先を追加すると期待どおりに機能するため、「キャンセル」すると、アクションシートがキーボードで重なります。アクションは実行されず、キーボードは終了しません。

回答:


5

素晴らしい回避策を@GxocTに感謝します!ユーザーを大いに助けました。
しかし、@ GxocTソリューションに基づいて自分のコードを共有したかったのですが、このシナリオで他の人に役立つことを期待しています。

CNContactViewControllerDelegate contactViewController(_:didCompleteWith:)キャンセル時に(そして完了時に)呼び出される必要がありました。

また、私のコードはないUIViewControllerのでself.navigationController

私はそれを助けることができるときに強制アンラップを使うのも好きではありません。私は過去に噛まれたのでif let、セットアップでs をチェーンしました

これが私がしたことです:

  1. そこにCNContactViewControllerスウィズル機能を拡張して配置し
    ます。

  2. 私の場合、スウィズル関数では、連絡先コントローラのオブジェクトとオブジェクトを使用して
    CNContactViewControllerDelegateデリゲート
    contactViewController(_:didCompleteWith:)を呼び出すだけです。self
    self.contact

  3. セットアップコードで、 代わりにクラスをclass_getInstanceMethod指定するswizzleMethod呼び出しを確認し ますCNContactViewControllerself

そしてSwiftコード:

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

キーボードは一瞬表示されますが、連絡先コントローラーが終了した直後にドロップします。
アップルがこれを修正することを望みます


もちろん、コードをコンパクトにするために強制アンラップを使用します。可能な場合、およびクラッシュにつながる可能性がある場合は、それらを回避する必要があります。btw自分にself.contactをデリゲートに渡すのが正しいかどうかわかりません。フローをキャンセルすると作成されない可能性がありますps:強制アンラップを回避するために実装を変更しました:D
GxocT

@GxocT-力のラップについて合意。そして、それらは必ずしもひどいわけではありませんが、他の人はあなたのようではなく、リスクを認識せずに常にそれらを使用するかもしれません;)。代わりに聞かせたら好きです!私が100%ではないとき、それはnilではないでしょう。self.contactについて-その真実Appleのlibコードが通常デリゲートに内部的に渡すものは知りませんが、self.contactには連絡先データがあり、CNContactViewController docはそれが「連絡先が表示されている」と言っているので、使用しても問題ないようです。私のコードは、完了デリゲートで渡された連絡先を実際には使用しないため、拡張機能でnilを渡すだけで済みます。
バレット

CNContactViewControllerのコンテンツ(テキストビューとキーボードを含む)は、別のプロセスにある必要があります。このビューコントローラーのXcodeで「ビュー階層」を使用できる場合、コンテンツが表示されないことがあります。その結果、キーボードやテキストビューを制御できません。
WildCat

5

キーボードを閉じる方法が見つかりませんでした。しかし、少なくとも、私のメソッドを使用してViewControllerをポップできます。

  1. 理由はわかりませんが、CNContactViewControllerでキーボードを閉じることはできません。endEditing:を試して、新しいUITextField firstResponderなどを作成しました。何もうまくいきませんでした。
  2. 「キャンセル」ボタンのアクションを変更してみました。このボタンはNavigationControllerスタックにありますが、何かを入力するたびにアクションが変更されます。
  3. 最後に、メソッドのスウィズリングを使用しました。前述のようにキーボードを閉じる方法は見つかりませんでしたが、「キャンセル」ボタンを押すと、CNContactViewControllerを閉じることができます。
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:redditトピックに関する追加情報を見つけることができます:https : //www.reddit.com/r/swift/comments/dc9n3a/bug_with_cnviewcontroller_ios_131/


2

ユーザーは実際に下にスワイプしてキーボードを非表示にし、[キャンセル]をタップしてアクションシートを表示できます。したがって、この問題は残念なことであり、間違いなくバグです(私はバグレポートを提出しました)。

ここに画像の説明を入力してください


あなたが提出したバグレポートにリンクしてもらえますか?
Paaske、


1

@Gxoctの優れた作業に感謝します。これは、で作業している人にとって非常に役立つ質問と投稿だと思いますCNContactViewController。私もこの問題を抱えていました(これまで)が、目的cです。上記のSwiftコードを目的cに解釈します。

- (void)viewDidLoad {
    [super viewDidLoad];
    Class class = [CNContactViewController class];

    SEL originalSelector = @selector(editCancel:);
    SEL swizzledSelector = @selector(dismiss); // we will gonna access this method & redirect the delegate via this method

    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

    BOOL didAddMethod =
        class_addMethod(class,
            originalSelector,
            method_getImplementation(swizzledMethod),
            method_getTypeEncoding(swizzledMethod));

    if (didAddMethod) {
        class_replaceMethod(class,
            swizzledSelector,
            method_getImplementation(originalMethod),
            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

CNContactViewController却下にアクセスするためのカテゴリを作成します。

@implementation CNContactViewController (Test)

- (void) dismiss{
    [self.delegate contactViewController:self didCompleteWithContact:self.contact];
}

@end

スウィズリングに慣れていない人はマットでこの投稿を試してみてください


0

@GxocTの回避策に感謝しますが、ここに投稿されたソリューションは、Redditに投稿したものとは異なります。

Redditの1つは私には有効ですが、これは機能しないため、ここに再投稿します。違いはswizzledMethodとの行にあります:

   let swizzledMethod = class_getInstanceMethod(object_getClass(self), swizzledSelector) {

更新されたコード全体は次のとおりです。

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(self), 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)
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.