Ok my First Controller is a UITabel and when a cell is passed it pushes to the Second View Controller. The Second Controller loads data from a webservice so the segue takes a little. I would like to show a SVProgressHUD when the cell is selected and then dismiss it after the data loaded but the SVProgressHUD instead shows after the Second View is loaded and only for a a couple milliseconds.I use this right now:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[SVProgressHUD show];
UITableViewCell *acell = [tableView cellForRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"pushCell" sender:acell];
}
Any way to show the SVProgressHUD immediately when cell is selected?
Apple recommends that when the user clicks on a tableViewCell the app should segue to the next view controller immediately. The fact that you feel you need to show a SVProgressHUD while the next view loads suggests to me that you are requesting the data using a synchronous method, which will block the main thread and make it appear that your app has frozen while it waits for the response.
The best practice for retrieving data is to always do so asynchronously (or on a background thread) so that your app always remains responsive. I would recommend changing your request to an asynchronous method (look into the NSURLRequest method initWithRequest:delegate:, for example). If you use that you can then show the SVProgressHUD in your second controller's viewDidLoad method, the transition to the second view controller will happen instantly when the user taps the table cell, and your app won't be locked up while the user waits for the data to download.
Here is more info about this method from the Apple Documentation
Related
In my app I am doing tableView. By clicking row, the new view will navigate. Before navigation of new view, application is processing data from server. Until processing of data finished, view will not navigate. In this time period I want to disable userinteraction of tableview, so user can not select further rows.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
tableView.userInteractionEnabled = NO;
}
Note that, When I again come back to the tableview, user will again allowed to select any row.
I do not have any idea, how can I enable userinteracation again.
I assume that you are getting your data asynchronously.
In this case, you can re-enable user interaction from your connection delegate method. If you are using NSURLRequest/NSUrlConnection, the delegate method would be: connectionDidFinishLoading: (don't forget also about connection:didFailWithError:).
From NSURLConnection Reference:
connectionDidFinishLoading:
Sent when a connection has finished loading successfully.
So, when the data is there and you are done with the processing, you re-enable the table view.
For Coming back after navigation, Put following sentence in your viewWillAppear That will again allow your table view user interaction to true.
tableView.userInteractionEnabled = YES;
Under certain circumstances, UITableView didSelectRowAtIndexPath is being called twice causing the error Pushing the same view controller instance more than once is not supported.
Here's are the sequence of events:
TableView::didSelectRowAtIndexPath.
TableView::viewWillDisappear.
PushedViewController::viewWillAppear.
TableView::didSelectRowAtIndexPath.
Error: Pushing the same view controller instance more than once is not supported'
The only thing worth noting is that the UITableView is loading images asynchronously, but that never calls didSelectRowAtIndexPath. Also, the PushedViewController is reused to avoid having to reload it each time a cell is selected in the UITableView.
Anyone have any idea what may be causing this?
Thanks.
I'm seeing this problem too, probably one out of a 1000 users gets affected, or less. I сan clearly see two didSelectRowAtIndexPath registering 50 ms one after another. My guess is that it is a bug in iOS - no new taps should be directed to old view once new view-controller has been pushed. Alas, it is likely up to us to write code guarding against this. Here's what I'm thinking:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
if (self.navigationController.topViewController != self)
return;
... do other stuff
}
Disable user interaction after the first "didSelectRow". It's possible for multiple taps to "stack up" during the transition.
It usually takes someone with amazing dexterity in their fingers to get this behavior, but still.
if you already created Storyboard Segue don't call;
[self performSegueWithIdentifier:#"TYPE" sender:self];
in this method;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
This occurs because of the segue you are using inside the didSelectRowAtIndexPath method. Ensure that this segue is created by ctrl dragging from the view button on top of the source view controller to destination view controller. The error occurs when you create the segue by ctrl dragging from the table view cell to destination view controller.
You do not need to change any of you code. Just delete the segue and create it in the correct way. Hope this solves your problem, if I got the question right.
Swift 5
In my case also helped to update the cells instead of this code:
tableView.reloadRows(at: [indexPath], with: .automatic)
to use this:
tableView.reloadData()
I have a UITableView with a list of items. On selection of an item I'm navigating to a new view that displays the details of the selected item using:
DetailsVC *detailsView = [[DetailsVC alloc] initWithNibName:#"DetailsVC" bundle:nil];
[self.navigationController pushViewController:detailsView animated:YES];
Now, the details view is getting the data from the remote location so on slow connection it can take a few seconds. What I want to do is display an activity indicator over the selected row on selection.
The problem is the display of the added indicator gets delayed until the next view is ready to navigate to, which makes the indicator usless.
I've tried to add this indicator in those 2 events with the same effect:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Is there a way to add the indicator (or more general, modify the content of a UITableViewCell) in the moment of selection, before navigation occurs.
As a experiment I've also tried to pop up an alert view in the same two events which resulted in poping up the alert after navigation to the details view.
So it's [[DetailsVC alloc] init...] that takes a few seconds, right? This problem is that any view changes you make don't fully take effect until returning all the way back to the runloop, so even if you setup the indicator before creating your object, it too is waiting for the init to finish to make itself visible. What you need to do is defer the creation of your DetailsVC until after the indicator is setup.
I might be a simpler change to use blocks but I can't recall the details of that off the top of my head (haven't used blocks much since code I've been writing lately has had to stay compatible with 3.x). But to use performSelector is easy too, take those 2 lines you first quoted in your question and put them into its own method, such as:
- (void)pushDetailsView {
DetailsVC *detailsView = [[DetailsVC alloc] initWithNibName:#"DetailsVC" bundle:nil];
[self.navigationController pushViewController:detailsView animated:YES];
}
And where you had those lines before, setup your indicator and then do this (a delay of 0 doesn't mean to called it immediately, but rather ASAP after returning all the way out of the current call stack):
[self performSelector:#selector(pushDetailsView) withObject:nil withDelay:0]
you can call cell = [self rowForIndexPath:indexPath] in one of your functions. and then add the indicator to cells subview
You can either
add the indicator when u select row and push new view when data came
(simultaneously remove indicator at that time) or
Push new view, call web service to get corresponding cell data (in
viewDidLoad or viewWillAppear) and display indicator simultaneously
and when new data comes remove indicator
I have a tab bar application which loads a UITableView when one of it's buttons are selected. It seems to load the view controller however it doesn't seem to be populating the data. I have tried setting the cell.text = #"cell" (while setting the number of rows > 0) and an NSLog in the CellForRowAtIndexPath proves that in fact the function is not being called. The same NSLog output to the console in the viewDidLoad function also generates no output so it seems as though its not getting called.
Any suggestions?
Try and call [tableView reloadData] from your -(void)viewWillAppear:(BOOL)animated; method. This should be called each time the user navigates to that tab and should set off the delegate callbacks that aren't currently being called. If they still aren't called make sure you have set the table delegate to self.
EDIT: Actually make sure you have set your table view's delegate and datasource to self;
I have data being refreshed in a modalViewController and when that data gets refreshed, the parent controller needs to refresh its data as well. I tried doing a [tableView reloadData]; but it didn't work properly since the actual array values aren't being refreshed. Is there a way for me to reload a controller without the user seeing any animation?
Thanks for any help!
Are you refreshing the data off the main thread? If so, you need to call the reloadData method using the following method:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
So for a tableView it would be something like:
[tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
The underlying view can get released while the modal view is showing, so you shouldn't try to modify it directly. Instead, you can use the viewWillAppear: method on the main view controller to make any necessary updates.
I'm not sure what you mean that "the actual array values aren't being refreshed". How the two view controllers exchange data depends a lot on what you are trying to accomplish. If the modal view is only used in one place, the main controller can access its properties directly. Or maybe you want a dedicated class to hold the state... Again, depends on the context.