1
votes

Selecting cells in a UITableView appears to work different on the iPad and the iPhone.


On the iPhone I see the following behaviour:

  • Tap a row and lift the finger up
    • The row is now selected
  • Tap another row, but do not lift the finger
    • The new row is now highlighted (looks like selected)
    • The old row stays selected
  • Move the finger to scroll (The finger is still on the iPhone from the previous step)
    • The new row loses its highlighting
    • The old row stays selected
  • Lift the finger
    • The old row stays selected


But on the iPad I observe this behaviour:

  • Tap a row and lift the finger up
    • The row is now selected
  • Tap another row, but do not lift the finger
    • The new row is now highlighted (looks like selected)
    • The old row loses its selection
  • Move the finger to scroll (The finger is still on the iPad from the previous step)
    • The new row loses its highlighting
    • The old row is still missing its selection
  • Lift the finger
    • The old row is selected again


What is the cleanest way to make the UITableView behave on the iPad like it does on the iPhone?

This is the minimal app to reproduce the problem. It is based on the "Empty Application" template of Xcode 5. It happens on both iOS 7 and 6.1.

ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UITableViewController

@end

ViewController.m

#import "ViewController.h"

@implementation ViewController

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 42;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    cell.textLabel.text = [@(indexPath.row) stringValue];

    return cell;
}

@end

AppDelegate.m

#import "AppDelegate.h"
#import "ViewController.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];

    ViewController *viewController = [[ViewController alloc] init];
    self.window.rootViewController = viewController;

    [self.window makeKeyAndVisible];
    return YES;
}

@end
1

1 Answers

3
votes

This happens because the UITableView deselects the selected row during touchesBegan. This only seems to happen on the iPad and not on the iPhone. Not sure why there is a difference.

In order to make the iPad version behave like the iPhone version, I ended up subclassing UITableView to override this behavior. I would love a better answer to this question, but here is my solution:

@interface CustomTableView : UITableView  

@property (nonatomic) BOOL inTouchSequence;    

@end

@implementation CustomTableView

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    self.inTouchSequence = YES;
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    self.inTouchSequence = NO;
    [super touchesEnded:touches withEvent:event];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    self.inTouchSequence = NO;
    [super touchesCancelled:touches withEvent:event];
}

@end

Then in my custom cells, I added a reference to the tableView and overrode the setSelected method to prevent the cells from being deselected by touchesBegan.

@interface CustomTableViewCell : UITableViewCell

@property (weak, nonatomic) CustomTableView* tableView;

@end

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    if (self.tableView.inTouchSequence) {
        // Skip deselection while tableview is being touched.
        return;
    }

    [super setSelected:selected animated:animated];

    // Custom selection logic
}