After reviewing all methods and discussing with friends, below is the solution I used, for a UIBarButtonItem the responds to both taps and long-press (TapOrLongPressBarButtonItem).
It is based on the following principals:
- Subclass UIBarButtonItem
- Use a custom view (so it's really trivial to handle the long-press - since our custom view has no problem responding to a long-press gesture handler...)
... So far - this approach was in the other SO thread - and I didn't like this approach since I couldn't find and easy enough way of making the custom view appear like an iPad navigation bar button... Soooo...
Use UIGlossyButton by Water Lou (thanks water!). This use is encapsulated within the subclass...
The resulting code is as follows:
@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
And all you need to do is:
1. Instantiate is as follows:
TapOrPressBarButtonItem* undoMenuButton = [[TapOrPressBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Undo", @"Undo Menu Title") andDelegate:self];
2. Connect the button to the navigation bar:
[self.navigationItem setLeftBarButtonItem:undoMenuButton animated:NO];
3. Implement the TapOrPressButtonDelegate protocol, and you're done...
-(void)buttonTapped:(UIButton*)button withBarButtonItem:(UIBarButtonItem*)barButtonItem {
[self menuItemUndo:barButtonItem];
}
-(void)buttonLongPressed:(UIButton*)button withBarButtonItem:(UIBarButtonItem*)barButtonItem {
[self undoMenuClicked:barButtonItem];
}
Hope this helps anyone else...