Looping UIScrollView with user interaction - iphone

I have a looping scrollView but I want the user to be able to speed up the scrolling by interacting with the scrollView (ie scrolling with their finger) if they choose. Do I have to do extra work to acheive this as at the moment the animated scrolling takes priority over the users interaction and slows the scroll down dramatically. Has anyone done something similar? As an example Im looking to acheive as similar affect to that used in the about screen on the angry birds game?
Many thanks
Here us the code for basice animation of the scroll view
[UIView beginAnimations:#"scroll " context:nil];
[UIView setAnimationDuration:15];
[scrollView setContentOffset:CGPointMake(0, 600) animated:NO];
[UIView commitAnimations];
As soon as the user tries to scroll, the scrollView slightly scrolls (it moves very slightly), then stops the user scroll action and continues with the animated scroll? So the overall appearance is as follows :
1.animating scroll
2. user tries to scroll and animation appears to stick
3. when user ends scroll animation continues
Any thoughts?
Many thanks again.

If I understand what you're trying to say, you want a continuous, slow scroll (600px per 15 seconds) which can be overruled by the user.
If this is what you want, I'd take a different approach than this one. The animation you're firing, when started, will probably just do what it's told, while the user is interacting as well, which will all get very messy.
Create an NSTimer which takes care of the slow scrolling motion, one pixel at a time. Then the user can still interact freely.
something like:
...
NSTimer *slow = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(scrollALittle:) userInfo:nil repeats:YES];
...
// maybe, somewhere:
[slow invalidate];
-(void)scrollALittle:(NSTimer*)theTimer
{
CGPoint offset = scrollview.contentOffset;
offset.y+=4; // 10 times 4 pix a second makes 600px/15 seconds.
// add check on max y?
// these checks are deliberately placed as close as possible to the contentOffset assignment
if ( scrollview.tracking ) return;
if ( scrollview.dragging ) return;
if ( scrollview.zooming ) return;
if ( scrollview.decelerating ) return;
scrollview.contentOffset = offset;
}
of course you can do a lot of things here, but this may be a starting point.

Related

Iphone: Page flipping animation

My code is flipping my view as if you were reading a book in reverse page order (right to left). I'm trying to achieve the opposite but I can't seem to figure out how to make the page flip from the other side of the views coordinate system.
Here's my code:
_memoryOneView.layer.anchorPoint = CGPointMake(1.0f, .5f);
_memoryOneView.layer.position = CGPointMake(_memoryOneView.layer.position.x + _memoryOneView.bounds.size.width/2.0f, _memoryOneView.layer.position.y);
[UIView animateWithDuration:.5 animations:^{
_memoryOneView.layer.transform = CATransform3DMakeRotation((M_PI / 2.0), 0, 1.0f, 0);
}];
I've tried making many of the values negative but this simply hides the animation behind views behind it. Any ideas? ty.
Edit I also need to be able to use this animation for dragging, meaning I need to have something I can easily transfer into code where the user is dragging to the next page.
iOS 5 has an in-built PageViewController exactly for this. Tutorial here: http://www.techotopia.com/index.php/An_Example_iOS_5_iPhone_UIPageViewController_Application

Updating UI concurrently on iOS

I have two scroll views in my app, one containing UIImageViews, one containing UIButtons. Using NSTimer, I'm making them scroll automatically. However, if one of the scroll views is tampered with (a finger touches it and starts scrolling manually), the other scroll view stops as well. Is there any way to stop this from happening? Or is it normal?
Also, the UIButtons inside the second scroll view are tap-able, but they don't show the standard highlighting. If I enable the glow effect, it works, but not the standard highlighting. Is there anyway I can make this work as well?
My code for NSTimer is
[NSTimer scheduledTimerWithTimeInterval:0.018
target:self
selector:#selector(onTimerScrollText)
userInfo:nil
repeats:YES];
- (void)onTimerScrollText {
CGFloat x = self.textScroller.contentOffset.x;
x += 0.5;
[self.textScroller setContentOffset:CGPointMake(x, 0)];
}
And it's pretty much the same for the image scroller.
Thanks!
The initial problem of one scrollview no longer scrolling until the other manual scroll has finished has been solved.
The solution is simple. Each timer needs to be added to the run loop:
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
Now they both work regardless of whether one is interrupted by one's finger.

GLKViewController: incorrect fps

Hope there are some GLKViewController experts out there because I have some problems :)
Just a quick description of my app. I have a UINavigationController in which I push different screens.
At some point, I get to my game screen which is a subclass of UINavigationController. In this screen, in viewDidLoad I manually create a EAGLContext, GLKView and instantiate a new GLKViewController (to handle my update&draw calls).
I am setting a preferred fps of 30.
The problem is that the first 3-4 update calls come with the correct DT, but then I have 2-3 frames with 1 second between them. I measure the DT using controller.timeSinceLastUpdate.
So I get like:
dt=0.33
dt=0.33
dt=0.33
dt=1.07
dt=1.05
dt=0.33
dt=0.33
After this, I get valid only DT times. I have no idea why those frames have that kind of delay. I measured the time it takes me in the update & draw method, and it's nowhere near 1 second.
Also, I'm not loading any textures/creating any geometry. Everything is done at loading since it is a rather small game.
Also, if I pop the game screen controller and then push back another instance of the game screen, this new GLKViewController will only call my update method aproximately every 1 second.
Did anybody have a problem with the framerate when using GLKViewController?
Thanks,
The problem is that you don't know what else the device is doing between your refreshes :)
You might only spent 0.1s working on the next frame but if there is a memory warning then other bits of your app will also take time to process. I guess that the gl controller will do it's best to keep to the preferred frame rate but if lots is going on in the background then there's not much it can do about it.
As long as you make sure that your code is rendering as fast as possible and isn't spiking in the same way as the framerate then it's not your render path. From your question it sounds like you've already tested that.
The other thing you might want to do is to watch out for other notifications that might be passed into your app (i.e. memory warnings).
Finally, is there a pattern to the slow frames - do they coincide with a new image being loaded or a file access? Have you done as much as possible beforehand? EDIT - rereading your question makes me think that you've already done this, sorry!
Sorry I can't be any more use :(
Ok, so I finally figured it out. It turns out that it's not even related to the GLKViewController (surprise surprise!).
It had something to do with the way I'm displaying the game screen view controller, like this:
GameAreaViewController* gameController = [[GameAreaViewController alloc] init];
[UIView beginAnimations:#"animation" context:nil];
[self.navigationController pushViewController: gameController animated:NO];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.navigationController.view cache:NO];
[UIView setAnimationDuration:0.7f];
[UIView commitAnimations];
SAFE_DEL(gameController);
If I use an animation duration of 0.3f, then I don't get any lag. At 0.5f sometimes I get it and at 0.7 I was always getting it.

UIScrollView's setContentOffset called before new subview is loaded

I have a UIView that I'm sliding in using a UIScrollView. Code looks like this:
[view setObject:object]; // updates a bunch of labels, images, etc
[scrollView addSubview:view];
[scrollView setContentOffset:CGPointMake(320,0) animated:YES];
Problem is, the scrollView doesn't seem to wait for the view to be completely loaded before animating its contentOffset, and so the animation is kinda jerky (almost non-existent on the first slide-in and on older devices). Oddly enough, switching the 3rd line to this fixes it:
[UIView animateWithDuration:0.3 animations:^{
[scrollView setContentOffset:CGPointMake(320,0) animated:NO];
}];
No lag whatsoever, perfectly smooth slide-in. However, this doesn't trigger any of the UIScrollViewDelegate calls, on which I also depend (but which aren't responsible for the lag).
Any idea of how I could tell the UIScrollView to wait for the view to be completely loaded before animating its contentOffset? Or maybe there's something else that I'm missing?
EDIT: before anyone else suggests it: yes, I did try:
[self performSelector:#selector(slideIn) withObject:nil afterDelay:1];
just to see if it would fix it. And yes it does fix the lag, but this is not an actual solution. performSelector:afterDelay: is never a solution, it's only a superficial fix. Plus, you're making the user wait extra seconds every time (since the actual delay may be much shorter than 1 second depending on the device model).
Performing selector after delay 0.01 works because the invocation is scheduled on the runloop vs. calling the method immediately. For that matter, afterDelay:0.0 may also work. Did not try in this particular case but works in may similar situations.
have you tried starting the scrolling in the "viewDidAppear" function of the viewcontroller... maybe this is called when the loading is finished.
otherwise try afterdelay with a very short time like 0.01 .. so that the call is scheduled next after the current work is done.
Try this, set contentSize after some delay (here delay is 2 second).
[view setObject:object];
[scrollView addSubview:view];
[self performSelector:#selector(contentSize) withObject:nil afterDelay:2];
}
-(void) contentSize {
[scrollView setContentOffset:CGPointMake(320,0) animated:YES];
}

Is it possible to change UIView's "center" property inside the overriden touchesEnded method and redraw the view?

I want to implement a simple animation based on UIView's center property. I have a simple view and I can drag it (UIView touchesMoved is overriden). The animation should fade slowly like the view is moved under it's own inertia for some time after the user releases it. But for now I want simply to move the view after touch ends. Here is the code I have in touchesEnded:
int i;
for (i=1;i<4;i++)
{
self.center = CGPointMake(10*i, 12*i);
[self setNeedsDisplay];
usleep(100000);
}
The problem is when I run this, the code is executed nicely but "UIView" changes to late. I changed usleep time and other parameters but the result is the same. It looks like all the "pending changes" in the view are performed only after the overriden touchesEnded is finished.
Is this the right way of implementing such user interface feature or should I look for some other approaches?
Thanks in advance.
If you know the final positions you want your view center to have you could do this with an animation:
[UIView animateWithDuration:20
delay:0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
youView.center = CGPointMake(newCenterX, newCenterY);
}
completion:^(BOOL finished){
}];
If that code is being run inside of touchesEnded you are blocking the main thread and that is why the animation happens all at once when its done. You will need run this loop in the background to update the view over time. When you update the UI remember to always call back to the main thread.
Some Threading Options:
performSelectorInBackground:withObject:
performSelectorOnMainThread:withObject:waitUntilDone:
Grand Central Dispatch
Threading