Navbar에서 뒤로 버튼 (이전 화면으로 돌아 가기, 부모보기로 돌아 가기) 버튼을 누르면 몇 가지 작업을 수행해야합니다.
이벤트를 포착하고 화면이 사라지기 전에 데이터를 일시 중지하고 저장하기 위해 일부 작업을 실행하기 위해 구현할 수있는 방법이 있습니까?
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
. 대신 이벤트를 트리거 한 작업 (예 : 뒤로 버튼 누름)에서 수행해야하는 작업을 분리하기 때문에 내 접근 방식이 더 좋습니다.
self.isMovingFromParentViewController
popToRootViewControllerAnimated
뒤로 버튼을 터치하지 않고 프로그래밍 방식으로 탐색 스택을 팝 할 때 TRUE 값이 있습니다. 답을 내려야합니까? (피험자는 "
override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) if isMovingFromParentViewController(){ println("back button pressed") } }
-viewDidDisappear:
당신이 얻을 것이다있을 가능성이 있기 때문에 -viewWillDisappear:
없이 -viewDidDisappear:
그 슬쩍 당신이 탐색 컨트롤러 항목을 해제 보내 주시면 시작할 때처럼 (후 취소 할 수 있습니다.
하지만 viewWillAppear()
하고 viewDidDisappear()
있다 '뒤로'버튼을 탭 때 호출, 그들은 또한 다른 시간에 호출됩니다. 이에 대한 자세한 내용은 답변 끝을 참조하십시오.
뒤로 버튼을 감지하는 것은 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
문제가 발생 하지 않은 경우 예제를 살펴 보겠습니다. 세 개의 뷰 컨트롤러가 있다고 가정 해보십시오.
상의 호출을 수행 할 수 있습니다 detailVC
당신은에서 이동로 listVC
에 settingsVC
다시에listVC
목록> 세부 사항 (push detailVC) Detail.viewDidAppear
<-나타나는
세부 사항> 설정 (푸시 설정 VC ) Detail.viewDidDisappear
<-사라짐
그리고 돌아가서 ...
설정> 세부 사항 (팝 설정 VC) <- Detail.viewDidAppear
나타나는
세부 사항> 목록 (팝 세부 사항 VC) Detail.viewDidDisappear
<-사라짐
공지 사항 viewDidDisappear
다시가는뿐만 아니라 향후하지 않을 경우에만, 여러 번이라고합니다. 빠른 작업이 필요할 수 있지만 네트워크 호출과 같은 더 복잡한 작업의 경우 저장하지 않을 수 있습니다.
didMoveToParantViewController:
는보기가 더 이상 보이지 않을 때 작업해야합니다. interactiveGesutre와 iOS7에 대한 유용한
_ = self.navigationController?.popViewController(animated: true)
되므로 뒤로 버튼을 누를 때만 호출되는 것은 아닙니다. 뒤로를 누를 때만 작동하는 통화를 찾고 있습니다.
첫 번째 방법
- (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
.
true
스 와이프가 완전히 튀어 나오지 않더라도 대화 형 스 와이프 팝 제스처 (뷰 컨트롤러의 왼쪽 가장자리에서)로 돌아갑니다 . 그래서 그 대신에 그것을 검사 willDisappear
에 그렇게 didDisappear
작동합니다.
이 문제로 이틀 동안 게임을했습니다. 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];
}
말했듯 purrrminator
이, by by에 의한 답 은 컨트롤러가 프로그래밍 방식으로 팝 될 때조차 실행될 것이기 elitalon
때문에 완전하지 않습니다 your stuff
.
지금까지 찾은 솔루션은 그리 좋지는 않지만 나에게 효과적입니다. elitalon
말한 것 외에도 프로그래밍 방식으로 터지는 지 확인합니다.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if ((self.isMovingFromParentViewController || self.isBeingDismissed)
&& !self.isPoppingProgrammatically) {
// Do your stuff here
}
}
프로그래밍 방식으로 팝업하기 전에 해당 속성을 컨트롤러에 추가하고 YES로 설정해야합니다.
self.isPoppingProgrammatically = YES;
[self.navigationController popViewControllerAnimated:YES];
당신의 도움을 주셔서 감사합니다!
가장 좋은 방법은 UINavigationController 델리게이트 메소드를 사용하는 것입니다
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
이를 사용하여 UINavigationController를 표시하는 컨트롤러를 알 수 있습니다.
if ([viewController isKindOfClass:[HomeController class]]) {
NSLog(@"Show home controller");
}
왼쪽의 navigationBar에 UIControl을 추가 하여이 문제를 해결했습니다.
UIControl *leftBarItemControl = [[UIControl alloc] initWithFrame:CGRectMake(0, 0, 90, 44)];
[leftBarItemControl addTarget:self action:@selector(onLeftItemClick:) forControlEvents:UIControlEventTouchUpInside];
self.leftItemControl = leftBarItemControl;
[self.navigationController.navigationBar addSubview:leftBarItemControl];
[self.navigationController.navigationBar bringSubviewToFront:leftBarItemControl];
그리고보기가 사라질 때 제거해야합니다.
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (self.leftItemControl) {
[self.leftItemControl removeFromSuperview];
}
}
그게 다야!
다음과 같이 뒤로 버튼 콜백을 사용할 수 있습니다.
- (BOOL) navigationShouldPopOnBackButton
{
[self backAction];
return NO;
}
- (void) backAction {
// your code goes here
// show confirmation alert, for example
// ...
}
신속한 버전의 경우 글로벌 범위와 같은 작업을 수행 할 수 있습니다
extension UIViewController {
@objc func navigationShouldPopOnBackButton() -> Bool {
return true
}
}
extension UINavigationController: UINavigationBarDelegate {
public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
return self.topViewController?.navigationShouldPopOnBackButton() ?? true
}
}
아래에서 뒤로 버튼 동작을 제어하려는 뷰 컨트롤러에 넣습니다.
override func navigationShouldPopOnBackButton() -> Bool {
self.backAction()//Your action you want to perform.
return true
}
navigationShouldPopOnBackButton
에서 왔습니까? 공개 API의 일부가 아닙니다.
7ynk3r 의 답변은 실제로 내가 사용한 것과 거의 비슷하지만 약간의 조정이 필요했습니다.
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
UIViewController *topViewController = self.topViewController;
BOOL wasBackButtonClicked = topViewController.navigationItem == item;
if (wasBackButtonClicked) {
if ([topViewController respondsToSelector:@selector(navBackButtonPressed)]) {
// if user did press back on the view controller where you handle the navBackButtonPressed
[topViewController performSelector:@selector(navBackButtonPressed)];
return NO;
} else {
// if user did press back but you are not on the view controller that can handle the navBackButtonPressed
[self popViewControllerAnimated:YES];
return YES;
}
} else {
// when you call popViewController programmatically you do not want to pop it twice
return YES;
}
}
UINavigationBarDelegate 프로토콜을 확인해야합니다 . 이 경우 navigationBar : shouldPopItem : 메소드를 사용할 수 있습니다.
finaly found solution .. 우리가 찾고 있던 방법은 UINavigationController의 대리자 메소드 인 "willShowViewController"입니다.
//IMPORT UINavigationControllerDelegate !!
class PushedController: UIViewController, UINavigationControllerDelegate {
override func viewDidLoad() {
//set delegate to current class (self)
navigationController?.delegate = self
}
func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
//MyViewController shoud be the name of your parent Class
if var myViewController = viewController as? MyViewController {
//YOUR STUFF
}
}
}
MyViewController
한다는 것 PushedController
입니다.