form1.cn
Make a little progress every day

Part 5:iOS表视图-索引、分组,静态表,删除、插入Cell,移动Cell,下拉刷新

19th of February 2017 Swift Swift 2175

在这一章中学习主要学习到了表视图的应用,普通表视图,表视图的Cell自定义,添加搜索栏,表视图添加索引、分组,静态表视图,删除、插入Cell,移动Cell,下拉刷新,以下为本章Demo代码:


普通表视图  -  下拉刷新基础  -  搜索基础

import UIKit

//-------------普通表视图  -  下拉刷新基础  -  搜索基础
class TableViewController: UITableViewController,UISearchBarDelegate {
    //自定义单元时需要,在storyboard中指定cell的类与标识
    
    var SearchData: NSArray! //搜索结果数据
    var TeamData: NSArray! //所有数据
    var tempArray: NSArray! //临时数据
    @IBOutlet weak var searchBar: UISearchBar!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        //获取plist文件路径
        let plistPath = Bundle.main.path(forResource: "team", ofType: "plist")
        //获取属性列表文件中的全部数据保存到pickerAllData中
        self.TeamData = NSArray(contentsOfFile: plistPath!)
        //初始化搜索
        self.contentForSearchText(searchText: "", scope: -1)
        
        //下拉刷新控件初始化
        let rc = UIRefreshControl()
        //定义控件的初始标题
        rc.attributedTitle = NSAttributedString(string: "下拉刷新")
        
        //下拉事件监听(事件处理),事年与处理方法关联起来(所有控件都有该方法)
        //@param1:事件在哪个对像里面,@param2:对应的处理方法,@param3:定义事件类型
        rc.addTarget(self, action: #selector(TableViewController.refreshTableView), for: UIControlEvents.valueChanged)
        
        //将 rc 赋给当前tableView的refreshControl属性
        self.refreshControl = rc
 
        
    }
    
    func refreshTableView() {
        
        //如果正在刷新
        if self.refreshControl?.isRefreshing == true {
            //改控件的标题 为 加载中...
            self.refreshControl?.attributedTitle = NSAttributedString(string: "加载中...")
            
            //TODO 到网络中加载数据
            //===================TODO 数据加载成功后
            let time: TimeInterval = 3.0//延迟加载时间(单位:秒)
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + time) {
                //数据加载成功后,停止下拉刷新
                self.refreshControl?.endRefreshing()
                //改控件的标题 改回 下拉刷新
                self.refreshControl?.attributedTitle = NSAttributedString(string: "下拉刷新")
                //重新加载表视图数据
                self.tableView.reloadData()
            }
        }
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    /
     过滤结果的方法
     @ searchText: 关键字,
     @ scope: 辅助搜索栏的Tag
     /
    func contentForSearchText(searchText: NSString, scope: Int){
        if searchText.length == 0 {//如果没有输入搜索内容
            self.SearchData = NSMutableArray(array: self.TeamData) //返回所有数据
            return
        }
        
        if scope == 0 { //如果英文
            //搜索的逻辑条件对像
            let scopePredicate = NSPredicate(format: "name contains[cd] %@", searchText)
            //集合对像都有这个方法,返回的是过滤后的集合
            self.tempArray = self.TeamData.filtered(using: scopePredicate) as NSArray!
        }else if scope == 1 { //如果中文
            //搜索的逻辑条件对像
            let scopePredicate = NSPredicate(format: "image contains[cd] %@", searchText)
            //集合对像都有这个方法,返回的是过滤后的集合
            self.tempArray = self.TeamData.filtered(using: scopePredicate) as NSArray!
        }
        
        self.SearchData = NSMutableArray(array: self.tempArray)
        
    }
    
    //searchBarDelegate 协议方法实现
    //获得焦点开始输入时
    func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
        self.searchBar.showsScopeBar = true //显示辅助搜索栏
        self.searchBar.sizeToFit() //加载适合的searchBar的大小
        return true
    }
    
    //当用户输入搜索内容时
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        //调用过滤方法,self.searchBar.selectedScopeButtonIndex 为辅助搜索栏的Tag
        self.contentForSearchText(searchText: searchText as NSString, scope: self.searchBar.selectedScopeButtonIndex)
        self.tableView.reloadData()//重新加载表视图数据
    }
    
    //当用户点击右侧cancel(取消搜索)按钮时
    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        self.contentForSearchText(searchText: "", scope: -1)//初始化搜索
        self.searchBar.showsScopeBar = false //隐藏辅助搜索栏
        self.searchBar.text = "" //清空搜索文本
        self.searchBar.sizeToFit() //加载适合的searchBar的大小
        self.searchBar.resignFirstResponder()//放弃第一响应者的方式,关闭键盘
        self.tableView.reloadData()//重新加载表视图数据
    }
    
    //点击键盘上的Search(搜索按钮)时
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        self.searchBar.showsScopeBar = false //隐藏辅助搜索栏
        self.searchBar.sizeToFit() //加载适合的searchBar的大小
        self.searchBar.resignFirstResponder()//放弃第一响应者的方式,关闭键盘
        self.tableView.reloadData()//重新加载表视图数据
    }
    
    //切换辅助搜索栏时
    func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
        self.contentForSearchText(searchText: self.searchBar.text! as String as NSString, scope: selectedScope)//重新搜索
        self.tableView.reloadData()//重新加载表视图数据
    }
    
    /------------------------------------协议分割线-------------------------------------*/
    
    //TableViewDataSource 协议方法实现
    //返回表视图节的个数
    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }

    //返回表视图每个节中的行数
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return self.SearchData.count
    }

    //返回每一个 自定义cell 的内容
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //请求可重用单元格,需要一个标识,CustomCellTableViewCell为自定义单元格类
        let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) as! CustomCellTableViewCell

        //读取每一行的数据到字典中
        let dict = self.SearchData[indexPath.row] as! NSDictionary
        
        //设置单元格中的主标题与图标
        cell.myLabel?.text = dict["name"] as? String
        cell.myImageView?.image = UIImage(named: dict["image"] as! String)

        return cell
    }
    

}


自定义的单元格(Cell)

import UIKit

//-----------自定义的单元格(Cell)
class CustomCellTableViewCell: UITableViewCell {

    @IBOutlet weak var myImageView: UIImageView!
    @IBOutlet weak var myLabel: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}


添加索引与分组的表视图

import UIKit

//--------------添加索引与分组的表视图
class IndexTableViewController: UITableViewController {
    
    var dictData: NSDictionary! //所有数据
    var groupName: NSArray! //所有组的名称
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //获取plist文件路径
        let plistPath = Bundle.main.path(forResource: "team_dictionary", ofType: "plist")
        //获取属性列表文件中的全部数据保存到pickerAllData中
        self.dictData = NSDictionary(contentsOfFile: plistPath!)
        //无排序的小组名称数组
        let listtemp = self.dictData.allKeys as NSArray
        //按照指定方法重新排序,这里是字母排序
        self.groupName = listtemp.sortedArray(using: #selector(NSNumber.compare(_:))) as NSArray
        
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    
    //TableViewDataSource 协议方法实现
    //返回表视图节的个数,所谓的分节
    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return self.groupName.count
    }
    
    //返回表视图每个节中的行数
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        //得到节的(组的)名称
        let G_name = self.groupName[section] as! String
        //根据组名称得到:每组对应的数组
        let G_dict = self.dictData[G_name] as! NSArray
        return G_dict.count
    }
    
    //返回每一个 自定义cell 的内容
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //请求可重用单元格,需要一个标识 indexdescet : 在视图中对应cell 中指定
        let cell = tableView.dequeueReusableCell(withIdentifier: "indexdescet", for: indexPath) as UITableViewCell
        
        //得到节的索引
        let section = indexPath.section
        //得到每一个节中 行的索引
        let row = indexPath.row
        //得到节的(组的)名称
        let G_name = self.groupName[section] as! String
        //根据组名称得到:每组对应的数组
        let G_dict = self.dictData[G_name] as! NSArray
        //设置单元格中的主标题与图标
        cell.textLabel?.text = G_dict[row] as? String
        
        return cell
    }
    
    /------------------------------------协议分割线-------------------------------------*/
    
    //TableView索引的方法实现
    //添加节标题,节标题默认是索引标题
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        //得到节的(组的)名称
        let G_name = self.groupName[section] as! String
        return G_name

    }
    
    //返回索引标题的集合(数组),右侧的索引列表
    override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        //NSArray和NSMutableArray的区别就是后者是可变数组
        var listitles = [String]()
        for item in self.groupName {
            let title = (item as AnyObject).substring(to: 1)//截取第一个字符
            listitles.append(title)//添加到数组中
        }
        return listitles
    }
    
    //分组:分组前需要先分节,然后在视图中 选中tableView标签指示器中的style进行选择

}


静态表视图,一般用于布局

import UIKit

//----------静态表视图,一般用于布局
class StaticTableViewController: UITableViewController {
    
    //静态表:视图中选中tableView 选择静态,选择分组,设置节个数

    override func viewDidLoad() {
        super.viewDidLoad()

        // Uncomment the following line to preserve selection between presentations
        // self.clearsSelectionOnViewWillAppear = false

        // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
        // self.navigationItem.rightBarButtonItem = self.editButtonItem()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: - Table view data source

    //=======注:静态表的话,需要注释掉下面的方法,否则节与行全是0
    
    /override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 0
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return 0
    }*/

}


删除,插入表视图

import UIKit

//-------------删除,插入表视图
class DeleteAddTableViewController: UITableViewController {
    
    @IBOutlet var textField: UITextField! //在视图中非界面中的textField
    
    var jsonData = [String]() //一个空数组

    override func viewDidLoad() {
        super.viewDidLoad()

        //通过代码实现导航控制器的右按钮,并已实现Done与Edit的切换,
        self.navigationItem.rightBarButtonItem = self.editButtonItem
        //加载时需要textField为隐藏状态
        self.textField.isHidden = true
        //设置textDelegate为当前视图控制器: 拖拽吧
        
        //设置数据(一般应该是请求接口得到的数据,这里就写死)
        self.jsonData = ["北京","河北","天津","上海","重庆","深圳","湖北","山东"]
        
        //dump(self.jsonData[0])
        
    }
    
    //重写setEditng,点击Edit时会调用该方法
    override func setEditing(_ editing: Bool, animated: Bool) {
        //调用父类的setEditing方法,需要父类中的一些默认操作
        super.setEditing(editing, animated: animated)
        //调用tableView的setEditing方法,使表视图处于编辑状态
        self.tableView.setEditing(editing, animated: animated)
        //editing = true 说明点击了Edit
        if editing {
            self.textField.isHidden = false
        }else{
            self.textField.isHidden = true
        }
        //print(editing)
    }
 
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    
    // MARK: - Table view data source
    //TableViewDataSource 协议方法实现
    //返回表视图节的个数
    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }
    
    //返回表视图每个节中的行数
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.jsonData.count + 1
    }
    
    //返回每一个 自定义cell 的内容
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //请求可重用单元格,需要一个标识,CustomCellTableViewCell为自定义单元格类
        let cell = tableView.dequeueReusableCell(withIdentifier: "deleteandAdd", for: indexPath)
        
        //如果单元格的索引等于最后一条为 Add..
        if indexPath.row == self.jsonData.count {
            //设置textField在Cell中的位置与大小
            self.textField.frame = CGRect(x: 10, y: 0, width: 300, height: 66)
            //清空textField的内容
            self.textField.text = ""
            //讲textField添加到Cell的contentView中
            cell.contentView.addSubview(self.textField)
        }else{//为正常数据(Delete)
            //设置扩展视图的类型,就是Cell右侧的小箭头
            cell.accessoryType = UITableViewCellAccessoryType.disclosureIndicator
            //设置单元的内容
            cell.textLabel?.text = self.jsonData[indexPath.row]
        }
        
        return cell
    }
    
    //编辑风格,editingStyle参数会返回是insert还是delete,然后做对应的操作
    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        //删除或添加单元格的Indexpath需要一个集合
        let tempArray: [IndexPath] = [indexPath]
        if editingStyle == UITableViewCellEditingStyle.insert {//如果是插入
            self.jsonData.append(self.textField.text!)//插入数据
            self.tableView.insertRows(at: tempArray, with: UITableViewRowAnimation.left)//插入单元格
        }else if editingStyle == UITableViewCellEditingStyle.delete {//如果是删
            self.jsonData.remove(at: indexPath.row)//删除数据
            self.tableView.deleteRows(at: tempArray, with: UITableViewRowAnimation.left)//删除单元格
        }
        //UITableViewRowAnimation.left 为设置操作的动画,有很多动画可选择
    }
    
    /------------------------------------协议分割线-------------------------------------*/
    
    //TableViewDelegate 协议方法实现
    //返回单元格在编辑状态下的样式,三种样式:none,insert,delete
    override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
        //如果单元格的索引等于最后一条为 Add..
        if indexPath.row == self.jsonData.count {
            return UITableViewCellEditingStyle.insert
        }else{//为正常数据(Delete)
            return UITableViewCellEditingStyle.delete
        }
    }
    
    //设置单元格在点击后是否高亮
    override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
        //如果单元格的索引等于最后一条为 Add..  点击后不高亮
        if indexPath.row == self.jsonData.count {
            return false
        }else{//为正常数据 点击后高亮
            return true
        }
    }
    
    //设置单元格的高度,只能通过代码实现
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50
    }

}


移动单元格

import UIKit

//---------------移动单元格
class MoveTableViewController: UITableViewController {
    
    var jsonData: NSMutableArray! //一个空数组

    override func viewDidLoad() {
        super.viewDidLoad()

        //通过代码实现导航控制器的右按钮,并已实现Done与Edit的切换,
        self.navigationItem.rightBarButtonItem = self.editButtonItem
        //设置数据(一般应该是请求接口得到的数据,这里就写死)
        self.jsonData = NSMutableArray(array: ["PHP","ASP","JSP","JAVASCRIPT","SWIFT","PYTHON","PERL","C/C++","RUBY"])
        
        
        //可变数组方式,移动单元格更适合用这种存储方式
        /var newarr: NSMutableArray!
        newarr = NSMutableArray(array: ["一","二","三"])
        newarr.removeObject(at: 0) //["二","三"]
        newarr.insert("一", at: 1) //["二","一","三"]
        print(newarr[1])*/
        
    }
    
    //重写setEditng,点击Edit时会调用该方法
    override func setEditing(_ editing: Bool, animated: Bool) {
        //调用父类的setEditing方法,需要父类中的一些默认操作
        super.setEditing(editing, animated: animated)
        //调用tableView的setEditing方法,使表视图处于编辑状态
        self.tableView.setEditing(editing, animated: animated)
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // MARK: - Table view data source
    //TableViewDataSource 协议方法实现
    //返回表视图节的个数
    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }
    
    //返回表视图每个节中的行数
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.jsonData.count
    }
    
    //返回每一个 自定义cell 的内容
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //请求可重用单元格,需要一个标识,CustomCellTableViewCell为自定义单元格类
        let cell = tableView.dequeueReusableCell(withIdentifier: "movecell", for: indexPath)
        
        //设置扩展视图的类型,就是Cell右侧的小箭头
        cell.accessoryType = UITableViewCellAccessoryType.disclosureIndicator
        //设置单元的内容
        cell.textLabel?.text = self.jsonData[indexPath.row] as? String
        
        return cell
    }
    
    //让你能够移动单元格
    override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
        return true
    }
    
    //在源索引路径上移动行,移动的一些操作
    override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        //得到要被移动的单元格数据
        let cellName = self.jsonData[sourceIndexPath.row]
        //删掉要被移动的单元格数据
        self.jsonData.removeObject(at: sourceIndexPath.row)
        //在移动的目录索引位置重新加入 要被移动的单元格数据
        self.jsonData.insert(cellName, at: destinationIndexPath.row)
       
        //dump(self.jsonData)
    }
    
    /------------------------------------协议分割线-------------------------------------*/
    
    //TableViewDelegate 协议方法实现
    //返回单元格在编辑状态下的样式,三种样式:none,insert,delete
    override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
        return UITableViewCellEditingStyle.none //左侧不返回添加或删除标识
    }

}