ECSlidingViewController freezes UIRefreshControl - ios

I'm using ECSlidingViewController in my app. I have a View Controller with a UITableView in it, which uses a UIRefreshControl to execute a long task. When I pull down to refresh, the refresh control starts animating normally but when I go to the top view controller (my main menu in this case) and then go back to my view controller, the refreshControl freezes completely until the task is completed.
To make it clearer, these are the steps I follow:
Pull down my tableView to refresh it.
A long task is executed on background, like:
- (void)pulledToRefresh:(UIRefreshControl *)refreshControl
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
long long a = 0;
for (long long i = 0; i < 100000000000; i++) {
a++;
}
dispatch_async(dispatch_get_main_queue(), ^{
[refreshControl endRefreshing];
});
});
}
While the refresh control is animating, I slide (I don't call the resetTopView:animated: method or any of those ones, I just make a pan gesture) my current view controller to the right to show my Main Menu. When this happens, the refresh control suddenly stops animating (it freezes).
I go back to the view controller I was using at first but it remains frozen.
The task completes and the refresh control ends the refresh naturally, removing itself from my tableView with a nice animation.
Any idea why this happens?

I may have found a solution for this.... Changing the "detectPanGestureRecognizer" method to below, seems to be working for me...
- (void)detectPanGestureRecognizer:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
_isInteractive = YES;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.defaultInteractiveTransition updateTopViewHorizontalCenterWithRecognizer:recognizer];
});
_isInteractive = NO;
}

Related

iOS UISearchController: Wait until UISearchController is dismissed after canceling search

I have a UISearchController that is dismissed when the user clicks the cancel button. After the user clicks the cancel button, I want the UISearchController to be dismissed first then the showNewTableData method needs to be called. Here is the code that I am using that works.
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
dispatch_async(dispatch_get_main_queue(), ^{
[self showNewTableData];
});
}
- (void)showNewTableData {
if (self.searchController.active && self.searchController.searchBar.text.length > 0) {
// show search data
} else {
// show non search data
}
}
Using dispatch_async seems to accomplish my requirements very well, but not sure if this is a good idea. If I don't use dispatch_async, I will end up showing search data because the search bar hasn't finished clearing text and it is still active. Any suggestions are appreciated.
Seeing as how you're trying to do a UI change, its fine to do this in the main thread.

Pull to refresh: screen disappears

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.

UIRefreshControl cancel touch on top of custom cell - Objective-c iOS

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

iOS GUI refresh

I am using setNeedsDisplay on my GUI, but there update is sometimes not done. I am using UIPageControllView, each page has UIScrollView with UIView inside.
I have the following pipeline:
1) application comes from background - called applicationWillEnterForeground
2) start data download from server
2.1) after data download is finished, trigger selector
3) use dispatch_async with dispatch_get_main_queue() to fill labels, images etc. with new data
3.1) call setNeedsDisplay on view (also tried on scroll view and page controller)
Problem is, that step 3.1 is called, but changes apper only from time to time. If I swap pages, the refresh is done and I can see new data (so download works correctly). But without manual page turn, there is no update.
Any help ?
Edit: code from step 3 and 3.1 (removed _needRefresh variables pointed in comments)
-(void)FillData {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *stateID = [DataManager ConvertStateToStringFromID:_activeCity.actual_weather.state];
if ([_activeCity.actual_weather.is_night boolValue] == YES)
{
self.contentBgImage.image = [UIImage imageNamed:[NSString stringWithFormat:#"bg_%#_noc", [_bgs objectForKey:stateID]]];
if (_isNight == NO)
{
_bgTransparencyInited = NO;
}
_isNight = YES;
}
else
{
self.contentBgImage.image = [UIImage imageNamed:[NSString stringWithFormat:#"bg_%#", [_bgs objectForKey:stateID]]];
if (_isNight == YES)
{
_bgTransparencyInited = NO;
}
_isNight = NO;
}
[self.contentBgImage setNeedsDisplay]; //refresh background image
[self CreateBackgroundTransparency]; //create transparent background if colors changed - only from time to time
self.contentView.parentController = self;
[self.contentView FillData]; //Fill UIView with data - set labels texts to new ones
//_needRefresh is set to YES after application comes from background
[self.contentView setNeedsDisplay]; //This do nothing ?
[_grad display]; //refresh gradient
});
}
And here is selector called after data download (in MainViewController)
-(void)FinishDownload:(NSNotification *)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
[_activeViewController FillData]; //call method shown before
//try call some more refresh - also useless
[self.pageControl setNeedsDisplay];
//[self reloadInputViews];
[self.view setNeedsDisplay];
});
}
In AppDelegate I have this for application comes from background:
-(void)applicationWillEnterForeground:(UIApplication *)application
{
MainViewController *main = (MainViewController *)[(SWRevealViewController *)self.window.rootViewController frontViewController];
[main UpdateData];
}
In MainViewController
-(void)UpdateData
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(FinishForecastDownload:) name:#"FinishDownload" object:nil]; //create selector
[[DataManager SharedManager] DownloadForecastDataWithAfterSelector:#"FinishDownload"]; //trigger download
}
try this:
[self.view performSelectorOnMainThread:#selector(setNeedsLayout) withObject:nil waitUntilDone:NO];
or check this link:
http://blackpixel.com/blog/2013/11/performselectoronmainthread-vs-dispatch-async.html
setNeedsDisplay triggers drawRect: and is used to "redraw the pixels" of the view , not to configure the view or its subviews.
You could override drawRect: and modify your labels, etc. there but that's not what it is made for and neither setNeedsLayout/layoutSubviews is.
You should create your own updateUI method where you use your fresh data to update the UI and not rely on specialized system calls meant for redrawing pixels (setNeedsDisplay) or adjusting subviews' frames (drawRect:).
You should set all your label.text's, imageView.image's, etc in the updateUI method. Also it is a good idea to try to only set those values through this method and not directly from any method.
None of proposed solutions worked. So at the end, I have simply remove currently showed screen from UIPageControllView and add this screen again. Something like changing the page there and back again programatically.
Its a bit slower, but works fine.

nested push animation... after [tableview scrollToRowAtIndexPath: ] when returning from Add new item

My app has a method to add a new item to a table (using Core Data for storage) which initializes the new object, adds it to Core Data, and segues to an Edit view.
After editing and hitting the default "Back" button in the Edit view, I do the following in ViewDidAppear in order to scroll my table to the newly added item:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// if a new event was just added then scroll the tableview to it
if (_newlyAddedEvent != nil) {
[self.eventListTable scrollToRowAtIndexPath:_newlyAddedEventIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
_newlyAddedEvent = nil;
}
// dismiss the progress HUD
[SVProgressHUD dismiss];
}
The code executes and throws the following two log messages:
nested push animation can result in corrupted navigation bar
and
Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
And of course, my navigation bar is all messed up.
_newlyAddedEvent is set in my controllerDidChangeContent method for the NSFetchedResultsController.
_newlyAddedEventIndexPath = newIndexPath; in the NSFetchedResultsController's didChangeObject method when type = NSFetchedResultsChangeInsert.
Can anyone give me a hint what to look for to fix this?
I've tried adding [self.eventListTable setDelaysContentTouches:NO]; in ViewDidLoad and it didn't help.
Thanks!
Put this code in dispatch main queue:
dispatch_async(dispatch_get_main_queue(),
^{
[self.eventListTable scrollToRowAtIndexPath:_newlyAddedEventIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
});

Resources