[ios] 제시된 뷰 컨트롤러가 닫힐 때 감지

VC2라는 뷰 컨트롤러 클래스의 인스턴스가 있다고 가정 해 보겠습니다. VC2에는 자동으로 해제되는 “취소”버튼이 있습니다. 하지만 “취소”버튼이 트리거되면 콜백을 감지하거나 수신 할 수 없습니다. VC2는 블랙 박스입니다.

뷰 컨트롤러 (VC1이라고 함)는 presentViewController:animated:completion:메서드를 사용하여 VC2를 표시 합니다.

VC2가 해제되었을 때 VC1이 감지해야하는 옵션은 무엇입니까?

편집 : @rory mckinnel의 의견과 @NicolasMiari의 답변에서 다음을 시도했습니다.

VC2에서 :

-(void)cancelButton:(id)sender
{
    [self dismissViewControllerAnimated:YES completion:^{

    }];
//    [super dismissViewControllerAnimated:YES completion:^{
//        
//    }];
}

VC1에서 :

//-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
- (void)dismissViewControllerAnimated:(BOOL)flag
                           completion:(void (^ _Nullable)(void))completion
{
    NSLog(@"%s ", __PRETTY_FUNCTION__);
    [super dismissViewControllerAnimated:flag completion:completion];
//    [self dismissViewControllerAnimated:YES completion:^{
//        
//    }];
}

그러나 dismissViewControllerAnimatedVC1에서 호출되지 않았습니다.



답변

문서에 따르면 발표하는 컨트롤러는 실제 해제를 담당합니다. 제시된 컨트롤러가 스스로 닫히면 발표자에게이를 수행하도록 요청합니다. 따라서 VC1 컨트롤러에서 dismissViewControllerAnimated를 재정의하면 VC2에서 취소를 누를 때 호출 될 것이라고 믿습니다. 해제를 감지 한 다음 실제 해제를 수행 할 슈퍼 클래스 버전을 호출합니다.

토론에서 알 수 있듯이 이것은 작동하지 않는 것 같습니다. 오히려 대신 호출하는 기본 메커니즘에 의존하는 것보다 dismissViewControllerAnimated:completionVC2 자체 통화 dismissViewControllerAnimated:completionself.presentingViewControllerVC2한다. 그러면 재정의가 직접 호출됩니다.

더 나은 접근 방식은 VC2가 모달 컨트롤러가 완료 될 때 호출되는 블록을 제공하도록하는 것입니다.

따라서 VC2에서 이름과 함께 블록 속성을 제공하십시오 onDoneBlock.

VC1에서는 다음과 같이 발표합니다.

  • VC1에서 VC2를 만듭니다.

  • VC2에 대한 완료 핸들러를 다음과 같이 설정하십시오. VC2.onDoneBlock={[VC2 dismissViewControllerAnimated:YES completion:nil]};

  • [self presentViewController : VC2 animated : YES 완료 : nil]을 사용하여 VC2 컨트롤러를 정상적으로 표시합니다.

  • VC2에서 취소 대상 작업 호출에서 self.onDoneBlock();

결과는 VC2가 누구에게나 그것이 완료되었음을 알려줍니다. onDoneBlock모달이 완료되었는지, 취소되었는지, 성공했는지 등을 나타내는 인수를 갖도록를 확장 할 수 있습니다 .


답변

이 목적으로 사용할 수있는 UIViewController라는 특수 부울 속성 isBeingDismissed이 있습니다.

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if isBeingDismissed {
        // TODO: Do your stuff here.
    }
}


답변

블록 속성 사용

VC2에서 선언

var onDoneBlock : ((Bool) -> Void)?

VC1에서 설정

VC2.onDoneBlock = { result in
                // Do something
            }

해고하려고 할 때 VC2를 호출하십시오.

onDoneBlock!(true)


답변

프레젠테이션 프레젠테이션보기 컨트롤러는 모두 프레젠테이션보기 컨트롤러 dismissViewController:animated:를 해제하기 위해 호출 할 수 있습니다 .

전자의 옵션은 디자인 적으로 “올바른”옵션입니다. 동일한 “부모”뷰 컨트롤러가 모달 ( “하위”) 뷰 컨트롤러를 표시하고 해제하는 역할을합니다.

그러나 후자가 더 편리합니다. 일반적으로 “닫기”버튼이 제시된 뷰 컨트롤러의 뷰에 연결되고 뷰 컨트롤러가 작업 대상으로 설정되어 있습니다.

이전 접근 방식을 채택하는 경우, 해제가 발생하는 프레젠테이션 뷰 컨트롤러의 코드 줄을 이미 알고 있습니다. 바로 뒤에 코드를 실행 dismissViewControllerAnimated:completion:하거나 완료 블록 내에서 코드를 실행합니다 .

후자의 접근 방식을 채택하는 경우 (제공된 뷰 컨트롤러가 자동으로 해제 됨), dismissViewControllerAnimated:completion:제시된 뷰 컨트롤러에서 호출 하면 UIKit이 제시하는 뷰 컨트롤러에서 해당 메서드를 차례로 호출하게됩니다.

토론

프리젠 테이션 뷰 컨트롤러는 자신이 제시 한 뷰 컨트롤러를 해제 할 책임이 있습니다. 제시된 뷰 컨트롤러 자체에서이 메서드를 호출하면 UIKit은 제시 뷰 컨트롤러에게 해제를 처리하도록 요청합니다.

( 출처 : UIViewController Class Reference )

따라서 이러한 이벤트를 가로 채기 위해 프레젠테이션 뷰 컨트롤러 에서 해당 메서드를 재정의 할 수 있습니다 .

override func dismiss(animated flag: Bool,
                         completion: (() -> Void)?) {
    super.dismiss(animated: flag, completion: completion)

    // Your custom code here...
}


답변

extension Foo: UIAdaptivePresentationControllerDelegate {
    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
        //call whatever you want
    }
}

vc.presentationController?.delegate = foo


답변

unwind segue를 사용하여이 작업을 수행 할 수 있으며 dismissModalViewController를 사용할 필요가 없습니다. VC1에서 unwind segue 방법을 정의하십시오.

unwind segue를 만드는 방법에 대한이 링크, https://stackoverflow.com/a/15839298/5647055를 참조하십시오 .

unwind segue가 설정되었다고 가정하면 “Cancel”버튼에 대해 정의 된 작업 방법에서 다음과 같이 segue를 수행 할 수 있습니다.

[self performSegueWithIdentifier:@"YourUnwindSegueName" sender:nil];

이제 VC2에서 “취소”버튼을 누를 때마다 해제되고 VC1이 나타납니다. 또한 VC1에서 정의한 unwind 메서드를 호출합니다. 이제 제시된 뷰 컨트롤러가 언제 닫혔는지 알 수 있습니다.


답변

다음을 사용하여 코디네이터에게 뷰 컨트롤러가 “완료”되었음을 알립니다. 이것은 AVPlayerViewControllertvOS 애플리케이션 의 하위 클래스에서 사용되며 playerVC 해제 전환이 완료된 후에 호출됩니다.

class PlayerViewController: AVPlayerViewController {
  var onDismissal: (() -> Void)?

  override func beginAppearanceTransition(_ isAppearing: Bool, animated: Bool) {
    super.beginAppearanceTransition(isAppearing, animated: animated)
    transitionCoordinator?.animate(alongsideTransition: nil,
      completion: { [weak self] _ in
         if !isAppearing {
            self?.onDismissal?()
        }
    })
  }
}