form1.cn
Make a little progress every day

Part 4:iOS数据源协议、委托协议与高级视图

07th of February 2017 Swift Swift 1917

在这一章中学习到了picker控件与collectionView视图,以下主要为普通picker的城市三级联动、collectionView视图使用的方法与对应DataSource和Delegate简单使用的Demo


picker控件:

import UIKit

class FirstViewController: UIViewController,UITextFieldDelegate,UIPickerViewDelegate,UIPickerViewDataSource {

    //注:在使用Delegate或DataSource协议时,需要将对应控件指向当前视图
    @IBOutlet weak var brithdayText: UITextField! //TextField 输出口
    @IBOutlet weak var locationText: UITextField! //TextField 输出口
    @IBOutlet weak var dataPicker: UIDatePicker! //DatePicker 输出口
    @IBOutlet weak var pickerView: UIPickerView! //PickerView 输出口
    @IBOutlet weak var buttonOk: UIButton! //Button 输出口
    
    var pickerAllDatas = [String: AnyObject]()
    var pickerProvincesIndex: Int = 0
    var pickerCitiesIndex: Int = 0
    var pickerCountyIndex: Int = 0
    
    //button点击事件
    @IBAction func onclickOk(_ sender: UIButton) {
        if self.dataPicker.isHidden == false { //如果dataPicker是显示状态

            //获取当前dataPicker选中的时间值
            let theDate: NSDate = self.dataPicker.date as NSDate
            //日期本地化
            theDate.description(with: NSLocale.current)
            //日期格式化
            let dateFormatter: DateFormatter = DateFormatter()
            dateFormatter.dateFormat = "YYYY-MM-dd" //时间格式 YYYY-MM-dd HH:mm:ss
            //取得最终日期
            let brithday = dateFormatter.string(from: theDate as Date)
            self.brithdayText.text = brithday //设置textField的值
            
        }else if self.pickerView.isHidden == false { //如果pickerView是显示状态
            
            let row1 = self.pickerView.selectedRow(inComponent: 0)//当前列的行索引
            let row2 = self.pickerView.selectedRow(inComponent: 1)//当前列的行索引
            let row3 = self.pickerView.selectedRow(inComponent: 2)//当前列的行索引
            //设置textField的值
            self.locationText.text = "\(self.provincesNmae(row: row1)) - \(self.citiesName(row: row2)) - \(self.countyName(row: row3))"
            
        }
        self.hiddenView()//隐藏规定的控件方法
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.hiddenView()//隐藏规定的控件方法
    }

    //textField 单行文本输入框 [以下为协议委托的方法实现...]
    //开始输入时
    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
        
        if textField.tag == 1 {
            self.dataPicker.isHidden = false //显示dataPicker
            self.pickerView.isHidden = true //隐藏pickerView
            self.brithdayText.text = "" //清空textfield的值
        }else if textField.tag == 2 {
            self.dataPicker.isHidden = true //隐藏dataPicker
            self.pickerView.isHidden = false //显示pickerView
            self.locationText.text = "" //清空textfield的值
            
            //获取plist文件路径
            let plistPath = Bundle.main.path(forResource: "location", ofType: "plist")
            //获取属性列表文件中的全部数据保存到pickerAllDatas中
            self.pickerAllDatas = NSDictionary(contentsOfFile: plistPath!) as! Dictionary
            
            //初始化省 市 县的索引
            self.pickerProvincesIndex = 0
            self.pickerCitiesIndex = 0
            self.pickerCountyIndex = 0
            //让省、市、县回到第一行
            self.pickerView.selectRow(0, inComponent: 0, animated: false)
            self.pickerView.selectRow(0, inComponent: 1, animated: false)
            self.pickerView.selectRow(0, inComponent: 2, animated: false)
            //用户点击textField触发开始输入协议时,重新reloadComponent
            self.pickerView.reloadComponent(0)
            self.pickerView.reloadComponent(1)
            self.pickerView.reloadComponent(2)
            
        }
        self.buttonOk.isHidden = false //显示button
        return false //返回false不弹出键盘
    }
    
    //实现协议UIPickerViewDataSource方法
    //为选择器指定拨轮的个数
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 3
    }
    
    //为选择器中的某个拨轮指定行数
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if (component == 0) {//省
            return self.pickerAllDatas.count
        }else if (component == 1) {//市
            if self.pickerAllDatas.count == 0 {//如果pickerAllDatas没有数据
                return 0
            }else{//pickerAllDatas有数据则读取
                let provincesString = self.provincesNmae(row: self.pickerProvincesIndex)
                return (self.pickerAllDatas[String(self.pickerProvincesIndex)]?[provincesString as Any] as! NSDictionary).count
            }
        }else if (component == 2) {//区
            if self.pickerAllDatas.count == 0 {
                return 0
            }else{
                let provincesString = self.provincesNmae(row: self.pickerProvincesIndex)
                let citiesString = self.citiesName(row: self.pickerCitiesIndex)
                let citiesDict = (self.pickerAllDatas[String(self.pickerProvincesIndex)]?[provincesString as Any] as! NSDictionary)[String(self.pickerCitiesIndex)] as! NSDictionary
                return (citiesDict[citiesString as Any] as! NSArray).count
            }
        }
        return 0
    }
    
    //实现协议UIPickerViewDelegate方法
    //提供拨轮中的每一行的显示数据
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if component == 0 {//省
            return self.provincesNmae(row: row)
        }else if component == 1 {//市
            return self.citiesName(row: row)
        }else if component == 2 {//区
            return self.countyName(row: row)
        }
        return ""
    }
    
    //点击拨轮中的行触发的事件
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        if component == 0 {//省
            self.pickerProvincesIndex = row
            self.pickerCitiesIndex = 0
            self.pickerCountyIndex = 0
            self.pickerView.reloadComponent(1) //重加载picker
            self.pickerView.reloadComponent(2) //重加载picker
            self.pickerView.selectRow(0, inComponent: 1, animated: true)//让市回到第一行且有动画效果
            self.pickerView.selectRow(0, inComponent: 2, animated: true)//让县回到第一行且有动画效果
        }else if component == 1 {//市
            self.pickerCitiesIndex = row
            self.pickerCountyIndex = 0
            self.pickerView.reloadComponent(2) ////重加载picker
            self.pickerView.selectRow(0, inComponent: 2, animated: true)//让县回到第一行且有动画效果
        }else if component == 2 {//县
            self.pickerCountyIndex = row
        }
    }
    
    /-------------------------------------------------------------------------*/
    
    //根据索引得到 省 的名称
    func provincesNmae(row: Int) ->String {
        return ((self.pickerAllDatas[String(row)] as AnyObject).allKeys[0] as? String)!
    }
    
    //根据索引得到 市 名称
    func citiesName(row: Int) ->String {
        let provincesString = self.provincesNmae(row: self.pickerProvincesIndex)
        return (((self.pickerAllDatas[String(self.pickerProvincesIndex)]?[provincesString as Any] as! NSDictionary)[String(row)] as AnyObject).allKeys[0] as? String)!
    }
    
    //根据索引得到 县 名称
    func countyName(row: Int) ->String {
        let provincesString = self.provincesNmae(row: self.pickerProvincesIndex)
        let citiesString = self.citiesName(row: self.pickerCitiesIndex)
        let citiesDict = (self.pickerAllDatas[String(self.pickerProvincesIndex)]?[provincesString as Any] as! NSDictionary)[String(self.pickerCitiesIndex)] as! NSDictionary
        let countArray = citiesDict[citiesString as Any] as! NSArray
        return (countArray[row] as? String)!
    }
    
    //隐藏规定的控件方法
    func hiddenView() {
        self.buttonOk.isHidden = true //隐藏button
        self.dataPicker.isHidden = true //隐藏dataPicker
        self.pickerView.isHidden = true //隐藏pickerView
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}


CollectionView视图:

import UIKit

class CollectionViewController: UICollectionViewController {
    //以上在UICollectionViewController中包含UICollectionViewDataSource与UICollectionViewDelegate协议
    //无需冗余引入,DataSource与Delegate也已经连线对应视图文件
    
    
    @IBOutlet weak var showLabel: UILabel!
    
    var events: NSArray!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        //获取plist文件路径
        let plistPath = Bundle.main.path(forResource: "events", ofType: "plist")
        self.events = NSArray(contentsOfFile: plistPath!)
        
    }
    
    //数据源协议UICollectionViewDataSource的方法实现
    //提供集合视图中节的个数,就是说有多少行
    override func numberOfSections(in collectionView: UICollectionView) -> Int {
        return self.events.count / 2//如果不能整除需要在除后+1
    }
    
    //提供某个节中的单元格个数,就是说每行有几列
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 2
    }
    
    //为单元格提供显示需要的数据,必须实现的方法
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        //使用可重用单元格标识获得一个可重用单元格对象,把超出屏幕的单元格对象拿来重用,可以节省内存
        //此除的 “Cell” 为可重用单元格标识,需要在该集合视图中的cell中设定(可理解为collection下面cell的名字)
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! MyCollectionViewCell
        //以上MyCollectionViewCell为自定义的UICollectionViewCell的子类
        
        //event为得到每一行中每一列的数据,indexPath.section为每一行的索引,indexPath.row每一条中列的索引
        let event = self.events[indexPath.section  2 + indexPath.row] as! NSDictionary
        //以上拿到数据为字典,需要强转为 NSDictionary
        
        //取得数据到对应的控件中
        cell.labelView.text = event["name"] as? String
        cell.imageView.image = UIImage(named: event["image"] as! String)
        
        return cell
    }
    
    //委托协议UICollectionViewDelegate的方法实现,方法有很多,这里只实现了选中单元格事件
    override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let event = self.events[indexPath.section  2 + indexPath.row] as! NSDictionary
        self.showLabel.text = event["name"] as? String
        //NSLog("select event name : %@", event["name"] as! String)
    }

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


}