├── 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 | 
8 |
9 | 
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 |
--------------------------------------------------------------------------------