My app is simple: I want to move a small uiimageview around a view of an iPad with the accelerometer (now Core Motion). So, I can move the uiimageview around with the accelerometer, but I am having trouble establishing working borders in landscape mode. So, here is my code. The problem is that the uiimageview tends to stick to the borders and some of the borders are not perfectly on the edge of the view. Here is my code, any help is greatly appreciated:
- (void)startMyMotionDetect
{
__block float stepMoveFactor = 15;
[self.motionManager
startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc] init]
withHandler:^(CMAccelerometerData *data, NSError *error)
{
dispatch_async(dispatch_get_main_queue(),
^{
CGRect rect = self.sumbarine.frame;
float movetoY = rect.origin.x + (data.acceleration.x * stepMoveFactor);
float maxY = self.view.frame.size.width-rect.size.width;
float movetoX = (rect.origin.y + rect.size.height)
- (data.acceleration.y * stepMoveFactor);
float maxX = self.view.frame.size.height;
if ( movetoX >0 && movetoX < maxX ) {
rect.origin.x += (data.acceleration.y * stepMoveFactor);
};
if ( movetoY > 0 && movetoY < maxY ) {
rect.origin.y += (data.acceleration.x * stepMoveFactor);
};
[UIView animateWithDuration:0 delay:0
options:UIViewAnimationOptionCurveEaseIn
animations:
^{
self.sumbarine.frame = rect;
}
completion:nil
];
}
);
}
];
}
The sticking issue comes from your conditions for moving. Right now, you say, if "moveToX/Y" is > 0, and less than the width/height, then add Acceleration X/Y.
What happens if origin is == or >greater than "maxX/Y"? Answer: Nothing. And thus, the submarine image acts like it's stuck to a board. Probably it can move on the alternate axis, until it's finally stuck at a condition where it no longer can move.
You'll want to play around with your conditional rules. I do not know what type of actions/input you're trying to implement for gameplay, so I can't tell you what to do next. I'm assuming the sub is supposed to go up/down depending on some user gesture/action.
One last piece of advice. Accelerometers are based on G-Force... What this means for you is even though acceleration on some axis is positive when you move say left... There's also an inverse curve when you need apply the force needed to stop. Try looking at the motiongraphs app (available in documentation) to help you get a better feel/sense of this stuff.
And remember, have fun!
Related
I've created an app where images fall from the sky and I have to "catch" them on the bottom with a user-moveable basket. I put the falling images and the basket image in CGRectMake frames and used CGRectIntersectsRect to detect for collisions. However, it's not detecting every collision. For example, I'll have visually "caught" 10 objects, the two frames definitely intersected, but it will only say I caught 2. What am I doing wrong?
Here is the code for the bucket:
(void)viewDidLoad
{
[super viewDidLoad];
basketView = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"bucket.png"]];
basketView.frame = CGRectMake(130.0, 412.0, 30.0, 30.0);
[self.view addSubview:basketView];
}
Here is the falling object and collision code:
- (void)onTimer
{
UIImageView* appleView = [[UIImageView alloc] initWithImage:appleImage];
int applePosition = round(random() % 320); //random starting x coord
double scale = 1 / round(random() % 100) + 1.0; //random apple size
double speed = 1 / round(random() % 100) + 1.0; //random apple speed
appleView.frame = CGRectMake(applePosition, -100.0, 25.0 * scale, 25.0 * scale);
[self.view addSubview:appleView];
[UIView beginAnimations:nil context:(__bridge void *)(appleView)];
[UIView setAnimationDuration:3 * speed];
appleView.frame = CGRectMake(applePosition, 500.0, 25.0 * scale, 25.0 * scale);
// Test for landing in bucket
if (CGRectIntersectsRect(basketView.frame, appleView.frame)) {
collision++;
printf("%i\n", collision);
[appleView removeFromSuperview];
}
}
EDIT: I made the basket huge, and now I'm getting spammed numbers and no apples are showing up. So I'm assuming that it's only checking for intersection with the apple at its starting point, not during the actual animation. How can I make it check for intersection DURING the animation?
Obviously if your timer function spawns new objects, the test will be done exactly after the creation of the object, so if the object is not actually created in the basket, your hit test will always be negative.
Now, you need another timer which will test for collisions every 10th of a sec or so (depending on how fast you move your objects, this could be more or less). An even better option (form the performance point of view) is to use CADisplayLink which works pretty much like a timer only it allows you to synchronise your tests to the refresh rate of the display.
I'm currently creating a iPhone app that displays my speed heading location and a few other sensor readings for use on my bike. I've managed to get the tilt reading from the accelerator. what I'm trying to do is move an image either left or right depending on what the tilt reading is.
_______________________________
: === :
: = = :
: === :
-------------------------------
lets say the = signs is my image, I'm trying to get it to move either towards the left or right edge depending on the tilt angle.
any ideas? I've managed to move it by animations but I don't want it to continuously move if I'm leaning for a long period of time.
First I added the accelerator data using:
UIAccelerometer *theAccelerometer = [UIAccelerometer sharedAccelerometer];
theAccelerometer.updateInterval = 0.1; //in seconds
theAccelerometer.delegate = self;
Notice the timing is 0.1 seconds. You will want to match that timing to the animation below. I then set the animation up in the accelerometer:didAccelerate: delegate call:
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
NSInteger newX = 0;
newX = imageView.frame.size.width/2 + ((320-imageView.frame.size.width) * (acceleration.x+1)/2.0);
[UIView animateWithDuration:0.1 animations:^{
imageView.center = CGPointMake(newX, imageView.center.y);
}] ;
}
It is important to match the animation duration with the updateInterval of the UIAccelerometer so that you make sure to complete the animation before the next update comes through.
The important part here is probably the math to determine the newX position. I basically took half of the width of the image, because that is the left most absolute position. From there I knew I had a 320-imageView.frame.size.width amount of room that I could add. If I added the entire 320-imageView.frame.size.width then I would be at 320-imageView.frame.size.width/2 which is the right-most position. From there I just needed to break up the 320-imageView.frame.size.width chunk according to how much we have accelerated. The accelerator.x data is in the range -1 to 1, so my entire range is 2. So I added 1 to the accelerator.x to normalize it from 0-2. Then I divided by 2.0 so that I would have a range from 0-1. Now I had a multiplier that I could multiply my 320-imageView.frame.size.width range by. If it was 0 I would be at the left-most position. If it was 1 I would be at the right-most position, and anything in between would be broken up linearly. So the final term is:
imageView.frame.size.width/2 + ((320-imageView.frame.size.width) * (acceleration.x+1)/2.0);
to get the new x position of the image.
I hope that is what you are looking for! I tested it on a device and it is pretty smooth as long as you match up your durations.
If you are asking the question about animation. This is code apple recommnad to animate UIView. And you can change value for x and y coordinate.
imageFrame.origin.y = 200;
[UIView animateWithDuration:0.5
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{
datePickerView.frame = imageFrame;
}
completion:^(BOOL finished){
self.datePickerViewFlag = YES;
}];
is there anyway we can do rotation of image in a pendulum effect in iphone using UIView Animation or CoreAnimation
Use CGAffineTransformRotate to rotate your UIImageView e.g.
yourImage.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI * (_angle/ 180.0));
Also, you'll need to set up the anchor point on your image, prior to rotating it, so that you can make it swing from the correct position. e.g.
yourImage.layer.anchorPoint = CGPointMake(0.5,0); // Set anchor to top middle.
Obviously, you'll need to set up a timer to adjust move the image and control the angle. You could do something like the following in a timer. (untested)
_angle = 45; // Set starting angle.
_direction = 1; // Set starting direction.
-(void) movePend
{
if(_direction == 45){
_direction = 1;
} else if(_direction == 180) {
_direction = 0;
}
_angle = (_direction) ? _angle-- : _angle++; // Determine which way to rotate.
yourImage.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI * (_angle/ 180.0));
}
Edit: See the answer below.
I finally give up and come here to ask you for my problem...
I'm using a UIScrollView for a scrolling menus with little icons.
On each page, with paging enabled, there's an icon in the center, and 2 and a half other visible icons on the left and right. I can move from one icon to its neighbour, and that is fine, but the point is that if I do a fast scrolling, it will not move from more than 3 icons, which is the width of the screen.
What I would want is to be able to scroll on more than 3 icons, and that the magnet behaviour is only triggered when it's slowing down.
I've tried to schedule the scroll view to calculate its velocity, and set the pagingEnabled attribute to NO when it's moving fast and YES again when it's slowing down, but as soon as it is set to YES, the view comes back very fast at its original position, as if it was not detecting that I had brought it to a new page. Would anyone know why it does this? And if I have a way to tell the view "ok, now the paging is enabled but look, you're 15 pages later. Just center on the current page, don't come back at the beginning."
Here's my update function (if it can help):
-(void)update:(ccTime)dt
{
float velocity = fabsf((self.previousOffset-self.scrollView.contentOffset.y)/dt);
self.previousOffset = self.scrollView.contentOffset.y;
CCLOG(#"Velocity: %f", velocity);
if(self.scrollView.pagingEnabled)
{
if(velocity > 100)
{
self.scrollView.pagingEnabled = NO;
}
}
else
{
if(velocity < 100)
{
self.scrollView.pagingEnabled = YES;
}
}
}
I finally found a solution, which was pretty obvious, but that I did not see at the beginning, by using setContentOffset on the scrollView.
Here is the new update function:
-(void)update:(ccTime)dt
{
float velocity = 1000;
if(self.previousOffset)
{
velocity = fabsf((self.previousOffset-self.scrollView.contentOffset.y)/dt);
}
self.previousOffset = self.scrollView.contentOffset.y;
if(velocity < 300)
{
CGSize screenSize = [[CCDirector sharedDirector] winSize];
float halfScreen = screenSize.width/2;
CCLayer *panel = (CCLayer *)[self getChildByTag:1];
SQScrollViewMenu *menu = (SQScrollViewMenu *)[panel getChildByTag:1];
SQMissionItem *currentItem = (SQMissionItem *)[menu getChildByTag:currentPage];
float contentOffsetY = [self.scrollView contentOffset].y;
CCLOG(#"Currentpage: %i ; currentoffsetY: %f", currentPage, contentOffsetY);
float distance = contentOffsetY + [currentItem position].x - halfScreen + panel.position.x + menu.position.x + panel.parent.position.x - 60;
CCLOG(#"Which gives a distance of: %f", distance);
[self.scrollView setContentOffset:CGPointMake(0, distance) animated:YES];
self.previousOffset = 0;
[self unschedule:#selector(update:)];
CCLOG(#"Is unscheduled");
}
}
And it is almost working... at least, it is on simulator. But as soon as I try it on my iPhone 4, it does not work anymore. It always go into that update function, but 7 times among 8, it just blocks the scrollView as it is and does not drags it back to the position I give it... but sometimes it does.
Would anyone have an idea? I found similar issues on the net, but none of them could resolve this...
I have a UIView that I rotate with this code:
helpView.transform = CGAffineTransformMakeRotation(degreesToRadians( rotationAngle ));
Where degreeToRadians just is a macro to convert from degrees to radians.
This works fine as long as the view is visible, eg alpha = 1. When I hide it (alpha = 0, which I animate) it does not rotate any more. I guess this is a smart way for the devices to "save" on drawing time, but is there any way I can force it to be drawn even when alpha is 0? Otherwise I will have to rotate it before I show it again.
Any good ideas?
Thanks
Edit: This is the code I use to show/hide the view.
-(void)showHelp
{
bool helpAlpha = !helpView.alpha;
CGFloat newScale;
if (helpView.alpha) {
newScale = kHelpSmall;
helpView.transform = CGAffineTransformMakeScale(kHelpBig, kHelpBig);
} else {
newScale = kHelpBig;
helpView.transform = CGAffineTransformMakeScale(kHelpSmall, kHelpSmall);
}
[UIView animateWithDuration:(kAnimationTimeShort / 2) animations:^(void) {
[helpView setAlpha:helpAlpha];
helpView.transform = CGAffineTransformMakeScale(newScale, newScale);
}];
}
As you see I also scale it for a nicer effect. Works perfect when visible, does not rotate when alpha = 0. Rotation is done in another method, where I would prefer to keep it as I also rotate some other views there.
You are resetting the transform every time you use CGAffineTransformMake*. If you do this, you will get either a rotated transform or a scaled one. I am assuming the scaled one is after the rotated one and hence you aren't able to see the view rotated. If you need both the effects to remain, you will have to use CGAffineTransformRotate. So a scale and rotate will be
helpView.transform = CGAffineTransformMakeScale(kHelpSmall, kHelpSmall);
helpView.transform = CGAffineTransformRotate(helpView.transform, degreesToRadians(rotationAngle));
The order might vary.