UIGestureRecognizer overriding "long presses" in UITableViewController - iphone

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;
}
}

Related

Removing cancel button in UIImagePickerController?

I am developing an app for ios 5.
I need to remove the cancel button on UIImagePickerController i searched for this problem on the forum but didnt get exact answer can someone please help me with this?
That is because it is not possible to remove that cancel button. That is an inbuilt function and you can not make changes in the same.
Swift Version, compatible 4+
To remove the navigation bar :
imagePicker.view.subviews
.filter { $0.isKind(of: UINavigationBar) }
.forEach { $0.isHidden = true }
for removing the buttons only :
imagePicker.view.subviews
.filter { $0.isKind(of: UIButton) }
.forEach { $0.isHidden = true }
VoilĂ 
I will give the best method to achieve this:
First create a subclass of the UiImagePickerController
in the subclass respond to the UINavigationBarDelegate method
- (BOOL)navigationBar:(UINavigationBar *)navigationBar
shouldPushItem:(UINavigationItem *)item
you dont have to do anything like setting the delegate or adding a protocol, just override the method inside your custom UIImagePcikerController.
in this method return NO for the cancel item (which is the first one)
I did this with ELCImagePickerController
There isn't a way to remove only the cancel button. UIImagePickerController exposes a property called showCameraControls, which will hide the bottom bar with the cancel button and the camera button, as well as the controls for flash, HDR, and flip camera, giving you just the camera preview.
If you want to provide an experience without a cancel button, you'll have to create a camera overlay view of what you want.
Assuming you have code invoking UIImagePickerController, you can turn off camera controls like this:
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
[imagePicker setShowsCameraControls:NO];
Assuming you'll overlay it with your own view without the cancel button, you'll add this (assuming you have a UIView called cameraOverlay:
[imagePicker setCameraOverlayView:cameraOverlay];
This will hide Navigationbar itself along with the Cancel and Title
let videoPicker = UIImagePickerController()
for view in videoPicker.view.subviews {
if let navBar = view as? UINavigationBar {
navBar.isHidden = true
}
}
If you want to remove the cancel button alone, dig deep into navBar
When you present the UIImagePickerView try puting the below code
for (UIView *subview in view.subviews) {
NSLog(#"subviews=%#",subview);
NSString *className = [NSString stringWithFormat:#"%#", [subview class]];
}
By the above code you can get the navigation controller used for displaying the cancel button.. Once you get the navigationController Set its leftside button to nil..
I have not used it but hope it can be of help to you

Modal View destructor not being called when InputAccessoryView is overriden

I am presenting a UIViewController modally and that view contains various UITextFields. I've overriden the modal views InputAccessoryView so that it shows a simple view, with one button that will ResignFirstResponder from every UITextField, thus dismissing the keyboard.
Recently I've been really cracking down on memory related issues so all my controllers now have a destructor. I've noticed that whenever the keyboard is shown with this overriden InputAccessoryView whenever the Modal View is dismissed the destructor will not be called. Does this mean that the UIViewController is not being destroyed? Am I incorrectly displaying the InputAccessoryView?
The InputAccessoryView code is as follows:
bool accessoryViewInit = false;
UIView accessoryView = new UIView(new RectangleF(0,0,320,30));
public override UIView InputAccessoryView
{
get
{
if (!accessoryViewInit)
{
accessoryView.BackgroundColor = UIColor.FromRGBA(0.0f, 0.0f, 0.f, 0.5f);
UIButton dismiss = new UIButton(new RectangleF(50,1, 200, 28));
dismiss.BackgroundColor = UIColor.Blue;
dismiss.SetTitle("Close Keyboard", UIControlState.Normal);
dismiss.TouchUpInside += delegate(object sender, EventArgs e) {
field1.ResignFirstResponder();
field2.ResignFirstResponder();
field3.ResignFirstResponder();
};
accessoryView.AddSubview(dismiss);
}
return accessoryView;
}
}
I have a feeling that is due to the fact I'm assigning a delegate to the TouchUpInside event for the button, is it keeping a reference to this, thus stopping the whole controller from being destroyed?
I've created a sample project which can be found at https://github.com/lukewhitt/InputAccessoryView-test
To recreate the problem: Run app, Present modal view by touching big red button. Now, if you dismiss the view without showing the keyboard, the destructor will be called. If you make on the UITextFields first responder (showing the keyboard and the InputAccessoryView) then dismiss the modal controller, the destructor won't be called.
EDIT
It would appear this is a bug in Monotouch that will be fixed in an up-coming release. In order to get around the problem, it seems you can't use anonymous delegates when assigning to events. So the dismiss.TouchUpInside would become:
public override UIView InputAccessoryView {
// code before
dismiss.TouchUpInside += HandleDismissTouch;
// rest of code
}
private void HandleDismissTouch (object sender, EventArgs e)
{
field1.ResignFirstResponder();
field2.ResignFirstResponder();
field3.ResignFirstResponder();
}
then in the code that dismisses the modal controller, I've added the following:
if (dismiss != null)
{
dismiss.TouchUpInside -= HandleDismissTouch;
dismiss.Dispose();
dismiss = null;
}
which causes the destructor to be called!
I think you're facing a bug in MT which will be fixed in MT 4. Looks like events handlers may prevent releasing controllers:
UIView events and garbage collection
Unfortunately, Geoff has not replied to the follow-up posters question which implications this has and if using anonymous delegates for events is an issue pre MT4.
I think a valid solution for now would be to manually Dispose().

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];
}

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

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;
}

How can I tell when something outside my UITableViewCell has been touched?

Similar to this question I have a custom subclass of UITableViewCell that has a UITextField. Its working fine except the keyboard for doesn't go away when the user touches a different table view cell or something outside the table. I'm trying to figure out the best place to find out when something outside the cell is touched, then I could call resignFirstResponder on the text field.
If the UITableViewCell could receive touch events for touches outside of its view then it could just resignFirstResponder itself but I don't see any way to get those events in the cell.
EDIT: I tried this (below) in my UITableViewCell subclass but it doesn't work, I think because touchesBegan:withEvent: doesn't get called if the event was handled by a control. I think I need to catch the events before they get send down the responder chain somehow.
The solution I'm considering is to add a touchesBegan:withEvent: method to the view controller. There I could send a resignFirstResponder to all tableview cells that are visible except the one that the touch was in (let it get the touch event and handle it itself).
Maybe something like this pseudo code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = // TBD - may need translate to cell's coordinates
for (UITableViewCell* aCell in [theTableView visibleCells]) {
if (![aCell pointInside:touchPoint withEvent:event]) {
[aCell resignFirstResponder];
}
}
}
I'm not sure if this is the best way to go about this. There doesn't seem to be any way for the tableviewcell itself to receive event notifications for events outside its view.
EDIT2: I thought I had an answer (I even posted it as an answer) using hitTest:withEvent: but that didn't work out. It doesn't always get called. :-(
[Edited: removed previous attempt which didn't always work, this one does]
OK, I finally figured a solution that fully works. I subclassed UITableView and overrode the hitTest:withEvent: method. It gets invoked for all touches anywhere in the table view, the only other possible touches are in the navbar or keyboard and the tableview's hitTest doesn't need to know about those.
This keeps track of the active cell in the table view, and whenever you tap a different cell (or non-cell) it sends a resignFirstResponder to the cell going inactive, which gives it a chance to hide its keyboard (or its datepicker).
-(UIView*) hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
// check to see if the hit is in this table view
if ([self pointInside:point withEvent:event]) {
UITableViewCell* newCell = nil;
// hit is in this table view, find out
// which cell it is in (if any)
for (UITableViewCell* aCell in self.visibleCells) {
if ([aCell pointInside:[self convertPoint:point toView:aCell] withEvent:nil]) {
newCell = aCell;
break;
}
}
// if it touched a different cell, tell the previous cell to resign
// this gives it a chance to hide the keyboard or date picker or whatever
if (newCell != activeCell) {
[activeCell resignFirstResponder];
self.activeCell = newCell; // may be nil
}
}
// return the super's hitTest result
return [super hitTest:point withEvent:event];
}
In my UITableViewCell subclasses that have a UITextField, I add the following code to get rid of the keyboard (or date picker, which slides up just like the keyboard):
-(BOOL)resignFirstResponder
{
[cTextField resignFirstResponder];
return [super resignFirstResponder];
}
Yay!
I think you're on the right track, but touchesBegan:withEvent: is a UIResponder method, so you'd actually have to override it in a UIView subclass rather than in your UIViewController subclass. Your options are:
If you're already subclassing UITableViewCell, override touchesBegan:withEvent: there.
If you're using a standard UITableViewCell, implement tableView:didSelectRowAtIndexPath in your UITableView's delegate.
That is a very good solution, the best I've found on the net. The only glitch I've discovered is that if you go from one cell with a textfield to another, the keyboard dismisses and reappears resulting in a jerky type animation.