使用MonoTouch的UINavigationController和UINavigationBarDelegate.ShouldPopItem()

时间:2011-06-20 15:24:28

标签: uinavigationcontroller xamarin.ios uinavigationbar back-button

当点击UINavigationBar的后退按钮(由UINavigationController控制)时,如何弹出UIAlertView?在某些情况下,我想问用户“你确定吗?”问题的类型,以便他可以中止操作并保持当前视图或弹出导航堆栈并转到父视图。

我发现最吸引人的方法是在UINavigationBar的委托上覆盖ShouldPopItem()。

现在,这里有一个非常相似的问题:iphone navigationController : wait for uialertview response before to quit the current view

还有一些类似性质的其他问题,例如: Checking if a UIViewController is about to get Popped from a navigation stack?How to tell when back button is pressed in a UINavigationControllerStack

所有这些都将“子类UINavigationController”作为可能的答案。

然后有一个像子类UINavigationController一样的读取通常不是一个好主意: Monotouch: UINavigationController, override initWithRootViewController

apple docs也说UINavigationController不是要被子类化的。

其他一些人声称在使用UINavigationController时甚至无法覆盖ShouldPopItem(),因为它不允许将自定义/子类UINavigationBarDelegate分配给UINavigationBar。

我的子类化尝试都没有奏效,我的自定义代表没有被接受。

我还在某处读过可能在我的自定义UINavigationController中实现ShouldPopItem(),因为它将自己指定为其UINavigationBar的Delegate。

没什么大惊讶,这没用。 UINavigationController的子类如何知道属于UINavigationBarDelegate的Methods。它被拒绝:“找不到合适的方法来覆盖”。删除已编译的“override”关键字,但完全忽略该方法(如预期的那样)。我认为,使用Obj-C可以实现几个协议(类似于C#AFAIK中的接口)来实现这一点。不幸的是,UINavigationBarDelegate不是一个接口而是MonoTouch中的一个类,所以这似乎是不可能的。

我在这里很丢失。当UINavigationBar由UINavigationController控制时,如何在UINavigationBar的Delegate上覆盖ShouldPopItem()?或者有没有其他方法可以弹出UIAlertView并在可能弹出导航堆栈之前等待它的结果?

5 个答案:

答案 0 :(得分:7)

这篇文章有点陈旧,但如果您仍然对解决方案感兴趣(但仍涉及子类化):

这实现了“你确定要退出吗?”按下后退按钮时发出警报,从此处的代码进行修改:http://www.hanspinckaers.com/custom-action-on-back-button-uinavigationcontroller/

如果在CustomNavigationController中实现UINavigationBarDelegate,则可以使用shouldPopItem方法:


CustomNavigationController.h:

#import <Foundation/Foundation.h>

@interface CustomNavigationController : UINavigationController <UIAlertViewDelegate, UINavigationBarDelegate> {

BOOL alertViewClicked;
BOOL regularPop;
}

@end

CustomNavigationController.m:

#import "CustomNavigationController.h"
#import "SettingsTableController.h"

@implementation CustomNavigationController


- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {

if (regularPop) {
    regularPop = FALSE;
    return YES;
}

if (alertViewClicked) {
    alertViewClicked = FALSE;
    return YES;
}

if ([self.topViewController isMemberOfClass:[SettingsTableViewController class]]) {
    UIAlertView * exitAlert = [[[UIAlertView alloc] initWithTitle:@"Are you sure you want to quit?" message:nil delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Yes", nil] autorelease];

    [exitAlert show];

    return NO;

}   
else {
    regularPop = TRUE;
    [self popViewControllerAnimated:YES];
    return NO;
}   
}

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
    //Cancel button
}

else if (buttonIndex == 1) {    
        //Yes button
    alertViewClicked = TRUE;
    [self popViewControllerAnimated:YES];
}           
}

@end

“regularPop”bool的奇怪逻辑是因为某些原因只是在shouldPopItem上返回“YES”而只弹出导航栏,而不是与navBar关联的视图 - 为此,你必须直接调用popViewControllerAnimated(然后调用shouldPopItem作为其逻辑的一部分。)

答案 1 :(得分:3)

作为参考,我在放弃ShouldPopItem()后采取的路线是将后退按钮替换为UIBarButtonItem,其中UIButton已分配为CustomViewUIButton被制作成看起来像原始后退按钮,使用两个图像用于正常和按下状态。最后,需要隐藏原始后退按钮。

过多的代码应该做什么。所以是的,谢谢Apple。

BTW:另一种可能性是使用秘密UIButton 101(实际上是后退按钮)创建UIButtonType,但我避免使用它,因为它可能会在以后的任何iOS版本中中断。

答案 2 :(得分:3)

仅覆盖UINavigationBarDelegate子类中的UINavigationController个方法,它应该可以正常工作。当您从代码中推送或弹出视图控制器时,不仅在按下后退按钮时,还要小心调用协议方法。这是因为它们是按下/弹出通知而非按钮按下操作。

答案 3 :(得分:3)

Xamarin确实提供了IUINavigationBarDelegate界面,允许您将UINavigationBarDelegate作为自定义UINavigationController课程的一部分来实施。

然而,接口不需要实现ShouldPopItem方法。所有界面都会将相应的Protocol属性添加到类中,以便可以将其用作UINavigationBarDelegate

因此,您需要将ShouldPopItem声明添加到类中,如下所示:

[Export ("navigationBar:shouldPopItem:")]
public bool ShouldPopItem (UINavigationBar navigationBar, UINavigationItem item)
{
}

答案 4 :(得分:0)

我已将此解决方案与本机Obj-C解决方案合并。这是我目前正在处理取消iOS中的BACK按钮的方式

似乎可以用这种方式处理NavigationBar的shouldPopItem方法:

  1. UINavigationController的子类
  2. 使用IUINavigationBarDelegate
  3. 标记您的自定义UINavigationController
  4. 使用“导出”属性

    添加此方法

    [导出(“navigationBar:shouldPopItem:”)] public bool ShouldPopItem(UINavigationBar navigationBar,UINavigationItem item) { }

  5. 现在你可以在ShoulPopItem方法中处理弹出。一个例子是创建一个像这样的接口

    public interface INavigationBackButton
    {
        // This method should return TRUE to cancel the "back operation" or "FALSE" to allow normal back
        bool BackButtonPressed();
    }
    

    然后使用此界面标记需要处理后退按钮的UIViewController。实现类似这样的东西

    public bool BackButtonPressed()
    {
        bool needToCancel = // Put your logic here. Remember to return true to CANCEL the back operation (like in Android)
        return needToCancel;
    }
    

    然后在你的ShouldPopItem实现中有这样的东西 坦克:https://github.com/onegray/UIViewController-BackButtonHandler/blob/master/UIViewController%2BBackButtonHandler.m

            [Export("navigationBar:shouldPopItem:")]
            public bool ShouldPopItem(UINavigationBar navigationBar, UINavigationItem item)
            {
                if (this.ViewControllers.Length < this.NavigationBar.Items.Length)
                    return true;
    
                bool shouldPop = true;
                UIViewController controller = this.TopViewController;
                if (controller is INavigationBackButton)
                    shouldPop = !((INavigationBackButton)controller).BackButtonPressed();
    
                if (shouldPop)
                {
                    //MonoTouch.CoreFoundation.DispatchQueue.DispatchAsync
                    CoreFoundation.DispatchQueue.MainQueue.DispatchAsync(
                        () =>
                        {
                            PopViewController(true);
                        });
                }
                else
                {
                    // Workaround for iOS7.1. Thanks to @boliva - http://stackoverflow.com/posts/comments/34452906
                    foreach (UIView subview in this.NavigationBar.Subviews)
                    {
                        if(subview.Alpha < 1f)
                            UIView.Animate(.25f, () => subview.Alpha = 1);
                    }
    
                }
    
                return false;                          
            }