一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Swift多列表格组件的实现具备排序功能

时间:2016-03-17 编辑:简简单单 来源:一聚教程网

1,支持排序的多列表格(multi-column sortable table control)效果图
    原文:Swift - 多列表格组件的实现(样例2:带排序功能)      原文:Swift - 多列表格组件的实现(样例2:带排序功能)
 
2,功能说明:
(1)表格列头文字增加下划线样式,表示可以点击。
(2)点击列头标题,内容条目便会根据该列数据进行排序显示(先升序、后降序,依次交替)
(3)排序列背景色会变为蓝色,同时列头会显示上下箭头表示排列顺序。

(4)这里排序的上下箭头不是图片,而是使用 Font Awesome 图标字体库。优点是可以很轻松地设置颜色和大小,而不会失真

 

3,项目代码
(代码中高亮部分表示新增的排序相关的代码) 

--- UICollectionGridViewController.swift(组件类) --


-- UICollectionGridViewController.swift(组件类) ---

import Foundation
import UIKit
 
//表格排序协议
protocol UICollectionGridViewSortDelegate {
    func sort(colIndex: Int, asc: Bool, rows: [[AnyObject]]) -> [[AnyObject]]
}
 
//多列表格组件(通过CollectionView实现)
class UICollectionGridViewController: UICollectionViewController {
    //表头数据
    var cols: [String]! = []
    //行数据
    var rows: [[AnyObject]]! = []
    
    //排序代理
    var sortDelegate: UICollectionGridViewSortDelegate!
    //选中的表格列(-1表示没有选中的)
    private var selectedColIdx = -1
    //列排序顺序
    private var asc = true
    //单元格内容居左时的左侧内边距
    private var cellPaddingLeft:CGFloat = 5
    
    init() {
        //初始化表格布局
        let layout = UICollectionGridViewLayout()
        super.init(collectionViewLayout: layout)
        layout.viewController = self
        collectionView!.backgroundColor = UIColor.whiteColor()
        collectionView!.registerClass(UICollectionViewCell.self,
            forCellWithReuseIdentifier: "cell")
        collectionView!.delegate = self
        collectionView!.dataSource = self
        collectionView!.directionalLockEnabled = true
        collectionView!.contentInset = UIEdgeInsetsMake(0, 10, 0, 10)
        collectionView!.bounces = false
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("UICollectionGridViewController.init(coder:) has not been implemented")
    }
    
    //设置列头数据
    func setColumns(columns: [String]) {
        cols = columns
    }
    
    //添加行数据
    func addRow(row: [AnyObject]) {
        rows.append(row)
        collectionView!.collectionViewLayout.invalidateLayout()
        collectionView!.reloadData()
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewDidLayoutSubviews() {
        collectionView!.frame = CGRectMake(0, 0, view.frame.width, view.frame.height)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    //返回表格总行数
    override func numberOfSectionsInCollectionView(collectionView: UICollectionView)
        -> Int {
        if cols.isEmpty {
            return 0
        }
        //总行数是:记录数+1个表头
        return rows.count + 1
    }
    
    //返回表格的列数
    override func collectionView(collectionView: UICollectionView,
        numberOfItemsInSection section: Int) -> Int {
        return cols.count
    }
    
    //单元格内容创建
    override func collectionView(collectionView: UICollectionView,
        cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell",
            forIndexPath: indexPath) as UICollectionViewCell
        //单元格边框
        cell.layer.border
        cell.backgroundColor = UIColor.whiteColor()
        cell.clipsToBounds = true
        
        //先清空内部原有的元素
        for subview in cell.subviews {
            subview.removeFromSuperview()
        }
        
        //添加内容标签
        let label = UILabel(frame: CGRectMake(0, 0, cell.frame.width, cell.frame.height))
        
        //第一列的内容左对齐,其它列内容居中
        if indexPath.row != 0 {
            label.textAlignment = NSTextAlignment.Center
        }else {
            label.textAlignment = NSTextAlignment.Left
            label.frame.origin.x = cellPaddingLeft
        }
        
        //设置列头单元格,内容单元格的数据
        if indexPath.section == 0 {
            let text = NSAttributedString(string: cols[indexPath.row], attributes: [
                NSUnderlineStyleAttributeName:NSUnderlineStyle.StyleSingle.rawValue,
                NSFontAttributeName:UIFont.boldSystemFontOfSize(15)
                ])
            label.attributedText = text
        } else {
            label.font = UIFont.systemFontOfSize(15)
            label.text = "\(rows[indexPath.section-1][indexPath.row])"
        }
        cell.addSubview(label)
        
        //列排序
        if indexPath.row == selectedColIdx {
            //排序列的单元格背景会变色
            cell.backgroundColor = UIColor(red: 122/255, green: 186/255, blue: 255/255,
                alpha: 1)
            //排序列列头显示升序降序图标,并调整列头标签相关位置
            if indexPath.section == 0 {
                let imageWidth: CGFloat = 14
                let imageHeight: CGFloat = 14
                let labelHeight = label.frame.height
                label.sizeToFit()
                label.frame = CGRectMake(cellPaddingLeft, 0, min(label.frame.width,
                    cell.frame.width - imageWidth), labelHeight)
     
                let iconType = asc ? FAType.FALongArrowUp : FAType.FALongArrowDown
                let imageView = UIImageView()
                imageView.frame = CGRectMake(label.frame.width+3,
                    cell.frame.height/2 - imageHeight/2, imageWidth, imageHeight)
                
                imageView.setFAIconWithName(iconType, textColor: UIColor.blueColor())
                 cell.addSubview(imageView)
            }
        }
        return cell
    }
    
    //单元格选中事件
    override func collectionView(collectionView: UICollectionView,
        didSelectItemAtIndexPath indexPath: NSIndexPath) {
        //打印出点击单元格的[行,列]坐标
        print("点击单元格的[行,列]坐标: [\(indexPath.section),\(indexPath.row)]")
        if indexPath.section == 0 && sortDelegate != nil {
            //如果点击的是表头单元格,则默认该列升序排列,再次点击则变降序排列,以此交替
            asc = (selectedColIdx != indexPath.row) ? true : !asc
            selectedColIdx = indexPath.row
            rows = sortDelegate.sort(indexPath.row, asc: asc, rows: rows)
            collectionView.reloadData()
        }
    }
}

--- UICollectionGridViewLayout.swift(布局类) ---

import Foundation
import UIKit
 
//多列表格组件布局类
class UICollectionGridViewLayout: UICollectionViewLayout {
    private var itemAttributes: [[UICollectionViewLayoutAttributes]] = []
    private var itemsSize: [NSValue] = []
    private var contentSize: CGSize = CGSizeZero
    //表格组件视图控制器
    var viewController: UICollectionGridViewController!
    
    //准备所有view的layoutAttribute信息
    override func prepareLayout() {
        if collectionView!.numberOfSections() == 0 {
            return
        }
        
        var column = 0
        var xOffset: CGFloat = 0
        var yOffset: CGFloat = 0
        var contentWidth: CGFloat = 0
        var contentHeight: CGFloat = 0
        
        if itemAttributes.count > 0 {
            for var section = 0; section < collectionView?.numberOfSections(); section++ {
                let numberOfItems = collectionView?.numberOfItemsInSection(section)
                for var index = 0; index < numberOfItems; index++ {
                    if section != 0 && index != 0 {
                        continue
                    }
                    
                    let attributes = layoutAttributesForItemAtIndexPath(
                        NSIndexPath(forItem: index, inSection: section))!
                    if section == 0 {
                        var frame = attributes.frame
                        frame.origin.y = collectionView!.contentOffset.y
                        attributes.frame = frame
                    }
                }
            }
            return
        }
        
        itemAttributes = []
        itemsSize = []
        
        if itemsSize.count != viewController.cols.count {
            calculateItemsSize()
        }
        
        for var section = 0; section < collectionView?.numberOfSections(); section++ {
            var sectionAttributes: [UICollectionViewLayoutAttributes] = []
            for var index = 0; index < viewController.cols.count; index++ {
                let itemSize = itemsSize[index].CGSizeValue()
                
                let indexPath = NSIndexPath(forItem: index, inSection: section)
                let attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath:
                    indexPath)
                //除第一列,其它列位置都左移一个像素,防止左右单元格间显示两条边框线
                if index == 0{
                    attributes.frame = CGRectIntegral(CGRectMake(xOffset, yOffset,
                        itemSize.width, itemSize.height))
                }else {
                    attributes.frame = CGRectIntegral(CGRectMake(xOffset-1, yOffset,
                        itemSize.width+1, itemSize.height))
                }
                
                if section == 0 && index == 0 {
                    attributes.zIndex = 1024
                } else if section == 0 || index == 0 {
                    attributes.zIndex = 1023
                }
                
                if section == 0 {
                    var frame = attributes.frame
                    frame.origin.y = collectionView!.contentOffset.y
                    attributes.frame = frame
                }
                
                sectionAttributes.append(attributes)
                
                xOffset = xOffset+itemSize.width
                column++
                
                if column == viewController.cols.count {
                    if xOffset > contentWidth {
                        contentWidth = xOffset
                    }
                    
                    column = 0
                    xOffset = 0
                    yOffset += itemSize.height
                }
            }
            itemAttributes.append(sectionAttributes)
        }
        
        let attributes = itemAttributes.last!.last! as UICollectionViewLayoutAttributes
        contentHeight = attributes.frame.origin.y + attributes.frame.size.height
        contentSize = CGSizeMake(contentWidth, contentHeight)
    }
    
    //需要更新layout时调用
    override func invalidateLayout() {
        itemAttributes = []
        itemsSize = []
        contentSize = CGSizeZero
        super.invalidateLayout()
    }
    
    // 返回内容区域总大小,不是可见区域
    override func collectionViewContentSize() -> CGSize {
        return contentSize
    }
    
    // 这个方法返回每个单元格的位置和大小
    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath)
        -> UICollectionViewLayoutAttributes? {
         return itemAttributes[indexPath.section][indexPath.row]
    }
    
    // 返回所有单元格位置属性
    override func layoutAttributesForElementsInRect(rect: CGRect)
        -> [UICollectionViewLayoutAttributes]? {
        var attributes: [UICollectionViewLayoutAttributes] = []
        for section in itemAttributes {
            attributes.appendContentsOf(section.filter(
                {(includeElement: UICollectionViewLayoutAttributes) -> Bool in
                return CGRectIntersectsRect(rect, includeElement.frame)
            }))
        }
        return attributes
    }
    
    //当边界发生改变时,是否应该刷新布局。
    //本例在宽度变化时,将重新计算需要的布局信息。
    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        let oldBounds = self.collectionView?.bounds
        if CGRectGetWidth(oldBounds!) != CGRectGetWidth(newBounds) {
            return true
        }else {
            return false
        }
    }
    
    //计算所有单元格的尺寸(每一列各一个单元格)
    func calculateItemsSize() {
        var remainingWidth = collectionView!.frame.width -
            collectionView!.contentInset.left - collectionView!.contentInset.right
        
        for var index = viewController.cols.count-1; index >= 0; index-- {
            let newItemSize = sizeForItemWithColumnIndex(index,
                remainingWidth: remainingWidth)
            remainingWidth -= newItemSize.width
            let newItemSizeValue = NSValue(CGSize: newItemSize)
            //由于遍历列的时候是从尾部开始遍历了,因此将结果插入数组的时候都是放人第一个位置
            itemsSize.insert(newItemSizeValue, atIndex: 0)
        }
    }
    
    //计算某一列的单元格尺寸
    func sizeForItemWithColumnIndex(columnIndex: Int, remainingWidth: CGFloat) -> CGSize {
        let columnString = viewController.cols[columnIndex]
        //根据列头标题文件,估算各列的宽度
        let size = NSString(string: columnString).sizeWithAttributes([
            NSFontAttributeName:UIFont.systemFontOfSize(15),
            NSUnderlineStyleAttributeName:NSUnderlineStyle.StyleSingle.rawValue
            ])
        
        //如果有剩余的空间则都给第一列
        if columnIndex == 0 {
            return CGSizeMake(max(remainingWidth, size.width + 17), size.height + 10)
        }
        //行高增加10像素,列宽增加17像素(为了容纳下排序图表)
        return CGSizeMake(size.width + 17, size.height + 10)
    }
}

--- ViewController.swift(测试类) ---

import UIKit
 
class ViewController: UIViewController, UICollectionGridViewSortDelegate {
    
    var gridViewController: UICollectionGridViewController!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        gridViewController = UICollectionGridViewController()
        gridViewController.setColumns(["客户", "消费金额", "消费次数", "满意度"])
        gridViewController.addRow(["hangge", "100", "8", "60%"])
        gridViewController.addRow(["张三", "223", "16", "81%"])
        gridViewController.addRow(["李四", "143", "25", "93%"])
        gridViewController.addRow(["王五", "75", "2", "53%"])
        gridViewController.addRow(["韩梅梅", "43", "12", "33%"])
        gridViewController.addRow(["李雷", "33", "27", "45%"])
        gridViewController.addRow(["王大力", "33", "22", "15%"])
        gridViewController.sortDelegate = self
        view.addSubview(gridViewController.view)
    }
    
    override func viewDidLayoutSubviews() {
        gridViewController.view.frame = CGRectMake(0, 50, view.frame.width,
            view.frame.height-60)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        
    }
    
    //表格排序函数
    func sort(colIndex: Int, asc: Bool, rows: [[AnyObject]]) -> [[AnyObject]] {
        let sortedRows = rows.sort { (firstRow: [AnyObject], secondRow: [AnyObject])
            -> Bool in
            let firstRowValue = firstRow[colIndex] as! String
            let secondRowValue = secondRow[colIndex] as! String
            if colIndex == 0 {
                //首例姓名使用字典排序法
                if asc {
                    return firstRowValue < secondRowValue
                }
                return firstRowValue > secondRowValue
            } else if colIndex == 1 || colIndex == 2 {
                //中间两列使用数字排序
                if asc {
                    return Int(firstRowValue)! < Int(secondRowValue)!
                }
                return Int(firstRowValue)! > Int(secondRowValue)!
            }
            //最后一列数据先去掉百分号,再转成数字比较
            let firstRowValuePercent = Int(firstRowValue.substringToIndex(
                firstRowValue.endIndex.advancedBy(-1)))
            let secondRowValuePercent = Int(secondRowValue.substringToIndex(
                secondRowValue.endIndex.advancedBy(-1)))
            if asc {
                return firstRowValuePercent < secondRowValuePercent
            }
            return firstRowValuePercent > secondRowValuePercent
            
        }
        return sortedRows
    }
}

热门栏目