iphone - present - uinavigationcontroller viewcontrollers




在導航控制器中設置後退按鈕的動作 (19)

Swift 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 {

    }
}

我試圖覆蓋導航控制器中後退按鈕的默認操作。 我為自定義按鈕提供了一個目標操作。 奇怪的是,當通過backbutton屬性賦值時,它並不關注它們,它只是彈出當前視圖並返回到根目錄:

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

只要我通過navigationItem上的leftBarButtonItem設置它,它會調用我的動作,但是這個按鈕看起來像是一個普通的圓形而不是箭頭後面的一個:

self.navigationItem.leftBarButtonItem = backButton;

如何在返回到根視圖之前調用我的自定義操作? 有沒有辦法覆蓋默認的後退動作,還是有一種方法,總是在離開視圖時調用(viewDidUnload不這樣做)?


Swift版本:

( https://.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
    }
}

@ 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並且默認情況下採用此行為。


最簡單的方法

您可以使用UINavigationController的委託方法。 當VC的後退按鈕被按下時,將會調用willShowViewController方法。當按下back btn時,可以調用任何你想要的

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

不可能直接做。 有幾個選擇:

  1. 創建您自己的自定義UIBarButtonItem ,如果測試通過,則在點擊時驗證並彈出
  2. 使用UITextField委託方法(如-textFieldShouldReturn:驗證表單字段內容,該方法在鍵盤上按下ReturnDone按鈕後調用

第一個選項的缺點是無法從自定義欄按鈕訪問後退按鈕的左箭頭樣式。 所以你必須使用一個圖像或者使用常規樣式按鈕。

第二個選項很好,因為您將文本字段返回到委託方法中,因此您可以將驗證邏輯定位到發送給委託回調方法的特定文本字段。


你可以嘗試訪問NavigationBars的右鍵項目並設置其選擇器屬性... 繼承一個引用UIBarButtonItem引用 ,另一件事如果這可以解決工作的工作是,將導航欄的右側按鈕項設置為一個自定義UIBarButtonItem創建並設置其選擇器...希望這有助於


使用Swift:

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

嘗試把它放到你想要檢測印刷機的視圖控制器中:

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

我不相信這很容易。 我相信解決這個問題的唯一方法就是讓自己的後退按鈕箭頭圖像放置在那裡。 起初我對此感到沮喪,但我明白為什麼,為了一致起見,它被遺漏了。

您可以通過創建常規按鈕並隱藏默認後退按鈕來關閉(沒有箭頭):

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

我已經實現了UIViewController-BackButtonHandler擴展。 它不需要子類化任何東西,只需將它放入您的項目並覆蓋UIViewController類中的navigationShouldPopOnBackButton方法即可:

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

下載示例應用


找到了一種保留後退按鈕樣式的解決方案。 將以下方法添加到您的視圖控制器。

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

它所做的只是用透明按鈕覆蓋後退按鈕;)


找到新的方式來做到這一點:

Objective-C的

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

迅速

override func didMoveToParentViewController(parent: UIViewController?) {
    if parent == nil {
        println("Back Pressed")
    }
}

此技術允許您在不影響任何視圖控制器的標題或在動畫期間看到後退按鈕文本更改的情況下更改“後退”按鈕的文本。

將其添加到調用視圖控制器中的init方法中:

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

然而,@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.
        }
    }
    

至少在Xcode 5中,有一個簡單而不錯的解決方案。 在IB中,從“實用工具”窗格中拖出一個欄按鈕項並將其放在導航欄左側的“後退”按鈕所在的位置。 將標籤設置為“返回”。 你將有一個功能按鈕,你可以綁定到你的IBAction並關閉你的viewController。 我正在做一些工作,然後觸發一個放鬆的繼續,它完美地工作。

不理想的是這個按鈕沒有得到<箭頭,並且沒有推進以前的VC標題,但我認為這可以被管理。 出於我的目的,我將新的後退按鈕設置為“完成”按鈕,因此它的目的很明確。

您還最終在IB導航器中有兩個後退按鈕,但為了清晰起見,標記它很容易。


迅速

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
}

這是我的Swift解決方案。 在您的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
    }

}

這種方法適用於我(但“後退”按鈕不會有“<”號):

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

通過使用當前正在離開的目標和動作變量'nil',您應該能夠連接保存對話框,以便在按鈕被選中時調用它們。 注意,這可能會在奇怪的時刻觸發。

我主要同意Amagrammer,但我認為用箭頭定制按鈕並不那麼困難。 我只需重命名後退按鈕,進行屏幕截圖,使用​​Photoshop所需的按鈕大小,並將其作為按鈕頂部的圖像。