iPhone - Update current view before pushing new ViewController on UITableViewCell selected - iphone

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

Related

Show Activity Indicator before passing Segue

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

How to call a detail view from a table view within a Storyboard

I have following problem.
I have created a tab based Application with three Views and Viewcontroller.
FirstView(Start screen stuff), SecondView (Detailpage), ThirdView (Table for listing items).
(Connections from storyboard were set automatically).
In the third view a table is integrated and the content is displayed fine.
Now I would like to call the SecondView , when a row in the table is selected.
I also tried to add a forth View , outside the tabBar Controller, to get the Detailview, but this also did not help.
I have tried several tutorials and forum tips, but cannot get it working.
The class is set to the right ViewController, the identifier is set to detail.
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"detail"];
[self.navigationController pushViewController:detail animated:YES];
detail.label1.text = #"It is working";
}
When clicking on the row, it becomes blue, but nothing happens. No error Message , nothing.
Hope that you can help :-)
OK, I have tried to "optimize" my design.
Still have the tab based Views, but when clicking on a row in the table, a new (not linked in Storyboard) view should appear to display the details of the selected quote.
I have created a view in the storyboard and called it "detailzitat"
I have created a DetailViewController.h/m as UIViewcontroller class
I have set the custom class to DetailViewController
I import the DetailViewController.h in the ThirdViewController.h
I have modified the didSelectRowAtIndexPath method in ThirdViewController.h accordingly.
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailVC = [self.storyboard instantiateViewControllerWithIdentifier:#"detailzitat"];
[self.navigationController pushViewController:detailVC animated:YES];
}
and my DetailViewController.m looks like
#import "DetailViewController.h"
#implementation DetailViewController
#synthesize label1, labeltext;
- (void)viewDidLoad
{
[super viewDidLoad];
labeltext=#"JUHU";
label1.text=labeltext;
}
But again, nothing happens, besides the row gets blue.
I do not understand. If I am using this code in a Non-Storyboard project, it is working.
What am I doing wrong ? Is there any tutorial for this combination within Storyboards ? Have not found one for this approach yet.
Try to learn from the different tutorials on the web, but the biggest problem is, most ones are not for iOS5 and I am not so good to transfer then.
Hope to get some hints :-)
You might want to think about your design. If I am understanding your description correctly, the user will be on the third tab, tap on a row in a table, and then you will be switching them back to the second tab. A navigation controller might be a more natural, less confusing, choice there.
But in any case, something like this will work sometimes:
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
DetailViewController *detailVC = [self.tabBarController.viewControllers objectAtIndex:1];
detailVC.label1.text = #"It is working"; // <- this will not work consistently!
self.tabBarController.selectedViewController = detailVC;
}
The problem with this is that while the user is on that third tab, it's possible that the second tab view controller's view is unloaded (due to memory pressure for example).
It's also possible the user went from tab 1 to tab 3 immediately and therefore the 2nd tab's view isn't even loaded yet at all. (To even test the above code you would have to select tab 2 and then tab 3.)
If the second tab's view hierarchy is not loaded, the label1 property will be nil, and so this will not work. A better strategy would be to create a new #property of type NSString* on the DetailViewController. And set that property instead of trying to set the label1 directly.
Then in your viewWillAppear: for the DetailViewController you can update your labels as needed. At that point of course you can be sure that label1 is loaded and connected to the correct UILabel.
I hope that helps.
I think problem is at self.navigationController. If your view is not inside the navigation controller this will not work. So what you do is create a new navigation controller object there and then use it to show your detail view.

How to enable userinteraction in tableview when coming back to tableview

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;

UITableView didSelectRowAtIndexPath called twice

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()

UITableViewController not highlighting cell when it's selected

Hi
I've got simple question which I don't know how to answer.
In my app I've got UITableViewController with cells. When I select one item (cell) it's getting higlighted and in other thread I'm loading chunk of data to display to the user (after load is done new VC is pushed). When doing it with thread user still can interact with application like, going back to other NavController and I do want that to happen. What I don't want to happen is that when loading isn't complete user can select other cell in table and it get's highlted. How I can prevent that (only highlit, I'm checking if there was a previous request so I'm not putting another thread untli previous request is done).
So basicly my question is, how can you foribd user from interacting with table view controller?
Set the selectionStyle of the UITableViewCell's to UITableViewCellSelectionStyleNone.
You can use the following to check if row can be selected:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (rowSelected) {
return nil;
}
return indexPath;
}
So, you only select it if no row is selected. In your didSelectRowAtIndexPath method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
rowSelected = YES;
// call method that is going to do something and mark rowSelected = NO;
}
You can deselect the row by using
[[tableView cellForRowAtIndexPath:indexPath] setSelected:NO animated:YES];
There is a risk that your users will be confused. A highlight is not enough. There should be very clear visual feedback that a network opperation is ongoing and that different rules apply.
either push the details view immediately after the user selected a row and show an activity indicator in there.
or give the whole table view a different look while loading data for the selected row: e.g. Show activity indicator in the selected row & hide the disclosure chevrons in all the other. While doing that, you can set the selection style to 'none'