I am working on a project that has a TableView which loads the contents of a JSON file on my server. Everything works correctly, but I have two problems.
1) When I change the View and load a different View, when I came back to this TableView ... the TableView tries to re-load the contents, there is no errors, but the progress bar appears briefly. How to avoid this from happening?
2) My second problem is that, once loaded, if I lose the internet connection and change the View, the content gets lost. Even I already downloaded.
How would I do the cache of this information?
Here is the code:
#interface ProgramacaoTableViewController ()
{
// Object thats hold the content
MProgramacao *_programacao;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// TS MESSAGE
[TSMessage setDefaultViewController:self];
[self.navigationController.navigationBar setTranslucent:YES];
// Add Refresh Control
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(refresh) forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
////
// Check Connection and Load Data
if ([self IsConnected]) {
// YES INTERNET
// show loader view
[ProgressHUD show:#"Loading.."];
// fetch the feed
_programacao = [[MProgramacao alloc] initFromURLWithString:#"http://myurl..."
completion:^(JSONModel *model, JSONModelError *err) {
//hide the loader view
[ProgressHUD dismiss];
//json fetched
[self.tableView reloadData];
}];
}
else {
// NO INTERNET
[TSMessage showNotificationWithTitle:NSLocalizedString(#"Error Message", nil)
subtitle:NSLocalizedString(#"try again", nil)
type:TSMessageNotificationTypeError];
}
}
I edit the code.
You should download the data just in the viewDidLoad, and then when the user WANT, he can pull the tableView to refresh. This is the correct way.
In this way, your tableView will remains loaded also when you push a viewController and then come back, and your "temporary cache" is your array _programacao.
If you want store the data also in case you close the app, you could use for example CoreData, but this is another thing that is not necessary for your purpose.
Related
I implemented pull to refresh functionality for my controller:
- viewDidLoad {
self.refresh = [[UIRefreshControl alloc] init];
[self.refresh addTarget:self action:#selector(refreshData) forControlEvents:UIControlEventValueChanged];
[self.tableView addSubview:self.refresh];
}
- (void) refreshData {
__weak myController *weakSelf = self;
[[NetworkcallManager instance] getCall:^(NSString *result){
if(weakSelf != nil) {
[weakSelf.refresh endRefreshing];
[weakSelf.tableView reloadData];
}
}];
}
The refresh works as expected, but when I pull down the screen too hard then the entire screen disappears for a moment and then the data gets refreshed.
Is there a way to avoid disappearance of the screen? I am wondering if it is because of reloadData function. In case it calls 'viewWillAppear' then I can understand the problem I am facing. (My viewWillAppear code results in momentarily blank screen).
Edit:
If I pull down without internet connection (turned off the wifi) then the screen doesn't disappear.
I got the issue. In the code I am purging the table and then updating it. As a result, the screen disappears momentarily.
i implemented a UIRefreshControl for UICollectionView so that user can pull to refresh the content in UICollectionView. I am testing on the iPad simulator.
On the first attempt, I'm able to pull and refresh the content. However, I notice that the loading icon is still loading and doesn't stop. On my second attempt with the loading icon still showing, I pulled to refresh but it fails to call my selector(refreshCollectionAction).
Here is what I did:
-(void)viewDidLoad
{
// Do any additional setup after loading the view.
[super viewDidLoad];
// Register collectionView pull down to refresh
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(refreshCollectionAction)
forControlEvents:UIControlEventValueChanged];
[self.collectionView addSubview:refreshControl];
self.collectionView.alwaysBounceVertical = YES;
.....
}
-(void)refreshCollectionAction
{
NSLog(#"refresh collection action");
// Empty product Items Array
[[ProductStore getInstance] emptyProductInStore];
NSInteger numOfProductInStore = [[[ProductStore getInstance] allProductItems] count];
if (numOfProductInStore <= 0) {
// Fetch data from webservice and reload collectionView
[self fetchFeed:#""];
}
}
Am I missing some configurations? fetchFeed will request the data from web services. I have verified that the webservice still works.
[self.refreshControl endRefreshing];
Call this method at the end of any refresh operation (whether it was initiated programmatically or by the user) to return the refresh control to its default state. If the refresh control is at least partially visible, calling this method also hides it. If animations are also enabled, the control is hidden using an animation.
UIRefreshControl Class Reference
#interface ProductSearchViewController ()
#property(nonatomic)UIRefreshControl *refreshControl;
#end
- (void)viewDidLoad
{
// Do any additional setup after loading the view.
[super viewDidLoad];
// Register collectionView pull down to refresh
self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:#selector(refreshCollectionAction)
forControlEvents:UIControlEventValueChanged];
[self.collectionView addSubview:self.refreshControl];
self.collectionView.alwaysBounceVertical = YES;
...
}
-(void)refreshCollectionAction
{
NSLog(#"refresh collection action");
// Empty product Items Array
[[posProductStore getInstance] emptyProductInStore];
NSInteger numOfProductInStore = [[[posProductStore getInstance] allProductItems] count];
if (numOfProductInStore <= 0) {
[self fetchFeed:#""];
}
[self.refreshControl endRefreshing];
}
so basically i declare refreshControl as a class variable. as Neil mentioned, i added [self.refreshControl endRefreshing] at the end of method -(void)refreshCollectionAction.
I have a scenario where my tableview reload is not working. Please let me know if I have modeled my design correctly or I need to change it.
I have a core view controller and each tab bar view controller is inherited from this core view controller
Each of the tab bar view controller has a tableview and implements the UITableViewDelegate and UITableViewDataSource protocol.
If the app is launched for the first time, In core view controller's viewDidLoad , I have a method to fetch data from web. In the child view controller's viewDidLoad method I have a method which populates the data source for the tableview.
So the issue is on the first launch, data is fetched and saved successfully in Core data but it does not reload the tableview so its empty.
So Currently as per the below code, on First launch I see the Alert view then the loading view and I see the data getting saved in Core Data. But the first tab bar's table view loads up empty with no data and the reason being, its [reload is not being called]. But on next launch the data is there.
Below is the code
Core View COntroller
- (void)viewDidLoad
{
[super viewDidLoad];
if (!self.myManagedObjectContext) {
//Get Shared Instance of managedObjectContext
self.myManagedObjectContext = [LTDataModel sharedInstance].mainObjectContext;
//Check if managed object context has required data
......
if (<data not found> {
[self fetchDataIntoContext];
}
}
-(void)fetchDataIntoContext {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Initializing..."
message:#"This is your first launch of App"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[message show];
UIView *loadingView = [[UILoadingView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:loadingView];
dispatch_queue_t fetchEventQ = dispatch_queue_create("Fetcher", NULL);
dispatch_async(fetchEventQ, ^{
<add data to core data>
[[LTDataModel sharedInstance] save];
dispatch_async(dispatch_get_main_queue(), ^{
[[self.view.subviews lastObject] removeFromSuperview];
});
});
}
Child View Controller
--(void)setMyArrayOfEvents:(NSMutableArray *)myArrayOfEvents {
if (_arrayOfMyEvents != arrayOfMyEvents) {
_arrayOfMyEvents = arrayOfMyEvents;
[self.eventTableView reloadData];
}
}
-(void) viewDidLoad {
[super viewDidLoad];
//Get Shared Instance of managedObjectContext if it does not exist
if (!self.myManagedObjectContext) {
self.myManagedObjectContext = [LTDataModel sharedInstance].mainObjectContext;
}
//Populate the array which feeds the tableview
[self populateTableViewArrayFromContext];
}
-(void)populateTableViewArrayFromContext
{
<Fetch data dfrom Core Data ......>
self.myArrayOfEvents = [[NSMutableArray alloc] initWithArray:localArray];
}
I solved the issue. The issue was not with Child VC's viewDidLoad not being called. Before the background fetching of data finishes the loading view is removed.
Hence the solution was I added the function that populates the data source arrays in the core (parent) view controller with empty body. The Child VC implements the functionality. And called the method before I remove the loading view. Look at code below that I changed in coew view controller
CORE VIEW CONTROLLER
-(void)fetchDataIntoContext {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Initializing..."
message:#"This is your first launch of App"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[message show];
UIView *loadingView = [[UILoadingView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:loadingView];
dispatch_queue_t fetchEventQ = dispatch_queue_create("Fetcher", NULL);
dispatch_async(fetchEventQ, ^{
<add data to core data>
[[LTDataModel sharedInstance] save];
dispatch_async(dispatch_get_main_queue(), ^{
**[self populateTableViewArrayFromContext];**
[[self.view.subviews lastObject] removeFromSuperview];
});
});
}
-(void)populateTableViewArrayFromContext {
//empty the implemetation is in Child VC
}
I have added the functionality of UIRefreshControl in my project that uses a UITableView. The app works by fetching entries from a web service to a tableview. Below is the code i have used to add UIRefreshControl:
- (void)viewDidLoad
{
[super viewDidLoad];
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
refreshControl.tintColor = [UIColor grayColor];
refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:#"Updating New Entries"];
[refreshControl addTarget:self action:#selector(pullToRefresh) forControlEvents:UIControlEventValueChanged];
self.refreshControl = refreshControl;
[self pullToRefresh];
}
- (void) pullToRefresh
{
counter = 1;
[self fetchEntriesNew:counter]; // My code for updating table view
[self performSelector:#selector(updateTable) withObject:nil afterDelay:2];
}
- (void)updateTable
{
[self.tableView reloadData];
[self.refreshControl endRefreshing];
}
Now if i pull to refresh, it refreshes by adding new entries if there are any and shows me the following view on top of the tableview:
Everything works great except when the app is launched or opened for the very first time, it does not show the view that i have showed in the above image, although it does refreshes the tableview. I want it to show the refresh control view every time it refreshes it. Can anyone point out what i am doing wrong? Thanks!
UPDATE: I have added [self refreshControl beginRefreshing] and the UIRefreshControl's spinner view is now showing but its above the first entry of the tableview. Can anyone point out how to correct it?
This problem had really puzzled me for a while.I found that 4-inch iOS devices don't have this problem, but 3.5-inch devices do.
I tried to find out the differences between the first time that the refreshControl beginRefreshing itself and when I operated a pull gesture.It's the pull operation.
And I checked Apple's document on UIRefreshControl.It says The control does not initiate the refresh operation directly. Instead, it sends the UIControlEventValueChanged event when a refresh operation should occur.
So I thought maybe I could add something to simulate a pull gesture to trigger refreshControl's showing.
[yourScrollView(or tableView) setContentOffset:CGPointMake(0.0f, -60.0f)
animated:YES];
[yourRefreshControl beginRefreshing];
It works!
PS. UIRefreshControl works with UIScrollView, too. [yourScrollView addSubview:yourRefreshControl] just works.
I would move your [self pullToRefresh] call to viewWillAppear instead of viewDidLoad.
There are two things that can be done to add UIRefreshControl in your tableview neither of them is added in your code
1. [self setRefreshControl:tableRefreshControl];
2. [self.m_TableView addSubview:tableRefreshControl];
Either add 1 or 2 if your class is subclass of UIViewController
If your class is subclass of UITableViewController then try to replace
self.refreshControl = refreshControl; with 2 line
before the code:
[refreshControl beginRefresh]
insert the code:
[refreshControl layoutIfNeeded]
My first screen that contains a uitableview, I used to call webservice method in viewdidload:
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title =#"Brand List";
getBrands *obj = [[getBrands alloc]init];
[obj getBrandsList];
getBrandsList, but before the webservice finished, it loads the screen, so the screen contains an empty list.
So I need to wait (ie. loading dialog) until the method completes and then show the screen with data.
How do I do this?
Create an new View with an UIActivityIndicatorView and an UILabel. Then add it on top of your window with some CATransition. When you finish download remove this screen.
-(IBAction) buttonClick: (id) sender{
//show loading dialog here(activity indicator)
[self performSelectorInBackground:#selector(callWebService) withObject:nil];
}
-(void) callWebService
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
//resend request and parse response.
// create new view controller object and push screen
// hide loading indicator
[pool release];
}