├── Example ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── Info.plist ├── Main.storyboard ├── Vc1Controller.swift ├── Vc2Controller.swift └── ViewController.swift ├── LICENSE ├── README.md ├── Source ├── .DS_Store ├── Area.plist ├── City.plist ├── Picker.swift ├── PickerView.swift ├── Province.plist ├── SelectionTextField.swift ├── ToolBarView.swift └── UsefulPickerView.swift ├── UsefulPickerVIew.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ ├── jasnig.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ ├── shiweifu.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── zeroj.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── jasnig.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── UsefulPickerVIew.xcscheme │ │ └── xcschememanagement.plist │ ├── shiweifu.xcuserdatad │ └── xcschemes │ │ ├── UsefulPickerVIew.xcscheme │ │ └── xcschememanagement.plist │ └── zeroj.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── UsefulPickerVIew.xcscheme │ └── xcschememanagement.plist ├── UsefulPickerVIewTests ├── Info.plist └── UsefulPickerVIewTests.swift ├── UsefulPickerVIewUITests ├── Info.plist └── UsefulPickerVIewUITests.swift ├── UsefulPickerView.podspec └── UsefulPickerView ├── .DS_Store ├── Example ├── .DS_Store ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── Main.storyboard ├── Vc1Controller.swift ├── Vc2Controller.swift └── ViewController.swift ├── Info.plist └── Source ├── .DS_Store ├── Area.plist ├── City.plist ├── Picker.swift ├── PickerView.swift ├── Province.plist ├── SelectionTextField.swift ├── ToolBarView.swift └── UsefulPickerView.swift /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/25. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Example/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Example/Vc1Controller.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Vc1Controller.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/24. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | 31 | import UIKit 32 | 33 | let singleData = ["swift", "ObjecTive-C", "C", "C++", "java", "php", "python", "ruby", "js"] 34 | // 每一列为数组 35 | let multipleData = [["1天", "2天", "3天", "4天", "5天", "6天", "7天"],["1小时", "2小时", "3小时", "4小时", "5小时"], ["1分钟","2分钟","3分钟","4分钟","5分钟","6分钟","7分钟","8分钟","9分钟","10分钟"]] 36 | 37 | // 注意这个数据的格式!!!!!! 38 | let multipleAssociatedData: [[[String: [String]?]]] = [// 数组 39 | 40 | [ ["交通工具": ["陆地", "空中", "水上"]],//字典 41 | ["食品": ["健康食品", "垃圾食品"]], 42 | ["游戏": ["益智游戏", "角色游戏"]] 43 | 44 | ],// 数组 45 | 46 | [ ["陆地": ["公交车", "小轿车", "自行车"]], 47 | ["空中": ["飞机"]], 48 | ["水上": ["轮船"]], 49 | ["健康食品": ["蔬菜", "水果"]], 50 | ["垃圾食品": ["辣条", "不健康小吃"]], 51 | ["益智游戏": ["消消乐", "消灭星星"]], 52 | ["角色游戏": ["lol", "cf"]] 53 | 54 | ] 55 | ] 56 | 57 | class Vc1Controller: UIViewController { 58 | 59 | 60 | @IBOutlet weak var selectedDataLabel: UILabel! 61 | 62 | 63 | @IBAction func singleBtnOnClick(sender: UIButton) { 64 | 65 | UsefulPickerView.showSingleColPicker("编程语言选择", data: singleData, defaultSelectedIndex: 2) {[unowned self] (selectedIndex, selectedValue) in 66 | self.selectedDataLabel.text = "选中了第\(selectedIndex)行----选中的数据为\(selectedValue)" 67 | } 68 | 69 | } 70 | @IBAction func multipleBtnOnClick(sender: UIButton) { 71 | 72 | 73 | UsefulPickerView.showMultipleColsPicker("持续时间选择", data: multipleData, defaultSelectedIndexs: [0,1,1]) {[unowned self] (selectedIndexs, selectedValues) in 74 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 75 | 76 | } 77 | 78 | } 79 | @IBAction func multipleAssociatedBtnOnClick(sender: UIButton) { 80 | 81 | // 注意这里设置的是默认的选中值, 而不是选中的下标,省得去数关联数组里的下标 82 | UsefulPickerView.showMultipleAssociatedColsPicker("多列关联数据", data: multipleAssociatedData, defaultSelectedValues: ["交通工具","陆地","自行车"]) {[unowned self] (selectedIndexs, selectedValues) in 83 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 84 | 85 | } 86 | 87 | } 88 | @IBAction func citiesBtnOnClick(sender: UIButton) { 89 | 90 | UsefulPickerView.showCitiesPicker("省市区选择", defaultSelectedValues: ["四川", "成都", "郫县"]) {[unowned self] (selectedIndexs, selectedValues) in 91 | // 处理数据 92 | let combinedString = selectedValues.reduce("", combine: { (result, value) -> String in 93 | result + " " + value 94 | }) 95 | 96 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(combinedString)" 97 | 98 | } 99 | } 100 | @IBAction func dateBtnOnClick(sender: UIButton) { 101 | 102 | 103 | UsefulPickerView.showDatePicker("日期选择") {[unowned self] ( selectedDate) in 104 | let formatter = NSDateFormatter() 105 | formatter.dateFormat = "yyyy-MM-dd" 106 | let string = formatter.stringFromDate(selectedDate) 107 | self.selectedDataLabel.text = "选中了的日期是\(string)" 108 | } 109 | 110 | 111 | } 112 | 113 | @IBAction func timeBtnOnClick(sender: UIButton) { 114 | var dateStyle = DatePickerSetting() 115 | dateStyle.dateMode = .Time 116 | 117 | /// /// @author ZeroJ, 16-04-25 17:04:28 118 | /// 注意使用这种方式的时候, 请设置 autoSetSelectedText = false, 否则显示的格式可能不是您需要的 119 | UsefulPickerView.showDatePicker("时间选择", datePickerSetting: dateStyle) { (selectedDate) in 120 | let formatter = NSDateFormatter() 121 | // H -> 24小时制 122 | formatter.dateFormat = "HH: mm" 123 | let string = formatter.stringFromDate(selectedDate) 124 | self.selectedDataLabel.text = "选中了的日期是\(string)" 125 | } 126 | } 127 | 128 | override func viewDidLoad() { 129 | super.viewDidLoad() 130 | 131 | // Do any additional setup after loading the view. 132 | } 133 | 134 | override func didReceiveMemoryWarning() { 135 | super.didReceiveMemoryWarning() 136 | // Dispose of any resources that can be recreated. 137 | } 138 | 139 | 140 | /* 141 | // MARK: - Navigation 142 | 143 | // In a storyboard-based application, you will often want to do a little preparation before navigation 144 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 145 | // Get the new view controller using segue.destinationViewController. 146 | // Pass the selected object to the new view controller. 147 | } 148 | */ 149 | 150 | } 151 | -------------------------------------------------------------------------------- /Example/Vc2Controller.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Vc2Controller.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/24. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | 31 | import UIKit 32 | 33 | class Vc2Controller: UIViewController { 34 | 35 | @IBOutlet weak var singleTextField: SelectionTextField! 36 | @IBOutlet weak var multipleTextField: SelectionTextField! 37 | @IBOutlet weak var multipleAssociatedTextField: SelectionTextField! 38 | @IBOutlet weak var citiesTextField: SelectionTextField! 39 | @IBOutlet weak var dateTextField: SelectionTextField! 40 | @IBOutlet weak var timeTextField: SelectionTextField! 41 | 42 | @IBOutlet weak var selectedDataLabel: UILabel! 43 | override func viewDidLoad() { 44 | super.viewDidLoad() 45 | 46 | singleTextField.showSingleColPicker("编程语言选择", data: singleData, defaultSelectedIndex: 2, autoSetSelectedText: true) {[unowned self] (textField, selectedIndex, selectedValue) in 47 | // 可以使用textField 也可以使用 self.singleTextField 48 | textField.text = "选中了第\(selectedIndex)行----选中的数据为\(selectedValue)" 49 | self.selectedDataLabel.text = "选中了第\(selectedIndex)行----选中的数据为\(selectedValue)" 50 | 51 | } 52 | 53 | 54 | multipleTextField.showMultipleColsPicker("持续时间选择", data: multipleData, defaultSelectedIndexs: [0,1,1], autoSetSelectedText: true) {[unowned self] (textField, selectedIndexs, selectedValues) in 55 | self.multipleTextField.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 56 | 57 | } 58 | 59 | 60 | // 注意这里设置的是默认的选中值, 而不是选中的下标 61 | multipleAssociatedTextField.showMultipleAssociatedColsPicker("多列关联数据", data: multipleAssociatedData, defaultSelectedValues: ["交通工具","陆地","自行车"], autoSetSelectedText: true) {[unowned self] (textField, selectedIndexs, selectedValues) in 62 | self.multipleAssociatedTextField.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 63 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 64 | 65 | } 66 | 67 | 68 | citiesTextField.showCitiesPicker("省市区选择", defaultSelectedValues: ["四川", "成都", "郫县"], autoSetSelectedText: false) {[unowned self] (textField, selectedIndexs, selectedValues) in 69 | self.citiesTextField.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 70 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 71 | 72 | } 73 | 74 | dateTextField.showDatePicker("日期选择", autoSetSelectedText: true) { (textField, selectedDate) in 75 | let formatter = NSDateFormatter() 76 | formatter.dateFormat = "yyyy-MM-dd" 77 | let string = formatter.stringFromDate(selectedDate) 78 | textField.text = string 79 | } 80 | 81 | var dateStyle = DatePickerSetting() 82 | dateStyle.dateMode = .Time 83 | 84 | /// /// @author ZeroJ, 16-04-25 17:04:28 85 | /// 注意使用这种方式的时候, 请设置 autoSetSelectedText = false, 否则显示的格式可能不是您需要的 86 | timeTextField.showDatePicker("时间选择", datePickerSetting: dateStyle, autoSetSelectedText: false) { (textField, selectedDate) in 87 | let formatter = NSDateFormatter() 88 | // H -> 24小时制 89 | formatter.dateFormat = "HH: mm" 90 | let string = formatter.stringFromDate(selectedDate) 91 | textField.text = string 92 | } 93 | } 94 | 95 | override func didReceiveMemoryWarning() { 96 | super.didReceiveMemoryWarning() 97 | // Dispose of any resources that can be recreated. 98 | } 99 | 100 | 101 | /* 102 | // MARK: - Navigation 103 | 104 | // In a storyboard-based application, you will often want to do a little preparation before navigation 105 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 106 | // Get the new view controller using segue.destinationViewController. 107 | // Pass the selected object to the new view controller. 108 | } 109 | */ 110 | 111 | } 112 | -------------------------------------------------------------------------------- /Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/25. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view, typically from a nib. 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 ZeroJ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #UsefulPickerView 2 | 3 | 4 | ##使用示例效果 5 | 6 | 7 | ![TextField使用示例.gif](http://upload-images.jianshu.io/upload_images/1271831-4d7f9d232c035146.gif?imageMogr2/auto-orient/strip) 8 | 9 | ![按钮使用示例.gif](http://upload-images.jianshu.io/upload_images/1271831-1fe46c6326188f7f.gif?imageMogr2/auto-orient/strip) 10 | ----- 11 | 12 | ### 可以简单快速灵活的实现上图中的效果 13 | 14 | 15 | --- 16 | 17 | ### 书写思路移步[这里](http://www.jianshu.com/p/ffb7d3628fb3) 18 | 19 | ## Requirements 20 | 21 | * iOS 8.0+ 22 | * Xcode 7.3 or above 23 | 24 | ## Installation 25 | 26 | ### CocoaPods 27 | ####1.在你的项目Podfile里面添加下面的内容 28 | 29 | source 'https://github.com/CocoaPods/Specs.git' 30 | 31 | platform :ios, '8.0' 32 | use_frameworks! 33 | 34 | pod 'UsefulPickerView', '~> 0.1.2' 35 | 36 | ###2.终端中执行命令 pod install 37 | ###3. 使用{Project}.xcworkspace打开项目 38 | 39 | 40 | --- 41 | ###或者直接下载,将下载文件的Scource文件夹下的文件拖进您的项目中就可以使用了 42 | --- 43 | 44 | ###Usage 45 | --- 46 | ###如果是使用cocoapods安装的需要在使用的文件中 47 | ###import UsefulPickerView 48 | --- 49 | 50 | ####1. TextField 使用, 可以使用xib或者代码初始化 51 | // 代码生成 52 | let test = SelectionTextField(frame: CGRect(x: 20, y: 340, width: 340, height: 28)) 53 | test.borderStyle = .RoundedRect 54 | test.placeholder = "代码初始化" 55 | test.showSingleColPicker("测试代码", data: singleData, defaultSelectedIndex: 0, autoSetSelectedText: true) { (textField, selectedIndex, selectedValue) in 56 | print(selectedValue) 57 | } 58 | view.addSubview(test) 59 | 60 | singleTextField.showSingleColPicker("编程语言选择", data: singleData, defaultSelectedIndex: 2, autoSetSelectedText: true) {[unowned self] (textField, selectedIndex, selectedValue) in 61 | // 可以使用textField 也可以使用 self.singleTextField 62 | textField.text = "选中了第\(selectedIndex)行----选中的数据为\(selectedValue)" 63 | self.selectedDataLabel.text = "选中了第\(selectedIndex)行----选中的数据为\(selectedValue)" 64 | 65 | } 66 | 67 | 68 | 69 | ####2. 按钮使用.在点击事件的方法里面 70 | UsefulPickerView.showSingleColPicker("编程语言选择", data: singleData, defaultSelectedIndex: 2) {[unowned self] (selectedIndex, selectedValue) in 71 | self.selectedDataLabel.text = "选中了第\(selectedIndex)行----选中的数据为\(selectedValue)" 72 | } 73 | UsefulPickerView.showMultipleColsPicker("持续时间选择", data: multipleData, defaultSelectedIndexs: [0,1,1]) {[unowned self] (selectedIndexs, selectedValues) in 74 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 75 | 76 | } 77 | 78 | 79 | 80 | 81 | 82 | ####如果你在使用中遇到问题: 可以通过[简书](http://www.jianshu.com/users/fb31a3d1ec30/latest_articles)私信给我 83 | 84 | ## License 85 | 86 | UsefulPickerView is released under the MIT license. See LICENSE for details. -------------------------------------------------------------------------------- /Source/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnig/UsefulPickerView/4a3075d3c22b4845d31c3a69f048290d68717b3f/Source/.DS_Store -------------------------------------------------------------------------------- /Source/City.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 上海 6 | 7 | 普陀 8 | 闸北 9 | 虹口 10 | 杨浦 11 | 卢湾 12 | 徐汇 13 | 长宁 14 | 静安 15 | 黄浦 16 | 金山 17 | 浦东新 18 | 嘉定 19 | 闵行 20 | 宝山 21 | 南汇 22 | 青浦 23 | 松江 24 | 奉贤 25 | 崇明 26 | 27 | 云南 28 | 29 | 德宏 30 | 玉溪 31 | 曲靖 32 | 保山 33 | 怒江 34 | 迪庆 35 | 昭通 36 | 昆明 37 | 楚雄 38 | 文山 39 | 西双版纳 40 | 丽江 41 | 红河 42 | 大理 43 | 临沧 44 | 思茅 45 | 46 | 内蒙古 47 | 48 | 巴彦淖尔 49 | 锡林郭勒盟 50 | 兴安盟 51 | 乌兰察布 52 | 鄂尔多斯 53 | 乌海 54 | 包头 55 | 呼和浩特 56 | 呼伦贝尔 57 | 通辽 58 | 阿拉善盟 59 | 赤峰 60 | 61 | 北京 62 | 63 | 通州 64 | 房山 65 | 昌平 66 | 顺义 67 | 怀柔 68 | 大兴 69 | 密云 70 | 平谷 71 | 延庆 72 | 东城 73 | 崇文 74 | 西城 75 | 朝阳 76 | 宣武 77 | 石景山 78 | 丰台 79 | 门头沟 80 | 海淀 81 | 82 | 台湾 83 | 84 | 新竹 85 | 基隆 86 | 高雄 87 | 台北 88 | 台南 89 | 嘉义 90 | 台中 91 | 宜兰 92 | 屏东 93 | 桃园 94 | 台东 95 | 苗栗 96 | 金门 97 | 花莲 98 | 云林 99 | 连江 100 | 澎湖 101 | 南投 102 | 彰化 103 | 104 | 吉林 105 | 106 | 松原 107 | 四平 108 | 白城 109 | 白山 110 | 吉林 111 | 通化 112 | 长春 113 | 延边朝鲜族 114 | 辽源 115 | 116 | 四川 117 | 118 | 宜宾 119 | 巴中 120 | 南充 121 | 成都 122 | 凉山彝族 123 | 眉山 124 | 阿坝 125 | 乐山 126 | 绵阳 127 | 广安 128 | 广元 129 | 德阳 130 | 资阳 131 | 达州 132 | 泸州 133 | 自贡 134 | 遂宁 135 | 甘孜藏族 136 | 雅安 137 | 内江 138 | 攀枝花 139 | 140 | 国外 141 | 142 | 德国 143 | 新加坡 144 | 美国 145 | 加拿大 146 | 澳大利亚 147 | 日本 148 | 英国 149 | 巴西 150 | 俄罗斯 151 | 尼日利亚 152 | 马来西亚 153 | 爱尔兰 154 | 奥地利 155 | 挪威 156 | 意大利 157 | 西班牙 158 | 泰国 159 | 芬兰 160 | 丹麦 161 | 荷兰 162 | 阿联酋 163 | 瑞典 164 | 瑞士 165 | 比利时 166 | 新西兰 167 | 法国 168 | 韩国 169 | 匈牙利 170 | 其他 171 | 越南 172 | 以色列 173 | 科威特 174 | 希腊 175 | 南非 176 | 葡萄牙 177 | 墨西哥 178 | 印尼 179 | 180 | 天津 181 | 182 | 武清 183 | 河东 184 | 和平 185 | 西青 186 | 津南 187 | 大港 188 | 东丽 189 | 塘沽 190 | 汉沽 191 | 河北 192 | 红桥 193 | 河西 194 | 南开 195 | 蓟县 196 | 宁河 197 | 静海 198 | 宝坻 199 | 北辰 200 | 201 | 宁夏 202 | 203 | 石嘴山 204 | 固原 205 | 中卫 206 | 银川 207 | 吴忠 208 | 209 | 安徽 210 | 211 | 淮南 212 | 黄山 213 | 蚌埠 214 | 合肥 215 | 宿州 216 | 六安 217 | 池州 218 | 芜湖 219 | 宣城 220 | 巢湖 221 | 亳州 222 | 阜阳 223 | 铜陵 224 | 淮北 225 | 滁州 226 | 马鞍山 227 | 安庆 228 | 229 | 山东 230 | 231 | 淄博 232 | 烟台 233 | 日照 234 | 荷泽 235 | 潍坊 236 | 济南 237 | 济宁 238 | 青岛 239 | 临沂 240 | 威海 241 | 莱芜 242 | 泰安 243 | 东营 244 | 聊城 245 | 枣庄 246 | 滨州 247 | 德州 248 | 249 | 山西 250 | 251 | 临汾 252 | 晋中 253 | 朔州 254 | 运城 255 | 晋城 256 | 阳泉 257 | 忻州 258 | 大同 259 | 长治 260 | 太原 261 | 吕梁 262 | 263 | 广东 264 | 265 | 珠海 266 | 惠州 267 | 清远 268 | 韶关 269 | 江门 270 | 揭阳 271 | 云浮 272 | 佛山 273 | 广州 274 | 深圳 275 | 河源 276 | 汕头 277 | 汕尾 278 | 茂名 279 | 肇庆 280 | 东莞 281 | 湛江 282 | 潮州 283 | 阳江 284 | 中山 285 | 梅州 286 | 287 | 广西 288 | 289 | 贺州 290 | 梧州 291 | 河池 292 | 百色 293 | 来宾 294 | 贵港 295 | 玉林 296 | 钦州 297 | 北海 298 | 柳州 299 | 桂林 300 | 南宁 301 | 防城港 302 | 崇左 303 | 304 | 新疆 305 | 306 | 伊犁 307 | 克拉玛依 308 | 哈密地 309 | 石河子 310 | 吐鲁番地 311 | 阿拉尔 312 | 阿勒泰地 313 | 乌鲁木齐 314 | 塔城地 315 | 昌吉 316 | 克孜勒 317 | 图木舒克 318 | 阿克苏地 319 | 五家渠 320 | 巴音郭楞 321 | 和田地 322 | 博尔塔拉 323 | 喀什地 324 | 325 | 江苏 326 | 327 | 连云港 328 | 盐城 329 | 无锡 330 | 宿迁 331 | 扬州 332 | 镇江 333 | 南京 334 | 徐州 335 | 泰州 336 | 南通 337 | 常州 338 | 淮安 339 | 苏州 340 | 341 | 江西 342 | 343 | 南昌 344 | 萍乡 345 | 景德镇 346 | 吉安 347 | 九江 348 | 新余 349 | 鹰潭 350 | 抚州 351 | 赣州 352 | 上饶 353 | 宜春 354 | 355 | 河北 356 | 357 | 张家口 358 | 邯郸 359 | 邢台 360 | 衡水 361 | 秦皇岛 362 | 廊坊 363 | 保定 364 | 承德 365 | 唐山 366 | 沧州 367 | 石家庄 368 | 369 | 河南 370 | 371 | 南阳 372 | 洛阳 373 | 三门峡 374 | 商丘 375 | 焦作 376 | 开封 377 | 驻马店 378 | 濮阳 379 | 许昌 380 | 安阳 381 | 信阳 382 | 漯河 383 | 平顶山 384 | 郑州 385 | 新乡 386 | 鹤壁 387 | 周口 388 | 389 | 浙江 390 | 391 | 丽水 392 | 舟山 393 | 宁波 394 | 衢州 395 | 温州 396 | 杭州 397 | 台州 398 | 嘉兴 399 | 绍兴 400 | 金华 401 | 湖州 402 | 403 | 海南 404 | 405 | 保亭 406 | 澄迈 407 | 南沙群岛 408 | 陵水黎族 409 | 中沙群岛 410 | 屯昌 411 | 昌江黎族 412 | 乐东黎族 413 | 琼海 414 | 儋州 415 | 文昌 416 | 万宁 417 | 白沙黎族 418 | 海口 419 | 三亚 420 | 五指山 421 | 琼中 422 | 东方 423 | 定安 424 | 西沙群岛 425 | 临高 426 | 427 | 湖北 428 | 429 | 咸宁 430 | 宜昌 431 | 黄冈 432 | 荆州 433 | 孝感 434 | 荆门 435 | 十堰 436 | 鄂州 437 | 天门 438 | 潜江 439 | 恩施 440 | 武汉 441 | 仙桃 442 | 神农架林 443 | 随州 444 | 黄石 445 | 襄樊 446 | 447 | 湖南 448 | 449 | 郴州 450 | 岳阳 451 | 怀化 452 | 娄底 453 | 张家界 454 | 益阳 455 | 湘西土家族苗族 456 | 常德 457 | 湘潭 458 | 永州 459 | 衡阳 460 | 株洲 461 | 长沙 462 | 邵阳 463 | 464 | 澳门 465 | 466 | 花地玛堂 467 | 圣安多尼堂 468 | 大堂 469 | 望德堂 470 | 风顺堂 471 | 嘉模堂 472 | 圣方济各堂 473 | 474 | 甘肃 475 | 476 | 兰州 477 | 金昌 478 | 甘南藏族 479 | 平凉 480 | 嘉峪关 481 | 天水 482 | 白银 483 | 武威 484 | 张掖 485 | 庆阳 486 | 定西 487 | 临夏回族 488 | 酒泉 489 | 陇南 490 | 491 | 福建 492 | 493 | 南平 494 | 厦门 495 | 福州 496 | 宁德 497 | 龙岩 498 | 莆田 499 | 漳州 500 | 泉州 501 | 三明 502 | 503 | 西藏 504 | 505 | 阿里地 506 | 拉萨 507 | 山南地 508 | 日喀则地 509 | 那曲地 510 | 昌都地 511 | 林芝地 512 | 513 | 贵州 514 | 515 | 毕节地 516 | 黔南 517 | 六盘水 518 | 黔东南 519 | 贵阳 520 | 遵义 521 | 铜仁地 522 | 黔西南 523 | 安顺 524 | 525 | 辽宁 526 | 527 | 铁岭 528 | 葫芦岛 529 | 营口 530 | 本溪 531 | 辽阳 532 | 盘锦 533 | 阜新 534 | 朝阳 535 | 锦州 536 | 抚顺 537 | 丹东 538 | 沈阳 539 | 鞍山 540 | 大连 541 | 542 | 重庆 543 | 544 | 开县 545 | 江北 546 | 沙坪坝 547 | 九龙坡 548 | 南岸 549 | 北碚 550 | 万盛 551 | 双桥 552 | 渝北 553 | 巴南 554 | 黔江 555 | 垫江 556 | 武隆 557 | 巫山 558 | 巫溪 559 | 云阳 560 | 奉节 561 | 忠县 562 | 梁平 563 | 璧山 564 | 荣昌 565 | 大足 566 | 铜梁 567 | 潼南 568 | 綦江 569 | 长寿 570 | 彭水 571 | 酉阳 572 | 合川 573 | 江津 574 | 南川 575 | 永川 576 | 丰都 577 | 城口 578 | 大渡口 579 | 渝中 580 | 涪陵 581 | 万州 582 | 石柱 583 | 秀山 584 | 585 | 陕西 586 | 587 | 咸阳 588 | 铜川 589 | 商洛 590 | 榆林 591 | 渭南 592 | 汉中 593 | 安康 594 | 延安 595 | 宝鸡 596 | 西安 597 | 598 | 青海 599 | 600 | 黄南 601 | 海东地 602 | 果洛 603 | 西宁 604 | 海北 605 | 玉树 606 | 海南 607 | 海西 608 | 609 | 香港 610 | 611 | 油尖旺 612 | 黄大仙 613 | 深水埗 614 | 观塘 615 | 九龙城 616 | 湾仔 617 | 葵青 618 | 离岛 619 | 中西 620 | 621 | 622 | 荃湾 623 | 元朗 624 | 沙田 625 | 西贡 626 | 屯门 627 | 大埔 628 | 629 | 630 | 黑龙江 631 | 632 | 鹤岗 633 | 大兴安岭地 634 | 大庆 635 | 七台河 636 | 齐齐哈尔 637 | 牡丹江 638 | 黑河 639 | 双鸭山 640 | 绥化 641 | 伊春 642 | 佳木斯 643 | 哈尔滨 644 | 鸡西 645 | 646 | 647 | 648 | -------------------------------------------------------------------------------- /Source/Picker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionTextField.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/16. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | import UIKit 31 | 32 | struct AssociatedDataModel { 33 | var key: String 34 | var valueArray: [String]? 35 | init (key: String, valueArray: [String]? = nil) { 36 | self.key = key 37 | self.valueArray = valueArray 38 | } 39 | } 40 | 41 | class Picker: UIView { 42 | let screenWidth = UIScreen.mainScreen().bounds.size.width 43 | let screenHeight = UIScreen.mainScreen().bounds.size.height 44 | 45 | // 使用模型初始化数据示例 46 | let associatedData: [[AssociatedDataModel]] = [ 47 | // 第一列数据 (key) 48 | [ AssociatedDataModel(key: "交通工具"), 49 | AssociatedDataModel(key: "食品"), 50 | AssociatedDataModel(key: "游戏") 51 | ], 52 | // 第二列数据 (valueArray) 53 | [ AssociatedDataModel(key: "交通工具", valueArray: ["陆地", "空中", "水上"]), 54 | AssociatedDataModel(key: "食品", valueArray: ["健康食品", "垃圾食品"]), 55 | AssociatedDataModel(key: "游戏", valueArray: ["益智游戏", "角色游戏"]), 56 | 57 | ], 58 | 59 | // 第三列数据 (valueArray) 60 | [ AssociatedDataModel(key: "陆地", valueArray: ["公交车", "小轿车", "自行车"]), 61 | AssociatedDataModel(key: "空中", valueArray: ["飞机"]), 62 | AssociatedDataModel(key: "水上", valueArray: ["轮船"]), 63 | AssociatedDataModel(key: "健康食品", valueArray: ["蔬菜", "水果"]), 64 | AssociatedDataModel(key: "垃圾食品", valueArray: ["辣条", "不健康小吃"]), 65 | AssociatedDataModel(key: "益智游戏", valueArray: ["消消乐", "消灭星星"]), 66 | AssociatedDataModel(key: "角色游戏", valueArray: ["lol", "cf"]) 67 | 68 | ] 69 | 70 | 71 | ] 72 | 73 | 74 | enum PickerStyles { 75 | case Single 76 | case Multiple 77 | case MultipleAssociated 78 | } 79 | 80 | 81 | var pickerStyle: PickerStyles = .Single 82 | 83 | // 完成按钮的响应Closure 84 | typealias BtnAction = () -> Void 85 | typealias SingleDoneAction = (selectedIndex: Int, selectedValue: String) -> Void 86 | typealias MultipleDoneAction = (selectedIndexs: [Int], selectedValues: [String]) -> Void 87 | 88 | private var cancelAction: BtnAction? = nil { 89 | didSet { 90 | tool.cancelAction = cancelAction 91 | } 92 | } 93 | //MARK:- 只有一列的时候用到的属性 94 | private var singleDoneOnClick:SingleDoneAction? = nil { 95 | didSet { 96 | tool.doneAction = {[unowned self] in 97 | 98 | self.singleDoneOnClick?(selectedIndex: self.selectedIndex, selectedValue: self.selectedValue) 99 | } 100 | } 101 | } 102 | 103 | private var defalultSelectedIndex: Int? = nil { 104 | didSet { 105 | if let defaultIndex = defalultSelectedIndex, singleData = singleColData {// 判断下标是否合法 106 | assert(defaultIndex >= 0 && defaultIndex < singleData.count, "设置的默认选中Index不合法") 107 | if defaultIndex >= 0 && defaultIndex < singleData.count { 108 | // 设置默认值 109 | selectedIndex = defaultIndex 110 | selectedValue = singleData[defaultIndex] 111 | // 滚动到默认位置 112 | pickerView.selectRow(defaultIndex, inComponent: 0, animated: false) 113 | 114 | } 115 | 116 | } else {// 没有默认值设置0行为默认值 117 | selectedIndex = 0 118 | selectedValue = singleColData![0] 119 | pickerView.selectRow(0, inComponent: 0, animated: false) 120 | 121 | } 122 | } 123 | } 124 | private var singleColData: [String]? = nil 125 | 126 | private var selectedIndex: Int = 0 127 | private var selectedValue: String = "" 128 | 129 | 130 | //MARK:- 有多列不关联的时候用到的属性 131 | var multipleDoneOnClick:MultipleDoneAction? = nil { 132 | didSet { 133 | 134 | tool.doneAction = {[unowned self] in 135 | self.multipleDoneOnClick?(selectedIndexs: self.selectedIndexs, selectedValues: self.selectedValues) 136 | } 137 | } 138 | } 139 | 140 | private var multipleColsData: [[String]]? = nil { 141 | didSet { 142 | if let multipleData = multipleColsData { 143 | for _ in multipleData.indices { 144 | selectedIndexs.append(0) 145 | selectedValues.append(" ") 146 | } 147 | 148 | } 149 | } 150 | } 151 | 152 | private var selectedIndexs: [Int] = [] 153 | private var selectedValues: [String] = [] 154 | 155 | private var defalultSelectedIndexs: [Int]? = nil { 156 | didSet { 157 | if let defaultIndexs = defalultSelectedIndexs { 158 | 159 | defaultIndexs.enumerate().forEach({ (component: Int, row: Int) in 160 | 161 | assert(component < pickerView.numberOfComponents && row < pickerView.numberOfRowsInComponent(component), "设置的默认选中Indexs有不合法的") 162 | if component < pickerView.numberOfComponents && row < pickerView.numberOfRowsInComponent(component){ 163 | 164 | // 滚动到默认位置 165 | pickerView.selectRow(row, inComponent: component, animated: false) 166 | 167 | // 设置默认值 168 | selectedIndexs[component] = row 169 | selectedValues[component] = self.pickerView(pickerView, titleForRow: row, forComponent: component) ?? " " 170 | 171 | 172 | } 173 | 174 | }) 175 | 176 | } else { 177 | multipleColsData?.indices.forEach({ (index) in 178 | // 滚动到默认位置 179 | pickerView.selectRow(0, inComponent: index, animated: false) 180 | 181 | // 设置默认选中值 182 | selectedIndexs[index] = 0 183 | 184 | selectedValues[index] = self.pickerView(pickerView, titleForRow: 0, forComponent: index) ?? " " 185 | 186 | }) 187 | } 188 | } 189 | } 190 | 191 | 192 | 193 | //MARK:- 有多列关联的时候用到的属性 194 | 195 | private var multipleAssociatedColsData: [[AssociatedDataModel]]? = nil { 196 | didSet { 197 | 198 | if let multipleAssociatedData = multipleAssociatedColsData { 199 | // 初始化选中的values 200 | for _ in multipleAssociatedData.indices { 201 | selectedIndexs.append(0) 202 | selectedValues.append(" ") 203 | } 204 | } 205 | } 206 | } 207 | 208 | // 设置第一组的数据, 使用数组是因为字典无序,需要设置默认选中值的时候获取到准确的下标滚动到相应的行 209 | private var defaultSelectedValues: [String]? = nil { 210 | didSet { 211 | 212 | if let defaultValues = defaultSelectedValues { 213 | // 设置默认值 214 | selectedValues = defaultValues 215 | defaultValues.enumerate().forEach { (component: Int, element: String) in 216 | var row: Int? = nil 217 | if component == 0 { 218 | let firstData = multipleAssociatedColsData![0] 219 | for (index,associatedModel) in firstData.enumerate() { 220 | if associatedModel.key == element { 221 | row = index 222 | } 223 | } 224 | } else { 225 | 226 | let associatedModels = multipleAssociatedColsData![component] 227 | var arr: [String]? 228 | 229 | for associatedModel in associatedModels { 230 | if associatedModel.key == selectedValues[component - 1] { 231 | arr = associatedModel.valueArray 232 | } 233 | } 234 | 235 | row = arr?.indexOf(element) 236 | 237 | } 238 | 239 | assert(row != nil, "第\(component)列设置的默认值有误") 240 | if row == nil { 241 | row = 0 242 | print("第\(component)列设置的默认值有误") 243 | } 244 | if component < pickerView.numberOfComponents { 245 | // print(" \(component) ----\(row!)") 246 | // 滚动到默认的位置 247 | pickerView.selectRow(row!, inComponent: component, animated: false) 248 | // 设置选中的下标 249 | selectedIndexs[component] = row! 250 | 251 | } 252 | 253 | } 254 | 255 | } else { 256 | multipleAssociatedColsData?.indices.forEach({ (index) in 257 | // 滚动到默认的位置 0 行 258 | pickerView.selectRow(0, inComponent: index, animated: false) 259 | // 设置默认的选中值 260 | selectedValues[index] = self.pickerView(pickerView, titleForRow: 0, forComponent: index) ?? " " 261 | selectedIndexs[index] = 0 262 | }) 263 | } 264 | 265 | } 266 | 267 | } 268 | 269 | 270 | 271 | private lazy var pickerView: UIPickerView! = { [unowned self] in 272 | let picker = UIPickerView() 273 | picker.delegate = self 274 | picker.dataSource = self 275 | picker.backgroundColor = UIColor.whiteColor() 276 | return picker 277 | }() 278 | 279 | private lazy var tool: ToolBarView! = ToolBarView() 280 | 281 | private let pickerViewHeight = 216.0 282 | private let toolBarHeight = 44.0 283 | 284 | let screenW = UIScreen.mainScreen().bounds.size.width 285 | 286 | //MARK:- 初始化 287 | init() { 288 | let frame = CGRect(x: 0.0, y: 0.0, width: Double(screenW), height: toolBarHeight + pickerViewHeight) 289 | super.init(frame: frame) 290 | commonInit() 291 | } 292 | 293 | required init?(coder aDecoder: NSCoder) { 294 | super.init(coder: aDecoder) 295 | commonInit() 296 | } 297 | 298 | deinit { 299 | print("\(self.debugDescription) --- 销毁") 300 | } 301 | 302 | func commonInit() { 303 | 304 | addSubview(tool) 305 | addSubview(pickerView) 306 | } 307 | 308 | override func layoutSubviews() { 309 | super.layoutSubviews() 310 | tool.frame = CGRect(x: 0.0, y: 0.0, width: Double(screenWidth), height: toolBarHeight) 311 | pickerView.frame = CGRect(x: 0.0, y: 44.0, width: Double(screenW), height: pickerViewHeight) 312 | } 313 | 314 | 315 | } 316 | 317 | extension Picker: UIPickerViewDelegate, UIPickerViewDataSource { 318 | func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int { 319 | 320 | switch pickerStyle { 321 | case .Single: 322 | return singleColData == nil ? 0 : 1 323 | case .Multiple: 324 | return multipleColsData?.count ?? 0 325 | case .MultipleAssociated: 326 | return multipleAssociatedColsData?.count ?? 0 327 | 328 | } 329 | 330 | } 331 | 332 | func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 333 | 334 | switch pickerStyle { 335 | case .Single: 336 | return singleColData?.count ?? 0 337 | case .Multiple: 338 | return multipleColsData?[component].count ?? 0 339 | case .MultipleAssociated: 340 | if let multipleAssociatedData = multipleAssociatedColsData { 341 | 342 | if component == 0 { 343 | return multipleAssociatedData[0].count 344 | }else { 345 | let associatedDataModels = multipleAssociatedData[component] 346 | var arr: [String]? 347 | 348 | for associatedDataModel in associatedDataModels { 349 | if associatedDataModel.key == selectedValues[component - 1] { 350 | arr = associatedDataModel.valueArray 351 | } 352 | } 353 | 354 | return arr?.count ?? 0 355 | 356 | } 357 | 358 | } 359 | 360 | return 0 361 | 362 | } 363 | 364 | } 365 | 366 | func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 367 | switch pickerStyle { 368 | case .Single: 369 | selectedIndex = row 370 | selectedValue = singleColData![row] 371 | case .Multiple: 372 | selectedIndexs[component] = row 373 | selectedValues[component] = self.pickerView(pickerView, titleForRow: row, forComponent: component) ?? " " 374 | case .MultipleAssociated: 375 | // 设置选中值 376 | selectedValues[component] = self.pickerView(pickerView, titleForRow: row, forComponent: component) ?? " " 377 | selectedIndexs[component] = row 378 | // 更新下一列关联的值 379 | if component < multipleAssociatedColsData!.count - 1 { 380 | pickerView.reloadComponent(component + 1) 381 | // 递归 382 | self.pickerView(pickerView, didSelectRow: 0, inComponent: component+1) 383 | pickerView.selectRow(0, inComponent: component+1, animated: true) 384 | 385 | } 386 | 387 | } 388 | 389 | 390 | } 391 | func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 392 | switch pickerStyle { 393 | case .Single: 394 | return singleColData?[row] 395 | case .Multiple: 396 | return multipleColsData?[component][row] 397 | case .MultipleAssociated: 398 | 399 | if let multipleAssociatedData = multipleAssociatedColsData { 400 | 401 | if component == 0 { 402 | return multipleAssociatedData[0][row].key 403 | }else { 404 | let associatedDataModels = multipleAssociatedData[component] 405 | var arr: [String]? 406 | 407 | for associatedDataModel in associatedDataModels { 408 | if associatedDataModel.key == selectedValues[component - 1] { 409 | arr = associatedDataModel.valueArray 410 | } 411 | } 412 | if arr?.count == 0 {// 空数组 413 | return nil 414 | } 415 | return arr?[row] 416 | 417 | } 418 | 419 | } 420 | 421 | return nil 422 | 423 | } 424 | } 425 | } 426 | 427 | //MARK: 快速使用方法 428 | extension Picker { 429 | 430 | /// 单列 431 | class func singleColPicker(singleColData: [String], defaultIndex: Int?,cancelAction: BtnAction?, doneAction: SingleDoneAction?) -> Picker { 432 | let pic = Picker() 433 | 434 | pic.pickerStyle = .Single 435 | pic.singleColData = singleColData 436 | pic.defalultSelectedIndex = defaultIndex 437 | 438 | pic.singleDoneOnClick = doneAction 439 | pic.cancelAction = cancelAction 440 | return pic 441 | 442 | } 443 | 444 | /// 多列不关联 445 | class func multipleCosPicker(multipleColsData: [[String]], defaultSelectedIndexs: [Int]?,cancelAction: BtnAction?, doneAction: MultipleDoneAction?) -> Picker { 446 | 447 | let pic = Picker() 448 | 449 | pic.pickerStyle = .Multiple 450 | 451 | pic.multipleColsData = multipleColsData 452 | pic.defalultSelectedIndexs = defaultSelectedIndexs 453 | pic.cancelAction = cancelAction 454 | pic.multipleDoneOnClick = doneAction 455 | return pic 456 | 457 | } 458 | 459 | /// 多列关联 460 | class func multipleAssociatedCosPicker(multipleAssociatedColsData: [[AssociatedDataModel]], defaultSelectedValues: [String]?,cancelAction: BtnAction?, doneAction: MultipleDoneAction?) -> Picker { 461 | 462 | let pic = Picker() 463 | pic.pickerStyle = .MultipleAssociated 464 | pic.multipleAssociatedColsData = multipleAssociatedColsData 465 | 466 | pic.defaultSelectedValues = defaultSelectedValues 467 | pic.cancelAction = cancelAction 468 | pic.multipleDoneOnClick = doneAction 469 | return pic 470 | 471 | } 472 | 473 | /// 城市选择器 474 | class func citiesPicker(defaultSelectedValues: [String]?, cancelAction: BtnAction?, doneAction: MultipleDoneAction?) -> Picker { 475 | 476 | let provincePath = NSBundle.mainBundle().pathForResource("Province", ofType: "plist") 477 | let cityPath = NSBundle.mainBundle().pathForResource("City", ofType: "plist") 478 | let areaPath = NSBundle.mainBundle().pathForResource("Area", ofType: "plist") 479 | 480 | let proviceArr = NSArray(contentsOfFile: provincePath!) 481 | let cityArr = NSDictionary(contentsOfFile: cityPath!) 482 | let areaArr = NSDictionary(contentsOfFile: areaPath!) 483 | 484 | var provinceModelArr: [AssociatedDataModel] = [] 485 | var citiesModelArr: [AssociatedDataModel] = [] 486 | var areasModelArr: [AssociatedDataModel] = [] 487 | 488 | proviceArr?.forEach({ (element) in 489 | if let province = element as? String { 490 | provinceModelArr.append(AssociatedDataModel(key: province)) 491 | 492 | let citys = cityArr?[province] as? [String] 493 | citiesModelArr.append(AssociatedDataModel(key: province, valueArray: citys)) 494 | 495 | citys?.forEach({ (element) in 496 | let city = element 497 | let areas = areaArr?[city]as? [String] 498 | areasModelArr.append(AssociatedDataModel(key: city, valueArray: areas)) 499 | 500 | }) 501 | } 502 | }) 503 | 504 | let citiesArr = [provinceModelArr, citiesModelArr, areasModelArr] 505 | 506 | 507 | let pic = Picker.multipleAssociatedCosPicker(citiesArr, defaultSelectedValues: defaultSelectedValues, cancelAction: cancelAction, doneAction: doneAction) 508 | return pic 509 | 510 | } 511 | } -------------------------------------------------------------------------------- /Source/Province.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 北京 6 | 上海 7 | 天津 8 | 重庆 9 | 浙江 10 | 广东 11 | 江苏 12 | 山东 13 | 福建 14 | 安徽 15 | 四川 16 | 湖北 17 | 河北 18 | 云南 19 | 黑龙江 20 | 吉林 21 | 辽宁 22 | 海南 23 | 湖南 24 | 河南 25 | 贵州 26 | 江西 27 | 广西 28 | 陕西 29 | 山西 30 | 青海 31 | 宁夏 32 | 甘肃 33 | 西藏 34 | 内蒙古 35 | 新疆 36 | 台湾 37 | 香港 38 | 澳门 39 | 国外 40 | 41 | 42 | -------------------------------------------------------------------------------- /Source/SelectionTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionTextField.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/16. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | 31 | import UIKit 32 | 33 | public class SelectionTextField: UITextField { 34 | 35 | public typealias BtnAction = () -> Void 36 | public typealias SingleDoneAction = (textField: UITextField, selectedIndex: Int, selectedValue: String) -> Void 37 | public typealias MultipleDoneAction = (textField: UITextField, selectedIndexs: [Int], selectedValues: [String]) -> Void 38 | public typealias DateDoneAction = (textField: UITextField, selectedDate: NSDate) -> Void 39 | 40 | public typealias MultipleAssociatedDataType = [[[String: [String]?]]] 41 | 42 | /// 保存pickerView的初始化 43 | private var setUpPickerClosure:(() -> PickerView)? 44 | /// 如果设置了autoSetSelectedText为true 将自动设置text的值, 默认以空格分开多列选择, 但你仍然可以在响应完成的closure中修改text的值 45 | private var autoSetSelectedText = false 46 | 47 | //MARK: 初始化 48 | override public init(frame: CGRect) { 49 | super.init(frame: frame) 50 | commonInit() 51 | 52 | } 53 | // 从xib或storyboard中初始化时候调用 54 | required public init?(coder aDecoder: NSCoder) { 55 | super.init(coder: aDecoder) 56 | commonInit() 57 | } 58 | 59 | deinit { 60 | NSNotificationCenter.defaultCenter().removeObserver(self) 61 | print("\(self.debugDescription) --- 销毁") 62 | } 63 | 64 | 65 | } 66 | 67 | // MARK: - 监听通知 68 | extension SelectionTextField { 69 | 70 | private func commonInit() { 71 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.didBeginEdit), name: UITextFieldTextDidBeginEditingNotification, object: self) 72 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.didEndEdit), name: UITextFieldTextDidEndEditingNotification, object: self) 73 | } 74 | // 开始编辑添加pickerView 75 | func didBeginEdit() { 76 | let pickerView = setUpPickerClosure?() 77 | pickerView?.delegate = self 78 | inputView = pickerView 79 | } 80 | // 编辑完成销毁pickerView 81 | func didEndEdit() { 82 | inputView = nil 83 | } 84 | 85 | override public func caretRectForPosition(position: UITextPosition) -> CGRect { 86 | return CGRectZero 87 | } 88 | 89 | } 90 | 91 | // MARK: - 使用方法 92 | extension SelectionTextField { 93 | 94 | /// 单列选择器 /// @author ZeroJ, 16-04-23 18:04:59 95 | /// 96 | /// - parameter title: 标题 97 | /// - parameter data: 数据 98 | /// - parameter defaultSeletedIndex: 默认选中的行数 99 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 100 | /// - parameter doneAction: 响应完成的Closure 101 | /// 102 | /// - returns: 103 | public func showSingleColPicker(toolBarTitle: String, data: [String], defaultSelectedIndex: Int?,autoSetSelectedText: Bool, doneAction: SingleDoneAction?) { 104 | 105 | self.autoSetSelectedText = autoSetSelectedText 106 | 107 | // 保存在这个closure中, 在开始编辑的时候在执行, 避免像之前在这里直接初始化pickerView, 每个SelectionTextField在调用这个方法的时候就初始化pickerView,当有多个pickerView的时候就很消耗内存 108 | setUpPickerClosure = {() -> PickerView in 109 | 110 | return PickerView.singleColPicker(toolBarTitle, singleColData: data, defaultIndex: defaultSelectedIndex, cancelAction: {[unowned self] in 111 | 112 | self.endEditing(true) 113 | 114 | }, doneAction: {[unowned self] (selectedIndex: Int, selectedValue: String) -> Void in 115 | 116 | doneAction?(textField:self, selectedIndex: selectedIndex, selectedValue: selectedValue) 117 | self.endEditing(true) 118 | 119 | }) 120 | 121 | 122 | } 123 | 124 | } 125 | 126 | /// 多列不关联选择器 /// @author ZeroJ, 16-04-23 18:04:59 127 | /// 128 | /// - parameter title: 标题 129 | /// - parameter data: 数据 130 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 131 | /// - parameter autoSetSelectedText: 设置为true的时候, 将俺默认的格式自动设置textField的值 132 | /// - parameter doneAction: 响应完成的Closure 133 | /// 134 | /// - returns: 135 | public func showMultipleColsPicker(toolBarTitle: String, data: [[String]], defaultSelectedIndexs: [Int]?, autoSetSelectedText: Bool, doneAction: MultipleDoneAction?) { 136 | self.autoSetSelectedText = autoSetSelectedText 137 | 138 | setUpPickerClosure = {() -> PickerView in 139 | 140 | return PickerView.multipleCosPicker(toolBarTitle, multipleColsData: data, defaultSelectedIndexs: defaultSelectedIndexs, cancelAction: { [unowned self] in 141 | 142 | self.endEditing(true) 143 | 144 | }, doneAction:{[unowned self] (selectedIndexs: [Int], selectedValues: [String]) -> Void in 145 | 146 | doneAction?(textField:self, selectedIndexs: selectedIndexs, selectedValues: selectedValues) 147 | self.endEditing(true) 148 | }) 149 | } 150 | 151 | } 152 | 153 | /// 多列关联选择器 /// @author ZeroJ, 16-04-23 18:04:59 154 | /// 155 | /// - parameter title: 标题 156 | /// - parameter data: 数据, 数据的格式参照示例 157 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 158 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 159 | /// - parameter doneAction: 响应完成的Closure 160 | /// 161 | /// - returns: 162 | public func showMultipleAssociatedColsPicker(toolBarTitle: String, data: MultipleAssociatedDataType, defaultSelectedValues: [String]?,autoSetSelectedText: Bool, doneAction: MultipleDoneAction?) { 163 | self.autoSetSelectedText = autoSetSelectedText 164 | 165 | setUpPickerClosure = {() -> PickerView in 166 | 167 | return PickerView.multipleAssociatedCosPicker(toolBarTitle, multipleAssociatedColsData: data, defaultSelectedValues: defaultSelectedValues,cancelAction: { [unowned self] in 168 | 169 | self.endEditing(true) 170 | 171 | }, doneAction:{[unowned self] (selectedIndexs: [Int], selectedValues: [String]) -> Void in 172 | 173 | doneAction?(textField:self, selectedIndexs: selectedIndexs, selectedValues: selectedValues) 174 | self.endEditing(true) 175 | }) 176 | } 177 | 178 | } 179 | 180 | 181 | /// 城市选择器 /// @author ZeroJ, 16-04-23 18:04:59 182 | /// 183 | /// - parameter title: 标题 184 | /// - parameter defaultSeletedValues: 默认选中的每一列的值, 注意不是行数 185 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 186 | /// - parameter doneAction: 响应完成的Closure 187 | /// 188 | /// - returns: 189 | 190 | public func showCitiesPicker(toolBarTitle: String, defaultSelectedValues: [String]?,autoSetSelectedText: Bool, doneAction: MultipleDoneAction?) { 191 | self.autoSetSelectedText = autoSetSelectedText 192 | 193 | setUpPickerClosure = {() -> PickerView in 194 | return PickerView.citiesPicker(toolBarTitle, defaultSelectedValues: defaultSelectedValues, cancelAction: { [unowned self] in 195 | self.endEditing(true) 196 | 197 | }, doneAction:{[unowned self] (selectedIndexs: [Int], selectedValues: [String]) -> Void in 198 | 199 | doneAction?(textField:self,selectedIndexs: selectedIndexs, selectedValues: selectedValues) 200 | self.endEditing(true) 201 | }) 202 | } 203 | 204 | } 205 | 206 | /// 日期选择器 /// @author ZeroJ, 16-04-23 18:04:59 207 | /// 208 | /// - parameter title: 标题 209 | /// - parameter datePickerSetting: 可配置UIDatePicker的样式 210 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 211 | /// - parameter doneAction: 响应完成的Closure 212 | /// 213 | /// - returns: 214 | public func showDatePicker(toolBarTitle: String, datePickerSetting: DatePickerSetting = DatePickerSetting(), autoSetSelectedText: Bool, doneAction: DateDoneAction?) { 215 | self.autoSetSelectedText = autoSetSelectedText 216 | 217 | setUpPickerClosure = {() -> PickerView in 218 | return PickerView.datePicker(toolBarTitle, datePickerSetting: datePickerSetting, cancelAction: { [unowned self] in 219 | self.endEditing(true) 220 | 221 | }, doneAction: {[unowned self] (selectedDate) in 222 | doneAction?(textField:self, selectedDate: selectedDate) 223 | self.endEditing(true) 224 | 225 | }) 226 | 227 | } 228 | } 229 | 230 | } 231 | 232 | 233 | // MARK: - PickerViewDelegate -- 如果设置了autoSetSelectedText为true 这些代理方法中将以默认的格式自动设置textField的值 234 | extension SelectionTextField: PickerViewDelegate { 235 | public func singleColDidSelecte(selectedIndex: Int, selectedValue: String) { 236 | if autoSetSelectedText { 237 | text = " " + selectedValue 238 | } 239 | } 240 | 241 | public func multipleColsDidSelecte(selectedIndexs: [Int], selectedValues: [String]) { 242 | 243 | 244 | if autoSetSelectedText { 245 | text = selectedValues.reduce("", combine: { (result, selectedValue) -> String in 246 | result + " " + selectedValue 247 | }) 248 | } 249 | } 250 | 251 | public func dateDidSelecte(selectedDate: NSDate) { 252 | if autoSetSelectedText { 253 | 254 | let formatter = NSDateFormatter() 255 | formatter.dateFormat = "yyyy-MM-dd" 256 | let string = formatter.stringFromDate(selectedDate) 257 | text = " " + string 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /Source/ToolBarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToolBarView.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/18. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | 31 | import UIKit 32 | 33 | public class ToolBarView: UIView { 34 | 35 | typealias CustomClosures = (titleLabel: UILabel, cancleBtn: UIButton, doneBtn: UIButton) -> Void 36 | public typealias BtnAction = () -> Void 37 | 38 | public var title = "请选择" { 39 | didSet { 40 | titleLabel.text = title 41 | } 42 | } 43 | 44 | public var doneAction: BtnAction? 45 | public var cancelAction: BtnAction? 46 | // 用来产生上下分割线的效果 47 | private lazy var contentView: UIView = { 48 | let content = UIView() 49 | content.backgroundColor = UIColor.whiteColor() 50 | return content 51 | }() 52 | // 文本框 53 | private lazy var titleLabel: UILabel = { 54 | let label = UILabel() 55 | label.textColor = UIColor.blackColor() 56 | label.textAlignment = .Center 57 | return label 58 | }() 59 | 60 | // 取消按钮 61 | private lazy var cancleBtn: UIButton = { 62 | let btn = UIButton() 63 | btn.setTitle("取消", forState: .Normal) 64 | btn.setTitleColor(UIColor.blackColor(), forState: .Normal) 65 | return btn 66 | }() 67 | 68 | // 完成按钮 69 | private lazy var doneBtn: UIButton = { 70 | let donebtn = UIButton() 71 | donebtn.setTitle("完成", forState: .Normal) 72 | donebtn.setTitleColor(UIColor.blackColor(), forState: .Normal) 73 | return donebtn 74 | }() 75 | 76 | override public init(frame: CGRect) { 77 | super.init(frame: frame) 78 | commonInit() 79 | 80 | } 81 | 82 | required public init?(coder aDecoder: NSCoder) { 83 | fatalError("init(coder:) has not been implemented") 84 | } 85 | 86 | private func commonInit() { 87 | backgroundColor = UIColor.lightTextColor() 88 | addSubview(contentView) 89 | contentView.addSubview(cancleBtn) 90 | contentView.addSubview(doneBtn) 91 | contentView.addSubview(titleLabel) 92 | 93 | doneBtn.addTarget(self, action: #selector(self.doneBtnOnClick(_:)), forControlEvents: .TouchUpInside) 94 | cancleBtn.addTarget(self, action: #selector(self.cancelBtnOnClick(_:)), forControlEvents: .TouchUpInside) 95 | } 96 | 97 | func doneBtnOnClick(sender: UIButton) { 98 | doneAction?() 99 | } 100 | func cancelBtnOnClick(sender: UIButton) { 101 | cancelAction?() 102 | 103 | } 104 | 105 | override public func layoutSubviews() { 106 | super.layoutSubviews() 107 | let margin = 15.0 108 | let contentHeight = Double(bounds.size.height) - 2.0 109 | contentView.frame = CGRect(x: 0.0, y: 1.0, width: Double(bounds.size.width), height: contentHeight) 110 | let btnWidth = contentHeight 111 | 112 | cancleBtn.frame = CGRect(x: margin, y: 0.0, width: btnWidth, height: btnWidth) 113 | doneBtn.frame = CGRect(x: Double(bounds.size.width) - btnWidth - margin, y: 0.0, width: btnWidth, height: btnWidth) 114 | let titleX = Double(CGRectGetMaxX(cancleBtn.frame)) + margin 115 | let titleW = Double(bounds.size.width) - titleX - btnWidth - margin 116 | 117 | 118 | titleLabel.frame = CGRect(x: titleX, y: 0.0, width: titleW, height: btnWidth) 119 | } 120 | 121 | 122 | } 123 | -------------------------------------------------------------------------------- /Source/UsefulPickerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UsefulPickerView.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/16. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | 31 | import UIKit 32 | 33 | public class UsefulPickerView: UIView { 34 | 35 | public typealias BtnAction = () -> Void 36 | public typealias SingleDoneAction = (selectedIndex: Int, selectedValue: String) -> Void 37 | public typealias MultipleDoneAction = (selectedIndexs: [Int], selectedValues: [String]) -> Void 38 | public typealias DateDoneAction = (selectedDate: NSDate) -> Void 39 | 40 | public typealias MultipleAssociatedDataType = [[[String: [String]?]]] 41 | 42 | private var pickerView: PickerView! 43 | //MARK:- 常量 44 | private let pickerViewHeight:CGFloat = 260.0 45 | 46 | private let screenWidth = UIScreen.mainScreen().bounds.size.width 47 | private let screenHeight = UIScreen.mainScreen().bounds.size.height 48 | private var hideFrame: CGRect { 49 | return CGRect(x: 0.0, y: screenHeight, width: screenWidth, height: pickerViewHeight) 50 | } 51 | private var showFrame: CGRect { 52 | return CGRect(x: 0.0, y: screenHeight - pickerViewHeight, width: screenWidth, height: pickerViewHeight) 53 | } 54 | 55 | /// 使用NSArray 可以存任何"东西", 如果使用 [Any], 那么当 56 | /// let a = ["1", "2"] var b:[Any] = a 会报错 57 | 58 | // MARK:- 初始化 59 | // 单列 60 | convenience init(frame: CGRect, toolBarTitle: String, singleColData: [String], defaultSelectedIndex: Int?, doneAction: SingleDoneAction?) { 61 | 62 | self.init(frame: frame) 63 | 64 | 65 | 66 | pickerView = PickerView.singleColPicker(toolBarTitle, singleColData: singleColData, defaultIndex: defaultSelectedIndex, cancelAction: {[unowned self] in 67 | // 点击取消的时候移除 68 | self.hidePicker() 69 | 70 | }, doneAction: {[unowned self] (selectedIndex, selectedValue) in 71 | doneAction?(selectedIndex: selectedIndex, selectedValue: selectedValue) 72 | self.hidePicker() 73 | 74 | }) 75 | pickerView.frame = hideFrame 76 | addSubview(pickerView) 77 | 78 | // 点击背景移除self 79 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 80 | addGestureRecognizer(tap) 81 | 82 | } 83 | // 多列不关联 84 | convenience init(frame: CGRect, toolBarTitle: String, multipleColsData: [[String]], defaultSelectedIndexs: [Int]?, doneAction: MultipleDoneAction?) { 85 | 86 | self.init(frame: frame) 87 | 88 | pickerView = PickerView.multipleCosPicker(toolBarTitle, multipleColsData: multipleColsData, defaultSelectedIndexs: defaultSelectedIndexs, cancelAction: {[unowned self] in 89 | // 点击取消的时候移除 90 | self.hidePicker() 91 | 92 | }, doneAction: {[unowned self] (selectedIndexs, selectedValues) in 93 | doneAction?(selectedIndexs: selectedIndexs, selectedValues: selectedValues) 94 | self.hidePicker() 95 | }) 96 | pickerView.frame = hideFrame 97 | addSubview(pickerView) 98 | 99 | // 点击背景移除self 100 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 101 | addGestureRecognizer(tap) 102 | 103 | } 104 | // 多列关联 105 | convenience init(frame: CGRect, toolBarTitle: String, multipleAssociatedColsData: MultipleAssociatedDataType, defaultSelectedValues: [String]?, doneAction: MultipleDoneAction?) { 106 | 107 | self.init(frame: frame) 108 | 109 | pickerView = PickerView.multipleAssociatedCosPicker(toolBarTitle, multipleAssociatedColsData: multipleAssociatedColsData, defaultSelectedValues: defaultSelectedValues, cancelAction: {[unowned self] in 110 | // 点击取消的时候移除 111 | self.hidePicker() 112 | 113 | }, doneAction: {[unowned self] (selectedIndexs, selectedValues) in 114 | doneAction?(selectedIndexs: selectedIndexs, selectedValues: selectedValues) 115 | self.hidePicker() 116 | }) 117 | 118 | pickerView.frame = hideFrame 119 | addSubview(pickerView) 120 | 121 | // 点击背景移除self 122 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 123 | addGestureRecognizer(tap) 124 | 125 | } 126 | // 城市选择器 127 | convenience init(frame: CGRect, toolBarTitle: String, defaultSelectedValues: [String]?, doneAction: MultipleDoneAction?) { 128 | 129 | self.init(frame: frame) 130 | 131 | pickerView = PickerView.citiesPicker(toolBarTitle, defaultSelectedValues: defaultSelectedValues, cancelAction: {[unowned self] in 132 | // 点击取消的时候移除 133 | self.hidePicker() 134 | 135 | }, doneAction: {[unowned self] (selectedIndexs, selectedValues) in 136 | doneAction?(selectedIndexs: selectedIndexs, selectedValues: selectedValues) 137 | self.hidePicker() 138 | }) 139 | 140 | pickerView.frame = hideFrame 141 | addSubview(pickerView) 142 | 143 | // 点击背景移除self 144 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 145 | addGestureRecognizer(tap) 146 | 147 | } 148 | // 日期选择器 149 | convenience init(frame: CGRect, toolBarTitle: String, datePickerSetting: DatePickerSetting, doneAction: DateDoneAction?) { 150 | 151 | self.init(frame: frame) 152 | 153 | pickerView = PickerView.datePicker(toolBarTitle, datePickerSetting: datePickerSetting, cancelAction: {[unowned self] in 154 | // 点击取消的时候移除 155 | self.hidePicker() 156 | 157 | }, doneAction: {[unowned self] (selectedDate) in 158 | doneAction?(selectedDate: selectedDate) 159 | self.hidePicker() 160 | }) 161 | 162 | pickerView.frame = hideFrame 163 | addSubview(pickerView) 164 | 165 | // 点击背景移除self 166 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 167 | addGestureRecognizer(tap) 168 | 169 | } 170 | 171 | override init(frame: CGRect) { 172 | super.init(frame: frame) 173 | addOrentationObserver() 174 | } 175 | 176 | 177 | required public init?(coder aDecoder: NSCoder) { 178 | fatalError("init(coder:) has not been implemented") 179 | } 180 | 181 | deinit { 182 | NSNotificationCenter.defaultCenter().removeObserver(self) 183 | print("\(self.debugDescription) --- 销毁") 184 | } 185 | 186 | 187 | } 188 | 189 | // MARK:- selector 190 | extension UsefulPickerView { 191 | 192 | private func addOrentationObserver() { 193 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.statusBarOrientationChange), name: UIApplicationDidChangeStatusBarOrientationNotification, object: nil) 194 | 195 | } 196 | // 屏幕旋转时移除pickerView 197 | func statusBarOrientationChange() { 198 | removeFromSuperview() 199 | } 200 | func tapAction(tap: UITapGestureRecognizer) { 201 | let location = tap.locationInView(self) 202 | // 点击空白背景移除self 203 | if location.y <= screenHeight - pickerViewHeight { 204 | self.hidePicker() 205 | } 206 | } 207 | } 208 | 209 | // MARK:- 弹出和移除self 210 | extension UsefulPickerView { 211 | 212 | private func showPicker() { 213 | // 通过window 弹出view 214 | let window = UIApplication.sharedApplication().keyWindow 215 | guard let currentWindow = window else { return } 216 | currentWindow.addSubview(self) 217 | 218 | // let pickerX = NSLayoutConstraint(item: self, attribute: .Leading, relatedBy: .Equal, toItem: currentWindow, attribute: .Leading, multiplier: 1.0, constant: 0.0) 219 | // 220 | // let pickerY = NSLayoutConstraint(item: self, attribute: .Top, relatedBy: .Equal, toItem: currentWindow, attribute: .Top, multiplier: 1.0, constant: 0.0) 221 | // let pickerW = NSLayoutConstraint(item: self, attribute: .Width, relatedBy: .Equal, toItem: currentWindow, attribute: .Width, multiplier: 1.0, constant: 0.0) 222 | // let pickerH = NSLayoutConstraint(item: self, attribute: .Height, relatedBy: .Equal, toItem: currentWindow, attribute: .Height, multiplier: 1.0, constant: 0.0) 223 | // self.translatesAutoresizingMaskIntoConstraints = false 224 | // 225 | // currentWindow.addConstraints([pickerX, pickerY, pickerW, pickerH]) 226 | 227 | UIView.animateWithDuration(0.25, animations: {[unowned self] in 228 | self.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.1) 229 | self.pickerView.frame = self.showFrame 230 | }, completion: nil) 231 | 232 | 233 | } 234 | 235 | func hidePicker() { 236 | // 把self从window中移除 237 | UIView.animateWithDuration(0.25, animations: { [unowned self] in 238 | self.backgroundColor = UIColor.clearColor() 239 | self.pickerView.frame = self.hideFrame 240 | 241 | }) {[unowned self] (_) in 242 | self.removeFromSuperview() 243 | } 244 | } 245 | } 246 | 247 | // MARK: - 快速使用方法 248 | extension UsefulPickerView { 249 | 250 | /// 单列选择器 /// @author ZeroJ, 16-04-23 18:04:59 251 | /// 252 | /// - parameter title: 标题 253 | /// - parameter data: 数据 254 | /// - parameter defaultSeletedIndex: 默认选中的行数 255 | /// - parameter doneAction: 响应完成的Closure 256 | /// 257 | /// - returns: 258 | public class func showSingleColPicker(toolBarTitle: String, data: [String], defaultSelectedIndex: Int?, doneAction: SingleDoneAction?) { 259 | let window = UIApplication.sharedApplication().keyWindow 260 | guard let currentWindow = window else { return } 261 | 262 | let testView = UsefulPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, singleColData: data,defaultSelectedIndex: defaultSelectedIndex ,doneAction: doneAction) 263 | 264 | testView.showPicker() 265 | 266 | } 267 | 268 | /// 多列不关联选择器 /// @author ZeroJ, 16-04-23 18:04:59 269 | /// 270 | /// - parameter title: 标题 271 | /// - parameter data: 数据 272 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 273 | /// - parameter doneAction: 响应完成的Closure 274 | /// 275 | /// - returns: 276 | public class func showMultipleColsPicker(toolBarTitle: String, data: [[String]], defaultSelectedIndexs: [Int]?,doneAction: MultipleDoneAction?) { 277 | let window = UIApplication.sharedApplication().keyWindow 278 | guard let currentWindow = window else { return } 279 | 280 | let testView = UsefulPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, multipleColsData: data, defaultSelectedIndexs: defaultSelectedIndexs, doneAction: doneAction) 281 | 282 | testView.showPicker() 283 | 284 | } 285 | 286 | /// 多列关联选择器 /// @author ZeroJ, 16-04-23 18:04:59 287 | /// 288 | /// - parameter title: 标题 289 | /// - parameter data: 数据, 数据的格式参照示例 290 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 291 | /// - parameter doneAction: 响应完成的Closure 292 | /// 293 | /// - returns: 294 | public class func showMultipleAssociatedColsPicker(toolBarTitle: String, data: MultipleAssociatedDataType, defaultSelectedValues: [String]?, doneAction: MultipleDoneAction?) { 295 | let window = UIApplication.sharedApplication().keyWindow 296 | guard let currentWindow = window else { return } 297 | 298 | let testView = UsefulPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, multipleAssociatedColsData: data, defaultSelectedValues: defaultSelectedValues, doneAction: doneAction) 299 | 300 | testView.showPicker() 301 | 302 | } 303 | 304 | 305 | /// 城市选择器 /// @author ZeroJ, 16-04-23 18:04:59 306 | /// 307 | /// - parameter title: 标题 308 | /// - parameter defaultSeletedValues: 默认选中的每一列的值, 注意不是行数 309 | /// - parameter doneAction: 响应完成的Closure 310 | /// 311 | /// - returns: 312 | public class func showCitiesPicker(toolBarTitle: String, defaultSelectedValues: [String]?, doneAction: MultipleDoneAction?) { 313 | 314 | let window = UIApplication.sharedApplication().keyWindow 315 | guard let currentWindow = window else { return } 316 | 317 | let testView = UsefulPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, defaultSelectedValues: defaultSelectedValues, doneAction: doneAction) 318 | 319 | testView.showPicker() 320 | 321 | } 322 | 323 | /// 日期选择器 /// @author ZeroJ, 16-04-23 18:04:59 324 | /// 325 | /// - parameter title: 标题 326 | /// - parameter datePickerSetting: 可配置UIDatePicker的样式 327 | /// - parameter doneAction: 响应完成的Closure 328 | /// 329 | /// - returns: 330 | public class func showDatePicker(toolBarTitle: String, datePickerSetting: DatePickerSetting = DatePickerSetting(), doneAction: DateDoneAction?) { 331 | 332 | let window = UIApplication.sharedApplication().keyWindow 333 | guard let currentWindow = window else { return } 334 | 335 | let testView = UsefulPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, datePickerSetting: datePickerSetting, doneAction: doneAction) 336 | 337 | testView.showPicker() 338 | 339 | } 340 | 341 | } 342 | 343 | 344 | -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/project.xcworkspace/xcuserdata/jasnig.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnig/UsefulPickerView/4a3075d3c22b4845d31c3a69f048290d68717b3f/UsefulPickerVIew.xcodeproj/project.xcworkspace/xcuserdata/jasnig.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/project.xcworkspace/xcuserdata/shiweifu.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnig/UsefulPickerView/4a3075d3c22b4845d31c3a69f048290d68717b3f/UsefulPickerVIew.xcodeproj/project.xcworkspace/xcuserdata/shiweifu.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/project.xcworkspace/xcuserdata/zeroj.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnig/UsefulPickerView/4a3075d3c22b4845d31c3a69f048290d68717b3f/UsefulPickerVIew.xcodeproj/project.xcworkspace/xcuserdata/zeroj.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/xcuserdata/jasnig.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/xcuserdata/jasnig.xcuserdatad/xcschemes/UsefulPickerVIew.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/xcuserdata/jasnig.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | UsefulPickerVIew.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 4B0BCBFC1CCE6A1B0000FE60 16 | 17 | primary 18 | 19 | 20 | 4B0BCC101CCE6A1B0000FE60 21 | 22 | primary 23 | 24 | 25 | 4B0BCC1B1CCE6A1B0000FE60 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/xcuserdata/shiweifu.xcuserdatad/xcschemes/UsefulPickerVIew.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/xcuserdata/shiweifu.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | UsefulPickerVIew.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 4B0BCBFC1CCE6A1B0000FE60 16 | 17 | primary 18 | 19 | 20 | 4B0BCC101CCE6A1B0000FE60 21 | 22 | primary 23 | 24 | 25 | 4B0BCC1B1CCE6A1B0000FE60 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/xcuserdata/zeroj.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/xcuserdata/zeroj.xcuserdatad/xcschemes/UsefulPickerVIew.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /UsefulPickerVIew.xcodeproj/xcuserdata/zeroj.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | UsefulPickerVIew.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 4B0BCBFC1CCE6A1B0000FE60 16 | 17 | primary 18 | 19 | 20 | 4B0BCC101CCE6A1B0000FE60 21 | 22 | primary 23 | 24 | 25 | 4B0BCC1B1CCE6A1B0000FE60 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /UsefulPickerVIewTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /UsefulPickerVIewTests/UsefulPickerVIewTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UsefulPickerVIewTests.swift 3 | // UsefulPickerVIewTests 4 | // 5 | // Created by jasnig on 16/4/25. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import UsefulPickerVIew 11 | 12 | class UsefulPickerVIewTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /UsefulPickerVIewUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /UsefulPickerVIewUITests/UsefulPickerVIewUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UsefulPickerVIewUITests.swift 3 | // UsefulPickerVIewUITests 4 | // 5 | // Created by jasnig on 16/4/25. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class UsefulPickerVIewUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /UsefulPickerView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "UsefulPickerView" 3 | s.version = "0.1.2" 4 | s.summary = "UsefulPickerView is written in Swift and it is useful." 5 | s.homepage = "https://github.com/jasnig/UsefulPickerView" 6 | s.license = { :type => "MIT" } 7 | s.authors = { "ZeroJ" => "854136959@qq.com" } 8 | 9 | s.requires_arc = true 10 | s.platform = :ios 11 | s.platform = :ios, "8.0" 12 | s.source = { :git => "https://github.com/jasnig/UsefulPickerView.git", :tag => s.version } 13 | s.framework = "UIKit" 14 | s.source_files = "Source/*.swift","Source/*.plist" 15 | end -------------------------------------------------------------------------------- /UsefulPickerView/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnig/UsefulPickerView/4a3075d3c22b4845d31c3a69f048290d68717b3f/UsefulPickerView/.DS_Store -------------------------------------------------------------------------------- /UsefulPickerView/Example/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnig/UsefulPickerView/4a3075d3c22b4845d31c3a69f048290d68717b3f/UsefulPickerView/Example/.DS_Store -------------------------------------------------------------------------------- /UsefulPickerView/Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/25. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /UsefulPickerView/Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /UsefulPickerView/Example/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /UsefulPickerView/Example/Vc1Controller.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Vc1Controller.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/24. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | 31 | import UIKit 32 | 33 | let singleData = ["swift", "ObjecTive-C", "C", "C++", "java", "php", "python", "ruby", "js"] 34 | // 每一列为数组 35 | let multipleData = [["1天", "2天", "3天", "4天", "5天", "6天", "7天"],["1小时", "2小时", "3小时", "4小时", "5小时"], ["1分钟","2分钟","3分钟","4分钟","5分钟","6分钟","7分钟","8分钟","9分钟","10分钟"]] 36 | 37 | // 注意这个数据的格式!!!!!! 38 | let multipleAssociatedData: [[[String: [String]?]]] = [// 数组 39 | 40 | [ ["交通工具": ["陆地", "空中", "水上"]],//字典 41 | ["食品": ["健康食品", "垃圾食品"]], 42 | ["游戏": ["益智游戏", "角色游戏"]] 43 | 44 | ],// 数组 45 | 46 | [ ["陆地": ["公交车", "小轿车", "自行车"]], 47 | ["空中": ["飞机"]], 48 | ["水上": ["轮船"]], 49 | ["健康食品": ["蔬菜", "水果"]], 50 | ["垃圾食品": ["辣条", "不健康小吃"]], 51 | ["益智游戏": ["消消乐", "消灭星星"]], 52 | ["角色游戏": ["lol", "cf"]] 53 | 54 | ] 55 | ] 56 | 57 | class Vc1Controller: UIViewController { 58 | 59 | 60 | @IBOutlet weak var selectedDataLabel: UILabel! 61 | 62 | 63 | @IBAction func singleBtnOnClick(_ sender: UIButton) { 64 | 65 | UsefulPickerView.showSingleColPicker("编程语言选择", data: singleData, defaultSelectedIndex: 2) {[unowned self] (selectedIndex, selectedValue) in 66 | self.selectedDataLabel.text = "选中了第\(selectedIndex)行----选中的数据为\(selectedValue)" 67 | } 68 | 69 | } 70 | @IBAction func multipleBtnOnClick(_ sender: UIButton) { 71 | 72 | 73 | UsefulPickerView.showMultipleColsPicker("持续时间选择", data: multipleData, defaultSelectedIndexs: [0,1,1]) {[unowned self] (selectedIndexs, selectedValues) in 74 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 75 | 76 | } 77 | 78 | } 79 | @IBAction func multipleAssociatedBtnOnClick(_ sender: UIButton) { 80 | 81 | // 注意这里设置的是默认的选中值, 而不是选中的下标,省得去数关联数组里的下标 82 | UsefulPickerView.showMultipleAssociatedColsPicker("多列关联数据", data: multipleAssociatedData, defaultSelectedValues: ["交通工具","陆地","自行车"]) {[unowned self] (selectedIndexs, selectedValues) in 83 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 84 | 85 | } 86 | 87 | } 88 | @IBAction func citiesBtnOnClick(_ sender: UIButton) { 89 | // 注意设置默认值得时候, 必须设置完整, 不能进行省略 ["四川", "成都", "成华区"] 比如不能设置为["四川", "成都"] 90 | // ["北京", "通州"] 不能设置为["北京"] 91 | UsefulPickerView.showCitiesPicker("省市区选择", defaultSelectedValues: ["北京", "/", "/"], selectTopLevel: true) {[unowned self] (selectedIndexs, selectedValues) in 92 | // 处理数据 93 | let combinedString = selectedValues.reduce("", { (result, value) -> String in 94 | result + " " + value 95 | }) 96 | 97 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(combinedString)" 98 | 99 | } 100 | } 101 | @IBAction func dateBtnOnClick(_ sender: UIButton) { 102 | 103 | 104 | UsefulPickerView.showDatePicker("日期选择") {[unowned self] ( selectedDate) in 105 | let formatter = DateFormatter() 106 | formatter.dateFormat = "yyyy-MM-dd" 107 | let string = formatter.string(from: selectedDate) 108 | self.selectedDataLabel.text = "选中了的日期是\(string)" 109 | } 110 | 111 | 112 | } 113 | 114 | @IBAction func timeBtnOnClick(_ sender: UIButton) { 115 | /// /// @author ZeroJ, 16-04-25 17:04:28 116 | // style里面可以更改的和系统的DatePicker属性是一一对应的 117 | var dateStyle = DatePickerSetting() 118 | dateStyle.dateMode = .time 119 | 120 | UsefulPickerView.showDatePicker("时间选择", datePickerSetting: dateStyle) { (selectedDate) in 121 | let formatter = DateFormatter() 122 | // H -> 24小时制 123 | formatter.dateFormat = "HH: mm" 124 | let string = formatter.string(from: selectedDate) 125 | self.selectedDataLabel.text = "选中了的日期是\(string)" 126 | } 127 | } 128 | 129 | override func viewDidLoad() { 130 | super.viewDidLoad() 131 | 132 | // Do any additional setup after loading the view. 133 | } 134 | 135 | override func didReceiveMemoryWarning() { 136 | super.didReceiveMemoryWarning() 137 | // Dispose of any resources that can be recreated. 138 | } 139 | 140 | 141 | /* 142 | // MARK: - Navigation 143 | 144 | // In a storyboard-based application, you will often want to do a little preparation before navigation 145 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 146 | // Get the new view controller using segue.destinationViewController. 147 | // Pass the selected object to the new view controller. 148 | } 149 | */ 150 | 151 | } 152 | -------------------------------------------------------------------------------- /UsefulPickerView/Example/Vc2Controller.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Vc2Controller.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/24. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | 31 | import UIKit 32 | 33 | class Vc2Controller: UIViewController { 34 | 35 | @IBOutlet weak var singleTextField: SelectionTextField! 36 | @IBOutlet weak var multipleTextField: SelectionTextField! 37 | @IBOutlet weak var multipleAssociatedTextField: SelectionTextField! 38 | @IBOutlet weak var citiesTextField: SelectionTextField! 39 | @IBOutlet weak var dateTextField: SelectionTextField! 40 | @IBOutlet weak var timeTextField: SelectionTextField! 41 | 42 | @IBOutlet weak var selectedDataLabel: UILabel! 43 | override func viewDidLoad() { 44 | super.viewDidLoad() 45 | 46 | // 代码生成 47 | let test = SelectionTextField(frame: CGRect(x: 20, y: 340, width: 340, height: 28)) 48 | test.borderStyle = .roundedRect 49 | test.placeholder = "代码初始化" 50 | test.showSingleColPicker("测试代码", data: singleData, defaultSelectedIndex: 0, autoSetSelectedText: true) { (textField, selectedIndex, selectedValue) in 51 | print(selectedValue) 52 | } 53 | view.addSubview(test) 54 | 55 | singleTextField.showSingleColPicker("编程语言选择", data: singleData, defaultSelectedIndex: 2, autoSetSelectedText: true) {[unowned self] (textField, selectedIndex, selectedValue) in 56 | // 可以使用textField 也可以使用 self.singleTextField 57 | textField.text = "选中了第\(selectedIndex)行----选中的数据为\(selectedValue)" 58 | self.selectedDataLabel.text = "选中了第\(selectedIndex)行----选中的数据为\(selectedValue)" 59 | 60 | } 61 | 62 | 63 | multipleTextField.showMultipleColsPicker("持续时间选择", data: multipleData, defaultSelectedIndexs: [0,1,1], autoSetSelectedText: true) { [unowned self] (textField, selectedIndexs, selectedValues) in 64 | self.multipleTextField.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 65 | 66 | } 67 | 68 | 69 | // 注意这里设置的是默认的选中值, 而不是选中的下标 70 | multipleAssociatedTextField.showMultipleAssociatedColsPicker("多列关联数据", data: multipleAssociatedData, defaultSelectedValues: ["交通工具","陆地","自行车"], autoSetSelectedText: true) {[unowned self] (textField, selectedIndexs, selectedValues) in 71 | self.multipleAssociatedTextField.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 72 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 73 | 74 | } 75 | 76 | // 注意设置默认值得时候, 必须设置完整, 不能进行省略 ["四川", "成都", "成华区"] 比如不能设置为["四川", "成都"] 77 | // ["北京", "通州"] 不能设置为["北京"] 78 | citiesTextField.showCitiesPicker("省市区选择", defaultSelectedValues: ["四川", "成都", "郫县"], autoSetSelectedText: false) {[unowned self] (textField, selectedIndexs, selectedValues) in 79 | self.citiesTextField.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 80 | self.selectedDataLabel.text = "选中了第\(selectedIndexs)行----选中的数据为\(selectedValues)" 81 | 82 | } 83 | 84 | dateTextField.showDatePicker("日期选择", autoSetSelectedText: true) { (textField, selectedDate) in 85 | let formatter = DateFormatter() 86 | formatter.dateFormat = "yyyy-MM-dd" 87 | let string = formatter.string(from: selectedDate) 88 | textField.text = string 89 | } 90 | /// /// @author ZeroJ, 16-04-25 17:04:28 91 | // style里面可以更改的和系统的DatePicker属性是一一对应的 92 | var dateStyle = DatePickerSetting() 93 | dateStyle.dateMode = .date 94 | let formatter = DateFormatter() 95 | formatter.dateFormat = "yyyy-MM-dd" 96 | let date = formatter.date(from: "2000-01-11") 97 | dateStyle.date = date! 98 | /// /// @author ZeroJ, 16-04-25 17:04:28 99 | /// 注意使用这种方式的时候, 请设置 autoSetSelectedText = false, 否则显示的格式可能不是您需要的 100 | timeTextField.showDatePicker("时间选择", datePickerSetting: dateStyle, autoSetSelectedText: false) { (textField, selectedDate) in 101 | let formatter = DateFormatter() 102 | // H -> 24小时制 103 | formatter.dateFormat = "yyyy-MM-dd" 104 | let string = formatter.string(from: selectedDate) 105 | textField.text = string 106 | } 107 | 108 | 109 | } 110 | 111 | override func didReceiveMemoryWarning() { 112 | super.didReceiveMemoryWarning() 113 | // Dispose of any resources that can be recreated. 114 | } 115 | 116 | 117 | /* 118 | // MARK: - Navigation 119 | 120 | // In a storyboard-based application, you will often want to do a little preparation before navigation 121 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 122 | // Get the new view controller using segue.destinationViewController. 123 | // Pass the selected object to the new view controller. 124 | } 125 | */ 126 | 127 | } 128 | -------------------------------------------------------------------------------- /UsefulPickerView/Example/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/25. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | } 17 | 18 | override func didReceiveMemoryWarning() { 19 | super.didReceiveMemoryWarning() 20 | // Dispose of any resources that can be recreated. 21 | } 22 | 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /UsefulPickerView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /UsefulPickerView/Source/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jasnig/UsefulPickerView/4a3075d3c22b4845d31c3a69f048290d68717b3f/UsefulPickerView/Source/.DS_Store -------------------------------------------------------------------------------- /UsefulPickerView/Source/City.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 上海 6 | 7 | 普陀 8 | 闸北 9 | 虹口 10 | 杨浦 11 | 卢湾 12 | 徐汇 13 | 长宁 14 | 静安 15 | 黄浦 16 | 金山 17 | 浦东新 18 | 嘉定 19 | 闵行 20 | 宝山 21 | 南汇 22 | 青浦 23 | 松江 24 | 奉贤 25 | 崇明 26 | 27 | 云南 28 | 29 | 德宏 30 | 玉溪 31 | 曲靖 32 | 保山 33 | 怒江 34 | 迪庆 35 | 昭通 36 | 昆明 37 | 楚雄 38 | 文山 39 | 西双版纳 40 | 丽江 41 | 红河 42 | 大理 43 | 临沧 44 | 思茅 45 | 46 | 内蒙古 47 | 48 | 巴彦淖尔 49 | 锡林郭勒盟 50 | 兴安盟 51 | 乌兰察布 52 | 鄂尔多斯 53 | 乌海 54 | 包头 55 | 呼和浩特 56 | 呼伦贝尔 57 | 通辽 58 | 阿拉善盟 59 | 赤峰 60 | 61 | 北京 62 | 63 | 通州 64 | 房山 65 | 昌平 66 | 顺义 67 | 怀柔 68 | 大兴 69 | 密云 70 | 平谷 71 | 延庆 72 | 东城 73 | 崇文 74 | 西城 75 | 朝阳 76 | 宣武 77 | 石景山 78 | 丰台 79 | 门头沟 80 | 海淀 81 | 82 | 台湾 83 | 84 | 新竹 85 | 基隆 86 | 高雄 87 | 台北 88 | 台南 89 | 嘉义 90 | 台中 91 | 宜兰 92 | 屏东 93 | 桃园 94 | 台东 95 | 苗栗 96 | 金门 97 | 花莲 98 | 云林 99 | 连江 100 | 澎湖 101 | 南投 102 | 彰化 103 | 104 | 吉林 105 | 106 | 松原 107 | 四平 108 | 白城 109 | 白山 110 | 吉林 111 | 通化 112 | 长春 113 | 延边朝鲜族 114 | 辽源 115 | 116 | 四川 117 | 118 | 宜宾 119 | 巴中 120 | 南充 121 | 成都 122 | 凉山彝族 123 | 眉山 124 | 阿坝 125 | 乐山 126 | 绵阳 127 | 广安 128 | 广元 129 | 德阳 130 | 资阳 131 | 达州 132 | 泸州 133 | 自贡 134 | 遂宁 135 | 甘孜藏族 136 | 雅安 137 | 内江 138 | 攀枝花 139 | 140 | 国外 141 | 142 | 德国 143 | 新加坡 144 | 美国 145 | 加拿大 146 | 澳大利亚 147 | 日本 148 | 英国 149 | 巴西 150 | 俄罗斯 151 | 尼日利亚 152 | 马来西亚 153 | 爱尔兰 154 | 奥地利 155 | 挪威 156 | 意大利 157 | 西班牙 158 | 泰国 159 | 芬兰 160 | 丹麦 161 | 荷兰 162 | 阿联酋 163 | 瑞典 164 | 瑞士 165 | 比利时 166 | 新西兰 167 | 法国 168 | 韩国 169 | 匈牙利 170 | 其他 171 | 越南 172 | 以色列 173 | 科威特 174 | 希腊 175 | 南非 176 | 葡萄牙 177 | 墨西哥 178 | 印尼 179 | 180 | 天津 181 | 182 | 武清 183 | 河东 184 | 和平 185 | 西青 186 | 津南 187 | 大港 188 | 东丽 189 | 塘沽 190 | 汉沽 191 | 河北 192 | 红桥 193 | 河西 194 | 南开 195 | 蓟县 196 | 宁河 197 | 静海 198 | 宝坻 199 | 北辰 200 | 201 | 宁夏 202 | 203 | 石嘴山 204 | 固原 205 | 中卫 206 | 银川 207 | 吴忠 208 | 209 | 安徽 210 | 211 | 淮南 212 | 黄山 213 | 蚌埠 214 | 合肥 215 | 宿州 216 | 六安 217 | 池州 218 | 芜湖 219 | 宣城 220 | 巢湖 221 | 亳州 222 | 阜阳 223 | 铜陵 224 | 淮北 225 | 滁州 226 | 马鞍山 227 | 安庆 228 | 229 | 山东 230 | 231 | 淄博 232 | 烟台 233 | 日照 234 | 荷泽 235 | 潍坊 236 | 济南 237 | 济宁 238 | 青岛 239 | 临沂 240 | 威海 241 | 莱芜 242 | 泰安 243 | 东营 244 | 聊城 245 | 枣庄 246 | 滨州 247 | 德州 248 | 249 | 山西 250 | 251 | 临汾 252 | 晋中 253 | 朔州 254 | 运城 255 | 晋城 256 | 阳泉 257 | 忻州 258 | 大同 259 | 长治 260 | 太原 261 | 吕梁 262 | 263 | 广东 264 | 265 | 珠海 266 | 惠州 267 | 清远 268 | 韶关 269 | 江门 270 | 揭阳 271 | 云浮 272 | 佛山 273 | 广州 274 | 深圳 275 | 河源 276 | 汕头 277 | 汕尾 278 | 茂名 279 | 肇庆 280 | 东莞 281 | 湛江 282 | 潮州 283 | 阳江 284 | 中山 285 | 梅州 286 | 287 | 广西 288 | 289 | 贺州 290 | 梧州 291 | 河池 292 | 百色 293 | 来宾 294 | 贵港 295 | 玉林 296 | 钦州 297 | 北海 298 | 柳州 299 | 桂林 300 | 南宁 301 | 防城港 302 | 崇左 303 | 304 | 新疆 305 | 306 | 伊犁 307 | 克拉玛依 308 | 哈密地 309 | 石河子 310 | 吐鲁番地 311 | 阿拉尔 312 | 阿勒泰地 313 | 乌鲁木齐 314 | 塔城地 315 | 昌吉 316 | 克孜勒 317 | 图木舒克 318 | 阿克苏地 319 | 五家渠 320 | 巴音郭楞 321 | 和田地 322 | 博尔塔拉 323 | 喀什地 324 | 325 | 江苏 326 | 327 | 连云港 328 | 盐城 329 | 无锡 330 | 宿迁 331 | 扬州 332 | 镇江 333 | 南京 334 | 徐州 335 | 泰州 336 | 南通 337 | 常州 338 | 淮安 339 | 苏州 340 | 341 | 江西 342 | 343 | 南昌 344 | 萍乡 345 | 景德镇 346 | 吉安 347 | 九江 348 | 新余 349 | 鹰潭 350 | 抚州 351 | 赣州 352 | 上饶 353 | 宜春 354 | 355 | 河北 356 | 357 | 张家口 358 | 邯郸 359 | 邢台 360 | 衡水 361 | 秦皇岛 362 | 廊坊 363 | 保定 364 | 承德 365 | 唐山 366 | 沧州 367 | 石家庄 368 | 369 | 河南 370 | 371 | 南阳 372 | 洛阳 373 | 三门峡 374 | 商丘 375 | 焦作 376 | 开封 377 | 驻马店 378 | 濮阳 379 | 许昌 380 | 安阳 381 | 信阳 382 | 漯河 383 | 平顶山 384 | 郑州 385 | 新乡 386 | 鹤壁 387 | 周口 388 | 389 | 浙江 390 | 391 | 丽水 392 | 舟山 393 | 宁波 394 | 衢州 395 | 温州 396 | 杭州 397 | 台州 398 | 嘉兴 399 | 绍兴 400 | 金华 401 | 湖州 402 | 403 | 海南 404 | 405 | 保亭 406 | 澄迈 407 | 南沙群岛 408 | 陵水黎族 409 | 中沙群岛 410 | 屯昌 411 | 昌江黎族 412 | 乐东黎族 413 | 琼海 414 | 儋州 415 | 文昌 416 | 万宁 417 | 白沙黎族 418 | 海口 419 | 三亚 420 | 五指山 421 | 琼中 422 | 东方 423 | 定安 424 | 西沙群岛 425 | 临高 426 | 427 | 湖北 428 | 429 | 咸宁 430 | 宜昌 431 | 黄冈 432 | 荆州 433 | 孝感 434 | 荆门 435 | 十堰 436 | 鄂州 437 | 天门 438 | 潜江 439 | 恩施 440 | 武汉 441 | 仙桃 442 | 神农架林 443 | 随州 444 | 黄石 445 | 襄樊 446 | 447 | 湖南 448 | 449 | 郴州 450 | 岳阳 451 | 怀化 452 | 娄底 453 | 张家界 454 | 益阳 455 | 湘西土家族苗族 456 | 常德 457 | 湘潭 458 | 永州 459 | 衡阳 460 | 株洲 461 | 长沙 462 | 邵阳 463 | 464 | 澳门 465 | 466 | 花地玛堂 467 | 圣安多尼堂 468 | 大堂 469 | 望德堂 470 | 风顺堂 471 | 嘉模堂 472 | 圣方济各堂 473 | 474 | 甘肃 475 | 476 | 兰州 477 | 金昌 478 | 甘南藏族 479 | 平凉 480 | 嘉峪关 481 | 天水 482 | 白银 483 | 武威 484 | 张掖 485 | 庆阳 486 | 定西 487 | 临夏回族 488 | 酒泉 489 | 陇南 490 | 491 | 福建 492 | 493 | 南平 494 | 厦门 495 | 福州 496 | 宁德 497 | 龙岩 498 | 莆田 499 | 漳州 500 | 泉州 501 | 三明 502 | 503 | 西藏 504 | 505 | 阿里地 506 | 拉萨 507 | 山南地 508 | 日喀则地 509 | 那曲地 510 | 昌都地 511 | 林芝地 512 | 513 | 贵州 514 | 515 | 毕节地 516 | 黔南 517 | 六盘水 518 | 黔东南 519 | 贵阳 520 | 遵义 521 | 铜仁地 522 | 黔西南 523 | 安顺 524 | 525 | 辽宁 526 | 527 | 铁岭 528 | 葫芦岛 529 | 营口 530 | 本溪 531 | 辽阳 532 | 盘锦 533 | 阜新 534 | 朝阳 535 | 锦州 536 | 抚顺 537 | 丹东 538 | 沈阳 539 | 鞍山 540 | 大连 541 | 542 | 重庆 543 | 544 | 开县 545 | 江北 546 | 沙坪坝 547 | 九龙坡 548 | 南岸 549 | 北碚 550 | 万盛 551 | 双桥 552 | 渝北 553 | 巴南 554 | 黔江 555 | 垫江 556 | 武隆 557 | 巫山 558 | 巫溪 559 | 云阳 560 | 奉节 561 | 忠县 562 | 梁平 563 | 璧山 564 | 荣昌 565 | 大足 566 | 铜梁 567 | 潼南 568 | 綦江 569 | 长寿 570 | 彭水 571 | 酉阳 572 | 合川 573 | 江津 574 | 南川 575 | 永川 576 | 丰都 577 | 城口 578 | 大渡口 579 | 渝中 580 | 涪陵 581 | 万州 582 | 石柱 583 | 秀山 584 | 585 | 陕西 586 | 587 | 咸阳 588 | 铜川 589 | 商洛 590 | 榆林 591 | 渭南 592 | 汉中 593 | 安康 594 | 延安 595 | 宝鸡 596 | 西安 597 | 598 | 青海 599 | 600 | 黄南 601 | 海东地 602 | 果洛 603 | 西宁 604 | 海北 605 | 玉树 606 | 海南 607 | 海西 608 | 609 | 香港 610 | 611 | 油尖旺 612 | 黄大仙 613 | 深水埗 614 | 观塘 615 | 九龙城 616 | 湾仔 617 | 葵青 618 | 离岛 619 | 中西 620 | 621 | 622 | 荃湾 623 | 元朗 624 | 沙田 625 | 西贡 626 | 屯门 627 | 大埔 628 | 629 | 630 | 黑龙江 631 | 632 | 鹤岗 633 | 大兴安岭地 634 | 大庆 635 | 七台河 636 | 齐齐哈尔 637 | 牡丹江 638 | 黑河 639 | 双鸭山 640 | 绥化 641 | 伊春 642 | 佳木斯 643 | 哈尔滨 644 | 鸡西 645 | 646 | 647 | 648 | -------------------------------------------------------------------------------- /UsefulPickerView/Source/Picker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionTextField.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/16. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | import UIKit 31 | 32 | struct AssociatedDataModel { 33 | var key: String 34 | var valueArray: [String]? 35 | init (key: String, valueArray: [String]? = nil) { 36 | self.key = key 37 | self.valueArray = valueArray 38 | } 39 | } 40 | 41 | class Picker: UIView { 42 | let screenWidth = UIScreen.main.bounds.size.width 43 | let screenHeight = UIScreen.main.bounds.size.height 44 | 45 | // 使用模型初始化数据示例 46 | let associatedData: [[AssociatedDataModel]] = [ 47 | // 第一列数据 (key) 48 | [ AssociatedDataModel(key: "交通工具"), 49 | AssociatedDataModel(key: "食品"), 50 | AssociatedDataModel(key: "游戏") 51 | ], 52 | // 第二列数据 (valueArray) 53 | [ AssociatedDataModel(key: "交通工具", valueArray: ["陆地", "空中", "水上"]), 54 | AssociatedDataModel(key: "食品", valueArray: ["健康食品", "垃圾食品"]), 55 | AssociatedDataModel(key: "游戏", valueArray: ["益智游戏", "角色游戏"]), 56 | 57 | ], 58 | 59 | // 第三列数据 (valueArray) 60 | [ AssociatedDataModel(key: "陆地", valueArray: ["公交车", "小轿车", "自行车"]), 61 | AssociatedDataModel(key: "空中", valueArray: ["飞机"]), 62 | AssociatedDataModel(key: "水上", valueArray: ["轮船"]), 63 | AssociatedDataModel(key: "健康食品", valueArray: ["蔬菜", "水果"]), 64 | AssociatedDataModel(key: "垃圾食品", valueArray: ["辣条", "不健康小吃"]), 65 | AssociatedDataModel(key: "益智游戏", valueArray: ["消消乐", "消灭星星"]), 66 | AssociatedDataModel(key: "角色游戏", valueArray: ["lol", "cf"]) 67 | 68 | ] 69 | 70 | 71 | ] 72 | 73 | 74 | enum PickerStyles { 75 | case single 76 | case multiple 77 | case multipleAssociated 78 | } 79 | 80 | 81 | var pickerStyle: PickerStyles = .single 82 | 83 | // 完成按钮的响应Closure 84 | typealias BtnAction = () -> Void 85 | typealias SingleDoneAction = (_ selectedIndex: Int, _ selectedValue: String) -> Void 86 | typealias MultipleDoneAction = (_ selectedIndexs: [Int], _ selectedValues: [String]) -> Void 87 | 88 | fileprivate var cancelAction: BtnAction? = nil { 89 | didSet { 90 | tool.cancelAction = cancelAction 91 | } 92 | } 93 | //MARK:- 只有一列的时候用到的属性 94 | fileprivate var singleDoneOnClick:SingleDoneAction? = nil { 95 | didSet { 96 | tool.doneAction = {[unowned self] in 97 | 98 | self.singleDoneOnClick?(self.selectedIndex, self.selectedValue) 99 | } 100 | } 101 | } 102 | 103 | fileprivate var defalultSelectedIndex: Int? = nil { 104 | didSet { 105 | if let defaultIndex = defalultSelectedIndex, let singleData = singleColData {// 判断下标是否合法 106 | assert(defaultIndex >= 0 && defaultIndex < singleData.count, "设置的默认选中Index不合法") 107 | if defaultIndex >= 0 && defaultIndex < singleData.count { 108 | // 设置默认值 109 | selectedIndex = defaultIndex 110 | selectedValue = singleData[defaultIndex] 111 | // 滚动到默认位置 112 | pickerView.selectRow(defaultIndex, inComponent: 0, animated: false) 113 | 114 | } 115 | 116 | } else {// 没有默认值设置0行为默认值 117 | selectedIndex = 0 118 | selectedValue = singleColData![0] 119 | pickerView.selectRow(0, inComponent: 0, animated: false) 120 | 121 | } 122 | } 123 | } 124 | fileprivate var singleColData: [String]? = nil 125 | 126 | fileprivate var selectedIndex: Int = 0 127 | fileprivate var selectedValue: String = "" 128 | 129 | 130 | //MARK:- 有多列不关联的时候用到的属性 131 | var multipleDoneOnClick:MultipleDoneAction? = nil { 132 | didSet { 133 | 134 | tool.doneAction = {[unowned self] in 135 | self.multipleDoneOnClick?(self.selectedIndexs, self.selectedValues) 136 | } 137 | } 138 | } 139 | 140 | fileprivate var multipleColsData: [[String]]? = nil { 141 | didSet { 142 | if let multipleData = multipleColsData { 143 | for _ in multipleData.indices { 144 | selectedIndexs.append(0) 145 | selectedValues.append(" ") 146 | } 147 | 148 | } 149 | } 150 | } 151 | 152 | fileprivate var selectedIndexs: [Int] = [] 153 | fileprivate var selectedValues: [String] = [] 154 | 155 | fileprivate var defalultSelectedIndexs: [Int]? = nil { 156 | didSet { 157 | if let defaultIndexs = defalultSelectedIndexs { 158 | 159 | defaultIndexs.enumerated().forEach({ (component: Int, row: Int) in 160 | 161 | assert(component < pickerView.numberOfComponents && row < pickerView.numberOfRows(inComponent: component), "设置的默认选中Indexs有不合法的") 162 | if component < pickerView.numberOfComponents && row < pickerView.numberOfRows(inComponent: component){ 163 | 164 | // 滚动到默认位置 165 | pickerView.selectRow(row, inComponent: component, animated: false) 166 | 167 | // 设置默认值 168 | selectedIndexs[component] = row 169 | selectedValues[component] = self.pickerView(pickerView, titleForRow: row, forComponent: component) ?? " " 170 | 171 | 172 | } 173 | 174 | }) 175 | 176 | } else { 177 | multipleColsData?.indices.forEach({ (index) in 178 | // 滚动到默认位置 179 | pickerView.selectRow(0, inComponent: index, animated: false) 180 | 181 | // 设置默认选中值 182 | selectedIndexs[index] = 0 183 | 184 | selectedValues[index] = self.pickerView(pickerView, titleForRow: 0, forComponent: index) ?? " " 185 | 186 | }) 187 | } 188 | } 189 | } 190 | 191 | 192 | 193 | //MARK:- 有多列关联的时候用到的属性 194 | 195 | fileprivate var multipleAssociatedColsData: [[AssociatedDataModel]]? = nil { 196 | didSet { 197 | 198 | if let multipleAssociatedData = multipleAssociatedColsData { 199 | // 初始化选中的values 200 | for _ in multipleAssociatedData.indices { 201 | selectedIndexs.append(0) 202 | selectedValues.append(" ") 203 | } 204 | } 205 | } 206 | } 207 | 208 | // 设置第一组的数据, 使用数组是因为字典无序,需要设置默认选中值的时候获取到准确的下标滚动到相应的行 209 | fileprivate var defaultSelectedValues: [String]? = nil { 210 | didSet { 211 | 212 | if let defaultValues = defaultSelectedValues { 213 | // 设置默认值 214 | selectedValues = defaultValues 215 | defaultValues.enumerated().forEach { (component: Int, element: String) in 216 | var row: Int? = nil 217 | if component == 0 { 218 | let firstData = multipleAssociatedColsData![0] 219 | for (index,associatedModel) in firstData.enumerated() { 220 | if associatedModel.key == element { 221 | row = index 222 | } 223 | } 224 | } else { 225 | 226 | let associatedModels = multipleAssociatedColsData![component] 227 | var arr: [String]? 228 | 229 | for associatedModel in associatedModels { 230 | if associatedModel.key == selectedValues[component - 1] { 231 | arr = associatedModel.valueArray 232 | } 233 | } 234 | 235 | row = arr?.index(of: element) 236 | 237 | } 238 | 239 | assert(row != nil, "第\(component)列设置的默认值有误") 240 | if row == nil { 241 | row = 0 242 | print("第\(component)列设置的默认值有误") 243 | } 244 | if component < pickerView.numberOfComponents { 245 | // print(" \(component) ----\(row!)") 246 | // 滚动到默认的位置 247 | pickerView.selectRow(row!, inComponent: component, animated: false) 248 | // 设置选中的下标 249 | selectedIndexs[component] = row! 250 | 251 | } 252 | 253 | } 254 | 255 | } else { 256 | multipleAssociatedColsData?.indices.forEach({ (index) in 257 | // 滚动到默认的位置 0 行 258 | pickerView.selectRow(0, inComponent: index, animated: false) 259 | // 设置默认的选中值 260 | selectedValues[index] = self.pickerView(pickerView, titleForRow: 0, forComponent: index) ?? " " 261 | selectedIndexs[index] = 0 262 | }) 263 | } 264 | 265 | } 266 | 267 | } 268 | 269 | 270 | 271 | fileprivate lazy var pickerView: UIPickerView! = { [unowned self] in 272 | let picker = UIPickerView() 273 | picker.delegate = self 274 | picker.dataSource = self 275 | picker.backgroundColor = UIColor.white 276 | return picker 277 | }() 278 | 279 | fileprivate lazy var tool: ToolBarView! = ToolBarView() 280 | 281 | fileprivate let pickerViewHeight = 216.0 282 | fileprivate let toolBarHeight = 44.0 283 | 284 | let screenW = UIScreen.main.bounds.size.width 285 | 286 | //MARK:- 初始化 287 | init() { 288 | let frame = CGRect(x: 0.0, y: 0.0, width: Double(screenW), height: toolBarHeight + pickerViewHeight) 289 | super.init(frame: frame) 290 | commonInit() 291 | } 292 | 293 | required init?(coder aDecoder: NSCoder) { 294 | super.init(coder: aDecoder) 295 | commonInit() 296 | } 297 | 298 | deinit { 299 | print("\(self.debugDescription) --- 销毁") 300 | } 301 | 302 | func commonInit() { 303 | 304 | addSubview(tool) 305 | addSubview(pickerView) 306 | } 307 | 308 | override func layoutSubviews() { 309 | super.layoutSubviews() 310 | tool.frame = CGRect(x: 0.0, y: 0.0, width: Double(screenWidth), height: toolBarHeight) 311 | pickerView.frame = CGRect(x: 0.0, y: 44.0, width: Double(screenW), height: pickerViewHeight) 312 | } 313 | 314 | 315 | } 316 | 317 | extension Picker: UIPickerViewDelegate, UIPickerViewDataSource { 318 | func numberOfComponents(in pickerView: UIPickerView) -> Int { 319 | 320 | switch pickerStyle { 321 | case .single: 322 | return singleColData == nil ? 0 : 1 323 | case .multiple: 324 | return multipleColsData?.count ?? 0 325 | case .multipleAssociated: 326 | return multipleAssociatedColsData?.count ?? 0 327 | 328 | } 329 | 330 | } 331 | 332 | func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 333 | 334 | switch pickerStyle { 335 | case .single: 336 | return singleColData?.count ?? 0 337 | case .multiple: 338 | return multipleColsData?[component].count ?? 0 339 | case .multipleAssociated: 340 | if let multipleAssociatedData = multipleAssociatedColsData { 341 | 342 | if component == 0 { 343 | return multipleAssociatedData[0].count 344 | }else { 345 | let associatedDataModels = multipleAssociatedData[component] 346 | var arr: [String]? 347 | 348 | for associatedDataModel in associatedDataModels { 349 | if associatedDataModel.key == selectedValues[component - 1] { 350 | arr = associatedDataModel.valueArray 351 | } 352 | } 353 | 354 | return arr?.count ?? 0 355 | 356 | } 357 | 358 | } 359 | 360 | return 0 361 | 362 | } 363 | 364 | } 365 | 366 | func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 367 | switch pickerStyle { 368 | case .single: 369 | selectedIndex = row 370 | selectedValue = singleColData![row] 371 | case .multiple: 372 | selectedIndexs[component] = row 373 | selectedValues[component] = self.pickerView(pickerView, titleForRow: row, forComponent: component) ?? " " 374 | case .multipleAssociated: 375 | // 设置选中值 376 | selectedValues[component] = self.pickerView(pickerView, titleForRow: row, forComponent: component) ?? " " 377 | selectedIndexs[component] = row 378 | // 更新下一列关联的值 379 | if component < multipleAssociatedColsData!.count - 1 { 380 | pickerView.reloadComponent(component + 1) 381 | // 递归 382 | self.pickerView(pickerView, didSelectRow: 0, inComponent: component+1) 383 | pickerView.selectRow(0, inComponent: component+1, animated: true) 384 | 385 | } 386 | 387 | } 388 | 389 | 390 | } 391 | func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 392 | switch pickerStyle { 393 | case .single: 394 | return singleColData?[row] 395 | case .multiple: 396 | return multipleColsData?[component][row] 397 | case .multipleAssociated: 398 | 399 | if let multipleAssociatedData = multipleAssociatedColsData { 400 | 401 | if component == 0 { 402 | return multipleAssociatedData[0][row].key 403 | }else { 404 | let associatedDataModels = multipleAssociatedData[component] 405 | var arr: [String]? 406 | 407 | for associatedDataModel in associatedDataModels { 408 | if associatedDataModel.key == selectedValues[component - 1] { 409 | arr = associatedDataModel.valueArray 410 | } 411 | } 412 | if arr?.count == 0 {// 空数组 413 | return nil 414 | } 415 | return arr?[row] 416 | 417 | } 418 | 419 | } 420 | 421 | return nil 422 | 423 | } 424 | } 425 | } 426 | 427 | //MARK: 快速使用方法 428 | extension Picker { 429 | 430 | /// 单列 431 | class func singleColPicker(_ singleColData: [String], defaultIndex: Int?,cancelAction: BtnAction?, doneAction: SingleDoneAction?) -> Picker { 432 | let pic = Picker() 433 | 434 | pic.pickerStyle = .single 435 | pic.singleColData = singleColData 436 | pic.defalultSelectedIndex = defaultIndex 437 | 438 | pic.singleDoneOnClick = doneAction 439 | pic.cancelAction = cancelAction 440 | return pic 441 | 442 | } 443 | 444 | /// 多列不关联 445 | class func multipleCosPicker(_ multipleColsData: [[String]], defaultSelectedIndexs: [Int]?,cancelAction: BtnAction?, doneAction: MultipleDoneAction?) -> Picker { 446 | 447 | let pic = Picker() 448 | 449 | pic.pickerStyle = .multiple 450 | 451 | pic.multipleColsData = multipleColsData 452 | pic.defalultSelectedIndexs = defaultSelectedIndexs 453 | pic.cancelAction = cancelAction 454 | pic.multipleDoneOnClick = doneAction 455 | return pic 456 | 457 | } 458 | 459 | /// 多列关联 460 | class func multipleAssociatedCosPicker(_ multipleAssociatedColsData: [[AssociatedDataModel]], defaultSelectedValues: [String]?,cancelAction: BtnAction?, doneAction: MultipleDoneAction?) -> Picker { 461 | 462 | let pic = Picker() 463 | pic.pickerStyle = .multipleAssociated 464 | pic.multipleAssociatedColsData = multipleAssociatedColsData 465 | 466 | pic.defaultSelectedValues = defaultSelectedValues 467 | pic.cancelAction = cancelAction 468 | pic.multipleDoneOnClick = doneAction 469 | return pic 470 | 471 | } 472 | 473 | /// 城市选择器 474 | class func citiesPicker(_ defaultSelectedValues: [String]?, cancelAction: BtnAction?, doneAction: MultipleDoneAction?) -> Picker { 475 | 476 | let provincePath = Bundle.main.path(forResource: "Province", ofType: "plist") 477 | let cityPath = Bundle.main.path(forResource: "City", ofType: "plist") 478 | let areaPath = Bundle.main.path(forResource: "Area", ofType: "plist") 479 | 480 | let proviceArr = NSArray(contentsOfFile: provincePath!) 481 | let cityArr = NSDictionary(contentsOfFile: cityPath!) 482 | let areaArr = NSDictionary(contentsOfFile: areaPath!) 483 | 484 | var provinceModelArr: [AssociatedDataModel] = [] 485 | var citiesModelArr: [AssociatedDataModel] = [] 486 | var areasModelArr: [AssociatedDataModel] = [] 487 | 488 | proviceArr?.forEach({ (element) in 489 | if let province = element as? String { 490 | provinceModelArr.append(AssociatedDataModel(key: province)) 491 | 492 | let citys = cityArr?[province] as? [String] 493 | citiesModelArr.append(AssociatedDataModel(key: province, valueArray: citys)) 494 | 495 | citys?.forEach({ (element) in 496 | let city = element 497 | let areas = areaArr?[city]as? [String] 498 | areasModelArr.append(AssociatedDataModel(key: city, valueArray: areas)) 499 | 500 | }) 501 | } 502 | }) 503 | 504 | let citiesArr = [provinceModelArr, citiesModelArr, areasModelArr] 505 | 506 | 507 | let pic = Picker.multipleAssociatedCosPicker(citiesArr, defaultSelectedValues: defaultSelectedValues, cancelAction: cancelAction, doneAction: doneAction) 508 | return pic 509 | 510 | } 511 | } 512 | -------------------------------------------------------------------------------- /UsefulPickerView/Source/Province.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 北京 6 | 上海 7 | 天津 8 | 重庆 9 | 浙江 10 | 广东 11 | 江苏 12 | 山东 13 | 福建 14 | 安徽 15 | 四川 16 | 湖北 17 | 河北 18 | 云南 19 | 黑龙江 20 | 吉林 21 | 辽宁 22 | 海南 23 | 湖南 24 | 河南 25 | 贵州 26 | 江西 27 | 广西 28 | 陕西 29 | 山西 30 | 青海 31 | 宁夏 32 | 甘肃 33 | 西藏 34 | 内蒙古 35 | 新疆 36 | 台湾 37 | 香港 38 | 澳门 39 | 国外 40 | 41 | 42 | -------------------------------------------------------------------------------- /UsefulPickerView/Source/SelectionTextField.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SelectionTextField.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/16. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | 31 | import UIKit 32 | 33 | open class SelectionTextField: UITextField { 34 | 35 | public typealias BtnAction = () -> Void 36 | public typealias SingleDoneAction = (_ textField: UITextField, _ selectedIndex: Int, _ selectedValue: String) -> Void 37 | public typealias MultipleDoneAction = (_ textField: UITextField, _ selectedIndexs: [Int], _ selectedValues: [String]) -> Void 38 | public typealias DateDoneAction = (_ textField: UITextField, _ selectedDate: Date) -> Void 39 | 40 | public typealias MultipleAssociatedDataType = [[[String: [String]?]]] 41 | 42 | /// 保存pickerView的初始化 43 | fileprivate var setUpPickerClosure:(() -> PickerView)? 44 | /// 如果设置了autoSetSelectedText为true 将自动设置text的值, 默认以空格分开多列选择, 但你仍然可以在响应完成的closure中修改text的值 45 | fileprivate var autoSetSelectedText = false 46 | 47 | //MARK: 初始化 48 | override public init(frame: CGRect) { 49 | super.init(frame: frame) 50 | commonInit() 51 | 52 | } 53 | // 从xib或storyboard中初始化时候调用 54 | required public init?(coder aDecoder: NSCoder) { 55 | super.init(coder: aDecoder) 56 | commonInit() 57 | } 58 | 59 | deinit { 60 | NotificationCenter.default.removeObserver(self) 61 | print("\(self.debugDescription) --- 销毁") 62 | } 63 | 64 | 65 | } 66 | 67 | // MARK: - 监听通知 68 | extension SelectionTextField { 69 | 70 | fileprivate func commonInit() { 71 | NotificationCenter.default.addObserver(self, selector: #selector(self.didBeginEdit), name: NSNotification.Name.UITextFieldTextDidBeginEditing, object: self) 72 | NotificationCenter.default.addObserver(self, selector: #selector(self.didEndEdit), name: NSNotification.Name.UITextFieldTextDidEndEditing, object: self) 73 | } 74 | // 开始编辑添加pickerView 75 | func didBeginEdit() { 76 | let pickerView = setUpPickerClosure?() 77 | pickerView?.delegate = self 78 | inputView = pickerView 79 | } 80 | // 编辑完成销毁pickerView 81 | func didEndEdit() { 82 | inputView = nil 83 | } 84 | 85 | override open func caretRect(for position: UITextPosition) -> CGRect { 86 | return CGRect.zero 87 | } 88 | 89 | } 90 | 91 | // MARK: - 使用方法 92 | extension SelectionTextField { 93 | 94 | /// 单列选择器 /// @author ZeroJ, 16-04-23 18:04:59 95 | /// 96 | /// - parameter title: 标题 97 | /// - parameter data: 数据 98 | /// - parameter defaultSeletedIndex: 默认选中的行数 99 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 100 | /// - parameter doneAction: 响应完成的Closure 101 | /// 102 | /// - returns: 103 | public func showSingleColPicker(_ toolBarTitle: String, data: [String], defaultSelectedIndex: Int?,autoSetSelectedText: Bool, doneAction: SingleDoneAction?) { 104 | 105 | self.autoSetSelectedText = autoSetSelectedText 106 | 107 | // 保存在这个closure中, 在开始编辑的时候在执行, 避免像之前在这里直接初始化pickerView, 每个SelectionTextField在调用这个方法的时候就初始化pickerView,当有多个pickerView的时候就很消耗内存 108 | setUpPickerClosure = {() -> PickerView in 109 | 110 | return PickerView.singleColPicker(toolBarTitle, singleColData: data, defaultIndex: defaultSelectedIndex, cancelAction: {[unowned self] in 111 | 112 | self.endEditing(true) 113 | 114 | }, doneAction: {[unowned self] (selectedIndex: Int, selectedValue: String) -> Void in 115 | 116 | doneAction?(self, selectedIndex, selectedValue) 117 | self.endEditing(true) 118 | 119 | }) 120 | 121 | 122 | } 123 | 124 | } 125 | 126 | /// 多列不关联选择器 /// @author ZeroJ, 16-04-23 18:04:59 127 | /// 128 | /// - parameter title: 标题 129 | /// - parameter data: 数据 130 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 131 | /// - parameter autoSetSelectedText: 设置为true的时候, 将俺默认的格式自动设置textField的值 132 | /// - parameter doneAction: 响应完成的Closure 133 | /// 134 | /// - returns: 135 | public func showMultipleColsPicker(_ toolBarTitle: String, data: [[String]], defaultSelectedIndexs: [Int]?, autoSetSelectedText: Bool, doneAction: MultipleDoneAction?) { 136 | self.autoSetSelectedText = autoSetSelectedText 137 | 138 | setUpPickerClosure = {() -> PickerView in 139 | 140 | return PickerView.multipleCosPicker(toolBarTitle, multipleColsData: data, defaultSelectedIndexs: defaultSelectedIndexs, cancelAction: { [unowned self] in 141 | 142 | self.endEditing(true) 143 | 144 | }, doneAction:{[unowned self] (selectedIndexs: [Int], selectedValues: [String]) -> Void in 145 | 146 | doneAction?(self, selectedIndexs, selectedValues) 147 | self.endEditing(true) 148 | }) 149 | } 150 | 151 | } 152 | 153 | /// 多列关联选择器 /// @author ZeroJ, 16-04-23 18:04:59 154 | /// 155 | /// - parameter title: 标题 156 | /// - parameter data: 数据, 数据的格式参照示例 157 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 158 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 159 | /// - parameter doneAction: 响应完成的Closure 160 | /// 161 | /// - returns: 162 | public func showMultipleAssociatedColsPicker(_ toolBarTitle: String, data: MultipleAssociatedDataType, defaultSelectedValues: [String]?,autoSetSelectedText: Bool, doneAction: MultipleDoneAction?) { 163 | self.autoSetSelectedText = autoSetSelectedText 164 | 165 | setUpPickerClosure = {() -> PickerView in 166 | 167 | return PickerView.multipleAssociatedCosPicker(toolBarTitle, multipleAssociatedColsData: data, defaultSelectedValues: defaultSelectedValues,cancelAction: { [unowned self] in 168 | 169 | self.endEditing(true) 170 | 171 | }, doneAction:{[unowned self] (selectedIndexs: [Int], selectedValues: [String]) -> Void in 172 | 173 | doneAction?(self, selectedIndexs, selectedValues) 174 | self.endEditing(true) 175 | }) 176 | } 177 | 178 | } 179 | 180 | 181 | /// 城市选择器 /// @author ZeroJ, 16-04-23 18:04:59 182 | /// 183 | /// - parameter title: 标题 184 | /// - parameter defaultSeletedValues: 默认选中的每一列的值, 注意不是行数 185 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 186 | /// - parameter doneAction: 响应完成的Closure 187 | /// 188 | /// - returns: 189 | 190 | public func showCitiesPicker(_ toolBarTitle: String, defaultSelectedValues: [String]?,autoSetSelectedText: Bool, doneAction: MultipleDoneAction?) { 191 | self.autoSetSelectedText = autoSetSelectedText 192 | 193 | setUpPickerClosure = {() -> PickerView in 194 | return PickerView.citiesPicker(toolBarTitle, defaultSelectedValues: defaultSelectedValues, cancelAction: { [unowned self] in 195 | self.endEditing(true) 196 | 197 | }, doneAction:{[unowned self] (selectedIndexs: [Int], selectedValues: [String]) -> Void in 198 | 199 | doneAction?(self,selectedIndexs, selectedValues) 200 | self.endEditing(true) 201 | }) 202 | } 203 | 204 | } 205 | 206 | /// 日期选择器 /// @author ZeroJ, 16-04-23 18:04:59 207 | /// 208 | /// - parameter title: 标题 209 | /// - parameter datePickerSetting: 可配置UIDatePicker的样式 210 | /// - parameter autoSetSelectedText: 设置为true的时候, 将按默认的格式自动设置textField的值 211 | /// - parameter doneAction: 响应完成的Closure 212 | /// 213 | /// - returns: 214 | public func showDatePicker(_ toolBarTitle: String, datePickerSetting: DatePickerSetting = DatePickerSetting(), autoSetSelectedText: Bool, doneAction: DateDoneAction?) { 215 | self.autoSetSelectedText = autoSetSelectedText 216 | 217 | setUpPickerClosure = {() -> PickerView in 218 | return PickerView.datePicker(toolBarTitle, datePickerSetting: datePickerSetting, cancelAction: { [unowned self] in 219 | self.endEditing(true) 220 | 221 | }, doneAction: {[unowned self] (selectedDate) in 222 | doneAction?(self, selectedDate) 223 | self.endEditing(true) 224 | 225 | }) 226 | 227 | } 228 | } 229 | 230 | } 231 | 232 | 233 | // MARK: - PickerViewDelegate -- 如果设置了autoSetSelectedText为true 这些代理方法中将以默认的格式自动设置textField的值 234 | extension SelectionTextField: PickerViewDelegate { 235 | public func singleColDidSelecte(_ selectedIndex: Int, selectedValue: String) { 236 | if autoSetSelectedText { 237 | text = " " + selectedValue 238 | } 239 | } 240 | 241 | public func multipleColsDidSelecte(_ selectedIndexs: [Int], selectedValues: [String]) { 242 | 243 | 244 | if autoSetSelectedText { 245 | text = selectedValues.reduce("", { (result, selectedValue) -> String in 246 | result + " " + selectedValue 247 | }) 248 | } 249 | } 250 | 251 | public func dateDidSelecte(_ selectedDate: Date) { 252 | if autoSetSelectedText { 253 | 254 | let formatter = DateFormatter() 255 | formatter.dateFormat = "yyyy-MM-dd" 256 | let string = formatter.string(from: selectedDate) 257 | text = " " + string 258 | } 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /UsefulPickerView/Source/ToolBarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToolBarView.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/18. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | 31 | import UIKit 32 | 33 | open class ToolBarView: UIView { 34 | 35 | typealias CustomClosures = (_ titleLabel: UILabel, _ cancleBtn: UIButton, _ doneBtn: UIButton) -> Void 36 | public typealias BtnAction = () -> Void 37 | 38 | open var title = "请选择" { 39 | didSet { 40 | titleLabel.text = title 41 | } 42 | } 43 | 44 | open var doneAction: BtnAction? 45 | open var cancelAction: BtnAction? 46 | // 用来产生上下分割线的效果 47 | fileprivate lazy var contentView: UIView = { 48 | let content = UIView() 49 | content.backgroundColor = UIColor.white 50 | return content 51 | }() 52 | // 文本框 53 | fileprivate lazy var titleLabel: UILabel = { 54 | let label = UILabel() 55 | label.textColor = UIColor.black 56 | label.textAlignment = .center 57 | return label 58 | }() 59 | 60 | // 取消按钮 61 | fileprivate lazy var cancleBtn: UIButton = { 62 | let btn = UIButton() 63 | btn.setTitle("取消", for: UIControlState()) 64 | btn.setTitleColor(UIColor.black, for: UIControlState()) 65 | return btn 66 | }() 67 | 68 | // 完成按钮 69 | fileprivate lazy var doneBtn: UIButton = { 70 | let donebtn = UIButton() 71 | donebtn.setTitle("完成", for: UIControlState()) 72 | donebtn.setTitleColor(UIColor.black, for: UIControlState()) 73 | return donebtn 74 | }() 75 | 76 | override public init(frame: CGRect) { 77 | super.init(frame: frame) 78 | commonInit() 79 | 80 | } 81 | 82 | required public init?(coder aDecoder: NSCoder) { 83 | fatalError("init(coder:) has not been implemented") 84 | } 85 | 86 | fileprivate func commonInit() { 87 | backgroundColor = UIColor.lightText 88 | addSubview(contentView) 89 | contentView.addSubview(cancleBtn) 90 | contentView.addSubview(doneBtn) 91 | contentView.addSubview(titleLabel) 92 | 93 | doneBtn.addTarget(self, action: #selector(self.doneBtnOnClick(_:)), for: .touchUpInside) 94 | cancleBtn.addTarget(self, action: #selector(self.cancelBtnOnClick(_:)), for: .touchUpInside) 95 | } 96 | 97 | func doneBtnOnClick(_ sender: UIButton) { 98 | doneAction?() 99 | } 100 | func cancelBtnOnClick(_ sender: UIButton) { 101 | cancelAction?() 102 | 103 | } 104 | 105 | override open func layoutSubviews() { 106 | super.layoutSubviews() 107 | let margin = 15.0 108 | let contentHeight = Double(bounds.size.height) - 2.0 109 | contentView.frame = CGRect(x: 0.0, y: 1.0, width: Double(bounds.size.width), height: contentHeight) 110 | let btnWidth = contentHeight 111 | 112 | cancleBtn.frame = CGRect(x: margin, y: 0.0, width: btnWidth, height: btnWidth) 113 | doneBtn.frame = CGRect(x: Double(bounds.size.width) - btnWidth - margin, y: 0.0, width: btnWidth, height: btnWidth) 114 | let titleX = Double(cancleBtn.frame.maxX) + margin 115 | let titleW = Double(bounds.size.width) - titleX - btnWidth - margin 116 | 117 | 118 | titleLabel.frame = CGRect(x: titleX, y: 0.0, width: titleW, height: btnWidth) 119 | } 120 | 121 | 122 | } 123 | -------------------------------------------------------------------------------- /UsefulPickerView/Source/UsefulPickerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UsefulPickerView.swift 3 | // UsefulPickerVIew 4 | // 5 | // Created by jasnig on 16/4/16. 6 | // Copyright © 2016年 ZeroJ. All rights reserved. 7 | // github: https://github.com/jasnig 8 | // 简书: http://www.jianshu.com/users/fb31a3d1ec30/latest_articles 9 | 10 | // 11 | // Permission is hereby granted, free of charge, to any person obtaining a copy 12 | // of this software and associated documentation files (the "Software"), to deal 13 | // in the Software without restriction, including without limitation the rights 14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | // copies of the Software, and to permit persons to whom the Software is 16 | // furnished to do so, subject to the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be included in 19 | // all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | // THE SOFTWARE. 28 | 29 | // 30 | 31 | import UIKit 32 | 33 | open class UsefulPickerView: UIView { 34 | 35 | public typealias BtnAction = () -> Void 36 | public typealias SingleDoneAction = (_ selectedIndex: Int, _ selectedValue: String) -> Void 37 | public typealias MultipleDoneAction = (_ selectedIndexs: [Int], _ selectedValues: [String]) -> Void 38 | public typealias DateDoneAction = (_ selectedDate: Date) -> Void 39 | 40 | public typealias MultipleAssociatedDataType = [[[String: [String]?]]] 41 | 42 | fileprivate var pickerView: PickerView! 43 | //MARK:- 常量 44 | fileprivate let pickerViewHeight:CGFloat = 260.0 45 | 46 | fileprivate let screenWidth = UIScreen.main.bounds.size.width 47 | fileprivate let screenHeight = UIScreen.main.bounds.size.height 48 | fileprivate var hideFrame: CGRect { 49 | return CGRect(x: 0.0, y: screenHeight, width: screenWidth, height: pickerViewHeight) 50 | } 51 | fileprivate var showFrame: CGRect { 52 | return CGRect(x: 0.0, y: screenHeight - pickerViewHeight, width: screenWidth, height: pickerViewHeight) 53 | } 54 | 55 | /// 使用NSArray 可以存任何"东西", 如果使用 [Any], 那么当 56 | /// let a = ["1", "2"] var b:[Any] = a 会报错 57 | 58 | // MARK:- 初始化 59 | // 单列 60 | convenience init(frame: CGRect, toolBarTitle: String, singleColData: [String], defaultSelectedIndex: Int?, doneAction: SingleDoneAction?) { 61 | 62 | self.init(frame: frame) 63 | 64 | 65 | 66 | pickerView = PickerView.singleColPicker(toolBarTitle, singleColData: singleColData, defaultIndex: defaultSelectedIndex, cancelAction: {[unowned self] in 67 | // 点击取消的时候移除 68 | self.hidePicker() 69 | 70 | }, doneAction: {[unowned self] (selectedIndex, selectedValue) in 71 | doneAction?(selectedIndex, selectedValue) 72 | self.hidePicker() 73 | 74 | }) 75 | pickerView.frame = hideFrame 76 | addSubview(pickerView) 77 | 78 | // 点击背景移除self 79 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 80 | addGestureRecognizer(tap) 81 | 82 | } 83 | // 多列不关联 84 | convenience init(frame: CGRect, toolBarTitle: String, multipleColsData: [[String]], defaultSelectedIndexs: [Int]?, doneAction: MultipleDoneAction?) { 85 | 86 | self.init(frame: frame) 87 | 88 | pickerView = PickerView.multipleCosPicker(toolBarTitle, multipleColsData: multipleColsData, defaultSelectedIndexs: defaultSelectedIndexs, cancelAction: {[unowned self] in 89 | // 点击取消的时候移除 90 | self.hidePicker() 91 | 92 | }, doneAction: {[unowned self] (selectedIndexs, selectedValues) in 93 | doneAction?(selectedIndexs, selectedValues) 94 | self.hidePicker() 95 | }) 96 | pickerView.frame = hideFrame 97 | addSubview(pickerView) 98 | 99 | // 点击背景移除self 100 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 101 | addGestureRecognizer(tap) 102 | 103 | } 104 | // 多列关联 105 | convenience init(frame: CGRect, toolBarTitle: String, multipleAssociatedColsData: MultipleAssociatedDataType, defaultSelectedValues: [String]?, doneAction: MultipleDoneAction?) { 106 | 107 | self.init(frame: frame) 108 | 109 | pickerView = PickerView.multipleAssociatedCosPicker(toolBarTitle, multipleAssociatedColsData: multipleAssociatedColsData, defaultSelectedValues: defaultSelectedValues, cancelAction: {[unowned self] in 110 | // 点击取消的时候移除 111 | self.hidePicker() 112 | 113 | }, doneAction: {[unowned self] (selectedIndexs, selectedValues) in 114 | doneAction?(selectedIndexs, selectedValues) 115 | self.hidePicker() 116 | }) 117 | 118 | pickerView.frame = hideFrame 119 | addSubview(pickerView) 120 | 121 | // 点击背景移除self 122 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 123 | addGestureRecognizer(tap) 124 | 125 | } 126 | // 城市选择器 127 | convenience init(frame: CGRect, toolBarTitle: String, defaultSelectedValues: [String]?, doneAction: MultipleDoneAction?, selectTopLevel: Bool = false) { 128 | 129 | self.init(frame: frame) 130 | 131 | pickerView = PickerView.citiesPicker(toolBarTitle, defaultSelectedValues: defaultSelectedValues, cancelAction: {[unowned self] in 132 | // 点击取消的时候移除 133 | self.hidePicker() 134 | 135 | }, doneAction: {[unowned self] (selectedIndexs, selectedValues) in 136 | doneAction?(selectedIndexs, selectedValues) 137 | self.hidePicker() 138 | }, selectTopLevel: selectTopLevel) 139 | 140 | pickerView.frame = hideFrame 141 | addSubview(pickerView) 142 | 143 | // 点击背景移除self 144 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 145 | addGestureRecognizer(tap) 146 | 147 | } 148 | // 日期选择器 149 | convenience init(frame: CGRect, toolBarTitle: String, datePickerSetting: DatePickerSetting, doneAction: DateDoneAction?) { 150 | 151 | self.init(frame: frame) 152 | 153 | pickerView = PickerView.datePicker(toolBarTitle, datePickerSetting: datePickerSetting, cancelAction: {[unowned self] in 154 | // 点击取消的时候移除 155 | self.hidePicker() 156 | 157 | }, doneAction: {[unowned self] (selectedDate) in 158 | doneAction?(selectedDate) 159 | self.hidePicker() 160 | }) 161 | 162 | pickerView.frame = hideFrame 163 | addSubview(pickerView) 164 | 165 | // 点击背景移除self 166 | let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:))) 167 | addGestureRecognizer(tap) 168 | 169 | } 170 | 171 | override init(frame: CGRect) { 172 | super.init(frame: frame) 173 | addOrentationObserver() 174 | } 175 | 176 | 177 | required public init?(coder aDecoder: NSCoder) { 178 | fatalError("init(coder:) has not been implemented") 179 | } 180 | 181 | deinit { 182 | NotificationCenter.default.removeObserver(self) 183 | print("\(self.debugDescription) --- 销毁") 184 | } 185 | 186 | 187 | } 188 | 189 | // MARK:- selector 190 | extension UsefulPickerView { 191 | 192 | fileprivate func addOrentationObserver() { 193 | NotificationCenter.default.addObserver(self, selector: #selector(self.statusBarOrientationChange), name: NSNotification.Name.UIApplicationDidChangeStatusBarOrientation, object: nil) 194 | 195 | } 196 | // 屏幕旋转时移除pickerView 197 | func statusBarOrientationChange() { 198 | removeFromSuperview() 199 | } 200 | func tapAction(_ tap: UITapGestureRecognizer) { 201 | let location = tap.location(in: self) 202 | // 点击空白背景移除self 203 | if location.y <= screenHeight - pickerViewHeight { 204 | self.hidePicker() 205 | } 206 | } 207 | } 208 | 209 | // MARK:- 弹出和移除self 210 | extension UsefulPickerView { 211 | 212 | fileprivate func showPicker() { 213 | // 通过window 弹出view 214 | let window = UIApplication.shared.keyWindow 215 | guard let currentWindow = window else { return } 216 | currentWindow.addSubview(self) 217 | 218 | // let pickerX = NSLayoutConstraint(item: self, attribute: .Leading, relatedBy: .Equal, toItem: currentWindow, attribute: .Leading, multiplier: 1.0, constant: 0.0) 219 | // 220 | // let pickerY = NSLayoutConstraint(item: self, attribute: .Top, relatedBy: .Equal, toItem: currentWindow, attribute: .Top, multiplier: 1.0, constant: 0.0) 221 | // let pickerW = NSLayoutConstraint(item: self, attribute: .Width, relatedBy: .Equal, toItem: currentWindow, attribute: .Width, multiplier: 1.0, constant: 0.0) 222 | // let pickerH = NSLayoutConstraint(item: self, attribute: .Height, relatedBy: .Equal, toItem: currentWindow, attribute: .Height, multiplier: 1.0, constant: 0.0) 223 | // self.translatesAutoresizingMaskIntoConstraints = false 224 | // 225 | // currentWindow.addConstraints([pickerX, pickerY, pickerW, pickerH]) 226 | 227 | UIView.animate(withDuration: 0.25, animations: {[unowned self] in 228 | self.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.1) 229 | self.pickerView.frame = self.showFrame 230 | }, completion: nil) 231 | 232 | 233 | } 234 | 235 | func hidePicker() { 236 | // 把self从window中移除 237 | UIView.animate(withDuration: 0.25, animations: { [unowned self] in 238 | self.backgroundColor = UIColor.clear 239 | self.pickerView.frame = self.hideFrame 240 | 241 | }, completion: {[unowned self] (_) in 242 | self.removeFromSuperview() 243 | }) 244 | } 245 | } 246 | 247 | // MARK: - 快速使用方法 248 | extension UsefulPickerView { 249 | 250 | /// 单列选择器 /// @author ZeroJ, 16-04-23 18:04:59 251 | /// 252 | /// - parameter title: 标题 253 | /// - parameter data: 数据 254 | /// - parameter defaultSeletedIndex: 默认选中的行数 255 | /// - parameter doneAction: 响应完成的Closure 256 | /// 257 | /// - returns: 258 | public class func showSingleColPicker(_ toolBarTitle: String, data: [String], defaultSelectedIndex: Int?, doneAction: SingleDoneAction?) { 259 | let window = UIApplication.shared.keyWindow 260 | guard let currentWindow = window else { return } 261 | 262 | let testView = UsefulPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, singleColData: data,defaultSelectedIndex: defaultSelectedIndex ,doneAction: doneAction) 263 | 264 | testView.showPicker() 265 | 266 | } 267 | 268 | /// 多列不关联选择器 /// @author ZeroJ, 16-04-23 18:04:59 269 | /// 270 | /// - parameter title: 标题 271 | /// - parameter data: 数据 272 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 273 | /// - parameter doneAction: 响应完成的Closure 274 | /// 275 | /// - returns: 276 | public class func showMultipleColsPicker(_ toolBarTitle: String, data: [[String]], defaultSelectedIndexs: [Int]?,doneAction: MultipleDoneAction?) { 277 | let window = UIApplication.shared.keyWindow 278 | guard let currentWindow = window else { return } 279 | 280 | let testView = UsefulPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, multipleColsData: data, defaultSelectedIndexs: defaultSelectedIndexs, doneAction: doneAction) 281 | 282 | testView.showPicker() 283 | 284 | } 285 | 286 | /// 多列关联选择器 /// @author ZeroJ, 16-04-23 18:04:59 287 | /// 288 | /// - parameter title: 标题 289 | /// - parameter data: 数据, 数据的格式参照示例 290 | /// - parameter defaultSeletedIndexs: 默认选中的每一列的行数 291 | /// - parameter doneAction: 响应完成的Closure 292 | /// 293 | /// - returns: 294 | public class func showMultipleAssociatedColsPicker(_ toolBarTitle: String, data: MultipleAssociatedDataType, defaultSelectedValues: [String]?, doneAction: MultipleDoneAction?) { 295 | let window = UIApplication.shared.keyWindow 296 | guard let currentWindow = window else { return } 297 | 298 | let testView = UsefulPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, multipleAssociatedColsData: data, defaultSelectedValues: defaultSelectedValues, doneAction: doneAction) 299 | 300 | testView.showPicker() 301 | 302 | } 303 | 304 | 305 | /// 城市选择器 /// @author ZeroJ, 16-04-23 18:04:59 306 | /// 307 | /// - parameter title: 标题 308 | /// - parameter defaultSeletedValues: 默认选中的每一列的值, 注意不是行数 309 | /// - parameter doneAction: 响应完成的Closure 310 | /// 311 | /// - returns: 312 | public class func showCitiesPicker(_ toolBarTitle: String, defaultSelectedValues: [String]?, selectTopLevel: Bool=false, doneAction: MultipleDoneAction?) { 313 | 314 | let window = UIApplication.shared.keyWindow 315 | guard let currentWindow = window else { return } 316 | 317 | let testView = UsefulPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, defaultSelectedValues: defaultSelectedValues, doneAction: doneAction, selectTopLevel: selectTopLevel) 318 | 319 | testView.showPicker() 320 | 321 | } 322 | 323 | /// 日期选择器 /// @author ZeroJ, 16-04-23 18:04:59 324 | /// 325 | /// - parameter title: 标题 326 | /// - parameter datePickerSetting: 可配置UIDatePicker的样式 327 | /// - parameter doneAction: 响应完成的Closure 328 | /// 329 | /// - returns: 330 | public class func showDatePicker(_ toolBarTitle: String, datePickerSetting: DatePickerSetting = DatePickerSetting(), doneAction: DateDoneAction?) { 331 | 332 | let window = UIApplication.shared.keyWindow 333 | guard let currentWindow = window else { return } 334 | 335 | let testView = UsefulPickerView(frame: currentWindow.bounds, toolBarTitle: toolBarTitle, datePickerSetting: datePickerSetting, doneAction: doneAction) 336 | 337 | testView.showPicker() 338 | 339 | } 340 | 341 | } 342 | 343 | 344 | --------------------------------------------------------------------------------