how do I detect a "long Tap" gesture on a UITableViewCell - iphone

In my app I'm displaying a table with different elements if a user taps on a tableviewcell a detailview gets pushed onto the navigationstack.
I now want to provide the user with abilty to "long tap" on a tableviewcell in order to pop up an alertview showing the user different options that can be applied on said tableviewcell (email information to a friend, print it out, add to bookmarks etc)
how do i implement a "long tap" gesture recognizer on a UITableViewCell??
thanks for your help

Have you looked at UILongPressGestureRecognizer?
See: http://developer.apple.com/library/ios/#documentation/uikit/reference/UILongPressGestureRecognizer_Class/Reference/Reference.html%23//apple_ref/occ/cl/UILongPressGestureRecognizer
You might want to consider a swipe gesture as seen in the Twitter app instead, though: I don't think that a long tap on a table cell would be very intuitive to use or expected by the user. Just my 2 cents.
Johannes

on a touch down you would use a timer to fire off a method call in X number of seconds (how ever long you want the long touch to be). Then if they let go before that time you cancel the timer:
-(void) startSingleTouch:(UITouch*)touch
{
tapTouch = touch;
tapTimer = [[NSTimer scheduledTimerWithTimeInterval:tapDelay target:self selector:#selector(singleTapGestureSucceeded) userInfo:nil repeats:NO] retain];
}
-(void) cancelSingleTouch
{
if(tapTimer)
{
[tapTimer invalidate];
[tapTimer release];
}
tapTouch = nil;
tapTimer = nil;
}

Related

KVO with two UIButtons

I have a custom UITableViewCell subclass that gets presented in a UITableView in a UIPopoverController. The UIPopoverController is presented from a UIBarButtonItem.
When selections in the UITableViewCell are made, I send a NSNotification to the UIViewController class that is presenting the UIPopoverController. The selections in the table update my view in my viewController.
Now, there is a requirement to have another UIBarButtonItem that does EXACTLY the same thing as one of the buttons in the UITableViewCell in the popover. Basically the use case is that this feature seems to be the most commonly used feature in our popover and they want an easy way to just turn it on and off from another button.
So what I did was create a new UIBarButtonItem, and have a target attached to it:
- (void)FilterOn:(id)sender {
isFilterOn = !isFilterOn;
if ([sender isKindOfClass:[UIBarButtonItem class]]) {
UIBarButtonItem *filter = (UIBarButtonItem *)sender;
if (isFilterOn) {
[filter setTitle:#"Filter On"];
[self DoFilter];
}
else {
[aboutMeBBI setTitle:#"Filter Off"];
[self ClearFilter];
}
}
}
So this part works. It updates the model, the title of the button changes. The problem is, let's say I turn filter on, then in the popover, I turn the filter off. I pass the notification to my viewController class to its normal update method that handles filtering and a bunch of other stuff. I added this simple snippet to when the filter button is pressed from the popover:
- (void)FilterSortOptionDidSelect:(NSNotification *)notification {
NSDictionary *userDict = [notification userInfo];
NSInteger theTag = [[userDict objectForKey:#"CellTag"] integerValue];
switch (theTag) {
case FILTER: {
// update Model
[self.FilterBarButtonItem setTitle:#"Filter off"];
}
break;
default:
break;
}
}
This isn't quite what I want though since if I click back on the filter button since for starters, if I click back on the Filter button, it'll still say Filter Off, and do the action for Filter Off, and then if I click it again, it'll do Filter on actions. I try to fake it in the switch statement by changing the title, but that isn't really what I want to do. I read a little about key value observing and I wasn't sure if I could use something like that here, to register my buttons state with that of a button in a UITableViewCell subclass in a UIPopoverController. If anyone has any ideas that would be great. Thanks.

UIGestureRecognizer overriding "long presses" in UITableViewController

I am using -(NSUInteger)numberOfTouches method of UIGestureRecognizer class to count number of touches in a UITableViewController. (my earlier question)
Although I am able to count them correctly, but it is overriding touch events in such a way that tableView's didSelectRowAtIndexPath method is only called on a very short tap.
On long press, the cell does gets highlighted, but didSelectRowAtIndexPath is not fired.
Please guide what shall be done.
Thanks.
There's a couple of things to try. First of all, your immediate problem is that the gesture recognizer is probably waiting to see if you're going to add taps/touches to satisfy it's requirements for firing. You could also add a long touch recognizer that calls your didSelectRowAtIndexPath, but I would first watch the very good WWDC session on gesture recognizers from 2010 first, given you're a paid developer, and that would give you a good idea of how to correctly implement multiple gesture recognizers so that they fire when you want them.
call it from viewDidLoad() also I implement this code for monotouch it give an idea about longPressGestureRecognizer.
void AddGestureRecognizersToImage (UIImageView imgView)
{
var longPressGesture = new UILongPressGestureRecognizer (this, new Selector
("ShowResetMenu"));
imgView.AddGestureRecognizer (longPressGesture);
}
[Export("ShowResetMenu")]
void ShowResetMenu (UILongPressGestureRecognizer gestureRecognizer)
{
if (gestureRecognizer.State == UIGestureRecognizerState.Began)
{
var menuController = UIMenuController.SharedMenuController;
var resetMenuItem = new UIMenuItem ("Reset", new Selector ("ResetImage"));
var location = gestureRecognizer.LocationInView (gestureRecognizer.View);
BecomeFirstResponder ();
menuController.MenuItems = new [] { resetMenuItem };
menuController.SetTargetRect (new RectangleF (location.X, location.Y, 2, 2), gestureRecognizer.View);
menuController.MenuVisible = true;
imageForReset = gestureRecognizer.View;
}
}

iPhone iOS 4 UIButton toggle highlighted state on and off

I got a UIButton with style "Info Dark" set up in interface builder in my iPhone 4 app. One of the properties of the button is "Highlighted", which displays a white highlight around the button.
I would like to toggle this white highlight on and off, indicating if the button function is active or not.
The button is linked for "Touch up inside" event in the interface builder with this callback:
infoButton.highlighted = !infoButton.highlighted;
After the first touch, the highlight disappears and does not toggle as I expect it to. What else do I need to do to make the highlight toggle and display the state of the button?
Thank you!
Update:
When loaded from the interface builder, the button stays highlighted, even as the view appears/disappears. What causes this to happen is the "shows touch on highlight" interface builder property. If I assign the code above to another button, the info button highlights on and off as expected. However, the touches of the info button itself interfere with the above code, causing the button to lose the "touch" highlight
Update 2: I added another info button, directly below the first info button, in the interface builder and made it glow permanently. To create the appearance of the toggle, I hide and unhide the glowInfoButton below the real one. This works as expected:
infoButton.highlighted = NO;
glowInfoButton.highlighted = YES;
glowInfoButton.enabled = NO;
glowInfoButton.hidden = YES;
- (IBAction)toggleInfoMode:(id)sender {
// infoButton.selected = !infoButton.selected;
glowInfoButton.hidden = !glowInfoButton.hidden;
}
The Highlighted image is what displays when the UIButton is being pressed, and is controlled within the UIButton itself.
You're looking for the Selected property. You can set a Selected Image in IB, and then put a infoButton.selected = !infoButton.isSelected; in your TouchUpInside callback.
The highlighted property doesn't work like that, buttons aren't toggles.
It's just to know if the button is being pressed, if I'm correct.
If you want to implement that functionality, I recommend you subclass UIButton or UIControl.
Now that I see what you really were after I would advise subclass UIButton and check for a call to an event then toggle highlight state accordingly. You can do this without adding the dummy button.
in a custom button class implementation file place the following code, or similar:
#import "HighlightedButton.h"
#implementation HighlightedButton
BOOL currentHighlightState;
-(void)toggleHighlight:(id)sender {
self.highlighted = currentHighlightState;
}
-(void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
//get the string indicating the action called
NSString *actionString = NSStringFromSelector(action);
//get the string for the action that you want to check for
NSString *touchUpInsideMethodName = [[self actionsForTarget:target forControlEvent:UIControlEventTouchUpInside] lastObject];
if ([touchUpInsideMethodName isEqualToString:actionString]){
//toggle variable
currentHighlightState = !currentHighlightState;
//allow the call to pass through
[super sendAction:action to:target forEvent:event];
//toggle the property after a delay (to make sure the event has processed)
[self performSelector:#selector(toggleHighlight:) withObject:nil afterDelay:.2];
} else {
//not an event we are interested in, allow it pass through with no additional action
[super sendAction:action to:target forEvent:event];
}
}
#end
That was a quick run at a proper solution, there is a flicker on toggle that you may not like. I am sure if you play around with some changes that can be corrected. I tried it and actually like it for your stated case.
The highlighted state of a UIButton is simply setting the button's alpha to 0.5f. So if you set the button to not change on highlight, then just toggle the alpha between 0.1 and 0.5.
For example:
- (void)buttonPressed:(id)sender {
if((((UIButton*)sender).alpha) != 1.0f){
[((UIButton*)sender) setAlpha:1.0f];
} else {
[((UIButton*)sender) setAlpha:0.5f];
}
}
Perhaps what you really want is
infoButton.enabled = NO;
This will dim the button and disable touches when set to no, allow normal operation when set to YES.
or in your case:
infoButton.enabled = !infoButton.isEnabled;
to toggle the availability of same.
If you put this in your touchupinside event, of course it will work only the first time. After that is disabled and does not receive touch events. You would put it in another method that decides whether or not the button should be enabled.
If you truly want it to change each time it is pressed then you probably should use a switch or you may look at the -imageForState, -setTitle:forState and/or -setTitleColor:forState methods. If you want to toggle the appearance each time it is touched, you could change these.

How to dismiss keyboard when using DecimalPad

I have a UITableView with a custom cell that has a TextField. I have the DecimalPad comes up, and as we all know, there is no done key. I previously had resolved this type of issue when I had a "Decimal only" textfield on a normal UIView by handling the TouchesEnded event and then checking to see if the TextField was the first responder and if so, it would then resign, but if that technique could work now then I'm not able to figure out who's TouchesEnded I should be using (The UIView that everything is presented on, the UITableView, the Cell, the CellControler, the TextField.. I think I've tried everything).
I'm hoping there's another, cleaner way of dealing with this.
Anyone?
I think David has the best idea - here is some Monotouch code to get you started. You will need to put this in the View Controller where the decimal pad is being shown:
UIView dismiss;
public override UIView InputAccessoryView
{
get
{
if (dismiss == null)
{
dismiss = new UIView(new RectangleF(0,0,320,27));
dismiss.BackgroundColor = UIColor.FromPatternImage(new UIImage("Images/accessoryBG.png"));
UIButton dismissBtn = new UIButton(new RectangleF(255, 2, 58, 23));
dismissBtn.SetBackgroundImage(new UIImage("Images/dismissKeyboard.png"), UIControlState.Normal);
dismissBtn.TouchDown += delegate {
textField.ResignFirstResponder();
};
dismiss.AddSubview(dismissBtn);
}
return dismiss;
}
}
If you're targeting iOS 4.0 or greater you can create an inputAccessoryView containing a Done button to attach to the keyboard that will dismiss the keyboard when tapped. Here is an example from the documentation on creating a simple inputAccessoryView.
You could dismiss it when the user taps on the background; I think that's the most intuitive way.
In Interface Builder, change your View's class to UIControl. This is a subclass of UIView, so your program will work the same way, but you also get the standard touch events.
From here it's simple, create a method for the Touch Down event:
[numberField resignFirstResponder]
Of course it might be slightly different with MonoTouch -- unfortunately I don't know much about it, but wanted to help.
Hopefully you can use the concept, and modify your code accordingly.
Or you may just add some gesture to your main view.
For example:
//Just initialise the gesture you want with action that dismisses your num pad
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
UISwipeGestureRecognizer *swipeToHideNumPad = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(hideNumPad:)];
swipeToHideNumPad.delegate = self;
swipeToHideNumPad.direction = UISwipeGestureRecognizerDirectionDown;
[swipeToHideNumPad setNumberOfTouchesRequired:1];
[self.view addGestureRecognizer:swipeToHideNumPad];
}
//action
- (void)hideNumPad:(UIGestureRecognizer *)gestureRecognizer
{
[self.amountTextField resignFirstResponder];
}

NSRunLoop timing issue

Maybe there is a better way, but I want to pop a choice list when a user taps a button in a UIalertView. I would like this list to pop while the alert view is still visible and have everything close when the user taps an item in the choice list.
I thought I could do it by adding the list as a subview in the UIAlertView and keep the UIalertView displayed with an NSRunLoop in a while loop that pops with a flag set by the choice list. I cannot get this to work, however, because the flag does not get set before the while loop drops back into the NSRunLoop. A second tap will get it to drop out of the while loop but that is not what I want.
- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex{
CGRect popUpPickerFrame = alertView.frame;
PopUpPicker *popUpPicker = [[PopUpPicker alloc] initWithFrame:CGRectMake(popUpPickerFrame.origin.x +150,popUpPickerFrame.origin.y-50,115,250)];
popUpPicker.delegate = self;
popUpPicker.aList = [NSArray arrayWithObjects:#"General Plan", #"Light Plan", #"Melatonin Plan", #"Bed Times", #"Done", nil];
popUpPicker.tag = 10;
[alertView addSubview:popUpPicker];
while (popUpPicker.tag == 10) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}
[popUpPicker release];
}
I am setting the popUpPicker.tag to the row the user taps in the tableView:didSelectRowAtIndexPath: method of the list which then calls the lists delegate method.
I can get the popup list to work fine but only after the UIAlertView closes.
Thanks for any help.
John
Your workflow is not applicable to concept of UIAlertView. It is not designed to provide choices in the list after you press some button. Somebody on WWDC 2011 said "Don't fight the framework." This advice is just for you. Avoid alerts except they are really needed, consider using action sheets for your task, or implement the workflow in ViewController.