I use NSTimer to do the auto slide and my code is like this:
NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval: 5
target: self
selector: #selector(handleTimer)
userInfo: nil
repeats: YES];
- (void)handleTimer
{
int page = _bannerScrollView.contentOffset.x / 296;
if ( page + 1 < [array count] )
{
page++;
_pageControl.currentPage = page++;
}
else
{
page = 0;
_pageControl.currentPage = page;
}
[self changePage];
}
- (void)changePage
{
int page = _pageControl.currentPage;
[_bannerScrollView setContentOffset:CGPointMake(296 * page, 0)];
}
And I need some animation stuff, because it is cool.
How can I do this? Thank you very much.
You can set frames according to ur needs and it will animate to next view with page control
[scrollview scrollRectToVisible:CGRectMake(320, 80,320, 350) animated:YES];
Related
I want to use NSTimer as a Countdown. The counter works fine;-)!
But I I switch to another viewController the timer doesn't runs in "foreground" - so the label isn't updated.... after coming back to this view.
My NSLOG shows that the timer is still runnig, like I want ;-)
So in whicht way I have to work, that my timer update my label?
Here is the code:
- (IBAction)startClock:(id)sender {
globalTimer = 20;
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(updateCircularProgressBar)
userInfo:nil
repeats:YES];
}
- (void)updateCircularProgressBar
{
// Values to be passed on to Circular Progress Bar
if (globalTimer > 0 && globalTimer <= 1200) {
globalTimer--;
minutesLeft = globalTimer / 60;
secondsLeft = globalTimer % 60;
[self drawCircularProgressBarWithMinutesLeft:minutesLeft secondsLeft:secondsLeft];
NSLog(#"Time left: %02d:%02d", minutesLeft, secondsLeft);
}
else {
[self drawCircularProgressBarWithMinutesLeft:0 secondsLeft:0];
[timer invalidate];
}
}
I tested to run the "updateCircularProgressBar" in my viewDidLoad but this shows NO result...
THX for Help
UPDATE more with more Code:
/*-------------------- TIMER --------------------*/
// Draws the progress circles on top of the already painted background
- (void)drawCircularProgressBarWithMinutesLeft:(NSInteger)minutes secondsLeft:(NSInteger)seconds
{
// Removing unused view to prevent them from stacking up
for (id subView in [self.view subviews]) {
if ([subView isKindOfClass:[CircularProgressTimer class]]) {
[subView removeFromSuperview];
}
}
// Init our view and set current circular progress bar value
CGRect progressBarFrame = CGRectMake(0, 0, 180, 180);
progressTimerView = [[CircularProgressTimer alloc] initWithFrame:progressBarFrame];
[progressTimerView setCenter:CGPointMake(160, 210)];
[progressTimerView setPercent:seconds];
if (minutes == 0 && seconds <= 0) {
[progressTimerView setInstanceColor:[UIColor magentaColor]];
}
// Here, setting the minutes left before adding it to the parent view
[progressTimerView setMinutesLeft:minutesLeft];
[progressTimerView setSecondsLeft:secondsLeft];
[self.view addSubview:progressTimerView];
progressTimerView = nil;
}
The label is in another file (CircularProgressTimer) which controls the progessbar...
I have a tall UIImageView scrolling across the screen when a button is pressed. I used a timer to change the y position of the image at a specific speed. The image is 10,000px tall.
Here's my simple code:
-(void) moveImage {
myImage.center = CGPointMake(myImage.center.x, myImage.center.y +6);
}
-(IBAction)StartGame:(id)sender{
moveImageTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(moveImage)userInfo:nil repeats:YES];
[StartGame setHidden:TRUE];
}
Here's what i've tried to stop the timer when the image has scrolled completely through the screen:
-(void) moveImage {
myImage.center = CGPointMake(myImage.center.x, myImage.center.y +6);
if ([myImage == GPointMake(myImage.center.x, myImage.center.y -568) {
[moveImageTimer invalidate];
}
That doesn't work but i thought it would. Can someone tell me why the IF statement doesn't work? Any help is appreciated, thanks
Since you want to stop when the image moves to the bottom corner.
-(void) moveImage {
CGPoint newPoint= CGPointMake(myImage.center.x, myImage.center.y +6);
myImage.center = newPoint;
if ((newPoint.y - (myImage.frame.size.width/2)) >= [UIScreen mainScreen].bounds.size.height)
{
[moveImageTimer invalidate];
}
}
you can check the condition
like if (imageView.center.y > imageView.frame.size.height - self.view.frame.size.height)
here imageView.frame.size.height - self.view.frame.size.height because the image size is twice screen size. You have to set you logic
imageView.center = CGPointMake(imageView.center.x, imageView.center.y + 6);
if (imageView.center.y > imageView.frame.size.height - self.view.frame.size.height)
{
[moveImageTimer invalidate];
}
}
I am trying to make a game movement animation in objective c so that when a button is pressed something will move across the screen while it is pressed. This works fine though the calling of the NSTimer which I am using is irregular and I am getting laggy movements. This is in iOS 6 so I wont use sprite kit as it is not available
Any suggestions on how to make it a regular call or to make it a smooth animation would be much appreciated
Thanks in advance
Here is the code:
-(void) click {
if (!self.animating) {
self.animating = YES;
timer = [NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(move)
userInfo:nil
repeats:YES];
}
}
- (void) cancelClick {
self.animating = NO;
[timer invalidate];
}
-(void) move {
CGPoint origin = self.target.frame.origin;
if ([self.direction isEqual:#"left"]) {
origin.x -= self.magnitude;
} else if ([self.direction isEqual:#"right"]) {
origin.x += _magnitude;
} else if ([self.direction isEqual:#"up"]) {
origin.y -= _magnitude;
} else {
origin.y += _magnitude;
}
[self.target move:0 toPoint:origin animated:YES delay:0 animationOptions:UIViewAnimationOptionCurveLinear];
}
I want to add an UIImageView to my view at the users touch location and have the UIImageView grow while the user is holding their finger down. Think of a ballon being blown up. I want the center of the UIImageView to remain at the user's touch location while its growing.
I figured the best way would be a UILongPressGestureRecognizer and wrote the below. This does work as I planed except the visual effect is somewhat choppy and clumsy.
Is there any way that I can animate the UIImageView's size until the UILongPressGestureRecognizer calls UIGestureRecognizerStateEnded?
Or, is there a better way to do this altogether?
declared in .h: CGPoint longPressLocation;
.m:
- (IBAction) handleInflation:(UILongPressGestureRecognizer *) inflateGesture {
longPressLocation= [inflateGesture locationInView:self.view];
switch (inflateGesture.state) {
case UIGestureRecognizerStateBegan:{
NSLog(#"Long press Began .................");
inflateTimer = [NSTimer scheduledTimerWithTimeInterval:.4 target:self selector:#selector(inflate) userInfo:nil repeats:YES];
UIImage *tempImage=[UIImage imageNamed:#"bomb.png"];
UIImageView *inflatableImageView = [[UIImageView alloc] initWithFrame:CGRectMake(longPressLocation.x-tempImage.size.width/2,
longPressLocation.y-tempImage.size.height/2,
tempImage.size.width, tempImage.size.height)];
inflatableImageView.image = tempImage;
[bonusGame addSubview:inflatableImageView];
inflatable=inflatableImageView;
}
break;
case UIGestureRecognizerStateChanged:{
NSLog(#"Long press Changed .................");
}
break;
case UIGestureRecognizerStateEnded:{
NSLog(#"Long press Ended .................");
[inflateTimer invalidate];
}
break;
default:
break;
}
}
-(void)inflate{
inflatable.frame=CGRectMake(inflatable.frame.origin.x,inflatable.frame.origin.y , inflatable.bounds.size.width+15, inflatable.bounds.size.height+15);
inflatable.center=longPressLocation;
}
Final Working Code:
- (IBAction) handleInflation:(UILongPressGestureRecognizer *) inflateGesture {
inflateGesture.minimumPressDuration = .01;
longPressLocation= [inflateGesture locationInView:self.view];
switch (inflateGesture.state) {
case UIGestureRecognizerStateBegan:{
NSLog(#"Long press Began .................");
inflateStart = [NSDate date];
inflateDisplayLink = [NSClassFromString(#"CADisplayLink") displayLinkWithTarget:self selector:#selector(inflate)];
[inflateDisplayLink setFrameInterval:1];
[inflateDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
UIImage *tempImage=[UIImage imageNamed:#"bomb.png"];
UIImageView *inflatableImageView = [[UIImageView alloc] initWithFrame:CGRectMake(longPressLocation.x-tempImage.size.width/2,
longPressLocation.y-tempImage.size.height/2,
tempImage.size.width, tempImage.size.height)];
inflatableImageView.image = tempImage;
[bonusGame addSubview:inflatableImageView];
inflatable=inflatableImageView;
}
break;
case UIGestureRecognizerStateChanged:{
NSLog(#"Long press Changed .................");
}
break;
case UIGestureRecognizerStateEnded:{
NSLog(#"Long press Ended .................");
[inflateDisplayLink invalidate];
}
break;
default:
break;
}
}
-(void)inflate{
NSDate *inflateEnd = [NSDate date];
NSTimeInterval inflateInterval;
inflateInterval = ([inflateEnd timeIntervalSince1970] - [inflateStart timeIntervalSince1970])*25;
inflatable.frame=CGRectMake(inflatable.frame.origin.x,
inflatable.frame.origin.y ,
inflatable.bounds.size.width+inflateInterval,
inflatable.bounds.size.height+inflateInterval);
inflatable.center=longPressLocation;
if(inflatable.bounds.size.width>200){
[inflateDisplayLink invalidate];
}
}
A timer may not be smooth. Instead check out CADisplayLink, which will provide a delegate callback whenever the device's screen is redrawn (~60hz), so you will get a per frame chance to adjust your balloon size.
Another thing to consider is the time between refreshes isn't constant, it could be a lot slower than when the last refresh occured, so if you are incrementing the size by a constant 15 every time you get a callback, then the animation may not seem smooth.
To combat this, when you start the animation take a timestamp and hold onto it, then when you inflate the balloon take another timestamp and determine the difference between now and the last redraw, then multiply the difference by some value, which will ensure a constant smooth size growth - this is called a timestep.
https://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CADisplayLink_ClassRef/Reference/Reference.html
Getting creative here, but I think this might work:
You start an animation that animates the images frame from its current size towards a maximum size.
Start the animation as soon as you detect the long press gesture.
If the gesture ends, get the current frame size from the presentationLayer of the animated view/image and update the view/image's frame to that size by starting a new animation with UIViewAnimationOptionBeginFromCurrentState so that the old animation will stop.
That should give you a smoothly growing balloon.
Try this....
imageView = [[UIImageView alloc] initWithFrame:(CGRectMake(120, 100, 30, 30))];
imageView.backgroundColor = [UIColor redColor];
[self.view addSubview:imageView];
imageView.userInteractionEnabled = TRUE;
UILongPressGestureRecognizer *g = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[g setMinimumPressDuration:0.001];
[imageView addGestureRecognizer:g];
And Add these methods
-(void)longPress:(UITapGestureRecognizer *)t
{
if (t.state == UIGestureRecognizerStateBegan)
{
[timer invalidate];
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(makeIncrc) userInfo:nil repeats:TRUE];
}
else if (t.state == UIGestureRecognizerStateEnded || t.state == UIGestureRecognizerStateCancelled)
{
[timer invalidate];
timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(makeDec) userInfo:nil repeats:TRUE];
}
}
-(void)makeIncrc
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:(UIViewAnimationCurveLinear)];
CGRect frame = imageView.frame;
frame.origin.x = frame.origin.x - 5;
frame.origin.y = frame.origin.y - 5;
frame.size.width = frame.size.width + 10;
frame.size.height = frame.size.height + 10;
imageView.frame = frame;
[UIView commitAnimations];
}
-(void)makeDec
{
if (imageView.frame.size.width < 20 || imageView.frame.size.height < 20)
{
[timer invalidate];
}
else
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:(UIViewAnimationCurveLinear)];
CGRect frame = imageView.frame;
frame.origin.x = frame.origin.x + 5;
frame.origin.y = frame.origin.y + 5;
frame.size.width = frame.size.width - 10;
frame.size.height = frame.size.height - 10;
imageView.frame = frame;
[UIView commitAnimations];
}
}
Here's a Swift version. I had to replace invalidate() calls with "paused" in order to avoid runaway inflation.
var inflateDisplayLink: CADisplayLink?
var inflateStart: NSDate!
var longPressLocation: CGPoint!
var inflatable: UIImageView!
func handleInflation(inflateGesture: UILongPressGestureRecognizer) {
longPressLocation = inflateGesture.locationInView(self.view)
let imageView = inflateGesture.view as! UIImageView
switch (inflateGesture.state) {
case .Began:
println("Long press Began .................")
inflateStart = NSDate()
let tempImageView = UIImageView(image: imageView.image)
tempImageView.contentMode = .ScaleAspectFit
tempImageView.frame = imageView.frame
self.view.addSubview(tempImageView)
inflatable = tempImageView
if inflateDisplayLink == nil {
inflateDisplayLink = CADisplayLink(target: self, selector: Selector("inflate"))
inflateDisplayLink!.frameInterval = 1
inflateDisplayLink!.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
} else {
inflateDisplayLink!.paused = false
}
break
case .Changed:
println("Long press Changed .................")
break
case .Ended:
println("Long press Ended .................")
inflateDisplayLink!.paused = true
break
default:
break
}
}
func inflate() {
var inflateEnd = NSDate()
// Convert from Double to CGFloat, due to bug (?) on iPhone5
var inflateInterval: CGFloat = CGFloat((Double(inflateEnd.timeIntervalSince1970) - Double(inflateStart.timeIntervalSince1970))) * 5.0
inflatable.frame = CGRectMake(inflatable.frame.origin.x, inflatable.frame.origin.y, inflatable.bounds.size.width + inflateInterval, inflatable.bounds.size.height + inflateInterval)
inflatable.center = longPressLocation.center
if inflatable.bounds.size.width > 200 {
inflateDisplayLink!.paused = true
}
}
I'm working on a project where I'm using a UICollectionView to create an 'image ticker' where I'm advertising a series of logos. The collectionView is one item high and twelve items long, and shows two to three items at a time (depending on size of the logos visible).
I would like to make a slow automatic scrolling animation from the first item to the last, and then repeat.
Has anyone been able to make this work? I can get the scrolling working using
[myCollection scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:(myImages.count -1) inSection:0] atScrollPosition:UICollectionViewScrollPositionRight animated:YES];
But this is way too fast!
[UIView animateWithDuration:10 delay:2 options:(UIViewAnimationOptionAutoreverse + UIViewAnimationOptionRepeat) animations:^{
[myCollection scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:(myImages.count -1) inSection:0] atScrollPosition:UICollectionViewScrollPositionRight animated:NO];
} completion:nil];
This yields the desired scrolling speed, but only the last few cells are visible in the series. I suspect they (and even the starting visible cells) are being dequeued immediately.
Any thoughts?
You can try this approach:
#property (nonatomic, assign) CGPoint scrollingPoint, endPoint;
#property (nonatomic, strong) NSTimer *scrollingTimer;
#synthesize scrollingPoint, endPoint;
#synthesize scrollingTimer;
- (void)scrollSlowly {
// Set the point where the scrolling stops.
self.endPoint = CGPointMake(0, 300);
// Assuming that you are starting at {0, 0} and scrolling along the x-axis.
self.scrollingPoint = CGPointMake(0, 0);
// Change the timer interval for speed regulation.
self.scrollingTimer = [NSTimer scheduledTimerWithTimeInterval:0.015 target:self selector:#selector(scrollSlowlyToPoint) userInfo:nil repeats:YES];
}
- (void)scrollSlowlyToPoint {
self.collectionView.contentOffset = self.scrollingPoint;
// Here you have to respond to user interactions or else the scrolling will not stop until it reaches the endPoint.
if (CGPointEqualToPoint(self.scrollingPoint, self.endPoint)) {
[self.scrollingTimer invalidate];
}
// Going one pixel to the right.
self.scrollingPoint = CGPointMake(self.scrollingPoint.x, self.scrollingPoint.y+1);
}
I use a "1 pixel offset" trick. When scrolling programmatically, just set the end contentOffset.x to 1 pixel more/less than it should. This should be unnoticeable. And in completion block set it to actual value. That way you can avoid the premature cell dequeuing problem and get smooth scrolling animation ;)
Here is an example of scrolling to the right page (e.g. from page 1 to 2). Notice that in the animations: block I actually scrolls one pixel less (pageWidth * nextPage - 1). I then restore the correct value in the completion: block.
CGFloat pageWidth = self.collectionView.frame.size.width;
int currentPage = self.collectionView.contentOffset.x / pageWidth;
int nextPage = currentPage + 1;
[UIView animateWithDuration:1
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[self.collectionView setContentOffset:CGPointMake(pageWidth * nextPage - 1, 0)];
} completion:^(BOOL finished) {
[self.collectionView setContentOffset:CGPointMake(pageWidth * nextPage, 0)];
}];
You could also have a "slow" scroll to the end of you UICollectionView without having to "jump" from indexpath to indexpath. I created this quickly for a collection view on a TVOS app:
func autoScroll () {
let co = collectionView.contentOffset.x
let no = co + 1
UIView.animateWithDuration(0.001, delay: 0, options: .CurveEaseInOut, animations: { [weak self]() -> Void in
self?.collectionView.contentOffset = CGPoint(x: no, y: 0)
}) { [weak self](finished) -> Void in
self?.autoScroll()
}
}
I just call the autoScroll() in the viewDidLoad() and the rest takes care of it itself. The speed of the scrolling is decided by the animation time of the UIView. You could (I haven't tried) add an NSTimer with 0 seconds instead so you can invalidate it on userscroll.
For anyone else finding this, I've updated Masa's suggestion to work on Swift and I've introduced a little bit of easing towards the end so it acts more like the original scrollItemsToIndexPath animated call. I have hundreds of items in my view so a steady pace wasn't an option for me.
var scrollPoint: CGPoint?
var endPoint: CGPoint?
var scrollTimer: NSTimer?
var scrollingUp = false
func scrollToIndexPath(path: NSIndexPath) {
let atts = self.collectionView!.layoutAttributesForItemAtIndexPath(path)
self.endPoint = CGPointMake(0, atts!.frame.origin.y - self.collectionView!.contentInset.top)
self.scrollPoint = self.collectionView!.contentOffset
self.scrollingUp = self.collectionView!.contentOffset.y > self.endPoint!.y
self.scrollTimer?.invalidate()
self.scrollTimer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "scrollTimerTriggered:", userInfo: nil, repeats: true)
}
func scrollTimerTriggered(timer: NSTimer) {
let dif = fabs(self.scrollPoint!.y - self.endPoint!.y) / 1000.0
let modifier: CGFloat = self.scrollingUp ? -30 : 30
self.scrollPoint = CGPointMake(self.scrollPoint!.x, self.scrollPoint!.y + (modifier * dif))
self.collectionView?.contentOffset = self.scrollPoint!
if self.scrollingUp && self.collectionView!.contentOffset.y <= self.endPoint!.y {
self.collectionView!.contentOffset = self.endPoint!
timer.invalidate()
} else if !self.scrollingUp && self.collectionView!.contentOffset.y >= self.endPoint!.y {
self.collectionView!.contentOffset = self.endPoint!
timer.invalidate()
}
}
Tweaking the values of dif and modifier adjusts the duration/level of ease for most situations.
In .h file,
declare this timer,
NSTimer *Timer;
Place this timer code,
[CollectionView reloadData];
Timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(AutoScroll) userInfo:nil repeats:YES];
then,
#pragma mark- Auto scroll image collectionview
-(void)AutoScroll
{
if (i<[Array count])
{
NSIndexPath *IndexPath = [NSIndexPath indexPathForRow:i inSection:0];
[self.ImageCollectionView scrollToItemAtIndexPath:IndexPath
atScrollPosition:UICollectionViewScrollPositionRight animated:NO];
i++;
if (i==[Array count])
{
i=0;
}
}
}
Hope it'll be useful.