0

I've implemented a really simple custom UISwitch that uses touches events like:

- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event

I can use this control in my views without problem. The Control simulates a UISwitch, so it can change value through a drag or through a tap.

My problem is that I can't let this Control work inside a Cell of a UITableView. Only the single tap seems to work (note that I'm not using gesture... but the events that I've previously listed) but I can't "swipe" the switch handle.

I instantiate the controller within the tableView:cellForRowAtIndexPath method adding the control as subview of the cell contentView:

[cell.contentView addSubview:customSwitch];

I suppose that it is something related with the fact that UITableView is a UIScrollView and I think that touches events get somehow "stolen" by it.

// EDIT --------------------

Here is the code related with the Touch events.

- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
    [super beginTrackingWithTouch:touch withEvent:event];

    self.dragged = NO;

    return YES;
}

- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
    if (!self.enabled) {
        return NO;
    }

    [super continueTrackingWithTouch:touch withEvent:event];
    self.dragged = YES;
    CGPoint touchPoint = [touch locationInView:self];

    float centerPoint = FIXED_WIDTH / 2.0;

    [self centerOn:(touchPoint.x > centerPoint) animated:YES completion:nil];

    return YES;
}

- (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
{
    [super endTrackingWithTouch:touch withEvent:event];

    CGPoint touchPoint = [touch locationInView:self];

    BOOL currentOn = self.on;
    BOOL nextOn;

    if (self.dragged) {
        float centerPoint = FIXED_WIDTH / 2.0;
        nextOn = (touchPoint.x > centerPoint);

        [self setOn:nextOn animated:NO];
    }else{
        nextOn = !self.on;
        [self setOn:nextOn animated:YES];
    }
    self.dragged = NO;

    if (currentOn != nextOn) {
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }
}

How can I make the control work inside a Cell without interfering with the UIScrollView/UITableView?

MatterGoal
  • 16,038
  • 19
  • 109
  • 186
  • Have you considered using tap and pan gestures? – Wain Jan 02 '15 at 17:42
  • @Wain I prefer to use the Touch events. Does it make difference in this case? – MatterGoal Jan 02 '15 at 17:45
  • Look through other questions like this one http://stackoverflow.com/questions/9350041/uicontrol-not-receiving-touches – Wain Jan 02 '15 at 19:51
  • @Wain I have this problem only if I put the Switch inside a UITableViewCell. – MatterGoal Jan 05 '15 at 10:31
  • @gabbler I've added all the code for the touch events. But I just want to mention that this control works has problem only when used inside a UITableViewCell (so I suppose the problem is related with the ScrollView) – MatterGoal Jan 06 '15 at 11:15
  • Is Switch a subview of UIControl or UISwitch. No `centerOn` method implementation is found – gabbler Jan 06 '15 at 11:25
  • Try to set your custom switch as cell's accessory view – Henit Nathwani Jan 06 '15 at 11:45
  • @MatterGoal: Have you checked the UISwitch frame, or any other views are on the switch ? – Vineesh TP Jan 06 '15 at 11:45
  • @MatterGoal Have you looked into `shouldRespondSimultaneously` and friends on `UIGestureRecognizerDelegate`? You might need to implement that in your switch to make sure it triggers together with the scroll https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIGestureRecognizerDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIGestureRecognizerDelegate/gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: – Rick Jan 06 '15 at 15:26
  • I have also created a substitute for `UISwitch`, and I found it a lot easier to implement a single `UITapGestureRecognizer` than to track touches. I didn't even have to implement a pan recognizer, and it still works with both taps and swipes. I have put this control into `UITableViewCell` instances without issue. I would encourage you to at least give this method a try. It can't take more than an hour to fork the control and reimplement it with gesture recognizers. – mbm29414 Jan 07 '15 at 13:36
  • Also, I'm concerned that you're instantiating this control in `tableView:cellForRowAtIndexPath:`. This is **NOT** where this should occur. You should do this in `init` or `initWithCoder:` in the `UITableViewCell` subclass. If not, you run the risk of adding a new instance of this control every time your `UITableView` recycles a cell. – mbm29414 Jan 07 '15 at 13:36
  • @mbm29414 You are right but at the moment I've just a few rows and the cells never need to get recreated. – MatterGoal Jan 08 '15 at 18:26
  • @Rick I'm not using gestures. – MatterGoal Jan 08 '15 at 18:27
  • @VineeshTP yes, all the frames/Sizes/Bounds/ are correct. – MatterGoal Jan 08 '15 at 18:27
  • 3
    @MatterGoal That is not a good reason for poor design. If, for some reason, you suddenly need to have more cells than the screen can display, you have to completely refactor this code. Much easier to avoid that (potentially) elusive bug by getting the architecture right at the outset. – mbm29414 Jan 08 '15 at 19:42
  • I totally agree with you. This is just a prototype app to test this component... I'll never write something like that in a "real" app. Thank you to be so rigid. – MatterGoal Jan 08 '15 at 20:53
  • @MatterGoal: Is there any view overlap on the switch / if you are accessing with tag, check the tags are correct. – Vineesh TP Jan 09 '15 at 03:44
  • @VineeshTP there aren't other views above the cell – MatterGoal Jan 09 '15 at 10:32
  • 2
    you didn't share any code from tableviewCell, please share code in tableViewCellForIndexPath – Vineesh TP Jan 09 '15 at 11:52

2 Answers2

1

You need to implement your custom switch using gestures instead of trying to directly process events.

The gesture recognizers work behind the scenes to coordinate their event processing which, for example, is why a UIScrollView can work inside of another UIScrollView. You need that coordination for the UITableView (really a UIScrollView) to properly handle swipe gestures within its content.

Search the web for Hardy Macia's UICustomSwitch sample code for a good example of matching the UISwitch behaviors in a custom control.

John Stephen
  • 7,625
  • 2
  • 31
  • 45
0

Did you miss the touchesBegan: and touchesMoved:?

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
    [super touchesBegan:touches withEvent:event];

    // Get the only touch (multipleTouchEnabled is NO)
    UITouch* touch = [touches anyObject];

    // Track the touch
    [self beginTrackingWithTouch:touch withEvent:event];
}

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    [super touchesMoved:touches withEvent:event];

    // Get the only touch (multipleTouchEnabled is NO)
    UITouch* touch = [touches anyObject];

    // Track the touch
    [self continueTrackingWithTouch:touch withEvent:event];
}
instaable
  • 3,449
  • 2
  • 24
  • 31
  • 1
    To the Down-voter: Care to explain why the down-vote? – instaable Jan 09 '15 at 07:46
  • Are you sure that functions are needed? I can make the switch work with no problem outside the cell. – MatterGoal Jan 09 '15 at 10:33
  • Also try implementing `sendActionsForControlEvents:` in `beginTrackingWithTouch` and `continueTrackingWithTouch`. To be specific: `[self sendActionsForControlEvents: UIControlEventTouchDown];` and `[self sendActionsForControlEvents: UIControlEventTouchDragInside]` respectively. – instaable Jan 09 '15 at 13:29
  • 1
    @MatterGoal I did read it. Maybe you overlooked my comment above. Try adding `[self sendActionsForControlEvents: UIControlEventTouchDown];` and `[self sendActionsForControlEvents: UIControlEventTouchDragInside]` in **YOUR** beginTracking and continueTracking methods. I implemented these in my project and it worked perfectly for me. – instaable Jan 10 '15 at 15:47
  • Sorry I've totally overlooked your comment – MatterGoal Jan 11 '15 at 09:24