├── .github
└── contributing.md
├── .gitignore
├── .swift-version
├── Demo
├── AppDelegate.swift
├── Base.lproj
│ └── Main.storyboard
├── Images.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-60@2x.png
│ │ ├── Icon-60@3x.png
│ │ ├── Icon-72.png
│ │ ├── Icon-72@2x.png
│ │ ├── Icon-76.png
│ │ ├── Icon-76@2x.png
│ │ ├── Icon-Small-50.png
│ │ ├── Icon-Small-50@2x.png
│ │ ├── Icon-Small.png
│ │ ├── Icon-Small@2x-1.png
│ │ ├── Icon-Small@2x.png
│ │ ├── Icon-Small@3x.png
│ │ ├── Icon-Spotlight-40.png
│ │ ├── Icon-Spotlight-40@2x-1.png
│ │ ├── Icon-Spotlight-40@2x.png
│ │ ├── Icon-Spotlight-40@3x.png
│ │ ├── Icon-iPadPro@2x.png
│ │ ├── Icon.png
│ │ └── Icon@2x.png
│ ├── Contents.json
│ ├── LaunchImage.launchimage
│ │ ├── Contents.json
│ │ ├── Splash_V3_1242x2208.png
│ │ ├── Splash_V3_320x480.png
│ │ ├── Splash_V3_640x1136-1.png
│ │ ├── Splash_V3_640x1136.png
│ │ ├── Splash_V3_640x960-1.png
│ │ ├── Splash_V3_640x960.png
│ │ └── Splash_V3_750x1334.png
│ ├── logo_0.imageset
│ │ ├── Contents.json
│ │ └── logo_0.png
│ ├── logo_1.imageset
│ │ ├── Contents.json
│ │ └── logo_1.png
│ ├── logo_10.imageset
│ │ ├── Contents.json
│ │ └── logo_10.png
│ ├── logo_2.imageset
│ │ ├── Contents.json
│ │ └── logo_2.png
│ ├── logo_3.imageset
│ │ ├── Contents.json
│ │ └── logo_3.png
│ ├── logo_4.imageset
│ │ ├── Contents.json
│ │ └── logo_4.png
│ ├── logo_5.imageset
│ │ ├── Contents.json
│ │ └── logo_5.png
│ ├── logo_6.imageset
│ │ ├── Contents.json
│ │ └── logo_6.png
│ ├── logo_7.imageset
│ │ ├── Contents.json
│ │ └── logo_7.png
│ ├── logo_8.imageset
│ │ ├── Contents.json
│ │ └── logo_8.png
│ └── logo_9.imageset
│ │ ├── Contents.json
│ │ └── logo_9.png
├── MyCell.swift
├── MyCell.xib
├── NiceButton.swift
└── ViewController.swift
├── DropDown.podspec
├── DropDown.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── DropDown.xcscmblueprint
│ │ └── IDEWorkspaceChecks.plist
└── xcshareddata
│ └── xcschemes
│ └── DropDown.xcscheme
├── DropDown
├── DropDown.h
├── Info.plist
├── helpers
│ ├── DPDConstants.swift
│ ├── DPDKeyboardListener.swift
│ └── DPDUIView+Extension.swift
├── resources
│ └── DropDownCell.xib
└── src
│ ├── DropDown+Appearance.swift
│ ├── DropDown.swift
│ └── DropDownCell.swift
├── DropDownTests
├── DropDownTests.swift
└── Info.plist
├── Info.plist
├── LICENSE
├── Package.swift
├── README.md
└── Screenshots
├── 1.png
├── 2.png
├── 3.png
├── customCells
├── links.png
└── xib.png
└── logo.png
/.github/contributing.md:
--------------------------------------------------------------------------------
1 | ### What where you trying to do
2 |
3 | A few lines to explain the goal.
4 |
5 | ### What actually happened
6 |
7 | A few lines to explain the bug.
8 |
9 | ### What you think went wrong (optional)
10 |
11 | If you have an idea, don't hesitate to comment it here!
12 |
13 | ### How to reproduce the issue
14 |
15 | Put some code here if necessary.
16 |
17 | ### Details
18 |
19 | OS version, logs or screenshots.
20 |
21 | Thank you for your contribution! 👍
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Swift ###
2 | # Xcode
3 | #
4 | build/
5 | *.pbxuser
6 | !default.pbxuser
7 | *.mode1v3
8 | !default.mode1v3
9 | *.mode2v3
10 | !default.mode2v3
11 | *.perspectivev3
12 | !default.perspectivev3
13 | xcuserdata
14 | *.xccheckout
15 | *.moved-aside
16 | DerivedData
17 | *.hmap
18 | *.ipa
19 | *.xcuserstate
20 |
21 | # CocoaPods
22 | #
23 | # We recommend against adding the Pods directory to your .gitignore. However
24 | # you should judge for yourself, the pros and cons are mentioned at:
25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
26 | #
27 | # Pods/
28 |
29 | # Carthage
30 | #
31 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
32 | # Carthage/Checkouts
33 |
34 | Carthage/Build
35 |
36 | # Mac OS X
37 | .DS_Store
38 |
39 | # SwiftPackageManager #
40 | Packages
41 | .build/
42 | xcuserdata
43 | DerivedData/
44 | #*.xcodeproj Keeping this here so we maintain Carthage support
45 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 5.0
2 |
--------------------------------------------------------------------------------
/Demo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // DropDown
4 | //
5 | // Created by Kevin Hirsch on 28/07/15.
6 | // Copyright (c) 2015 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import DropDown
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | var window: UIWindow?
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | DropDown.startListeningToKeyboard()
19 |
20 | return true
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/Demo/Base.lproj/Main.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 |
36 |
47 |
57 |
63 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
91 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
125 |
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 |
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "1x"
17 | },
18 | {
19 | "size" : "29x29",
20 | "idiom" : "iphone",
21 | "filename" : "Icon-Small@2x.png",
22 | "scale" : "2x"
23 | },
24 | {
25 | "size" : "29x29",
26 | "idiom" : "iphone",
27 | "filename" : "Icon-Small@3x.png",
28 | "scale" : "3x"
29 | },
30 | {
31 | "size" : "40x40",
32 | "idiom" : "iphone",
33 | "filename" : "Icon-Spotlight-40@2x-1.png",
34 | "scale" : "2x"
35 | },
36 | {
37 | "size" : "40x40",
38 | "idiom" : "iphone",
39 | "filename" : "Icon-Spotlight-40@3x.png",
40 | "scale" : "3x"
41 | },
42 | {
43 | "size" : "57x57",
44 | "idiom" : "iphone",
45 | "filename" : "Icon.png",
46 | "scale" : "1x"
47 | },
48 | {
49 | "size" : "57x57",
50 | "idiom" : "iphone",
51 | "filename" : "Icon@2x.png",
52 | "scale" : "2x"
53 | },
54 | {
55 | "size" : "60x60",
56 | "idiom" : "iphone",
57 | "filename" : "Icon-60@2x.png",
58 | "scale" : "2x"
59 | },
60 | {
61 | "size" : "60x60",
62 | "idiom" : "iphone",
63 | "filename" : "Icon-60@3x.png",
64 | "scale" : "3x"
65 | },
66 | {
67 | "idiom" : "ipad",
68 | "size" : "20x20",
69 | "scale" : "1x"
70 | },
71 | {
72 | "idiom" : "ipad",
73 | "size" : "20x20",
74 | "scale" : "2x"
75 | },
76 | {
77 | "size" : "29x29",
78 | "idiom" : "ipad",
79 | "filename" : "Icon-Small.png",
80 | "scale" : "1x"
81 | },
82 | {
83 | "size" : "29x29",
84 | "idiom" : "ipad",
85 | "filename" : "Icon-Small@2x-1.png",
86 | "scale" : "2x"
87 | },
88 | {
89 | "size" : "40x40",
90 | "idiom" : "ipad",
91 | "filename" : "Icon-Spotlight-40.png",
92 | "scale" : "1x"
93 | },
94 | {
95 | "size" : "40x40",
96 | "idiom" : "ipad",
97 | "filename" : "Icon-Spotlight-40@2x.png",
98 | "scale" : "2x"
99 | },
100 | {
101 | "size" : "50x50",
102 | "idiom" : "ipad",
103 | "filename" : "Icon-Small-50.png",
104 | "scale" : "1x"
105 | },
106 | {
107 | "size" : "50x50",
108 | "idiom" : "ipad",
109 | "filename" : "Icon-Small-50@2x.png",
110 | "scale" : "2x"
111 | },
112 | {
113 | "size" : "72x72",
114 | "idiom" : "ipad",
115 | "filename" : "Icon-72.png",
116 | "scale" : "1x"
117 | },
118 | {
119 | "size" : "72x72",
120 | "idiom" : "ipad",
121 | "filename" : "Icon-72@2x.png",
122 | "scale" : "2x"
123 | },
124 | {
125 | "size" : "76x76",
126 | "idiom" : "ipad",
127 | "filename" : "Icon-76.png",
128 | "scale" : "1x"
129 | },
130 | {
131 | "size" : "76x76",
132 | "idiom" : "ipad",
133 | "filename" : "Icon-76@2x.png",
134 | "scale" : "2x"
135 | },
136 | {
137 | "size" : "83.5x83.5",
138 | "idiom" : "ipad",
139 | "filename" : "Icon-iPadPro@2x.png",
140 | "scale" : "2x"
141 | },
142 | {
143 | "idiom" : "ios-marketing",
144 | "size" : "1024x1024",
145 | "scale" : "1x"
146 | }
147 | ],
148 | "info" : {
149 | "version" : 1,
150 | "author" : "xcode"
151 | },
152 | "properties" : {
153 | "pre-rendered" : true
154 | }
155 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-72.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-76.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x-1.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon-iPadPro@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon-iPadPro@2x.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/AppIcon.appiconset/Icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/AppIcon.appiconset/Icon@2x.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "extent" : "full-screen",
5 | "idiom" : "iphone",
6 | "subtype" : "736h",
7 | "filename" : "Splash_V3_1242x2208.png",
8 | "minimum-system-version" : "8.0",
9 | "orientation" : "portrait",
10 | "scale" : "3x"
11 | },
12 | {
13 | "orientation" : "landscape",
14 | "idiom" : "iphone",
15 | "extent" : "full-screen",
16 | "minimum-system-version" : "8.0",
17 | "subtype" : "736h",
18 | "scale" : "3x"
19 | },
20 | {
21 | "extent" : "full-screen",
22 | "idiom" : "iphone",
23 | "subtype" : "667h",
24 | "filename" : "Splash_V3_750x1334.png",
25 | "minimum-system-version" : "8.0",
26 | "orientation" : "portrait",
27 | "scale" : "2x"
28 | },
29 | {
30 | "orientation" : "portrait",
31 | "idiom" : "iphone",
32 | "extent" : "full-screen",
33 | "minimum-system-version" : "7.0",
34 | "filename" : "Splash_V3_640x960-1.png",
35 | "scale" : "2x"
36 | },
37 | {
38 | "extent" : "full-screen",
39 | "idiom" : "iphone",
40 | "subtype" : "retina4",
41 | "filename" : "Splash_V3_640x1136.png",
42 | "minimum-system-version" : "7.0",
43 | "orientation" : "portrait",
44 | "scale" : "2x"
45 | },
46 | {
47 | "orientation" : "portrait",
48 | "idiom" : "ipad",
49 | "extent" : "full-screen",
50 | "minimum-system-version" : "7.0",
51 | "scale" : "1x"
52 | },
53 | {
54 | "orientation" : "landscape",
55 | "idiom" : "ipad",
56 | "extent" : "full-screen",
57 | "minimum-system-version" : "7.0",
58 | "scale" : "1x"
59 | },
60 | {
61 | "orientation" : "portrait",
62 | "idiom" : "ipad",
63 | "extent" : "full-screen",
64 | "minimum-system-version" : "7.0",
65 | "scale" : "2x"
66 | },
67 | {
68 | "orientation" : "landscape",
69 | "idiom" : "ipad",
70 | "extent" : "full-screen",
71 | "minimum-system-version" : "7.0",
72 | "scale" : "2x"
73 | },
74 | {
75 | "orientation" : "portrait",
76 | "idiom" : "iphone",
77 | "extent" : "full-screen",
78 | "filename" : "Splash_V3_320x480.png",
79 | "scale" : "1x"
80 | },
81 | {
82 | "orientation" : "portrait",
83 | "idiom" : "iphone",
84 | "extent" : "full-screen",
85 | "filename" : "Splash_V3_640x960.png",
86 | "scale" : "2x"
87 | },
88 | {
89 | "orientation" : "portrait",
90 | "idiom" : "iphone",
91 | "extent" : "full-screen",
92 | "filename" : "Splash_V3_640x1136-1.png",
93 | "subtype" : "retina4",
94 | "scale" : "2x"
95 | },
96 | {
97 | "orientation" : "portrait",
98 | "idiom" : "ipad",
99 | "extent" : "to-status-bar",
100 | "scale" : "1x"
101 | },
102 | {
103 | "orientation" : "portrait",
104 | "idiom" : "ipad",
105 | "extent" : "full-screen",
106 | "scale" : "1x"
107 | },
108 | {
109 | "orientation" : "landscape",
110 | "idiom" : "ipad",
111 | "extent" : "to-status-bar",
112 | "scale" : "1x"
113 | },
114 | {
115 | "orientation" : "landscape",
116 | "idiom" : "ipad",
117 | "extent" : "full-screen",
118 | "scale" : "1x"
119 | },
120 | {
121 | "orientation" : "portrait",
122 | "idiom" : "ipad",
123 | "extent" : "to-status-bar",
124 | "scale" : "2x"
125 | },
126 | {
127 | "orientation" : "portrait",
128 | "idiom" : "ipad",
129 | "extent" : "full-screen",
130 | "scale" : "2x"
131 | },
132 | {
133 | "orientation" : "landscape",
134 | "idiom" : "ipad",
135 | "extent" : "to-status-bar",
136 | "scale" : "2x"
137 | },
138 | {
139 | "orientation" : "landscape",
140 | "idiom" : "ipad",
141 | "extent" : "full-screen",
142 | "scale" : "2x"
143 | }
144 | ],
145 | "info" : {
146 | "version" : 1,
147 | "author" : "xcode"
148 | }
149 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_1242x2208.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_1242x2208.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_320x480.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_320x480.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_640x1136-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_640x1136-1.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_640x1136.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_640x1136.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_640x960-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_640x960-1.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_640x960.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_640x960.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_750x1334.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/LaunchImage.launchimage/Splash_V3_750x1334.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_0.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo_0.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_0.imageset/logo_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/logo_0.imageset/logo_0.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_1.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo_1.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_1.imageset/logo_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/logo_1.imageset/logo_1.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_10.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo_10.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_10.imageset/logo_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/logo_10.imageset/logo_10.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo_2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_2.imageset/logo_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/logo_2.imageset/logo_2.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_3.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo_3.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_3.imageset/logo_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/logo_3.imageset/logo_3.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_4.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo_4.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_4.imageset/logo_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/logo_4.imageset/logo_4.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_5.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo_5.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_5.imageset/logo_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/logo_5.imageset/logo_5.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_6.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo_6.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_6.imageset/logo_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/logo_6.imageset/logo_6.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_7.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo_7.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_7.imageset/logo_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/logo_7.imageset/logo_7.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_8.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo_8.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_8.imageset/logo_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/logo_8.imageset/logo_8.png
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_9.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "logo_9.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Demo/Images.xcassets/logo_9.imageset/logo_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Demo/Images.xcassets/logo_9.imageset/logo_9.png
--------------------------------------------------------------------------------
/Demo/MyCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MyCell.swift
3 | // DropDown
4 | //
5 | // Created by Kevin Hirsch on 17/08/16.
6 | // Copyright © 2016 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import DropDown
11 |
12 | class MyCell: DropDownCell {
13 |
14 | @IBOutlet weak var logoImageView: UIImageView!
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/Demo/MyCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/Demo/NiceButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NiceButton.swift
3 | // DropDown
4 | //
5 | // Created by Kevin Hirsch on 06/06/16.
6 | // Copyright © 2016 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class NiceButton: UIButton {
12 |
13 | override func awakeFromNib() {
14 | super.awakeFromNib()
15 |
16 | let view = UIView()
17 | view.backgroundColor = UIColor(red: 0.6494, green: 0.8155, blue: 1.0, alpha: 1.0)
18 |
19 | view.translatesAutoresizingMaskIntoConstraints = false
20 | addSubview(view)
21 |
22 | view.addConstraint(NSLayoutConstraint(
23 | item: view,
24 | attribute: .height,
25 | relatedBy: .equal,
26 | toItem: nil,
27 | attribute: .height,
28 | multiplier: 1,
29 | constant: 1
30 | )
31 | )
32 |
33 | addConstraint(NSLayoutConstraint(
34 | item: view,
35 | attribute: .left,
36 | relatedBy: .equal,
37 | toItem: self,
38 | attribute: .left,
39 | multiplier: 1,
40 | constant: 0
41 | )
42 | )
43 |
44 | addConstraint(NSLayoutConstraint(
45 | item: view,
46 | attribute: .right,
47 | relatedBy: .equal,
48 | toItem: self,
49 | attribute: .right,
50 | multiplier: 1,
51 | constant: 0
52 | )
53 | )
54 |
55 | addConstraint(NSLayoutConstraint(
56 | item: view,
57 | attribute: .bottom,
58 | relatedBy: .equal,
59 | toItem: self,
60 | attribute: .bottom,
61 | multiplier: 1,
62 | constant: 0
63 | )
64 | )
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/Demo/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // DropDown
4 | //
5 | // Created by Kevin Hirsch on 28/07/15.
6 | // Copyright (c) 2015 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import DropDown
11 |
12 | class ViewController: UIViewController {
13 |
14 | //MARK: - Properties
15 |
16 | @IBOutlet weak var chooseArticleButton: UIButton!
17 | @IBOutlet weak var amountButton: UIButton!
18 | @IBOutlet weak var chooseButton: UIButton!
19 | @IBOutlet weak var centeredDropDownButton: UIButton!
20 | @IBOutlet weak var rightBarButton: UIBarButtonItem!
21 | let textField = UITextField()
22 |
23 | //MARK: - DropDown's
24 |
25 | let chooseArticleDropDown = DropDown()
26 | let amountDropDown = DropDown()
27 | let chooseDropDown = DropDown()
28 | let centeredDropDown = DropDown()
29 | let rightBarDropDown = DropDown()
30 |
31 | lazy var dropDowns: [DropDown] = {
32 | return [
33 | self.chooseArticleDropDown,
34 | self.amountDropDown,
35 | self.chooseDropDown,
36 | self.centeredDropDown,
37 | self.rightBarDropDown
38 | ]
39 | }()
40 |
41 | //MARK: - Actions
42 |
43 | @IBAction func chooseArticle(_ sender: AnyObject) {
44 | chooseArticleDropDown.show()
45 | }
46 |
47 | @IBAction func changeAmount(_ sender: AnyObject) {
48 | amountDropDown.show()
49 | }
50 |
51 | @IBAction func choose(_ sender: AnyObject) {
52 | chooseDropDown.show()
53 | }
54 |
55 | @IBAction func showCenteredDropDown(_ sender: AnyObject) {
56 | centeredDropDown.show()
57 | }
58 |
59 | @IBAction func showBarButtonDropDown(_ sender: AnyObject) {
60 | rightBarDropDown.show()
61 | }
62 |
63 | @IBAction func changeDIsmissMode(_ sender: UISegmentedControl) {
64 | switch sender.selectedSegmentIndex {
65 | case 0: dropDowns.forEach { $0.dismissMode = .automatic }
66 | case 1: dropDowns.forEach { $0.dismissMode = .onTap }
67 | default: break;
68 | }
69 | }
70 |
71 | @IBAction func changeDirection(_ sender: UISegmentedControl) {
72 | switch sender.selectedSegmentIndex {
73 | case 0: dropDowns.forEach { $0.direction = .any }
74 | case 1: dropDowns.forEach { $0.direction = .bottom }
75 | case 2: dropDowns.forEach { $0.direction = .top }
76 | default: break;
77 | }
78 | }
79 |
80 | @IBAction func changeUI(_ sender: UISegmentedControl) {
81 | switch sender.selectedSegmentIndex {
82 | case 0: setupDefaultDropDown()
83 | case 1: customizeDropDown(self)
84 | default: break;
85 | }
86 | }
87 |
88 | @IBAction func showKeyboard(_ sender: AnyObject) {
89 | textField.becomeFirstResponder()
90 | }
91 |
92 | @IBAction func hideKeyboard(_ sender: AnyObject) {
93 | view.endEditing(false)
94 | }
95 |
96 | func setupDefaultDropDown() {
97 | DropDown.setupDefaultAppearance()
98 |
99 | dropDowns.forEach {
100 | $0.cellNib = UINib(nibName: "DropDownCell", bundle: Bundle(for: DropDownCell.self))
101 | $0.customCellConfiguration = nil
102 | }
103 | }
104 |
105 | func customizeDropDown(_ sender: AnyObject) {
106 | let appearance = DropDown.appearance()
107 |
108 | appearance.cellHeight = 60
109 | appearance.backgroundColor = UIColor(white: 1, alpha: 1)
110 | appearance.selectionBackgroundColor = UIColor(red: 0.6494, green: 0.8155, blue: 1.0, alpha: 0.2)
111 | // appearance.separatorColor = UIColor(white: 0.7, alpha: 0.8)
112 | appearance.cornerRadius = 10
113 | appearance.shadowColor = UIColor(white: 0.6, alpha: 1)
114 | appearance.shadowOpacity = 0.9
115 | appearance.shadowRadius = 25
116 | appearance.animationduration = 0.25
117 | appearance.textColor = .darkGray
118 | // appearance.textFont = UIFont(name: "Georgia", size: 14)
119 |
120 | if #available(iOS 11.0, *) {
121 | appearance.setupMaskedCorners([.layerMaxXMaxYCorner, .layerMinXMaxYCorner])
122 | }
123 |
124 | dropDowns.forEach {
125 | /*** FOR CUSTOM CELLS ***/
126 | $0.cellNib = UINib(nibName: "MyCell", bundle: nil)
127 |
128 | $0.customCellConfiguration = { (index: Index, item: String, cell: DropDownCell) -> Void in
129 | guard let cell = cell as? MyCell else { return }
130 |
131 | // Setup your custom UI components
132 | cell.logoImageView.image = UIImage(named: "logo_\(index % 10)")
133 | }
134 | /*** ---------------- ***/
135 | }
136 | }
137 |
138 | //MARK: - UIViewController
139 |
140 | override func viewDidLoad() {
141 | super.viewDidLoad()
142 |
143 | setupDropDowns()
144 | dropDowns.forEach { $0.dismissMode = .onTap }
145 | dropDowns.forEach { $0.direction = .any }
146 |
147 | view.addSubview(textField)
148 | }
149 |
150 | //MARK: - Setup
151 |
152 | func setupDropDowns() {
153 | setupChooseArticleDropDown()
154 | setupAmountDropDown()
155 | setupChooseDropDown()
156 | setupCenteredDropDown()
157 | setupRightBarDropDown()
158 | }
159 |
160 | func setupChooseArticleDropDown() {
161 | chooseArticleDropDown.anchorView = chooseArticleButton
162 |
163 | // Will set a custom with instead of anchor view width
164 | // dropDown.width = 100
165 |
166 | // By default, the dropdown will have its origin on the top left corner of its anchor view
167 | // So it will come over the anchor view and hide it completely
168 | // If you want to have the dropdown underneath your anchor view, you can do this:
169 | chooseArticleDropDown.bottomOffset = CGPoint(x: 0, y: chooseArticleButton.bounds.height)
170 |
171 | // You can also use localizationKeysDataSource instead. Check the docs.
172 | chooseArticleDropDown.dataSource = [
173 | "iPhone SE | Black | 64G",
174 | "Samsung S7",
175 | "Huawei P8 Lite Smartphone 4G",
176 | "Asus Zenfone Max 4G",
177 | "Apple Watwh | Sport Edition"
178 | ]
179 |
180 | // Action triggered on selection
181 | chooseArticleDropDown.selectionAction = { [weak self] (index, item) in
182 | self?.chooseArticleButton.setTitle(item, for: .normal)
183 | }
184 |
185 | chooseArticleDropDown.multiSelectionAction = { [weak self] (indices, items) in
186 | print("Muti selection action called with: \(items)")
187 | if items.isEmpty {
188 | self?.chooseArticleButton.setTitle("", for: .normal)
189 | }
190 | }
191 |
192 | // Action triggered on dropdown cancelation (hide)
193 | // dropDown.cancelAction = { [unowned self] in
194 | // // You could for example deselect the selected item
195 | // self.dropDown.deselectRowAtIndexPath(self.dropDown.indexForSelectedRow)
196 | // self.actionButton.setTitle("Canceled", forState: .Normal)
197 | // }
198 |
199 | // You can manually select a row if needed
200 | // dropDown.selectRowAtIndex(3)
201 | }
202 |
203 | func setupAmountDropDown() {
204 | amountDropDown.anchorView = amountButton
205 |
206 | // By default, the dropdown will have its origin on the top left corner of its anchor view
207 | // So it will come over the anchor view and hide it completely
208 | // If you want to have the dropdown underneath your anchor view, you can do this:
209 | amountDropDown.bottomOffset = CGPoint(x: 0, y: amountButton.bounds.height)
210 |
211 | // You can also use localizationKeysDataSource instead. Check the docs.
212 | amountDropDown.dataSource = [
213 | "10 €",
214 | "20 €",
215 | "30 €",
216 | "40 €",
217 | "50 €",
218 | "60 €",
219 | "70 €",
220 | "80 €",
221 | "90 €",
222 | "100 €",
223 | "110 €",
224 | "120 €"
225 | ]
226 |
227 | // Action triggered on selection
228 | amountDropDown.selectionAction = { [weak self] (index, item) in
229 | self?.amountButton.setTitle(item, for: .normal)
230 | }
231 | }
232 |
233 | func setupChooseDropDown() {
234 | chooseDropDown.anchorView = chooseButton
235 |
236 | // By default, the dropdown will have its origin on the top left corner of its anchor view
237 | // So it will come over the anchor view and hide it completely
238 | // If you want to have the dropdown underneath your anchor view, you can do this:
239 | chooseDropDown.bottomOffset = CGPoint(x: 0, y: chooseButton.bounds.height)
240 |
241 | // You can also use localizationKeysDataSource instead. Check the docs.
242 | chooseDropDown.dataSource = [
243 | "Lorem ipsum dolor",
244 | "sit amet consectetur",
245 | "cadipisci en..."
246 | ]
247 |
248 | // Action triggered on selection
249 | chooseDropDown.selectionAction = { [weak self] (index, item) in
250 | self?.chooseButton.setTitle(item, for: .normal)
251 | }
252 | }
253 |
254 | func setupCenteredDropDown() {
255 | // Not setting the anchor view makes the drop down centered on screen
256 | // centeredDropDown.anchorView = centeredDropDownButton
257 |
258 | // You can also use localizationKeysDataSource instead. Check the docs.
259 | centeredDropDown.dataSource = [
260 | "The drop down",
261 | "Is centered on",
262 | "the view because",
263 | "it has no anchor view defined.",
264 | "Click anywhere to dismiss."
265 | ]
266 |
267 | centeredDropDown.selectionAction = { [weak self] (index, item) in
268 | self?.centeredDropDownButton.setTitle(item, for: .normal)
269 | }
270 | }
271 |
272 | func setupRightBarDropDown() {
273 | rightBarDropDown.anchorView = rightBarButton
274 |
275 | // You can also use localizationKeysDataSource instead. Check the docs.
276 | rightBarDropDown.dataSource = [
277 | "Menu 1",
278 | "Menu 2",
279 | "Menu 3",
280 | "Menu 4"
281 | ]
282 | }
283 | }
284 |
--------------------------------------------------------------------------------
/DropDown.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = "DropDown"
4 | s.version = "2.3.13"
5 | s.summary = "A Material Design drop down"
6 |
7 | s.description = <<-DESC
8 | This drop down is to overcome the loss of usability and user experience due to the UIPickerView. Material Design did a good job there so this drop down is very inspired by it. It appears at the right location instead of the bottom of the screen as default with UIPickerView and if possible, all options are displayed at once.
9 | DESC
10 |
11 | s.homepage = "https://github.com/AssistoLab/DropDown"
12 | s.screenshots = "https://github.com/AssistoLab/DropDown/blob/master/Screenshots/1.png?raw=true", "https://github.com/AssistoLab/DropDown/blob/master/Screenshots/2.png?raw=true"
13 |
14 | s.license = { :type => "MIT", :file => "LICENSE" }
15 |
16 | s.author = { "kevin-hirsch" => "kevin.hirsch.be@gmail.com" }
17 | s.social_media_url = "http://twitter.com/kevinh6113"
18 |
19 | s.platform = :ios, '8.0'
20 | s.source = {
21 | :git => "https://github.com/AssistoLab/DropDown.git",
22 | :tag => "v#{s.version.to_s}"
23 | }
24 |
25 | s.source_files = "DropDown/src", "DropDown/src/**/*.{h,m}", "DropDown/helpers", "DropDown/helpers/**/*.{h,m}"
26 | s.resources = "DropDown/resources/*.{png,xib}"
27 | s.requires_arc = true
28 |
29 | s.swift_version = '5.0'
30 | end
31 |
--------------------------------------------------------------------------------
/DropDown.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0A76442B1B676C2300BF1A2D /* DropDownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A76442A1B676C2300BF1A2D /* DropDownTests.swift */; };
11 | 0A8518D51D6480190089529A /* MyCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A8518D41D6480190089529A /* MyCell.swift */; };
12 | 0A8518D71D6481E30089529A /* MyCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0A8518D61D6481E30089529A /* MyCell.xib */; };
13 | 0AB5D8881D0EEEFF002D3A17 /* DPDConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB5D87E1D0EEEFF002D3A17 /* DPDConstants.swift */; };
14 | 0AB5D8891D0EEEFF002D3A17 /* DPDKeyboardListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB5D87F1D0EEEFF002D3A17 /* DPDKeyboardListener.swift */; };
15 | 0AB5D88A1D0EEEFF002D3A17 /* DPDUIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB5D8801D0EEEFF002D3A17 /* DPDUIView+Extension.swift */; };
16 | 0AB5D88B1D0EEEFF002D3A17 /* DropDownCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0AB5D8821D0EEEFF002D3A17 /* DropDownCell.xib */; };
17 | 0AB5D88C1D0EEEFF002D3A17 /* DropDown+Appearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB5D8841D0EEEFF002D3A17 /* DropDown+Appearance.swift */; };
18 | 0AB5D88D1D0EEEFF002D3A17 /* DropDown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB5D8851D0EEEFF002D3A17 /* DropDown.swift */; };
19 | 0AB5D88E1D0EEEFF002D3A17 /* DropDownCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB5D8861D0EEEFF002D3A17 /* DropDownCell.swift */; };
20 | 0AB5D88F1D0EEEFF002D3A17 /* DropDown.h in Headers */ = {isa = PBXBuildFile; fileRef = 0AB5D8871D0EEEFF002D3A17 /* DropDown.h */; settings = {ATTRIBUTES = (Public, ); }; };
21 | 0AB5D8901D0EEFA8002D3A17 /* DropDown.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AB5D8711D0EEECD002D3A17 /* DropDown.framework */; };
22 | 0AB5D8911D0EEFA8002D3A17 /* DropDown.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0AB5D8711D0EEECD002D3A17 /* DropDown.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
23 | 0AB5D8981D0EF15D002D3A17 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB5D8951D0EF15D002D3A17 /* AppDelegate.swift */; };
24 | 0AB5D8991D0EF15D002D3A17 /* NiceButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB5D8961D0EF15D002D3A17 /* NiceButton.swift */; };
25 | 0AB5D89A1D0EF15D002D3A17 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AB5D8971D0EF15D002D3A17 /* ViewController.swift */; };
26 | 0AB5D8A11D0EF173002D3A17 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0AB5D89F1D0EF173002D3A17 /* Main.storyboard */; };
27 | 0AC1C33A1B6B884100A8DC0D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0AC1C3391B6B884100A8DC0D /* Images.xcassets */; };
28 | /* End PBXBuildFile section */
29 |
30 | /* Begin PBXContainerItemProxy section */
31 | 0A7644251B676C2300BF1A2D /* PBXContainerItemProxy */ = {
32 | isa = PBXContainerItemProxy;
33 | containerPortal = 0A7644071B676C2300BF1A2D /* Project object */;
34 | proxyType = 1;
35 | remoteGlobalIDString = 0A76440E1B676C2300BF1A2D;
36 | remoteInfo = DropDown;
37 | };
38 | 0AB5D8921D0EEFA8002D3A17 /* PBXContainerItemProxy */ = {
39 | isa = PBXContainerItemProxy;
40 | containerPortal = 0A7644071B676C2300BF1A2D /* Project object */;
41 | proxyType = 1;
42 | remoteGlobalIDString = 0AB5D8701D0EEECD002D3A17;
43 | remoteInfo = DropDown;
44 | };
45 | /* End PBXContainerItemProxy section */
46 |
47 | /* Begin PBXCopyFilesBuildPhase section */
48 | 0AB5D8941D0EEFA8002D3A17 /* Embed Frameworks */ = {
49 | isa = PBXCopyFilesBuildPhase;
50 | buildActionMask = 2147483647;
51 | dstPath = "";
52 | dstSubfolderSpec = 10;
53 | files = (
54 | 0AB5D8911D0EEFA8002D3A17 /* DropDown.framework in Embed Frameworks */,
55 | );
56 | name = "Embed Frameworks";
57 | runOnlyForDeploymentPostprocessing = 0;
58 | };
59 | /* End PBXCopyFilesBuildPhase section */
60 |
61 | /* Begin PBXFileReference section */
62 | 0A76440F1B676C2300BF1A2D /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
63 | 0A7644241B676C2300BF1A2D /* DropDownTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DropDownTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
64 | 0A7644291B676C2300BF1A2D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
65 | 0A76442A1B676C2300BF1A2D /* DropDownTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropDownTests.swift; sourceTree = ""; };
66 | 0A8518D41D6480190089529A /* MyCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyCell.swift; sourceTree = ""; };
67 | 0A8518D61D6481E30089529A /* MyCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MyCell.xib; sourceTree = ""; };
68 | 0AB5D8521D0EEC2A002D3A17 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = /Users/kevin/Documents/Xcode/DropDown/Info.plist; sourceTree = ""; };
69 | 0AB5D8711D0EEECD002D3A17 /* DropDown.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DropDown.framework; sourceTree = BUILT_PRODUCTS_DIR; };
70 | 0AB5D8751D0EEECD002D3A17 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
71 | 0AB5D87E1D0EEEFF002D3A17 /* DPDConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPDConstants.swift; sourceTree = ""; };
72 | 0AB5D87F1D0EEEFF002D3A17 /* DPDKeyboardListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DPDKeyboardListener.swift; sourceTree = ""; };
73 | 0AB5D8801D0EEEFF002D3A17 /* DPDUIView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DPDUIView+Extension.swift"; sourceTree = ""; };
74 | 0AB5D8821D0EEEFF002D3A17 /* DropDownCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DropDownCell.xib; sourceTree = ""; };
75 | 0AB5D8841D0EEEFF002D3A17 /* DropDown+Appearance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DropDown+Appearance.swift"; sourceTree = ""; };
76 | 0AB5D8851D0EEEFF002D3A17 /* DropDown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDown.swift; sourceTree = ""; };
77 | 0AB5D8861D0EEEFF002D3A17 /* DropDownCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropDownCell.swift; sourceTree = ""; };
78 | 0AB5D8871D0EEEFF002D3A17 /* DropDown.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DropDown.h; sourceTree = ""; };
79 | 0AB5D8951D0EF15D002D3A17 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
80 | 0AB5D8961D0EF15D002D3A17 /* NiceButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NiceButton.swift; sourceTree = ""; };
81 | 0AB5D8971D0EF15D002D3A17 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
82 | 0AB5D8A01D0EF173002D3A17 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
83 | 0AC1C3391B6B884100A8DC0D /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Demo/Images.xcassets; sourceTree = ""; };
84 | /* End PBXFileReference section */
85 |
86 | /* Begin PBXFrameworksBuildPhase section */
87 | 0A76440C1B676C2300BF1A2D /* Frameworks */ = {
88 | isa = PBXFrameworksBuildPhase;
89 | buildActionMask = 2147483647;
90 | files = (
91 | 0AB5D8901D0EEFA8002D3A17 /* DropDown.framework in Frameworks */,
92 | );
93 | runOnlyForDeploymentPostprocessing = 0;
94 | };
95 | 0A7644211B676C2300BF1A2D /* Frameworks */ = {
96 | isa = PBXFrameworksBuildPhase;
97 | buildActionMask = 2147483647;
98 | files = (
99 | );
100 | runOnlyForDeploymentPostprocessing = 0;
101 | };
102 | 0AB5D86D1D0EEECD002D3A17 /* Frameworks */ = {
103 | isa = PBXFrameworksBuildPhase;
104 | buildActionMask = 2147483647;
105 | files = (
106 | );
107 | runOnlyForDeploymentPostprocessing = 0;
108 | };
109 | /* End PBXFrameworksBuildPhase section */
110 |
111 | /* Begin PBXGroup section */
112 | 0A7644061B676C2300BF1A2D = {
113 | isa = PBXGroup;
114 | children = (
115 | 0AB5D8721D0EEECD002D3A17 /* DropDown */,
116 | 0A7644111B676C2300BF1A2D /* Demo */,
117 | 0AC1C3391B6B884100A8DC0D /* Images.xcassets */,
118 | 0A7644271B676C2300BF1A2D /* DropDownTests */,
119 | 0A7644101B676C2300BF1A2D /* Products */,
120 | );
121 | sourceTree = "";
122 | };
123 | 0A7644101B676C2300BF1A2D /* Products */ = {
124 | isa = PBXGroup;
125 | children = (
126 | 0A76440F1B676C2300BF1A2D /* Demo.app */,
127 | 0A7644241B676C2300BF1A2D /* DropDownTests.xctest */,
128 | 0AB5D8711D0EEECD002D3A17 /* DropDown.framework */,
129 | );
130 | name = Products;
131 | sourceTree = "";
132 | };
133 | 0A7644111B676C2300BF1A2D /* Demo */ = {
134 | isa = PBXGroup;
135 | children = (
136 | 0AB5D8951D0EF15D002D3A17 /* AppDelegate.swift */,
137 | 0AB5D8971D0EF15D002D3A17 /* ViewController.swift */,
138 | 0A8518D41D6480190089529A /* MyCell.swift */,
139 | 0A8518D61D6481E30089529A /* MyCell.xib */,
140 | 0AB5D89F1D0EF173002D3A17 /* Main.storyboard */,
141 | 0AB5D8961D0EF15D002D3A17 /* NiceButton.swift */,
142 | 0A7644121B676C2300BF1A2D /* Supporting Files */,
143 | );
144 | path = Demo;
145 | sourceTree = "";
146 | };
147 | 0A7644121B676C2300BF1A2D /* Supporting Files */ = {
148 | isa = PBXGroup;
149 | children = (
150 | 0AB5D8521D0EEC2A002D3A17 /* Info.plist */,
151 | );
152 | name = "Supporting Files";
153 | sourceTree = "";
154 | };
155 | 0A7644271B676C2300BF1A2D /* DropDownTests */ = {
156 | isa = PBXGroup;
157 | children = (
158 | 0A76442A1B676C2300BF1A2D /* DropDownTests.swift */,
159 | 0A7644281B676C2300BF1A2D /* Supporting Files */,
160 | );
161 | path = DropDownTests;
162 | sourceTree = "";
163 | };
164 | 0A7644281B676C2300BF1A2D /* Supporting Files */ = {
165 | isa = PBXGroup;
166 | children = (
167 | 0A7644291B676C2300BF1A2D /* Info.plist */,
168 | );
169 | name = "Supporting Files";
170 | sourceTree = "";
171 | };
172 | 0AB5D8721D0EEECD002D3A17 /* DropDown */ = {
173 | isa = PBXGroup;
174 | children = (
175 | 0AB5D8871D0EEEFF002D3A17 /* DropDown.h */,
176 | 0AB5D8751D0EEECD002D3A17 /* Info.plist */,
177 | 0AB5D87D1D0EEEFF002D3A17 /* helpers */,
178 | 0AB5D8811D0EEEFF002D3A17 /* resources */,
179 | 0AB5D8831D0EEEFF002D3A17 /* src */,
180 | );
181 | path = DropDown;
182 | sourceTree = "";
183 | };
184 | 0AB5D87D1D0EEEFF002D3A17 /* helpers */ = {
185 | isa = PBXGroup;
186 | children = (
187 | 0AB5D87E1D0EEEFF002D3A17 /* DPDConstants.swift */,
188 | 0AB5D87F1D0EEEFF002D3A17 /* DPDKeyboardListener.swift */,
189 | 0AB5D8801D0EEEFF002D3A17 /* DPDUIView+Extension.swift */,
190 | );
191 | path = helpers;
192 | sourceTree = "";
193 | };
194 | 0AB5D8811D0EEEFF002D3A17 /* resources */ = {
195 | isa = PBXGroup;
196 | children = (
197 | 0AB5D8821D0EEEFF002D3A17 /* DropDownCell.xib */,
198 | );
199 | path = resources;
200 | sourceTree = "";
201 | };
202 | 0AB5D8831D0EEEFF002D3A17 /* src */ = {
203 | isa = PBXGroup;
204 | children = (
205 | 0AB5D8841D0EEEFF002D3A17 /* DropDown+Appearance.swift */,
206 | 0AB5D8851D0EEEFF002D3A17 /* DropDown.swift */,
207 | 0AB5D8861D0EEEFF002D3A17 /* DropDownCell.swift */,
208 | );
209 | path = src;
210 | sourceTree = "";
211 | };
212 | /* End PBXGroup section */
213 |
214 | /* Begin PBXHeadersBuildPhase section */
215 | 0AB5D86E1D0EEECD002D3A17 /* Headers */ = {
216 | isa = PBXHeadersBuildPhase;
217 | buildActionMask = 2147483647;
218 | files = (
219 | 0AB5D88F1D0EEEFF002D3A17 /* DropDown.h in Headers */,
220 | );
221 | runOnlyForDeploymentPostprocessing = 0;
222 | };
223 | /* End PBXHeadersBuildPhase section */
224 |
225 | /* Begin PBXNativeTarget section */
226 | 0A76440E1B676C2300BF1A2D /* Demo */ = {
227 | isa = PBXNativeTarget;
228 | buildConfigurationList = 0A76442E1B676C2300BF1A2D /* Build configuration list for PBXNativeTarget "Demo" */;
229 | buildPhases = (
230 | 0A76440B1B676C2300BF1A2D /* Sources */,
231 | 0A76440C1B676C2300BF1A2D /* Frameworks */,
232 | 0A76440D1B676C2300BF1A2D /* Resources */,
233 | 0AB5D8941D0EEFA8002D3A17 /* Embed Frameworks */,
234 | );
235 | buildRules = (
236 | );
237 | dependencies = (
238 | 0AB5D8931D0EEFA8002D3A17 /* PBXTargetDependency */,
239 | );
240 | name = Demo;
241 | productName = DropDown;
242 | productReference = 0A76440F1B676C2300BF1A2D /* Demo.app */;
243 | productType = "com.apple.product-type.application";
244 | };
245 | 0A7644231B676C2300BF1A2D /* DropDownTests */ = {
246 | isa = PBXNativeTarget;
247 | buildConfigurationList = 0A7644311B676C2300BF1A2D /* Build configuration list for PBXNativeTarget "DropDownTests" */;
248 | buildPhases = (
249 | 0A7644201B676C2300BF1A2D /* Sources */,
250 | 0A7644211B676C2300BF1A2D /* Frameworks */,
251 | 0A7644221B676C2300BF1A2D /* Resources */,
252 | );
253 | buildRules = (
254 | );
255 | dependencies = (
256 | 0A7644261B676C2300BF1A2D /* PBXTargetDependency */,
257 | );
258 | name = DropDownTests;
259 | productName = DropDownTests;
260 | productReference = 0A7644241B676C2300BF1A2D /* DropDownTests.xctest */;
261 | productType = "com.apple.product-type.bundle.unit-test";
262 | };
263 | 0AB5D8701D0EEECD002D3A17 /* DropDown */ = {
264 | isa = PBXNativeTarget;
265 | buildConfigurationList = 0AB5D87A1D0EEECD002D3A17 /* Build configuration list for PBXNativeTarget "DropDown" */;
266 | buildPhases = (
267 | 0AB5D86C1D0EEECD002D3A17 /* Sources */,
268 | 0AB5D86D1D0EEECD002D3A17 /* Frameworks */,
269 | 0AB5D86E1D0EEECD002D3A17 /* Headers */,
270 | 0AB5D86F1D0EEECD002D3A17 /* Resources */,
271 | );
272 | buildRules = (
273 | );
274 | dependencies = (
275 | );
276 | name = DropDown;
277 | productName = DropDown;
278 | productReference = 0AB5D8711D0EEECD002D3A17 /* DropDown.framework */;
279 | productType = "com.apple.product-type.framework";
280 | };
281 | /* End PBXNativeTarget section */
282 |
283 | /* Begin PBXProject section */
284 | 0A7644071B676C2300BF1A2D /* Project object */ = {
285 | isa = PBXProject;
286 | attributes = {
287 | LastSwiftUpdateCheck = 0730;
288 | LastUpgradeCheck = 1020;
289 | ORGANIZATIONNAME = "Kevin Hirsch";
290 | TargetAttributes = {
291 | 0A76440E1B676C2300BF1A2D = {
292 | CreatedOnToolsVersion = 6.4;
293 | LastSwiftMigration = 1020;
294 | };
295 | 0A7644231B676C2300BF1A2D = {
296 | CreatedOnToolsVersion = 6.4;
297 | LastSwiftMigration = 1020;
298 | TestTargetID = 0A76440E1B676C2300BF1A2D;
299 | };
300 | 0AB5D8701D0EEECD002D3A17 = {
301 | CreatedOnToolsVersion = 7.3.1;
302 | LastSwiftMigration = 1020;
303 | };
304 | };
305 | };
306 | buildConfigurationList = 0A76440A1B676C2300BF1A2D /* Build configuration list for PBXProject "DropDown" */;
307 | compatibilityVersion = "Xcode 3.2";
308 | developmentRegion = en;
309 | hasScannedForEncodings = 0;
310 | knownRegions = (
311 | en,
312 | Base,
313 | );
314 | mainGroup = 0A7644061B676C2300BF1A2D;
315 | productRefGroup = 0A7644101B676C2300BF1A2D /* Products */;
316 | projectDirPath = "";
317 | projectRoot = "";
318 | targets = (
319 | 0A76440E1B676C2300BF1A2D /* Demo */,
320 | 0A7644231B676C2300BF1A2D /* DropDownTests */,
321 | 0AB5D8701D0EEECD002D3A17 /* DropDown */,
322 | );
323 | };
324 | /* End PBXProject section */
325 |
326 | /* Begin PBXResourcesBuildPhase section */
327 | 0A76440D1B676C2300BF1A2D /* Resources */ = {
328 | isa = PBXResourcesBuildPhase;
329 | buildActionMask = 2147483647;
330 | files = (
331 | 0AC1C33A1B6B884100A8DC0D /* Images.xcassets in Resources */,
332 | 0A8518D71D6481E30089529A /* MyCell.xib in Resources */,
333 | 0AB5D8A11D0EF173002D3A17 /* Main.storyboard in Resources */,
334 | );
335 | runOnlyForDeploymentPostprocessing = 0;
336 | };
337 | 0A7644221B676C2300BF1A2D /* Resources */ = {
338 | isa = PBXResourcesBuildPhase;
339 | buildActionMask = 2147483647;
340 | files = (
341 | );
342 | runOnlyForDeploymentPostprocessing = 0;
343 | };
344 | 0AB5D86F1D0EEECD002D3A17 /* Resources */ = {
345 | isa = PBXResourcesBuildPhase;
346 | buildActionMask = 2147483647;
347 | files = (
348 | 0AB5D88B1D0EEEFF002D3A17 /* DropDownCell.xib in Resources */,
349 | );
350 | runOnlyForDeploymentPostprocessing = 0;
351 | };
352 | /* End PBXResourcesBuildPhase section */
353 |
354 | /* Begin PBXSourcesBuildPhase section */
355 | 0A76440B1B676C2300BF1A2D /* Sources */ = {
356 | isa = PBXSourcesBuildPhase;
357 | buildActionMask = 2147483647;
358 | files = (
359 | 0AB5D89A1D0EF15D002D3A17 /* ViewController.swift in Sources */,
360 | 0A8518D51D6480190089529A /* MyCell.swift in Sources */,
361 | 0AB5D8991D0EF15D002D3A17 /* NiceButton.swift in Sources */,
362 | 0AB5D8981D0EF15D002D3A17 /* AppDelegate.swift in Sources */,
363 | );
364 | runOnlyForDeploymentPostprocessing = 0;
365 | };
366 | 0A7644201B676C2300BF1A2D /* Sources */ = {
367 | isa = PBXSourcesBuildPhase;
368 | buildActionMask = 2147483647;
369 | files = (
370 | 0A76442B1B676C2300BF1A2D /* DropDownTests.swift in Sources */,
371 | );
372 | runOnlyForDeploymentPostprocessing = 0;
373 | };
374 | 0AB5D86C1D0EEECD002D3A17 /* Sources */ = {
375 | isa = PBXSourcesBuildPhase;
376 | buildActionMask = 2147483647;
377 | files = (
378 | 0AB5D88D1D0EEEFF002D3A17 /* DropDown.swift in Sources */,
379 | 0AB5D88C1D0EEEFF002D3A17 /* DropDown+Appearance.swift in Sources */,
380 | 0AB5D88A1D0EEEFF002D3A17 /* DPDUIView+Extension.swift in Sources */,
381 | 0AB5D8881D0EEEFF002D3A17 /* DPDConstants.swift in Sources */,
382 | 0AB5D88E1D0EEEFF002D3A17 /* DropDownCell.swift in Sources */,
383 | 0AB5D8891D0EEEFF002D3A17 /* DPDKeyboardListener.swift in Sources */,
384 | );
385 | runOnlyForDeploymentPostprocessing = 0;
386 | };
387 | /* End PBXSourcesBuildPhase section */
388 |
389 | /* Begin PBXTargetDependency section */
390 | 0A7644261B676C2300BF1A2D /* PBXTargetDependency */ = {
391 | isa = PBXTargetDependency;
392 | target = 0A76440E1B676C2300BF1A2D /* Demo */;
393 | targetProxy = 0A7644251B676C2300BF1A2D /* PBXContainerItemProxy */;
394 | };
395 | 0AB5D8931D0EEFA8002D3A17 /* PBXTargetDependency */ = {
396 | isa = PBXTargetDependency;
397 | target = 0AB5D8701D0EEECD002D3A17 /* DropDown */;
398 | targetProxy = 0AB5D8921D0EEFA8002D3A17 /* PBXContainerItemProxy */;
399 | };
400 | /* End PBXTargetDependency section */
401 |
402 | /* Begin PBXVariantGroup section */
403 | 0AB5D89F1D0EF173002D3A17 /* Main.storyboard */ = {
404 | isa = PBXVariantGroup;
405 | children = (
406 | 0AB5D8A01D0EF173002D3A17 /* Base */,
407 | );
408 | name = Main.storyboard;
409 | sourceTree = "";
410 | };
411 | /* End PBXVariantGroup section */
412 |
413 | /* Begin XCBuildConfiguration section */
414 | 0A76442C1B676C2300BF1A2D /* Debug */ = {
415 | isa = XCBuildConfiguration;
416 | buildSettings = {
417 | ALWAYS_SEARCH_USER_PATHS = NO;
418 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
419 | CLANG_CXX_LIBRARY = "libc++";
420 | CLANG_ENABLE_MODULES = YES;
421 | CLANG_ENABLE_OBJC_ARC = YES;
422 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
423 | CLANG_WARN_BOOL_CONVERSION = YES;
424 | CLANG_WARN_COMMA = YES;
425 | CLANG_WARN_CONSTANT_CONVERSION = YES;
426 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
427 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
428 | CLANG_WARN_EMPTY_BODY = YES;
429 | CLANG_WARN_ENUM_CONVERSION = YES;
430 | CLANG_WARN_INFINITE_RECURSION = YES;
431 | CLANG_WARN_INT_CONVERSION = YES;
432 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
433 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
434 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
435 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
436 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
437 | CLANG_WARN_STRICT_PROTOTYPES = YES;
438 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
439 | CLANG_WARN_UNREACHABLE_CODE = YES;
440 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
441 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
442 | COPY_PHASE_STRIP = NO;
443 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
444 | ENABLE_STRICT_OBJC_MSGSEND = YES;
445 | ENABLE_TESTABILITY = YES;
446 | GCC_C_LANGUAGE_STANDARD = gnu99;
447 | GCC_DYNAMIC_NO_PIC = NO;
448 | GCC_NO_COMMON_BLOCKS = YES;
449 | GCC_OPTIMIZATION_LEVEL = 0;
450 | GCC_PREPROCESSOR_DEFINITIONS = (
451 | "DEBUG=1",
452 | "$(inherited)",
453 | );
454 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
455 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
456 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
457 | GCC_WARN_UNDECLARED_SELECTOR = YES;
458 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
459 | GCC_WARN_UNUSED_FUNCTION = YES;
460 | GCC_WARN_UNUSED_VARIABLE = YES;
461 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
462 | MTL_ENABLE_DEBUG_INFO = YES;
463 | ONLY_ACTIVE_ARCH = YES;
464 | SDKROOT = iphoneos;
465 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
466 | SWIFT_VERSION = 4.2;
467 | };
468 | name = Debug;
469 | };
470 | 0A76442D1B676C2300BF1A2D /* Release */ = {
471 | isa = XCBuildConfiguration;
472 | buildSettings = {
473 | ALWAYS_SEARCH_USER_PATHS = NO;
474 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
475 | CLANG_CXX_LIBRARY = "libc++";
476 | CLANG_ENABLE_MODULES = YES;
477 | CLANG_ENABLE_OBJC_ARC = YES;
478 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
479 | CLANG_WARN_BOOL_CONVERSION = YES;
480 | CLANG_WARN_COMMA = YES;
481 | CLANG_WARN_CONSTANT_CONVERSION = YES;
482 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
483 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
484 | CLANG_WARN_EMPTY_BODY = YES;
485 | CLANG_WARN_ENUM_CONVERSION = YES;
486 | CLANG_WARN_INFINITE_RECURSION = YES;
487 | CLANG_WARN_INT_CONVERSION = YES;
488 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
489 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
490 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
491 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
492 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
493 | CLANG_WARN_STRICT_PROTOTYPES = YES;
494 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
495 | CLANG_WARN_UNREACHABLE_CODE = YES;
496 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
497 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
498 | COPY_PHASE_STRIP = NO;
499 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
500 | ENABLE_NS_ASSERTIONS = NO;
501 | ENABLE_STRICT_OBJC_MSGSEND = YES;
502 | GCC_C_LANGUAGE_STANDARD = gnu99;
503 | GCC_NO_COMMON_BLOCKS = YES;
504 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
505 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
506 | GCC_WARN_UNDECLARED_SELECTOR = YES;
507 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
508 | GCC_WARN_UNUSED_FUNCTION = YES;
509 | GCC_WARN_UNUSED_VARIABLE = YES;
510 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
511 | MTL_ENABLE_DEBUG_INFO = NO;
512 | SDKROOT = iphoneos;
513 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
514 | SWIFT_VERSION = 4.2;
515 | VALIDATE_PRODUCT = YES;
516 | };
517 | name = Release;
518 | };
519 | 0A76442F1B676C2300BF1A2D /* Debug */ = {
520 | isa = XCBuildConfiguration;
521 | buildSettings = {
522 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
523 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
524 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
525 | CLANG_ENABLE_MODULES = YES;
526 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
527 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
528 | PRODUCT_BUNDLE_IDENTIFIER = com.assistoLab.Demo;
529 | PRODUCT_NAME = "$(TARGET_NAME)";
530 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
531 | SWIFT_VERSION = 5.0;
532 | TARGETED_DEVICE_FAMILY = "1,2";
533 | };
534 | name = Debug;
535 | };
536 | 0A7644301B676C2300BF1A2D /* Release */ = {
537 | isa = XCBuildConfiguration;
538 | buildSettings = {
539 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
540 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
541 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
542 | CLANG_ENABLE_MODULES = YES;
543 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
544 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
545 | PRODUCT_BUNDLE_IDENTIFIER = com.assistoLab.Demo;
546 | PRODUCT_NAME = "$(TARGET_NAME)";
547 | SWIFT_VERSION = 5.0;
548 | TARGETED_DEVICE_FAMILY = "1,2";
549 | };
550 | name = Release;
551 | };
552 | 0A7644321B676C2300BF1A2D /* Debug */ = {
553 | isa = XCBuildConfiguration;
554 | buildSettings = {
555 | BUNDLE_LOADER = "$(TEST_HOST)";
556 | GCC_PREPROCESSOR_DEFINITIONS = (
557 | "DEBUG=1",
558 | "$(inherited)",
559 | );
560 | INFOPLIST_FILE = DropDownTests/Info.plist;
561 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
562 | PRODUCT_BUNDLE_IDENTIFIER = "com.kevin.hirsch.$(PRODUCT_NAME:rfc1034identifier)";
563 | PRODUCT_NAME = "$(TARGET_NAME)";
564 | SWIFT_VERSION = 5.0;
565 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Demo.app/Demo";
566 | };
567 | name = Debug;
568 | };
569 | 0A7644331B676C2300BF1A2D /* Release */ = {
570 | isa = XCBuildConfiguration;
571 | buildSettings = {
572 | BUNDLE_LOADER = "$(TEST_HOST)";
573 | INFOPLIST_FILE = DropDownTests/Info.plist;
574 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
575 | PRODUCT_BUNDLE_IDENTIFIER = "com.kevin.hirsch.$(PRODUCT_NAME:rfc1034identifier)";
576 | PRODUCT_NAME = "$(TARGET_NAME)";
577 | SWIFT_VERSION = 5.0;
578 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Demo.app/Demo";
579 | };
580 | name = Release;
581 | };
582 | 0AB5D87B1D0EEECD002D3A17 /* Debug */ = {
583 | isa = XCBuildConfiguration;
584 | buildSettings = {
585 | CLANG_ANALYZER_NONNULL = YES;
586 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
587 | CURRENT_PROJECT_VERSION = 1;
588 | DEBUG_INFORMATION_FORMAT = dwarf;
589 | DEFINES_MODULE = YES;
590 | DYLIB_COMPATIBILITY_VERSION = 1;
591 | DYLIB_CURRENT_VERSION = 1;
592 | DYLIB_INSTALL_NAME_BASE = "@rpath";
593 | ENABLE_TESTABILITY = YES;
594 | INFOPLIST_FILE = DropDown/Info.plist;
595 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
596 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
597 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
598 | PRODUCT_BUNDLE_IDENTIFIER = com.assistoLab.DropDown;
599 | PRODUCT_NAME = "$(TARGET_NAME)";
600 | SKIP_INSTALL = YES;
601 | SWIFT_VERSION = 5.0;
602 | TARGETED_DEVICE_FAMILY = "1,2";
603 | VERSIONING_SYSTEM = "apple-generic";
604 | VERSION_INFO_PREFIX = "";
605 | };
606 | name = Debug;
607 | };
608 | 0AB5D87C1D0EEECD002D3A17 /* Release */ = {
609 | isa = XCBuildConfiguration;
610 | buildSettings = {
611 | CLANG_ANALYZER_NONNULL = YES;
612 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
613 | CURRENT_PROJECT_VERSION = 1;
614 | DEFINES_MODULE = YES;
615 | DYLIB_COMPATIBILITY_VERSION = 1;
616 | DYLIB_CURRENT_VERSION = 1;
617 | DYLIB_INSTALL_NAME_BASE = "@rpath";
618 | INFOPLIST_FILE = DropDown/Info.plist;
619 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
620 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
621 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
622 | PRODUCT_BUNDLE_IDENTIFIER = com.assistoLab.DropDown;
623 | PRODUCT_NAME = "$(TARGET_NAME)";
624 | SKIP_INSTALL = YES;
625 | SWIFT_VERSION = 5.0;
626 | TARGETED_DEVICE_FAMILY = "1,2";
627 | VERSIONING_SYSTEM = "apple-generic";
628 | VERSION_INFO_PREFIX = "";
629 | };
630 | name = Release;
631 | };
632 | /* End XCBuildConfiguration section */
633 |
634 | /* Begin XCConfigurationList section */
635 | 0A76440A1B676C2300BF1A2D /* Build configuration list for PBXProject "DropDown" */ = {
636 | isa = XCConfigurationList;
637 | buildConfigurations = (
638 | 0A76442C1B676C2300BF1A2D /* Debug */,
639 | 0A76442D1B676C2300BF1A2D /* Release */,
640 | );
641 | defaultConfigurationIsVisible = 0;
642 | defaultConfigurationName = Release;
643 | };
644 | 0A76442E1B676C2300BF1A2D /* Build configuration list for PBXNativeTarget "Demo" */ = {
645 | isa = XCConfigurationList;
646 | buildConfigurations = (
647 | 0A76442F1B676C2300BF1A2D /* Debug */,
648 | 0A7644301B676C2300BF1A2D /* Release */,
649 | );
650 | defaultConfigurationIsVisible = 0;
651 | defaultConfigurationName = Release;
652 | };
653 | 0A7644311B676C2300BF1A2D /* Build configuration list for PBXNativeTarget "DropDownTests" */ = {
654 | isa = XCConfigurationList;
655 | buildConfigurations = (
656 | 0A7644321B676C2300BF1A2D /* Debug */,
657 | 0A7644331B676C2300BF1A2D /* Release */,
658 | );
659 | defaultConfigurationIsVisible = 0;
660 | defaultConfigurationName = Release;
661 | };
662 | 0AB5D87A1D0EEECD002D3A17 /* Build configuration list for PBXNativeTarget "DropDown" */ = {
663 | isa = XCConfigurationList;
664 | buildConfigurations = (
665 | 0AB5D87B1D0EEECD002D3A17 /* Debug */,
666 | 0AB5D87C1D0EEECD002D3A17 /* Release */,
667 | );
668 | defaultConfigurationIsVisible = 0;
669 | defaultConfigurationName = Release;
670 | };
671 | /* End XCConfigurationList section */
672 | };
673 | rootObject = 0A7644071B676C2300BF1A2D /* Project object */;
674 | }
675 |
--------------------------------------------------------------------------------
/DropDown.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/DropDown.xcodeproj/project.xcworkspace/xcshareddata/DropDown.xcscmblueprint:
--------------------------------------------------------------------------------
1 | {
2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "6575E401F0E075B1FD6E179DAD8CCDF3853A7A8E",
3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4 |
5 | },
6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7 | "E4EC3EC9984A396270972820B35B740A9BEF1CCB" : 0,
8 | "6575E401F0E075B1FD6E179DAD8CCDF3853A7A8E" : 0
9 | },
10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "26F545BD-A325-473F-B7B3-A037533DDB11",
11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
12 | "E4EC3EC9984A396270972820B35B740A9BEF1CCB" : "",
13 | "6575E401F0E075B1FD6E179DAD8CCDF3853A7A8E" : "DropDown\/"
14 | },
15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "DropDown",
16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204,
17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "DropDown.xcodeproj",
18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
19 | {
20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/kevin-hirsch\/DropDown.git",
21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "6575E401F0E075B1FD6E179DAD8CCDF3853A7A8E"
23 | },
24 | {
25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/kevin-hirsch\/FloatingLabel.git",
26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "E4EC3EC9984A396270972820B35B740A9BEF1CCB"
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/DropDown.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/DropDown.xcodeproj/xcshareddata/xcschemes/DropDown.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/DropDown/DropDown.h:
--------------------------------------------------------------------------------
1 | //
2 | // DropDown.h
3 | // DropDown
4 | //
5 | // Created by Kevin Hirsch on 13/06/16.
6 | // Copyright © 2016 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for DropDown.
12 | FOUNDATION_EXPORT double DropDownVersionNumber;
13 |
14 | //! Project version string for DropDown.
15 | FOUNDATION_EXPORT const unsigned char DropDownVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/DropDown/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 | FMWK
17 | CFBundleShortVersionString
18 | 2.3.12
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/DropDown/helpers/DPDConstants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constants.swift
3 | // DropDown
4 | //
5 | // Created by Kevin Hirsch on 28/07/15.
6 | // Copyright (c) 2015 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | import UIKit
12 |
13 | internal struct DPDConstant {
14 |
15 | internal struct KeyPath {
16 |
17 | static let Frame = "frame"
18 |
19 | }
20 |
21 | internal struct ReusableIdentifier {
22 |
23 | static let DropDownCell = "DropDownCell"
24 |
25 | }
26 |
27 | internal struct UI {
28 |
29 | static let TextColor = UIColor.black
30 | static let SelectedTextColor = UIColor.black
31 | static let TextFont = UIFont.systemFont(ofSize: 15)
32 | static let BackgroundColor = UIColor(white: 0.94, alpha: 1)
33 | static let SelectionBackgroundColor = UIColor(white: 0.89, alpha: 1)
34 | static let SeparatorColor = UIColor.clear
35 | static let CornerRadius: CGFloat = 2
36 | static let RowHeight: CGFloat = 44
37 | static let HeightPadding: CGFloat = 20
38 |
39 | struct Shadow {
40 |
41 | static let Color = UIColor.darkGray
42 | static let Offset = CGSize.zero
43 | static let Opacity: Float = 0.4
44 | static let Radius: CGFloat = 8
45 |
46 | }
47 |
48 | }
49 |
50 | internal struct Animation {
51 |
52 | static let Duration = 0.15
53 | static let EntranceOptions: UIView.AnimationOptions = [.allowUserInteraction, .curveEaseOut]
54 | static let ExitOptions: UIView.AnimationOptions = [.allowUserInteraction, .curveEaseIn]
55 | static let DownScaleTransform = CGAffineTransform(scaleX: 0.9, y: 0.9)
56 |
57 | }
58 |
59 | }
60 |
61 | #endif
62 |
--------------------------------------------------------------------------------
/DropDown/helpers/DPDKeyboardListener.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeyboardListener.swift
3 | // DropDown
4 | //
5 | // Created by Kevin Hirsch on 30/07/15.
6 | // Copyright (c) 2015 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | import UIKit
12 |
13 | internal final class KeyboardListener {
14 |
15 | static let sharedInstance = KeyboardListener()
16 |
17 | fileprivate(set) var isVisible = false
18 | fileprivate(set) var keyboardFrame = CGRect.zero
19 | fileprivate var isListening = false
20 |
21 | deinit {
22 | stopListeningToKeyboard()
23 | }
24 |
25 | }
26 |
27 | //MARK: - Notifications
28 |
29 | extension KeyboardListener {
30 |
31 | func startListeningToKeyboard() {
32 | if isListening {
33 | return
34 | }
35 |
36 | isListening = true
37 |
38 | NotificationCenter.default.addObserver(
39 | self,
40 | selector: #selector(keyboardWillShow(_:)),
41 | name: UIResponder.keyboardWillShowNotification,
42 | object: nil)
43 | NotificationCenter.default.addObserver(
44 | self,
45 | selector: #selector(keyboardWillHide(_:)),
46 | name: UIResponder.keyboardWillHideNotification,
47 | object: nil)
48 | }
49 |
50 | func stopListeningToKeyboard() {
51 | NotificationCenter.default.removeObserver(self)
52 | }
53 |
54 | @objc
55 | fileprivate func keyboardWillShow(_ notification: Notification) {
56 | isVisible = true
57 | keyboardFrame = keyboardFrame(fromNotification: notification)
58 | }
59 |
60 | @objc
61 | fileprivate func keyboardWillHide(_ notification: Notification) {
62 | isVisible = false
63 | keyboardFrame = keyboardFrame(fromNotification: notification)
64 | }
65 |
66 | fileprivate func keyboardFrame(fromNotification notification: Notification) -> CGRect {
67 | return ((notification as NSNotification).userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue ?? CGRect.zero
68 | }
69 |
70 | }
71 |
72 | #endif
73 |
--------------------------------------------------------------------------------
/DropDown/helpers/DPDUIView+Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Constraints.swift
3 | // DropDown
4 | //
5 | // Created by Kevin Hirsch on 28/07/15.
6 | // Copyright (c) 2015 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | import UIKit
12 |
13 | //MARK: - Constraints
14 |
15 | internal extension UIView {
16 |
17 | func addConstraints(format: String, options: NSLayoutConstraint.FormatOptions = [], metrics: [String: AnyObject]? = nil, views: [String: UIView]) {
18 | addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: options, metrics: metrics, views: views))
19 | }
20 |
21 | func addUniversalConstraints(format: String, options: NSLayoutConstraint.FormatOptions = [], metrics: [String: AnyObject]? = nil, views: [String: UIView]) {
22 | addConstraints(format: "H:\(format)", options: options, metrics: metrics, views: views)
23 | addConstraints(format: "V:\(format)", options: options, metrics: metrics, views: views)
24 | }
25 |
26 | }
27 |
28 |
29 |
30 | //MARK: - Bounds
31 |
32 | internal extension UIView {
33 |
34 | var windowFrame: CGRect? {
35 | return superview?.convert(frame, to: nil)
36 | }
37 |
38 | }
39 |
40 | internal extension UIWindow {
41 |
42 | static func visibleWindow() -> UIWindow? {
43 | var currentWindow = UIApplication.shared.keyWindow
44 |
45 | if currentWindow == nil {
46 | let frontToBackWindows = Array(UIApplication.shared.windows.reversed())
47 |
48 | for window in frontToBackWindows {
49 | if window.windowLevel == UIWindow.Level.normal {
50 | currentWindow = window
51 | break
52 | }
53 | }
54 | }
55 |
56 | return currentWindow
57 | }
58 |
59 | }
60 |
61 | #endif
62 |
--------------------------------------------------------------------------------
/DropDown/resources/DropDownCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/DropDown/src/DropDown+Appearance.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DropDown+Appearance.swift
3 | // DropDown
4 | //
5 | // Created by Kevin Hirsch on 13/06/16.
6 | // Copyright © 2016 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | import UIKit
12 |
13 | extension DropDown {
14 |
15 | public class func setupDefaultAppearance() {
16 | let appearance = DropDown.appearance()
17 |
18 | appearance.cellHeight = DPDConstant.UI.RowHeight
19 | appearance.backgroundColor = DPDConstant.UI.BackgroundColor
20 | appearance.selectionBackgroundColor = DPDConstant.UI.SelectionBackgroundColor
21 | appearance.separatorColor = DPDConstant.UI.SeparatorColor
22 | appearance.cornerRadius = DPDConstant.UI.CornerRadius
23 | appearance.shadowColor = DPDConstant.UI.Shadow.Color
24 | appearance.shadowOffset = DPDConstant.UI.Shadow.Offset
25 | appearance.shadowOpacity = DPDConstant.UI.Shadow.Opacity
26 | appearance.shadowRadius = DPDConstant.UI.Shadow.Radius
27 | appearance.animationduration = DPDConstant.Animation.Duration
28 | appearance.textColor = DPDConstant.UI.TextColor
29 | appearance.selectedTextColor = DPDConstant.UI.SelectedTextColor
30 | appearance.textFont = DPDConstant.UI.TextFont
31 | }
32 |
33 | }
34 |
35 | #endif
36 |
--------------------------------------------------------------------------------
/DropDown/src/DropDown.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DropDown.swift
3 | // DropDown
4 | //
5 | // Created by Kevin Hirsch on 28/07/15.
6 | // Copyright (c) 2015 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | import UIKit
12 |
13 | public typealias Index = Int
14 | public typealias Closure = () -> Void
15 | public typealias SelectionClosure = (Index, String) -> Void
16 | public typealias MultiSelectionClosure = ([Index], [String]) -> Void
17 | public typealias ConfigurationClosure = (Index, String) -> String
18 | public typealias CellConfigurationClosure = (Index, String, DropDownCell) -> Void
19 | private typealias ComputeLayoutTuple = (x: CGFloat, y: CGFloat, width: CGFloat, offscreenHeight: CGFloat)
20 |
21 | /// Can be `UIView` or `UIBarButtonItem`.
22 | @objc
23 | public protocol AnchorView: class {
24 |
25 | var plainView: UIView { get }
26 |
27 | }
28 |
29 | extension UIView: AnchorView {
30 |
31 | public var plainView: UIView {
32 | return self
33 | }
34 |
35 | }
36 |
37 | extension UIBarButtonItem: AnchorView {
38 |
39 | public var plainView: UIView {
40 | return value(forKey: "view") as! UIView
41 | }
42 |
43 | }
44 |
45 | /// A Material Design drop down in replacement for `UIPickerView`.
46 | public final class DropDown: UIView {
47 |
48 | //TODO: handle iOS 7 landscape mode
49 |
50 | /// The dismiss mode for a drop down.
51 | public enum DismissMode {
52 |
53 | /// A tap outside the drop down is required to dismiss.
54 | case onTap
55 |
56 | /// No tap is required to dismiss, it will dimiss when interacting with anything else.
57 | case automatic
58 |
59 | /// Not dismissable by the user.
60 | case manual
61 |
62 | }
63 |
64 | /// The direction where the drop down will show from the `anchorView`.
65 | public enum Direction {
66 |
67 | /// The drop down will show below the anchor view when possible, otherwise above if there is more place than below.
68 | case any
69 |
70 | /// The drop down will show above the anchor view or will not be showed if not enough space.
71 | case top
72 |
73 | /// The drop down will show below or will not be showed if not enough space.
74 | case bottom
75 |
76 | }
77 |
78 | //MARK: - Properties
79 |
80 | /// The current visible drop down. There can be only one visible drop down at a time.
81 | public static weak var VisibleDropDown: DropDown?
82 |
83 | //MARK: UI
84 | fileprivate let dismissableView = UIView()
85 | fileprivate let tableViewContainer = UIView()
86 | fileprivate let tableView = UITableView()
87 | fileprivate var templateCell: DropDownCell!
88 | fileprivate lazy var arrowIndication: UIImageView = {
89 | UIGraphicsBeginImageContextWithOptions(CGSize(width: 20, height: 10), false, 0)
90 | let path = UIBezierPath()
91 | path.move(to: CGPoint(x: 0, y: 10))
92 | path.addLine(to: CGPoint(x: 20, y: 10))
93 | path.addLine(to: CGPoint(x: 10, y: 0))
94 | path.addLine(to: CGPoint(x: 0, y: 10))
95 | UIColor.black.setFill()
96 | path.fill()
97 | let img = UIGraphicsGetImageFromCurrentImageContext()
98 | UIGraphicsEndImageContext()
99 | let tintImg = img?.withRenderingMode(.alwaysTemplate)
100 | let imgv = UIImageView(image: tintImg)
101 | imgv.frame = CGRect(x: 0, y: -10, width: 15, height: 10)
102 | return imgv
103 | }()
104 |
105 |
106 | /// The view to which the drop down will displayed onto.
107 | public weak var anchorView: AnchorView? {
108 | didSet { setNeedsUpdateConstraints() }
109 | }
110 |
111 | /**
112 | The possible directions where the drop down will be showed.
113 |
114 | See `Direction` enum for more info.
115 | */
116 | public var direction = Direction.any
117 |
118 | /**
119 | The offset point relative to `anchorView` when the drop down is shown above the anchor view.
120 |
121 | By default, the drop down is showed onto the `anchorView` with the top
122 | left corner for its origin, so an offset equal to (0, 0).
123 | You can change here the default drop down origin.
124 | */
125 | public var topOffset: CGPoint = .zero {
126 | didSet { setNeedsUpdateConstraints() }
127 | }
128 |
129 | /**
130 | The offset point relative to `anchorView` when the drop down is shown below the anchor view.
131 |
132 | By default, the drop down is showed onto the `anchorView` with the top
133 | left corner for its origin, so an offset equal to (0, 0).
134 | You can change here the default drop down origin.
135 | */
136 | public var bottomOffset: CGPoint = .zero {
137 | didSet { setNeedsUpdateConstraints() }
138 | }
139 |
140 | /**
141 | The offset from the bottom of the window when the drop down is shown below the anchor view.
142 | DropDown applies this offset only if keyboard is hidden.
143 | */
144 | public var offsetFromWindowBottom = CGFloat(0) {
145 | didSet { setNeedsUpdateConstraints() }
146 | }
147 |
148 | /**
149 | The width of the drop down.
150 |
151 | Defaults to `anchorView.bounds.width - offset.x`.
152 | */
153 | public var width: CGFloat? {
154 | didSet { setNeedsUpdateConstraints() }
155 | }
156 |
157 | /**
158 | arrowIndication.x
159 |
160 | arrowIndication will be add to tableViewContainer when configured
161 | */
162 | public var arrowIndicationX: CGFloat? {
163 | didSet {
164 | if let arrowIndicationX = arrowIndicationX {
165 | tableViewContainer.addSubview(arrowIndication)
166 | arrowIndication.tintColor = tableViewBackgroundColor
167 | arrowIndication.frame.origin.x = arrowIndicationX
168 | } else {
169 | arrowIndication.removeFromSuperview()
170 | }
171 | }
172 | }
173 |
174 | //MARK: Constraints
175 | fileprivate var heightConstraint: NSLayoutConstraint!
176 | fileprivate var widthConstraint: NSLayoutConstraint!
177 | fileprivate var xConstraint: NSLayoutConstraint!
178 | fileprivate var yConstraint: NSLayoutConstraint!
179 |
180 | //MARK: Appearance
181 | @objc public dynamic var cellHeight = DPDConstant.UI.RowHeight {
182 | willSet { tableView.rowHeight = newValue }
183 | didSet { reloadAllComponents() }
184 | }
185 |
186 | @objc fileprivate dynamic var tableViewBackgroundColor = DPDConstant.UI.BackgroundColor {
187 | willSet {
188 | tableView.backgroundColor = newValue
189 | if arrowIndicationX != nil { arrowIndication.tintColor = newValue }
190 | }
191 | }
192 |
193 | public override var backgroundColor: UIColor? {
194 | get { return tableViewBackgroundColor }
195 | set { tableViewBackgroundColor = newValue! }
196 | }
197 |
198 | /**
199 | The color of the dimmed background (behind the drop down, covering the entire screen).
200 | */
201 | public var dimmedBackgroundColor = UIColor.clear {
202 | willSet { super.backgroundColor = newValue }
203 | }
204 |
205 | /**
206 | The background color of the selected cell in the drop down.
207 |
208 | Changing the background color automatically reloads the drop down.
209 | */
210 | @objc public dynamic var selectionBackgroundColor = DPDConstant.UI.SelectionBackgroundColor
211 |
212 | /**
213 | The separator color between cells.
214 |
215 | Changing the separator color automatically reloads the drop down.
216 | */
217 | @objc public dynamic var separatorColor = DPDConstant.UI.SeparatorColor {
218 | willSet { tableView.separatorColor = newValue }
219 | didSet { reloadAllComponents() }
220 | }
221 |
222 | /**
223 | The corner radius of DropDown.
224 |
225 | Changing the corner radius automatically reloads the drop down.
226 | */
227 | @objc public dynamic var cornerRadius = DPDConstant.UI.CornerRadius {
228 | willSet {
229 | tableViewContainer.layer.cornerRadius = newValue
230 | tableView.layer.cornerRadius = newValue
231 | }
232 | didSet { reloadAllComponents() }
233 | }
234 |
235 | /**
236 | Alias method for `cornerRadius` variable to avoid ambiguity.
237 | */
238 | @objc public dynamic func setupCornerRadius(_ radius: CGFloat) {
239 | tableViewContainer.layer.cornerRadius = radius
240 | tableView.layer.cornerRadius = radius
241 | reloadAllComponents()
242 | }
243 |
244 | /**
245 | The masked corners of DropDown.
246 |
247 | Changing the masked corners automatically reloads the drop down.
248 | */
249 | @available(iOS 11.0, *)
250 | @objc public dynamic func setupMaskedCorners(_ cornerMask: CACornerMask) {
251 | tableViewContainer.layer.maskedCorners = cornerMask
252 | tableView.layer.maskedCorners = cornerMask
253 | reloadAllComponents()
254 | }
255 |
256 | /**
257 | The color of the shadow.
258 |
259 | Changing the shadow color automatically reloads the drop down.
260 | */
261 | @objc public dynamic var shadowColor = DPDConstant.UI.Shadow.Color {
262 | willSet { tableViewContainer.layer.shadowColor = newValue.cgColor }
263 | didSet { reloadAllComponents() }
264 | }
265 |
266 | /**
267 | The offset of the shadow.
268 |
269 | Changing the shadow color automatically reloads the drop down.
270 | */
271 | @objc public dynamic var shadowOffset = DPDConstant.UI.Shadow.Offset {
272 | willSet { tableViewContainer.layer.shadowOffset = newValue }
273 | didSet { reloadAllComponents() }
274 | }
275 |
276 | /**
277 | The opacity of the shadow.
278 |
279 | Changing the shadow opacity automatically reloads the drop down.
280 | */
281 | @objc public dynamic var shadowOpacity = DPDConstant.UI.Shadow.Opacity {
282 | willSet { tableViewContainer.layer.shadowOpacity = newValue }
283 | didSet { reloadAllComponents() }
284 | }
285 |
286 | /**
287 | The radius of the shadow.
288 |
289 | Changing the shadow radius automatically reloads the drop down.
290 | */
291 | @objc public dynamic var shadowRadius = DPDConstant.UI.Shadow.Radius {
292 | willSet { tableViewContainer.layer.shadowRadius = newValue }
293 | didSet { reloadAllComponents() }
294 | }
295 |
296 | /**
297 | The duration of the show/hide animation.
298 | */
299 | @objc public dynamic var animationduration = DPDConstant.Animation.Duration
300 |
301 | /**
302 | The option of the show animation. Global change.
303 | */
304 | public static var animationEntranceOptions = DPDConstant.Animation.EntranceOptions
305 |
306 | /**
307 | The option of the hide animation. Global change.
308 | */
309 | public static var animationExitOptions = DPDConstant.Animation.ExitOptions
310 |
311 | /**
312 | The option of the show animation. Only change the caller. To change all drop down's use the static var.
313 | */
314 | public var animationEntranceOptions: UIView.AnimationOptions = DropDown.animationEntranceOptions
315 |
316 | /**
317 | The option of the hide animation. Only change the caller. To change all drop down's use the static var.
318 | */
319 | public var animationExitOptions: UIView.AnimationOptions = DropDown.animationExitOptions
320 |
321 | /**
322 | The downScale transformation of the tableview when the DropDown is appearing
323 | */
324 | public var downScaleTransform = DPDConstant.Animation.DownScaleTransform {
325 | willSet { tableViewContainer.transform = newValue }
326 | }
327 |
328 | /**
329 | The color of the text for each cells of the drop down.
330 |
331 | Changing the text color automatically reloads the drop down.
332 | */
333 | @objc public dynamic var textColor = DPDConstant.UI.TextColor {
334 | didSet { reloadAllComponents() }
335 | }
336 |
337 | /**
338 | The color of the text for selected cells of the drop down.
339 |
340 | Changing the text color automatically reloads the drop down.
341 | */
342 | @objc public dynamic var selectedTextColor = DPDConstant.UI.SelectedTextColor {
343 | didSet { reloadAllComponents() }
344 | }
345 |
346 | /**
347 | The font of the text for each cells of the drop down.
348 |
349 | Changing the text font automatically reloads the drop down.
350 | */
351 | @objc public dynamic var textFont = DPDConstant.UI.TextFont {
352 | didSet { reloadAllComponents() }
353 | }
354 |
355 | /**
356 | The NIB to use for DropDownCells
357 |
358 | Changing the cell nib automatically reloads the drop down.
359 | */
360 | public var cellNib = UINib(nibName: "DropDownCell", bundle: bundle) {
361 | didSet {
362 | tableView.register(cellNib, forCellReuseIdentifier: DPDConstant.ReusableIdentifier.DropDownCell)
363 | templateCell = nil
364 | reloadAllComponents()
365 | }
366 | }
367 |
368 | /// Correctly specify Bundle for Swift Packages
369 | fileprivate static var bundle: Bundle {
370 | #if SWIFT_PACKAGE
371 | return Bundle.module
372 | #else
373 | return Bundle(for: DropDownCell.self)
374 | #endif
375 | }
376 |
377 | //MARK: Content
378 |
379 | /**
380 | The data source for the drop down.
381 |
382 | Changing the data source automatically reloads the drop down.
383 | */
384 | public var dataSource = [String]() {
385 | didSet {
386 | deselectRows(at: selectedRowIndices)
387 | reloadAllComponents()
388 | }
389 | }
390 |
391 | /**
392 | The localization keys for the data source for the drop down.
393 |
394 | Changing this value automatically reloads the drop down.
395 | This has uses for setting accibility identifiers on the drop down cells (same ones as the localization keys).
396 | */
397 | public var localizationKeysDataSource = [String]() {
398 | didSet {
399 | dataSource = localizationKeysDataSource.map { NSLocalizedString($0, comment: "") }
400 | }
401 | }
402 |
403 | /// The indicies that have been selected
404 | fileprivate var selectedRowIndices = Set()
405 |
406 | /**
407 | The format for the cells' text.
408 |
409 | By default, the cell's text takes the plain `dataSource` value.
410 | Changing `cellConfiguration` automatically reloads the drop down.
411 | */
412 | public var cellConfiguration: ConfigurationClosure? {
413 | didSet { reloadAllComponents() }
414 | }
415 |
416 | /**
417 | A advanced formatter for the cells. Allows customization when custom cells are used
418 |
419 | Changing `customCellConfiguration` automatically reloads the drop down.
420 | */
421 | public var customCellConfiguration: CellConfigurationClosure? {
422 | didSet { reloadAllComponents() }
423 | }
424 |
425 | /// The action to execute when the user selects a cell.
426 | public var selectionAction: SelectionClosure?
427 |
428 | /**
429 | The action to execute when the user selects multiple cells.
430 |
431 | Providing an action will turn on multiselection mode.
432 | The single selection action will still be called if provided.
433 | */
434 | public var multiSelectionAction: MultiSelectionClosure?
435 |
436 | /// The action to execute when the drop down will show.
437 | public var willShowAction: Closure?
438 |
439 | /// The action to execute when the user cancels/hides the drop down.
440 | public var cancelAction: Closure?
441 |
442 | /// The dismiss mode of the drop down. Default is `OnTap`.
443 | public var dismissMode = DismissMode.onTap {
444 | willSet {
445 | if newValue == .onTap {
446 | let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissableViewTapped))
447 | dismissableView.addGestureRecognizer(gestureRecognizer)
448 | } else if let gestureRecognizer = dismissableView.gestureRecognizers?.first {
449 | dismissableView.removeGestureRecognizer(gestureRecognizer)
450 | }
451 | }
452 | }
453 |
454 | fileprivate var minHeight: CGFloat {
455 | return tableView.rowHeight
456 | }
457 |
458 | fileprivate var didSetupConstraints = false
459 |
460 | //MARK: - Init's
461 |
462 | deinit {
463 | stopListeningToNotifications()
464 | }
465 |
466 | /**
467 | Creates a new instance of a drop down.
468 | Don't forget to setup the `dataSource`,
469 | the `anchorView` and the `selectionAction`
470 | at least before calling `show()`.
471 | */
472 | public convenience init() {
473 | self.init(frame: .zero)
474 | }
475 |
476 | /**
477 | Creates a new instance of a drop down.
478 |
479 | - parameter anchorView: The view to which the drop down will displayed onto.
480 | - parameter selectionAction: The action to execute when the user selects a cell.
481 | - parameter dataSource: The data source for the drop down.
482 | - parameter topOffset: The offset point relative to `anchorView` used when drop down is displayed on above the anchor view.
483 | - parameter bottomOffset: The offset point relative to `anchorView` used when drop down is displayed on below the anchor view.
484 | - parameter cellConfiguration: The format for the cells' text.
485 | - parameter cancelAction: The action to execute when the user cancels/hides the drop down.
486 |
487 | - returns: A new instance of a drop down customized with the above parameters.
488 | */
489 | public convenience init(anchorView: AnchorView, selectionAction: SelectionClosure? = nil, dataSource: [String] = [], topOffset: CGPoint? = nil, bottomOffset: CGPoint? = nil, cellConfiguration: ConfigurationClosure? = nil, cancelAction: Closure? = nil) {
490 | self.init(frame: .zero)
491 |
492 | self.anchorView = anchorView
493 | self.selectionAction = selectionAction
494 | self.dataSource = dataSource
495 | self.topOffset = topOffset ?? .zero
496 | self.bottomOffset = bottomOffset ?? .zero
497 | self.cellConfiguration = cellConfiguration
498 | self.cancelAction = cancelAction
499 | }
500 |
501 | override public init(frame: CGRect) {
502 | super.init(frame: frame)
503 | setup()
504 | }
505 |
506 | public required init?(coder aDecoder: NSCoder) {
507 | super.init(coder: aDecoder)
508 | setup()
509 | }
510 |
511 | }
512 |
513 | //MARK: - Setup
514 |
515 | private extension DropDown {
516 |
517 | func setup() {
518 | tableView.register(cellNib, forCellReuseIdentifier: DPDConstant.ReusableIdentifier.DropDownCell)
519 |
520 | DispatchQueue.main.async {
521 | //HACK: If not done in dispatch_async on main queue `setupUI` will have no effect
522 | self.updateConstraintsIfNeeded()
523 | self.setupUI()
524 | }
525 |
526 | tableView.rowHeight = cellHeight
527 | setHiddentState()
528 | isHidden = true
529 |
530 | dismissMode = .onTap
531 |
532 | tableView.delegate = self
533 | tableView.dataSource = self
534 |
535 | startListeningToKeyboard()
536 |
537 | accessibilityIdentifier = "drop_down"
538 | }
539 |
540 | func setupUI() {
541 | super.backgroundColor = dimmedBackgroundColor
542 |
543 | tableViewContainer.layer.masksToBounds = false
544 | tableViewContainer.layer.cornerRadius = cornerRadius
545 | tableViewContainer.layer.shadowColor = shadowColor.cgColor
546 | tableViewContainer.layer.shadowOffset = shadowOffset
547 | tableViewContainer.layer.shadowOpacity = shadowOpacity
548 | tableViewContainer.layer.shadowRadius = shadowRadius
549 |
550 | tableView.backgroundColor = tableViewBackgroundColor
551 | tableView.separatorColor = separatorColor
552 | tableView.layer.cornerRadius = cornerRadius
553 | tableView.layer.masksToBounds = true
554 | }
555 |
556 | }
557 |
558 | //MARK: - UI
559 |
560 | extension DropDown {
561 |
562 | public override func updateConstraints() {
563 | if !didSetupConstraints {
564 | setupConstraints()
565 | }
566 |
567 | didSetupConstraints = true
568 |
569 | let layout = computeLayout()
570 |
571 | if !layout.canBeDisplayed {
572 | super.updateConstraints()
573 | hide()
574 |
575 | return
576 | }
577 |
578 | xConstraint.constant = layout.x
579 | yConstraint.constant = layout.y
580 | widthConstraint.constant = layout.width
581 | heightConstraint.constant = layout.visibleHeight
582 |
583 | tableView.isScrollEnabled = layout.offscreenHeight > 0
584 |
585 | DispatchQueue.main.async { [weak self] in
586 | self?.tableView.flashScrollIndicators()
587 | }
588 |
589 | super.updateConstraints()
590 | }
591 |
592 | fileprivate func setupConstraints() {
593 | translatesAutoresizingMaskIntoConstraints = false
594 |
595 | // Dismissable view
596 | addSubview(dismissableView)
597 | dismissableView.translatesAutoresizingMaskIntoConstraints = false
598 |
599 | addUniversalConstraints(format: "|[dismissableView]|", views: ["dismissableView": dismissableView])
600 |
601 |
602 | // Table view container
603 | addSubview(tableViewContainer)
604 | tableViewContainer.translatesAutoresizingMaskIntoConstraints = false
605 |
606 | xConstraint = NSLayoutConstraint(
607 | item: tableViewContainer,
608 | attribute: .leading,
609 | relatedBy: .equal,
610 | toItem: self,
611 | attribute: .leading,
612 | multiplier: 1,
613 | constant: 0)
614 | addConstraint(xConstraint)
615 |
616 | yConstraint = NSLayoutConstraint(
617 | item: tableViewContainer,
618 | attribute: .top,
619 | relatedBy: .equal,
620 | toItem: self,
621 | attribute: .top,
622 | multiplier: 1,
623 | constant: 0)
624 | addConstraint(yConstraint)
625 |
626 | widthConstraint = NSLayoutConstraint(
627 | item: tableViewContainer,
628 | attribute: .width,
629 | relatedBy: .equal,
630 | toItem: nil,
631 | attribute: .notAnAttribute,
632 | multiplier: 1,
633 | constant: 0)
634 | tableViewContainer.addConstraint(widthConstraint)
635 |
636 | heightConstraint = NSLayoutConstraint(
637 | item: tableViewContainer,
638 | attribute: .height,
639 | relatedBy: .equal,
640 | toItem: nil,
641 | attribute: .notAnAttribute,
642 | multiplier: 1,
643 | constant: 0)
644 | tableViewContainer.addConstraint(heightConstraint)
645 |
646 | // Table view
647 | tableViewContainer.addSubview(tableView)
648 | tableView.translatesAutoresizingMaskIntoConstraints = false
649 |
650 | tableViewContainer.addUniversalConstraints(format: "|[tableView]|", views: ["tableView": tableView])
651 | }
652 |
653 | public override func layoutSubviews() {
654 | super.layoutSubviews()
655 |
656 | // When orientation changes, layoutSubviews is called
657 | // We update the constraint to update the position
658 | setNeedsUpdateConstraints()
659 |
660 | let shadowPath = UIBezierPath(roundedRect: tableViewContainer.bounds, cornerRadius: cornerRadius)
661 | tableViewContainer.layer.shadowPath = shadowPath.cgPath
662 | }
663 |
664 | fileprivate func computeLayout() -> (x: CGFloat, y: CGFloat, width: CGFloat, offscreenHeight: CGFloat, visibleHeight: CGFloat, canBeDisplayed: Bool, Direction: Direction) {
665 | var layout: ComputeLayoutTuple = (0, 0, 0, 0)
666 | var direction = self.direction
667 |
668 | guard let window = UIWindow.visibleWindow() else { return (0, 0, 0, 0, 0, false, direction) }
669 |
670 | barButtonItemCondition: if let anchorView = anchorView as? UIBarButtonItem {
671 | let isRightBarButtonItem = anchorView.plainView.frame.minX > window.frame.midX
672 |
673 | guard isRightBarButtonItem else { break barButtonItemCondition }
674 |
675 | let width = self.width ?? fittingWidth()
676 | let anchorViewWidth = anchorView.plainView.frame.width
677 | let x = -(width - anchorViewWidth)
678 |
679 | bottomOffset = CGPoint(x: x, y: 0)
680 | }
681 |
682 | if anchorView == nil {
683 | layout = computeLayoutBottomDisplay(window: window)
684 | direction = .any
685 | } else {
686 | switch direction {
687 | case .any:
688 | layout = computeLayoutBottomDisplay(window: window)
689 | direction = .bottom
690 |
691 | if layout.offscreenHeight > 0 {
692 | let topLayout = computeLayoutForTopDisplay(window: window)
693 |
694 | if topLayout.offscreenHeight < layout.offscreenHeight {
695 | layout = topLayout
696 | direction = .top
697 | }
698 | }
699 | case .bottom:
700 | layout = computeLayoutBottomDisplay(window: window)
701 | direction = .bottom
702 | case .top:
703 | layout = computeLayoutForTopDisplay(window: window)
704 | direction = .top
705 | }
706 | }
707 |
708 | constraintWidthToFittingSizeIfNecessary(layout: &layout)
709 | constraintWidthToBoundsIfNecessary(layout: &layout, in: window)
710 |
711 | let visibleHeight = tableHeight - layout.offscreenHeight
712 | let canBeDisplayed = visibleHeight >= minHeight
713 |
714 | return (layout.x, layout.y, layout.width, layout.offscreenHeight, visibleHeight, canBeDisplayed, direction)
715 | }
716 |
717 | fileprivate func computeLayoutBottomDisplay(window: UIWindow) -> ComputeLayoutTuple {
718 | var offscreenHeight: CGFloat = 0
719 |
720 | let width = self.width ?? (anchorView?.plainView.bounds.width ?? fittingWidth()) - bottomOffset.x
721 |
722 | let anchorViewX = anchorView?.plainView.windowFrame?.minX ?? window.frame.midX - (width / 2)
723 | let anchorViewY = anchorView?.plainView.windowFrame?.minY ?? window.frame.midY - (tableHeight / 2)
724 |
725 | let x = anchorViewX + bottomOffset.x
726 | let y = anchorViewY + bottomOffset.y
727 |
728 | let maxY = y + tableHeight
729 | let windowMaxY = window.bounds.maxY - DPDConstant.UI.HeightPadding - offsetFromWindowBottom
730 |
731 | let keyboardListener = KeyboardListener.sharedInstance
732 | let keyboardMinY = keyboardListener.keyboardFrame.minY - DPDConstant.UI.HeightPadding
733 |
734 | if keyboardListener.isVisible && maxY > keyboardMinY {
735 | offscreenHeight = abs(maxY - keyboardMinY)
736 | } else if maxY > windowMaxY {
737 | offscreenHeight = abs(maxY - windowMaxY)
738 | }
739 |
740 | return (x, y, width, offscreenHeight)
741 | }
742 |
743 | fileprivate func computeLayoutForTopDisplay(window: UIWindow) -> ComputeLayoutTuple {
744 | var offscreenHeight: CGFloat = 0
745 |
746 | let anchorViewX = anchorView?.plainView.windowFrame?.minX ?? 0
747 | let anchorViewMaxY = anchorView?.plainView.windowFrame?.maxY ?? 0
748 |
749 | let x = anchorViewX + topOffset.x
750 | var y = (anchorViewMaxY + topOffset.y) - tableHeight
751 |
752 | let windowY = window.bounds.minY + DPDConstant.UI.HeightPadding
753 |
754 | if y < windowY {
755 | offscreenHeight = abs(y - windowY)
756 | y = windowY
757 | }
758 |
759 | let width = self.width ?? (anchorView?.plainView.bounds.width ?? fittingWidth()) - topOffset.x
760 |
761 | return (x, y, width, offscreenHeight)
762 | }
763 |
764 | fileprivate func fittingWidth() -> CGFloat {
765 | if templateCell == nil {
766 | templateCell = (cellNib.instantiate(withOwner: nil, options: nil)[0] as! DropDownCell)
767 | }
768 |
769 | var maxWidth: CGFloat = 0
770 |
771 | for index in 0.. maxWidth {
777 | maxWidth = width
778 | }
779 | }
780 |
781 | return maxWidth
782 | }
783 |
784 | fileprivate func constraintWidthToBoundsIfNecessary(layout: inout ComputeLayoutTuple, in window: UIWindow) {
785 | let windowMaxX = window.bounds.maxX
786 | let maxX = layout.x + layout.width
787 |
788 | if maxX > windowMaxX {
789 | let delta = maxX - windowMaxX
790 | let newOrigin = layout.x - delta
791 |
792 | if newOrigin > 0 {
793 | layout.x = newOrigin
794 | } else {
795 | layout.x = 0
796 | layout.width += newOrigin // newOrigin is negative, so this operation is a substraction
797 | }
798 | }
799 | }
800 |
801 | fileprivate func constraintWidthToFittingSizeIfNecessary(layout: inout ComputeLayoutTuple) {
802 | guard width == nil else { return }
803 |
804 | if layout.width < fittingWidth() {
805 | layout.width = fittingWidth()
806 | }
807 | }
808 |
809 | }
810 |
811 | //MARK: - Actions
812 |
813 | extension DropDown {
814 |
815 | /**
816 | An Objective-C alias for the show() method which converts the returned tuple into an NSDictionary.
817 |
818 | - returns: An NSDictionary with a value for the "canBeDisplayed" Bool, and possibly for the "offScreenHeight" Optional(CGFloat).
819 | */
820 | @objc(show)
821 | public func objc_show() -> NSDictionary {
822 | let (canBeDisplayed, offScreenHeight) = show()
823 |
824 | var info = [AnyHashable: Any]()
825 | info["canBeDisplayed"] = canBeDisplayed
826 | if let offScreenHeight = offScreenHeight {
827 | info["offScreenHeight"] = offScreenHeight
828 | }
829 |
830 | return NSDictionary(dictionary: info)
831 | }
832 |
833 | /**
834 | Shows the drop down if enough height.
835 |
836 | - returns: Wether it succeed and how much height is needed to display all cells at once.
837 | */
838 | @discardableResult
839 | public func show(onTopOf window: UIWindow? = nil, beforeTransform transform: CGAffineTransform? = nil, anchorPoint: CGPoint? = nil) -> (canBeDisplayed: Bool, offscreenHeight: CGFloat?) {
840 | if self == DropDown.VisibleDropDown && DropDown.VisibleDropDown?.isHidden == false { // added condition - DropDown.VisibleDropDown?.isHidden == false -> to resolve forever hiding dropdown issue when continuous taping on button - Kartik Patel - 2016-12-29
841 | return (true, 0)
842 | }
843 |
844 | if let visibleDropDown = DropDown.VisibleDropDown {
845 | visibleDropDown.cancel()
846 | }
847 |
848 | willShowAction?()
849 |
850 | DropDown.VisibleDropDown = self
851 |
852 | setNeedsUpdateConstraints()
853 |
854 | let visibleWindow = window != nil ? window : UIWindow.visibleWindow()
855 | visibleWindow?.addSubview(self)
856 | visibleWindow?.bringSubviewToFront(self)
857 |
858 | self.translatesAutoresizingMaskIntoConstraints = false
859 | visibleWindow?.addUniversalConstraints(format: "|[dropDown]|", views: ["dropDown": self])
860 |
861 | let layout = computeLayout()
862 |
863 | if !layout.canBeDisplayed {
864 | hide()
865 | return (layout.canBeDisplayed, layout.offscreenHeight)
866 | }
867 |
868 | isHidden = false
869 |
870 | if anchorPoint != nil {
871 | tableViewContainer.layer.anchorPoint = anchorPoint!
872 | }
873 |
874 | if transform != nil {
875 | tableViewContainer.transform = transform!
876 | } else {
877 | tableViewContainer.transform = downScaleTransform
878 | }
879 |
880 | layoutIfNeeded()
881 |
882 | UIView.animate(
883 | withDuration: animationduration,
884 | delay: 0,
885 | options: animationEntranceOptions,
886 | animations: { [weak self] in
887 | self?.setShowedState()
888 | },
889 | completion: nil)
890 |
891 | accessibilityViewIsModal = true
892 | UIAccessibility.post(notification: .screenChanged, argument: self)
893 |
894 | //deselectRows(at: selectedRowIndices)
895 | selectRows(at: selectedRowIndices)
896 |
897 | return (layout.canBeDisplayed, layout.offscreenHeight)
898 | }
899 |
900 | public override func accessibilityPerformEscape() -> Bool {
901 | switch dismissMode {
902 | case .automatic, .onTap:
903 | cancel()
904 | return true
905 | case .manual:
906 | return false
907 | }
908 | }
909 |
910 | /// Hides the drop down.
911 | public func hide() {
912 | if self == DropDown.VisibleDropDown {
913 | /*
914 | If one drop down is showed and another one is not
915 | but we call `hide()` on the hidden one:
916 | we don't want it to set the `VisibleDropDown` to nil.
917 | */
918 | DropDown.VisibleDropDown = nil
919 | }
920 |
921 | if isHidden {
922 | return
923 | }
924 |
925 | UIView.animate(
926 | withDuration: animationduration,
927 | delay: 0,
928 | options: animationExitOptions,
929 | animations: { [weak self] in
930 | self?.setHiddentState()
931 | },
932 | completion: { [weak self] finished in
933 | guard let `self` = self else { return }
934 |
935 | self.isHidden = true
936 | self.removeFromSuperview()
937 | UIAccessibility.post(notification: .screenChanged, argument: nil)
938 | })
939 | }
940 |
941 | fileprivate func cancel() {
942 | hide()
943 | cancelAction?()
944 | }
945 |
946 | fileprivate func setHiddentState() {
947 | alpha = 0
948 | }
949 |
950 | fileprivate func setShowedState() {
951 | alpha = 1
952 | tableViewContainer.transform = CGAffineTransform.identity
953 | }
954 |
955 | }
956 |
957 | //MARK: - UITableView
958 |
959 | extension DropDown {
960 |
961 | /**
962 | Reloads all the cells.
963 |
964 | It should not be necessary in most cases because each change to
965 | `dataSource`, `textColor`, `textFont`, `selectionBackgroundColor`
966 | and `cellConfiguration` implicitly calls `reloadAllComponents()`.
967 | */
968 | public func reloadAllComponents() {
969 | DispatchQueue.executeOnMainThread {
970 | self.tableView.reloadData()
971 | self.setNeedsUpdateConstraints()
972 | }
973 | }
974 |
975 | /// (Pre)selects a row at a certain index.
976 | public func selectRow(at index: Index?, scrollPosition: UITableView.ScrollPosition = .none) {
977 | if let index = index {
978 | tableView.selectRow(
979 | at: IndexPath(row: index, section: 0), animated: true, scrollPosition: scrollPosition
980 | )
981 | selectedRowIndices.insert(index)
982 | } else {
983 | deselectRows(at: selectedRowIndices)
984 | selectedRowIndices.removeAll()
985 | }
986 | }
987 |
988 | public func selectRows(at indices: Set?) {
989 | indices?.forEach {
990 | selectRow(at: $0)
991 | }
992 |
993 | // if we are in multi selection mode then reload data so that all selections are shown
994 | if multiSelectionAction != nil {
995 | tableView.reloadData()
996 | }
997 | }
998 |
999 | public func deselectRow(at index: Index?) {
1000 | guard let index = index
1001 | , index >= 0
1002 | else { return }
1003 |
1004 | // remove from indices
1005 | if let selectedRowIndex = selectedRowIndices.firstIndex(where: { $0 == index }) {
1006 | selectedRowIndices.remove(at: selectedRowIndex)
1007 | }
1008 |
1009 | tableView.deselectRow(at: IndexPath(row: index, section: 0), animated: true)
1010 | }
1011 |
1012 | // de-selects the rows at the indices provided
1013 | public func deselectRows(at indices: Set?) {
1014 | indices?.forEach {
1015 | deselectRow(at: $0)
1016 | }
1017 | }
1018 |
1019 | /// Returns the index of the selected row.
1020 | public var indexForSelectedRow: Index? {
1021 | return (tableView.indexPathForSelectedRow as NSIndexPath?)?.row
1022 | }
1023 |
1024 | /// Returns the selected item.
1025 | public var selectedItem: String? {
1026 | guard let row = (tableView.indexPathForSelectedRow as NSIndexPath?)?.row else { return nil }
1027 |
1028 | return dataSource[row]
1029 | }
1030 |
1031 | /// Returns the height needed to display all cells.
1032 | fileprivate var tableHeight: CGFloat {
1033 | return tableView.rowHeight * CGFloat(dataSource.count)
1034 | }
1035 |
1036 | //MARK: Objective-C methods for converting the Swift type Index
1037 | @objc public func selectRow(_ index: Int, scrollPosition: UITableView.ScrollPosition = .none) {
1038 | self.selectRow(at:Index(index), scrollPosition: scrollPosition)
1039 | }
1040 |
1041 | @objc public func clearSelection() {
1042 | self.selectRow(at:nil)
1043 | }
1044 |
1045 | @objc public func deselectRow(_ index: Int) {
1046 | tableView.deselectRow(at: IndexPath(row: Index(index), section: 0), animated: true)
1047 | }
1048 |
1049 | @objc public var indexPathForSelectedRow: NSIndexPath? {
1050 | return tableView.indexPathForSelectedRow as NSIndexPath?
1051 | }
1052 | }
1053 |
1054 | //MARK: - UITableViewDataSource - UITableViewDelegate
1055 |
1056 | extension DropDown: UITableViewDataSource, UITableViewDelegate {
1057 |
1058 | public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
1059 | return dataSource.count
1060 | }
1061 |
1062 | public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
1063 | let cell = tableView.dequeueReusableCell(withIdentifier: DPDConstant.ReusableIdentifier.DropDownCell, for: indexPath) as! DropDownCell
1064 | let index = (indexPath as NSIndexPath).row
1065 |
1066 | configureCell(cell, at: index)
1067 |
1068 | return cell
1069 | }
1070 |
1071 | fileprivate func configureCell(_ cell: DropDownCell, at index: Int) {
1072 | if index >= 0 && index < localizationKeysDataSource.count {
1073 | cell.accessibilityIdentifier = localizationKeysDataSource[index]
1074 | }
1075 |
1076 | cell.optionLabel.textColor = textColor
1077 | cell.optionLabel.font = textFont
1078 | cell.selectedBackgroundColor = selectionBackgroundColor
1079 | cell.highlightTextColor = selectedTextColor
1080 | cell.normalTextColor = textColor
1081 |
1082 | if let cellConfiguration = cellConfiguration {
1083 | cell.optionLabel.text = cellConfiguration(index, dataSource[index])
1084 | } else {
1085 | cell.optionLabel.text = dataSource[index]
1086 | }
1087 |
1088 | customCellConfiguration?(index, dataSource[index], cell)
1089 | }
1090 |
1091 | public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
1092 | cell.isSelected = selectedRowIndices.first{ $0 == (indexPath as NSIndexPath).row } != nil
1093 | }
1094 |
1095 | public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
1096 | let selectedRowIndex = (indexPath as NSIndexPath).row
1097 |
1098 |
1099 | // are we in multi-selection mode?
1100 | if let multiSelectionCallback = multiSelectionAction {
1101 | // if already selected then deselect
1102 | if selectedRowIndices.first(where: { $0 == selectedRowIndex}) != nil {
1103 | deselectRow(at: selectedRowIndex)
1104 |
1105 | let selectedRowIndicesArray = Array(selectedRowIndices)
1106 | let selectedRows = selectedRowIndicesArray.map { dataSource[$0] }
1107 | multiSelectionCallback(selectedRowIndicesArray, selectedRows)
1108 | return
1109 | }
1110 | else {
1111 | selectedRowIndices.insert(selectedRowIndex)
1112 |
1113 | let selectedRowIndicesArray = Array(selectedRowIndices)
1114 | let selectedRows = selectedRowIndicesArray.map { dataSource[$0] }
1115 |
1116 | selectionAction?(selectedRowIndex, dataSource[selectedRowIndex])
1117 | multiSelectionCallback(selectedRowIndicesArray, selectedRows)
1118 | tableView.reloadData()
1119 | return
1120 | }
1121 | }
1122 |
1123 | // Perform single selection logic
1124 | selectedRowIndices.removeAll()
1125 | selectedRowIndices.insert(selectedRowIndex)
1126 | selectionAction?(selectedRowIndex, dataSource[selectedRowIndex])
1127 |
1128 | if let _ = anchorView as? UIBarButtonItem {
1129 | // DropDown's from UIBarButtonItem are menus so we deselect the selected menu right after selection
1130 | deselectRow(at: selectedRowIndex)
1131 | }
1132 |
1133 | hide()
1134 |
1135 | }
1136 |
1137 | }
1138 |
1139 | //MARK: - Auto dismiss
1140 |
1141 | extension DropDown {
1142 |
1143 | public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
1144 | let view = super.hitTest(point, with: event)
1145 |
1146 | if dismissMode == .automatic && view === dismissableView {
1147 | cancel()
1148 | return nil
1149 | } else {
1150 | return view
1151 | }
1152 | }
1153 |
1154 | @objc
1155 | fileprivate func dismissableViewTapped() {
1156 | cancel()
1157 | }
1158 |
1159 | }
1160 |
1161 | //MARK: - Keyboard events
1162 |
1163 | extension DropDown {
1164 |
1165 | /**
1166 | Starts listening to keyboard events.
1167 | Allows the drop down to display correctly when keyboard is showed.
1168 | */
1169 | @objc public static func startListeningToKeyboard() {
1170 | KeyboardListener.sharedInstance.startListeningToKeyboard()
1171 | }
1172 |
1173 | fileprivate func startListeningToKeyboard() {
1174 | KeyboardListener.sharedInstance.startListeningToKeyboard()
1175 |
1176 | NotificationCenter.default.addObserver(
1177 | self,
1178 | selector: #selector(keyboardUpdate),
1179 | name: UIResponder.keyboardWillShowNotification,
1180 | object: nil)
1181 | NotificationCenter.default.addObserver(
1182 | self,
1183 | selector: #selector(keyboardUpdate),
1184 | name: UIResponder.keyboardWillHideNotification,
1185 | object: nil)
1186 | }
1187 |
1188 | fileprivate func stopListeningToNotifications() {
1189 | NotificationCenter.default.removeObserver(self)
1190 | }
1191 |
1192 | @objc
1193 | fileprivate func keyboardUpdate() {
1194 | self.setNeedsUpdateConstraints()
1195 | }
1196 |
1197 | }
1198 |
1199 | private extension DispatchQueue {
1200 | static func executeOnMainThread(_ closure: @escaping Closure) {
1201 | if Thread.isMainThread {
1202 | closure()
1203 | } else {
1204 | main.async(execute: closure)
1205 | }
1206 | }
1207 | }
1208 |
1209 | #endif
1210 |
--------------------------------------------------------------------------------
/DropDown/src/DropDownCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DropDownCellTableViewCell.swift
3 | // DropDown
4 | //
5 | // Created by Kevin Hirsch on 28/07/15.
6 | // Copyright (c) 2015 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | #if os(iOS)
10 |
11 | import UIKit
12 |
13 | open class DropDownCell: UITableViewCell {
14 |
15 | //UI
16 | @IBOutlet open weak var optionLabel: UILabel!
17 |
18 | var selectedBackgroundColor: UIColor?
19 | var highlightTextColor: UIColor?
20 | var normalTextColor: UIColor?
21 |
22 | }
23 |
24 | //MARK: - UI
25 |
26 | extension DropDownCell {
27 |
28 | override open func awakeFromNib() {
29 | super.awakeFromNib()
30 |
31 | backgroundColor = .clear
32 | }
33 |
34 | override open var isSelected: Bool {
35 | willSet {
36 | setSelected(newValue, animated: false)
37 | }
38 | }
39 |
40 | override open var isHighlighted: Bool {
41 | willSet {
42 | setSelected(newValue, animated: false)
43 | }
44 | }
45 |
46 | override open func setHighlighted(_ highlighted: Bool, animated: Bool) {
47 | setSelected(highlighted, animated: animated)
48 | }
49 |
50 | override open func setSelected(_ selected: Bool, animated: Bool) {
51 | let executeSelection: () -> Void = { [weak self] in
52 | guard let `self` = self else { return }
53 |
54 | if let selectedBackgroundColor = self.selectedBackgroundColor {
55 | if selected {
56 | self.backgroundColor = selectedBackgroundColor
57 | self.optionLabel.textColor = self.highlightTextColor
58 | } else {
59 | self.backgroundColor = .clear
60 | self.optionLabel.textColor = self.normalTextColor
61 | }
62 | }
63 | }
64 |
65 | if animated {
66 | UIView.animate(withDuration: 0.3, animations: {
67 | executeSelection()
68 | })
69 | } else {
70 | executeSelection()
71 | }
72 |
73 | accessibilityTraits = selected ? .selected : .none
74 | }
75 |
76 | }
77 |
78 | #endif
79 |
--------------------------------------------------------------------------------
/DropDownTests/DropDownTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DropDownTests.swift
3 | // DropDownTests
4 | //
5 | // Created by Kevin Hirsch on 28/07/15.
6 | // Copyright (c) 2015 Kevin Hirsch. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import XCTest
11 |
12 | class DropDownTests: 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 | XCTAssert(true, "Pass")
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 |
--------------------------------------------------------------------------------
/DropDownTests/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 |
--------------------------------------------------------------------------------
/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDisplayName
6 | DropDown
7 | CFBundleDevelopmentRegion
8 | en
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 kevin-hirsch
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.
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "DropDown",
6 | platforms: [
7 | .iOS(.v9)
8 | ],
9 | products: [
10 | .library(
11 | name: "DropDown",
12 | targets: ["DropDown"]
13 | )
14 | ],
15 | targets: [
16 | .target(
17 | name: "DropDown",
18 | dependencies: [],
19 | path: "DropDown",
20 | exclude: ["Info.plist", "DropDown.h"],
21 | resources: [
22 | .process("DropDown/resources")
23 | ]
24 | )
25 | ],
26 | swiftLanguageVersions: [.v5]
27 | )
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://twitter.com/kevinh6113)
4 | [](https://github.com/AssistoLab/DropDown/blob/master/README.md)
5 | [](https://github.com/AssistoLab/DropDown)
6 | [](http://cocoadocs.org/docsets/DropDown/)
7 | [](https://github.com/Carthage/Carthage)
8 |
9 |
10 | A Material Design drop down for iOS written in Swift.
11 | ***
12 |
13 | [](Screenshots/1.png)
14 | [](Screenshots/2.png)
15 | [](Screenshots/3.png)
16 |
17 | ## Demo
18 |
19 | Do `pod try DropDown` in your console and run the project to try a demo.
20 | To install [CocoaPods](http://www.cocoapods.org), run `sudo gem install cocoapods` in your console.
21 |
22 | ## Installation 📱
23 |
24 | `DropDown` supports Swift 5.0 since version `2.3.13`.
25 | `DropDown` supports Swift 4.2 since version `2.3.4`.
26 |
27 | If you need Swift 4.0, use version 2.3.2:
28 | - Manually: use tag `2.3.2`
29 | - CocoaPods: `pod 'DropDown', '2.3.2'`
30 | - Carthage: `github "AssistoLab/DropDown" == 2.3.2`
31 |
32 | ### CocoaPods
33 |
34 | Use [CocoaPods](http://www.cocoapods.org).
35 |
36 | 1. Add `pod 'DropDown'` to your *Podfile*.
37 | 2. Install the pod(s) by running `pod install`.
38 | 3. Add `import DropDown` in the .swift files where you want to use it
39 |
40 | ### Carthage
41 |
42 | Use [Carthage](https://github.com/Carthage/Carthage).
43 |
44 | 1. Create a file name `Cartfile`.
45 | 2. Add the line `github "AssistoLab/DropDown"`.
46 | 3. Run `carthage update`.
47 | 4. Drag the built `DropDown.framework` into your Xcode project.
48 |
49 | ### Source files
50 |
51 | A regular way to use DropDown in your project would be using Embedded Framework. There are two approaches, using source code and adding submodule.
52 |
53 | Add source code:
54 |
55 | 1. Download the [latest code version](http://github.com/AssistoLab/DropDown/archive/master.zip).
56 | 2. Unzip the download file, copy `DropDown` folder to your project folder
57 |
58 | Add submodule
59 |
60 | 1. In your favorite terminal, `cd` into your top-level project directory, and entering the following command:
61 | ``` bash
62 | $ git submodule add git@github.com:AssistoLab/DropDown.git
63 | ```
64 |
65 | After you get the source code either by adding it directly or using submodule, then do the following steps:
66 |
67 | - Open `DropDown` folder, and drag `DropDown.xcodeproj` into the file navigator of your app project, under you app project.
68 | - In Xcode, navigate to the target configuration window by clicking the blue project icon, and selecting the application target under the "Targets" heading in the sidebar.
69 | - Open "Build Phases" panel in the tab bar at the top of the window, expend the "Target Dependencies" group and add `DropDown.framework` under DropDown icon in the popup window by clicking `+`. Similarly, you can also add `DropDown.framework` in "Embedded Binaries" under "General" tab.
70 |
71 | ## Basic usage ✨
72 |
73 | ```swift
74 | let dropDown = DropDown()
75 |
76 | // The view to which the drop down will appear on
77 | dropDown.anchorView = view // UIView or UIBarButtonItem
78 |
79 | // The list of items to display. Can be changed dynamically
80 | dropDown.dataSource = ["Car", "Motorcycle", "Truck"]
81 | ```
82 |
83 | Optional properties:
84 |
85 | ```swift
86 | // Action triggered on selection
87 | dropDown.selectionAction = { [unowned self] (index: Int, item: String) in
88 | print("Selected item: \(item) at index: \(index)")
89 | }
90 |
91 | // Will set a custom width instead of the anchor view width
92 | dropDownLeft.width = 200
93 | ```
94 |
95 | Display actions:
96 |
97 | ```swift
98 | dropDown.show()
99 | dropDown.hide()
100 | ```
101 |
102 | ## Important ⚠️
103 |
104 | Don't forget to put:
105 |
106 | ```swift
107 | DropDown.startListeningToKeyboard()
108 | ```
109 |
110 | in your `AppDelegate`'s `didFinishLaunching` method so that the drop down will handle its display with the keyboard displayed even the first time a drop down is showed.
111 |
112 | ## Advanced usage 🛠
113 |
114 | ### Direction
115 |
116 | The drop down can be shown below or above the anchor view with:
117 | ```swift
118 | dropDown.direction = .any
119 | ```
120 |
121 | With `.any` the drop down will try to displa itself below the anchor view when possible, otherwise above if there is more place than below.
122 | You can restrict the possible directions by using `.top` or `.bottom`.
123 |
124 | ### Offset
125 |
126 | By default, the drop down will be shown onto to anchor view. It will hide it.
127 | If you need the drop down to be below your anchor view when the direction of the drop down is `.bottom`, you can precise an offset like this:
128 |
129 | ```swift
130 | // Top of drop down will be below the anchorView
131 | dropDown.bottomOffset = CGPoint(x: 0, y:(dropDown.anchorView?.plainView.bounds.height)!)
132 | ```
133 |
134 | If you set the drop down direction to `.any` or `.top` you can also precise the offset when the drop down will shown above like this:
135 |
136 | ```swift
137 | // When drop down is displayed with `Direction.top`, it will be above the anchorView
138 | dropDown.topOffset = CGPoint(x: 0, y:-(dropDown.anchorView?.plainView.bounds.height)!)
139 | ```
140 | *Note the minus sign here that is use to offset to the top.*
141 |
142 | ### Cell configuration
143 |
144 | #### Formatted text
145 |
146 | By default, the cells in the drop down have the `dataSource` values as text.
147 | If you want a custom formatted text for the cells, you can set `cellConfiguration` like this:
148 |
149 | ```swift
150 | dropDown.cellConfiguration = { [unowned self] (index, item) in
151 | return "- \(item) (option \(index))"
152 | }
153 | ```
154 |
155 | #### Custom cell
156 |
157 | You can also create your own custom cell, from your .xib file. To have something like this for example:
158 |
[](Screenshots/3.png)
159 |
160 | You can check out a concrete example in the Demo inside this project (go to `ViewController.swift`, line 125).
161 |
162 | For this you have to:
163 |
164 | - Create a [`DropDownCell`](DropDown/src/DropDownCell.swift) subclass (e.g. *MyCell.swift*)
165 | ```swift
166 | class MyCell: DropDownCell {
167 | @IBOutlet weak var logoImageView: UIImageView!
168 | }
169 | ```
170 | - Create your custom xib (e.g. *MyCell.xib*) and design your cell view in it
171 | - Link the cell in your xib to your custom class
172 | - At least have a label in your xib to link to the [`optionLabel`](DropDown/src/DropDownCell.swift#L14) `IBOutlet` in code (`optionLabel` is a property of `DropDownCell`)
173 |
[](Screenshots/customCells/links.png)
174 |
[](Screenshots/customCells/xib.png)
175 | - Then, you simply need to do this:
176 | ```swift
177 | let dropDown = DropDown()
178 |
179 | // The view to which the drop down will appear on
180 | dropDown.anchorView = view // UIView or UIBarButtonItem
181 |
182 | // The list of items to display. Can be changed dynamically
183 | dropDown.dataSource = ["Car", "Motorcycle", "Truck"]
184 |
185 | /*** IMPORTANT PART FOR CUSTOM CELLS ***/
186 | dropDown.cellNib = UINib(nibName: "MyCell", bundle: nil)
187 |
188 | dropDown.customCellConfiguration = { (index: Index, item: String, cell: DropDownCell) -> Void in
189 | guard let cell = cell as? MyCell else { return }
190 |
191 | // Setup your custom UI components
192 | cell.logoImageView.image = UIImage(named: "logo_\(index)")
193 | }
194 | /*** END - IMPORTANT PART FOR CUSTOM CELLS ***/
195 | ```
196 | - And you're good to go! 🙆
197 |
198 | For a complete example, don't hesitate to check the demo app and code.
199 |
200 | ### Events
201 |
202 | ```swift
203 | dropDown.cancelAction = { [unowned self] in
204 | println("Drop down dismissed")
205 | }
206 |
207 | dropDown.willShowAction = { [unowned self] in
208 | println("Drop down will show")
209 | }
210 | ```
211 |
212 | ### Dismiss modes
213 |
214 | ```swift
215 | dropDown.dismissMode = .onTap
216 | ```
217 |
218 | You have 3 dismiss mode with the `DismissMode` enum:
219 |
220 | - `onTap`: A tap oustide the drop down is needed to dismiss it (Default)
221 | - `automatic`: No tap is needed to dismiss the drop down. As soon as the user interact with anything else than the drop down, the drop down is dismissed
222 | - `manual`: The drop down can only be dismissed manually (in code)
223 |
224 | ### Others
225 |
226 | You can manually (pre)select a row with:
227 |
228 | ```swift
229 | dropDown.selectRow(at: 3)
230 | ```
231 |
232 | The data source is reloaded automatically when changing the `dataSource` property.
233 | If needed, you can reload the data source manually by doing:
234 |
235 | ```swift
236 | dropDown.reloadAllComponents()
237 | ```
238 |
239 | You can get info about the selected item at any time with this:
240 |
241 | ```swift
242 | dropDown.selectedItem // String?
243 | dropDown.indexForSelectedRow // Int?
244 | ```
245 |
246 | ## Customize UI 🖌
247 |
248 | You can customize these properties of the drop down:
249 |
250 | - `textFont`: the font of the text for each cells of the drop down.
251 | - `textColor`: the color of the text for each cells of the drop down.
252 | - `selectedTextColor`: the color of the text for selected cells of the drop down.
253 | - `backgroundColor`: the background color of the drop down.
254 | - `selectionBackgroundColor`: the background color of the selected cell in the drop down.
255 | - `cellHeight`: the height of the drop down cells.
256 | - `dimmedBackgroundColor`: the color of the background (behind the drop down, covering the entire screen).
257 | - `cornerRadius`: the corner radius of the drop down (see [info](#Issues) below if you encounter any issue)
258 | - `setupMaskedCorners`: the masked corners of the dropdown. Use this along with `cornerRadius` to set the corner radius only on certain corners.
259 |
260 | You can change them through each instance of `DropDown` or via `UIAppearance` like this for example:
261 |
262 | ```swift
263 | DropDown.appearance().textColor = UIColor.black
264 | DropDown.appearance().selectedTextColor = UIColor.red
265 | DropDown.appearance().textFont = UIFont.systemFont(ofSize: 15)
266 | DropDown.appearance().backgroundColor = UIColor.white
267 | DropDown.appearance().selectionBackgroundColor = UIColor.lightGray
268 | DropDown.appearance().cellHeight = 60
269 | ```
270 |
271 | ## Expert mode 🤓
272 |
273 | when calling the `show` method, it returns a tuple like this:
274 |
275 | ```swift
276 | (canBeDisplayed: Bool, offscreenHeight: CGFloat?)
277 | ```
278 |
279 | - `canBeDisplayed`: Tells if there is enough height to display the drop down. If its value is `false`, the drop down is not showed.
280 | - `offscreenHeight`: If the drop down was not able to show all cells from the data source at once, `offscreenHeight` will contain the height needed to display all cells at once (without having to scroll through them). This can be used in a scroll view or table view to scroll enough before showing the drop down.
281 |
282 | ## Issues
283 |
284 | If you experience the compiler error *"Ambiguous use of 'cornerRadius'"* on line:
285 | ```swift
286 | DropDown.appearance().cornerRadius = 10
287 | ```
288 |
289 | Please use intead:
290 | ```swift
291 | DropDown.appearance().setupCornerRadius(10) // available since v2.3.6
292 | ```
293 |
294 | ## Requirements
295 |
296 | * Xcode 8+
297 | * Swift 3.0
298 | * iOS 8+
299 | * ARC
300 |
301 | ## License
302 |
303 | This project is under MIT license. For more information, see `LICENSE` file.
304 |
305 | ## Credits
306 |
307 | DropDown was inspired by the Material Design version of the [Simple Menu](http://www.google.com/design/spec/components/menus.html#menus-simple-menus).
308 |
309 | DropDown was done to integrate in a project I work on:
310 | [](https://assis.to)
311 |
312 | It will be updated when necessary and fixes will be done as soon as discovered to keep it up to date.
313 |
314 | I work at
315 | [](http://pinch.eu)
316 |
317 | You can find me on Twitter [@kevinh6113](https://twitter.com/kevinh6113).
318 |
319 | Enjoy!
320 |
--------------------------------------------------------------------------------
/Screenshots/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Screenshots/1.png
--------------------------------------------------------------------------------
/Screenshots/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Screenshots/2.png
--------------------------------------------------------------------------------
/Screenshots/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Screenshots/3.png
--------------------------------------------------------------------------------
/Screenshots/customCells/links.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Screenshots/customCells/links.png
--------------------------------------------------------------------------------
/Screenshots/customCells/xib.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Screenshots/customCells/xib.png
--------------------------------------------------------------------------------
/Screenshots/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AssistoLab/DropDown/2ab6f6ce19f0117d1a76ea043ef8f57722c65d16/Screenshots/logo.png
--------------------------------------------------------------------------------