I am using http://www.chaosinmotion.com/flowcover.m and would like to know how I may display a label for each image when it is displayed in the flowcover (the current image)
Thanks,
:)
I suggest reading the documentation before posting questions. With no familiarity with this library at all, in 30 seconds, I downloaded the source, found the docs, and skimmed until I found - (void)flowCover:(FlowCoverView *)view didSelect:(int)cover, which appears to be a delegate method.
At a glance, it looks like you're not meant to know what's front and center, only what the user picked (which may very well mean "what just became front and center"). Test it and see.
You can easily get this notification by changing the Delegate Protocol in FlowCoverView.h like this:
#protocol FlowCoverViewDelegate
- (int)flowCoverNumberImages:(FlowCoverView *)view;
- (UIImage *)flowCover:(FlowCoverView *)view cover:(int)cover;
- (void)flowCover:(FlowCoverView *)view didSelect:(int)cover;
- (void)flowCover:(FlowCoverView *)view highlighted:(int)cover; //<-- Added
#end
And replacing your -[FlowCoverView driveAnimation] method in FlowCoverView.m with this:
- (void)driveAnimation
{
double elapsed = CACurrentMediaTime() - startTime;
if (elapsed >= runDelta) [self endAnimation];
else [self updateAnimationAtTime:elapsed];
/*
* Change by Uppfinnarn <uppfinnarn#gmail.com>.
* This will give the Delegate a message every time the highlighted cover (eg. the one in the center) changes.
* Can be used to display subtitles for example.
*/
if (delegate) [delegate flowCover:self highlighted:(int)floor(offset + 0.01)]; // make sure .99 is 1
}
Create a UILabel somewhere and place it on top of the FlowCoverView (make sure it's opaque is NO so that it doesn't create a black box behind it), then monitor -[flowCover:highlighted:] to know which cover is currently in the center and change the text accordingly.
Note that this won't give you a notification for when the view is first created, only once the animation moves. You have to figure out the first title yourself.
Also, this won't give you notifications while the user is dragging with their finger, only when it's "gliding freely".
EDIT:
Replace your -[FlowCoverView touchesMoved:withEvent:] method with this to get callbacks when the user is dragging with their fingers too:
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGRect r = self.bounds;
UITouch *t = [touches anyObject];
CGPoint where = [t locationInView:self];
double pos = (where.x / r.size.width) * 10 - 5;
if (touchFlag) {
// determine if the user is dragging or not
int dx = fabs(where.x - startTouch.x);
int dy = fabs(where.y - startTouch.y);
if ((dx < 3) && (dy < 3)) return;
touchFlag = NO;
}
int max = [self numTiles]-1;
offset = startOff + (startPos - pos);
if (offset > max) offset = max;
if (offset < 0) offset = 0;
/*
* Change by Uppfinnarn <uppfinnarn#gmail.com>.
* Based on Ilkka Pirttimaa <ilkka.pirttimaa#gmail.com>'s solution for notifications.
* when the animation stops.
*
* This will give the Delegate a message every time the highlighted cover (eg. the one in the center) changes.
* Can be used to display subtitles for example.
*/
if(delegate) [delegate flowCover:self highlighted:(int)floor(offset + 0.01)];
[self draw];
double time = CACurrentMediaTime();
if (time - startTime > 0.2) {
startTime = time;
lastPos = pos;
}
}
Related
all. I'm trying to follow a tutorial on making a ball bounce around the screen of an iPhone. The tutorial constructs the application in a MVC scheme. I'm having trouble wrapping my head around this concept when it comes to the drawRect method in the View implementation.
This is my Model header file:
#import <Foundation/Foundation.h>
#import "TestView.h"
#define BALL_SIZE 20.0
#define VIEW_WIDTH 320.0
#define VIEW_HEIGHT 460.0
#interface TestModel : NSObject
{
TestView* ball;
CGPoint ballVelocity;
CGFloat lastTime;
CGFloat timeDelta;
}
- (void) updateModelWithTime:(CFTimeInterval) timestamp;
- (void) checkCollisionWithScreenEdges;
#property (readonly) TestView* ball;
#end
The tutorial instructs me the user to override the init method of NSObject. I've also included the methods for controlling the "animation" logic:
- (id) init {
self = [super init];
if (self) {
ball = [[TestView alloc] initWithFrame: CGRectMake(0.0, 0.0, BALL_SIZE, BALL_SIZE)];
// Set the initial velocity for the ball
ballVelocity = CGPointMake(200.0, -200.0);
// Initialize the last time
lastTime = 0.0;
}
return self;
}
- (void) checkCollisionWithScreenEdges {
// Left Edge
if (ball.frame.origin.x <= 0) {
ballVelocity.x = abs(ballVelocity.x);
}
// Right Edge
if (ball.frame.origin.x >= VIEW_WIDTH - BALL_SIZE) {
ballVelocity.x = -1 * abs(ballVelocity.x);
}
// Top Edge
if (ball.frame.origin.y <= 0) {
ballVelocity.y = abs(ballVelocity.y);
}
// Bottom Edge
if (ball.frame.origin.y >= VIEW_HEIGHT - BALL_SIZE) {
ballVelocity.y = -1 * abs(ballVelocity.y);
}
}
- (void) updateModelWithTime:(CFTimeInterval) timestamp {
if (lastTime == 0.0) {
// initialize lastTime if first time through
lastTime = timestamp;
} else {
// Calculate time elapsed since last call
timeDelta = timestamp - lastTime;
// Update the lastTime
lastTime = timestamp;
[self checkCollisionWithScreenEdges];
// Calculate the new position of the ball
CGFloat x = ball.frame.origin.x + ballVelocity.x * timeDelta;
CGFloat y = ball.frame.origin.y + ballVelocity.y * timeDelta;
ball.frame = CGRectMake(x, y, BALL_SIZE, BALL_SIZE);
}
}
The View implementation file is the following:
#import "TestView.h"
#implementation TestView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect) rect {
}
#end
Finally, my View Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
gameModel = [[TestModel alloc] init];
[self.view addSubview:gameModel.ball];
// Set up the CADisplayLink for the animation
gameTimer = [CADisplayLink displayLinkWithTarget:self selector:#selector(updateDisplay:)];
// Add the display link to the current run loop
[gameTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void) updateDisplay:(CADisplayLink *) sender {
[gameModel updateModelWithTime:sender.timestamp];
}
OK, so now that I've provided a look at the structure of the code (hopefully I've given enough) I can get to my question. So when I add anything to drawRect a new object is drawn and does not get "animated" by the model logic methods.
Right now I have a bouncing square. When I try to fill the square with an ellipse in drawRect, I get a new object, drawn how I want, that just sits at 0,0 while the bouncing square is still active.
I'm sure I'm missing something really big here, but I've been banging my head against the wall for hours and can't figure it out. Any help would be greatly appreciated!
A couple of things here:
1- Have you overriden the view class in SB to TestView?
2- Looking at your code, I do not see how your model is connected to your View through your controller. Recall from the MVC model, that the Controller is on top and talks down (pyramid style) to the model and the view and so, somewhere in your view controller:
a) you need to instantiate your model
b) get values from your model and pass them to your view variables - which would be called in drawrect
Last but not least, lookup setNeedsDisplay which is the way to call and refresh the view.
Hope this helps without spoiling the assignment.
We want to count number of shakes done by user.
We tried motionBegan, motionEnded but its of no use.
Because they are fired only when user start a shake or ends a shake but i want to count shakes continuously.
May be something like this, when user moves iPhone to left one side and right one side, I count it as one shake.
Any help would be appreciated.
Thanks
You could use the UIAccelerometer to achieve what you want.
You use motionBegin to detect a start :
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
UIAccelerometer* acc = [UIAccelorometer sharedAccelerometer];
acc.delegate = self;
acc.updateInterval = /* whatever you feel like OK */ 0.1;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
UIAccelerometer* acc = [UIAccelorometer sharedAccelerometer];
acc.delegate = nil;
}
and in the delegate method :
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
//
// You detect a full shake
//
}
Before implementing this, you should know that UIAccelerometer has been deprecated in iOS5. This means you will have to use what Apple recommands to use instead. I'm not updated yet on the topic.
Here is the documentation about it : link.
I'm trying to implement a gauge animation using (+ and - buttons) on iphone, but i have no idea where to start? Any help is really welcome. See the image below (this is what I'm trying to do). Thanks for your help.
Here is some open source code (with an example) that implements the gauge view. You of course would still need to do the buttons yourself, and possible add a different visual style.
http://www.cocoacontrols.com/platforms/ios/controls/meterview
You need to rotate the needle based on the angle... Here is the logic
You can refer my answer here... Rotating a UIImageView around a point over 10 seconds?
fireInterval = 10;
//Adjust starting and ending angle
mStartingAngle = 45;
mEndingAngle = 180;
//Implementation
-(void) startTimer
{
mPreviousTime = [NSDate timeIntervalSinceReferenceDate];
}
In the loop
-(void) updateFunction
{
NSTimeInterval timeNow = [NSDate timeIntervalSinceReferenceDate];
//NewValue = (((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
//Mapping values between mStartAngle and mEndAngle
mCurrentAngle = (((timeNow - mPreviousTime) * (mEndingAngle - mStartingAngle)) / (previousTime+fireInterval - mPreviousTime)) + mStartingAngle;
if( mPreviousTime + fireInterval <= timeNow )
{
NSLog(#"10 seconds completed");
mPreviousTime = timeNow;
}
}
And rotate the needle based on mCurrentAngle....
I'm looking for a little help. I'm usually pretty good at finding what I need, but this one's tricky.
Here's the scene for my test app: I have 2 scrollviews that are 1024x85 and they only move horizontal. I also have a UISwitch below them. Above the scrollviews I have two labels that display the content offset of each scrollview as it moves (so I can see what's going on).
What I want to do: After the user slides each of the views side to side I would like to use the UISwitch to lock those scrollviews together wherever they may be sitting.
This is the updated code:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGPoint p = scrollOne.contentOffset;
CGPoint r = scrollTwo.contentOffset;
// Print the contentOffset labels
scrollOneLabel.text = [NSString stringWithFormat:#"%.2f", p.x];
scrollTwoLabel.text = [NSString stringWithFormat:#"%.2f", r.x];
// If lock is on, the distance between offsets is locked (but limited to max and min)
if (lockSwitch.on) {
NSInteger offset = scrollOne.contentOffset.x - scrollTwo.contentOffset.x;
if (scrollView == scrollOne) {
NSInteger maxOffset = scrollTwo.contentSize.width - scrollTwo.frame.size.width;
[scrollTwo setContentOffset: CGPointMake(MIN(MAX(0.0,scrollOne.contentOffset.x - offset), maxOffset), 0.0)];
} else if (scrollView == scrollTwo) {
NSInteger maxOffset = scrollOne.contentSize.width - scrollOne.frame.size.width;
[scrollOne setContentOffset: CGPointMake(MIN(MAX(0.0,scrollTwo.contentOffset.x + offset), maxOffset), 0.0)];
}
}
// If the lock is not on, both move independently
}
It locks the scrollviews together, but as soon as I move one of the scrollviews the second scrollview jumps to the same content offset as the first. I'm trying to lock them where they are at that moment instead of lining them up when the user touches one of them.
Thank is in advance for any help.
So basically you want to keep the offset between the two constant? In that case you need a variable, let's call it offset, which would be scrollOne.contentOffset.x - scrollTwo.contentOffset.x and after locking them just react to the delegate method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// If lock is on, the distance between offsets is locked (but limited to max and min)
if (lockSwitch.on) {
if (scrollView == scrollOne) {
NSInteger maxOffset = scrollTwo.contentSize.width - scrollTwo.frame.size.width;
[scrollTwo setContentOffset: CGPointMake(MIN(MAX(0.0,scrollOne.contentOffset.x - offset), maxOffset), 0.0)];
} else if (scrollView == scrollTwo) {
NSInteger maxOffset = scrollOne.contentSize.width - scrollOne.frame.size.width;
[scrollOne setContentOffset: CGPointMake(MIN(MAX(0.0,scrollTwo.contentOffset.x + offset), maxOffset), 0.0)];
}
}
// If the lock is not on, both move independently
}
EDIT: You have to set the offset when the UISwitch changes state, not on every scrolling event. Have something like this tied to your UISwitch with the [addTarget:selector:forControlEvents:] method. Remember, the offset has to be a global variable in your class.
- (void)lockChanged:(id)sender {
UISwitch *lock = sender;
if (lock.on) {
offset = (int)(scrollOne.contentOffset.x - scrollTwo.contentOffset.x);
} else {
offset = 0;
}
}
So I a little backstory. I wanted to implement a particle effect and sound effect that both last about 3 sec or so when the user shakes their iDevice. But first issue arrived when the build in UIEvent for shakes refused to work. So I took the advice of a few Cocos veterans to just use some script to get "violent" accelerometer inputs as shakes. Worked great until now.
The problem is that if you keep shaking it just stacks the particle and sounds over and over. Now this wouldn't be that big of a deal except it happens even if you are careful to try and not do so. So what I am hoping to do is disable the accelerometer when the particle effect/sound effect start and then reenable it as soon as they finish. Now I don't know if I should do this by schedule, NStimer, or some other function. I am open to ALL suggestions. here is my current "shake" code.
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
const float violence = 1;
static BOOL beenhere;
BOOL shake = FALSE;
if (beenhere) return;
beenhere = TRUE;
if (acceleration.x > violence * 1.5 || acceleration.x < (-1.5* violence))
shake = TRUE;
if (acceleration.y > violence * 2 || acceleration.y < (-2 * violence))
shake = TRUE;
if (acceleration.z > violence * 3 || acceleration.z < (-3 * violence))
shake = TRUE;
if (shake) {
id particleSystem = [CCParticleSystemQuad particleWithFile:#"particle.plist"];
[self addChild: particleSystem];
// Super simple Audio playback for sound effects!
[[SimpleAudioEngine sharedEngine] playEffect:#"Sound.mp3"];
shake = FALSE;
}
beenhere = FALSE;
}
UIAcceleration has a timestamp property. I would modify your code to save the current timestamp on a succesful shake in a static variable (perhaps static NSTimeInterval timestampOfLastShake?). Then modify if (shake) to if (shake && acceleration.timestamp - 3.0f >= timestampOfLastShake)
resulting code:
static NSTimeInterval timestampOfLastShake = 0.0f;
if (shake && acceleration.timestamp - 3.0f >= timestampOfLastShake ) {
timestampOfLastShake = acceleration.timestamp;
id particleSystem = [CCParticleSystemQuad particleWithFile:#"particle.plist"];
[self addChild: particleSystem];
// Super simple Audio playback for sound effects!
[[SimpleAudioEngine sharedEngine] playEffect:#"Sound.mp3"];
shake = FALSE;
}
You realize that you are doing single axis acceleration checks and you are not ensuring the acceleration is repeated (i.e. shake). In other words, if you drop your phone, your code will think it's someone shaking the device back-and-fourth few times (which is what a shake is) and fire many times a second. So, either apply multi-axis checks against time or simply use the shake UIEvent. All you will need to do is in your UIView (or UIWindow better yet), implement - (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event making sure the view becomes first responder. This will take care of all acceleration filtering, etc. and app won't be bombarded by all the acceleration noise (you can drop your phone on the table and it won't mistake that for a shake).
Go here for doc: http://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/MotionEvents/MotionEvents.html
Or:
- (BOOL)canBecomeFirstResponder {
return YES;
}
// Now call [self becomeFirstResponder]; somewhere, say in viewDidAppear of the controller.
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (event.subtype == UIEventSubtypeMotionShake) {
// You've got a shake, do something
}
}
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
}