UISlider events not cancelled in iOS 8? - ios

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.

Related

Extremely slow framerate in SpriteKit game

I have a game built in SpriteKit that I occasionally work on. I started working on it when iOS 8 was still the latest version of iOS. Back then, it always ran at 60fps or almost (on a physical device). However, since iOS 9, and now on iOS 10, the game runs at a measly 12fps on my iPhone 5. It actually usually runs faster on the simulator (13 - 14fps), which is unheard of.
I did an analysis with Instruments and it appears that some of the slowness comes from the app doing two enumerations every frame. Here they are:
-(void)moveBackground
{
[self enumerateChildNodesWithName:#"stars" usingBlock:^(SKNode *node, BOOL *stop)
{
SKSpriteNode *bg = (SKSpriteNode *)node;
CGPoint bgVelocity = CGPointMake(0, -150); //The speed at which the background image will move
CGPoint amountToMove = CGPointMultiplyScalar (bgVelocity,0.08);
bg.position = CGPointAdd(bg.position,amountToMove);
if (bg.position.y <= 0)
{
bg.position = CGPointMake(bg.position.x, bg.position.y + bg.size.height/2);
}
}];
[self enumerateChildNodesWithName:#"opbg" usingBlock:^(SKNode *node, BOOL *stop)
{
SKSpriteNode *opbg = (SKSpriteNode *)node;
CGPoint bgVelocity = CGPointMake(0, -150); //The speed at which the background image will move
CGPoint amountToMove = CGPointMultiplyScalar (bgVelocity,0.08);
opbg.position = CGPointAdd(opbg.position,amountToMove);
if (opbg.position.y <= 0)
{
[self.opbg removeFromParent];
}
}];
}
These enumerations move the starting background until it is offscreen, then remove it from the parent, and cycle the regular background once that one is done. Is there a more efficient way of doing this? Additionally, is there another way of optimizing the app without losing any quality? My images are relatively large (background is 1080x3840), but I'm not sure if I can use smaller ones and then upscale them without losing quality.
Any help improving my framerate is appreciated, and I can show more code if needed.
Thanks
The biggest change between iOS 8 and 9 was the addition of a cameranode:
https://developer.apple.com/reference/spritekit/skcameranode
And an infamous amount of performance degradation.
There's a litany of Apple forum complaints about performance on iOS 9. There was no communication from Apple in a timely and satisfactory manner. Many SpriteKit users and potential users were lost as a result of the lack of meaningful fixes and explanations.
In your case, switching to use of the camera to do your parallaxes might regain some performance.
Here's some of the issues regarding performance being discussed, so you might see if anyone was doing something similar:
https://forums.developer.apple.com/thread/14487
https://forums.developer.apple.com/thread/30651

`touchesBegan:withEvent:` is delayed at left edge of screen

I'm experiencing an issue where the first call to touchesBegan:withEvent: on a UIView or UIViewController is delayed when you touch on the left edge of the screen. This seems to be a new issue with iOS 10, and only happens on devices with 3D Touch (iPhone 6s and newer). In fact, if you disable 3D Touch in General->Accessibility, the issue goes away.
However, the issue doesn't seem to happen when you use UIGestureRecognizers. My workaround at the moment is to create a UIGestureRecognizer subclass that overrides the touches* methods and forwards them to my old implementation.
Is this just a bug or is there a way to get rid of the delay?
try adding this to the viewdidappear method. this might fix the issue. it happened with me as well but i got this code from stack overflow that fixed my issue. hope it helps you too
let window = view.window!
let gr0 = window.gestureRecognizers![0] as UIGestureRecognizer
let gr1 = window.gestureRecognizers![1] as UIGestureRecognizer
gr0.delaysTouchesBegan = false
gr1.delaysTouchesBegan = false
Like danialias I'm working on a game. The solution I found (currently working, tested on an iPhone with 3D Touch enabled, where this was a real issue up to this point..) works for both games/apps:
It seems that UITapGestureRecognizer doesn't suffer from this delay, so simply add one to your view, and use it to handle taps.
In my game, I store touches and handle them on every interval update, so I overrode
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
and there I stored the UITouch instance and returned NO.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
[self.touches addObject:touch];
return NO;
}
Purteeek solution seems to work nicely in my case too. This is an objective-C implementation for SpriteKit:
- (void)didMoveToView:(SKView *)view {
UIGestureRecognizer* gr0 = view.window.gestureRecognizers[0];
UIGestureRecognizer* gr1 = view.window.gestureRecognizers[1];
gr0.delaysTouchesBegan = false;
gr1.delaysTouchesBegan = false;
}
This doesn't mess with other gesture recognizers, and the system 3D Touch still works fine. I wonder why this is not the default behavior.
in iOS 13.2. it seems that trick is not possible:
[Warning] Trying to set delaysTouchesBegan to NO on a system gate gesture
recognizer - this is unsupported and will have undesired side effects
Looks like the only solution is disable 3D touch in Settings.
This works for me
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let window = view.window,
let recognizers = window.gestureRecognizers {
recognizers.forEach { r in
r.delaysTouchesBegan = false
r.cancelsTouchesInView = false
r.isEnabled = false
}
}
}

Touch not working in mainscene

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;
}

Core Plot scrolling interruption

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;
}

Cocos2d handling touch with multiple layers

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.

Resources