Swift在TableView中自定义索引IndexView

效果图

IndexView.gif

过程

思路:

自定义tableView的IndexView实现方式有两种:第一种,tableView中自带UITableIndexView,可以拿到这个view,然后进行处理; 第二种,就是自己定义一个view,将这个view添加上去;哈哈,我采用的是第二种方法,挺灵活的。

实现步骤:

1. 弄一个tableView的分类UITableView+IndexView,提供一个IndexView属性和一个显示IndexView的方法:
extension UITableView {

var indexView: CustomIndexView? {
    set(view) {
        objc_setAssociatedObject(self, &IndexViewParamKey, view, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
    get {
        return objc_getAssociatedObject(self, &IndexViewParamKey) as? CustomIndexView
    }
}

func customIndexView(indexTitles: [String]) {
    indexView = CustomIndexView(indexTitles: indexTitles)
    self.superview?.addSubview(indexView!)
    
    //滑动到对应的section
    indexView?.selectedSection = { [weak self] section in
        self?.scrollToRow(at: IndexPath(item: 0, section: section), at: UITableViewScrollPosition.top, animated: false)
    }
}

}
2.接下来就是自定义IndexView了,根据tableView中将要显示多少组,依次创建UILabel;
func setupLabels() {
    let itemX: CGFloat = 0
    var itemY: CGFloat = 0
    for i in 0..<indexTitles.count {
        let label = UILabel(frame: CGRect(x: itemX, y: itemY, width: itemWidth, height: itemHeight))
        label.text = indexTitles[i]
        label.tag = Tag + i
        label.textAlignment = .center
        label.textColor = UIColor.black
        label.font = fontSize
        
        addSubview(label)
        
        itemY += itemHeight
        
        if i == indexTitles.count - 1 {
            self.frame.size.height = itemY
        }
    }
    
    self.frame = CGRect(x: ScreenWidth - itemWidth, y: 0, width: itemWidth, height: itemY)
    self.center.y = ScreenHeight * 0.5
    
}
3. 监听自定义的IndexView的touchesBegan,touchesMoved,touchesCancelled,touchesEnded方法:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    panAnimationWithTouches(touches: touches)
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    panAnimationWithTouches(touches: touches)
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    panAnimationFinished()
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    panAnimationFinished()
}
4. 外部调用
 //titlesArray 是section 对应的titles数组
tableView.customIndexView(indexTitles: titlesArray)

总结

所有的代码如下:

import UIKit

let ScreenWidth = UIScreen.main.bounds.size.width
let ScreenHeight = UIScreen.main.bounds.size.height
private var IndexViewParamKey = "IndexViewParamKey"

extension UITableView {

var indexView: CustomIndexView? {
    set(view) {
        objc_setAssociatedObject(self, &IndexViewParamKey, view, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
    get {
        return objc_getAssociatedObject(self, &IndexViewParamKey) as? CustomIndexView
    }
}

func customIndexView(indexTitles: [String]) {
    indexView = CustomIndexView(indexTitles: indexTitles)
    self.superview?.addSubview(indexView!)
    
    indexView?.selectedSection = { [weak self] section in
        self?.scrollToRow(at: IndexPath(item: 0, section: section), at: UITableViewScrollPosition.top, animated: false)
    }
}

}


//MARK:- 自定义IndexView
class CustomIndexView: UIView {

typealias SimpleCallBackWithInt = (_ index: Int) -> ()
var selectedSection: SimpleCallBackWithInt?

private var tipLabel: UILabel!

private var indexTitles: [String] = [String]()
private var itemHeight: CGFloat = 30
private var itemWidth: CGFloat = 40
private var tipLabelWidth: CGFloat = 60

private var Tag = 11111
private let fontSize = UIFont.systemFont(ofSize: 16)
private let animationDuration: TimeInterval = 0.25

override init(frame: CGRect) {
    super.init(frame: frame)
    
}

convenience init(frame: CGRect = CGRect.zero, indexTitles: [String]) {
    
    self.init(frame: frame)
    self.indexTitles = indexTitles
    setupUI()
}

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

func setupUI() {
    backgroundColor = UIColor.lightGray
    setupLabels()
    setupTipLabel()
}

func setupTipLabel() {
    tipLabel = UILabel(frame: CGRect(x: -tipLabelWidth - 20, y: 0, width: tipLabelWidth, height: tipLabelWidth))
    tipLabel.font = fontSize
    tipLabel.textAlignment = .center
    tipLabel.textColor = UIColor.black
    tipLabel.backgroundColor = UIColor.orange
    tipLabel.layer.cornerRadius = tipLabelWidth * 0.5
    tipLabel.layer.masksToBounds = true
    tipLabel.alpha = 0
    self.addSubview(tipLabel)
}

func setupLabels() {
    let itemX: CGFloat = 0
    var itemY: CGFloat = 0
    for i in 0..<indexTitles.count {
        let label = UILabel(frame: CGRect(x: itemX, y: itemY, width: itemWidth, height: itemHeight))
        label.text = indexTitles[i]
        label.tag = Tag + i
        label.textAlignment = .center
        label.textColor = UIColor.black
        label.font = fontSize
        
        addSubview(label)
        
        itemY += itemHeight
        
        if i == indexTitles.count - 1 {
            self.frame.size.height = itemY
        }
    }
    
    self.frame = CGRect(x: ScreenWidth - itemWidth, y: 0, width: itemWidth, height: itemY)
    self.center.y = ScreenHeight * 0.5
    
}

func showTipsLabel(section: Int, centerY: CGFloat) {
    guard let tipLabel = self.tipLabel else { return }
    
    selectedSection?(section)
    tipLabel.text = self.indexTitles[section]
    tipLabel.center.y = centerY
    
    UIView.animate(withDuration: animationDuration, animations: {
        tipLabel.alpha = 1

    })
}

func panAnimationWithTouches(touches: Set<UITouch>) {
    guard let touch = touches.first else { return }
    let point = touch.location(in: self)
    for i in 0..<indexTitles.count {
        if let label = self.viewWithTag(Tag + i) {
            if label.frame.contains(point) {
                showTipsLabel(section: i, centerY: label.center.y)
            }
        }
    }
}

func panAnimationFinished() {
    guard let tipLabel = self.tipLabel else { return }
    
    UIView.animate(withDuration: animationDuration, animations: {
        tipLabel.alpha = 0
        
    })
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    panAnimationWithTouches(touches: touches)
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    panAnimationWithTouches(touches: touches)
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    panAnimationFinished()
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    panAnimationFinished()
}

deinit {
    self.tipLabel.removeFromSuperview()
    self.removeFromSuperview()
    self.selectedSection = nil
}
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 好奇触摸事件是如何从屏幕转移到APP内的?困惑于Cell怎么突然不能点击了?纠结于如何实现这个奇葩响应需求?亦或是...
    Lotheve阅读 57,985评论 51 603
  • 7、不使用IB是,下面这样做有什么问题? 6、请说说Layer和View的关系,以及你是如何使用它们的。 1.首先...
    AlanGe阅读 719评论 0 1
  • 在iOS开发中经常会涉及到触摸事件。本想自己总结一下,但是遇到了这篇文章,感觉总结的已经很到位,特此转载。作者:L...
    WQ_UESTC阅读 6,114评论 4 26
  • 在接下来的两章中,您将创建 TouchTracker,该应用程序中用户可以通过触摸屏幕来画画。 在本章中,您将创建...
    titvax阅读 705评论 0 0
  • 一直喜欢看书,小时候接触到的课外书有限,兴趣点也有限,选择上没啥困难。上大学后,发现书的类目太多了,很多想看的书都...
    lei__阅读 373评论 0 0