简体   繁体   中英

Custom UITableView section index

Is it possible to customize the UITableView's section index? I mean, changing the font style/size, background (which is semitransparent by default) etc. I'm guessing that the answer would be NO.

So, are there any open-source solutions out there that can be used for implementing a custom UITableView section index? If not, how should i go about creating such a component/control/view?

Update (2017-08-31): Finally edited for ARC, modern Objective-C and iOS SDK (API deprecations).

I made this class some time ago. Feel free to use it as reference. It does not have any properties to set the appearance, but you can modify those directly in the source code (it has lots of hard-coded constants, distances, colors etc.)

Interface:

#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

Implementation:

#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

And finally, the index view's delegate (which ideally is the table view's delegate/data source) does this on notification:

(eg, UITableViewController subclass implementation)

- (void) tableIndexView:(TableIndexView *)tableIndexView didSwipeToSection:(NSUInteger)section {
    [_tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]
                      atScrollPosition:UITableViewScrollPositionTop
                              animated:NO];
}

Alternatively, you can have the TableIndexView keep a pointer to the UITableView in an ivar, and on swipe, manipulate the table view directly (obviating the need for a delegate). but the index view does not own the table view, so it kind of feels wrong.

self.tableView.sectionIndexColor = [UIColor brownColor];
self.tableView.sectionIndexBackgroundColor = [UIColor clearColor];
self.tableView.sectionIndexTrackingBackgroundColor = [UIColor blueColor];

In iOS 6 you can configure the Table Index using the methods below on UITableView:

  • sectionIndexMinimumDisplayRowCount
  • sectionIndexColor
  • sectionIndexTrackingBackgroundColor

I ended up using a custom view. It's not possible to customize the table index.

Swift version:

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

To customize the index view height ( UITableViewStylePlain style only):

tableView.sectionIndexMinimumDisplayRowCount = 15

https://github.com/Hyabusa/CMIndexBar

Use this plugin from Hyabusa. Simple replacment for UITableView Index that allows setting of colors

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];

Delegate

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

I started a custom implementation of the table index on GitHub. You may try this one: https://github.com/r-dent/RGIndexView Feel free to contribute.

its help for ios 6 and ios 7&8

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

    if(!IS_IOS6)
    {

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

It is possible to adjust it if you're okay with accessing private properties. I believe this would pass store approval but don't take my word for it. Here are the properties/functions you would be able to access. https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/UIKit.framework/UITableViewIndex.h

I've tested changing the font with the following and it worked.

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()
        }
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM