I'm using CPTScatterPlot to display data, also I have data labels above all plot symbols. I need to detect user touches and using - plot:dataLabelWasSelectedAtRecordIndex: to detect it. But if I click on the data label and try to scroll, scrolling is not working.
Is there any way to detect if user touches and yet not interrupt scrolling?
We've made some changes to the event handling and related delegate methods on the release-2.0 branch of the Core Plot code. Get the latest code from GitHub and switch to the release-2.0 branch to get the updated code. Note that this update requires the Accelerate framework and raises the minimum system requirements from the 1.x releases on the master branch.
I fix this problem in my code, using graph plot space delegate instead of CPTPlot delegate.
-(BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDownEvent:(CPTNativeEvent *)event atPoint:(CGPoint)point {
_lastPlotSpaceDownPoint=point;
return YES;
}
-(BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceUpEvent:(CPTNativeEvent *)event atPoint:(CGPoint)point {
CGFloat distance = hypotf((_lastPlotSpaceDownPoint.x - point.x), (_lastPlotSpaceDownPoint.y - point.y));
if (abs(distance) < 5) {
NSUInteger recordIndex=[self.linePlot indexOfVisiblePointClosestToPlotAreaPoint:point];
// your code here
}
return YES;
}
Related
iOS 9.0 added new property force on UITouch class. For new iPhones (6S) this enables to get value of user's finger presure.
The value of force property seems to be set between 0 and 6.66667.
Also iOS 9 added peek and pop feature - when a user aplies certain finger presure level on some controls, programed action is triggered.
My question is: What are these presure levels (for peek and pop) in terms of value of the force property of UITouch?
In another words, to what value do I need to set threshold for the force property for the user to be required to apply the same finger pressure level as when they use 'peek' (or pop) feature?
You can try to observe the force value by using the below function. It seems that for a peek the force value is 1.33 (normalized force = 0.20) and for a pop the force value is 5.0 (normalized force = 0.75.) At the peek force level, it triggers the UIViewControllerPreviewingDelegate method (UIViewController *)previewingContext:(id<UIViewControllerPreviewing>)previewingContext viewControllerForLocation:(CGPoint)location for peek.
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGFloat maximumPossibleForce = touch.maximumPossibleForce;
CGFloat force = touch.force;
NSLog(#"***** force value : %f", force);
CGFloat normalizedForce = force/maximumPossibleForce;
NSLog(#"Normalized force : %f", normalizedForce);
if (normalizedForce > 0.75)
{
// Pop
}
else if (normalizedForce > 0.20)
{
// Peek
}
}
By default you don't need to set force threshold for pop and peek operations, values are predefined in the framework. You can refer this link https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/Adopting3DTouchOniPhone/3DTouchAPIs.html
on how to implement peek and pop in your view controller. If you want to customise when to peek and pop then you should be checking force value which is not recommended. As per apple documentation
The force of the touch, where a value of 1.0 represents the force of an average touch (predetermined by the system, not user-specific).
Peek is basically for showing preview for which you will have to implemented various things. You can get sample code here https://developer.apple.com/library/ios/samplecode/ViewControllerPreviews/Introduction/Intro.html. Pop is normal action.
I am unable to figure out the why. could someone please say where the bug is?
Xcode 6.1.1
Cocos2d 3.1.0
I used a break point to see if the touch method is getting called or not.
Its never being called when i test and touch in device.
I used this line blow in main method
self.userInteractionEnabled = YES;
also super on enter is called below main
Make certain your object instance has a content size set in onEnter. Touch may be enabled, but with (0,0) content size, they are not being dispatched. Also, you must have a touchBegan method in your code. The following lines are pretty much 'boilerplate' for my UI bearing classes:
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
}
- (void)onEnter {
[super onEnter];
self.userInteractionEnabled = YES;
// replace following two lines by whatever works for your
// specifics, but make certain that you have them and that
// the object's geometry falls in the display screen.
// setPositionInPointsW is in my own CCNode category, not in cocos2d
[self setPositionInPointsW:self.viewPort.origin];
self.contentSizeInPoints = self.viewPort.size;
}
I'm fairly new to Cocos2d and I'm "really" struggling with sizing a CCTableView. What I'm referring to is how can its height be controlled so that I have space at the top and the bottom.
I have my table view functioning and I'm trying to visually wrap the top and bottom with a sprite to provide the look and feel of a frame. But when I touch the table view to scroll
It up or down, it occupies most of the screen vertically. I started messing with the contentSize, thinking this would alter where each cell appears and disappears from the top and bottom. But this had no effect.
Having had s Google about, I'm reading that I have to use a Clipping Node with a Stencil? Coming from UIkit, this seems like a bit of a faff. Would anyone like to hazard at a tutorial, bit of code with a simple CCTableView representation, sat in the middle of the scene where each cell appears and dissapears under a content frame?
You should take a look at CCCropNode in the cocos2d extensions package:
https://github.com/cocos2d/cocos2d-swift-ext/tree/master/Extensions/CCCropNode
the CCCropNode uses a glScissor to clip the drawing to a specified area: Much simpler than the CCClippingNode (which uses the stencil buffer).
Also this forum post might help:
http://forum.cocos2d-swift.org/t/ccclippingnode-woes/14752
You'll still have issues with touches outside of the target region hitting into your scroll / table area (and I've found that the CCCropNode touch clipping doesn't work - need to investigate that as it is causing me problems).
I faced the same issue and I managed to solve the touches outside the table region by adding code to CCScrollView.m as following:-
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(CCTouch *)touch
{
// Added by Season
//*************************************************************
CGPoint lcgpTouch = [touch locationInWorld];
if (!(lcgpTouch.x >= [self position].x && lcgpTouch.x <= [self position].x + [self contentSize].width &&
lcgpTouch.y >= [self position].y && lcgpTouch.y <= [self position].y + [self contentSize].height)) {
return NO;
}
//*************************************************************
if (!_contentNode) return NO;
if (!self.visible) return NO;
if (!self.userInteractionEnabled) return NO;
And you are required to setContentSize to CCTableView in order to have this piece of code get the size correctly.
Additional info:
I have some CCSprite on top of the outside target region that suppose to response to touchBegan failed after the above coding, then I changed all CCSprite to CCButton and it works like a charm. You do not need to do what I did here, you can modify coding to allow CCSprite to response to touchBegan easily.
Hope this coding above help you all.
We're currently testing our apps (iOS 7 apps) that are in the store on an iOS 8 device. We noticed a big performance problem with UISliders.
If we pull the slider fast from left to right several times, the slider will not immediately go to our last position. It will perform every move we have done with our finger. It seems as if the intermediate touch events are not properly cancelled.
On iOS 7, the slider performance is fine.
Has anyone experienced the same problem? Is this a known problem? Is there a solution to this?
I'm running into the same problem with SmartGo Kifu; filed as rdar://18245085. Here's my current workaround: use a class derived from UISlider, and override continueTrackingWithTouch:withEvent: to filter out events that are coming in too fast.
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
if (self.tracking)
{
const NSTimeInterval TOO_FAST_TO_HANDLE = 0.1;
if ([event timestamp] - previousTimestamp >= TOO_FAST_TO_HANDLE)
{
previousTimestamp = [event timestamp];
return [super continueTrackingWithTouch:touch withEvent:event];
}
}
return self.tracking;
}
Still hope this will get fixed for the final release of iOS 8, or somebody has a better way to handle this.
I've been busy for a few days trying to figure out how to handle touch in my Cocos2d project. The situation is a bit different as normal. I have a few different game layers that have items on it that I need to control with touch:
ControlLayer: Holds the game controls
(movement, action button). This layer is on top.
GameplayLayer: Holds the game objects
(CCSprites). This layer is directly beneath the ControlLayer.
Now my touches work fine in the ControlLayer, I can move my playable character around and make him jump and do other silly stuff. Yet I cannot grasp how to implement the touches to some of my CCSprites.
The information I've gathered so far makes me think I need get all my touch input from the control layer. Then I somehow need to 'cascade' the touch information to the GameplayLayer so I can handle the input there. Another option would be for me to get the CGRect information from my sprites by somehow creating an array with pointers to the objects that should be touchable. I should be able to use that information in the ControlLayer to check for each item in that list if the item was touched.
What is the best option to do this, and how do I implement this? I'm kind of new to programming with cocoa and Objective C so I'm not really sure what the best option is for this language and how to access the sprites CGRect information ([mySpriteName boundingBox]) in another class then the layer it is rendered in.
At the moment the only way I'm sure to get it to work is create duplicate CGRects for each CCSprite position and so I can check them, but I know this is not the right way to do it.
What I have so far (to test) is this:
ControlLayer.m
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView: [touch view]];
CGRect rect = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
//Tried some stuff here to get see if I could get a sprite by tagname so I could use it's bounding box but that didn't work
// Check for touch with specific location
if (CGRectContainsPoint([tree boundingBox], location)) {
CCLOG(#"CGRect contains the location, touched!");
}
CCLOG(#"Layer touched at %#", NSStringFromCGPoint(location));
}
Thanks in advance for helping me!
The easiest and simplest way to solve your problem, IMO, is by using ccTouchBegan/Moved/Ended instead of ccTouchesBegan/Moved/Ended. Meaning, you are handling a single touch at a particular moment so you avoid getting confuses over multiple touches, plus the most important feature of ccTouchBegan is a CCLayer can 'consume' the touch and stop it from propagating to the next layers. More explanation after code samples below.
Here are steps to do it. Implement these sets of methods in all CCLayer subclasses that should handle touch events:
First, register with CCTouchDispatcher:
- (void)registerWithTouchDispatcher {
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
Next, implement ccTouchBegan, example below is from a game I've created (some part omitted of course):
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
if (scene.state != lvlPlaying) {
// don't accept touch if not playing
return NO;
}
CGPoint location = [self convertTouchToNodeSpace:touch];
if (scene.mode == modePlaying && !firstTouch) {
if (CGRectContainsPoint(snb_putt.sprite.boundingBox, location)) {
touchOnPutt = touch.timestamp;
// do stuff
// return YES to consume the touch
return YES;
}
}
// default to not consume touch
return NO;
}
And finally implement ccTouchMoved and ccTouchEnded like the ccTouches* counterparts, except that they handle single touch instead of touches. The touch that is passed to these methods is restricted to the one that is consumed in ccTouchBegan so no need to do validation in these two methods.
Basically this is how it works. A touch event is passed by CCScene to each of its CCLayers one by one based on the z-ordering (i.e starts from the top layer to the bottom layer), until any of the layers consume the touch. So if a layer at the top (e.g. control layer) consume the touch, the touch won't be propagated to the next layer (e.g. object layer). This way each layer only has to worry about itself to decide whether to consume the touch or not. If it decides that the touch cannot be used, then it just has to not consume the touch (return NO from ccTouchBegan) and the touch will automatically propagate down the layers.
Hope this helps.