UILongPressGestureRecognizer *longPressGesture =
[[[UILongPressGestureRecognizer alloc] initWithTarget: self
action: @selector(longPress:)]
autorelease];
[self addGestureRecognizer: longPressGesture];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver: self
selector: @selector(unselectSelf:)
name: UIMenuControllerWillHideMenuNotification
object: nil];
- (void) unselectSelf: (NSNotification *) notification {
self.selected = NO;
} // unselectSelf
In the long press handler, check the state (to prevent multiple firings of the gesture recognizer from making the menus spaz out), and then set up the menu thing.
- (void) longPress: (UILongPressGestureRecognizer *) recognizer {
if (recognizer.state != UIGestureRecognizerStateBegan) return;
Put the stuff from Adding a pop up menu thingie here
self.selected = YES; // Highlight to make it doubly-obvious what's being menu'd
} // longPress
UITableViewCell *cell = ...; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;The delegate method (accessoryTypeForRowWithIndexPath) has been deprecated, so don't use that.
Use UITableViewCellAccessoryCheckmark for a checkmark.
Use UITableViewCellAccessoryNone to remove the checkmark.
And then override these. In this case, I split up an array into an array of arrays, each sub-array being the contents of a section that corresponds to a string to display in the index. _tableIndex is an array of strings for display of the index.
- (NSArray *) sectionIndexTitlesForTableView: (UITableView *) tableView {
return _tableIndex;
} // sectionIndexTitles
- (NSInteger) tableView: (UITableView *) tableView
sectionForSectionIndexTitle: (NSString *) title
atIndex: (NSInteger) index {
return index;
} // sectionForSectionIndexTitle
if (someProperty) cell.textLabel.textColor = [UIColor grayColor];
else cell.textLabel.textColor = [UIColor blackColor];
- (BOOL) tableView: (UITableView *) tableView
canMoveRowAtIndexPath: (NSIndexPath *) indexPath {
if (indexPath.row == 0) return NO;
else if (indexPath.row == _cues.count - 1) return NO;
else return YES;
} // canMoveRowAtIndexPath
And then prevent rows from being dragged to the first and last position:
- (NSIndexPath *) tableView: (UITableView *) tableView
targetIndexPathForMoveFromRowAtIndexPath: (NSIndexPath *) source
toProposedIndexPath: (NSIndexPath *) destination {
if (destination.row > 0 && destination.row < _cues.count - 1) {
// No violence necessary.
return destination;
}
NSIndexPath *indexPath = nil;
// If your table can have <= 2 items, you might want to robusticize the index math.
if (destination.row == 0) {
indexPath = [NSIndexPath indexPathForRow: 1 inSection: 0];
} else {
indexPath = [NSIndexPath indexPathForRow: _cues.count - 2
inSection: 0];
}
return indexPath;
} // targetIndexPathForMoveFromRowAtIndexPathPleaseOHaiThanksForMovingKthxBai NSIndexPath *indexPath = [NSIndexPath indexPathForRow: row
inSection: 0];
willSelectRowAtIndexPath, return nil to reject the selection, return the passed-in indexPath to use as-is, or return your choice of selected cells.
- (NSIndexPath *) tableView: (UITableView *) tableView
willSelectRowAtIndexPath: (NSIndexPath *) indexPath {
// don't select the top row sort control.
if (indexPath.row == 0) return nil;
else return indexPath;
} // willSelectRowAtIndexPath
- (IBAction) elaborateType: (id) sender {
if (![sender isKindOfClass: [UIButton class]]) return; // be paranoid
UITableViewCell *cell = (UITableViewCell *)[sender superview];
if (![cell isKindOfClass: [UITableViewCell class]]) return; // be paranoid
NSIndexPath *indexPath = [self.kindPickerTableView indexPathForCell: cell];
// do something with indexPath.row and/or indexPath.section.
} // elaborateType NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
if (indexPath != nil) [self doStuff];
- (void) tableView: (UITableView *) tableView
commitEditingStyle: (UITableViewCellEditingStyle) editingStyle
forRowAtIndexPath: (NSIndexPath *) indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[_cues removeObjectAtIndex: indexPath.row]; // manipulate your data structure.
[tableView deleteRowsAtIndexPaths: [NSArray arrayWithObject: indexPath]
withRowAnimation: UITableViewRowAnimationFade];
[self updateUI]; // Do whatever other UI updating you need to do.
}
} // commitEditingStyle
-reloadData, but don't. That's an awfully big hammer. Instead just reload a row
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: row
inSection: 0];
NSArray *array = [NSArray arrayWithObject: indexPath];
[_playlistTableView reloadRowsAtIndexPaths: array
withRowAnimation: UITableViewRowAnimationNone];layoutSubviews in your UITableViewCell subclass. Any view position changes will automatically be animated.
- (void) layoutSubviews {
// skootch stuff over
if (self.editing && !self.showingDeleteConfirmation) {
#define MARGIN 40.0
CGRect frame = CGRectMake (MARGIN + 4.0, 4.0, 70.0, 46.0);
_profileView.frame = frame;
frame = CGRectMake (MARGIN + 80.0, 0, 240.0 - MARGIN, 55.0);
_titleLabel.frame = frame;
// layout normally
} else {
CGRect frame = CGRectMake (4.0, 4.0, 70.0, 46.0);
_profileView.frame = frame;
frame = CGRectMake (80.0, 0, 240.0, 55.0);
_titleLabel.frame = frame;
}
[super layoutSubviews];
} // layoutSubviews
The showingDeleteConfirmation test is so you don't move things around if the user does the "swipe-right to show delete button" thing.-canEditRowAtIndexPath:
- (BOOL) tableView: (UITableView *) tableView
canEditRowAtIndexPath: (NSIndexPath *) indexPath {
if (indexPath.row == 0) return NO;
else return YES;
} // canEditRowAtIndexPath
[self.cuesTableView setEditing: YES animated: YES];
- (void) tableView: (UITableView *) tableView
moveRowAtIndexPath: (NSIndexPath *) from
toIndexPath: (NSIndexPath *) to {
// How to rearrange stuff if you're backed by an NSMutableArray:
GRCue *cue = [_cues objectAtIndex: from.row];
[cue retain]; // Let it survive being removed from the array.
[_cues removeObjectAtIndex: from.row];
[_cues insertObject: cue atIndex: to.row];
[cue release];
} // moveRowAtIndexPath
- (void)tableView: (UITableView *) tableView
didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
// Do something logical with indexPath.row, etc.
} // didSelectRowAtIndexPath
NSIndexPath *someRow = [NSIndexPath indexPathForRow: random() % blah.count
inSection: 0];
[self.cuesTableView scrollToRowAtIndexPath: someRow
atScrollPosition: UITableViewScrollPositionBottom
animated: YES];
You can also scroll to PositionTop and PositionMiddle. If you crash, make sure you've done a -reloadData on the table view prior to trying to scroll.
NSIndexPath *indexPath = [NSIndexPath indexPathForRow: index
inSection: 0];
[self.tableView reloadData]; // necessary if selecting row in -viewDidLoad
[self.tableView selectRowAtIndexPath: indexPath
animated: YES
scrollPosition: UITableViewScrollPositionNone];
cell.textLabel.backgroundColor = [UIColor redColor]; cell.contentView.backgroundColor = [UIColor redColor];
UIImage *image = [_assets iconAtIndex: indexPath.row];
if (image) cell.imageView.image = image;
// Optional
- (NSInteger) numberOfSectionsInTableView: (UITableView *) tableView {
return 1;
} // numberOfSectionsInTableView
- (NSInteger) tableView: (UITableView *) tableView
numberOfRowsInSection: (NSInteger) section {
return dataArray.count;
} // numberOfRowsInSection
- (UITableViewCell *) tableView: (UITableView *) tableView
cellForRowAtIndexPath: (NSIndexPath *) indexPath {
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier: @"BlahTableViewCell"];
if (!cell) {
cell = [[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: @"BlahTableViewCell"];
[cell autorelease]; // Delete for ARC
}
cell.textLabel.text = [dataArray objectAtIndex: indexPath.row];
return cell;
} // cellForRowAtIndexPath
Make the cell with UITableViewCellStyleSubtitle:
cell = [[[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleSubtitle
reuseIdentifier: @"UITableViewCell"] autorelease];
and set the label values:
cell.textLabel.text = @"somewhere";
cell.detailTextLabel.text = @"over the rainbow";
_tableView.scrollEnabled = NO;
- (NSString *) tableView: (UITableView *) tableview
titleForHeaderInSection: (NSInteger) section {
NSArray *sections = [BWTermStorage sections];
return [sections objectAtIndex: section];
} // titleForHeaderInSection
You can return a title for a footer.
You can also return a view:
- (UIView *) tableView: (UITableView *) tableView
viewForHeaderInSection: (NSInteger) section;
The appropriate constants are
UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, and UITableViewCellStyleSubtitle, and settable properties include imageView, textLabel, and detailTextLabel.

1) Make a nib with a single object at the top, a UITableViewCell. Lay it out as you wish. I use view tags to get to the objects contained therein.
enum {
kTermLabelTag = 1,
kDetailLabelTag = 2
};
2) Register the nib for a cell reuse identifier. I do it in my -viewDidLoad.
static NSString *g_cellReuseIdentifier = @"BWLookupCellReuseIdentifier"; // file global
...
UINib *nib = [UINib nibWithNibName: @"BWLookupTableViewCell" bundle: nil];
[self.resultsView registerNib: nib
forCellReuseIdentifier: g_cellReuseIdentifier];
3) Dequeue the cell as usual
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier: g_cellReuseIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleSubtitle
reuseIdentifier: g_cellReuseIdentifier];
}
4) Dig into the cell with the tags, and go nuts
UILabel *termLabel = (UILabel *)[cell viewWithTag: kTermLabelTag];
UILabel *detailLabel = (UILabel *)[cell viewWithTag: kDetailLabelTag];
termLabel.text = termString;
detailLabel.text = definition;
Or you can hack it by setting the cell's indent to 1. That scoots the whole cell contents over by 10 pixels, letting you miss the rounded corners. Really handy if you're prototyping, want something to look non-horrible, but don't want to go the full-blown correct solution.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let selectedRow = self.tableView.indexPathForSelectedRow else { return }
guard let vc = segue.destination as? InfographicViewController else { return }
let counter = counters[selectedRow.row]
let stats = counter.currentTextStats
vc.stats = stats
}
@IBOutlet var sessionsTableView : UITableView!
let cellReuseIdentifier = "Cell"
...
override func viewDidLoad() {
super.viewDidLoad()
self.sessionsTableView.register(UITableViewCell.self,
forCellReuseIdentifier: cellReuseIdentifier)
}
...
extension ViewController : UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier,
for: indexPath) as UITableViewCell
cell.textLabel?.text = "Spork (indexPath.row)"
return cell
}
}