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
문제가 발생 하지 않은 경우 예제를 살펴 보겠습니다. 세 개의 뷰 컨트롤러가 있다고 가정 해보십시오.
- ListVC : 사물의 테이블보기
- DetailVC : 일에 대한 세부 사항
- 설정 VC : 사물의 일부 옵션
상의 호출을 수행 할 수 있습니다 detailVC
당신은에서 이동로 listVC
에 settingsVC
다시에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];
}