xcode - pageviewcontroller override gestureRecognizers - ios

I have used page-based application example shipped with xcode to build my app. The page view controller works fine, however, I have such problem:
In each view controller representing page data, I have some buttons, and clicking it leads to another view controller's view. I use this to add the view to view hierachy:
[self.view addSubview: self.articleViewController.view];
articleViewController just has a scrollview inside and show some text data. The problem is, if I swipe the view to scroll up/down, when it reaches the end page view controller takes this gesture and move to previous/next page, which not what I want. I want articleViewController receives no gestures from page view controller but only scrolling itself.
Hopefully I described good enough..
How can I disable gestures in my articleViewController? I have tried to study this post: UIPageViewController Gesture recognizers but haven't figured it out to solve my problem.
An example project to illustrate the problem: https://dl.dropbox.com/u/43017476/PageTest.zip

I know you cited the same question, but I don't like the accepted answer. Check out this answer, which is a bit further down the same post. I had the same issue and adding the block of code from that answer to the end of my viewDidLoad in my UIPageViewController fixed the issue I was having.
Basically what's happening is that the UIPageViewController is consuming all of the touch events, so you need to remove certain UIGestureRecognizers from the UIPageViewController so that it doesn't respond to those. It might be a little trickier than the example I posted as that is just removing UITapGestureRecognizer, but it's the same basic concept. You need to make sure that the UIPageViewController only advances to the next page on a right->left swipe gesture, and not an up->down swipe gesture.
Check out the UISwipeGestureRecognizerDirection documentation.

Related

Swift canEditRowAtIndexPath not working in UIPageViewController EZSwipeViewController

I have the following scenario and need help in resolving a tricky situation in the scenario
There is an Xcode project and am using EzSwipeController for swipe (pagination effect) between three View Controllers at the moment.
In my first ViewController (this viewController is fetched from my custom dynamic framework as part of my requirement) -
Code to fetch ViewController:
userProfile.createProfileUI(userSession!) { result in
switch result {
case let .Success(profileViewController):
myDetailsVC = profileViewController //myDetailsVc is passed to EZSwipControllerDataSource array
default:
break
}
}
The other two ViewControllers are within my project storyboard
The Problem -
In the first ViewController, there is a tableView with canEditRowAtIndexPath enabled for few cells (phone numbers).
So when I try to swipe the row, the EZSwipeController responds first and
hence, I am not able to edit the row.
Here is what is happening - http://recordit.co/SOJgdeYchP
Here is what should happen - http://recordit.co/EBPSbjH31q
How do I handle this problem? Is there a way where I can override the default swipe controller action when I try to edit the row?
Please help!
Attach the swipe gesture recognizer to a parent in the hierarchy.
If you're using a UITableViewController, replace it with a UIViewController with a UITableView inside it. Then just drag the gesture recognizer onto the view controller in Storyboard, and it'll attach to the UIViewController's Content View instead of the UITableView.
Though at the end of the day, this is inherently a flawed approach, since swiping to flip pages in an app is only ever viable if you don't have any elements on the pages that also have swipe gestures in them. If you do, even if you code a workaround to make the gesture recognizer for the element in the view controller (in this case, a table view cell) fire instead of the page flipping swipe gesture, that creates an inconsistent user experience.
My suggestion: don't use a table view for such a form altogether. On top of the aforementioned mechanical UX issue, from a user perception perspective, there's nothing indicating visually that this is a table view and not just a scroll view, so there's nothing indicating to the user that swipe actions (Delete) are available. Use a scroll view instead, and take a different approach to deleting (Delete buttons that are always visible, Delete buttons that are only visible after the user hits Edit somewhere, etc.).

Part of view with storyboard

So I'm working on a Google Maps-app, and I want a certain behavior. When tapping a marker I want the "Detail View", displayed below to popup. If swiping that detail view up I want it to cover the whole screen and make "MORE DETAIL" appear. If swiping down, it will close it.
I just can't figure out how to implement this behavior. Container View? Just a regular UIView wit gesture recognizer? Modal segue?
I'm not asking for code, just some words to get me on the right track.
Edit: For anyone else looking for this kind of behavior, every answer below is perfectly fine. I went with the container view, since the detail view is doing some network requests and UITableView work.
Can be done with Pan gesture and I would say by having Parent ViewController (Container) and Child ViewController instead UIView. Let me know if you would need more help :)
go with UIView and UIGestureRecognizer, it would be faster and simpler. Modal segue does not cover the whole page as you want, and container view is too much work to put on just a simple thing

MMDrawerController pan gesture behaviour when center controller has Scroll View

I'm looking for help from someone who have used MMDrawerController.
I've downloaded example project and it has a table view as a center view controller. Pan gesture works correctly - it starts to open the drawer only if the gesture is mostly horizontal. Drawer does not open when you scroll table view. This is the desired behavior for me.
But when I set up my own project and there's scrollView or tableView - scrolling them up and down opens the drawer if the scrolling gesture has even the slightest horizontal component which is confusing and barely usable.
I've tried to understand what makes the difference in the example project looking through code, but with no success. I didn't find any gesture recognizer callback overrides or anything like that what is changing the gesture behavior.
I've looked through threads on SO considering MMDrawerController, but didn't find anything similar.
I do know that I can override some things in MMDrawerController subclass to change the gesture recognition completely and probably achieve desired behavior this way, but I don't want to reinvent the wheel here. Probably there is some easy answer that I overlooked.
I've found the origin of my problem. It has nothing to do with the MMDrawerController. In my project, I had a category which implements gestureRecognizer delegate's method which de facto overrides implementation in all ViewControllers. I should use subclass instead of a category in this case, this was a very bad design.

Disable page transition at VC pushed by Navigation controller

I have root page view controller with three navigation controllers, each of them initially navigates to view controller with main table. After user clicks on the table there is a segue with push going to another VC (with detail table).
Transition style is scroll.
it's work fine. But, I have following effect: page scrolling works everywhere.
I need to scroll between pages only when user in table vc. Left swipe in detail table vc going to previous navigation controller, not back to the main table vc.
What I need to do to correct described behavior?
X-Code 5, iOS 7
Upd: I can prevent page transition by returning a nil at datasource before/after methods, but the scrolling is still showing on the screen.
When I tried to disable scroll by setting datasource to nil in 2 ways:
1) in pageViewController:didFinishAnimating) a have an exception: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid parameter not satisfying: [views count] == 3'
2) in main table vc in willAppear/willDisapper and have an exception: Terminating app due to uncaught exception 'NSGenericException', reason: '* Collection <__NSArrayM: 0x8a445d0> was mutated while being enumerated.'
Upd 2: here the link to my project: http://yadi.sk/d/MWmdA3XLCdn4U . Press a blue button with "download" picture to get the file, or use the language switcher at the bottom of page.
Upd 3: Thanks to David. His solution is working. In addition: I just lay Pan GR at DetailTable VC in my storyboard and link it to a view, without any lines of code.
Two ideas come to mind:
subclass UIPageViewController, and in your subclass provide a method to change the array of view controllers from the "real" array to just the current visible one. The user will not see any affect of this, but it will prevent the page controller from doing anything.. Obviously when you get back to the root view, you tell the page view controller to undo the change (swap back to the real array).
implement the dataSource protocol, and set a flag so that when you want to prevent paging, the dataSource returns nil for the next/previous view controller.
Note: I have not done this with a pageController, but I have done similar things with navigation controllers.
EDIT1: I played with your project, and yes, its not going to be easy to prevent the scrolling, but it is possible. The key issue is that the page controller has a scrollview subview that is hosting the pages. It has a pan gesture recognizer in it to recognize the dragging behavior and respond to it. Unfortunately, for the "scrolling" variation of the page controller, the gesture recognizers are not exposed. There is an interesting thread on this whole subject here on SO.
So you have a few options:
look for the scroll view in the subviews, find the gesture recognizer, and disable it.
disable the same scrollView
do some fancy tricks after you push the first view controller. But that I mean, once the animation is over, pull that view out of the subviews, remove the paging controller from the window, and install the pulled out view directly as the root view controller. This can be made to work, but since offsets change. you may need to actually create a transparent view the same size as the window and insert the pulled view there first.
add your own pan gesture recognizer, and just ignore the results. So in viewDidLoad in any view that you don't want paging, add the code below. It seems to work quite well.
Code:
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)];
[self.view addGestureRecognizer:pan];
EDIT2:
The preferred way to do this is going to be using your own pan gesture recognizer, as shown in the code above. What you can do is when the view appears that you DO NOT want to activate the paging control, add the gesture recognizer in viewDidLoad. When the user navigates back to the main view, then that pan recognizer will not be active, and the page control will work normally. I tried this with your demo project, it works fine.
While the other ways can be made to work, its more complicated as you see. To change the scrollView or its recognizer, you have to go probing around in the page view subviews, which is something Apple frowns on, and is fragile (meaning it can break easy in the future). You would have to provide public methods in your subclass, and figure out how to get a reference to the page control to each subview.

How to implement navigation between UIViewControllers with forward and back transition in IOS7?

Let's say I have a list of articles I want to navigate through. With iOS 6 there were two simple solutions:
Using UIPageViewController
Using a custom solution with an UIScrollView, maybe a nested one
That's pretty straight forward but it lacks of flexibility regarding custom transitions. With UIPageViewController I only have two (Page Curl and Scroll), with UIScrollView there is only Scroll.
The transition effect I am looking for is the one introduced by Apple with iOS 7. The one we get by pushing a new controller to the stack, see the screenshot:
That comes with a really nice user experience in my opinion, but it only works the way back, not forward. But as the Safari browser supports also navigating in both directions, I am wondering how it is implemented there and, eventually, how I could implement it for my list of articles.
Thanks for any hints!
I looked at the exact same problem recently and built a working demo that uses UIViewController containment and a subclass of UIPanGestureRecognizer. It supports:
Gesture-based paging in both directions (tracks touch rather than simply triggers animation on swipe)
Ability to enable/disable wrapping (moving from page 0 to lastIndex and back)
Ability to enable/disable parallax paging (when disabled you just get standard UIScrollView like paging
https://github.com/alfiehanssen/ios-viewcontroller-containment
An alternative would be to build a pager based on the new UIViewControllerInteractiveTransitioning and associated protocols.
Hope this helps...
Have a stack of views or view controllers within your own container view controller, and use a pan gesture recogniser to track the finger swipes, and translate this into altering the frame/center of the top view.
There are plenty of iOS Slide Menu projects in github that will probably show you how to do the pan gesture recogniser.

Resources