내비게이션 컨트롤러에서 뒤로 버튼에 대한 동작 설정


180

탐색 컨트롤러에서 뒤로 버튼의 기본 동작을 덮어 쓰려고합니다. 타겟 버튼에 타겟에 액션을 제공했습니다. 백 버튼 속성을 통해 할당 할 때 이상한 점은주의를 기울이지 않고 현재보기를 팝업하고 루트로 돌아갑니다.

UIBarButtonItem *backButton = [[UIBarButtonItem alloc] 
                                  initWithTitle: @"Servers" 
                                  style:UIBarButtonItemStylePlain 
                                  target:self 
                                  action:@selector(home)];
self.navigationItem.backBarButtonItem = backButton;

내가 통해 설정 자마자 leftBarButtonItemnavigationItem내 조치를 호출하는 대신 화살표가 붙은 다시 하나의 일반 라운드 같은 그러나 다음 버튼 외모 :

self.navigationItem.leftBarButtonItem = backButton;

루트보기로 돌아 가기 전에 사용자 지정 작업을 호출하려면 어떻게해야합니까? 기본 뒤로 동작을 덮어 쓸 수있는 방법이 있습니까, 아니면 뷰를 떠날 때 항상 호출되는 메소드가 viewDidUnload있습니까?


액션 : @selector (홈)]; 선택자 조치 다음에 필요 : @selector (home :)]; 그렇지 않으면 작동하지 않습니다
PartySoft

7
@PartySoft 메소드가 콜론으로 선언되지 않는 한 사실이 아닙니다. 매개 변수를 사용하지 않는 버튼 호출 선택기를 갖는 것이 완벽하게 유효합니다.
mbm29414


3
왜 Apple은 뒤로 버튼 모양의 버튼을 제공하지 않습니까? 꽤 명백한 것 같습니다.
JohnK

답변:


363

프레스를 감지하려는 뷰 컨트롤러에 이것을 넣으십시오.

-(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];
}

1
이것은 매끄럽고, 깨끗하고, 훌륭하고 매우 잘 생각되는 해결책입니다.
boliva


3
버튼을 통해 대리인에게 메시지를 보내고 대리인이 컨트롤러를 팝업하면 여전히 작동하지 않습니다.
SAHM

21
또 다른 문제는 사용자가 뒤로 버튼을 눌렀거나 프로그래밍 방식으로 [self.navigationController popViewControllerAnimated : YES]를 호출 한 경우 구별 할 수 없다는 것입니다.
Chase Roberts

10
참고로, 스위프트 버전 :if (find(self.navigationController!.viewControllers as! [UIViewController],self)==nil)
hEADcRASH

177

UIViewController-BackButtonHandler 확장을 구현했습니다 . 서브 클래스를 만들 필요가 없으며 프로젝트에 넣고 클래스의 navigationShouldPopOnBackButton메소드를 재정의하십시오 UIViewController.

-(BOOL) navigationShouldPopOnBackButton {
    if(needsShowConfirmation) {
        // Show confirmation alert
        // ...
        return NO; // Ignore 'Back' button this time
    }
    return YES; // Process 'Back' button click and Pop view controler
}

샘플 앱을 다운로드하십시오 .


16
이것은 사용자 정의 UIButton을 사용하는 것보다 내가 본 가장 깨끗한 솔루션입니다. 감사!
ramirogm 2013 년

4
navigationBar:shouldPopItem:그것의 한 부분이기 때문에 개인 방법은 아닙니다 UINavigationBarDelegate프로토콜입니다.
onegray

1
그러나 UINavigationController는 이미 대리자를 구현 YES합니까? 아니면 앞으로? 서브 클래 싱은 아마도 더 안전한 옵션 일 것입니다
Sam

8
방금 iOS 7.1 에서이 (꽤 멋진 BTW)를 구현 NO했으며 뒤로 버튼 을 반환 한 후 비활성화 된 상태로 유지됩니다 (시각적으로 터치 이벤트를 수신하고 반응하기 때문에 시각적으로 보임). 확인에 else명령문을 추가 shouldPop하고 탐색 막대 하위 뷰를 순환하면서 alpha애니메이션 블록 내에서 필요한 경우 값을 1로 다시 설정하여 문제를 해결했습니다. gist.github.com/idevsoftware/9754057
boliva

2
이것은 내가 본 것 중 최고의 확장 중 하나입니다. 정말 고맙습니다.
Srikanth

42

Amagrammer와 달리 가능합니다. 하위 클래스를 만들어야합니다 navigationController. 나는 여기에 모든 것을 설명했다 (예제 코드 포함).


Apple의 설명서 ( developer.apple.com/iphone/library/documentation/UIKit/… )는 "이 클래스는 서브 클래스를위한 것이 아닙니다"라고 말합니다. 이것이 무엇을 의미하는지 잘 모르겠지만 "일반적으로 그렇게하지 않아도됩니다"또는 "컨트롤러를 엉망으로하면 앱이 거부됩니다"를 의미 할 수 있습니다 ...
Kuba Suder

이것이 유일한 방법입니다. 한스 포인트를 더 주시면 좋겠어요!
Adam Eberbach

1
실제로이 방법을 사용하여 뷰가 종료되는 것을 방지 할 수 있습니까? 뷰를 종료하지 않으려면 popViewControllerAnimated 메소드가 무엇을 리턴하도록 하시겠습니까?
JosephH

1
그래, 할 수있어 구현에서 수퍼 클래스 메소드를 호출하지 마십시오. 그렇게하지 말아야합니다. 사용자는 탐색으로 돌아갈 것으로 예상합니다. 당신이 할 수있는 일은 확인을 요청하는 것입니다. Apple의 문서에 따르면 popViewController는 "스택에서 팝된 뷰 컨트롤러"를 반환합니다. 따라서 아무것도 나타나지 않으면 nil을 반환해야합니다.
HansPinckaers

1
@HansPickaers 뷰 종료를 막는 것에 대한 귀하의 답변이 다소 부정확 할 수 있다고 생각합니다. popViewControllerAnimated :의 서브 클래스 구현에서 '확인'메시지를 표시하면 NavigationBar는 리턴하는 내용에 관계없이 트리의 한 레벨을 계속 애니메이션합니다. 탐색 모음의 뒤로 버튼 호출 shouldPopNavigationItem을 클릭하기 때문인 것 같습니다. 권장대로 하위 클래스 메서드에서 nil을 반환합니다.
deepwinter

15

스위프트 버전 :

( https://stackoverflow.com/a/19132881/826435 )

뷰 컨트롤러에서 프로토콜을 준수하고 필요한 모든 작업을 수행하면됩니다.

extension MyViewController: NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool {
        performSomeActionOnThePressOfABackButton()
        return false
    }
}

그런 다음 클래스를 만들고 NavigationController+BackButton아래 코드를 복사하여 붙여 넣습니다.

protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress() -> Bool
}

extension UINavigationController {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
        // Prevents from a synchronization issue of popping too many navigation items
        // and not enough view controllers or viceversa from unusual tapping
        if viewControllers.count < navigationBar.items!.count {
            return true
        }

        // Check if we have a view controller that wants to respond to being popped
        var shouldPop = true
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            shouldPop = viewController.shouldPopOnBackButtonPress()
        }

        if (shouldPop) {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            // Prevent the back button from staying in an disabled state
            for view in navigationBar.subviews {
                if view.alpha < 1.0 {
                    UIView.animate(withDuration: 0.25, animations: {
                        view.alpha = 1.0
                    })
                }
            }

        }

        return false
    }
}

어쩌면 나는 뭔가를 놓쳤지만 그것은 나를 위해 작동하지 않습니다. 확장의 performSomeActionOnThePressOfABackButton 메소드는 결코 호출되지 않습니다
Turvy

@FlorentBreton은 오해일까요? shouldPopOnBackButtonPress버그가없는 한 호출해야합니다. performSomeActionOnThePressOfABackButton존재하지 않는 구성 방법 일뿐입니다.
kgaidis

내가 방법을 만들어 그 이유는, 그것을 이해 performSomeActionOnThePressOfABackButton뒷면의 버튼을 누를 때 특정 동작을 실행하기 위해 내 컨트롤러를하지만,이 메소드가 호출되지 않았다, 행동은 정상적인 다시 반환입니다
박이

1
나에게도 효과가 없습니다. shouldPop 메소드는 호출되지 않습니다. 어딘가에 대리인을 설정 했습니까?
Tim Autin

@TimAutin 방금 이것을 다시 테스트했으며 뭔가 변경된 것처럼 보입니다. 주요 부분은에 이해하기 UINavigationController는이 navigationBar.delegate탐색 컨트롤러로 설정됩니다. 따라서 메소드를 호출해야합니다. 그러나 Swift에서는 하위 클래스에서도 호출 할 수 없습니다. 그러나 Objective-C에서 호출되도록 했으므로 지금은 Objective-C 버전을 사용하려고합니다. 스위프트 버그 일 수 있습니다.
kgaidis

5

직접 할 수는 없습니다. 몇 가지 대안이 있습니다.

  1. UIBarButtonItem테스트 통과시 탭 및 팝업으로 확인되는 나만의 맞춤 설정 만들기
  2. 키보드 에서 또는 버튼을 누른 후 호출 UITextField되는와 같은 대리자 메소드를 사용하여 양식 필드 컨텐츠의 유효성을 검증하십시오.-textFieldShouldReturn:ReturnDone

첫 번째 옵션의 단점은 사용자 지정 막대 버튼에서 뒤로 버튼의 왼쪽 화살표 스타일에 액세스 할 수 없다는 것입니다. 따라서 이미지를 사용하거나 일반 스타일 버튼으로 이동해야합니다.

두 번째 옵션은 대리자 메서드에서 텍스트 필드를 다시 가져 오기 때문에 유용합니다. 따라서 유효성 검사 논리를 대리자 콜백 메서드로 전송 된 특정 텍스트 필드에 대상으로 지정할 수 있습니다.


5

몇 가지 스레딩 이유 때문에 @HansPinckaers가 언급 한 솔루션이 나에게 맞지 않았지만 뒤로 버튼을 터치하는 가장 쉬운 방법을 찾았습니다. 다른 사람. 트릭은 정말 쉽습니다. 투명 UIButton을 UINavigationBar의 하위 뷰로 추가하고 선택기를 마치 실제 버튼 인 것처럼 설정하십시오! 다음은 Monotouch와 C #을 사용한 예이지만 objective-c 로의 번역을 찾기가 어렵지 않아야합니다.

public class Test : UIViewController {
    public override void ViewDidLoad() {
        UIButton b = new UIButton(new RectangleF(0, 0, 60, 44)); //width must be adapted to label contained in button
        b.BackgroundColor = UIColor.Clear; //making the background invisible
        b.Title = string.Empty; // and no need to write anything
        b.TouchDown += delegate {
            Console.WriteLine("caught!");
            if (true) // check what you want here
                NavigationController.PopViewControllerAnimated(true); // and then we pop if we want
        };
        NavigationController.NavigationBar.AddSubview(button); // insert the button to the nav bar
    }
}

재미있는 사실 : 테스트 목적으로 가짜 버튼에 적합한 치수를 찾으려면 배경색을 파란색으로 설정하고 뒤로 버튼 뒤에 표시 합니다! 어쨌든 원래 버튼을 대상으로하는 모든 터치를 계속 잡습니다.


3

이 기술을 사용하면 뷰 컨트롤러의 제목에 영향을 주거나 애니메이션 중에 뒤로 버튼 텍스트가 변경되는 것을 보지 않고 "뒤로"버튼의 텍스트를 변경할 수 있습니다.

이것을 호출 뷰 컨트롤러 의 init 메소드에 추가하십시오 .

UIBarButtonItem *temporaryBarButtonItem = [[UIBarButtonItem alloc] init];   
temporaryBarButtonItem.title = @"Back";
self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
[temporaryBarButtonItem release];

3

가장 쉬운 방법

UINavigationController의 델리게이트 메소드를 사용할 수 있습니다. 이 방법 willShowViewController은 VC의 뒤로 버튼을 눌렀을 때 호출됩니다.

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

뷰 컨트롤러가 상속 된 navigationController의 대리자로 설정되고 UINavigationControllerDelegate 프로토콜을 준수하는지 확인
Justin Milo

3

여기 내 스위프트 솔루션이 있습니다. UIViewController의 서브 클래스에서 navigationShouldPopOnBackButton 메소드를 대체하십시오.

extension UIViewController {
    func navigationShouldPopOnBackButton() -> Bool {
        return true
    }
}

extension UINavigationController {

    func navigationBar(navigationBar: UINavigationBar, shouldPopItem item: UINavigationItem) -> Bool {
        if let vc = self.topViewController {
            if vc.navigationShouldPopOnBackButton() {
                self.popViewControllerAnimated(true)
            } else {
                for it in navigationBar.subviews {
                    let view = it as! UIView
                    if view.alpha < 1.0 {
                        [UIView .animateWithDuration(0.25, animations: { () -> Void in
                            view.alpha = 1.0
                        })]
                    }
                }
                return false
            }
        }
        return true
    }

}

UIViewController에서 메서드 탐색 재정의 ShouldPopOnBackButton이 작동하지 않음-프로그램이 재정의 된 메서드가 아닌 상위 메서드를 실행합니다. 그에 대한 해결책이 있습니까? 누구 같은 문제가 있습니까?
Pawel Cala

반환 true의 경우는 rootview에있는 모든 방법을 다시 간다
Pawriwes

@Pawriwes 여기에 나를 위해 작동하는 것 같다 내가 쓴 솔루션입니다 : stackoverflow.com/a/34343418/826435이
kgaidis

3

뒤로 버튼 스타일을 유지하는 솔루션을 찾았습니다. 다음 방법을 뷰 컨트롤러에 추가하십시오.

-(void) overrideBack{

    UIButton *transparentButton = [[UIButton alloc] init];
    [transparentButton setFrame:CGRectMake(0,0, 50, 40)];
    [transparentButton setBackgroundColor:[UIColor clearColor]];
    [transparentButton addTarget:self action:@selector(backAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.navigationController.navigationBar addSubview:transparentButton];


}

이제 다음 방법에 필요한 기능을 제공하십시오.

-(void)backAction:(UIBarButtonItem *)sender {
    //Your functionality
}

투명 버튼으로 뒤로 버튼을 덮는 것입니다.)


3

navigationBar (_ navigationBar : shouldPop) 재정의 : 작동하더라도 좋지 않습니다 . 나를 위해 다시 탐색 할 때 무작위 충돌이 발생했습니다. navigationItem에서 기본 backButton을 제거하고 아래와 같이 사용자 정의 뒤로 버튼을 만들어 뒤로 버튼을 재정의하는 것이 좋습니다.

override func viewDidLoad(){
   super.viewDidLoad()
   
   navigationItem.leftBarButton = .init(title: "Go Back", ... , action: #selector(myCutsomBackAction) 

   ...
 
}

==========================================

비동기식 으로 Swift5 에서 UIAlert 를 사용하여 이전 응답 빌드


protocol NavigationControllerBackButtonDelegate {
    func shouldPopOnBackButtonPress(_ completion: @escaping (Bool) -> ())
}

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
      
        if viewControllers.count < navigationBar.items!.count {
            return true
        }
        
        // Check if we have a view controller that wants to respond to being popped
        
        if let viewController = topViewController as? NavigationControllerBackButtonDelegate {
            
            viewController.shouldPopOnBackButtonPress { shouldPop in
                if (shouldPop) {
                    /// on confirm => pop
                    DispatchQueue.main.async {
                        self.popViewController(animated: true)
                    }
                } else {
                    /// on cancel => do nothing
                }
            }
            /// return false => so navigator will cancel the popBack
            /// until user confirm or cancel
            return false
        }else{
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        }
        return true
    }
}

컨트롤러에서


extension MyController: NavigationControllerBackButtonDelegate {
    
    func shouldPopOnBackButtonPress(_ completion: @escaping (Bool) -> ()) {
    
        let msg = "message"
        
        /// show UIAlert
        alertAttention(msg: msg, actions: [
            
            .init(title: "Continuer", style: .destructive, handler: { _ in
                completion(true)
            }),
            .init(title: "Annuler", style: .cancel, handler: { _ in
                completion(false)
            })
            ])
   
    }

}

if viewControllers.count <navigationBar.items! .count {return true} 확인하십시오. 진행 상황에 대한 세부 정보를 제공 할 수 있습니까?
H4Hugo

// 너무 많은 탐색 항목을 표시하는 동기화 문제가 발생하지 않으며 //보기 컨트롤러가 충분하지 않거나 그 반대의 경우 비정상적인 탭핑이 발생하지 않습니다
brahimm

2

나는 이것이 가능하다고 쉽게 믿지 않습니다. 내가이 문제를 해결할 수있는 유일한 방법은 자신의 뒤로 버튼 화살표 이미지를 배치하는 것입니다. 처음에는 실망 스러웠지만 일관성을 위해 왜 빠졌는지 알았습니다.

일반 버튼을 만들고 기본 뒤로 버튼을 숨기면 (화살표없이) 가까이 갈 수 있습니다.

self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:@"Servers" style:UIBarButtonItemStyleDone target:nil action:nil] autorelease];
self.navigationItem.hidesBackButton = YES;

2
그래, 문제는 그것이 정상적인 뒤로 버튼처럼 보이기를 원한다는 것입니다, 그냥 내 커스텀 액션을 먼저 호출해야합니다 ...
앵무새

2

다만 서브 클래스에 의해 더 쉬운 방법이 대리자 메서드 의를 UINavigationBar하고 오버라이드 (override) ShouldPopItem방법 .


UINavigationController 클래스의 서브 클래스를 말하고 shouldPopItem 메소드를 구현해야한다고 생각합니다. 그것은 나를 위해 잘 작동합니다. 그러나 해당 메소드는 예상 한대로 단순히 YES 또는 NO를 리턴하지 않아야합니다. 설명과 솔루션은 여기에서 볼 수 있습니다 : stackoverflow.com/a/7453933/462162
arlomedia

2

애플의 공식 문서 https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html에 따르면 onegray의 솔루션은 안전하지 않습니다 .

"카테고리에서 선언 된 메소드의 이름이 원래 클래스의 메소드 또는 동일한 클래스 (또는 수퍼 클래스)의 다른 카테고리에있는 메소드와 동일하면 어떤 메소드 구현이 사용되는지에 대해 동작이 정의되지 않습니다. 클래스에 카테고리를 사용하는 경우 문제가되지 않지만 카테고리를 사용하여 표준 Cocoa 또는 Cocoa Touch 클래스에 메소드를 추가 할 때 문제가 발생할 수 있습니다. "


2

스위프트 사용하기 :

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    if self.navigationController?.topViewController != self {
        print("back button tapped")
    }
}

iOS 10 및 그 이전 버전에서는 더 이상 작동하지 않습니다.
Murray Sagal

2

내비게이션 바 뒤로 버튼 이벤트가 발생하기 전에 잡는 것에 대한 @oneway 의 Swift 3 버전은 다음과 같습니다 . 에 UINavigationBarDelegate사용할 수 없으므로 호출 UIViewController될 때 트리거되는 대리자를 만들어야합니다 navigationBar shouldPop.

@objc public protocol BackButtonDelegate {
      @objc optional func navigationShouldPopOnBackButton() -> Bool 
}

extension UINavigationController: UINavigationBarDelegate  {

    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {

        if viewControllers.count < (navigationBar.items?.count)! {                
            return true
        }

        var shouldPop = true
        let vc = self.topViewController

        if vc.responds(to: #selector(vc.navigationShouldPopOnBackButton)) {
            shouldPop = vc.navigationShouldPopOnBackButton()
        }

        if shouldPop {
            DispatchQueue.main.async {
                self.popViewController(animated: true)
            }
        } else {
            for subView in navigationBar.subviews {
                if(0 < subView.alpha && subView.alpha < 1) {
                    UIView.animate(withDuration: 0.25, animations: {
                        subView.alpha = 1
                    })
                }
            }
        }

        return false
    }
}

그런 다음 뷰 컨트롤러에서 델리게이트 기능을 추가하십시오.

class BaseVC: UIViewController, BackButtonDelegate {
    func navigationShouldPopOnBackButton() -> Bool {
        if ... {
            return true
        } else {
            return false
        }        
    }
}

사용자가 되돌아 갈지 여부를 결정하기 위해 경고 컨트롤러를 추가하려는 경우가 종종 있습니다. , 당신은 항상 할 수있는 그런 경우 return false에서와 navigationShouldPopOnBackButton()기능이 같은 일을하여 뷰 컨트롤러를 닫습니다 :

func navigationShouldPopOnBackButton() -> Bool {
     let alert = UIAlertController(title: "Warning",
                                          message: "Do you want to quit?",
                                          preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { UIAlertAction in self.yes()}))
            alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { UIAlertAction in self.no()}))
            present(alert, animated: true, completion: nil)
      return false
}

func yes() {
     print("yes")
     DispatchQueue.main.async {
            _ = self.navigationController?.popViewController(animated: true)
        }
}

func no() {
    print("no")       
}

: 나는 오류를 얻고 Value of type 'UIViewController' has no member 'navigationShouldPopOnBackButton' 난 당신의 코드를 컴파일 할 때 라인에 대해 if vc.responds(to: #selector(v..., 또한 self.topViewController선택 사양 반환하고 또한 대한 경고가있다.
Sankar

FWIW, 나는 그 코드를 다음 let vc = self.topViewController as! MyViewController과 같이 수정했습니다. 지금까지는 잘 작동하는 것 같습니다. 이것이 올바른 변경이라고 생각되면 코드를 편집 할 수 있습니다. 또한 수행하지 않아야한다고 생각하면 이유를 알게되어 기쁩니다. 이 코드에 감사드립니다. 이 답변은 투표에 따라 묻혀 있기 때문에 아마도 이것에 대한 블로그 게시물을 작성해야합니다.
Sankar

@SankarP 오류가 발생한 이유는 귀하 MyViewController가 준수하지 않을 수 있기 때문 BackButtonDelegate입니다. 랩 해제를 강요하는 대신 guard let vc = self.topViewController as? MyViewController else { return true }충돌이 발생 하지 않도록해야합니다.
Lawliet

감사. 가드 설명은 다음 guard let vc = self.topViewController as? MyViewController else { self.popViewController(animated: true) return true }과 같아야 합니다. 화면을 올바르게 전송할 수없는 경우 화면이 올바른 페이지로 이동하는지 확인하십시오. navigationBar함수 가이 코드가 존재하는 뷰 컨트롤러뿐만 아니라 모든 VC에서 호출 된다는 것을 이해 합니다. 답변에서 코드를 업데이트하는 것이 좋을 수도 있습니다. 감사.
Sankar

2

스위프트 4 iOS 11.3 버전 :

이것은 https://stackoverflow.com/a/34343418/4316579의 kgaidis의 답변을 기반으로합니다.

확장 기능이 언제 작동을 멈췄는지 잘 모르겠지만이 글을 쓰는 시점 (Swift 4)에서 아래 설명과 같이 UINavigationBarDelegate 적합성을 선언하지 않으면 확장 기능이 더 이상 실행되지 않는 것으로 보입니다.

이것이 왜 확장이 더 이상 작동하지 않는지 궁금해하는 사람들에게 도움이되기를 바랍니다.

extension UINavigationController: UINavigationBarDelegate {
    public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {

    }
}

1

현재 'nil'을 떠나는 대상 및 동작 변수를 사용하면 버튼을 "선택"할 때 호출되도록 저장 대화 상자를 연결할 수 있어야합니다. 이상한 순간에 트리거 될 수 있습니다.

나는 주로 Amagrammer에 동의하지만 화살표가있는 버튼을 사용자 정의하는 것이 어려울 것이라고 생각하지 않습니다. 뒤로 버튼의 이름을 바꾸고 스크린 샷을 찍고 필요한 버튼 크기를 포토샵으로 가져간 다음 버튼 상단에 이미지가 표시됩니다.


나는 당신이 포토샵을 할 수 있다는 데 동의합니다. 제가 정말로 원한다면이 작업을 수행 할 수 있다고 생각합니다.하지만 이제는 원하는 방식으로 작동하도록 모양을 바꾸고 조금 느끼기를 결정했습니다.
John Ballinger

예, 조치가 backBarButtonItem에 첨부 될 때 트리거되지 않는 것을 제외하고는 그렇습니다. 이것이 버그인지 또는 기능인지는 모르겠습니다. 심지어 애플조차도 모를 가능성이있다. 포토샵 실습과 관련하여 다시 한 번 나는 애플이 표준 기호를 잘못 사용하여 앱을 거부 할 것이라는 점에 경계 할 것이다.
Amagrammer

헤즈 업 :이 답변은 복제본에서 병합되었습니다.
Shog9

1

NavigationBars 오른쪽 버튼 항목에 액세스하고 선택기 속성을 설정할 수 있습니다 ... 여기에 참조 UIBarButtonItem 참조가 있습니다. 여기서 가장 많은 작업을 수행하는 경우 탐색 모음의 오른쪽 버튼 항목을 사용자 정의 UIBarButtonItem으로 설정하십시오 선택기를 만들고 설정하십시오 ...


헤즈 업 :이 답변은 복제본에서 병합되었습니다.
Shog9

1

이와 같은 사용자 입력이 필요한 양식의 경우 탐색 스택의 일부 대신 "모달"로 호출하는 것이 좋습니다. 이렇게하면 양식에서 비즈니스를 관리해야하며 사용자 지정 단추를 사용하여 양식을 확인하고 닫을 수 있습니다. 다른 앱과 동일하게 보이지만 더 많은 제어 기능을 제공하는 탐색 모음을 디자인 할 수도 있습니다.


헤즈 업 :이 답변은 복제본에서 병합되었습니다.
Shog9

1

뒤로 버튼을 가로 채려면 투명 UIControl로 덮고 터치를 가로 채십시오.

@interface MyViewController : UIViewController
{
    UIControl   *backCover;
    BOOL        inhibitBackButtonBOOL;
}
@end

@implementation MyViewController
-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    // Cover the back button (cannot do this in viewWillAppear -- too soon)
    if ( backCover == nil ) {
        backCover = [[UIControl alloc] initWithFrame:CGRectMake( 0, 0, 80, 44)];
#if TARGET_IPHONE_SIMULATOR
        // show the cover for testing
        backCover.backgroundColor = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.15];
#endif
        [backCover addTarget:self action:@selector(backCoverAction) forControlEvents:UIControlEventTouchDown];
        UINavigationBar *navBar = self.navigationController.navigationBar;
        [navBar addSubview:backCover];
    }
}

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

    [backCover removeFromSuperview];
    backCover = nil;
}

- (void)backCoverAction
{
    if ( inhibitBackButtonBOOL ) {
        NSLog(@"Back button aborted");
        // notify the user why...
    } else {
        [self.navigationController popViewControllerAnimated:YES]; // "Back"
    }
}
@end

헤즈 업 :이 답변은 복제본에서 병합되었습니다.
Shog9

1

적어도 Xcode 5에는 간단하고 아주 좋은 (완벽하지 않은) 솔루션이 있습니다. IB에서 도구 모음에서 막대 단추 항목을 끌어서 뒤로 단추가있는 탐색 막대의 왼쪽에 놓으십시오. 라벨을 "뒤로"로 설정하십시오. IBAction에 연결하고 viewController를 닫을 수있는 기능 버튼이 있습니다. 나는 몇 가지 작업을하고 긴장 풀기 트리거를 트리거하고 완벽하게 작동합니다.

이상적이지 않은 것은이 버튼에 <화살표가 없으며 이전 VC 제목을 전달하지 않지만 이것이 관리 될 수 있다고 생각합니다. 내 목적을 위해 새 뒤로 버튼을 "완료"버튼으로 설정하여 목적이 명확합니다.

또한 IB 네비게이터에는 두 개의 뒤로 단추가 있지만 명확성을 위해 레이블을 쉽게 지정할 수 있습니다.

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


1

빠른

override func viewWillDisappear(animated: Bool) {
    let viewControllers = self.navigationController?.viewControllers!
    if indexOfArray(viewControllers!, searchObject: self) == nil {
        // do something
    }
    super.viewWillDisappear(animated)
}

func indexOfArray(array:[AnyObject], searchObject: AnyObject)-> Int? {
    for (index, value) in enumerate(array) {
        if value as UIViewController == searchObject as UIViewController {
            return index
        }
    }
    return nil
}

1

이 접근 방식은 저에게 효과적이었습니다. 그러나 "뒤로"버튼에는 "<"기호가 없습니다.

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIBarButtonItem* backNavButton = [[UIBarButtonItem alloc] initWithTitle:@"Back"
                                                                      style:UIBarButtonItemStyleBordered
                                                                     target:self
                                                                     action:@selector(backButtonClicked)];
    self.navigationItem.leftBarButtonItem = backNavButton;
}

-(void)backButtonClicked
{
    // Do something...
    AppDelegate* delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
    [delegate.navController popViewControllerAnimated:YES];
}

1

@onegray의 답변의 신속한 버전

protocol RequestsNavigationPopVerification {
    var confirmationTitle: String { get }
    var confirmationMessage: String { get }
}

extension RequestsNavigationPopVerification where Self: UIViewController {
    var confirmationTitle: String {
        return "Go back?"
    }

    var confirmationMessage: String {
        return "Are you sure?"
    }
}

final class NavigationController: UINavigationController {

    func navigationBar(navigationBar: UINavigationBar, shouldPopItem item: UINavigationItem) -> Bool {

        guard let requestsPopConfirm = topViewController as? RequestsNavigationPopVerification else {
            popViewControllerAnimated(true)
            return true
        }

        let alertController = UIAlertController(title: requestsPopConfirm.confirmationTitle, message: requestsPopConfirm.confirmationMessage, preferredStyle: .Alert)

        alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel) { _ in
            dispatch_async(dispatch_get_main_queue(), {
                let dimmed = navigationBar.subviews.flatMap { $0.alpha < 1 ? $0 : nil }
                UIView.animateWithDuration(0.25) {
                    dimmed.forEach { $0.alpha = 1 }
                }
            })
            return
        })

        alertController.addAction(UIAlertAction(title: "Go back", style: .Default) { _ in
            dispatch_async(dispatch_get_main_queue(), {
                self.popViewControllerAnimated(true)
            })
        })

        presentViewController(alertController, animated: true, completion: nil)

        return false
    }
}

이제 모든 컨트롤러 RequestsNavigationPopVerification에서이 규칙을 준수하면 이 동작이 기본적으로 채택됩니다.


1

사용하다 isMovingFromParentViewController

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(true)

    if self.isMovingFromParentViewController {
        // current viewController is removed from parent
        // do some work
    }
}

이것이 뒤로 버튼을 눌렀 음을 증명하는 방법에 대해 더 설명해 주시겠습니까?
Murray Sagal

이것은 간단하지만로드 할 수있는 모든 자식보기에서 해당보기로 돌아올 경우에만 작동합니다. 자식이 부모에게 돌아가서이보기를 건너 뛰면 코드가 호출되지 않습니다 (부모에서 이동하지 않고보기가 이미 사라졌습니다). 그러나 OP의 요청에 따라 뒤로 버튼이 트리거 될 때만 이벤트를 처리하는 것과 동일한 문제입니다. 그래서 이것은 그의 질문에 대한 간단한 대답입니다.
CMont

이것은 매우 간단하고 우아합니다. 나는 그것을 좋아한다. 단 한 가지 문제 : 중간에 취소하더라도 사용자가 스 와이프하면 뒤로 이동하면 실행됩니다. 아마도 더 나은 해결책은이 코드를에 넣는 것 viewDidDisappear입니다. 그렇게하면 뷰가 사라지면 발사됩니다.
폰 타인 저드

1

그러나 @William의 대답은 정확하지만 사용자가 슬쩍 뒤로 이동 제스처를 시작하면 viewWillDisappear메소드가 호출되며 스 와이프해도 self탐색 스택에 self.navigationController.viewControllers있지 않습니다 (즉, 포함하지 않음 self) 이 완료되지 않았고 뷰 컨트롤러가 실제로 튀어 나오지 않았습니다. 따라서 해결책은 다음과 같습니다.

  1. 다음과 같이 스 와이프하여 이동 제스처를 비활성화하고 viewDidAppear뒤로 버튼 사용 만 허용합니다.

    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    {
        self.navigationController.interactivePopGestureRecognizer.enabled = NO;
    }
  2. 또는 viewDidDisappear다음과 같이 대신 사용하십시오 .

    - (void)viewDidDisappear:(BOOL)animated
    {
        [super viewDidDisappear:animated];
        if (![self.navigationController.viewControllers containsObject:self])
        {
            // back button was pressed or the the swipe-to-go-back gesture was
            // completed. We know this is true because self is no longer
            // in the navigation stack.
        }
    }

0

지금까지 찾은 솔루션은 그리 좋지는 않지만 나에게 효과적입니다. 이 답변을 받아 프로그래밍 방식으로 터지는 지 확인합니다.

- (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];

0

새로운 방법을 찾았습니다.

목표 -C

- (void)didMoveToParentViewController:(UIViewController *)parent{
    if (parent == NULL) {
        NSLog(@"Back Pressed");
    }
}

빠른

override func didMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        println("Back Pressed")
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.