PageViewController scroll bug (over scrolls) - ios

I was following the tutorial here and had to make a few changes to fit my project (code of my variation here, sorry I put it on GitHub because it's a bit lengthy to copy and paste). Sometimes when I swipe right to get to the last VC, it automatically closes on sight. Other times it works properly where if I swipe to the last VC it stays there until I swipe right again to close it. I've placed print statements in the code to help me find where the bug is. Here are the print statements when it works and when it doesn't work. Looking at the logs, since the exact same code is accessed in the same order, I'm not sure how to fix this bug. This problem doesn't occur when the transition style is PageCurl instead of Scroll.

First you need to know how pageViewControllers work.
After the first page is loaded, the moment you start to scroll it will try to load the next page. this is just for the first page. after that as soon as you reach a page it will preload the next page (depending on the direction) and have it ready.
In your case when you reach page 5 the pageViewController will try to preload the next page which leads to this part of your code
guard orderedViewControllersCount != nextIndex else {
print("last item in coachamrks going to dismiss")
self.dismissViewControllerAnimated(true, completion: nil)
return orderedViewControllers.first
}
and it will dismiss the viewController as soon as you reach page 5.
This is not a bug. It's the default behavior of pageViewController.
I think the reason that you sometimes find it behaving the way you wrongly expected is that when you scroll through the pages fast enough the pageViewController stops preloading pages for performance reasons.
The only way you could achieve what you want is a hackish way of accessing the scrollView inside of the pageViewController but it is not safe and might break your app if Apple changes the architecture of pageViewControllers in a future update.
I suggest you put a button in the last page and use it to dismiss the pageViewController.

Related

Forward accessibility scroll not working

This problem has been bothering me for few days now, I have probably tried looking all the questions available but nothing seems to solve my problem.
I have a UIPageViewController and when VO is on backward 3 finger scroll is working but when I try forward 3 finger swipe it just says page 1 of 2 but doesn't scroll or swipes.
UIPageViewController has UIViewControllers in it, which shows HTML content, if I set my
UIViewController.isAccessibilityElement = true
Then it scrolls to next but obviously doesn't get into reading content of view controller.
EDIT:
I have also tried overriding, what I'm noticing is thats being called only for backward scrolls when I try forward scrolling (swipe for next view controller) this is not even called.
accessibilityScroll(_ direction: UIAccessibilityScrollDirection) -> Bool
Any help will be useful.

Pre-loading pages with a `UIPageViewController`, including on the first page turn

When swiping between pages, a UIPageViewController nicely makes sure the next/previous content pages are loaded and ready to go. This is great, but there's a catch: it never does this on the first page turn after a call to setViewControllers (see below). I suspect this may be an efficiency saving where it's reasoned a common case is where a user never swipes. Either way, I'm not taking this as a bug.
So I can (almost) resign to a noticeable delay on the first page turn. After this, everything should be smooth. However my problem is that in one scenario I often make further calls to setViewControllers dynamically depending on user input, and so in this case pre-loading is effectively broken.
What I would like is an API to tell UIPageViewController to always pre-load neighbouring pages - I know a user will always swipe. Failing that, my best workaround is this:
let page = getContentPage()
myPageViewController.setViewControllers([page], direction: .forward,
animated: true, completion: nil)
// Here we must manually pre-load
let nextPage = pageViewController(myPageViewController, viewControllerAfter: page)
nextPage?.loadViewIfNeeded() // 1.
nextPage?.view.layoutIfNeeded() // 2.
This half-works: I now get pre-loading of the view (1.) which saves noticeable time.
My problem is Auto Layout. It's evident that UIPageViewController's pre-loading also nicely handles layout in advance, whereas my manual approach still has a perceptible delay immediately on a page turn. Adding layoutIfNeeded in (2.) does indeed prompt a layout but I still observe a further call on the page turn regardless (to be clear: this only happens for my manual approach on an as-yet unseen page). Worse, when I call (2.) pages don't layout correctly - e.g. UILabels don't properly wrap their text content.
How can I either effectively emulate or prompt a UIPageViewController to always pre-load neighbouring pages, including on the first page turn?

Delay when using instantiateViewControllerWithIdentifier but not performSegueWithIdentifier?

The code below is used to push another view controller onto the navigation stack.
When using instantiateViewControllerWithIdentifier, the segue is noticeably sluggish the first time (~3 seconds) but occurs reasonably fast each subsequent time. Other SO posts suggested ensuring the segue occurs on the main thread, which the code accomplishes, but this didn't fix the problem.
However, using performSegueWithIdentifier causes no delay.
The viewDidLoad code for SendViewController is the same for the first and subsequent pushes.
Tried blanking out viewDidLoad for the destination view controller, but still the lag exists for instantiateViewControllerWithIdentifier but not for performSegueWithIdentifier.
How to fix the delay with instantiateViewControllerWithIdentifier?
No delay:
#IBAction func buttonTapped(sender: UIButton) {
performSegueWithIdentifier(SendSegue, sender: self)
}
Results in delay when showing SendViewController for first time:
#IBAction func buttonTapped(sender: UIButton) {
dispatch_async(dispatch_get_main_queue()) {
let vc = self.storyboard!.instantiateViewControllerWithIdentifier(self.SendViewControllerID) as! SendViewController
self.navigationController!.pushViewController(vc, animated: true)
}
}
This issue could occur in many different scenarios. The best way determine what is causing your specific problem is by profiling with the instruments included in Xcode.
Click and hold the Build button in your xcode window. You will see four options appear, select Profile.
Once the build runs a window with instruments will pop up. Select, Time Profiling from the options.
A new window will appear with various metrics in it. The top left corner will have a red record button. Click the red record button and this will launch the app on your phone.
Proceed to the transition giving you problems. End the recording after the transition occurs by selecting the same button you started the recording with.
Review the "Details" pane in the bottom left corner. You will see a column titled "Running time" that shows the time it took to execute every method in your code (both OS methods and user generated code)
Determine if anything is out of place or occurs that is not intended. Possibly go back and execute the transition again to compare the difference between the two. Clicking the function in the list will take you directly to the code being executed. This can be very helpful.
It is very likely that if a transition takes 3-5 seconds one particular function will be obvious when following these steps. Happy profiling!
WWDC from last year has a great segment on this as well. Def worth checking out here: (open in Safari only) WWDC Profiling Talk
The problem was isolated to the presence of a UITextField in the destination view controller, that is, removing the UITextField removes the lag.
Then it was further isolated to the presence of a custom font.
In other words, using the system font on the UITextField, rather than a custom font, removes the lag. No explanation why, but it works.
After time profiling I realized it was the call to instantiateViewController which I couldn't find anything that could help me with that.
Unfortunately, the only thing that worked was either using a separate storyboard for that view controller and instantiating it from there, or redoing the view controller programmatically.

How to restart UIRefreshControl animation?

I have several tableviews in a tab bar controller. On some of these I have set a UIRefreshControl.
After swiping down to start a refresh the 'spinner' animation begins. If I select a different tab and then move back to the original tab, the refresh control is still shown but the animation has stopped.
How do I restart this animation?
I've tried starting the refresh again and got no results.
I also inspected the view hierarchy, hoping to find a UIActivityIndicatorView but could only find a _UIRefreshControlModernReplicatorView that seems to be the view doing the animation. I don't know of any method I can call of this to get the animation restarted.
I had a similar problem, although not exactly the same problem as described in the original question.
The issue: When I moved from a screen with a tableView with refresh control (not a UITableViewController) to a different one, then I found when I returned to the screen, the activity indicator portion of the refresh control was displaying (not animating) as an artifact just under the top of the table view.
I tried a lot of ideas (hiding the refresh control, ensuring endRefreshing was called, etc.) until I found something that actually worked:
In viewWillAppear(animated:), add the following:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.refreshControl = nil
tableView.refreshControl = refresher
}
Just adding that code fixed the issue for me. Your mileage may vary, but perhaps somebody will find this useful.

UIWebView freezes when text is selected

My app, on very rare occasions, freezes when I select text in a UIWebView. Here's how it happens:
Tap and hold to select text
Text gets selected
Whole app freezes, unresponsive to touch, but I can still see operatings running
May freeze for around 10s. After that the UIMenuController appears. If I try to scroll while it is freezing, the scrolling happens after the app is responsive again
If the text is still selected, it freezes again if I scroll. Happens again and again
If I managed to scroll the selected text out of the view, scrolling the rest of the webview is fine, until I scroll the selected text back
If I get out of the UIViewController containing the UIWebView, I can confirm that they are both deallocated. Go back into a new UIViewController with UIWebView and it happens again.
This continues to happen with new instances of UIWebView, and even when I close and resume the app. Only stops if I force a restart of the app.
Does anyone have any clue how I can debug this? I've never seen this in simulator. Only happens very rarely on device.
I suspect you either have set up your view hierarchy improperly, or have modified it on a thread not the main thread. So tell us exactly where the UIWebView resides (super views), and take a good look at code that modifies sub view arrays.
For instance, you don't add the web view to a UINavigationController's sub views directly, but to its view controllers array etc.
All else fails create a demo project that also displays the problem and upload it to DropBox where we can run it ourselves.

Resources