I am stuck in something I hope you guys can help
I have a scrollview , when the user scrolling a subview appear with animation from bottom to top. the timer then start counting 5 sec and then call another method to hide the subview
I implemented and it works as wanted except :
while the subview appear and when it's almost to hide , if I scrolled that moment the subview appear statically and never hide . try to scrolling again another subview dynamically work over the static one ( as it duplicated or something)
this is my code for controlling show and hide of subview
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if(!show){
[self showSubview];
if (!myidleTimer)
[self resetIdleTimer];
}
}
-(void)resetIdleTimer
{
//convert the wait period into minutes rather than seconds
int timeout = kApplicationTimeoutInMinutes;// equal to 5 seconds
[myidleTimer invalidate];
myidleTimer = [NSTimer scheduledTimerWithTimeInterval:timeout target:self selector:#selector(idleTimerExceeded) userInfo:nil repeats:NO];
}
-(void)idleTimerExceeded
{
if (show){
[myidleTimer invalidate];
[self hideSubview];
show=false;
}
}
"show" is a bool to insure when to hide and when to show
her is the show / hide implementation
-(void)hideSubview{
[UIView animateWithDuration:0.5
animations:^{
subview.frame = CGRectMake(0, screenWidth, screenHeight, 60);//move it out of screen
} completion:^(BOOL finished) {
[subview removeFromSuperview];
subview.frame=CGRectMake(0,screenWidth, screenHeight, 0);
}];
show=false;
}
-(void) showSubview{
subview = [[UIView alloc] init ];
[self.view addSubview:subview];
subview.frame = CGRectMake(0, screenWidth, screenHeight, 60);
[UIView animateWithDuration:1.0
animations:^{
subview.frame = CGRectMake(0, screenWidth-60, screenHeight, 60);
}];
show=TRUE;
}
I hope that's clear enough to understand and be able to help me identify the problem
thanks in advance
The timer will not fire while the scroll view is being scrolled, if you create the timer the way you do. Instead, create it as below.
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:#selector(doStuff:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: adds the timer to the run loop using the defaultRunLoopMode instead of the NSRunLoopCommonModes, which is the one you want to have timer fire while the user scrolls.
Related
I have a UISlider and I am trying to add subviews dynamically on the track of the UISlider with animation. I need 1 second time delay before adding each subview. How can I achieve this?
This is a basic setup of how you can go about achieving it:
#interface ViewController () {
NSTimer *_timer;
CGRect sliderFrame;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.slider.minimumValue = 0.0f;
self.slider.maximumValue = 100.0f;
sliderFrame = self.slider.frame;
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(addLabel:) userInfo:nil repeats:YES];
[_timer fire];
}
- (void) addLabel:(NSTimer*) timer {
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(sliderFrame.origin.x + self.slider.value/100 * self.slider.frame.size.width, sliderFrame.origin.y - 10, 20, 20)];
label.text = #"1";
[self.view addSubview:label];
}
#end
Assuming you are using an animation block, then, within your block you can use setAnimationDelay. For example;
[UIView animateWithDuration:5.0 animations:
^{
[UIView setAnimationDelay:1.0];
...
} completion:^(BOOL finished){
...
}];
This would do a 5 second animation with a delay of 1 second before it started.
you can hook up the valueChangeEvent with an action method.
You can put an animation block in that method with passing the seconds to wait before starting an animation as delay parameter.
[UIView animateWithDuration:(NSTimeInterval) delay:(NSTimeInterval) options:(UIViewAnimationOptions) animations:^(void)animations completion:^(BOOL finished)completion]
then you can change add the view with whatever animation your require.
I am working on a sample project in which I have a vertical scrollview and a horizontal scrollview. The vertical scrollview has a number of subviews. In scrollviewDidScroll I am performing some actions. Along with this I now want to animate a subview inside the vertical scrollview when that particular subview is visible on the screen. For this I am doing a certain calculation which is correct. The animation is as follows:
The subview contains multiple custom views. I am trying to animate(reducing and then again increasing alpha value for the view) each of these views in some particular time sequence(so that the animation looks to be in sequence). For this I am posting notification to the views and the animation sequence and logic is perfect according to what I want.
But I am facing the problem that the code is executing when I put the breakpoint, but the animation does not show up. In case if I post the notification after I stop scrolling then the animation happens perfectly fine. But I want is the animation to happen even when I am scrolling and the view is on screen.
I am adding my code snippet as below:
SubView: (Inside my scrollview)
- (void)animateSequenceTemplate {
if (!hasSequenceTemplateAnimated) {
[self performSelector:#selector(animateSequenceSlides) withObject:nil afterDelay:0];
hasSequenceTemplateAnimated = YES;
}
else {
return;
}
}
- (void)animateSequenceSlides {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:imageAsset forKey:#"assetMO"];
[[NSNotificationCenter defaultCenter]postNotificationName:AnimateSequenceSlide object:nil userInfo:userInfo];
}
Subviews inside the above subview:
- (void)animateSlideView:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
if ([userInfo objectForKey:#"assetMO"] == assetMO) {
[[NSNotificationCenter defaultCenter]removeObserver:self name:AnimateSequenceSlide object:nil];
CGFloat duration = 0.035;
float delay = (self.slideCount * duration);
CGFloat timeDelay = 0;
[self performSelector:#selector(animationPhase1) withObject:nil afterDelay:delay];
timeDelay = delay + duration;
[self performSelector:#selector(animationPhase2) withObject:nil afterDelay:timeDelay];
if (self.slideCount == (self.maxSlideCount - 1)) {
timeDelay += duration * 18; //No animation till 18 frames
}
else {
timeDelay += duration;
}
[self performSelector:#selector(animationPhase3) withObject:nil afterDelay:timeDelay];
timeDelay += duration;
[self performSelector:#selector(animationPhase4) withObject:nil afterDelay:timeDelay];
}
}
- (void)animationPhase1 {
[UIView animateWithDuration:0.035 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ {
[self setAlpha:0.85];
}completion:^(BOOL finished) {
}];
}
There occurs two scenarios inside - (void)animateSequenceTemplate:
I call as [self animateSequenceSlides];. In this case the animation does not show up.
[self performSelector:#selector(animateSequenceSlides) withObject:nil afterDelay:0.0f];. In this case the animation shows up but after the scrollview rests.
I am compelled to use the UIView animation using perform selector because if I remove this and use either nested UIView animate blocks / or directly call these methods, then again animation does not show up. Now at lease it shows up after the I rest scrolling.
I would like advice on a solution or any guess on what mistake I might be making.
It possible, that you executing it not on the main queue.
dispatch_async(dispatch_get_main_queue, block()) might help.
When working with animation, main queue the only place for executing code, that makes animation happen.
EDIT:
[self performSelector:#selector(animationPhase1) withObject:nil afterDelay:delay];
I’m not sure, where it happens, but I don't think on main thread.
Try this:
[self performSelectorOnMainThread:#selector(animationPhase1)
withObject:nil
waitUntilDone:YES];
I’m not sure about delay, that’s why I prefer GCD functions and blocks.
Finally, i got the solution to my question and it is working very fine as expected.
Changes Made:
From "animateSequenceTemplate", calling "animateSequenceSlides"
method directly
In "animateSlideView" method mentioned above, instead of using perform selector I created NSTimer object with the required delay,
repeat set to NO, and adding the timer in the current run loop
Did the same for calling all the four animationPhase methods
Code works fine as accepted
- (void)animateSequenceTemplate {
if (!hasSequenceTemplateAnimated && self.isSequenceSlideCreated) {
hasSequenceTemplateAnimated = YES;
[self animateSequenceSlides];
}
}
- (void)animateSlideView:(NSNotification *)notification {
NSDictionary *userInfo = notification.userInfo;
if ([userInfo objectForKey:#"assetMO"] == assetMO) {
[[NSNotificationCenter defaultCenter]removeObserver:self name:AnimateSequenceSlide object:nil];
CGFloat duration = 0.035;
float delay = (self.slideCount * duration);
CGFloat timeDelay = 0;
NSTimer *animate1 = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:#selector(animationPhase1) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate1 forMode:NSRunLoopCommonModes];
timeDelay = delay + duration;
NSTimer *animate2 = [NSTimer scheduledTimerWithTimeInterval:timeDelay target:self selector:#selector(animationPhase2) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate2 forMode:NSRunLoopCommonModes];
if (self.slideCount == (self.maxSlideCount - 1)) {
timeDelay += duration * 18; //No animation till 18 frames
}
else {
timeDelay += duration;
}
NSTimer *animate3 = [NSTimer scheduledTimerWithTimeInterval:timeDelay target:self selector:#selector(animationPhase3) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate3 forMode:NSRunLoopCommonModes];
timeDelay += duration;
NSTimer *animate4 = [NSTimer scheduledTimerWithTimeInterval:timeDelay target:self selector:#selector(animationPhase4) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop]addTimer:animate4 forMode:NSRunLoopCommonModes];
}
}
I am not satisfied with the goodness of the code structure. If you think of any other smart way of doing it, please share.
I am using NSTimer to move through my UIScrollview, but I can't get to make it stop at every page for like let's say a second or two so users can be able to preview and click if they want to or when they are scrolling around the images in the UIScrollview my Code is below....:
-(void) onTimer {
CGPoint rightOffset = CGPointMake(responseScroll.contentSize.width - responseScroll.bounds.size.width, 0);
[responseScroll setContentOffset:rightOffset animated:YES];
}
-(void)viewDidLoad {
/-------/
[NSTimer scheduledTimerWithTimeInterval:0.008 target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
}
Make the timer interval 2.0:
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
This means that the timer will fire every 2 seconds.
If you want to shift one page over, you need to adjust your onTimer method:
CGPoint rightOffset = CGPointMake(responseScroll.contentOffset.x + responseScroll.frame.size.width, 0);
[responseScroll setContentOffset:rightOffset animated:YES];
Consider using UIAnimations instead
-(void)viewDidLoad{
[self moveScrollView];
}
-(void)moveScrollView{
CGPoint rightOffset = CGPointMake(responseScroll.contentOffset.x+responseScroll.bounds.size.width, 0);
if(rightOffset.x+responseScroll.bounds.size.width<=responseScroll.contentSize.width){
[UIView animateWithDuration:1 animations:^{
[responseScroll setContentOffset:rightOffset animated:NO];
} completion:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self moveScrollView];
});
}];
}
}
Here is my answer, this is in swift. This will scroll the pages in scrollview infinitely.
private func startBannerSlideShow()
{
UIView.animate(withDuration: 6, delay: 0.1, options: .allowUserInteraction, animations: {
scrollviewOutlt.contentOffset.x = (scrollviewOutlt.contentOffset.x == scrollviewOutlt.bounds.width*2) ? 0 : scrollviewOutlt.contentOffset.x+scrollviewOutlt.bounds.width
}, completion: { (status) in
self.startBannerSlideShow()
})
}
I have a horizontal UIScrollView with paging. There are two buttons. One scrolls scrollView to the left another to the right.
The code for the left one:
- (IBAction)goLeftAction:(id)sender
{
CGFloat pageWidth = _theScrollView.frame.size.width;
int page = floor((_theScrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
if(page>0){
page -=1;
[_theScrollView scrollRectToVisible:CGRectMake(_theScrollView.frame.size.width*page, 0, self.view.frame.size.width, self.view.frame.size.height) animated:YES];
}
}
I want button to make scrollView show a bouncing effect when we are at the first page (page=0) and push LEFT.
Please help find the way to achieve this.
EDIT:
Here is the code if someone need it.
First I added to goLeftAction:
[_theScrollView setPagingEnabled:NO];
[_theScrollView setScrollEnabled:NO];
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(bounceScrollView) userInfo:nil repeats:NO];
And this next:
- (void)bounceScrollView
{
[self.theScrollView scrollRectToVisible:CGRectMake(100, 0, self.view.frame.size.width, self.view.frame.size.height) animated:YES];
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(unbounceScrollView) userInfo:nil repeats:NO];
}
- (void)unbounceScrollView
{
[self.theScrollView scrollRectToVisible:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) animated:YES];
[_theScrollView setPagingEnabled:YES];
[_theScrollView setScrollEnabled:YES];
}
This is an interesting question. Have you seen this similar question on SO? It bounces vertically, not horizontally, but the concept is the same.
Set pagingEnabled and scrollEnabled to NO
Animate bounce using scrollRectToVisible:animated:
Set pagingEnabled and scrollEnabled back to YES
This discussion also has some sample code that may be useful.
EDIT:
I had a play around with the code in your edit above, and managed to get the bounce working in the correct direction. I had to use setContentOffset:animated: instead of scrollRectToVisible:animated:, and I also increased the timer interval to 0.3 (0.1 is not enough time for the bounce to reach the full distance before unbounceScrollView is called).
My code in goLeftAction::
[self.scrollView setPagingEnabled:NO];
[self.scrollView setScrollEnabled:NO];
[NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:#selector(bounceScrollView) userInfo:nil repeats:NO];
The bounce methods:
- (void)bounceScrollView
{
[self.scrollView setContentOffset:CGPointMake(-100, 0) animated:YES];
[NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:#selector(unbounceScrollView) userInfo:nil repeats:NO];
}
- (void)unbounceScrollView
{
[self.scrollView setContentOffset:CGPointMake(0, 0) animated:YES];
[self.scrollView setPagingEnabled:YES];
[self.scrollView setScrollEnabled:YES];
}
I'm developing an app with a scroll view in it. It's not immediately obvious that there's more content, and there's no scroll indicator (scroll view is paged).
So, to give the user a 'hey, there's something down here...', I would like to have the scroll view do a subtle bounce - down then up - on launch. I've tried this:
- (void)viewDidLoad
....
[NSTimer scheduledTimerWithTimeInterval:0.8 target:self selector:#selector(bounceScrollView) userInfo:nil repeats:NO];
}
- (void)bounceScrollView
{
[self.verticalScrollViews[0] scrollRectToVisible:CGRectMake(0, 600, 1, 1) animated:YES];
[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(_unbounceScrollView) userInfo:nil repeats:NO];
}
- (void)_unbounceScrollView
{
[self.verticalScrollViews[0] scrollRectToVisible:CGRectZero animated:YES];
}
However, this code makes the view get 'stuck' at about halfway between two pages.
Any help?
Idea 1: You need to turn off paging, animate the bounce, and turn the paging back on.
Idea 2: Your second move is coming way too soon:
scheduledTimerWithTimeInterval:0.01
Experiment with longer time intervals! I would start with 0.4.
Idea 3: Instead of your bounce, why not use flashScrollIndicators? This is exactly what it is for!
I had a memory problems when using NSTimer. That's why I used another solution for scrollView preview.
[UIView animateWithDuration:1.0
delay:0.00
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
_scrollView.contentOffset = CGPointMake(200,0);
}
completion:^(BOOL finished){
[UIView animateWithDuration:1.0
delay:0.00
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
_scrollView.contentOffset = CGPointMake(0,0);
}
completion:^(BOOL finished){
}
];
}
];