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.
Related
I have an issue with my UIRefreshControl : after the first time it was triggered, it cancels touch on top of my first custom cell (even if it is hidden), like if it was invisible.
If I remove it from superview, then add it again, the issue is still there.
Actually I have a UITableView that is populated with some data. The user can refresh the table view by pulling it and for that I've added an UIRefreshControl to my table view like this in viewDidLoad()
self.refreshControl = [[UIRefreshControl alloc]init];
self.refreshControl.tintColor = [UIColor colorWithRed:0.35 green:0.78 blue:0.98 alpha:1.0];
[_tableView addSubview:self.refreshControl];
I've not added selector to the refreshControl object because I want the tableView to refresh only when the user stopped touching the screen. So for that I've added this ScrollView's delegate method :
- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView {
if (self.refreshControl.isRefreshing) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_sync(dispatch_get_main_queue(), ^{
[self refreshTable];
});
});
}
}
And here is my refresh method :
- (void)refreshTable {
//refresh code
[self.refreshControl endRefreshing];
}
I don't want to put my app online with this issue, even if this does not block user to use it !
Thanks in advance for your help !
I've found the solution by going to this post
So effectively in my case I did not have to add a selector, just implement those UIScrollViewDelegate's method :)
Maybe this is purely simulator related. I have not tried it on an actual device yet.
I'm on the latest greatest MacBook with a 1TB flash drive, and 95% free processor, and less than full memory consumption.
I have a UIPopoverController with 4 items in it, sized to those items.
There's nothing complicated or multi-threaded or long running in any way associated in the UIPopoverController in question.
I've set the appear and dismiss animation at 0, yet when I tap on an item in the list, there seems to be an random indeterminate delay between 0 and .4 seconds in the popover disappearing. Of course the 0 is expected, but the times when it's nearly a half second is very noticeably longer and disconcerting.
Any idea what may be causing that?
Code that shows the popover...
-(IBAction)theLandImpsButtonPressed:(UIButton *)sender
{
iRpNameValuePopover *thePopoverContent = [[iRpNameValuePopover alloc] init];
thePopoverContent.theTableValues = [self getLandImpsChoicesList];
impsLandPopover = [[UIPopoverController alloc] initWithContentViewController:thePopoverContent];
thePopoverContent.thePopoverController = impsLandPopover;
impsLandPopover.popoverContentSize = [iRpUIHelper sizeForPopoverThatHasTitle:NO andListContent:thePopoverContent.theTableValues];
impsLandPopover.delegate = self;
[impsLandPopover presentPopoverFromRect:self.theLandImpsButton.bounds inView:self.theLandImpsButton permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
}
Code that dismisses the popover...
BTW, there is no evaluation time incurred here [self userChoiceIsValid] because it simply returns YES right now.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
_theChosenNameValueItem = [self.theTableValues objectAtIndex:indexPath.row];
[self acceptUserChoiceAndClose];
}
// This contentViewController is encapsulated INSIDE a UIPopoverViewController, and this class cannot itself
// close the popover which contains it, hence the need for the reference to the popover controller
// It is the popover's delegate... the one that created the popover, that is able to close it.
-(void)acceptUserChoiceAndClose
{
_theUserChoseAValue = NO; // Start by assuming they didn't chose a valid value.
if ([self userChoiceIsValid])
{
// Set variable that indicates the user chose a value which can be saved to core data, and/or presented on screen.
_theUserChoseAValue = YES;
// Close the popover.
[_thePopoverController dismissPopoverAnimated:NO];
// Notify the class that presented the popover that the popover has been dismissed.
// It will still be available to the dismissal method where code can retrieve the user's choice, and set the popover to nil.
if (_thePopoverController.delegate && [_thePopoverController.delegate respondsToSelector:#selector(popoverControllerDidDismissPopover:)])
{
[_thePopoverController.delegate popoverControllerDidDismissPopover:_thePopoverController];
}
}
else
{
[self showValidationFailureMessageToUser];
}
}
Dismissing the viewController in main thread will solve the issue.
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:nil];
});
I would check it out in the profiler and see what the time is being spent on.
There's a good tutorial here.
UIPopoverPresentationController *popOverView;
//////
Dismiss it...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[popOverView.presentedViewController dismissViewControllerAnimated:NO completion:nil];
popOverView = nil;
});
});
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.
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 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]