This website describes what I want to achieve under the 'Handling a Complex Multitouch Sequence' section. Unfortunately, it is shown in Objective-C. I can translate most of it into Swift, but when it comes to a few lines, like the lines with CFDictionary, I honestly have no idea what's going on.
If somebody could help me understand/translate listings 3-4 and 3-5 or suggest another method for handling multi-touch events, I would be very grateful. I have left '???' where I am not sure.
Here is the Obj-C (storing touch initial location):
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self cacheBeginPointForTouches:touches];
}
- (void)cacheBeginPointForTouches:(NSSet *)touches {
if ([touches count] > 0) {
for (UITouch *touch in touches) {
CGPoint *point = (CGPoint *)CFDictionaryGetValue(touchBeginPoints, touch);
if (point == NULL) {
point = (CGPoint *)malloc(sizeof(CGPoint));
CFDictionarySetValue(touchBeginPoints, touch, point);
}
*point = [touch locationInView:view.superview];
}
}
}
Here is retrieving the initial location:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGAffineTransform newTransform = [self incrementalTransformWithTouches:touches];
}
- (CGAffineTransform)incrementalTransformWithTouches:(NSSet *)touches {
NSArray *sortedTouches = [[touches allObjects] sortedArrayUsingSelector:#selector(compareAddress:)];
// Other code here
CGAffineTransform transform = CGAffineTransformIdentity;
UITouch *touch1 = [sortedTouches objectAtIndex:0];
UITouch *touch2 = [sortedTouches objectAtIndex:1];
CGPoint beginPoint1 = *(CGPoint *)CFDictionaryGetValue(touchBeginPoints, touch1);
CGPoint currentPoint1 = [touch1 locationInView:view.superview];
CGPoint beginPoint2 = *(CGPoint *)CFDictionaryGetValue(touchBeginPoints, touch2);
CGPoint currentPoint2 = [touch2 locationInView:view.superview];
// Compute the affine transform
return transform;
}
Here's what I've managed:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
cacheBeginPoint(touches: touches)
}
func cacheBeginPoint(touches: Set<UITouch>) {
if touches.count > 0 {
for touch: UITouch in touches {
var point: CGPoint? = CFDictionaryGetValue(???)
if point == nil {
point = ???
CFDictionarySetValue(???)
}
point = touch.location(in: view?.superview)
}
}
}
and:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
var newTransform: CGAffineTransform = incrementalTransform(touches: touches)
}
func incrementalTransform(touches: Set<UITouch>) -> CGAffineTransform {
var sortedTouches: NSArray = ???
var transform = CGAffineTransform.identity
var touch1: UITouch? = sortedTouches[0]
var touch2: UITouch? = sortedTouches[1]
var beginPoint1: = CFDictionaryGetValue(???)
var currentPoint1: = touch1?.location(in: view?.superview)
var beginPoint2: = CFDictionaryGetValue(???)
var currentPoint2: = touch2?.location(in: view?.superview)
return transform
}
Related
I am currently developing an iOS app using swift. I used override to write my own tocuhesBegan and tochesEnded functions. Now when I use self.button.
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
for touch in touches {
var tap = touch as? UITouch
var touchPoint: CGPoint = tap!.locationInView(self)
self.touchDown(touchPoint)
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
super.touchesEnded(touches, withEvent: event)
self.releaseTouch()
}
override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
super.touchesCancelled(touches, withEvent: event)
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
super.touchesMoved(touches, withEvent: event)
}
func touchDown(point: CGPoint){
if rippleEffect == true {
touchView.frame = touchViewInitFrame
self.addSubview(touchView)
var touchViewFrameXPosition: CGFloat = point.x - (frame.size.width) / 2
println("X position = \(touchViewFrameXPosition)")
var touchViewFrameYPosition: CGFloat = -(self.frame.size.width - self.frame.size.height) / 2
touchViewFrame = CGRectMake(touchViewFrameXPosition, touchViewFrameYPosition, frame.size.width, frame.size.width)
UIView.animateWithDuration(0.25, delay: 0.0, options: .CurveEaseInOut, animations: {
self.touchView.frame = self.touchViewFrame
}, completion: nil)
}
}
func releaseTouch(){
if rippleEffect == true {
UIView.animateWithDuration(0.0, delay: 0.0, options: .CurveEaseInOut, animations: {
self.touchView.removeFromSuperview()
}, completion: nil)
}
}
it doesn't go to the function I specified in the selector. Is anyone else having this issue or does anyone know what's going on?
Here is the code that I used where I am having the issue. It is a subclass of UIButton.
If you override any of the touches methods, you are supposed to override all four and call super.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
//your code
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesEnded:touches withEvent:event];
//your code
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesMoved:touches withEvent:event];
//your code
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesCancelled:touches withEvent:event];
//your code
}
You probably have two different handles for your view and your button, to prove that try to touch anywhere else in the screen and your touch will be detected, however while pressing the button the touch is handle and never pass to the other handlers. You have to options, intercept all messages and handle if it should be passed or not for the original handles before/after you do what you need to do, or implement the button handler and make it pass the message up the chain:
From the apple documentation:
Overriding hit-testing ensures that the superview receives all touches
because, by setting itself as the hit-test view, the superview
intercepts and receives touches that are normally passed to the
subview first. If a superview does not override hitTest:withEvent:,
touch events are associated with the subviews where they first
occurred and are never sent to the superview.
I have a simple piece of code in which i have overridden touchesBegan,Ended,Cancelled(empty block) and touchesMoved.
If i click ( i'm testing on Desktop PC ) with the mouse touchesBegan it's called, but touchesEnded is called only when i move finger for a while. That makes impossible to recognize a single tap or a drag of the finger, and handle them differently.
I don't understand if this is an emulator problem or i am misunderstanding the whole process. Did you have the same problem?
I have a simple solution for my application, like check a "first move" variable in touchesBegan, but this is a pure technical question.
Thank you in advance.
This is all i use, apart from drawRect that it's not important.
I guess it's not a problem in my code.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.allObjects[0] as UITouch
let touchPoint = touch.locationInView(self)
println("touchesBegan")
if (Draw!){
path.moveToPoint(touchPoint)
path.addLineToPoint(touchPoint)
self.setNeedsDisplay()
}
}
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.allObjects[0] as UITouch
let touchPoint = touch.locationInView(self)
println("touchesMoved")
if (Draw!){
path.addLineToPoint(touchPoint)
self.setNeedsDisplay()
}
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.allObjects[0] as UITouch
let touchPoint = touch.locationInView(self)
println("touchesEnded")
if (Draw!){
path.addLineToPoint(touchPoint
path = UIBezierPath() // Create new path for next line
self.setNeedsDisplay()
}
}
I am quite sure this is an emulator problem.
Please see the code below. It works like a charm at my end.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
didMove = false;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
didMove = true;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (didMove == true)
{
//finger was moved
NSLog(#"finger was moved");
}
else
{
//it's a tap
NSLog(#"finger not moved it's a tap");
}
}
While we are at it, can I bring your attention to UIGestureRecognizers? Try to use them since they make life a breeze.
I have been trying to make a simple app where when you tap a circle, it will disappear, and a new one will come up somewhere else.
Here is all my code, edited, but it still does not work.
#import "GameScene.h"
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
[self LabelShow];
}
int Count = 0;
int CountCheck = 0;
-(void) LabelShow {
//Nothing yet
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
while (CountCheck == Count) {
int FNum1 = 350;
int TNum1 = 650;
int newx = (arc4random()%(TNum1-FNum1))+FNum1;
NSLog(#"RandomXCoord: %i", newx);
int FNum2 = 100;
int TNum2 = 700;
int newy = (arc4random()%(TNum2-FNum2))+FNum2;
NSLog(#"RandomYCoord: %i", newy);
//Location to newx and newy
CGPoint location = CGPointMake(newx, newy);
SKSpriteNode *Circle = [SKSpriteNode spriteNodeWithImageNamed:#"Circle.png"];
Circle.xScale = 0.2;
Circle.yScale = 0.2;
Circle.position = location;
//Shows chircle
[self addChild:Circle];
CountCheck++;
for (UITouch *touch in touches)
{
CGPoint locationTouch = [touch locationInNode:self];
if ([Circle containsPoint:locationTouch]) {
NSLog(#"Success!");
[Circle runAction:[SKAction fadeOutWithDuration:0]];
Count++;
}
}
}
}
#end
As I stated, I have the code that puts the circle on the screen in another method. But whenever I run this code (click the circle), the if statement at the bottom does not get executed.
I tried all the things in this thread:
Can't tap SKSpriteNode - no touch detected ios
, but I can't get it to work still, and have changed the code back to the original.
Could anyone tell me how I could be able to get the if statement executed when you tap the SKSpriteNode?
Thank you for reading this question.
Edit I changed the code, but it still doesn't work
Add the following above your Circle.xScale = 0.2; statement
Circle.name = #"Circle";
and replace your for-loop with the following
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"Circle"]) {
NSLog(#"Success!");
[node removeFromParent];
++Count;
}
}
What you need is containsPoint method.
Here is the touchesBeganWithEvent: method you are looking for:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
if ([circle containsPoint:location] {
// executed when you touch a circle
}
}
}
Note that you can add more statements and conditions.
I created a game for myself, and I had to do the same thing for a character in it. I subclassed SKNode, and added the character details (SKSpriteNode, name, SKAction, etc) in the subclassed model. Then I added the UITouchBegin method to the subclass, so my character would listen to touches.
It is a lot easier to use subclassing, as you can leave all the legwork to the subclass, and focus on the rest of your game. Adding this code to your subclass will take care of it all:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self removeFromParent];
}
You can even set your scene as your character class's delegate, and as soon as the character is removed from parentview, you can create a new one.
If you get stuck on the subclass issues, this answer really helped me out. Let me know if any questions and good luck :)
The other responses seem to detect touches on a SKSpriteNode, not a tap. This code detects tap events.
Edit TapMaxDelta to change how much movement is allowed before cancelling the tap gesture.
class TapNode : SKSpriteNode {
// Tap Vars
var firstPoint : CGPoint?
var TapMaxDelta = CGFloat(10)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init() {
let texture = SKTexture(imageNamed: "Test.png")
super.init(texture: texture, color: UIColor.clear, size: texture.size())
isUserInteractionEnabled = true
}
// ================================================================================================
// Touch Functions
// ================================================================================================
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first {
firstPoint = firstTouch.location(in: self)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let firstTouch = touches.first, let firstPoint = firstPoint {
let curPoint = firstTouch.location(in: self)
if abs(curPoint.x - firstPoint.x) <= TapMaxDelta && abs(curPoint.y - firstPoint.y) <= TapMaxDelta {
print("tap yo")
}
}
}
}
I am having trouble creating a function "touchesBegan" and then creating a UIPoint and UITouch constant or variable that holds an x and y coordinate. I have the exact code I want in Objective-C but I do not know what it's equivalent is in Swift. Here is the Objective-C code which I want to basically translate into Swift code...
NOTE: This is a Single View Application, NOT a game... Thanks in advance.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.view];
if (point.x < 160) {
var = 10;
}
else{
var = 20;
}
}
As of Swift 1.2, use this
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)
{
var touch = touches.first as! UITouch
var point = touch.locationInView(self)
if point.x < 160 {
var = 10;
}
else{
var = 20;
}
}
Where's the problem?
var touch = touches.anyObject() as! UITouch
var point = touch.locationInView(self.view)
if point.x < 160 {
var variableName = 10;
}
else{
var variableName = 20;
}
Swift 1.2 changed the syntax for touchesBegan. See the UIResponder Reference.
func touchesBegan(_ touches: Set<NSObject>, withEvent event: UIEvent)
And don't forget to reference the super.
super.touchesBegan(touches, withEvent: event)
Here is an edited example of implementing this code from techotopia.com which might give you more information on the other three touches functions.
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch = touches.anyObject() as! UITouch
var point = touch.locationInView(self.view)
// Insert if statements
super.touchesBegan(touches, withEvent: event)
}
I have a UISegmentedControl with 4 segments. When it is selected, it should maintain the selected state. When the same segment is clicked again, it should deselect itself.
How to achieve this?
Since UISegmentedControl only sends an action if a not selected segment is selected, you have to subclass UISegmentedControl to make a tiny change in its touch handling. I use this class:
#implementation MBSegmentedControl
// this sends a value changed event even if we reselect the currently selected segment
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSInteger current = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
if (current == self.selectedSegmentIndex) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
#end
Now you will get UIControlEventValueChanged events even if the segment is already selected. Simply save the current index in a variable and compare it in the action. If the two indexes match you have to unselect the touched segment.
// _selectedSegmentIndex is an instance variable of the view controller
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_selectedSegmentIndex = self.segment.selectedSegmentIndex;
}
- (IBAction)segmentChanged:(UISegmentedControl *)sender {
if (sender.selectedSegmentIndex == _selectedSegmentIndex) {
NSLog(#"Segment %d deselected", sender.selectedSegmentIndex);
sender.selectedSegmentIndex = UISegmentedControlNoSegment;
_selectedSegmentIndex = UISegmentedControlNoSegment;
}
else {
NSLog(#"Segment %d selected", sender.selectedSegmentIndex);
_selectedSegmentIndex = sender.selectedSegmentIndex;
}
}
iOS 7 changed how touches are handled for UISegmentedControl. The selectedSegmentIndex is now changed during touchesEnded:.
So the updated Subclass should look like this:
#implementation MBSegmentedControl
+ (BOOL)isIOS7 {
static BOOL isIOS7 = NO;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSInteger deviceSystemMajorVersion = [[[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:#"."] objectAtIndex:0] integerValue];
if (deviceSystemMajorVersion >= 7) {
isIOS7 = YES;
}
else {
isIOS7 = NO;
}
});
return isIOS7;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
if (![[self class] isIOS7]) {
// before iOS7 the segment is selected in touchesBegan
if (previousSelectedSegmentIndex == self.selectedSegmentIndex) {
// if the selectedSegmentIndex before the selection process is equal to the selectedSegmentIndex
// after the selection process the superclass won't send a UIControlEventValueChanged event.
// So we have to do this ourselves.
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex;
[super touchesEnded:touches withEvent:event];
if ([[self class] isIOS7]) {
// on iOS7 the segment is selected in touchesEnded
if (previousSelectedSegmentIndex == self.selectedSegmentIndex) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
}
#end
Swift 2.2 version, fixed the problem Grzegorz noticed.
class ReselectableSegmentedControl: UISegmentedControl {
#IBInspectable var allowReselection: Bool = true
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let previousSelectedSegmentIndex = self.selectedSegmentIndex
super.touchesEnded(touches, withEvent: event)
if allowReselection && previousSelectedSegmentIndex == self.selectedSegmentIndex {
if let touch = touches.first {
let touchLocation = touch.locationInView(self)
if CGRectContainsPoint(bounds, touchLocation) {
self.sendActionsForControlEvents(.ValueChanged)
}
}
}
}
}
Swift 3.0 changes the fix for this to look like the following:
class MyDeselectableSegmentedControl: UISegmentedControl {
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let previousIndex = selectedSegmentIndex
super.touchesEnded(touches, with: event)
if previousIndex == selectedSegmentIndex {
let touchLocation = touches.first!.location(in: self)
if bounds.contains(touchLocation) {
sendActions(for: .valueChanged)
}
}
}
}
Here is a fix for a problem that when you try to cancel selection by starting tap on UISegmentControl and later you finish touch outside - it still does deselection.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint locationPoint = [[touches anyObject] locationInView:self];
CGPoint viewPoint = [self convertPoint:locationPoint fromView:self];
if ([self pointInside:viewPoint withEvent:event]) {
int oldValue = self.selectedSegmentIndex;
[super touchesEnded:touches withEvent:event];
if (oldValue == self.selectedSegmentIndex)
{
[super setSelectedSegmentIndex:UISegmentedControlNoSegment];
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
}
You can do it with the following (thank you Grzegorz's answer and Matthias's answer):
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex;
[super touchesEnded:touches withEvent:event];
CGPoint locationPoint = [[touches anyObject] locationInView:self];
CGPoint viewPoint = [self convertPoint:locationPoint fromView:self];
if ([self pointInside:viewPoint withEvent:event] && previousSelectedSegmentIndex == self.selectedSegmentIndex) {
self.selectedSegmentIndex = UISegmentedControlNoSegment;
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
I have made an open-source (MIT Licensed) class STASegmentedControl (supports iOS 7+), that has this functionality baked in (and more).
In reference to the answer posted by #Matthias Bauch. I had to make little changes as per Swift 2.2 in Xcode 7.3:
class ReselectableSegmentedControl: UISegmentedControl {
#IBInspectable var allowReselection: Bool = true
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let previousSelectedSegmentIndex = self.selectedSegmentIndex
super.touchesEnded(touches, withEvent: event)
if allowReselection && previousSelectedSegmentIndex == self.selectedSegmentIndex {
if let touch = touches.first {
let touchLocation = touch.locationInView(self)
if CGRectContainsPoint(bounds, touchLocation) {
self.sendActionsForControlEvents(.ValueChanged)
}
}
}
}
}
swift 3.1 version posted by #Kushal Ashok
class ReselectableSegmentedControl: UISegmentedControl {
#IBInspectable var allowReselection: Bool = true
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let previousSelectedSegmentIndex = self.selectedSegmentIndex
super.touchesEnded(touches, with: event)
if allowReselection && previousSelectedSegmentIndex == self.selectedSegmentIndex {
if let touch = touches.first {
let touchLocation = touch.location(in: self)
if bounds.contains(touchLocation) {
self.sendActions(for: .valueChanged)
}
}
}
}
}
Here is a solution wich is independent from IOS Version. It's choosing the behaviour itself.
#interface CustomSegmentedControl : UISegmentedControl
#end
#implementation CustomSegmentedControl{
BOOL _touchBegan;
BOOL _reactOnTouchBegan;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
_touchBegan = YES;
NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex;
[super touchesBegan:touches withEvent:event];
if (_reactOnTouchBegan) {
// before iOS7 the segment is selected in touchesBegan
if (previousSelectedSegmentIndex == self.selectedSegmentIndex) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
_touchBegan = NO;
NSInteger previousSelectedSegmentIndex = self.selectedSegmentIndex;
[super touchesEnded:touches withEvent:event];
if (!_reactOnTouchBegan) {
CGPoint locationPoint = [[touches anyObject] locationInView:self];
CGPoint viewPoint = [self convertPoint:locationPoint fromView:self];
if ([self pointInside:viewPoint withEvent:event]) {
// on iOS7 the segment is selected in touchesEnded
if (previousSelectedSegmentIndex == self.selectedSegmentIndex) {
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
}
}
- (void)sendActionsForControlEvents:(UIControlEvents)controlEvents {
if(controlEvents == UIControlEventValueChanged){
_reactOnTouchBegan = _touchBegan;
}
[super sendActionsForControlEvents:controlEvents];
}
#end
Very helpful! Thank you! I wanted a little more control over the events for my project, so I adapted #Matthias's answer to send a custom "Value unchanged" event. I put an example on GitHub.
I also incorporated #Grzegorz's fix so it behaves properly if the user drags her finger outside the segmented control.
In reference to #Stunner, this is my contribution to achieve the goal. I changed something and added the property _previousSelectedSegmentIndex; in the #Stunner code the variable previousSelectedSegmentIndex was useless:
#implementation STASegmentedControl
{
NSInteger _previousSelectedSegmentIndex;
}
- (void)setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex
{
[super setSelectedSegmentIndex: selectedSegmentIndex];
_previousSelectedSegmentIndex = self.selectedSegmentIndex;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
CGPoint locationPoint = [[touches anyObject] locationInView:self];
CGPoint viewPoint = [self convertPoint:locationPoint fromView:self];
if (self.toggleableSegments) { // toggle selected segment on/off
if ([self pointInside:viewPoint withEvent:event] && _previousSelectedSegmentIndex == self.selectedSegmentIndex) {
self.selectedSegmentIndex = UISegmentedControlNoSegment;
[self sendActionsForControlEvents:UIControlEventValueChanged];
}
}
_previousSelectedSegmentIndex = self.selectedSegmentIndex;
}