Обновление (2017-08-31): Окончательно отредактировано для ARC, современного Objective-C и iOS SDK (устаревшие API).
Я создал этот класс некоторое время назад.Не стесняйтесь использовать его в качестве ссылки.У него нет каких-либо свойств для настройки внешнего вида, но вы можете изменить их непосредственно в исходном коде (в нем много жестко заданных констант, расстояний, цветов и т. Д.)
Интерфейс:
#import <UIKit/UIKit.h>
@class TableIndexView;
@protocol TableIndexViewDelegate <NSObject>
- (void) tableIndexView:(TableIndexView*) tableIndexView
didSwipeToSection:(NSUInteger) section;
@end
@interface TableIndexView : UIView
@property (nonatomic, weak) id<TableIndexViewDelegate> delegate;
@property (nonatomic) NSUInteger numberOfSections;
- (id)initWithTableView:(UITableView *)tableView;
@end
Реализация:
#import "TableIndexView.h"
#import <QuartzCore/QuartzCore.h>
#define TableIndexViewDefaultWidth 20.0f
#define TableIndexViewDefaultMargin 16.0f
@interface TableIndexView()
@property (nonatomic) NSUInteger currentSection;
@property (nonatomic, strong) UIView* backgroundView;
@property (nonatomic, strong) UIView* contentView;
- (void)show;
- (void)hide;
@end
@implementation TableIndexView
@synthesize delegate = _delegate;
@synthesize numberOfSections = _numberOfSections;
- (id)initWithTableView:(UITableView *)tableView {
CGRect tableBounds = [tableView bounds];
CGRect outerFrame = CGRectZero;
outerFrame.origin.x = tableBounds.size.width - (40 + TableIndexViewDefaultWidth);
outerFrame.origin.y = 0;
outerFrame.size.width = (40 + TableIndexViewDefaultWidth);
outerFrame.size.height = tableBounds.size.height;
CGRect indexFrame = CGRectZero;
indexFrame.origin.x = tableBounds.size.width - (TableIndexViewDefaultWidth + TableIndexViewDefaultMargin);
indexFrame.origin.y = TableIndexViewDefaultMargin;
indexFrame.size.width = TableIndexViewDefaultWidth;
indexFrame.size.height = tableBounds.size.height - 2*TableIndexViewDefaultMargin;
if ((self = [super initWithFrame:outerFrame])) {
// Initialization code
self.backgroundColor = [UIColor clearColor];
[self setUserInteractionEnabled:YES];
// Content View (Background color, Round Corners)
indexFrame.origin.x = 20;
_backgroundView = [[UIView alloc] initWithFrame:indexFrame];
_backgroundView.backgroundColor = [UIColor colorWithRed:1.00f
green:1.00f
blue:1.00f
alpha:0.75f];
CGFloat radius = 0.5f*TableIndexViewDefaultWidth;
_backgroundView.layer.cornerRadius = radius;
[self addSubview:_backgroundView];
_numberOfSections = [[tableView dataSource] numberOfSectionsInTableView:tableView];
CGRect contentFrame = CGRectZero;
contentFrame.origin.x = 0;
contentFrame.origin.y = radius;
contentFrame.size.width = TableIndexViewDefaultWidth;
contentFrame.size.height = indexFrame.size.height - 2*radius;
_contentView = [[UIView alloc] initWithFrame:contentFrame];
_contentView.backgroundColor = [UIColor clearColor];
[_backgroundView addSubview:_contentView];
CGFloat labelWidth = contentFrame.size.width;
CGFloat labelHeight = 12;
CGFloat interLabelHeight = (contentFrame.size.height - (_numberOfSections)*labelHeight)/(_numberOfSections - 1.0);
CGFloat fontSize = 12;
for (NSUInteger i=0; i < _numberOfSections; i++) {
if ( _numberOfSections > 20 && i%2 == 0 ) {
// Skip even section labels if count is greater than, say, 20
continue;
}
CGRect labelFrame = CGRectZero;
labelFrame.size.width = labelWidth;
labelFrame.size.height = labelHeight;
labelFrame.origin.x = 0;
labelFrame.origin.y = i*(labelHeight+interLabelHeight);
UILabel* label = [[UILabel alloc] initWithFrame:labelFrame];
label.text = [NSString stringWithFormat:@"%lu", i+1];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor blackColor];
label.backgroundColor = [UIColor clearColor];
label.font = [UIFont systemFontOfSize:floorf(1.0f*fontSize)];
[_contentView addSubview:label];
}
[_backgroundView setHidden:YES];
}
return self;
}
#pragma mark - Control Actions
- (void)didTap:(id) sender {
[_backgroundView setHidden:NO];
}
- (void)didRelease:(id) sender {
[_backgroundView setHidden:YES];
}
#pragma mark - Internal Operation
- (void)show {
[self didTap:nil];
}
- (void)hide {
[self didRelease:nil];
}
#pragma mark - UIResponder Methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView:_contentView];
CGFloat ratio = location.y / _contentView.frame.size.height;
NSUInteger newSection = ratio*_numberOfSections;
if (newSection != _currentSection) {
_currentSection = newSection;
[_delegate tableIndexView:self didSwipeToSection:_currentSection];
}
[_backgroundView setHidden:NO];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView:_contentView];
CGFloat ratio = location.y / _contentView.frame.size.height;
NSUInteger newSection = ratio*_numberOfSections;
if (newSection != _currentSection) {
_currentSection = newSection;
if (newSection < _numberOfSections) {
if (_delegate) {
[_delegate tableIndexView:self didSwipeToSection:_currentSection];
}
else{
// **Perhaps call the table view directly
}
}
}
[_backgroundView setHidden:NO];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[_backgroundView setHidden:YES];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[_backgroundView setHidden:YES];
}
@end
И, наконец, делегат представления индекса (который в идеале является делегатом / источником данных табличного представления) делает это при уведомлении:
(например, подкласс UITableViewController)реализация)
- (void) tableIndexView:(TableIndexView *)tableIndexView didSwipeToSection:(NSUInteger)section {
[_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]
atScrollPosition:UITableViewScrollPositionTop
animated:NO];
}
В качестве альтернативы вы можете сделать так, чтобы TableIndexView сохранял указатель на UITableView в ivar, а при пролистывании манипулировал табличным представлением напрямую (устраняя необходимость в делегате).но индексному представлению не принадлежит табличное представление, поэтому оно кажется неправильным.