UIPageViewController programmatically allow datasource to be called - ios

I have a UIPageViewController that displays images with Transition style scroll. I want to handle tap and pan gestures so in order to do that, I did a little hack of putting another view on top of the uipageviewcontroller and assign tap and pan gesture recognizers. In this case, i use -setViewControllers:direction:animated:completion: to perform swipes when i detect pan gestures to the left or right.
My problem is that when using -setViewControllers:direction:animated:completion: the datasource methods viewControllerBeforeViewController and viewControllerAfterViewController doesn't get called. Also the Delegate method -pageViewController:didFinishAnimating:previousViewControllers:transitionCompleted: doesnt get callled.
what do i have to do for these methods to be called using setViewControllers?

I think you can get UIPageController's gesture recognisers, and use them for disable or enable UIPageController interaction by using method of UIGestureRecogniser:
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer
If you use setViewControllers... methods of dataSource will not called, because you've provided viewControllers. Also you can handle completion of appearing animation in the completion block of this method. (you can call methods of the dataSource and delegate by self)
UPD
When I had same task, I implemented custom page view (based on UIScrollView in pages state), and used gesture recognisers of the scrollView to avoid scrolling, while pan gesture is recognised inside some page. I can't remember why I've implemented custom control, maybe because I can't find solution for same problem. I remember that my page view used dataSource to get views for the pages. I think you can implement custom control with your special logic.

Related

iOS didSelectItemAtIndexPath doesn't get called if a tap gesture recognizer is added to its background view

I have a UIView A, and I added a UICollectionView B to A as a subView, so now view A is B's background view. Here didSelectItemAtIndexPath will get called normally at this time. But if I add a tap gesture recognizer to View A, then didSelectItemAtIndexPath won't be called. The code is simple like below
UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tap2)];
[viewA addGestureRecognizer:tap2];
How can I make didSelectItemAtIndexPath get called for this situation? My expected behavior is both tapping cell and tapping background methods get called normally and separately as they have different tap behaviors.
From Apple's documentation:
You should always attach your gesture recognizers to the collection view itself—not to a specific cell or view. The UICollectionView class is a descendant of UIScrollView, so attaching your gesture recognizers to the collection view is less likely to interfere with the other gestures that must be tracked. In addition, because the collection view has access to your data source and your layout object, you still have access to all the information you need to manipulate cells and views appropriately.
https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/IncorporatingGestureSupport/IncorporatingGestureSupport.html
I have done this and it works very well. Generally I would call indexPathForItemAtPoint: on the collectionView to get the indexPath and then get the cell from cellForItemAtIndexPath. In your case you can check to see that indexPathForItemAtPoint returns nil (ie the user is clicking on an area between the cells) and then do your code for that. You can also use UIView convertRect methods to figure out exactly where the touch it.

UITableView inside a UIScrollView not responding to swipe events

My question is similar to a bunch of questions on stackoverflow (eg. swipe to delete in a UITableView which is embeded in a UIScrollView)
I need to implement a swipe action on a table view cell for a tableview controller which is a custom view embedded in a scrollview. However, the swipe events are not working properly and I understand the problem is that the UIScrollView is eating up the events for the UITableView so I tried out a bunch of options.
1) I subclassed the UIScrollView with the touchesBegan, touchesEnd etc methods but they never seem to get hit.
2) I could get hits on scrollViewWillBeginDragging but I am not sure how to call the responder for the UITableViewController from this.
3) I implemented a gesture recognizer in the tableviewcontroller class but that also didn't help.
I am confused as to how to go about so some code in Swift 3 syntax would be really helpful.

Propagating specific touch event to view underneath

I want to implement a feature similar to this:
View A and View B are all child views of the same parent UIView. View A and View B are sibling views. View A is the same frame size as the parent view. View B is only half of the size and it's above View A. I want to capture swipe events/gestures on View B, but still propagate other events/gestures (e.g. Tap) from View B to View A. So user can still interact with View A (e.g. tap on the button on View A) while View B is only capturing swipe gestures.
I have tried implementing following methods on View B
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
but seems like it's all or nothing approach. as I cannot detect in these methods if the event is a tap or a swipe. And I didn't find any API which can programatically trigger a tap on a UIView (not calling touch related methods).
You should move your rear sibling view to be a subview of the front view as this effective looks the same but makes the UIResponder chain behave correctly. UIGestureRecognizer can then do the rest of the work for you. Specifically it has a property, CancelsTocuhesInViews that you can turn off so it doesn't eat touch events and it has a delegate with methods that can allow you to specify the conditions under which a UIGestureRegonizer should fire. Implement the UIGestureRecognizerDelegate methods that you want to make your logic work. For instance if you want the overlay view to handle a tap only if a bunch of other gestureRecognizers don't handle the tap first then you implement gestureRecognizer(_:shouldRequireFailureOf:) and have it return true for every other gesture recognizer you want to go first; your gesture recognizer will then only fire when all of these other gesture recognizers have failed to handle the tap.
If I understand you correctly, what you want to do is usually done using UIGestureRecognizer objects and its subclasses. You can make one gesture recognizer (i.e. a swipe (or was it pan?) gesture recognizer) depend on another one (one for taps).
You can tell one gesture recognizer to only trigger when another has failed to detect its gesture. I haven't had to do that yet, so can't give you more detail, but I'd look into those classes and see if there's a way to chain them so it handles a swipe, and just does not handle taps. If you don't handle a tap, it should automatically go to any subview that will handle it, I think.

Why isn't the swipe gesture triggering a segue to the next view?

I've added a swipe gesture recognizer to a UICollectionView on storyboard, with a Navigation Controller set up. I'm following this tutorial, except I'm using an UICollectionView for the first view.
The actual problem is that when I run the program, and I try to swipe, nothing happens. Everything else happens.
I have tried following the directions over and over and in different ways, but nothing seems to work. I've created another project and just using regular view, it worked fine, which makes me think that the UICollectionView is part of the problem. Am I putting the gesture recognizer in the wrong view? I've tried putting it in the UIView of the first View Controller and in the UICollectionView, but it doesn't work.
What am I doing wrong?
Clearly, if I left any information out or you need any information at all, please don't hesitate to ask in the comments for it.
EDIT: The connections of the swipe:
Did you have define in storyboard, the segue from swipe gesture to next viewController ?
did you swipe in the correct direction (this can be set in storybord, select your swipe, attributes inspector, Right / Left / UP / Down
The UICollectionView Is probably taking the gesture over and telling the controller that It cannot handle the collection view gesture and your swipe gesture at the same time. Implement the UIGestureRecognizerDelegate protocol and return YES in this delegate method - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer. Make sure that the delegate of the swipe gesture is set on the controller from the storyboard.

Prevent UIScrollView's UIPanGestureRecognizer from blocking UIScreenEdgePanGestureRecognizer

I have a UIScrollView that fills the screen on one page of my app, but I want to allow the user to pan from the edge of the screen to reveal a view behind it. The problem is that the UIScrollView steals the touches from my UIScreenEdgePanGestureRecognizer at the edge of the screen. Unfortunately, I can't access the UIScreenEdgePanGestureRecognizer from the view controller that has the UIScrollView (and vice-versa), so I am not able to use the method requireGestureRecognizerToFail: because I cannot able to specify which gesture recognizer should be allowed to fail. The view controller with the scroll view is a child view controller of a container view controller that has the screen edge pan gesture recognizer attached to one of the container view controller's own views.
I'm also unable to use the delegate method
-(BOOL)gestureRecognizer:shouldRequireFailureOfGestureRecognizer:
because the UIScrollView won't allow me to set my view controller as the delegate of the scroll view's UIPanGestureRecognizer.
How can I prevent the scrollview from stealing the edge pan touches from my own gesture recognizer?
Unfortunately, creating this behavior can be a real P*** i* t** A**.
Fortunately, creating this behavior is possible using the UIGestureRecognizer Delegate even if you can't access one GestureRecognizer directly.
-(BOOL)gestureRecognizer:shouldRequireFailureOfGestureRecognizer:
-(BOOL)gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
-(BOOL)gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:
The second parameter ('otherGestureRecognizer') passed in the delegate methods holds the UIScrollView's PanGestureRecognizer (or private Apple - subclasses) when your gestureRecognizer 'collides' with the scrollView's.
So simply set your UIScreenEdgePanGestureRecognizer's delegate to reply to the delegate methods.
The naming of these two methods is very suboptimal and to be honest, i don't really know what the correct return values for your case are.
I just had this problem yesterday and i solved it by brutal trial & error.
Im my case, returning NO from both shouldRequireToFail and shouldBeRequiredToFail methods and YES from the simultaneous-method solved my problem.
Note: Returning NO from both methods changed the behavior compared to not
even implementing the methods at all. Even though the documentation says
the default return value is NO.
However, ANY GestureRecognizer behavior can be achieved by using the delegate methods. But as i said above, the naming of the methods is just very confusing. + there is as much as NO useful documentation for these methods.
This can be done without having to set the screen pan gesture's delegate to your view controller.
[scrollView.panGestureRecognizer requireGestureRecognizerToFail:screenPanGesture];

Resources