2012-03-21 2 views
4

В Keynote (и других приложениях) я заметил, что «стандартный» интерфейс делает Undo/Redo, предоставляя кнопку «Отменить» на панели инструментов.Кнопка отмены iPad (a-la Keynote и другие приложения)

Нажатие кнопки (всегда активируется) Отключает недавнюю операцию. (Если для отмены операции не было последней операции, она отобразит меню Отменить/Восстановить).

Длинное нажатие кнопки «Отменить» открывает меню «Отменить/Повторить».

Я искал методы реализации этого, и лучший ответ, который я нашел до сих пор, находится на following link.

Интересно, знает ли кто-нибудь более простой способ?

Спасибо!

+0

Жесткий диск iDevice предназначен для встряхивания устройства. – tripleee

+0

Да, но встряхивание iPad намного менее удобно, чем встряхивание телефона, поэтому приложения Apple ввели это соглашение. – rickster

+2

tripleee, если вы посмотрите на iPad-приложения, вы увидите, что общий метод отмены - это меню отмены, а не жест. когда вы думаете об этом, дрожание, чтобы отменить, может выглядеть круто, но совершенно непрактично, особенно если у вас есть многочисленные отскоки подряд ... – Reuven

ответ

4

После рассмотрения всех методов и обсуждения с друзьями ниже используется решение, которое я использовал, поскольку UIBarButtonItem отвечает как кранам, так и длительным нажатием (TapOrLongPressBarButtonItem).

Он основан на следующих принципах:

  1. Подкласс UIBarButtonItem
  2. Используйте настраиваемое представление (так что это на самом деле тривиально обрабатывать продолжительное нажатие - так как наш пользовательский вид не имеет никаких проблем в ответ на длинный -струмент жестов -press ...)

... До сих пор - этот подход был в other SO thread - и я не нравится этот подход, поскольку я не мог найти и достаточно простой способ сделать пользовательский вид появляется как панель навигации IPad кнопка ... Soooo ...

Используйте UIGlossyButton от Water Lou (благодаря воде!). Это использование инкапсулируется в подклассе ...

Результирующий код выглядит следующим образом:

@protocol TapOrPressButtonDelegate; 
@interface TapOrPressBarButtonItem : UIBarButtonItem { 
    UIGlossyButton* _tapOrPressButton; 
    __weak id<TapOrPressButtonDelegate> _delegate; 
} 
- (id)initWithTitle:(NSString*)title andDelegate:(id<TapOrPressButtonDelegate>)delegate; 
@end 

@protocol TapOrPressButtonDelegate<NSObject> 
- (void)buttonTapped:(UIButton*)button withBarButtonItem:(UIBarButtonItem*)barButtonItem; 
- (void)buttonLongPressed:(UIButton*)button withBarButtonItem:(UIBarButtonItem*)barButtonItem; 
@end 

@implementation TapOrPressBarButtonItem 
- (void)buttonLongPressed:(UILongPressGestureRecognizer*)gesture { 
    if (gesture.state != UIGestureRecognizerStateBegan) 
     return; 
    if([_delegate respondsToSelector:@selector(buttonLongPressed:withBarButtonItem:)]) { 
     [_delegate buttonLongPressed:_tapOrPressButton withBarButtonItem:self]; 
    } 
} 

- (void)buttonTapped:(id)sender { 
    if (sender != _tapOrPressButton) { 
     return; 
    } 

    if([_delegate respondsToSelector:@selector(buttonTapped:withBarButtonItem:)]) { 
     [_delegate buttonTapped:_tapOrPressButton withBarButtonItem:self]; 
    } 
} 

- (id)initWithTitle:(NSString*)title andDelegate:(id<TapOrPressButtonDelegate>)delegate { 
    if (self = [super init]) { 
     // Store delegate reference 
     _delegate = delegate; 

     // Create the customm button that will have the iPad-nav-bar-default appearance 
     _tapOrPressButton = [UIGlossyButton buttonWithType:UIButtonTypeCustom]; 
     [_tapOrPressButton setTitle:title forState:UIControlStateNormal]; 
     [_tapOrPressButton setNavigationButtonWithColor:[UIColor colorWithRed:123.0/255 green:130.0/255 blue:139.0/255 alpha:1.0]]; 
     // Calculate width... 
     CGSize labelSize = CGSizeMake(1000, 30); 
     labelSize = [title sizeWithFont:_tapOrPressButton.titleLabel.font constrainedToSize:labelSize lineBreakMode:UILineBreakModeMiddleTruncation]; 
     _tapOrPressButton.frame = CGRectMake(0, 0, labelSize.width+20, 30); 

     // Add a handler for a tap 
     [_tapOrPressButton addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside]; 
     // Add a handler for a long-press 
     UILongPressGestureRecognizer* buttonLongPress_ = [[UILongPressGestureRecognizer alloc] initWithTarget:self 
                             action:@selector(buttonLongPressed:)]; 
     [_tapOrPressButton addGestureRecognizer:buttonLongPress_]; 

     // Set this button as the custom view of the bar item... 
     self.customView = _tapOrPressButton; 
    } 
    return self; 
} 

// Safe guards... 
- (id)initWithImage:(UIImage *)image style:(UIBarButtonItemStyle)style target:(id)target action:(SEL)action { 
    NSLog(@"%s not supported!", __FUNCTION__); 
    return nil; 
} 

- (id)initWithImage:(UIImage *)image landscapeImagePhone:(UIImage *)landscapeImagePhone style:(UIBarButtonItemStyle)style target:(id)target action:(SEL)action { 
    NSLog(@"%s not supported!", __FUNCTION__); 
    return nil; 
} 

- (id)initWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style target:(id)target action:(SEL)action { 
    NSLog(@"%s not supported!", __FUNCTION__); 
    return nil; 
} 
- (id)initWithBarButtonSystemItem:(UIBarButtonSystemItem)systemItem target:(id)target action:(SEL)action { 
    NSLog(@"%s not supported!", __FUNCTION__); 
    return nil; 
} 

- (id)initWithCustomView:(UIView *)customView { 
    NSLog(@"%s not supported!", __FUNCTION__); 
    return nil; 
} 

@end 

И все, что вам нужно сделать, это:

1. Instantiate выглядит следующим образом:

TapOrPressBarButtonItem* undoMenuButton = [[TapOrPressBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Undo", @"Undo Menu Title") andDelegate:self];

2. Подключите кнопку к панели навигации:

[self.navigationItem setLeftBarButtonItem:undoMenuButton animated:NO];

3. Реализация протокола TapOrPressButtonDelegate, и вы сделали ...

-(void)buttonTapped:(UIButton*)button withBarButtonItem:(UIBarButtonItem*)barButtonItem { 
    [self menuItemUndo:barButtonItem]; 
} 

-(void)buttonLongPressed:(UIButton*)button withBarButtonItem:(UIBarButtonItem*)barButtonItem { 
    [self undoMenuClicked:barButtonItem]; 
}

Надеются, что это помогает любому else ...

0

Если вы используете IB (или в Xcode4 конструктор ... я предполагаю, что он вызывается), то вы можете выбрать «Отменить» от первого ответчика и перетащить это действие на кнопку. Я могу дать вам более конкретные инструкции, если это не распространяется на него.

Вот как это выглядит Undo Interface Builder image

Это на левом снизу колонок «Received действий» в нижнем

+0

Спасибо, но я ищу пример кода (или, по крайней мере, твердого направления/ведущего) о том, как получить обратный вызов для нажатия _and_ для длительного нажатия на элемент панели инструментов. – Reuven

+0

@ Reuven Я обнаружил, что в библиотеке разработчиков [ios] (https://developer.apple.com/library/ios/#samplecode/SimpleUndo/Introduction/Intro.html#//apple_ref/doc/uid/DTS40008408) – Coder404

+0

Мне жаль, что я не понимаю. То, что я ищу, не должно обрабатывать Undo - это как _imitate_ поведение Undo UIBarButton, как реализовано (например) в собственном приложении Apple Keynote. – Reuven

0

Я считаю, что ключ на самом деле в самом UINavigationBar. В отличие от UIButtons или других обычных объектов отслеживания касания, я подозреваю, что UIBarItems не обрабатывают свои собственные штрихи. Они не наследуют методы UIResponder или UIControl. Однако UINavigationBar конечно. И я лично добавлял жесты прямо в UINavigationBar много раз.

Предлагаю вам переопределить обработку касаний в подклассе UINavigationBar и проверить штрихи на его дочерние элементы. Если ребенок является вашей специальной кнопкой отмены, вы можете обращаться с ним соответствующим образом.

+0

Спасибо, но я ищу код примера (или, по крайней мере, твердое направление/руководство) о том, как получить обратный вызов для нажатия _and_ для длительного нажатия на элемент панели инструментов. – Reuven

+0

Итак, добавьте длинный идентификатор распознавания жестов в панель навигации, а когда кнопка нажата, откройте меню? –

+0

Да, в основном вы добавили бы жест в navBar и в селектор жестов проверили, находится ли точка касания в пределах прямоугольника соответствующей кнопки. Тогда сделайте то, что нужно сделать :) –

0
UIButton* undoButton = [UIButton buttonWithType:UIButtonTypeCustom]; 
[undoButton addTarget:self action:@selector(undoPressStart:) forControlEvents:UIControlEventTouchDown]; 
[undoButton addTarget:self action:@selector(undoPressFinish:) forControlEvents:UIControlEventTouchUpInside]; 
UIBarButtonItem* navButton = [[[UIBarButtonItem alloc] initWithCustomView:undoButton] autorelease]; 
self.navigationItem.rightBarButtonItem = navButton; 

Вы не обязательно должны добавить UIBarButtonItem как rightBarButtonItem, это просто и простой способ, чтобы показать вам, как создать свой UIBarButtonItem с настраиваемым видом, который является UIButton вы хотите обрабатывать события.

Вам необходимо реализовать undoPressStart: и undoPressFinish: путем сохранения состояния. Я бы сказал, при запуске, сохранить текущий NSDate или некоторое зернистое представление времени. Если вы закончите, проверьте, прошло ли время и если оно превышает определенный порог, покажите меню - иначе (а также, если дата начала не была зафиксирована) выполните отмену.

В качестве улучшения вы, скорее всего, захотите также наблюдать за событием UIControlEventTouchDragExit, чтобы отменить длительное нажатие.

+0

подход к созданию кнопки (как пользовательский вид UIBarButtonItem), и в некоторых отношениях даже проще, чем вы описали - поскольку представление кнопки можно использовать в качестве цели распознавателя жестов с длинным нажатием, и все мы настроены ... НО это не достаточно полное направление, поскольку созданная пользовательская кнопка не имеет апелляции rance к нему - и в результате не похож на другие элементы панели бара ... Поэтому я не могу принять этот ответ. – Reuven

+0

Серьезно? Поскольку вы не хотите вкладывать энергию в разработку пользовательской кнопки, вы откажетесь от действительного ответа? Вопрос должен быть перефразирован, чтобы указать на это искусственное ограничение, которое вы навязываете. – NSProgrammer

+0

На самом деле, я подумал (и до сих пор), что, учитывая, что я предоставил ссылку (http://stackoverflow.com/questions/2655630/how-can-you-add-a-uigesturerecognizer-to-a-uibarbuttonitem-as- в-общем-отменить) на вопрос и уточнил, что я смотрю дальше, тогда, конечно, я не буду принимать ответы, которые появляются в этой ссылке ... Но поскольку я не хочу, чтобы это было пламенем, войны, и я на самом деле не хотел отклонять ваш ответ (только чтобы не принять его ... поскольку он был только там, я сделаю +1, если вы отредактируете ответ [мой голос заблокирован иначе] ...) – Reuven

Смежные вопросы