UINavigationBar not updating bar button item until after table view update - ios

I have a UITableViewController embedded in a navigation controller. My rightBarButtonItem is a refresh button. When pressed, the button triggers an update from a server. The update is synchronous (when I have time, I'll change it to an asynchronous request). What I want to do sounds simple in theory. Once the button is pressed:
Change the bar button item to a UIActivityIndicatorView
Refresh the data
Reload the tableView
Change the bar button item back to a refresh button.
What's happening, however, is that I will set the rightBarButton to a view with an activity indicator view, but it's not getting updated until after the table view reloads, which is obviously pointless. My server update routine looks like this:
View Controller calls a separate model object's "refresh" method.
model object synchronously gets the new data from the server
View controller calls [self.tableView reloadData];
Since it's synchronous, I thought it would simply progress one step at a time, but that's not working. How can I make it so that the activityIndicatorView shows and disappears when it's supposed to?

When you modify the navigation bar, the modifications aren't actually applied to the screen immediately, they are applied during the next screen update cycle, or next screen refresh, or next redraw.
In your code, it seems like you send a synchronous request to the server immediately after setting up your navigation bar. That request now blocks the main thread, and the system cannot refresh the screen, as all UI code runs on the main thread.
Using an asynchronous request, or simply sending the request on another thread, or some other witchcraft you'd like to try with a synchronous request will help you solve your problem. But the essence of these solutions would be the main thread will not be blocked by any synchronous methods.

Related

advice - Bottom navigation handling

I am new in iOS and I have tab bar. I have 4 tabs like Facebook.
In my case all data should be updated from server anytime when user goes one of that screen.
One guy said calling viewDidLoad frequently can make memory leaks. Is that true?
So what's the best play here? Every time call viewDidLoad() and load data from server or there is another way to handle this not calling viewDidLoad() every time.
There is no pull to refresh in that screens
viewDidLoad() will only be called the first time the view controller is loaded. Using a tab bar controller will usually keep the view controllers in memory as the user switches tabs so if you want the loading to occur every time the user goes to a new screen, this is not the best place for it.
I would suggest using viewWillAppear or viewDidAppear. If you're updating data from the network, make sure to do the loading on a background thread to ensure the interface does not get blocked (regardless of which method you use).
Personally I would put network loading code inside viewDidAppear as to me it makes more sense to call the network after the view has finished appearing since it will presumably not finish immediately. This way it is also easier to present a UI element that shows data is loading to the user.

App crashes if the view disappears while the UiTableView is being reloaded

I have a view with the list of entities pulled from a remote source (via web-service). App crashes, if back button is pressed (view with tableview disappears) while the entities are being loaded on to the table view.
If any keyboard appeared, resign the keyboard.
What about using NSOperation to retrieve the data from your Web Service, and cancel it if the user press the back button?
http://nshipster.com/nsoperation/
Another Option would be to check the view in the completion handler (when your data is ready)
you have to stop the remote service call in viewWillDisappear (or) use IgnoringInteractionEvents up to that page load.

popViewController while pushViewController is animating resulting in corrupted navigation bar

I have a situation in my app where a navigation controller pushViewController:animated:YES is triggered by a user. The user can also trigger a popViewController:animated:YES by tapping another control. The intent is that the popViewController is the undo or inverse of the pushViewController.
However, if the user triggers the popViewController:animated:YES while the pushViewController animation is still happening, I get a message logged to the console:
2014-08-22 08:26:36.601 MyApp[22380:60b] nested pop animation can result in corrupted navigation bar
2014-08-22 08:26:36.960 MyApp[22380:60b] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
And indeed, the navigation bar does get corrupted: the back button is no longer visible. I have to go do something else then come back to the affected page in my app to get it working properly again.
What can I do to avoid this race condition? My first thought was protect the popViewController call with a check to see if a current navigation animation is already occurring, and waiting for that to finish (or even canceling the popViewController call completely). However, I haven't been able to find a way of detecting that an action is already occurring.
There are two solutions to this problem. I suggest you to implement first one.
1 . Avoid user interaction on multiple controls at a time by setting exclusive touch to them.
Set exclusiveTouch property to YES for those controls if they shares superView(parent view) else you will have to set this property YES to their parent views.
2 . Implement UINavigationControllerDelegate protocol in that view controller where user is tapping multiple controls at a time.
– navigationController:willShowViewController:animated:
– navigationController:didShowViewController:animated:
Set a flag when first delegate method gets called & reset it in second. Use this flag on every push/pop operation.

how to reload tableview upon return to tableviewcontroller

In my app, there are tabs. I have one tableviewcontroller that contains messages and I have implemented pull to refresh so that works fine. However, if the user goes from the message tab to another tab and then back to the message tab, the uitableview doesn't reload and the user has to pull to refresh. I have thought of putting [self.tableview reloadData] or [self loadObjects] (i am using Parse) in viewDidLoad/viewWilAppear, but that doesn't seem to work...it's because they are only called when the view controller is initially visited right? So I'm wondering as to where I should put that code so that the table view can be reloaded every time the view controller is revisited?
viewWillAppear and viewDidAppear are both called every time the view controller appears. If you call reloadData in one of those methods then it will refresh the table view.
I think your problem is you aren't updating your data source. You will need to make another call to Parse otherwise your table view will just reload with the same data.
I don't think that this behavior is desirable. I suggest you tu wait for a reasonable timeout before updating data, or your user will experience lags and high network usage.
However to do that, you should set a delegate that fires when the corresponding view is loaded (take a look here how to get the event that switch tab menu on iphone ) and then call
[yourTableView reloadData]
inside didSelectViewController

Cancel requests on subview when navigating from parent viewcontroller

I'm currently working on an app with a viewcontroller that has a number of subviews. Some of these subviews make requests (as in urlRequests) for data to display within the subview.
Now if someone navigates away from the main viewController I want to cancel any unfinished requests in the subviews. Is it enough to cancel these requests in the "dealloc" method of a subview as obviously they don't have a viewwilldisappear method. I'm using ARC and iOS 6 for what it's worth. Otherwise should I create my own cancel method in the subviews, and then loop through them in the viewWillDisappear of the main viewcontroller to call this method?
Or is the approach of the subviews making the request wrong to begin with?
The closest thing I could find to my question was view will disappear is not firing which seems to suggest dealloc should work.
Thanks
From my point of view, your views (the subviews) should only take care of displaying a content.
The requests should be done by a dataManager singleton for example or from the view controller (You could use an NSOperationQueue).
I would then create a cancel method that stops the current request and remove the next ones from the queue.
Putting the cancel code in the dealloc or the viewDidDisappear is up to you. If you think you're view will only disappear for a short amount of time then put it in the dealloc.
You could also listen to the UIApplicationDidEnterBackgroundNotification notification to cancel the requests too.

Resources