自定义 UITableView 部分索引

Posted

技术标签:

【中文标题】自定义 UITableView 部分索引【英文标题】:Custom UITableView section index 【发布时间】:2011-05-13 04:58:09 【问题描述】:

是否可以自定义 UITableView 的section index?我的意思是,更改字体样式/大小、背景(默认为半透明)等。我猜答案是否定的。

那么,是否有任何开源解决方案可用于实现?如果没有,我应该如何创建这样的组件/控件/视图?

【问题讨论】:

可能重复的问题 - ***.com/questions/2643400/… 【参考方案1】:

更新 (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 在 ivar 中保留指向 UITableView 的指针,然后在滑动时直接操作表格视图(无需委托)。但是索引视图不拥有表视图,所以感觉有点不对。

【讨论】:

感谢您添加此内容。它可以与 1) 更新 ARC,2) 可能有关如何将视图添加到表视图的一些细节以及 3) 有关如何生成正确索引标题的一些指示,但我认为无论如何它都值得赏金。它在这里出现了很多,并且没有简单的方法来自定义内置索引。 我还没有过渡到 ARC,坦率地说,我有点不情愿,不知何故(感觉!)。您将视图添加到承载表视图的同一视图中(当然是在表上方)。理想情况下,同一个视图控制器应该同时管理表和索引视图。目前,索引名称只是整数:1、2、3 等(不能用于电话通讯簿),但应该很容易自定义。 索引视图在初始化时传递给表,并从那里获取表边界。我可以让它自己添加为表格的子视图,但这感觉不对(两个视图都是管理视图控制器的子视图 - 或者更确切地说是它的视图 - 所以索引视图不会自行附加)。 是的,您必须将其添加为对等级视图 - 这意味着您不能将其作为表视图控制器的一部分。而且你不能将它添加为表格视图的子视图,因为它是一个滚动视图,所以它会离开屏幕...... 我要添加:1) 设置外观的属性(宽度、颜色,可能是索引画布的阴影或边框),2) 索引视图委托的“数据源”方法提供自定义索引条目。【参考方案2】:
self.tableView.sectionIndexColor = [UIColor brownColor];
self.tableView.sectionIndexBackgroundColor = [UIColor clearColor];
self.tableView.sectionIndexTrackingBackgroundColor = [UIColor blueColor];

【讨论】:

从 iOS6 开始,这绝对是最好的答案,至少对于基本自定义而言。【参考方案3】:

在 iOS 6 中,您可以在 UITableView 上使用以下方法配置表格索引:

sectionIndexMinimumDisplayRowCount sectionIndexColor sectionIndexTrackingBackgroundColor

【讨论】:

【参考方案4】:

我最终使用了自定义视图。无法自定义表格索引。

【讨论】:

您是如何准确实现这样的自定义视图的?你将它添加到 UITableView 还是什么?【参考方案5】:

Swift 版本:

tableView.sectionIndexBackgroundColor = UIColor.clearColor()
tableView.sectionIndexTrackingBackgroundColor = UIColor.clearColor()
tableView.sectionIndexColor = UIColor.redColor()

自定义索引视图高度(仅限UITableViewStylePlain 样式):

tableView.sectionIndexMinimumDisplayRowCount = 15

【讨论】:

我很惊讶这不是选定的答案。因为它是最简单的解决方案,而且效果很好。 tableView.sectionIndexMinimumDisplayRowCount = 15 是做什么的?它如何影响节索引? 安德烈,developer.apple.com/reference/uikit/uitableview/…【参考方案6】:

如果您可以访问私有属性,则可以对其进行调整。 我相信这会通过商店的批准,但不要相信我的话。 以下是您可以访问的属性/功能。 https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/UIKit.framework/UITableViewIndex.h

我已经测试了使用以下内容更改字体并且它有效。

func viewDidLoad() 
    super.viewDidLoad()

    DispatchQueue.main.async  [unowned self] in
        if let tableViewIndex = self.tableView.subviews.first(where:  String(describing: type(of: $0)) == "UITableViewIndex" ) 
            tableViewIndex.setValue(*Insert Font Here*, forKey: "font")
            self.tableView.reloadSectionIndexTitles()
        
    


【讨论】:

【参考方案7】:

https://github.com/Hyabusa/CMIndexBar

使用 Hyabusa 的这个插件。 允许设置颜色的 UITableView 索引的简单替换

CMIndexBar *indexBar = [[CMIndexBar alloc] initWithFrame:CGRectMake(self.view.frame.size.width-35, 10.0, 28.0, self.view.frame.size.height-20)];
[indexBar setIndexes:[NSMutableArray arrayWithObjects:@"A",@"B",@"C",@"D",@"E",@"F",@"G", nil]];
[self.view addSubview:indexBar];
[indexBar release];

委托

- (void)indexSelectionDidChange:(CMIndexBar *)IndexBar:(int)index:(NSString*)title;

【讨论】:

【参考方案8】:

我在 GitHub 上开始了表索引的自定义实现。你可以试试这个:https://github.com/r-dent/RGIndexView 随意贡献。

【讨论】:

【参考方案9】:

它对 ios 6 和 ios 7&8 的帮助

if ([tableview respondsToSelector:@selector(setSectionIndexColor:)])


    if(!IS_IOS6)
    

        tableview.sectionIndexBackgroundColor = [UIColor clearColor];
    
    tableview.sectionIndexColor = [UIColor whiteColor];

【讨论】:

以上是关于自定义 UITableView 部分索引的主要内容,如果未能解决你的问题,请参考以下文章

UITableView 中的自定义绘图在 5 个单元格后失败

iPhone 上的自定义 UITableView 索引标题

尝试从 iOS 中 UITableView 的自定义单元格内的 textField 获取活动单元格的索引

将自定义对象的 NSArray 转换为按字母顺序划分的带有索引的 UITableView?

自定义 UITableView 部分标题 alpha 0

iOS - TableView - 在点击按钮上获取标题部分的索引