UITableView Swipe to delete not working properly on iOS8 - objective-c

I have a view controller, having a uitableview inside a uiview. The delegate and datasource embeded properly and everything is working fine.
I am using commitEditingStyle and canEditRowAtIndexPathMethods delegate methods for swipe to delete functionality.
The usage of methods are as follows.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSDictionary *dataElement = [cartArray objectAtIndex:indexPath.row];
NSString *productId = [dataElement objectForKey:#"id"];
NSString *productSizeId = [dataElement objectForKey:#"beden_id"];
[self deleteTableRowWithId:productId sizeId:productSizeId];
}
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
The swipe gesture and delegate methods are working very fine without any problem on iOS7 tested both on emulator and device, however on iOS8 the swipe gesture doesn't work properly.
I tested this on iOS8 using iPhone 6 and to activate swipe I need to swipe like 20 times or more and from the edge table cell, sometimes cant even manage to get the swipe delete button at all.
I checked and tried most of the solutions on stack-overflow. Any help or suggestion will be deeply appreciated.
Hierarchy in the storyboard is like in the picture below.

Same issue, swipes would only work about 20% of the time on both simulator and device. In my case it was caused by ECSlidingViewController (a popular sliding menu implementation), which sets up a horizontal pan gesture to open the menu. This gesture needs to be disabled in the viewDidAppear: of the controller where you want swipe-to-delete to work properly:
#import "ECSlidingViewController.h"
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// Disable sliding menu pan gesture so it doesn't conflict with table view swipe-to-delete
self.slidingViewController.panGesture.enabled = NO;
}
Make sure to re-enable the pan gesture if you want other controllers to be able to use it:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Re-enable sliding menu pan gesture so other controllers can use it
self.slidingViewController.panGesture.enabled = YES;
}
See this pull request for more info on the issue: https://github.com/ECSlidingViewController/ECSlidingViewController/pull/70
Long story short, whether you're using ECSlidingViewController or not, make sure there are no other pan gestures set up that collide with the swipe-to-delete pan gesture.

Sometimes, there are other gesture recognizers than just the panGesture. If you want to retain the pan gesture or other recognizers, and still allow swipe to delete, I have accomplished such with the following category in the visible view controller:
#interface UIView (CellSwipeAdditions)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
#end
#implementation UIView (CellSwipeAdditions)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
#end

Related

How can a UIViewController inside a UIPageController get touch events?

It seems that the button simply doesn't get pressed.
I tried:
UIPageViewController Gesture recognizers
However, here is the catch
if page transition of UIPageViewController is UIPageViewControllerTransitionStyleScroll then
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
NSLog(#"%#",self.pageViewController.gestureRecognizers.description);
NSLog(#"%#",self.pageViewController.gestureRecognizers.description);
will simply be empty.
I tried adding my own gestureRecoqnizers instead of button into the UIViewControllers's subview. It still doesn't work.
So I have a UIPageViewController that display a UIViewController whose view has some button. How do I get that button pressed?
I also tried both solutions here:
http://emlyn.net/posts/stopping-gesture-recognizers-in-their-tracks
None works. First of all I cannot disable gesture recoqnizers of the UIPageViewController because there is no such thing. In scrollView mode, UIPageViewController doesn't have gesture recoqnizers.
I tried to add my own gesture recoqnizers to my own button but those are never called.
I want the UIPageViewController to still handle swipe though. But not tap.
I can't access the gesture recoqnizers of UIPageViewControllers because self.pageViewController.gestureRecognizers is empty. The UIPageViewController seems to just "absorb" all tap events. So my button doesn't get it.
I can add button in front of UIPageViewController but that button will absorb the swiping action which I want UIPageVIewController to still handle.
By the way if we uses the template from IOS (Create a page based app, and change the transition to scroll the pageviewcontroller.gesturerecoqnizers will be empty
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Configure the page view controller and add it as a child view controller.
self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];//Turn this to scroll view
self.pageViewController.delegate = self;
SDDataViewController *startingViewController = [self.modelController viewControllerAtIndex:0 storyboard:self.storyboard];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:NULL];
self.pageViewController.dataSource = self.modelController;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
// Set the page view controller's bounds using an inset rect so that self's view is visible around the edges of the pages.
CGRect pageViewRect = self.view.bounds;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
pageViewRect = CGRectInset(pageViewRect, 40.0, 40.0);
}
self.pageViewController.view.frame = pageViewRect;
[self.pageViewController didMoveToParentViewController:self];
// Add the page view controller's gesture recognizers to the book view controller's view so that the gestures are started more easily.
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
NSLog(#"%#",self.pageViewController.gestureRecognizers.description); //This is empty
while (false);
}
Maybe you can set up a custom gesture recognizer in your UIPageViewController. This way, you will be able to filter through UITouch Events, by implementing this in your gesture recognizer delegate (the one for your UIPageViewController) :
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch {
if (touch.tapCount > 1) { // It will ignore touch with less than two finger
return YES;
}
return NO;
}
This, you will tell the UIPageViewController gestureRecognizer to not care about touch with one finger. You'll have to try it, but i think it might do the trick. The problem here is you have to ask your user for a two-finger swipe...
Check here for an other answer

iOS: Change interface orientation when pop view controller

All view controllers in my app are working only in portrait orientation except one which can be portrait or landscape oriented.
I have some usage scenario like following:
I push controller which works in both orientations to UITabBarController
User change orientation from portait to landscape
User press "back button"
After these actions application remains in landscape orientation and does not change it automatically to portrait.
I control view controller orientation using supportedInterfaceOrientations (I use iOS 6.0). What I do wrong? How can I get correct behaviour when application automatically change orientation to allowed when user press back button? Thank you for answer!
In iOS 6 (and possibly earlier), if a view controller is offscreen when the device rotates, it does not get any notification. Nor does it get sent willAnimateRotationToInterfaceOrientation:duration: when it becomes the top view controller.
You need to keep track of the current orientation of the view controller and check the device orientation in viewWillAppear:. If they are different, you can use willAnimateRotationToInterfaceOrientation:duration: to set it correctly.
Since this is something you are likely to do a lot, you may want to create a generic superclass that your view controllers inherit from.
A typical solutions is:
#implementation MyHandlesOffscreenRotationController
{
BOOL isShowingPortrait;
}
- (void) viewDidLoad
{
[super viewDidLoad];
isShowingPortrait = UIInterfaceOrientationIsPortrait(
[[UIApplication sharedApplication] statusBarOrientation]);
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
BOOL currIsPortrait = UIInterfaceOrientationIsPortrait(
[[UIApplication sharedApplication] statusBarOrientation]);
if ((isShowingPortrait && !currIsPortrait) ||
(!isShowingPortrait && currIsPortrait)) {
[self willAnimateRotationToInterfaceOrientation:
[[UIApplication sharedApplication] statusBarOrientation]
duration:0.0f];
}
}
#end
Just override -(BOOL)shouldAutoRotate and - (NSUInteger)supportedInterfaceOrientations inside a UINavigationController category, then ViewController will force rotate to its supported orientation after pop from other ViewController.
#implementation UINavigationController (Rotate)
- (BOOL)shouldAutorotate
{
return [self.topViewController shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
#end
iOS 9 and above
At the time of pop just write the below-mentioned code in your viewWillAppear method.
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger: UIInterfaceOrientationPortrait]forKey:#"orientation"];
With this, your view will appear in portrait mode.
P.L. has a nice solution in this topic: Present and instantly dismiss an empty modal view controller which allows only portrait | landscape
How to change the device orientation programmatically in iOS 6

UISearchBar in UIView

I'm having difficulty following directions. I have a UISearchBar in a UIView. The user will enter the search string into the UISearchBar and click the search icon to search. The results will display in a new window (UITableView).
My search has shown me this:
A UISearchDisplayController cannot be added to a UIView because it
doesn't inherit from a UIView. You can add a UISearchBar in Interface
builder with an IBOutlet and then create a UISearchDisplayController
with that UISearchBar programmatically.
Just do it in code e.g. (assuming the view controller is vc): [vc
addSubview:mySearchDisplayController.searchBar]; // Note that
searchBar is the view, and mySearchDisplayController only CONTROLS the
searchBar etc.
and also this:
Just make your view controller implement the UISearchBarDelegate. In
your xib file, all you need to do is to add a UISearchBar to your view
and configure it as necessary, create an outlet for it (optional
really but helps to be explicit), and assign the delegate outlet to
your view controller. Then, to respond to the search bar events,
implement the UISearchBarDelegate protocol methods as necessary. For
example:
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[self handleSearch:searchBar]; }
(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
[self handleSearch:searchBar]; }
(void)handleSearch:(UISearchBar *)searchBar {
NSLog(#"User searched for %#", searchBar.text);
[searchBar resignFirstResponder]; // if you want the keyboard to go away }
(void)searchBarCancelButtonClicked:(UISearchBar *) searchBar {
NSLog(#"User canceled search");
[searchBar resignFirstResponder]; // if you want the keyboard to go away }
I'm just not getting it! Should I be adding mySearchController to my UIView? or my UISearchBar? Adding it to my UIView, nothing happens; adding it to my UISearchBar really wigs out the application. I don't even get an error - it just hangs.
Then, there is the second part: The delegate. Should I put the delegate in my UIView? Or in the UISearchDisplayController? Not sure which direction to go in and nothing so far is working. Please help.
All I really want at this point is just to get the handleSearch method to get executed. Thank you very much in advance for any help.
Very confused.
The idea is that you create a Search Bar in Interface Builder as an outlet and then use the following code to create UISearchDisplayController:
self.searchDisplayController2 = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchDisplayController2.delegate = self;
self.searchDisplayController2.searchResultsDelegate = self;
self.searchDisplayController2.searchResultsDataSource = self;
This code would be implemented in your View Controller, say in viewDidLoad. You will also have to implement at least two delegate functions in the same VC:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

UIView smaller than another UIView

I have an UIView high 30, than i add an UITableView as subview with 30 as origin.y. Because the table remains “under” the main view I cannot select the cells. Consequently I have implemented this method:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Tocco intercettato");
if (oldSet && oldEvent) {
[table touchesEnded:oldSet withEvent:oldEvent];
}
oldSet = touches;
oldEvent = event;
[table touchesBegan:touches withEvent:event];
[table touchesEnded:touches withEvent:event];
}
The taps work, but the slides does not work and consequently I cannot slide the table. Some idea in order to resolve?
Passing the touches from an overlying UIView to an underlying UITableView does not work (as you're seeing, the taps go through but not the scrolling movements). This is because UITableView (as a subclass of UIScrollView) does all sorts of under-the-hood weirdness with the responder chain (your code is also kind of odd, where you call both touchesBegan and touchesEnded from the overlying view's touchesBegan method, but this isn't why you can't scroll the table).
A simple way to achieve what you want (assuming I've understood that correctly) is to override the hitTest:withEvent: method on your overlying view and have the method return the underlying UITableView (instead of self, which is the default implementation):
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UITableView *tv = (UITableView *)[self.superview.subviews objectAtIndex:0];
return tv;
}
Note that this code assumes the UITableView is the first view added to your view controller, and that the overlying UIView is added after that.
Edit: OK, after re-reading your question, I think I understand your question. You have a main view with a height of 30, and to that view you've added a UITableView at y = 30. I think adding this method to your main view will do what you need:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (point.y < self.frame.size.height) {
return self;
} else {
return myTableView; // assumes myTableView is a reference to the
// UITableView that you've added
}
}
Essentially, you're telling the OS to pass all touches on to myTableView if they're outside the bounds of your main view.
However, a simpler way would be to add both the 30-pixel-high main view (the orange thing) and the UITableView as subviews of another view (or view controller). That way, you wouldn't have to do anything kludgy to get your table to behave like a normal table.

UIGestureRecognizer blocking table view scrolling

I'm using a custom UIGestureRecognizer subclass to track gestures on my InfoView class. The InfoView class is a subview of a custom UITableViewCell subclass called InfoCell.
I've added my gesture recognizer to my root view (the parent view of everything else on screen, because the purpose of my custom gesture recognizer is to allow dragging of InfoCell views between tables). Now, everything works as it should except one thing. I'm using the following code in my UIGestureRecognizer subclass to detect touches on the InfoView view:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UIView *touchView = [[touches anyObject] view];
if ([touchView isKindOfClass:[InfoView class]]) {
// Do stuff
}
The problem here is that the touches on the InfoView object are being intercepted, therefore they are not being forwarded to the UITableView which contains the InfoCell, which is the parent view of the InfoView. This means that I can no longer scroll the table view by dragging on the InfoView view, which is an issue because the InfoView covers the entire InfoCell.
Is there any way I can forward the touches onto the table view so that it can scroll? I've tried a bunch of things already:
[super touchesBegan:touches withEvent:event];
[touchView.superview.superview touchesBegan:touches withEvent:event]; (touchView.superview.superview gets a reference to its parent UITableView)
But nothing has worked so far. Also, the cancelsTouchesInView propery of my UIGestureRecognizer is set to NO, so thats not interfering with the touches.
Help is appreciated. Thanks!
Check out the UIGestureRecognizerDelegate method: - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
If this returns YES it will prevent your gesture recognizer from stomping on the one that UIScrollView is using to detect scrolling.
UIGestureRecognizer has a property "cancelsTouchesInView" which is set to YES by default. This means that touches in a UIView are cancelled when a gesture is recognized. Try to set it to NO to allow the UIScrollView to receive further touch events.
I had a line in my touchesBegan method that set the state property of the gesture recognizer to UIGestureRecognizerStateBegan. Removing this line seems to fix the problem.
You can try add this notification
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if ([gestureRecognizer class] == [UIPanGestureRecognizer class]) {
UIPanGestureRecognizer *panGestureRec = (UIPanGestureRecognizer *)gestureRecognizer;
CGPoint point = [panGestureRec velocityInView:self];
if (fabsf(point.x) > fabsf(point.y) ) {
return YES;
}
}
return NO;
}