i'm developing an application that allows the user to rotate the needles of a clock. for that i'm using the transform property of the needle's imageview :
CGAffineTransform newTrans = CGAffineTransformRotate(initialTransform, -angleDif);
minutesNeedle.transform = newTrans;
for the purpose of detecting the user's touch i must detect the needle's space (minute or hour's frame). And here my problem show off, after rotating the image it looks like the image is stretched and interfere with the other image. the UIView class documentation says that after being transformed the view's frame changes and you must use center and bounds properties. However, i cannot use the bounds property to detect the touches user which are located in the superview.
is there any other way to do this animation without changing the frame size (i doubt). Or can i resize the frame in the touchesEnded method (i've tried but in vain). Please help i'm blocking there for one week!! Thanks
Maybe I'm missing something, but it seems like you can avoid this conflict altogether. Create a separate view for handling touches, independent of the needle view. On a touch event, move the needle view appropriately. This separate view would be the size of the clock essentially and be overlaid on the other views, having a clear background. It would have a delegate with appropriate callbacks so your view controller (or whatever owns the needle) can adjust it.
As far as what the callbacks would be, I would just forward the touch events like:
- (void)touchReceiverView:(TouchReceiverView *)view didReceiveTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
Do it with CALayers is WAY much easier and performance is better this way.
CALayer *handLayer = [CALayer layer];
handLayer.contents = (id)[UIImage imageNamed:#"hand.png"].CGImage;
handLayer.anchorPoint = CGPointMake(0.5,0.0)];
[myview.layer addSublayer:handLayer];
//i.e.: if handLayer represents the seconds hand then repeat this every second ;)
handLayer.transform = CGAffineTransformMakeRotation (angle); //set the angle here
Someone wrote a ClockView sample using CALayers, maybe you find it useful.
Related
i am having some very strange issue. Basically i am implementing a drag and drop view with a snap to the horizontal 1D grid. Well, so when i drag a view and its center coordinate X is bigger or smaller then a different view, the non-dragged view should be animated to the left or right of its original position.
This works fine most of the time. But in some special cases its not working. In some situations specific situations the view does not receive any gesture callbacks anymore. I can reproduce this issue and have found out that when i remove the code that applies the animation, everything its working fine.
Basically this is the code that is called when the dragged view is at a position where the view below should be moved to the left or right
/**
* Animate element to its saved position
*/
- (void)switchElement:(unsigned int)draggedIndex with:(unsigned int)otherIndex
{
// first animate
UIView *view = views[draggedIndex];
UIView *otherView = views[otherIndex];
// IF I COMMENT THIS OUT, EVERYTHING WORKS FINE
otherView.frame = [self getImageRectForIndex:draggedIndex];
// now switch internally
if(draggedIndex != otherIndex)
{
// switch views
views[draggedIndex] = otherView;
views[otherIndex] = view;
}
}
Any idea if there is something to have in mind if i animate UIViews and have gesture recognizers attached to them?
If somebody is willing, i can paste the whole class here to test it.
SOLUTION
I am having some "highlight" views in my design. And i have moved the relevant views behind those transparent background views by accident. So now i am not using addSubview: but insertSubview:atIndex: instead.
But marking #Anthonin C. answers as the right one. Because it pointed me in the correct direction (I found it out by overriding the hitTest: method)
Would you please track the otherView.bounds property to verify that your touch isn't out of bounds. I faced this issue recently and Apple documentation provide solution here : Delivering touch events to a view outside the bounds of its parent view
Sorry, I don't have enough reputation to comment your answer.
I'm looking for a way to test if a UIView is (partially) obscured by an UIActionSheet, UIAlertView, HUD, etc., or if it is (partially) off the screen in a sliding panel, UIScrollView, etc.. This needs to be a generic solution because there are too many ways to obscure a view.
What comes to mind is to perform a few hit tests at key points where the UIView should be and check how many, if any, hit the given UIView.
Example: Imagine there is no hand in the image below. I'd like to perform a programmatic hit test at the point where the finger is. If View A.2 is not returned, then it is obscured.
I've looked into hit tests with touch events, but how can I programmatically perform a hit test and see which UIView is hit without physical touch events?
References:
Hit-Testing in iOS
Event handling for iOS - how hitTest:withEvent: and pointInside:withEvent: are related?
I found one answer.
First, convert the coordinates of the view you want to perform a hit test on (let's use UIView *testView) to those of the screen coordinates.
CGPoint screenOrigin =
[testView.superview convertPoint:testView.frame.origin
toView:nil];
Next, make a couple test hit points (for example, for the upper left and lower right corners of the view). This is an example of one test point.
CGPoint hitTestPoint = CGPointMake(30, screenOrigin.y + 30); // upper left
Then find the top most window* and call hitTest:withEvent: on it. The event can be nil.
UIView *hitTest = [mainWindow hitTest:hitTestPoint withEvent:nil];
Then check if the hitTest result is equal to the testView:
if (testView && [testView isEqual:hitTest] /* && [testView isEqual:hitTest2] */) {
// The view is not obscured nor off the screen in a sliding panel
}
*Finding the top most window is still an open question, but for most people something like this may work:
UIWindow *mainWindow = [[[UIApplication sharedApplication] windows] firstObject]
I have tried many times to implement a D-Pad for my new maze game, but am having trouble doing so. I am doing it in the style of 4 UIButtons and if you press one, it moves another image up, down, left or right. I have tried using the quartzcore and CAAnimation, but only know limited code.
I have the methods and the buttons declared, but cannot code a working one.
I was doing:
-(IBAction)Up:(id)sender{
CGPoint origin1 = self.Player.center;
CGPoint target1 = CGPointMake(self.Player.center.x, self.Player.center.y-124);
CABasicAnimation *bounce1 = [CABasicAnimation animationWithKeyPath:#"position.y"];
bounce1.duration = 0.1;
bounce1.fromValue = [NSNumber numberWithInt:origin1.y];
bounce1.toValue = [NSNumber numberWithInt:target1.y];
[self.Player.layer addAnimation:bounce1 forKey:#"position"];
}
The ghost moves down, but bops back up immediately. I've been stumped for hours and it may be glaring me in the face, but please understand my noobishness.
In Core Animation, there are two layer hierarchies: one of model layers and one of presentation layers. Presentation layers are what you see; model layers are what you often interact with in code. This separation is valuable because it lets you create implicit animation -- e.g. set the position of a layer, and it'll animate to the new spot. (But if you assign a value to position, you want that to be the value you read back immediately thereafter, even if the animation is still in progress.)
When you use addAnimation:forKey:, you're affecting the presentation, not the model. So, during the animation, you see the Player layer moving, but that layer is "really" still where you left it. As soon as the animation ends, it's removed from the layer, so the presentation matches the model again -- you see it at its original position.
If you want the model position to change as well, you need to change it separately:
self.player.layer.position = CGPointMake(self.player.layer.position.x, target1.y);
You can either update the model immediately after adding the animation (you'll see the change after the animation completes), or use a completion block to schedule it at the end of the animation. There are subtleties to consider for either approach.
Often, though, if you just want to animate a specific change like that, especially for the main layer of a UIView, it's simpler to use the implicit animation API on UIView:
[UIView animateWithDuration:0.1 animations: ^{
self.player.center = CGPointMake(self.player.center.x, target1.y);
}];
This will both update the model value and create and run an animation for moving from the current position to the target position.
BTW: It helps to follow Cocoa code style conventions: use initial caps only for class names or other type names, and lowercase for variable/property/method names.
I'm using this to move my image:
meteorDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(moveImage:)];
Here is my moveImage: method:
-(void) moveImage:(CADisplayLink *)sender{
CGPoint img;
imgLocation = imgImageView.center;
if (img < 100) {
img.y++;
}
else{
imgLocation.y = 0;
}
imgImageView.center = imgLocation;
}
This updates the location of my image on screen no matter what. Why does setNeedsDisplay have no effect here? What is the point of setNeedsDisplay?
I assume imgImageView is an instance of UIImageView. If it's not an instance of UIImageView, edit your question to tell us what it is.
Changing the center property of a view never requires your app to redraw the view. The display server (which is a separate process named backboardd in iOS 6 and springboard in earlier versions) has a copy of your view's pixel data already. When you change a view's center, the view just informs the display server of its new position, and the display server copies the view's pixel data to the appropriate parts of the screen.
Thus changing the center property of a view never requires you to send setNeedsDisplay.
More specifically, UIImageView doesn't even implement drawRect:. Instead, UIImageView just sends its image's pixel data to the display server when you set its image. So even if you send setNeedsDisplay to a UIImageView, it still won't run any drawRect: method.
By the way, you can do this in your own UIView subclasses too, by setting self.layer.contents to a CGImageRef instead of implementing drawRect:. You will need to #import <QuartzCore/QuartzCore.h> to do this. Of course, UIImageView does other things too, like handling animated and resizable images.
When you set properties that affect the layout of an object, setNeedsDisplay will be called internally. You need to call it yourself for custom properties, etc, that don't automatically call it.
setNeedsDisplay only applies to the contents of a layer or view. I.e., if the contents of a view has changed and needs to be redrawn, you would use setNeedsDisplay to tell the view that it needs to be redrawn and that would trigger its drawRect method.
Transformations on a view, such as position, scale, and rotation, etc., are handled directly by the GPU and do not require the view to be redrawn; therefore, setNeedsDisplay does not apply when you're simply moving or transforming a view.
I never developed in Cocos2D. However, this animated app is not easy to make with regular UIView animation and CAAnimation.
I want a number of UIImageView's (from 1 to 30) to float around the screen with the certain path and I want them to be responsive for touch (they would do some animation when touched). I also need them to go back and forth the screen (new path would be calculated) when they are touched or reach the edge of screen. It's important to retrieve X and Y position of each element whenever needed.
Question is: what Cocos2D classes are best looking at (for a beginner) to make that happen? I've tried UIView animation and CAAnimation but I came across some difficulties so I have a feeling Cocos2D may bring better results. Thank you.
Yes, cocos2d makes it much easier. You want to create a CCSprite with the initWithFile: method. Example:
CCSprite *mySprite = [CCSprite initWithFile:#"fire.png"];
[self addChild:mySprite];
Where fire.png has been added to the project and self is the scene instance.