UINavigationController и UINavigationBarDelegate.ShouldPopItem () с MonoTouch - PullRequest
11 голосов
/ 20 июня 2011

Как открыть UIAlertView, когда была нажата кнопка «Назад» на панели UINavigationBar (контролируемой UINavigationController)?При определенных условиях я хочу спросить пользователя «Вы уверены?»тип вопроса, чтобы он мог либо прервать действие и остаться в текущем представлении, либо открыть стек навигации и перейти к родительскому представлению.

Самый привлекательный подход, который я нашел, заключался в переопределении ShouldPopItem () в делегате UINavigationBar.

Теперь здесь есть довольно похожий вопрос: iphone navigationController: дождитесь ответа uialertview, прежде чем выйти из текущего представления

Есть также несколько других вопросов аналогичногоnature, например здесь: Проверка того, что UIViewController собирается получить Popped из стека навигации? и Как узнать, когда кнопка назад нажата в UINavigationControllerStack

Все эти состояния "подкласс UINavigationController" как возможные ответы.

Тогда есть этот, который читается как подкласс UINavigationController, как правило, не очень хорошая идея: Monotouch: UINavigationController, переопределить initWithRootViewController

яблочные документы aЯ также говорю, что UINavigationController не предназначен для использования в качестве подкласса.

Некоторые другие утверждают, что переопределение ShouldPopItem () даже невозможно при использовании UINavigationController, поскольку это не позволяет назначать пользовательский / подклассированный UINavigationBarDelegate для UINavigationBar.

Ни одна из моих попыток создания подкласса не сработала, мой пользовательский Делегат не был принят.

Я также где-то читал, что может быть возможно реализовать ShouldPopItem () в моем пользовательском UINavigationController, поскольку он назначает себя какДелегат его UINavigationBar.

Не удивительно, это не сработало.Как подкласс UINavigationController узнает о методах, принадлежащих UINavigationBarDelegate.Было отклонено: «не найдено подходящего метода для переопределения».Удаление ключевого слова override скомпилировано, но метод полностью игнорируется (как и ожидалось).Я думаю, что с Obj-C можно реализовать несколько протоколов (аналогично интерфейсам в C # AFAIK) для достижения этой цели.К сожалению, UINavigationBarDelegate - это не интерфейс, а класс в MonoTouch, так что это кажется невозможным.

Я в значительной степени потерян здесь.Как переопределить mustPopItem () в делегате UINavigationBar, когда он контролируется UINavigationController?Или есть какой-либо другой способ вызвать UIAlertView и дождаться его результата, прежде чем, возможно, отобразить стек навигации?

Ответы [ 5 ]

7 голосов
/ 17 сентября 2011

Этот пост немного устарел, но на случай, если вы все еще заинтересованы в решении (хотя все еще включает подклассы):

Это реализует "Вы уверены, что хотите выйти?" оповещение при нажатии кнопки «Назад», измененное из кода здесь: http://www.hanspinckaers.com/custom-action-on-back-button-uinavigationcontroller/

Оказывается, если вы реализуете UINavigationBarDelegate в CustomNavigationController, вы можете использовать метод 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

Странная логика с bool «normalPop» заключается в том, что по какой-то причине, просто возвращая «YES» на mustPopItem, выдает только панель навигации, а не представление, связанное с navBar - для этого вам нужно напрямую вызвать popViewControllerAnimated (который затем Вызывает shouldPopItem как часть его логики.)

3 голосов
/ 31 октября 2014

Xamarin предоставляет интерфейс IUINavigationBarDelegate, позволяющий вам реализовать UINavigationBarDelegate как часть вашего пользовательского класса UINavigationController.

Интерфейс, однако, не требует реализации метода ShouldPopItem. Все, что делает интерфейс, это добавляет соответствующий атрибут Protocol в класс, чтобы его можно было использовать как UINavigationBarDelegate.

Кроме того, вам необходимо добавить объявление ShouldPopItem в класс следующим образом:

[Export ("navigationBar:shouldPopItem:")]
public bool ShouldPopItem (UINavigationBar navigationBar, UINavigationItem item)
{
}
3 голосов
/ 17 сентября 2012

Переопределите только UINavigationBarDelegate методы в подклассе UINavigationController, и он должен просто работать.Будьте осторожны, чтобы методы протокола также вызывались, когда вы нажимаете или извлекаете контроллер представления изнутри вашего кода, а не только когда нажата кнопка возврата.Это потому, что это push / pop уведомления, а не нажатия кнопки.

3 голосов
/ 19 сентября 2011

Для справки, маршрут, который я выбрал после отказа от ShouldPopItem(), состоит в замене кнопки возврата на UIBarButtonItem, которой назначен пользовательский UIButton, назначенный как CustomView. UIButton создается так, чтобы выглядеть как оригинальная кнопка возврата, используя два изображения для нормального и нажатого состояний. Наконец, требуется скрыть оригинальную кнопку «назад».

Слишком много кода для того, что он должен делать. Так что да, спасибо Apple.

Кстати: еще одна возможность - создать UIButton с секретом UIButtonType 101 (который на самом деле является кнопкой возврата), но я избежал этого, поскольку он может сломаться в любой более поздней версии iOS.

0 голосов
/ 13 августа 2015

Я объединил это решение с нативным решением Obj-C. Именно так я сейчас работаю с отменой кнопки BACK в iOS

Похоже, что обработать метод shouldPopItem панели навигации можно следующим образом:

  1. Подкласс UINavigationController
  2. Отметьте свой пользовательский UINavigationController с помощью IUINavigationBarDelegate
  3. Добавьте этот метод с атрибутом экспорта

    [Export ("navigationBar: shouldPopItem:")] public bool ShouldPopItem (UINavigationBar navigationBar, элемент UINavigationItem) { }

Теперь вы можете обрабатывать выталкивание в методе 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;                          
        }
...