UINavigationController Interactive Pop Gesture only works first time - ios

I have been working on an app for some time and just realized the swiping back in the detail view only returns me to the master view the first time. It also isn't smooth, even when it works on the first time. Instead of smoothly going to the master view, it jumps all at once, even when I swipe slowly. It used to work correctly, but I haven't been testing for this specifically, so I don't know when it stopped working and what I changed to cause this.
A little about how my app is setup...
I have a split view controller that is connected to my MasterTableViewController and DetailViewController.
Both of those are have TableViews and are embedded in Navigation Controllers.
I have set it up so that the app originally loads to the MasterTableViewController instead of going immediately to the DetailViewController, but even when I take this out, the interactive pop gesture doesn't work.
I don't believe I've messed with any of the back button controls. I have looked through my code and storyboard and can't find anywhere that I have. This is part of what is most confusing because these questions (1, 2, and 3) all seem to have problems stemming from changing the back button or can be fixed by entering the following line of code:
self.navigationController.interactivePopGestureRecognizer.delegate = nil
Adding that to my code seems to have no impact on how it behaves.
Here is a picture of how it is setup for reference:
I can usually figure out these things on my own, but this problem baffles me because it works the first time, but not any others. As far as I can tell, nothing changes between the first time and the others. I don't know if anybody else has had the same issue, but any help on why this might be happening would be greatly appreciated. I can provide code or answers to questions on how I am doing certain things if needed. I haven't put any in because there are so many different things controlling this piece that I don't know where to start.

When are you calling self.navigationController.interactivePopGestureRecognizer.delegate = nil?
Doing this will definitely disable interactive pop. It sounds like you may be calling this after a certain UIViewController appears.
What other modifications to UINavigationController are you making? Are you using appearance delegate?
Are you subclassing? If so, are you calling super in all of your method overrides?
Also check your overrides of viewWillAppear in child ViewControllers. This method gets called during an interactive pop. If you are doing a lot of computation (or synchronous calls) on the main thread within this method, it could cause frame drop, hence the choppy animation.
Hope this helps

From Alex Chase's answer : Also check your overrides of viewWillAppear in child ViewControllers. This method gets called during an interactive pop.
added it to viewWillAppear and it worked:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.interactivePopGestureRecognizer?.delegate = self
}

Related

UITableView Header Snaps to the Top of the Screen on Certain iPhones

Sometimes (but not all the time!) on iPhones (and iPhone simulators) I notice my UITableView header has this 'snapping' behavior that, when I try to drag down from the top of the screen it snaps back up instead of fluidly moving back up like a tableView normally behaves.
I'm wondering if anyone knows of this bug, what causes it, or how I can fix it? I feel like it might have something to do with UITableViewHeader but I'm not sure.
Unfortunately, I cannot share the code, but I don't believe it is something in the code. I manually commented on almost every line of the code and the problem persists!
Here was the problem for me and I'm pretty sure its a bug in XCode/Swift.
At first my navigation controller in the storyboard has this setting:
In my code I have the following method declared:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
let hideBar = (viewController == self)
navigationController.setNavigationBarHidden(hideBar, animated: animated)
}
Used to disable the navigation bar on the home screen. The combination of these two (if the method is declared on the home screen) causes a bug as shown above. I am able to repeat this bug in a new project.
The solution to this bug is to check the Show Navigation Bar box on the Navigation Controller
Since code is not provided for this question, I am answering based on my assumption.
Assumption 1 - Using manual layout.
If you have code in layoutSubviews(). Then you might want to check your calculation again. Make sure view frames are calculated one time in layoutSubview() method.
Assumption 2 - View animation is blocked/delayed by some other tasks on main thread.
Make sure to run no-UI/API code in background thread.
Reuse cell instances: for specific type of cell you should have only one instance, no more.
Don’t bind data at cellForRowAtIndexPath: method ‘cause at this time cell is not displayed yet. Instead use tableView:willDisplayCell:forRowAtIndexPath: method in the delegate of UITableView.
Hey, can you move your header view content in first cell of first section, so that you can avoid header view problem. Then check if snappy problem is occurring.

touchesBegan delay after presenting UIView

I'm trying to load a UIView and then right away detect touches on that new view. Currently, overriding touchesBegan gives a delay of around a second.
So, I load up the UIView and immediately keep tapping on the screen. It takes around a second for touchesBegan to be called. From that point on, all is well. However, I can't afford the ~second wait initially.
I have stripped back all the code in the UIView to just the barebones incase anything was clogging up the main thread, but the delay still persists.
How can I go about getting immediate feedback of touches from a newly presented UIView? Thanks.
-- EDIT BELOW --
I've been playing around with this for the past few hours. Even when creating a custom UIWindow and overriding sendEvent, the UITouchPhase gets halted when the new view is displayed. To begin receiving events again I have to take my finger off the screen and place it back on the screen. (I don't want to have to do this).
The problem seems to lie with the segue to the new view controller. When it segues, the touch phase is ended. If I simply add a subview to the current view controller, I see the desired functionality (i.e. instant responding to touch).
Given my newly presented view contains a lot of logic, I wanted to wrap it all up in it's own view controller rather than add it to the presenter view controller. Is there a way for me to do this and use 'addSubview` to present it? This should hopefully achieve the desired effect.
In the end, I created a custom view controller with it's own xib. Where I would have segued, I now instantiate that custom view controller and append it's view. This has eliminated the touch lag.
Have you disabled multi-touch? There's an inherent delay while the controller waits to see if there's a follow up touch (on all single touches). The initial sluggishness might be from loading up the multi-touch code and deciding what to do about it.
myViewController.view.multipleTouchEnabled=NO;
As to your final question, look into view controller containment. Since iOS 5 Apple has provided the hooks officially and safely to present one view controller as a sub view of another.
Sadly I've no insight as to the greater issue.
I found an answer that worked for me from a similar question asked here:
iOS: Why touchesBegan has some delay in some specific area in UIView
This solution isn't check-marked on that thread, so I'll copy it here to make it easier to find.
override func viewDidAppear(animated: Bool) {
let window = view.window!
let gr0 = window.gestureRecognizers![0] as UIGestureRecognizer
let gr1 = window.gestureRecognizers![1] as UIGestureRecognizer
gr0.delaysTouchesBegan = false
gr1.delaysTouchesBegan = false
}

Sliding my view controller back in iOS 7 is very slow for only one view controller

Has anyone encountered a similar issue? I have one view controller that it lags greatly on the sliding when I pull my finger across the screen to go back, but only on about the first half of the slide. The rest goes smoothly. If so, how does this get counteracted? I honestly can't seem to find anything that might cause it. Even removing all my gesture recognizers does nothing to help it. But other view controllers slide perfectly in my app.
It's hard to say without you being more specific, but it sounds like you might be performing some intensive tasks in the viewWillAppear: function of the view controller you're returning to. If the main thread is blocked here, even for a short amount of time, it could result in this behavior.

Performance issue of dismissViewController with Core Data

I have two View Controllers, the main one and popup one. Each of them contains a UITableView and a fetchedResultController to provide data.
the popup is setup inside storyboard, and I add BarButtonItem to dismiss the popup. Dismiss code is very simple
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil]
the issue is It took couple seconds for the popup to dismiss, the app freeze once I click the "dismiss" button.
If I commented the setup of fetchedResultController in the Popup view controller, the dismiss happens immediately. So I assume the problem is with core data. but no idea what it could be.
I have also tried run Instruments to see the problem, no other codes of mine is executed except the above dismiss codes. (although I'm not experienced with Instruments well)
Any help/hint would be appreciated.
OK, It turns out that the performance issue has nothing to do with core data and tableview. the guilty goes to a UISwith, which I apply a Scale to it to make it smaller than usual switch. After I remove the scale code, it behaves normal. so Now the problem is I need to figure out why the scale make it so slow, but it's another question.

Using a custom segue for moving backwards on a navigation stack

I'm moving my App to Storyboards and, so far, so good.
However, I've found something that I don't really understand and worries me. I would appreciate if someone can provide some insight on this.
My app uses a normal Navigation Controller. For moving "forward" to new View Controllers, I'm using custom segues; no problems there. However, there's a point in the App where I want to move back to the beginning of the Navigation Stack. I have also configured that "navigation" using a custom segue, for that, I created the segue in Interface Builder by dragging the last view controller to the first one (that already looks weird to me), and I've implemented the custom segue perform method in the following way:
-(void)perform
{
UIViewController *src = (UIViewController *)self.sourceViewController;
UIViewController *dest = (UIViewController *)self.destinationViewController;
[src.navigationController popToRootViewControllerAnimated:NO];
// Custom animation code here
}
... It works great. However, I don't understand why it works. In my mind, the custom segue should be instantiating a new instance of my first view controller and assign it as "dest", but it looks like the segue is smart enough to realize I want to navigate to a previous, existent, instance of a View Controller and, instead of creating a new instance, it assigns to "dest" the existing one.
Does anybody know if using segues in this way is ok? Is it possible that it works by chance but might stop working in the future? Am I wasting memory in anyway as the segue is instantiating a View Controller I'm not going to use?
Thanks a lot in advance!
Am I wasting memory in anyway as the segue is instantiating a View
Controller I'm not going to use?
Yes sir! By using a segue, you effectively allocate a new view controller as it's needed to set the DestinationController property for your custom segue. Test by yourself : add a static counter into your root controller, increment it each time this class is initialized and display it in your view : you'll see it getting incremented every time you pop to root using this trick.
Does anybody know if using segues in this way is ok?
As long as you're effectively wasting memory, no!
There's at least one solution to this problem : release the DestinationController of the segue in your (void)perform implentation. This is really quick to implement, but kinda ugly since you allocate and immediately release your view controller every time... even if it's better than just leaking it, it's not what I'd call a good practice!
To my mind, a better way to achieve what you want would be to not use a segue for that transition, just to use a button or whatever and call popToRootViewController:animated when getting a touch on this button.
Is it possible that it works by chance but might stop working in the
future?
For both the first solution I suggested and the way you're currently doing it, I see absolutely no reason : these are not complicated tweaks, just 'bad-implemented' standard navigation. The second solution is perfectly normal so no worries.

Resources