[swift] CNContactViewController의 iOS 13.1에서 키보드 오버레이 작업 시트

iOS 13.0 및 이전 버전에서 CNContactViewController에 연락처를 추가하기 위해 예상대로 작동하므로 iOS 13.1에만 해당되는 것으로 보입니다. 수행중인 작업이없고 키보드가 닫히지 않습니다.



답변

위대한 해결 방법을 위해 @GxocT에 대한 찬사! 내 사용자들을 엄청나게 도와주었습니다.
그러나 @GxocT 솔루션을 기반으로 코드를 공유하고 싶었습니다.이 시나리오에서 다른 사람들에게 도움이되기를 바랍니다.

나는 CNContactViewControllerDelegate contactViewController(_:didCompleteWith:)취소 할 때뿐만 아니라 전화해야했습니다.

또한 내 코드가 아니 었 UIViewController으므로self.navigationController

나는 또한 그것을 도울 수있을 때 강제로 풀기를 사용하는 것을 좋아하지 않습니다. 나는 과거에 물렸으므로 if let셋업에서 체인을 연결 했습니다.

내가 한 일은 다음과 같습니다.

  1. CNContactViewControllerswizzle 기능을 확장 하고 배치하십시오
    .

  2. 내 경우에는 swizzle 함수 에서 연락처 컨트롤러 와 함께
    CNContactViewControllerDelegate대리자
    contactViewController(_:didCompleteWith:)를 호출 self하고
    self.contact객체를 객체에서

  3. 설정 코드에서 swizzleMethod 호출이
    대신 클래스 를 class_getInstanceMethod지정 하는지 확인하십시오.
    CNContactViewControllerself

그리고 스위프트 코드 :

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

키보드는 여전히 잠깐 동안 표시되지만 연락처 컨트롤러가 해제 된 직후에 삭제됩니다.
사과가 이것을 고치기를 바랍니다.


답변

키보드를 해제하는 방법을 찾을 수 없습니다. 그러나 적어도 내 방법을 사용하여 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)
        }
    }
}

추신 : reddit 주제에 대한 추가 정보를 찾을 수 있습니다 : https://www.reddit.com/r/swift/comments/dc9n3a/bug_with_cnviewcontroller_ios_131/


답변

사용자는 실제로 아래로 스 와이프하여 키보드를 닫은 다음 취소를 탭하고 작업 시트를 볼 수 있습니다. 따라서이 문제는 유감스럽고 분명히 버그 (및 버그 보고서를 제출했지만) 치명적이지는 않습니다 (확실히 말하면 해결 방법은 사용자가 쉽게 알 수 없습니다).

여기에 이미지 설명을 입력하십시오


답변

Xcode Simulator에서 테스트 한 iOS 13.4에서 수정되었습니다.


답변

그의 훌륭한 작업에 대해 @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

Swizzling에 익숙하지 않은 사람들은 matt에 의해이 게시물 을 시도 할 수 있습니다


답변

해결 방법으로 @GxocT를 보내 주셔서 감사하지만 여기에 게시 된 솔루션은 Reddit에 게시 한 솔루션과 다릅니다.

Reddit에있는 것이 나를 위해 일합니다. 이것은 그렇지 않으므로 여기에 다시 게시하고 싶습니다. 차이점은 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)
    }
}


답변