I am trying to force EGORefreshTableHeaderView to update from the code. When I pull down everything works perfect and the TableView (root) gets refreshed. But I have a modal view where the user can subscribe to certain entities. When he subscribes to one the reload method in the first (root) table view gets triggered. This method establishes a connection to a server, loads some specific data based on the subscription, stores it in a CoreData DB and updates the TableView (root).
The problem is that when the user is only connected to 3G or Edge network the download, which is processed in an own thread, can take several seconds. To indicate the user that something happens I would like to show the EGORefreshTableHeaderView.
I found out that I can set the indent of the refresh view and manually show the loading icon but I was wondering if there is not an easier solution by just triggering a delegate or a method on the EGORefreshTableHeaderView?
Did you try using egoRefreshScrollViewDataSourceStartManualLoading?
Assuming your EGORefreshTableHeaderView instance is named _refreshTableHeaderView, then a call like:
[_refreshTableHeaderView egoRefreshScrollViewDataSourceStartManualLoading:self.tableView];
works for me...
So, it's been too long since I used this, and I forgot I applied the change myself...
I modified EGORefreshTableHeaderDelegate (declared in EGORefreshTableHeaderView.h) to add this additional protocol:
- (void)egoRefreshScrollViewDataSourceStartManualLoading:(UIScrollView *)scrollView;
And the implementation (in EGORefreshTableHeaderView.m):
- (void)egoRefreshScrollViewDataSourceStartManualLoading:(UIScrollView *)scrollView {
[self setState:EGOOPullRefreshLoading];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.2];
scrollView.contentInset = UIEdgeInsetsMake(60.0f, 0.0f, 0.0f, 0.0f);
[UIView commitAnimations];
if ([_delegate respondsToSelector:#selector(egoRefreshTableHeaderDidTriggerRefresh:)]) {
[_delegate egoRefreshTableHeaderDidTriggerRefresh:self];
}
}
Let me know if you need more help there.
(And thank-you enormego for the great work!)
Thanks to Reuven and his code I have improved it a little bit that it can also be used in an UIScrollView that is larger as the screen. Additionally, I have changed the deprecated commitAnimations to block animations.
#pragma mark - Manually refresh view update
- (void)egoRefreshScrollViewDataSourceStartManualLoading:(UIScrollView *)scrollView {
[self.refreshHeaderView setState:EGOOPullRefreshLoading];
//animating pull down scroll view
[UIView animateWithDuration:0.2
animations:^{
scrollView.contentInset = UIEdgeInsetsMake(60.0f, 0.0f, 0.0f, 0.0f);
scrollView.contentOffset = CGPointMake(0, -60.0f);
}
];
//triggering refreshview regular refresh
if ([self.tableView.delegate respondsToSelector:#selector(egoRefreshTableHeaderDidTriggerRefresh:)]) {
[self egoRefreshTableHeaderDidTriggerRefresh:self.refreshHeaderView];
}
}
Related
I am using the EGORefreshTableHeaderView [1] to fetch new data from a server into a UITableView.
This works pretty good but in iOS 5.1 the EGORefreshTableHeaderView does not scroll back into the intended height when the user releases the pull down. Normally it should scroll back to an contentInset of 60px. Then the loading view should be visible for the time which the loading process takes and after that scroll back to 0px inset.
The first scroll-back should happen in the egoRefreshScrollViewDidEndDragging:scrollView method.
- (void)egoRefreshScrollViewDidEndDragging:(UIScrollView *)scrollView {
BOOL _loading = NO;
if ([_delegate respondsToSelector:#selector(egoRefreshTableHeaderDataSourceIsLoading:)]) {
_loading = [_delegate egoRefreshTableHeaderDataSourceIsLoading:self];
}
if (scrollView.contentOffset.y <= - 65.0f && !_loading) {
if ([_delegate respondsToSelector:#selector(egoRefreshTableHeaderDidTriggerRefresh:)]) {
[_delegate egoRefreshTableHeaderDidTriggerRefresh:self];
}
[self setState:EGOOPullRefreshLoading];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.2];
scrollView.contentInset = UIEdgeInsetsMake(60.0f, 0.0f, 0.0f, 0.0f);
[UIView commitAnimations];
//I've also tried it with block animations! But doesn't work!
/*[UIView animateWithDuration:0.2 animations:^{
scrollView.contentInset = UIEdgeInsetsMake(60.0f, 0.0f, 0.0f, 0.0f);
}];*/
}
}
The problem is that when a user releases the scroll view on the half of the screen (shown in the screenshot below), the scrollview does not bounce back into the 60px inset where it should reload the data.
My first idea was that it is because of the animations. So I changed it to block animations but nothing changes. I guess the problem is that the animations are not executed on commitAnimations rather at the end of the loading.
Does anyone have a solution for this?
[1]... https://github.com/enormego/EGOTableViewPullRefresh
I would pull up their demo application and follow their delegate methods.
put this inside didEndDragging:
[_delegate egoRefreshScrollViewDidEndDragging:scrollView];
I have a subclass of UIScrollView that I'm using for images slideshow, with infinite scrolling and circular slideshow.
I used to animate the transition in this way: (Because I wanted the transition to be slower)
[UIView animateWithDuration:1.0
delay:0 options:(UIViewAnimationCurveEaseOut)
animations:^{
self.scrollView.contentOffset = newOffset;}
completion:NULL];
And it worked just fine.
Then I watched the lecture "Advanced Scrolling Techniques" from WWDC 2011, and they recommend to implement infinite scrolling by overriding layoutSubviews.
So I changed my implementation and override layoutSubviews
Once I did that the transition animation stopped working.
If I comment out my custom layoutSubviews - It's working again!
Why??
What can I do to make my own scrolling animation while overriding layoutSubviews?
Thanks!
OK, I think I found the problem, and a possible fix.
The key to understand the problem is understanding that when you call
[UIView animateWithDuration...]
All the properties that you change inside the animation block, are changed immediately. They do not change as the animation actually executing (visually).
So in my case, I'm animating the contentOffset to my target value.
This operation invokes layoutSubviews immediately.
In my implementation of layoutSubviews I'm checking to see if enough movement has been accumulated. If so - I'm rearranging my ImageViews, and set the contentOffset to its initial value. Once I do that there is nothing to animate anymore, because nothing has changed!
Animate contentOffset to X:
calls setContentOffset to X:
invokes layoutSubview
Sets the contentOffset back to Y! (after rearranging the views)
Nothing to animate at this point...
Possible Fix
I'm not sure if it is the right way to do it, but its working working.
So what we need to do is to ensure that no changes will be made in layoutSubviews while we are in the middle of animation block.
So I created a BOOL instance variable named isAnimating, and set it appropriately in the animation block:
self.isAnimating = YES;
[UIView animateWithDuration:self.transitionDuration
delay:0
options:(UIViewAnimationOptionCurveEaseOut)
animations:^{
self.contentOffset = newOffset;
}
completion:^(BOOL finished) {
self.isAnimating = NO;
[self setNeedsLayout];
}];
Now in layoutSubviews we need to take isAnimating into account:
-(void) layoutSubviews
{
[super layoutSubviews];
if (self.isAnimating == NO)
{
[self rearrangeIfNecessary];
}
}
And it's working again!
Hey there StackOverflow! I've finished programming my app, and everything is working fine- so I've decided that since the function part is done, I want to start working on form. However, I have a bit of code that's giving me trouble, and I'm not sure why. My app has two views that I switch between. In viewDidLoad, I have these two lines of code:
[PlannerView setScrollEnabled:YES];
[PlannerView setContentSize:CGSizeMake(320, 735)];
and then later, I switch between the main view and planner view when a button is pressed, like this:
if (isPlannerView) {
// [self setView:MainView];
[UIView transitionFromView:PlannerView
toView:MainView
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromRight
completion:^(BOOL finished){
[self setView:TimerView];
}];
isPlannerView = NO;
} else {
[UIView transitionFromView:MainView
toView:PlannerView
duration:0.5
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:^(BOOL finished){
}];
// [self setView:PlannerView];
isPlannerView = YES;
}
Now, when I use hte commented line of code (self setview), it will scroll with no problems. However, when I use UIView transitionFromView, it no longer scrolls. Any idea what is going wrong here?
The documentation for transitionFromView:toView:duration:options:completion: says:
This method modifies the views in their view hierarchy only. It does
not modify your application’s view controllers in any way. For
example, if you use this method to change the root view displayed by a
view controller, it is your responsibility to update the view
controller appropriately to handle the change.
So make sure that viewDidLoad: is actually being called. It also states:
During an animation, user interactions are temporarily disabled for
the views being animated. (Prior to iOS 5, user interactions are
disabled for the entire application.) If you want users to be able to
interact with the views, include the
UIViewAnimationOptionAllowUserInteraction constant in the options
parameter.
So make sure that user-interaction is enabled on the view after the animation is finished.
completion:^(BOOL finished){
[PlannerView setUserInteractionEnabled:YES];
}];
Source: UIView Class Reference
I just upgraded my iPhone 4 from iOS 4.2.1 to 4.3.2, and to XCode 4.0.2, and I am encountering some bizarre issues with uiview animations. When I first launch my app, code like this executes perfectly:
[UIView beginAnimations:#"fadeAlphaIn" context:nil];
[UIView setAnimationDuration:0.5f];
viewClue.alpha = 1.0f;
[UIView commitAnimations];
But then, after dismissing a presenting and then dismissing a modal view by the standard method:
[self presentModalViewController:more animated:YES];
and
[self dismissModalViewControllerAnimated:YES];
the first animation no longer works. Instead of fading in, for example, the viewClue view simply jumps from alpha = 0 to alpha = 1. Similarly, other animations altering other views' frame property just force the frame to jump from the initial to final value without animation. These animations worked fine before the modal view was presented and dismissed.
I understand that others have experienced animation issues with the upgrade to iOS 4.3.2, but the way the modal view disrupts animation seems very odd. Has anyone else experienced this problem? Any ideas as to a solution? I'm thinking of just adding the modal view as a subview and animation it as it hides and appears, but using the standard modal view method would be much preferred.
Thanks for your help,
James
EDIT: Some more code showing how the app's map is animated
-(void) viewMapfunc
{
AudioServicesPlaySystemSound(soundID);
if(mapvisible){
[UIView animateWithDuration:0.5
delay:0.1
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
map.frame = CGRectMake(0, 350, 320, 27);
mapscroll.frame = CGRectMake(0, 27, 320, 0);
}
completion:nil];
mapvisible = NO;
viewMapLabel.text = #"View Map";
}else {
[UIView animateWithDuration:0.5
delay:0.1
options:UIViewAnimationOptionAllowUserInteraction
animations:^{
map.frame = CGRectMake(0, 50, 320, 300);
mapscroll.frame = CGRectMake(0, 27, 320, 300);
}
completion:nil];
mapvisible = YES;
viewMapLabel.text = #"Hide Map";
}
}
Try to check two things:
Do you commit all started animations? I got all kinds of strange effects after not committing one of them.
Do any animations take place in the same time? Especially with the same view.
Whether any animations take place right after changing properties. Something like:
-
view.alpha = 1;
[UIView beginAnimations:…];
view.alpha = 0;
[UIView commitAnimations:…];
In this example, view will not change it's alpha value from 1 to 0. It will change it instantly. To start an animation you have to extract animations block to another method and call it with performSelectorInMainThread:withObject:afterDelay:. Delay can be even 0.
I solved it by restarting my animation in my UIView subclass:
override func willMove(toWindow newWindow: UIWindow?) {
if newWindow != nil {
spinner.startSpinning() // Restart any animation here
}
}
In the end, I just removed all modal views and implemented them in other ways. For some reason, using modal views messed up animations. Makes no sense, but removing them fixed the problem. If anyone can enlighten me as to why this is going on, it might be nice for memory concerns...
I had the same issue. The root of my trouble was that my animation was being triggered by a notification, and I was adding an observer on each viewWillAppear, but forgot to remove in viewDidDisappear (remember that iOS 6 no longer calls viewDidUnload reliably).
Essentially, I was calling my animation function twice in quick succession, which was causing the visible irregularity. Hopefully this helps someone out down the line!
I've managed to solve this same issue in my own application.
I noticed while debugging that my UIImageViews which I was animating had different memory addresses before and after I pushed my modal view controller(s). At no other time did these UIImageViews switch their memory addresses.
I thought this might have been the root of the issue and it seems I was right.
My client's code had been allocating/initializing my View Controller's UIImageViews in
-viewDidAppear instead of in -viewDidLoad. Thus, every time I launched and dismissed a modal view controller my UIImageViews I was animating would get reinitialized.
Check for yourself if your map object's memory address is changing before and after you launch your modals, and if it is be sure to move your initialization logic to a more proper section of your code.
Hope this helps you!
Dexter
I was using UIView animateWithDuration: and I solved it by not using the completion block. This is code from a subclassed UIView. In the view controller's viewWillAppear: I set self.shouldAnimate to YES, and in the view controller's viewWillDisappear: I set self.shouldAnimate to NO.
-(void)continueRotate {
if (self.shouldAnimate) {
[self rotateRadarView:self.radarInner];
}
}
-(void)rotateRadarView:(UIView *)view {
[UIView animateWithDuration:0.6 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(continueRotate)];
[view setTransform:CGAffineTransformRotate(view.transform, M_PI_2)];
}completion:nil];
}
Ok, this might sound simple, but somehow I can't get this function to work .
- (void)setViewMovedUp:(BOOL)movedUp {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
CGRect rect = self.view.frame;
NSLog(#"%f",self.view.frame.size.height);
if (movedUp)
{
NSLog(#"test Moved Up");
rect.origin.y += 60;
rect.size.height -= 300;
}
self.view.frame = rect ;
NSLog(#"%f after:",self.view.frame.size.height);
[UIView commitAnimations];
}
All the NSLogs are printing results fine and "Test Moved Up" appears so I know there is nothing wrong upstream .
This function is part of the implementation file of my MainViewController ( which manages the root view of my Window app ) .
I assume I have to call : self.view.frame, right ?
No error or warning appears, but it just doesn`t move despite printing out the logs correctly ..
Edit, this is the hierarchy in IB :
Edit again : I'm actually wondering if I'm allowed to move that view up, are we allowed to move views up if they are at the top of the hierarchy ? Then what would be revealed behind ?
Try removing just the three animation messages.
If it moves (I think it won't, but ...)
Try passing in #"moveup" as the first argument to beginAnimations
Try using setAnimationCurve
If it doesn't move
Is the view inside another view that sets a layout for this view?
Are you calling this inside the main thread (not in a background thread)
EDIT: Update
You shouldn't need to do this, but try
[self.view setNeedsDisplay];
If that doesn't work, try it on the window. Changing the frame is supposed to do this automatically.