[iphone] 탐색 표시 줄에서 ‘뒤로’버튼을 누를 때 감지

Navbar에서 뒤로 버튼 (이전 화면으로 돌아 가기, 부모보기로 돌아 가기) 버튼을 누르면 몇 가지 작업을 수행해야합니다.

이벤트를 포착하고 화면이 사라지기 전에 데이터를 일시 중지하고 저장하기 위해 일부 작업을 실행하기 위해 구현할 수있는 방법이 있습니까?



답변

업데이트 : 일부 의견에 따르면, 원래 답변의 솔루션은 iOS 8 이상의 특정 시나리오에서 작동하지 않는 것 같습니다. 추가 세부 정보가 없으면 실제로 해당되는지 확인할 수 없습니다.

그러나 그러한 상황에서는 대안이 있습니다. 재정 의하여 뷰 컨트롤러가 갑자기 터지는시기를 감지 할 수 있습니다 willMove(toParentViewController:). 기본 아이디어는 is 일 때 뷰 컨트롤러가 팝업되는 것 parent입니다 nil.

확인 “컨테이너 뷰 컨트롤러 구현” 자세한 내용은.


iOS 5 부터이 상황을 처리하는 가장 쉬운 방법은 새로운 방법을 사용하는 것입니다 - (BOOL)isMovingFromParentViewController.

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];

  if (self.isMovingFromParentViewController) {
    // Do your stuff here
  }
}

- (BOOL)isMovingFromParentViewController 탐색 스택에서 컨트롤러를 밀거나 튀길 때 의미가 있습니다.

그러나 모달 뷰 컨트롤러를 제시하는 경우 - (BOOL)isBeingDismissed대신 사용해야 합니다.

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];

  if (self.isBeingDismissed) {
    // Do your stuff here
  }
}

이 질문 에서 언급했듯이 두 속성을 결합 할 수 있습니다.

- (void)viewWillDisappear:(BOOL)animated {
  [super viewWillDisappear:animated];

  if (self.isMovingFromParentViewController || self.isBeingDismissed) {
    // Do your stuff here
  }
}

다른 솔루션은의 존재에 의존합니다 UINavigationBar. 대신 이벤트를 트리거 한 작업 (예 : 뒤로 버튼 누름)에서 수행해야하는 작업을 분리하기 때문에 내 접근 방식이 더 좋습니다.


답변

하지만 viewWillAppear()하고 viewDidDisappear() 있다 ‘뒤로’버튼을 탭 때 호출, 그들은 또한 다른 시간에 호출됩니다. 이에 대한 자세한 내용은 답변 끝을 참조하십시오.

UIViewController.parent 사용

뒤로 버튼을 감지하는 것은 willMoveToParentViewController(_:)OR 의 도움으로 VC가 부모 (NavigationController)에서 제거 될 때 더 잘 수행됩니다.didMoveToParentViewController()

parent가 nil이면 뷰 컨트롤러가 탐색 스택에서 튀어 나와 해제됩니다. 부모가 0이 아닌 경우 스택에 추가되고 표시됩니다.

// Objective-C
-(void)willMoveToParentViewController:(UIViewController *)parent {
     [super willMoveToParentViewController:parent];
    if (!parent){
       // The back button was pressed or interactive gesture used
    }
}


// Swift
override func willMove(toParent parent: UIViewController?) {
    super.willMove(toParent: parent)
    if parent == nil {
        // The back button was pressed or interactive gesture used
    }
}

스왑 willMove에 대한 didMove체크의 self.parent 작업을 수행하는 뷰 컨트롤러가 그라운드를 떠납니다.

해고 중지

비동기 저장을 수행해야하는 경우 부모를 확인하면 전환을 “일시 중지”할 수 없습니다. 이를 위해 다음을 구현할 수 있습니다. 여기서 단점은 멋진 iOS 스타일 / 애니메이션 뒤로 버튼을 잃는다는 것입니다. 또한 대화식 스 와이프 제스처로주의하십시오. 이 사례를 처리하려면 다음을 사용하십시오.

var backButton : UIBarButtonItem!

override func viewDidLoad() {
    super.viewDidLoad()

     // Disable the swipe to make sure you get your chance to save
     self.navigationController?.interactivePopGestureRecognizer.enabled = false

     // Replace the default back button
    self.navigationItem.setHidesBackButton(true, animated: false)
    self.backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "goBack")
    self.navigationItem.leftBarButtonItem = backButton
}

// Then handle the button selection
func goBack() {
    // Here we just remove the back button, you could also disabled it or better yet show an activityIndicator
    self.navigationItem.leftBarButtonItem = nil
    someData.saveInBackground { (success, error) -> Void in
        if success {
            self.navigationController?.popViewControllerAnimated(true)
            // Don't forget to re-enable the interactive gesture
            self.navigationController?.interactivePopGestureRecognizer.enabled = true
        }
        else {
            self.navigationItem.leftBarButtonItem = self.backButton
            // Handle the error
        }
    }
}

더 많은 볼 것 / 나타남

viewWillAppear viewDidDisappear문제가 발생 하지 않은 경우 예제를 살펴 보겠습니다. 세 개의 뷰 컨트롤러가 있다고 가정 해보십시오.

  1. ListVC : 사물의 테이블보기
  2. DetailVC : 일에 대한 세부 사항
  3. 설정 VC : 사물의 일부 옵션

상의 호출을 수행 할 수 있습니다 detailVC당신은에서 이동로 listVCsettingsVC다시에listVC

목록> 세부 사항 (push detailVC) Detail.viewDidAppear<-나타나는
세부 사항> 설정 (푸시 설정 VC ) Detail.viewDidDisappear<-사라짐

그리고 돌아가서 …
설정> 세부 사항 (팝 설정 VC) <- Detail.viewDidAppear나타나는
세부 사항> 목록 (팝 세부 사항 VC) Detail.viewDidDisappear<-사라짐

공지 사항 viewDidDisappear다시가는뿐만 아니라 향후하지 않을 경우에만, 여러 번이라고합니다. 빠른 작업이 필요할 수 있지만 네트워크 호출과 같은 더 복잡한 작업의 경우 저장하지 않을 수 있습니다.


답변

첫 번째 방법

- (void)didMoveToParentViewController:(UIViewController *)parent
{
    if (![parent isEqual:self.parentViewController]) {
         NSLog(@"Back pressed");
    }
}

두 번째 방법

-(void) viewWillDisappear:(BOOL)animated {
    if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
       // back button was pressed.  We know this is true because self is no longer
       // in the navigation stack.  
    }
    [super viewWillDisappear:animated];
}


답변

이것이 효과가 없다고 주장하는 사람들은 착각합니다.

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if self.isMovingFromParent {
        print("we are being popped")
    }
}

잘 작동합니다. 그렇다면 광범위한 신화를 일으키는 원인은 무엇입니까?

문제는 다른 메소드 의 잘못된 구현 , 즉 구현 willMove(toParent:)을 호출 하는 것을 잊었 기 때문인 것으로 보입니다 super.

당신이 구현하는 경우 willMove(toParent:)호출하지 않고 super, 다음 self.isMovingFromParent될 것입니다 false와의 사용은 viewWillDisappear실패하게 나타납니다. 실패하지 않았다. 넌 파산 했어

참고 : 진짜 문제는 보통이고 두 번째 것을 검출하는 뷰 컨트롤러 첫 번째 뷰 컨트롤러가 튀어되었다. Unified UIViewController “가장 앞쪽에”감지되는 일반적인 토론도 참조하십시오.

편집 의견은 이것이 viewDidDisappear아닌이어야 한다고 제안합니다 viewWillDisappear.


답변

이 문제로 이틀 동안 게임을했습니다. IMO의 가장 좋은 방법은 다음과 같이 확장 클래스와 프로토콜을 만드는 것입니다.

@protocol UINavigationControllerBackButtonDelegate <NSObject>
/**
 * Indicates that the back button was pressed.
 * If this message is implemented the pop logic must be manually handled.
 */
- (void)backButtonPressed;
@end

@interface UINavigationController(BackButtonHandler)
@end

@implementation UINavigationController(BackButtonHandler)
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
    UIViewController *topViewController = self.topViewController;
    BOOL wasBackButtonClicked = topViewController.navigationItem == item;
    SEL backButtonPressedSel = @selector(backButtonPressed);
    if (wasBackButtonClicked && [topViewController respondsToSelector:backButtonPressedSel]) {
        [topViewController performSelector:backButtonPressedSel];
        return NO;
    }
    else {
        [self popViewControllerAnimated:YES];
        return YES;
    }
}
@end

이것은 뷰 컨트롤러가 팝업 될 때마다 UINavigationController호출을 받기 때문에 작동합니다 navigationBar:shouldPopItem:. 여기에서 뒤로 밀렸는지 여부를 감지합니다 (다른 버튼). 당신이해야 할 유일한 것은 다시 누르면 뷰 컨트롤러에서 프로토콜을 구현하는 것입니다.

backButtonPressedSel모든 것이 정상이면 내부에보기 컨트롤러를 수동으로 팝해야합니다 .

이미 서브 클래 싱 UINavigationViewController되고 구현 된 navigationBar:shouldPopItem:것을 염려하지 않아도 방해가되지 않습니다.

뒤로 제스처 비활성화에 관심이있을 수도 있습니다.

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}


답변

이것은 Swift가있는 iOS 9.3.x에서 작동합니다.

override func didMoveToParentViewController(parent: UIViewController?) {
    super.didMoveToParentViewController(parent)

    if parent == self.navigationController?.parentViewController {
        print("Back tapped")
    }
}

다른 솔루션과 달리 예기치 않은 트리거는 아닙니다.


답변

기록을 위해, 나는 이것이 그가 찾고 있던 것 이상이라고 생각합니다.

    UIBarButtonItem *l_backButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRewind target:self action:@selector(backToRootView:)];

    self.navigationItem.leftBarButtonItem = l_backButton;


    - (void) backToRootView:(id)sender {

        // Perform some custom code

        [self.navigationController popToRootViewControllerAnimated:YES];
    }