IOS Scrollview Marquee style - iphone

I have a set of 4 images that i want to show in a UIScrollView. This scroll view contains ad banners. So they have to keep scrolling vertically down automatically similar to marquee effect - user does not need to touch the screen to scroll.This process should keep repeating.
If the user touches the screen, the scrolling should stop & when user re-touches the screen the scrolling should restart from where it had left.

There is as simpler way, I guess. UIImageView can help you. Follow these Steps :
1) Add 4 UIImageViews Vertically Down.
2) Create 4 Different Arrays for these ImageViews.
NSArray *frames1 = [NSArray arrayWithObjects:[UIImage imageWithName:#"a.png"],[UIImage imageWithName:#"b.png"],[UIImage imageWithName:#"c.png"],[UIImage imageWithName:#"d.png"],nil];
NSArray *frames2 = [NSArray arrayWithObjects:[UIImage imageWithName:#"b.png"],[UIImage imageWithName:#"c.png"],[UIImage imageWithName:#"d.png"],[UIImage imageWithName:#"a.png"],nil];
NSArray *frames3 = [NSArray arrayWithObjects:[UIImage imageWithName:#"c.png"],[UIImage imageWithName:#"d.png"],[UIImage imageWithName:#"a.png"],[UIImage imageWithName:#"b.png"],nil];
NSArray *frames4 = [NSArray arrayWithObjects:[UIImage imageWithName:#"d.png"],[UIImage imageWithName:#"a.png"],[UIImage imageWithName:#"b.png"],[UIImage imageWithName:#"c.png"],nil];
3) Provide these Arrays to all these different ImageViews.
yourImageView1.animationImages = frames1;
yourImageView2.animationImages = frames2;
yourImageView3.animationImages = frames3;
yourImageView4.animationImages = frames4;
4) You can add some Effects also...
// all frames will execute in 1.75 seconds
yourImageView.animationDuration = 1.75;
// repeat the annimation forever
yourImageView.animationRepeatCount = 0;
5) Simply startAnimating the ImageViews.
[yourImageView1 startAnimating];
[yourImageView2 startAnimating];
[yourImageView3 startAnimating];
[yourImageView4 startAnimating];
6) You can always use -touchesEnded method to start and stop Animation.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ([[touches anyObject] view] == yourImageView1) {
//Here you can write the code to stop and start Animation of UIImageView
}
}
I think this will give you the effect what you want.
GoodLuck !!!

This is how I have done it finally :-) .
Call the below method from viewDidLoad
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(scrollTheScrollView) userInfo:nil repeats:YES];
Now implement the method
-(void)scrollTheScrollView{
static int i=0;
i++;
if(self.scrollView.contentOffset.y == 700.0)//This 700.0 will be different for you
i=0;
NSLog(#"Content offset in scrolling method is %#",self.scrollView);
[self.scrollView setContentOffset:CGPointMake(0, i*5)];}

I made an API for this, if you're interested.
https://github.com/dokun1/DOMarqueeLabel

Related

NSTimer scheduledTimerWithTimeInterval and CoreAnimation [duplicate]

This question already has an answer here:
How to make marquee UILabel / UITextField / NSTextField
(1 answer)
Closed 9 years ago.
I am writing an app using a cocoa control that animates text,
https://www.cocoacontrols.com/controls/marqueelabel
It uses CoreText and QuartzCore.
It's a pretty great control, but i'm having trouble.
When instantiated the animated labels that I create using the control start to scroll immediately, however I'm finding that when I create an animated label using NSTimer it is not animating.
I am using
- (void)viewDidLoad
{
[super viewDidLoad];
....
[self.view addSubview:continuousLabel1];
[NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(updatePrice)
userInfo:nil repeats:YES];
}
to call a method that creates the animated label, when I call the method directly the labels create and animate, however when called using the above scheduled timer, new labels created only animate on the first call, not on the timers repeat calls to the method.
I have checked that my method is being call on the main thread/ main runloop, any ideas?
For clarity the work flow is:
Timer calls method --> Label 1 is created and starts to Animate.
5 Seconds Late...
Timer calls method again --> Label 2 is created However is does not begin to animate as expected.
EDIT:
- (void)updatePrice
{
MarqueeLabel *continuousLabel2 = [[MarqueeLabel alloc] initWithFrame:CGRectMake(10, 230, self.view.frame.size.width-20, 20) rate:100.0f andFadeLength:10.0f];
continuousLabel2.tag = 101;
continuousLabel2.marqueeType = MLContinuous;
continuousLabel2.animationCurve = UIViewAnimationOptionCurveLinear;
continuousLabel2.continuousMarqueeExtraBuffer = 50.0f;
continuousLabel2.numberOfLines = 1;
continuousLabel2.opaque = NO;
continuousLabel2.enabled = YES;
continuousLabel2.shadowOffset = CGSizeMake(0.0, -1.0);
continuousLabel2.textAlignment = NSTextAlignmentLeft;
continuousLabel2.textColor = [UIColor colorWithRed:0.234 green:0.234 blue:0.234 alpha:1.000];
continuousLabel2.backgroundColor = [UIColor clearColor];
continuousLabel2.font = [UIFont fontWithName:#"Helvetica-Bold" size:17.000];
continuousLabel2.text = #"This is another long label that doesn't scroll continuously with a custom space between labels! You can also tap it to pause and unpause it!";
[self.view addSubview:continuousLabel2];
if( [NSThread isMainThread])
{
NSLog(#"In Main Thread");
}
if ([NSRunLoop currentRunLoop] == [NSRunLoop mainRunLoop]) {
NSLog(#"In Main Loop");
}
}
The problem is that you are calling updatePrice every 5 seconds and in this method you are creating a new label, that that is stacking up, and notice that it is going darker and darker every 5 seconds.
And if you check the label is scrolling.
Solution:
If you want multiple instances of the label change its coordinates.
If you want single instance then compare check and create it just once.
You should read the section Important Animation Note from the README file.
My guess is that you will need to call controllerLabelsShouldAnimate: passing your current view controller to animate those labels added after the view controller has already been shown.

Slow preformance during replaceScene

I'm facing a performance issue whenever the code reach "replaceScene". It happens only in the Play Scene. So after the game is over, I display a score, and then, it's time for CCDirector to do replaceScene in order to go back to the main menu.
After waiting for around 20 seconds then it finally display the main menu. But somehow this is not right, player will feel that the game suddenly hang. Then I tried to put some animation like preloader, it happens the same, the preloader picture did animate a while then suddenly stop, and I think due to the same issue triggered by replaceScene, although still it'll display the main menu scene. Care to give some tips how to speed up the releasing of all the objects which no longer needed.
Hoping to get a solution from experts here. Thanks.
Here is my code :
............
//button at the score pop up sprite
CCMenuItem *btContinue = [CCMenuItemImage itemFromNormalImage:BTCONTINUE
selectedImage:BTCONTINUE_ON
target:self
selector:#selector(goLoader)];
btContinue.anchorPoint = ccp(0,0);
btContinue.position = ccp(340, 40);
CCMenu *menu = [CCMenu menuWithItems:btContinue, nil];
menu.position = CGPointZero;
[self addChild:menu z:ZPOPUP_CONTENT];
//prepare the loader, but set visible to NO first
CCSprite *loaderBg = [CCSprite spriteWithFile:LOADER_FINISH];
loaderBg.anchorPoint = ccp(0,0);
loaderBg.position = ccp(0,0);
loaderBg.visible = NO;
[self addChild:loaderBg z:ZLOADER_BG tag:TAG_LOADER_BG];
NSLog(#"prepare loader finish");
//animate loader
CCSprite *loaderPic = [[CCSprite alloc] initWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:LOADER]];
loaderPic.anchorPoint = ccp(0.5,0.5);
loaderPic.position = ccp(200,35);
loaderPic.visible = NO;
[self addChild:loaderPic z:ZLOADER_PIC tag:TAG_LOADER_PIC];
[loaderPic runAction:[CCRepeatForever actionWithAction:[CCRotateBy actionWithDuration:0.05f angle:10.0f]]];
}
-(void)goLoader {
NSLog(#"goMainMenuScene");
CCSprite *tmpBg = (CCSprite *) [self getChildByTag:TAG_LOADER_BG];
if (tmpBg != nil)
tmpBg.visible = YES;
CCSprite *tmpPic = (CCSprite *) [self getChildByTag:TAG_LOADER_PIC];
if (tmpPic != nil)
tmpPic.visible = YES;
double time = 2.0;
id delay = [CCDelayTime actionWithDuration: time];
id proceed = [CCCallFunc actionWithTarget:self selector:#selector(goMainMenuScene)];
id seq = [CCSequence actions: delay, proceed, nil];
[self runAction:seq];
}
-(void)goMainMenuScene {
[[GameManager sharedGameManager] runSceneWithID:SCENE_MAIN_MENU];
}
Your problem is most likely the new scene, and whatever happens in the new scene's and its child node's init methods. Loading resource files can take quite a while. If you defer doing this into onEnter you might see better results. But 20 seconds, that's a lot. Check what you're doing that takes THIS long. I bet it's loading a gross amount of resources, or loading them in an extremely inefficient way. JPG files are known to load very slowly, if you use JPG convert them to PNG.

UIAlertView Rendering Error

I have been working on an app for a couple of months now, but have finally run into an issue that I can't solve myself, and can't find anything on the internet to help.
I am using several normal UIAlertViews, in my app. Some have 2 buttons, some have 3 buttons, and a couple have 2 buttons and a text field. However all have the same issue. When you call [someAlertView show]; the alert view appears as normal, but then suddenly its graphics context seems to get corrupted as you can see from the screenshot.
This happens on both iPhone and iPad simulators (both 5.0 and 5.1), and happens on an iPad and iPhone4S device as well.
The image showing through is whatever happens to be behind the alertView.
The Alert still works, I can click the buttons, type in the text field, then when it dismisses the delegate methods are called correctly and everything goes back to normal. When the alertView appears again, the same thing happens.
The view behind the alert is a custom UIScrollView subclass with a content size of approximately 4000 pixels by 1000 with a UIImage as the background. The png file is mostly transparent, so is only about 80kB in memory size, and the phone is having no issues rendering it - the scroll view is still fully responsive and not slow.
It also has a CADisplayLink timer attached to it as part of the subclass. I have tried disabling this just before the alertView is shown, but it makes no difference so I am doubtful that is the issue.
This app is a partial rewrite of one I made for a university project, and that one could display UIAlertViews over the top of a scrollView of the same size and subclass without issue. The difference between this app and that one is that in my old app, I had subclassed UIAlertView to add extra things such as a pickerView, however I decided that I didn't like the way it looked so moved everything out of the alert and am just sticking with a standard UIAlertView.
This is how the alertView in the screenshot is called:
- (IBAction)loadSimulation:(id)sender {
importAlert = [[UIAlertView alloc] initWithTitle:#"Load Simulation" message:#"Enter Filename:" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Load", nil];
[importAlert setAlertViewStyle:UIAlertViewStylePlainTextInput];
[importAlert showPausingSimulation:self.simulationView]; //Calling [importAlert show]; makes no difference.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
[self hideOrganiser]; //Not an issue as the problem occurs on iPad as well.
}
}
With this being the categorised AlertView to add the ability to stop the scrollViews CADisplay link.
#interface UIAlertView(pauseDisplayLink)
- (void)showPausingSimulation:(UILogicSimulatorView*)simulationView;
#end
#implementation UIAlertView(pauseDisplayLink)
- (void)showPausingSimulation:(UILogicSimulatorView *)simulationView {
[simulationView stopRunning];
[simulationView removeDisplayLink]; //displayLink needs to be removed from the run loop, otherwise it will keep going in the background and get corrupted.
[self show];
}
I get no memory warnings when this happens, so I am doubtful it is due to lack of resources.
Has anyone come across an issue like this before? If you need further information I can try to provide it, but I am limited in what code I can post. Any help would be appreciated, I've been trying to solve this for two weeks and can't figure it out.
Edit:
It appears that it is not the AlertView at all (or rather it is not just the alertView), as the problem goes away when I remove the scroll view behind it, so there must be some issue between the two. This is the code for my UIScrollView subclass:
.h file:
#import
#import
#class ECSimulatorController;
#interface UILogicSimulatorView : UIScrollView {
CADisplayLink *displayLink;
NSInteger _updateRate;
ECSimulatorController* _hostName;
}
#property (nonatomic) NSInteger updateRate;
#property (nonatomic, strong) ECSimulatorController* hostName;
- (void) removeDisplayLink;
- (void) reAddDisplayLink;
- (void) displayUpdated:(CADisplayLink*)timer;
- (void) startRunning;
- (void) stopRunning;
- (void) refreshRate:(NSInteger)rate;
- (void) setHost:(id)host;
- (void)setMinimumNumberOfTouches:(NSInteger)touches;
- (void)setMaximumNumberOfTouches:(NSInteger)touches;
#end
.m file:
#import "UILogicSimulatorView.h"
#import "ECSimulatorController.h"
#import <QuartzCore/QuartzCore.h>
#implementation UILogicSimulatorView
#synthesize updateRate = _updateRate;
#synthesize hostName = _hostName;
- (void)reAddDisplayLink {
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; //allows the display link to be re-added to the run loop after having been removed.
}
- (void)removeDisplayLink {
[displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; //allows the display link to be removed from the Run loop without deleting it. Removing it is essential to prevent corruption between the games and the simulator as both use CADisplay link, and only one can be in the run loop at a given moment.
}
- (void)startRunning {
[self refreshRate:self.updateRate];
[displayLink setPaused:NO];
}
- (void)refreshRate:(NSInteger)rate {
if (rate > 59) {
rate = 59; //prevent the rate from being set too an undefined value.
}
NSInteger frameInterval = 60 - rate; //rate is the number of frames to skip. There are 60FPS, so this converts to frame interval.
[displayLink setFrameInterval:frameInterval];
}
- (void)stopRunning {
[displayLink setPaused:YES];
}
- (void)displayUpdated:(CADisplayLink*)timer {
//call the function that the snakeController host needs to update
[self.hostName updateStates];
}
- (void)setHost:(ECSimulatorController*)host;
{
self.hostName = host; //Host allows the CADisplay link to call a selector in the object which created this one.
}
- (id)initWithFrame:(CGRect)frame
{
//Locates the UIScrollView's gesture recogniser
if(self = [super initWithFrame:frame])
{
[self setMinimumNumberOfTouches:2];
displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(displayUpdated:)]; //CADisplayLink will update the logic gate states.
self.updateRate = 1;
[displayLink setPaused:YES];
}
return self;
}
- (void)setMinimumNumberOfTouches:(NSInteger)touches{
for (UIGestureRecognizer *gestureRecognizer in [self gestureRecognizers])
{
if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
{
//Changes the minimum number of touches to 'touches'. This allows the UIPanGestureRecogniser in the object which created this one to work with one finger.
[(UIPanGestureRecognizer*)gestureRecognizer setMinimumNumberOfTouches:touches];
}
}
}
- (void)setMaximumNumberOfTouches:(NSInteger)touches{
for (UIGestureRecognizer *gestureRecognizer in [self gestureRecognizers])
{
if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
{
//Changes the maximum number of touches to 'touches'. This allows the UIPanGestureRecogniser in the object which created this one to work with one finger.
[(UIPanGestureRecognizer*)gestureRecognizer setMaximumNumberOfTouches:touches];
}
}
}
#end
Well, I have managed to come up a solution to this. Really it is probably just masking the issue rather than finding the route cause, but at this point I will take it.
First some code:
#interface UIView (ViewCapture)
- (UIImage*)captureView;
- (UIImage*)captureViewInRect:(CGRect)rect;
#end
#implementation UIView (ViewCapture)
- (UIImage*)captureView {
return [self captureViewInRect:self.frame];
}
- (UIImage*)captureViewInRect:(CGRect)rect
{
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
[self.layer renderInContext:context];
UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return screenShot;
}
#end
- (void)showPausingSimulation:(UILogicSimulatorView *)simulationView {
[simulationView stopRunning];
UIView* superView = simulationView.superview;
CGPoint oldOffset = simulationView.contentOffset;
for (UIView* subview in simulationView.subviews) {
//offset subviews so they appear when content offset is (0,0)
CGRect frame = subview.frame;
frame.origin.x -= oldOffset.x;
frame.origin.y -= oldOffset.y;
subview.frame = frame;
}
simulationView.contentOffset = CGPointZero; //set the offset to (0,0)
UIImage* image = [simulationView captureView]; //Capture the frame of the scrollview
simulationView.contentOffset = oldOffset; //restore the old offset
for (UIView* subview in simulationView.subviews) {
//Restore the original positions of the subviews
CGRect frame = subview.frame;
frame.origin.x += oldOffset.x;
frame.origin.y += oldOffset.y;
subview.frame = frame;
}
[simulationView setHidden:YES];
UIImageView* imageView = [[UIImageView alloc] initWithFrame:simulationView.frame];
[imageView setImage:image];
[imageView setTag:999];
[superView addSubview:imageView];
[imageView setHidden:NO];
superView = nil;
imageView = nil;
image = nil;
[self show];
}
- (void)dismissUnpausingSimulation:(UILogicSimulatorView *)simulationView {
UIView* superView = simulationView.superview;
UIImageView* imageView = (UIImageView*)[superView viewWithTag:999];
[imageView removeFromSuperview];
imageView = nil;
superView = nil;
[simulationView setHidden:NO];
[simulationView startRunning];
}
Then modifying the dismiss delegate method in my class to have this line:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
[alertView dismissUnpausingSimulation:self.simulationView];
...
When the alert view is called, but before it is shown, I need to hide the simulator to prevent it corrupting the alert. However just hiding it is ugly as then all is visible behind is a empty view.
To fix this, I first make a UIImage from the simulator views graphics context. I then create a UIImageView with the same frame as the simulator and set the UIImage as its image.
I then hide the simulator view (curing the alert issue), and add my new UIImageView to the simulators superview. I also set the tag of the image view so I can find it later.
When the alert dismisses, the image view is then recovered based on its tag, and removed from its superview. The simulator is then unhidden.
The result is that the rendering issue is gone.
I know its too late for an answer to this question. Lately I had experianced this very same issue.
My Case:
Added couple of custom UIViews with background images and some controlls to the scroll view with shadow effect. I had also set the shadowOffset.
The Solution:
After some step by step analysis, I found out that setting the setShadowOpacity caused The rendering problem for me. When i commented that line of code, it cured the UIAlertView back to normal appearance.
More:
To make sure, I created a new project mimicing the original ui with shadowOpacity. But it didnt caused the rendering problem as i expected. So I am not sure about the root cause. For me it was setShadowOpacity.

Fetching image From Animating UIImageView

I'm using animation method of for implementing slide show in UIimageView as:
mainSlideShowImageView.animationImages=images;
mainSlideShowImageView.animationDuration = 75.00;
mainSlideShowImageView.animationRepeatCount = 0; //infinite
[mainSlideShowImageView startAnimating];
Now, what I want is to know which image is currently on image view screen.
How can I do so?
If it is not possible please tell that too.
Edit: 'images' here is array of UIImage.
AFAIK, there is no method which returns imageview's current image , however I have created a workaround for this....
array= [NSArray arrayWithObjects:[UIImage imageNamed:#"1.jpg"],[UIImage imageNamed:#"2.jpg"],[UIImage imageNamed:#"3.jpg"],[UIImage imageNamed:#"4.jpg"],[UIImage imageNamed:#"5.jpg"],nil];
mainSlideShowImageView.animationImages= array;
mainSlideShowImageView.animationDuration=15.0;
i=0;
timer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(increase) userInfo:nil repeats:YES];
[mainSlideShowImageView startAnimating];
-(void)increase
{
i++;
if (i>4) {
i=0;
}
}
-(void)getimage
{
UIImage* im = [array objectAtIndex:i];
// im is the current image of imageview
}
I have taken 5 images here and animation duration of 15 , that means image will change at every 3 seconds, so I have kept timer to fire at every 3 seconds ... and since array contains 5 images ... so if 'i' goes beyond 4 I have put it back to 0 in method increase

pragmatically scroll UIScrollView upon shake gesture

HI,
I have a view that has three UIScrollViews on the screen. I want to randomly scroll the UIScrollViews to different positions whenever a user shakes the iPhone, but I am unable to get it done.
I have implemented the following in my ViewController class to detect and handle the shake gesture, the 3 UIScrollViews also belong to the same class. The shake gesture is detected, but the UIScrollViews do not change. What am I doing Wrong??
i have tried both motionBegan and motionEnded.
-(BOOL)canBecomeFirstResponder {
return YES;
}
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)viewWillDisappear:(BOOL)animated {
[self resignFirstResponder];
[super viewWillDisappear:animated];
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
int randomTag = arc4random() % [dirContents count];
CGRect nextImageView = [[scrollView1 viewWithTag:2] frame];
[scrollView1 scrollRectToVisible:nextImageView animated:YES];
randomTag = arc4random() % [dirContents count];
nextImageView = [[scrollView2 viewWithTag:4] frame];
[scrollView2 scrollRectToVisible:nextImageView animated:YES];
randomTag = arc4random() % [dirContents count];
nextImageView = [[scrollView3 viewWithTag:4] frame];
[scrollView3 scrollRectToVisible:nextImageView animated:YES];
NSLog(#"Shake Detected End");
}
}
Thank You
Have you tried using SetContentOffset instead of scrollRectToVisible yet?
if the images in your tableView are of equal height the offset per "Element" is always the same.
[scrollView3 setContentOffset:yourRandomOffsetInPixels animated:YES];
maybe this works.
also consider, that The Problem might be that your shake-detection Method runs on a separate Thread this would mean that you have to call your motionEnded Method on the mainthread like so:
[self performSelectorOnMainThread:#selector(motionEnded) withObject:nil waitUntilDone:NO];
Did you check your nextImageView variable to see if it was correct ?
Further more if you are trying will have the motion of a slot machine, I would recommend you to use UITableView instead of doing it by yourself with scrollView
Just one quick question. In your example code, you generate a random tag:
randomTag = arc4random() % [dirContents count];
but then you use a specific tag value (4 in this case)? I assume it still doesn't work when you use the randomTag value? and that you were just doing some testing?
nextImageView = [[scrollView2 viewWithTag:4] frame];