├── .build
└── workspace-state.json
├── .gitignore
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── Drop Down TextField.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── Drop Down TextField.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── Drop Down TextField
├── AppDelegate.h
├── AppDelegate.m
├── Drop Down TextField-Info.plist
├── Drop Down TextField-Prefix.pch
├── Images.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ └── LaunchImage.launchimage
│ │ └── Contents.json
├── LaunchScreen.storyboard
├── Main.storyboard
├── Montserrat-Regular.otf
├── ViewController.h
├── ViewController.m
├── en.lproj
│ └── InfoPlist.strings
└── main.m
├── DropDownTextFieldSwift
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
├── Base.lproj
│ └── LaunchScreen.storyboard
├── Info.plist
├── Main.storyboard
├── SceneDelegate.swift
└── ViewController.swift
├── IQDropDownTextField.podspec.json
├── IQDropDownTextField
├── IQDropDownTextField+DateTime.h
├── IQDropDownTextField+DateTime.m
├── IQDropDownTextField+Internal.h
├── IQDropDownTextField+Internal.m
├── IQDropDownTextField.h
├── IQDropDownTextField.m
├── IQDropDownTextFieldConstants.h
└── PrivacyInfo.xcprivacy
├── IQDropDownTextFieldSwift.podspec.json
├── IQDropDownTextFieldSwift
├── IQDropDownTextField+Date.swift
├── IQDropDownTextField+Menu.swift
├── IQDropDownTextField+Picker.swift
├── IQDropDownTextField.swift
├── IQDropDownTextFieldConstants.swift
├── IQDropDownTextFieldDataSource.swift
├── IQDropDownTextFieldDelegate.swift
└── PrivacyInfo.xcprivacy
├── Images
├── date.png
├── date_time.png
├── large_text.png
├── multi_list.png
├── simple.png
└── time.png
├── LICENSE
├── LICENSE.md
├── Package.swift
├── Podfile
├── Podfile.lock
└── README.md
/.build/workspace-state.json:
--------------------------------------------------------------------------------
1 | {
2 | "object" : {
3 | "artifacts" : [
4 |
5 | ],
6 | "dependencies" : [
7 |
8 | ]
9 | },
10 | "version" : 6
11 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | .DS_Store
3 | */build/*
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | profile
14 | *.moved-aside
15 | DerivedData
16 | .idea/
17 | *.hmap
18 | *.xccheckout
19 |
20 | #CocoaPods
21 | Pods
22 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Drop Down TextField.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Drop Down TextField.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Drop Down TextField.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Drop Down TextField.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Drop Down TextField/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // Drop Down TextField
4 | //
5 | // Created by hp on 10/11/13.
6 | // Copyright (c) 2013 Iftekhar. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class ViewController;
12 |
13 | @interface AppDelegate : UIResponder
14 |
15 | @property (strong, nonatomic) UIWindow *window;
16 |
17 | @property (strong, nonatomic) ViewController *viewController;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Drop Down TextField/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // Drop Down TextField
4 | //
5 | // Created by hp on 10/11/13.
6 | // Copyright (c) 2013 Iftekhar. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import
11 | #import "ViewController.h"
12 |
13 | @implementation AppDelegate
14 |
15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
16 | {
17 | return YES;
18 | }
19 |
20 | - (void)applicationWillResignActive:(UIApplication *)application
21 | {
22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
23 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
24 | }
25 |
26 | - (void)applicationDidEnterBackground:(UIApplication *)application
27 | {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | - (void)applicationWillEnterForeground:(UIApplication *)application
33 | {
34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
35 | }
36 |
37 | - (void)applicationDidBecomeActive:(UIApplication *)application
38 | {
39 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
40 | }
41 |
42 | - (void)applicationWillTerminate:(UIApplication *)application
43 | {
44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
45 | }
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/Drop Down TextField/Drop Down TextField-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | DropDown TextField
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIcons
12 |
13 | CFBundleIcons~ipad
14 |
15 | CFBundleIdentifier
16 | $(PRODUCT_BUNDLE_IDENTIFIER)
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 | CFBundleName
20 | ${PRODUCT_NAME}
21 | CFBundlePackageType
22 | APPL
23 | CFBundleShortVersionString
24 | $(MARKETING_VERSION)
25 | CFBundleSignature
26 | ????
27 | CFBundleVersion
28 | $(CURRENT_PROJECT_VERSION)
29 | LSRequiresIPhoneOS
30 |
31 | UIAppFonts
32 |
33 | Montserrat-Regular.otf
34 |
35 | UILaunchStoryboardName
36 | LaunchScreen
37 | UIMainStoryboardFile
38 | Main
39 | UIRequiredDeviceCapabilities
40 |
41 | armv7
42 |
43 | UISupportedInterfaceOrientations
44 |
45 | UIInterfaceOrientationPortrait
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Drop Down TextField/Drop Down TextField-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header for all source files of the 'Drop Down TextField' target in the 'Drop Down TextField' project
3 | //
4 |
5 | #import
6 |
7 | #ifndef __IPHONE_4_0
8 | #warning "This project uses features only available in iOS SDK 4.0 and later."
9 | #endif
10 |
11 | #ifdef __OBJC__
12 | #import
13 | #import
14 | #endif
15 |
--------------------------------------------------------------------------------
/Drop Down TextField/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" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
--------------------------------------------------------------------------------
/Drop Down TextField/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Drop Down TextField/Images.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "portrait",
5 | "idiom" : "iphone",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "11.0",
8 | "subtype" : "2436h",
9 | "scale" : "3x"
10 | },
11 | {
12 | "orientation" : "portrait",
13 | "idiom" : "iphone",
14 | "extent" : "full-screen",
15 | "minimum-system-version" : "8.0",
16 | "subtype" : "736h",
17 | "scale" : "3x"
18 | },
19 | {
20 | "orientation" : "portrait",
21 | "idiom" : "iphone",
22 | "extent" : "full-screen",
23 | "minimum-system-version" : "8.0",
24 | "subtype" : "667h",
25 | "scale" : "2x"
26 | }
27 | ],
28 | "info" : {
29 | "version" : 1,
30 | "author" : "xcode"
31 | }
32 | }
--------------------------------------------------------------------------------
/Drop Down TextField/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Drop Down TextField/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 |
33 |
40 |
41 |
42 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
--------------------------------------------------------------------------------
/Drop Down TextField/Montserrat-Regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackiftekhar/IQDropDownTextField/099faf11b3b2a18f4852cb76b0b908a334fae05d/Drop Down TextField/Montserrat-Regular.otf
--------------------------------------------------------------------------------
/Drop Down TextField/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // Drop Down TextField
4 | //
5 | // Created by hp on 10/11/13.
6 | // Copyright (c) 2013 Iftekhar. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "IQDropDownTextField.h"
11 |
12 | @interface ViewController : UIViewController
13 | {
14 | IBOutlet IQDropDownTextField *textFieldTextPicker;
15 | IBOutlet IQDropDownTextField *textFieldOptionalTextPicker;
16 | IBOutlet IQDropDownTextField *textFieldDatePicker;
17 | IBOutlet IQDropDownTextField *textFieldTimePicker;
18 | IBOutlet IQDropDownTextField *textFieldDateTimePicker;
19 |
20 | }
21 | @end
22 |
--------------------------------------------------------------------------------
/Drop Down TextField/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // Drop Down TextField
4 | //
5 | // Created by hp on 10/11/13.
6 | // Copyright (c) 2013 Iftekhar. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 |
11 | @interface ViewController ()
12 |
13 | //@property IQDropDownTextField *dropDown;
14 |
15 | @end
16 |
17 | @implementation ViewController
18 |
19 | - (void)viewDidLoad
20 | {
21 | [super viewDidLoad];
22 |
23 | // self.dropDown = [[IQDropDownTextField alloc] init];
24 | // [self.dropDown setItemList:@[@"London",@"Johannesburg",@"Moscow",@"Mumbai",@"Tokyo",@"Sydney",@"Paris",@"Bangkok",@"New York",@"Istanbul",@"Dubai",@"Singapore"]];
25 | // self.dropDown.dropDownMode = IQDropDownModeTextField;
26 | // self.dropDown.isOptionalDropDown = YES;
27 | // [self.view addSubview:self.dropDown];
28 |
29 | textFieldTextPicker.showDismissToolbar = YES;
30 | textFieldOptionalTextPicker.showDismissToolbar = YES;
31 | textFieldDatePicker.showDismissToolbar = YES;
32 | textFieldTimePicker.showDismissToolbar = YES;
33 | textFieldDateTimePicker.showDismissToolbar = YES;
34 |
35 | UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
36 | [indicator startAnimating];
37 |
38 | UISwitch *aSwitch = [[UISwitch alloc] init];
39 |
40 | [textFieldTextPicker setItemList:@[@"London",@"Johannesburg",@"Moscow",@"Mumbai",@"Tokyo",@"Sydney",@"Paris",@"Bangkok",@"New York",@"Istanbul",@"Dubai",@"Singapore"]];
41 | textFieldTextPicker.selectedRow = 2;
42 | [textFieldTextPicker setItemListView:@[[NSNull null],indicator,[NSNull null],aSwitch,[NSNull null],[NSNull null],[NSNull null],[NSNull null],[NSNull null],[NSNull null],[NSNull null],[NSNull null],[NSNull null]]];
43 |
44 | /*
45 | Uncomment the following lines to set a custom font or text color for the items, as well as a custom text color for
46 | the optional item.
47 | */
48 | // textFieldTextPicker.font = [UIFont fontWithName:@"Montserrat-Regular" size:16];
49 | // textFieldTextPicker.textColor = [UIColor redColor];
50 | // textFieldTextPicker.optionalItemTextColor = [UIColor brownColor];
51 |
52 | [textFieldOptionalTextPicker setItemList:[NSArray arrayWithObjects:@"1",@"2",@"3",@"4",@"5",@"6", nil]];
53 | textFieldOptionalTextPicker.selectedRow = 3;
54 | [textFieldOptionalTextPicker setItemListUI:[NSArray arrayWithObjects:@"1 Year Old",@"2 Years Old",@"3 Years Old",@"4 Years Old",@"5 Years Old",@"6 Years Old", nil]];
55 |
56 | // NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
57 | // [formatter setDateFormat:@"EEE MMMM dd yyyy"];
58 | // [textFieldDatePicker setDateFormatter:formatter];
59 | [textFieldDatePicker setDropDownMode:IQDropDownModeDatePicker];
60 | [textFieldTimePicker setDropDownMode:IQDropDownModeTimePicker];
61 | [textFieldDateTimePicker setDropDownMode:IQDropDownModeDateTimePicker];
62 | }
63 |
64 | -(void)textField:(nonnull IQDropDownTextField*)textField didSelectItem:(nullable NSString*)item
65 | {
66 | NSLog(@"%@: %@",NSStringFromSelector(_cmd),item);
67 | }
68 |
69 | -(void)textField:(IQDropDownTextField *)textField didSelectDate:(nullable NSDate *)date
70 | {
71 | NSLog(@"%@: %@",NSStringFromSelector(_cmd),date);
72 | }
73 |
74 | -(BOOL)textField:(nonnull IQDropDownTextField*)textField canSelectItem:(nullable NSString*)item
75 | {
76 | NSLog(@"%@: %@",NSStringFromSelector(_cmd),item);
77 | return YES;
78 | }
79 |
80 | -(IQProposedSelection)textField:(nonnull IQDropDownTextField*)textField proposedSelectionModeForItem:(nullable NSString*)item
81 | {
82 | NSLog(@"%@: %@",NSStringFromSelector(_cmd),item);
83 | return IQProposedSelectionBoth;
84 | }
85 |
86 | -(void)textFieldDidBeginEditing:(UITextField *)textField
87 | {
88 | NSLog(@"%@",NSStringFromSelector(_cmd));
89 | }
90 |
91 | -(void)textFieldDidEndEditing:(UITextField *)textField
92 | {
93 | NSLog(@"%@",NSStringFromSelector(_cmd));
94 | }
95 |
96 | -(void)doneClicked:(UIBarButtonItem*)button
97 | {
98 | [self.view endEditing:YES];
99 |
100 | NSLog(@"textFieldTextPicker.selectedItem: %@", textFieldTextPicker.selectedItem);
101 | NSLog(@"textFieldOptionalTextPicker.selectedItem: %@", textFieldOptionalTextPicker.selectedItem);
102 | NSLog(@"textFieldDatePicker.selectedItem: %@", textFieldDatePicker.selectedItem);
103 | NSLog(@"textFieldTimePicker.selectedItem: %@", textFieldTimePicker.selectedItem);
104 | NSLog(@"textFieldDateTimePicker.selectedItem: %@", textFieldDateTimePicker.selectedItem);
105 | }
106 |
107 | - (IBAction)isOptionalToggle:(UIButton *)sender {
108 | textFieldOptionalTextPicker.isOptionalDropDown = !textFieldOptionalTextPicker.isOptionalDropDown;
109 | textFieldTextPicker.isOptionalDropDown = !textFieldTextPicker.isOptionalDropDown;
110 | textFieldDatePicker.isOptionalDropDown = !textFieldDatePicker.isOptionalDropDown;
111 | textFieldTimePicker.isOptionalDropDown = !textFieldTimePicker.isOptionalDropDown;
112 | textFieldDateTimePicker.isOptionalDropDown = !textFieldDateTimePicker.isOptionalDropDown;
113 | // self.dropDown.isOptionalDropDown = !self.dropDown.isOptionalDropDown;
114 | }
115 |
116 | - (IBAction)resetAction:(UIButton *)sender {
117 | textFieldTextPicker.selectedItem = nil;
118 | textFieldOptionalTextPicker.selectedItem = nil;
119 | textFieldDatePicker.selectedItem = nil;
120 | textFieldTimePicker.date = nil;
121 | textFieldDateTimePicker.selectedItem = nil;
122 | // self.dropDown.selectedItem = nil;
123 | }
124 |
125 | - (void)didReceiveMemoryWarning
126 | {
127 | [super didReceiveMemoryWarning];
128 | // Dispose of any resources that can be recreated.
129 | }
130 |
131 | @end
132 |
--------------------------------------------------------------------------------
/Drop Down TextField/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/Drop Down TextField/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // Drop Down TextField
4 | //
5 | // Created by hp on 10/11/13.
6 | // Copyright (c) 2013 Iftekhar. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "AppDelegate.h"
12 |
13 | int main(int argc, char *argv[])
14 | {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/DropDownTextFieldSwift/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // DropDownTextFieldSwift
4 | //
5 | // Created by Iftekhar on 31/08/20.
6 | // Copyright © 2020 Iftekhar. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @main
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | func application(_ application: UIApplication,
15 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 |
20 | // MARK: UISceneSession Lifecycle
21 |
22 | @available(iOS 13.0, *)
23 | func application(_ application: UIApplication,
24 | configurationForConnecting connectingSceneSession: UISceneSession,
25 | options: UIScene.ConnectionOptions) -> UISceneConfiguration {
26 | // Called when a new scene session is being created.
27 | // Use this method to select a configuration to create the new scene with.
28 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
29 | }
30 |
31 | @available(iOS 13.0, *)
32 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
33 | // Called when the user discards a scene session.
34 | // If any sessions were discarded while the application was not running,
35 | // this will be called shortly after application:didFinishLaunchingWithOptions.
36 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/DropDownTextFieldSwift/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/DropDownTextFieldSwift/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/DropDownTextFieldSwift/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/DropDownTextFieldSwift/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 | UISceneStoryboardFile
37 | Main
38 |
39 |
40 |
41 |
42 | UILaunchStoryboardName
43 | LaunchScreen
44 | UIMainStoryboardFile
45 | Main
46 | UIRequiredDeviceCapabilities
47 |
48 | armv7
49 |
50 | UISupportedInterfaceOrientations
51 |
52 | UIInterfaceOrientationPortrait
53 | UIInterfaceOrientationLandscapeLeft
54 | UIInterfaceOrientationLandscapeRight
55 |
56 | UISupportedInterfaceOrientations~ipad
57 |
58 | UIInterfaceOrientationPortrait
59 | UIInterfaceOrientationPortraitUpsideDown
60 | UIInterfaceOrientationLandscapeLeft
61 | UIInterfaceOrientationLandscapeRight
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/DropDownTextFieldSwift/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 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
52 |
59 |
66 |
67 |
68 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
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 |
--------------------------------------------------------------------------------
/DropDownTextFieldSwift/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // DropDownTextFieldSwift
4 | //
5 | // Created by Iftekhar on 31/08/20.
6 | // Copyright © 2020 Iftekhar. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @available(iOS 13.0, *)
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
17 | options connectionOptions: UIScene.ConnectionOptions) {
18 | }
19 |
20 | func sceneDidDisconnect(_ scene: UIScene) {
21 | }
22 |
23 | func sceneDidBecomeActive(_ scene: UIScene) {
24 | }
25 |
26 | func sceneWillResignActive(_ scene: UIScene) {
27 | }
28 |
29 | func sceneWillEnterForeground(_ scene: UIScene) {
30 | }
31 |
32 | func sceneDidEnterBackground(_ scene: UIScene) {
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/DropDownTextFieldSwift/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // DropDownTextFieldSwift
4 | //
5 | // Created by Iftekhar on 31/08/20.
6 | // Copyright © 2020 Iftekhar. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import IQDropDownTextFieldSwift
11 |
12 | class ViewController: UIViewController {
13 |
14 | // @IBOutlet var mainStackView: UIStackView!
15 |
16 | @IBOutlet var textFieldTextPicker: IQDropDownTextField!
17 | @IBOutlet var textFieldOptionalTextPicker: IQDropDownTextField!
18 | @IBOutlet var textFieldMultiListTextPicker: IQDropDownTextField!
19 | @IBOutlet var textFieldDatePicker: IQDropDownTextField!
20 | @IBOutlet var textFieldTimePicker: IQDropDownTextField!
21 | @IBOutlet var textFieldDateTimePicker: IQDropDownTextField!
22 | @IBOutlet var menuButton: UIButton!
23 |
24 | // private var dropDown: IQDropDownTextField = IQDropDownTextField()
25 |
26 | // swiftlint:disable function_body_length
27 | override func viewDidLoad() {
28 | super.viewDidLoad()
29 |
30 | // self.dropDown.itemList = ["London",
31 | // "Johannesburg",
32 | // "Moscow",
33 | // "Mumbai",
34 | // "Tokyo",
35 | // "Sydney",
36 | // "Paris",
37 | // "Bangkok",
38 | // "New York",
39 | // "Istanbul",
40 | // "Dubai",
41 | // "Singapore"]
42 | // self.dropDown.dropDownMode = .list
43 | // self.dropDown.selectedRow = 2
44 | // self.dropDown.isOptionalDropDown = true
45 | // self.mainStackView.addArrangedSubview(self.dropDown)
46 |
47 | textFieldTextPicker.showDismissToolbar = true
48 | textFieldOptionalTextPicker.showDismissToolbar = true
49 | textFieldMultiListTextPicker.showDismissToolbar = true
50 | textFieldDatePicker.showDismissToolbar = true
51 | textFieldTimePicker.showDismissToolbar = true
52 | textFieldDateTimePicker.showDismissToolbar = true
53 |
54 | let indicator: UIActivityIndicatorView! = {
55 | if #available(iOS 13.0, *) {
56 | return UIActivityIndicatorView(style: .medium)
57 | } else {
58 | return UIActivityIndicatorView(style: .gray)
59 | }
60 | }()
61 | indicator.startAnimating()
62 |
63 | let aSwitch: UISwitch = UISwitch()
64 |
65 | textFieldTextPicker.itemList = ["London",
66 | "Johannesburg",
67 | "Moscow",
68 | "Mumbai",
69 | "Tokyo",
70 | "Sydney",
71 | "Paris",
72 | "Bangkok",
73 | "New York",
74 | "Istanbul",
75 | "Dubai",
76 | "Singapore"]
77 |
78 | // textFieldTextPicker.itemList = ["Thomas Jefferson High School for Science and Technology",
79 | // "Gwinnett School of Mathematics, Science and Technology",
80 | // "California Academy of Mathematics and Science",
81 | // "Loveless Academic Magnet Program High School",
82 | // "Irma Lerma Rangel Young Women's Leadership School",
83 | // "Middlesex County Academy, Mathematics and Engineering Technologies",
84 | // "Queens High School for the Sciences at York College"]
85 | // textFieldTextPicker.adjustsFontSizeToFitWidth = false
86 |
87 | textFieldTextPicker.selectedRow = 2
88 | let viewList: [UIView?] = [nil, indicator, nil, aSwitch]
89 | textFieldTextPicker.itemListView = viewList
90 |
91 | /*
92 | Uncomment the following lines to set a custom font or text color for the items,
93 | as well as a custom text color for the optional item.
94 | */
95 | // textFieldTextPicker.font = [UIFont fontWithName:@"Montserrat-Regular" size:16];
96 | // textFieldTextPicker.textColor = [UIColor redColor];
97 | // textFieldTextPicker.optionalItemTextColor = [UIColor brownColor];
98 |
99 | textFieldOptionalTextPicker.itemList = ["1", "2", "3", "4", "5", "6"]
100 | textFieldOptionalTextPicker.selectedRow = 3
101 |
102 | textFieldOptionalTextPicker.selectionFormatHandler = { (selectedItem, _) in
103 |
104 | if let selectedItem = selectedItem {
105 | if selectedItem == "1" {
106 | return selectedItem + " Year Old"
107 | } else {
108 | return selectedItem + " Years Old"
109 | }
110 | } else {
111 | return ""
112 | }
113 | }
114 | textFieldMultiListTextPicker.dropDownMode = .multiList
115 | let heightFeet: [String] = ["Not Sure", "4", "5", "6", "7", "8"]
116 | let heightInches: [String] = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"]
117 |
118 | // textFieldMultiListTextPicker.widthsForComponents = [100, 150]
119 | // textFieldMultiListTextPicker.heightsForComponents = [30, 100]
120 | textFieldMultiListTextPicker.isOptionalDropDowns = [true, false]
121 | textFieldMultiListTextPicker.optionalItemTexts = ["Select Feet", "Select Inches"]
122 | textFieldMultiListTextPicker.multiItemList = [heightFeet, heightInches]
123 | textFieldMultiListTextPicker.multiListSelectionFormatHandler = { (selectedItems, selectedIndexes) in
124 |
125 | if selectedIndexes.first == 0 {
126 | return "Not Sure"
127 | } else if let first = selectedItems.first, let first = first,
128 | let last = selectedItems.last, let last = last {
129 | return "\(first)' \(last)\""
130 | } else {
131 | return ""
132 | }
133 | }
134 |
135 | // NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
136 | // [formatter setDateFormat:@"EEE MMMM dd yyyy"];
137 | // [textFieldDatePicker setDateFormatter:formatter];
138 |
139 | textFieldDatePicker.dropDownMode = .date
140 | textFieldTimePicker.dropDownMode = .time
141 | textFieldDateTimePicker.dropDownMode = .dateTime
142 |
143 | textFieldTextPicker.delegate = self
144 | textFieldTextPicker.dataSource = self
145 |
146 | textFieldOptionalTextPicker.delegate = self
147 | textFieldOptionalTextPicker.dataSource = self
148 |
149 | textFieldDatePicker.delegate = self
150 | textFieldDatePicker.dataSource = self
151 |
152 | textFieldTimePicker.delegate = self
153 | textFieldTimePicker.dataSource = self
154 |
155 | textFieldDateTimePicker.delegate = self
156 | textFieldDateTimePicker.dataSource = self
157 |
158 | if #available(iOS 15.0, *) {
159 | menuButton.isHidden = false
160 | } else {
161 | menuButton.isHidden = true
162 | }
163 |
164 | // dropDown.delegate = self
165 | // dropDown.dataSource = self
166 | }
167 | // swiftlint:enable function_body_length
168 |
169 | func textFieldDidBeginEditing(textField: UITextField) {
170 | print(#function)
171 | }
172 |
173 | func textFieldDidEndEditing(textField: UITextField) {
174 | print(#function)
175 | }
176 |
177 | func doneClicked(button: UIBarButtonItem!) {
178 | self.view.endEditing(true)
179 |
180 | // print("textFieldTextPicker.selectedItem: \(textFieldTextPicker.selectedItem)")
181 | // print("textFieldOptionalTextPicker.selectedItem: \(textFieldOptionalTextPicker.selectedItem)")
182 | // print("textFieldDatePicker.selectedItem: \(textFieldDatePicker.selectedItem)")
183 | // print("textFieldTimePicker.selectedItem: \(textFieldTimePicker.selectedItem)")
184 | // print("textFieldDateTimePicker.selectedItem: \(textFieldDateTimePicker.selectedItem)")
185 | }
186 |
187 | @IBAction func menuToggle(_ sender: UIButton) {
188 | if #available(iOS 15.0, *) {
189 | textFieldTextPicker.showMenuButton = !textFieldTextPicker.showMenuButton
190 | textFieldOptionalTextPicker.showMenuButton = !textFieldOptionalTextPicker.showMenuButton
191 | textFieldMultiListTextPicker.showMenuButton = !textFieldMultiListTextPicker.showMenuButton
192 | textFieldDatePicker.showMenuButton = !textFieldDatePicker.showMenuButton
193 | textFieldTimePicker.showMenuButton = !textFieldTimePicker.showMenuButton
194 | textFieldDateTimePicker.showMenuButton = !textFieldDateTimePicker.showMenuButton
195 | }
196 | }
197 |
198 | @IBAction func isOptionalToggle(_ sender: UIButton) {
199 | textFieldTextPicker.isOptionalDropDown = !textFieldTextPicker.isOptionalDropDown
200 | textFieldOptionalTextPicker.isOptionalDropDown = !textFieldOptionalTextPicker.isOptionalDropDown
201 | textFieldMultiListTextPicker.isOptionalDropDown = !textFieldMultiListTextPicker.isOptionalDropDown
202 | textFieldDatePicker.isOptionalDropDown = !textFieldDatePicker.isOptionalDropDown
203 | textFieldTimePicker.isOptionalDropDown = !textFieldTimePicker.isOptionalDropDown
204 | textFieldDateTimePicker.isOptionalDropDown = !textFieldDateTimePicker.isOptionalDropDown
205 | // self.dropDown.isOptionalDropDown = !self.dropDown.isOptionalDropDown
206 | }
207 |
208 | @IBAction func resetAction(_ sender: UIButton) {
209 | textFieldTextPicker.selectedItem = nil
210 | textFieldOptionalTextPicker.selectedItem = nil
211 | textFieldMultiListTextPicker.selectedItem = nil
212 | textFieldDatePicker.selectedItem = nil
213 | textFieldTimePicker.date = nil
214 | textFieldDateTimePicker.selectedItem = nil
215 | // self.dropDown.selectedItem = nil
216 | }
217 | }
218 |
219 | extension ViewController: IQDropDownTextFieldDelegate, IQDropDownTextFieldDataSource {
220 |
221 | func textField(textField: IQDropDownTextField, didSelectItem item: String?) {
222 | print(#function)
223 | // print(item)
224 | }
225 |
226 | func textField(textField: IQDropDownTextField, didSelectItems items: [String?]) {
227 | }
228 |
229 | func textField(textField: IQDropDownTextField, didSelectDate date: Date?) {
230 | print(#function)
231 | }
232 |
233 | func textField(textField: IQDropDownTextField, canSelectItem item: String) -> Bool {
234 | print(#function)
235 | // print(item)
236 | return true
237 | }
238 |
239 | func textField(textField: IQDropDownTextField, proposedSelectionModeForItem item: String) -> IQProposedSelection {
240 | print(#function)
241 | // print(item)
242 | return .both
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/IQDropDownTextField.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "IQDropDownTextField",
3 | "version": "4.0.4",
4 | "source": {
5 | "git": "https://github.com/hackiftekhar/IQDropDownTextField.git",
6 | "tag": "4.0.4"
7 | },
8 | "summary": "TextField with DropDown support using UIPickerView",
9 | "homepage": "https://github.com/hackiftekhar/IQDropDownTextField",
10 | "license": "MIT",
11 | "authors": {
12 | "Iftekhar Qurashi": "hack.iftekhar@gmail.com"
13 | },
14 | "platforms": {
15 | "ios": "11.0"
16 | },
17 | "source_files": [
18 | "IQDropDownTextField/*.{h,m}"
19 | ],
20 | "resource_bundles": {"IQDropDownTextField": "IQDropDownTextField/PrivacyInfo.xcprivacy"},
21 | "public_header_files": [
22 | "IQDropDownTextField/IQDropDownTextField.h",
23 | "IQDropDownTextField/IQDropDownTextField+DateTime.h",
24 | "IQDropDownTextField/IQDropDownTextFieldConstants.h"
25 | ],
26 | "frameworks": [
27 | "UIKit",
28 | "Foundation",
29 | "CoreGraphics"
30 | ],
31 | "requires_arc": true
32 | }
33 |
--------------------------------------------------------------------------------
/IQDropDownTextField/IQDropDownTextField+DateTime.h:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextField+DateTime.h
3 | // IQDropDownTextField
4 | //
5 | // Created by Iftekhar on 12/19/23.
6 | //
7 |
8 | #import "IQDropDownTextField.h"
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @interface IQDropDownTextField (DateTime)
13 |
14 | /**
15 | These are the picker object which internally used for showing list. Changing some properties might not work properly so do it at your own risk.
16 | */
17 | @property (nonnull, nonatomic, readonly) UIDatePicker *datePicker;
18 | @property (nonnull, nonatomic, readonly) UIDatePicker *timePicker;
19 | @property (nonnull, nonatomic, readonly) UIDatePicker *dateTimePicker;
20 |
21 |
22 | ///--------------------------------------------------------
23 | /// @name IQDropDownModeDatePicker/IQDropDownModeTimePicker
24 | ///*-------------------------------------------------------
25 |
26 | /**
27 | Selected date in UIDatePicker.
28 | */
29 | @property(nullable, nonatomic, copy) NSDate *date;
30 |
31 | /**
32 | Select date in UIDatePicker.
33 | */
34 | - (void)setDate:(nullable NSDate *)date animated:(BOOL)animated;
35 |
36 | /**
37 | DateComponents for date picker.
38 | */
39 | @property (nullable, nonatomic, readonly, copy) NSDateComponents *dateComponents;
40 |
41 | /**
42 | year
43 | */
44 | @property (nonatomic, readonly) NSInteger year;
45 |
46 | /**
47 | month
48 | */
49 | @property (nonatomic, readonly) NSInteger month;
50 |
51 | /**
52 | day
53 | */
54 | @property (nonatomic, readonly) NSInteger day;
55 |
56 | /**
57 | hour
58 | */
59 | @property (nonatomic, readonly) NSInteger hour;
60 |
61 | /**
62 | minute
63 | */
64 | @property (nonatomic, readonly) NSInteger minute;
65 |
66 | /**
67 | second
68 | */
69 | @property (nonatomic, readonly) NSInteger second;
70 |
71 |
72 | ///-------------------------------
73 | /// @name IQDropDownModeDatePicker
74 | ///-------------------------------
75 |
76 | /**
77 | Select date in UIDatePicker. Default is UIDatePickerModeDate
78 | */
79 | @property (nonatomic, assign) UIDatePickerMode datePickerMode;
80 |
81 | /**
82 | Date formatter to show date as text in textField.
83 | */
84 | @property (nullable, nonatomic, retain) NSDateFormatter *dateFormatter UI_APPEARANCE_SELECTOR;
85 |
86 |
87 | ///-------------------------------
88 | /// @name IQDropDownModeTimePicker
89 | ///-------------------------------
90 |
91 | /**
92 | Time formatter to show time as text in textField.
93 | */
94 | @property (nullable, nonatomic, retain) NSDateFormatter *timeFormatter UI_APPEARANCE_SELECTOR;
95 |
96 | @property (nullable, nonatomic, retain) NSDateFormatter *dateTimeFormatter UI_APPEARANCE_SELECTOR;
97 |
98 | @end
99 |
100 | NS_ASSUME_NONNULL_END
101 |
--------------------------------------------------------------------------------
/IQDropDownTextField/IQDropDownTextField+DateTime.m:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextField+DateTime.m
3 | // IQDropDownTextField
4 | //
5 | // Created by Iftekhar on 12/19/23.
6 | //
7 |
8 | #import "IQDropDownTextField+DateTime.h"
9 | #import "IQDropDownTextField+Internal.h"
10 | #import
11 |
12 | @implementation IQDropDownTextField (DateTime)
13 |
14 | - (nonnull UIDatePicker *) timePicker
15 | {
16 | UIDatePicker *_timePicker = objc_getAssociatedObject(self, _cmd);
17 | if (!_timePicker)
18 | {
19 | _timePicker = [[UIDatePicker alloc] init];
20 | [_timePicker setAutoresizingMask:(UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight)];
21 | [_timePicker setDatePickerMode:UIDatePickerModeTime];
22 | if (@available(iOS 13.4, *)) {
23 | [_timePicker setPreferredDatePickerStyle:UIDatePickerStyleWheels];
24 | }
25 | [_timePicker addTarget:self action:@selector(timeChanged:) forControlEvents:UIControlEventValueChanged];
26 | objc_setAssociatedObject(self, _cmd, _timePicker, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
27 | }
28 |
29 | return _timePicker;
30 | }
31 |
32 | - (nonnull UIDatePicker *) dateTimePicker
33 | {
34 | UIDatePicker *_dateTimePicker = objc_getAssociatedObject(self, _cmd);
35 |
36 | if (!_dateTimePicker)
37 | {
38 | _dateTimePicker = [[UIDatePicker alloc] init];
39 | [_dateTimePicker setAutoresizingMask:(UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight)];
40 | [_dateTimePicker setDatePickerMode:UIDatePickerModeDateAndTime];
41 | if (@available(iOS 13.4, *)) {
42 | [_dateTimePicker setPreferredDatePickerStyle:UIDatePickerStyleWheels];
43 | }
44 | [_dateTimePicker addTarget:self action:@selector(dateTimeChanged:) forControlEvents:UIControlEventValueChanged];
45 | objc_setAssociatedObject(self, _cmd, _dateTimePicker, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
46 | }
47 | return _dateTimePicker;
48 | }
49 |
50 | - (nonnull UIDatePicker *) datePicker
51 | {
52 | UIDatePicker *_datePicker = objc_getAssociatedObject(self, _cmd);
53 |
54 | if (!_datePicker)
55 | {
56 | _datePicker = [[UIDatePicker alloc] init];
57 | [_datePicker setAutoresizingMask:(UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight)];
58 | [_datePicker setDatePickerMode:UIDatePickerModeDate];
59 | if (@available(iOS 13.4, *)) {
60 | [_datePicker setPreferredDatePickerStyle:UIDatePickerStyleWheels];
61 | }
62 | [_datePicker addTarget:self action:@selector(dateChanged:) forControlEvents:UIControlEventValueChanged];
63 | objc_setAssociatedObject(self, _cmd, _datePicker, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
64 | }
65 |
66 | return _datePicker;
67 | }
68 |
69 | #pragma mark - UIDatePicker delegate
70 |
71 | - (void)dateChanged:(nonnull UIDatePicker *)datePicker
72 | {
73 | [self _setSelectedItem:[self.dateFormatter stringFromDate:datePicker.date] animated:NO shouldNotifyDelegate:YES];
74 | }
75 |
76 | - (void)timeChanged:(nonnull UIDatePicker *)timePicker
77 | {
78 | [self _setSelectedItem:[self.timeFormatter stringFromDate:timePicker.date] animated:NO shouldNotifyDelegate:YES];
79 | }
80 |
81 | - (void)dateTimeChanged:(nonnull UIDatePicker *)datePicker
82 | {
83 | [self _setSelectedItem:[self.dateTimeFormatter stringFromDate:datePicker.date] animated:NO shouldNotifyDelegate:YES];
84 | }
85 |
86 | -(nullable NSDate *)date
87 | {
88 | switch (self.dropDownMode)
89 | {
90 | case IQDropDownModeDatePicker:
91 | {
92 | if (self.isOptionalDropDown)
93 | {
94 | return [super.text length] ? [self.datePicker.date copy] : nil; break;
95 | }
96 | else
97 | {
98 | return [self.datePicker.date copy];
99 | }
100 | }
101 | case IQDropDownModeTimePicker:
102 | {
103 | if (self.isOptionalDropDown)
104 | {
105 | return [super.text length] ? [self.timePicker.date copy] : nil; break;
106 | }
107 | else
108 | {
109 | return [self.timePicker.date copy];
110 | }
111 | }
112 | case IQDropDownModeDateTimePicker:
113 | {
114 | if (self.isOptionalDropDown)
115 | {
116 | return [super.text length] ? [self.dateTimePicker.date copy] : nil; break;
117 | }
118 | else
119 | {
120 | return [self.dateTimePicker.date copy];
121 | }
122 | }
123 | default: return nil; break;
124 | }
125 | }
126 |
127 | -(void)setDate:(nullable NSDate *)date
128 | {
129 | [self setDate:date animated:NO];
130 | }
131 |
132 | - (void)setDate:(nullable NSDate *)date animated:(BOOL)animated
133 | {
134 | switch (self.dropDownMode)
135 | {
136 | case IQDropDownModeDatePicker:
137 | [self _setSelectedItem:[self.dateFormatter stringFromDate:date] animated:animated shouldNotifyDelegate:NO];
138 | break;
139 | case IQDropDownModeTimePicker:
140 | [self _setSelectedItem:[self.timeFormatter stringFromDate:date] animated:animated shouldNotifyDelegate:NO];
141 | break;
142 | case IQDropDownModeDateTimePicker:
143 | [self _setSelectedItem:[self.dateTimeFormatter stringFromDate:date] animated:animated shouldNotifyDelegate:NO];
144 | break;
145 | default:
146 | break;
147 | }
148 | }
149 |
150 | -(nullable NSDateComponents *)dateComponents
151 | {
152 | return [[NSCalendar currentCalendar] components: kCFCalendarUnitSecond | kCFCalendarUnitMinute | kCFCalendarUnitHour | kCFCalendarUnitDay | kCFCalendarUnitMonth | kCFCalendarUnitYear | kCFCalendarUnitEra fromDate:self.date];
153 | }
154 |
155 | - (NSInteger)year { return [[self dateComponents] year]; }
156 | - (NSInteger)month { return [[self dateComponents] month]; }
157 | - (NSInteger)day { return [[self dateComponents] day]; }
158 | - (NSInteger)hour { return [[self dateComponents] hour]; }
159 | - (NSInteger)minute { return [[self dateComponents] minute]; }
160 | - (NSInteger)second { return [[self dateComponents] second]; }
161 |
162 | -(UIDatePickerMode)datePickerMode
163 | {
164 | NSNumber *datePickerMode = objc_getAssociatedObject(self, @selector(datePickerMode));
165 | return [datePickerMode integerValue];
166 | }
167 |
168 | - (void)setDatePickerMode:(UIDatePickerMode)datePickerMode
169 | {
170 | if (self.dropDownMode == IQDropDownModeDatePicker)
171 | {
172 | objc_setAssociatedObject(self, @selector(datePickerMode), @(datePickerMode), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
173 |
174 | [self.datePicker setDatePickerMode:datePickerMode];
175 |
176 | switch (datePickerMode) {
177 | case UIDatePickerModeCountDownTimer:
178 | [self.dateFormatter setDateStyle:NSDateFormatterNoStyle];
179 | [self.dateFormatter setTimeStyle:NSDateFormatterNoStyle];
180 | break;
181 | case UIDatePickerModeDate:
182 | [self.dateFormatter setDateStyle:NSDateFormatterShortStyle];
183 | [self.dateFormatter setTimeStyle:NSDateFormatterNoStyle];
184 | break;
185 | case UIDatePickerModeTime:
186 | [self.timeFormatter setDateStyle:NSDateFormatterNoStyle];
187 | [self.timeFormatter setTimeStyle:NSDateFormatterShortStyle];
188 | break;
189 | case UIDatePickerModeDateAndTime:
190 | [self.dateTimeFormatter setDateStyle:NSDateFormatterShortStyle];
191 | [self.dateTimeFormatter setTimeStyle:NSDateFormatterShortStyle];
192 | break;
193 | }
194 | }
195 | }
196 |
197 | -(nullable NSDateFormatter *)dateFormatter
198 | {
199 | NSDateFormatter *dateFormatter = objc_getAssociatedObject(self, @selector(dateFormatter));
200 |
201 | if (!dateFormatter) {
202 |
203 | if ([[IQDropDownTextField appearance] dateFormatter])
204 | {
205 | dateFormatter = [[IQDropDownTextField appearance] dateFormatter];
206 | }
207 | else
208 | {
209 | static NSDateFormatter *defaultDateFormatter = nil;
210 |
211 | static dispatch_once_t onceToken;
212 | dispatch_once(&onceToken, ^{
213 | defaultDateFormatter = [[NSDateFormatter alloc] init];
214 | [defaultDateFormatter setDateStyle:NSDateFormatterMediumStyle];
215 | [defaultDateFormatter setTimeStyle:NSDateFormatterNoStyle];
216 | });
217 |
218 | dateFormatter = defaultDateFormatter;
219 | }
220 | }
221 |
222 | return dateFormatter;
223 | }
224 |
225 | -(void)setDateFormatter:(nullable NSDateFormatter *)dateFormatter
226 | {
227 | objc_setAssociatedObject(self, @selector(dateFormatter), dateFormatter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
228 | [self.datePicker setLocale:dateFormatter.locale];
229 | }
230 |
231 | -(nullable NSDateFormatter *)timeFormatter
232 | {
233 | NSDateFormatter *timeFormatter = objc_getAssociatedObject(self, @selector(timeFormatter));
234 |
235 | if (!timeFormatter)
236 | {
237 | if ([[IQDropDownTextField appearance] timeFormatter])
238 | {
239 | timeFormatter = [[IQDropDownTextField appearance] timeFormatter];
240 | }
241 | else
242 | {
243 | static NSDateFormatter *defaultTimeFormatter = nil;
244 |
245 | static dispatch_once_t onceToken;
246 | dispatch_once(&onceToken, ^{
247 | defaultTimeFormatter = [[NSDateFormatter alloc] init];
248 | [defaultTimeFormatter setDateStyle:NSDateFormatterNoStyle];
249 | [defaultTimeFormatter setTimeStyle:NSDateFormatterShortStyle];
250 | });
251 |
252 | timeFormatter = defaultTimeFormatter;
253 | }
254 | }
255 |
256 | return timeFormatter;
257 | }
258 |
259 | -(void)setTimeFormatter:(nullable NSDateFormatter *)timeFormatter
260 | {
261 | objc_setAssociatedObject(self, @selector(timeFormatter), timeFormatter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
262 | [self.timePicker setLocale:timeFormatter.locale];
263 | }
264 |
265 | -(nullable NSDateFormatter *)dateTimeFormatter
266 | {
267 | NSDateFormatter *dateTimeFormatter = objc_getAssociatedObject(self, @selector(dateTimeFormatter));
268 |
269 | if (!dateTimeFormatter) {
270 | if ([[IQDropDownTextField appearance] dateTimeFormatter])
271 | {
272 | dateTimeFormatter = [[IQDropDownTextField appearance] dateTimeFormatter];
273 | }
274 | else
275 | {
276 | static NSDateFormatter *defaultDateTimeFormatter = nil;
277 |
278 | static dispatch_once_t onceToken;
279 | dispatch_once(&onceToken, ^{
280 | defaultDateTimeFormatter = [[NSDateFormatter alloc] init];
281 | [defaultDateTimeFormatter setDateStyle:NSDateFormatterMediumStyle];
282 | [defaultDateTimeFormatter setTimeStyle:NSDateFormatterShortStyle];
283 | });
284 |
285 | dateTimeFormatter = defaultDateTimeFormatter;
286 | }
287 | }
288 |
289 | return dateTimeFormatter;
290 | }
291 |
292 | -(void)setDateTimeFormatter:(nullable NSDateFormatter *)dateTimeFormatter
293 | {
294 | objc_setAssociatedObject(self, @selector(dateTimeFormatter), dateTimeFormatter, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
295 | [self.dateTimePicker setLocale:dateTimeFormatter.locale];
296 | }
297 |
298 | @end
299 |
--------------------------------------------------------------------------------
/IQDropDownTextField/IQDropDownTextField+Internal.h:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextField+IQDropDownTextField_Internal.h
3 | // IQDropDownTextField
4 | //
5 | // Created by Iftekhar on 12/19/23.
6 | //
7 |
8 | #import "IQDropDownTextField.h"
9 |
10 | NS_ASSUME_NONNULL_BEGIN
11 |
12 | @interface IQDropDownTextField (Internal)
13 |
14 | -(void)_setSelectedItem:(nullable NSString *)selectedItem animated:(BOOL)animated shouldNotifyDelegate:(BOOL)shouldNotifyDelegate;
15 |
16 | @end
17 |
18 | NS_ASSUME_NONNULL_END
19 |
--------------------------------------------------------------------------------
/IQDropDownTextField/IQDropDownTextField+Internal.m:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextField+Internal.m
3 | // IQDropDownTextField
4 | //
5 | // Created by Iftekhar on 12/19/23.
6 | //
7 |
8 | #import "IQDropDownTextField+Internal.h"
9 | #import "IQDropDownTextField+DateTime.h"
10 |
11 | @implementation IQDropDownTextField (Internal)
12 |
13 | -(void)_setSelectedItem:(NSString *)selectedItem animated:(BOOL)animated shouldNotifyDelegate:(BOOL)shouldNotifyDelegate
14 | {
15 | switch (self.dropDownMode)
16 | {
17 | case IQDropDownModeTextPicker:
18 | {
19 | if ([self.itemList containsObject:selectedItem])
20 | {
21 | NSInteger index = [self.itemList indexOfObject:selectedItem];
22 | [self setSelectedRow:index animated:animated];
23 |
24 | if (shouldNotifyDelegate && [self.delegate respondsToSelector:@selector(textField:didSelectItem:row:)])
25 | [self.delegate textField:self didSelectItem:selectedItem row:index];
26 | }
27 | else
28 | {
29 | NSInteger selectedIndex = self.isOptionalDropDown ? IQOptionalTextFieldIndex : 0;
30 |
31 | [self setSelectedRow:selectedIndex animated:animated];
32 |
33 | if (shouldNotifyDelegate && [self.delegate respondsToSelector:@selector(textField:didSelectItem:row:)])
34 | [self.delegate textField:self didSelectItem:nil row:selectedIndex];
35 | }
36 | }
37 | break;
38 | case IQDropDownModeDatePicker:
39 | {
40 | NSDate *date = [self.dateFormatter dateFromString:selectedItem];
41 | if (date)
42 | {
43 | super.text = selectedItem;
44 | [self.datePicker setDate:date animated:animated];
45 |
46 | if (shouldNotifyDelegate && [self.delegate respondsToSelector:@selector(textField:didSelectDate:)])
47 | [self.delegate textField:self didSelectDate:date];
48 | }
49 | else if (self.isOptionalDropDown && [selectedItem length] == 0)
50 | {
51 | super.text = @"";
52 | [self.datePicker setDate:[NSDate date] animated:animated];
53 |
54 | if (shouldNotifyDelegate && [self.delegate respondsToSelector:@selector(textField:didSelectDate:)])
55 | [self.delegate textField:self didSelectDate:nil];
56 | }
57 | break;
58 | }
59 | case IQDropDownModeTimePicker:
60 | {
61 | NSDate *time = [self.timeFormatter dateFromString:selectedItem];
62 |
63 | if (time)
64 | {
65 | NSDate *day = [NSDate dateWithTimeIntervalSinceReferenceDate: 0];
66 | NSDateComponents *componentsDay = [[NSCalendar currentCalendar] components: NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate: day];
67 | NSDateComponents *componentsTime = [[NSCalendar currentCalendar] components: NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate: time];
68 | componentsDay.hour = componentsTime.hour;
69 | componentsDay.minute = componentsTime.minute;
70 | componentsDay.second = componentsTime.second;
71 |
72 | NSDate *date = [[NSCalendar currentCalendar] dateFromComponents: componentsDay];
73 |
74 | super.text = selectedItem;
75 | [self.timePicker setDate:date animated:animated];
76 |
77 | if (shouldNotifyDelegate && [self.delegate respondsToSelector:@selector(textField:didSelectDate:)])
78 | [self.delegate textField:self didSelectDate:date];
79 | }
80 | else if (self.isOptionalDropDown && [selectedItem length] == 0)
81 | {
82 | super.text = @"";
83 | [self.timePicker setDate:[NSDate date] animated:animated];
84 |
85 | if (shouldNotifyDelegate && [self.delegate respondsToSelector:@selector(textField:didSelectDate:)])
86 | [self.delegate textField:self didSelectDate:nil];
87 | }
88 | break;
89 | }
90 | case IQDropDownModeDateTimePicker:
91 | {
92 | NSDate *date = [self.dateTimeFormatter dateFromString:selectedItem];
93 | if (date)
94 | {
95 | super.text = selectedItem;
96 | [self.dateTimePicker setDate:date animated:animated];
97 |
98 | if (shouldNotifyDelegate && [self.delegate respondsToSelector:@selector(textField:didSelectDate:)])
99 | [self.delegate textField:self didSelectDate:date];
100 | }
101 | else if (self.isOptionalDropDown && [selectedItem length] == 0)
102 | {
103 | super.text = @"";
104 | [self.dateTimePicker setDate:[NSDate date] animated:animated];
105 |
106 | if (shouldNotifyDelegate && [self.delegate respondsToSelector:@selector(textField:didSelectDate:)])
107 | [self.delegate textField:self didSelectDate:nil];
108 | }
109 | break;
110 | }
111 | case IQDropDownModeTextField:{
112 | super.text = selectedItem;
113 | }
114 | break;
115 | }
116 | }
117 |
118 | @end
119 |
--------------------------------------------------------------------------------
/IQDropDownTextField/IQDropDownTextField.h:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextField.h
3 | // https://github.com/hackiftekhar/IQDropDownTextField
4 | // Copyright (c) 2013-15 Iftekhar Qurashi.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 |
25 | #import
26 | #import "IQDropDownTextFieldConstants.h"
27 |
28 | NS_ASSUME_NONNULL_BEGIN
29 |
30 | /**
31 | Integer constant to use with `selectedRow` property, this will select `Select` option in optional textField.
32 | */
33 | extern NSInteger const IQOptionalTextFieldIndex;
34 |
35 |
36 | @class IQDropDownTextField;
37 |
38 | /**
39 | Drop down text field delegate.
40 | */
41 | @protocol IQDropDownTextFieldDelegate
42 |
43 | @optional
44 | -(void)textField:(nonnull IQDropDownTextField*)textField didSelectItem:(nullable NSString*)item row:(NSInteger)row; //Called when textField changes it's selected item. Supported for IQDropDownModeTextPicker
45 |
46 | -(void)textField:(nonnull IQDropDownTextField*)textField didSelectDate:(nullable NSDate*)date; //Called when textField changes it's selected item. Supported for IQDropDownModeTimePicker, IQDropDownModeDatePicker, IQDropDownModeDateTimePicker
47 |
48 | @end
49 |
50 |
51 | /**
52 | Drop down text field data source. This is only valid for IQDropDownModeTextField mode
53 | */
54 | @protocol IQDropDownTextFieldDataSource
55 |
56 | @optional
57 | -(BOOL)textField:(nonnull IQDropDownTextField*)textField canSelectItem:(nonnull NSString*)item row:(NSInteger)row; //Check if an item can be selected by dropdown texField.
58 | -(IQProposedSelection)textField:(nonnull IQDropDownTextField*)textField proposedSelectionModeForItem:(nonnull NSString*)item row:(NSInteger)row; //If canSelectItem return NO, then textField:proposedSelectionModeForItem: asked for proposed selection mode.
59 |
60 | @end
61 |
62 |
63 | /**
64 | Add a UIPickerView as inputView
65 | */
66 | @interface IQDropDownTextField : UITextField
67 |
68 | /**
69 | This is picker object which internally used for showing list. Changing some properties might not work properly so do it at your own risk.
70 | */
71 | @property (nonnull, nonatomic, readonly) UIPickerView *pickerView;
72 |
73 | @property(nullable, nonatomic,weak) IBOutlet id delegate; // default is nil. weak reference
74 | @property(nullable, nonatomic,weak) IBOutlet id dataSource; // default is nil. weak reference
75 |
76 | /**
77 | If YES then a toolbar will be added at the top to dismiss textfield, if NO then toolbar will be removed. Default to NO.
78 | */
79 | @property (nonatomic, assign) BOOL showDismissToolbar;
80 |
81 | /**
82 | DropDownMode style to show in picker. Default is IQDropDownModeTextPicker.
83 | */
84 | @property (nonatomic, assign) IQDropDownMode dropDownMode;
85 |
86 | /**
87 | Label for the optional item if isOptionalDropDown is YES. Default is Select.
88 | */
89 | @property (nullable, nonatomic, copy) IBInspectable NSString *optionalItemText;
90 |
91 | /**
92 | If YES then it will add a optionalItemLabel item at top of dropDown list. If NO then first field will automatically be selected. Default is YES
93 | */
94 | @property (nonatomic, assign) IBInspectable BOOL isOptionalDropDown;
95 |
96 | /**
97 | Use selectedItem property to get/set dropdown text.
98 | */
99 | @property(nullable, nonatomic,copy) NSString *text NS_DEPRECATED_IOS(3_0, 5_0, "Please use selectedItem property to get/set dropdown selected text instead");
100 |
101 | /**
102 | attributedText is unavailable in IQDropDownTextField.
103 | */
104 | @property(nullable, nonatomic,copy) NSAttributedString *attributedText NS_UNAVAILABLE;
105 |
106 | /**
107 | Sets a custom font for the IQDropdownTextField items. Default is boldSystemFontOfSize:18.0.
108 | */
109 | @property (nullable, strong, nonatomic) UIFont *dropDownFont;
110 |
111 | /**
112 | Sets a custom color for the IQDropdownTextField items. Default is blackColor.
113 | */
114 | @property (nullable, strong, nonatomic) UIColor *dropDownTextColor;
115 |
116 | /**
117 | Sets a custom color for the optional item. Default is lightGrayColor.
118 | */
119 | @property (nullable, strong, nonatomic) UIColor *optionalItemTextColor;
120 |
121 |
122 | ///----------------------
123 | /// @name Title Selection
124 | ///----------------------
125 |
126 | /**
127 | Selected item of pickerView.
128 | */
129 | @property (nullable, nonatomic, copy) NSString *selectedItem;
130 |
131 | /**
132 | Set selected item of pickerView.
133 | */
134 | - (void)setSelectedItem:(nullable NSString*)selectedItem animated:(BOOL)animated;
135 |
136 |
137 | ///-------------------------------
138 | /// @name IQDropDownModeTextPicker
139 | ///-------------------------------
140 |
141 | /**
142 | Items to show in pickerView. For example. @[ @"1", @"2", @"3" ]. This field must be set.
143 | */
144 | @property (nullable, nonatomic, copy) NSArray *itemList;
145 |
146 | /**
147 | UIView items to show in pickerView. For example. @[ view1, view2, view3 ]. If itemList text needs to be shown then pass [NSNull null] instead of UIView object at appropriate index. This field is optional.
148 | */
149 | @property (nullable, nonatomic, copy) NSArray <__kindof NSObject*> *itemListView;
150 |
151 | /**
152 | If this is set then we'll show textfield's text from this list instead from regular itemList. This is only for showing different messaging in textfield's text. This itemListUI array count must be equal to itemList array count.
153 | */
154 | @property (nullable, nonatomic, copy) NSArray *itemListUI;
155 |
156 | /**
157 | Selected row index of selected item.
158 | */
159 | @property (nonatomic, assign) NSInteger selectedRow;
160 |
161 | /**
162 | Defines Picker labels fontSizeAdjustment by width. Default is NO
163 | */
164 | @property (nonatomic, assign) IBInspectable BOOL adjustPickerLabelFontSizeWidth;
165 |
166 | /**
167 | Select row index of selected item.
168 | */
169 | - (void)setSelectedRow:(NSInteger)row animated:(BOOL)animated;
170 |
171 | @end
172 |
173 | NS_ASSUME_NONNULL_END
174 |
175 | // This import is forcefully written as bottom to automatically include the DateTime category when importing IQDropDownTextField
176 | #import "IQDropDownTextField+DateTime.h"
177 |
--------------------------------------------------------------------------------
/IQDropDownTextField/IQDropDownTextField.m:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextField.m
3 | // https://github.com/hackiftekhar/IQDropDownTextField
4 | // Copyright (c) 2013-15 Iftekhar Qurashi.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 |
25 | #import "IQDropDownTextField.h"
26 | #import "IQDropDownTextField+Internal.h"
27 |
28 | NSInteger const IQOptionalTextFieldIndex = -1;
29 |
30 | @interface IQDropDownTextField ()
31 |
32 | @property (nonnull, nonatomic, strong) UIToolbar *dismissToolbar;
33 |
34 | @property BOOL hasSetInitialIsOptional;
35 | @property NSInteger pickerSelectedRow;
36 |
37 | @end
38 |
39 | @implementation IQDropDownTextField
40 |
41 | @synthesize dropDownMode = _dropDownMode;
42 | @synthesize itemList = _itemList;
43 | @synthesize isOptionalDropDown = _isOptionalDropDown;
44 | @synthesize optionalItemText = _optionalItemText;
45 | @synthesize adjustPickerLabelFontSizeWidth = _adjustPickerLabelFontSizeWidth;
46 |
47 | @synthesize dropDownFont = _dropDownFont;
48 | @synthesize dropDownTextColor = _dropDownTextColor;
49 | @synthesize optionalItemTextColor = _optionalItemTextColor;
50 |
51 | @dynamic delegate;
52 | @dynamic text;
53 | @dynamic attributedText;
54 |
55 | @synthesize pickerView = _pickerView;
56 |
57 | #pragma mark - NSObject
58 |
59 | - (void)dealloc {
60 | [_pickerView setDelegate:nil];
61 | [_pickerView setDataSource:nil];
62 | _pickerView = nil;
63 | self.delegate = nil;
64 | _dataSource = nil;
65 | _optionalItemText = nil;
66 | _itemList = nil;
67 | _dropDownFont = nil;
68 | _dropDownTextColor = nil;
69 | _optionalItemTextColor = nil;
70 | }
71 |
72 | #pragma mark - Initialization
73 |
74 | - (void)initialize
75 | {
76 | [self setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];
77 | [self setContentHorizontalAlignment:UIControlContentHorizontalAlignmentCenter];
78 |
79 | if (self.optionalItemText == nil)
80 | {
81 | self.optionalItemText = NSLocalizedString(@"Select", nil);
82 | }
83 |
84 | //These will update the UI and other components, all the validation added if awakeFromNib for textField is called after custom UIView awakeFromNib call
85 | {
86 | self.dropDownMode = self.dropDownMode;
87 |
88 | self.isOptionalDropDown = self.hasSetInitialIsOptional?self.isOptionalDropDown:YES;
89 | self.adjustPickerLabelFontSizeWidth = self.adjustPickerLabelFontSizeWidth;
90 | }
91 | _pickerSelectedRow = -1;
92 | }
93 |
94 | - (instancetype)initWithFrame:(CGRect)frame
95 | {
96 | self = [super initWithFrame:frame];
97 | if (self)
98 | {
99 | [self initialize];
100 | }
101 | return self;
102 | }
103 |
104 | -(void)awakeFromNib
105 | {
106 | [super awakeFromNib];
107 | [self initialize];
108 | }
109 |
110 | - (BOOL)becomeFirstResponder
111 | {
112 | BOOL result = [super becomeFirstResponder];
113 | if (_pickerSelectedRow >= 0) {
114 | [_pickerView selectRow:_pickerSelectedRow inComponent:0 animated:NO];
115 | }
116 |
117 | return result;
118 | }
119 |
120 | #pragma mark - UITextField overrides
121 |
122 | - (CGRect)caretRectForPosition:(nonnull UITextPosition *)position
123 | {
124 | if (self.dropDownMode == IQDropDownModeTextField) {
125 | return [super caretRectForPosition:position];
126 | } else {
127 | return CGRectZero;
128 | }
129 | }
130 |
131 | #pragma mark - UIPickerView data source
132 |
133 | - (NSInteger)numberOfComponentsInPickerView:(nonnull UIPickerView *)pickerView
134 | {
135 | return 1;
136 | }
137 |
138 | - (NSInteger)pickerView:(nonnull UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
139 | {
140 | return self.itemList.count + (self.isOptionalDropDown ? 1 : 0);
141 | }
142 |
143 | #pragma mark UIPickerView delegate
144 |
145 | - (nonnull UIView *)pickerView:(nonnull UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view
146 | {
147 | row = row - (self.isOptionalDropDown ? 1 : 0);
148 |
149 | if (row == IQOptionalTextFieldIndex) {
150 | UILabel *labelText = (UILabel*)view;
151 |
152 | if (labelText == nil)
153 | {
154 | labelText = [[UILabel alloc] init];
155 | [labelText setTextAlignment:NSTextAlignmentCenter];
156 | [labelText setAdjustsFontSizeToFitWidth:YES];
157 | labelText.backgroundColor = [UIColor clearColor];
158 | labelText.backgroundColor = [UIColor clearColor];
159 | }
160 |
161 | if (_dropDownFont) {
162 | if (_dropDownFont.pointSize < 30) {
163 | labelText.font = [_dropDownFont fontWithSize:30];
164 | } else {
165 | labelText.font = _dropDownFont;
166 | }
167 | } else {
168 | labelText.font = [UIFont boldSystemFontOfSize:30.0];
169 | }
170 |
171 | labelText.textColor = _optionalItemTextColor ? _optionalItemTextColor : [UIColor lightGrayColor];
172 | labelText.text = self.optionalItemText;
173 |
174 | return labelText;
175 |
176 | } else {
177 | if (_itemListView.count > row && [_itemListView[row] isKindOfClass:[UIView class]])
178 | {
179 | //Archiving and Unarchiving is necessary to copy UIView instance.
180 | NSData *viewData = [NSKeyedArchiver archivedDataWithRootObject:_itemListView[row]];
181 | UIView *copyOfView = [NSKeyedUnarchiver unarchiveObjectWithData:viewData];
182 | return copyOfView;
183 | }
184 | else
185 | {
186 | UILabel *labelText = (UILabel*)view;
187 | if (labelText == nil)
188 | {
189 | labelText = [[UILabel alloc] init];
190 | [labelText setTextAlignment:NSTextAlignmentCenter];
191 | [labelText setAdjustsFontSizeToFitWidth:YES];
192 | labelText.backgroundColor = [UIColor clearColor];
193 | labelText.backgroundColor = [UIColor clearColor];
194 | }
195 |
196 | if (_dropDownFont) {
197 | labelText.font = _dropDownFont;
198 | } else {
199 | labelText.font = [UIFont boldSystemFontOfSize:18.0];
200 | }
201 |
202 | NSString *text = [self.itemList objectAtIndex:row];
203 |
204 | [labelText setText:text];
205 |
206 | {
207 | BOOL canSelect = YES;
208 |
209 | if ([self.dataSource respondsToSelector:@selector(textField:canSelectItem:row:)])
210 | {
211 | canSelect = [self.dataSource textField:self canSelectItem:text row:row];
212 | }
213 |
214 | if (canSelect)
215 | {
216 | if (_dropDownTextColor) {
217 | labelText.textColor = _dropDownTextColor;
218 | } else {
219 | labelText.textColor = [UIColor blackColor];
220 | }
221 | }
222 | else
223 | {
224 | labelText.textColor = [UIColor lightGrayColor];
225 | }
226 |
227 | labelText.adjustsFontSizeToFitWidth = self.adjustPickerLabelFontSizeWidth;
228 | }
229 | return labelText;
230 | }
231 | }
232 | }
233 |
234 | - (void)pickerView:(nonnull UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
235 | {
236 | _pickerSelectedRow = row;
237 |
238 | row = row - (self.isOptionalDropDown ? 1 : 0);
239 |
240 | if (row == IQOptionalTextFieldIndex) {
241 | [self _setSelectedItem:nil animated:NO shouldNotifyDelegate:YES];
242 | } else if (row >= 0) {
243 |
244 | NSString *text = [self.itemList objectAtIndex:row];
245 |
246 | BOOL canSelect = YES;
247 |
248 | if ([self.dataSource respondsToSelector:@selector(textField:canSelectItem:row:)])
249 | {
250 | canSelect = [self.dataSource textField:self canSelectItem:text row:row];
251 | }
252 |
253 | if (canSelect)
254 | {
255 | [self _setSelectedItem:text animated:NO shouldNotifyDelegate:YES];
256 | }
257 | else
258 | {
259 | IQProposedSelection proposedSelection = IQProposedSelectionBoth;
260 |
261 | if ([self.dataSource respondsToSelector:@selector(textField:proposedSelectionModeForItem:row:)])
262 | {
263 | proposedSelection = [self.dataSource textField:self proposedSelectionModeForItem:text row:row];
264 | }
265 |
266 | NSInteger aboveIndex = row-1;
267 | NSInteger belowIndex = row+1;
268 |
269 | if (proposedSelection == IQProposedSelectionAbove)
270 | {
271 | belowIndex = self.itemList.count;
272 | }
273 | else if (proposedSelection == IQProposedSelectionBelow)
274 | {
275 | aboveIndex = -1;
276 | }
277 |
278 | while (aboveIndex >= 0 || belowIndex < self.itemList.count)
279 | {
280 | if (aboveIndex >= 0)
281 | {
282 | NSString *aboveText = [self.itemList objectAtIndex:aboveIndex];
283 |
284 | if ([self.dataSource textField:self canSelectItem:aboveText row:aboveIndex])
285 | {
286 | [self _setSelectedItem:aboveText animated:YES shouldNotifyDelegate:YES];
287 | return;
288 | }
289 |
290 | aboveIndex--;
291 | }
292 |
293 | if (belowIndex < self.itemList.count)
294 | {
295 | NSString *belowText = [self.itemList objectAtIndex:aboveIndex];
296 |
297 | if ([self.dataSource textField:self canSelectItem:belowText row:belowIndex])
298 | {
299 | [self _setSelectedItem:belowText animated:YES shouldNotifyDelegate:YES];
300 | return;
301 | }
302 |
303 | belowIndex++;
304 | }
305 | }
306 |
307 | return [self setSelectedRow:0 animated:YES];
308 | }
309 | }
310 | }
311 |
312 | #pragma mark - Selected Row
313 |
314 | - (NSInteger)selectedRow
315 | {
316 | NSInteger pickerViewSelectedRow = _pickerSelectedRow; //It may return -1
317 | pickerViewSelectedRow = MAX(pickerViewSelectedRow, 0);
318 |
319 | return pickerViewSelectedRow - (self.isOptionalDropDown ? 1 : 0);
320 | }
321 |
322 | -(void)setSelectedRow:(NSInteger)selectedRow
323 | {
324 | [self setSelectedRow:selectedRow animated:NO];
325 | }
326 |
327 | - (void)setSelectedRow:(NSInteger)row animated:(BOOL)animated
328 | {
329 | if (row == IQOptionalTextFieldIndex) {
330 | if (self.isOptionalDropDown) {
331 | super.text = @"";
332 | } else if (_itemList.count > 0) {
333 | super.text = [_itemListUI?:_itemList objectAtIndex:0];
334 | } else {
335 | super.text = @"";
336 | }
337 | } else {
338 | super.text = [_itemListUI?:_itemList objectAtIndex:row];
339 | }
340 |
341 | NSInteger pickerViewRow = row + (self.isOptionalDropDown ? 1 : 0);
342 | _pickerSelectedRow = pickerViewRow;
343 | [self.pickerView selectRow:pickerViewRow inComponent:0 animated:animated];
344 | }
345 |
346 | #pragma mark - Toolbar
347 |
348 | -(void)setShowDismissToolbar:(BOOL)showDismissToolbar
349 | {
350 | self.inputAccessoryView = (showDismissToolbar ? self.dismissToolbar : nil);
351 | }
352 |
353 | -(BOOL)showDismissToolbar
354 | {
355 | return (self.inputAccessoryView == _dismissToolbar);
356 | }
357 |
358 | -(nonnull UIToolbar *)dismissToolbar
359 | {
360 | if (_dismissToolbar == nil)
361 | {
362 | _dismissToolbar = [[UIToolbar alloc] init];
363 | _dismissToolbar.translucent = YES;
364 | [_dismissToolbar sizeToFit];
365 | UIBarButtonItem *buttonFlexible = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
366 | UIBarButtonItem *buttonDone = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(resignFirstResponder)];
367 | [_dismissToolbar setItems:@[buttonFlexible, buttonDone]];
368 | }
369 |
370 | return _dismissToolbar;
371 | }
372 |
373 | #pragma mark - Setters
374 | - (void)setDropDownMode:(IQDropDownMode)dropDownMode
375 | {
376 | _dropDownMode = dropDownMode;
377 |
378 | switch (_dropDownMode)
379 | {
380 | case IQDropDownModeTextPicker:
381 | {
382 | self.inputView = self.pickerView;
383 | [self setSelectedRow:self.selectedRow animated:YES];
384 | }
385 | break;
386 | case IQDropDownModeDatePicker:
387 | {
388 | self.inputView = self.datePicker;
389 |
390 | if (self.isOptionalDropDown == NO)
391 | {
392 | [self setDate:self.datePicker.date];
393 | }
394 | }
395 | break;
396 | case IQDropDownModeTimePicker:
397 | {
398 | self.inputView = self.timePicker;
399 |
400 | if (self.isOptionalDropDown == NO)
401 | {
402 | [self setDate:self.timePicker.date];
403 | }
404 | }
405 | break;
406 | case IQDropDownModeDateTimePicker:
407 | {
408 | self.inputView = self.dateTimePicker;
409 |
410 | if (self.isOptionalDropDown == NO)
411 | {
412 | [self setDate:self.dateTimePicker.date];
413 | }
414 | }
415 | break;
416 | case IQDropDownModeTextField:
417 | {
418 | self.inputView = nil;
419 | }
420 | break;
421 | default:
422 | break;
423 | }
424 | }
425 |
426 | - (void)setItemList:(nullable NSArray *)itemList
427 | {
428 | _itemList = itemList;
429 |
430 | //Refreshing pickerView
431 | [self setIsOptionalDropDown:_isOptionalDropDown];
432 |
433 | [self setSelectedRow:self.selectedRow];
434 | }
435 |
436 | - (nullable NSString*)selectedItem
437 | {
438 | switch (_dropDownMode)
439 | {
440 | case IQDropDownModeTextPicker:
441 | {
442 | NSInteger selectedRow = self.selectedRow;
443 |
444 | if (selectedRow >= 0)
445 | {
446 | return [_itemList objectAtIndex:selectedRow];
447 | }
448 | else
449 | {
450 | return nil;
451 | }
452 | }
453 | break;
454 | case IQDropDownModeDatePicker:
455 | {
456 | return [super.text length] ? [self.dateFormatter stringFromDate:self.datePicker.date] : nil; break;
457 | }
458 | break;
459 | case IQDropDownModeTimePicker:
460 | {
461 | return [super.text length] ? [self.timeFormatter stringFromDate:self.timePicker.date] : nil; break;
462 | }
463 | break;
464 | case IQDropDownModeDateTimePicker:
465 | {
466 | return [super.text length] ? [self.dateTimeFormatter stringFromDate:self.dateTimePicker.date] : nil; break;
467 | }
468 | break;
469 | case IQDropDownModeTextField:
470 | {
471 | return super.text;
472 | }
473 | break;
474 | }
475 | }
476 |
477 | -(void)setSelectedItem:(nullable NSString *)selectedItem
478 | {
479 | [self _setSelectedItem:selectedItem animated:NO shouldNotifyDelegate:NO];
480 | }
481 |
482 | -(void)setSelectedItem:(nullable NSString *)selectedItem animated:(BOOL)animated
483 | {
484 | [self _setSelectedItem:selectedItem animated:animated shouldNotifyDelegate:NO];
485 | }
486 |
487 | -(nullable NSString *)optionalItemText
488 | {
489 | if (_optionalItemText.length)
490 | {
491 | return _optionalItemText;
492 | }
493 | else
494 | {
495 | return NSLocalizedString(@"Select", nil);
496 | }
497 | }
498 |
499 | -(void)setOptionalItemText:(nullable NSString *)optionalItemText
500 | {
501 | _optionalItemText = [optionalItemText copy];
502 |
503 | [self _updateOptionsList];
504 | }
505 |
506 | -(BOOL)adjustPickerLabelFontSizeWidth {
507 | return _adjustPickerLabelFontSizeWidth;
508 | }
509 | -(void)setAdjustPickerLabelFontSizeWidth:(BOOL)adjustPickerLabelFontSizeWidth {
510 | _adjustPickerLabelFontSizeWidth = adjustPickerLabelFontSizeWidth;
511 |
512 | [self _updateOptionsList];
513 | }
514 |
515 | -(void)setIsOptionalDropDown:(BOOL)isOptionalDropDown
516 | {
517 | if (_hasSetInitialIsOptional == NO || _isOptionalDropDown != isOptionalDropDown)
518 | {
519 | NSInteger previousSelectedRow = self.selectedRow;
520 |
521 | _isOptionalDropDown = isOptionalDropDown;
522 | _hasSetInitialIsOptional = YES;
523 |
524 | if (_hasSetInitialIsOptional == YES && self.dropDownMode == IQDropDownModeTextPicker)
525 | {
526 | [self.pickerView reloadAllComponents];
527 |
528 | [self setSelectedRow:previousSelectedRow];
529 | }
530 | }
531 | }
532 |
533 | - (void) _updateOptionsList {
534 |
535 | switch (_dropDownMode)
536 | {
537 | case IQDropDownModeDatePicker:
538 | {
539 | if (self.isOptionalDropDown == NO)
540 | {
541 | [self setDate:self.datePicker.date];
542 | }
543 | }
544 | break;
545 | case IQDropDownModeTimePicker:
546 | {
547 | if (self.isOptionalDropDown == NO)
548 | {
549 | [self setDate:self.timePicker.date];
550 | }
551 | }
552 | break;
553 | case IQDropDownModeDateTimePicker:
554 | {
555 | if (self.isOptionalDropDown == NO)
556 | {
557 | [self setDate:self.dateTimePicker.date];
558 | }
559 | }
560 | case IQDropDownModeTextPicker:
561 | {
562 | [self.pickerView reloadAllComponents];
563 | }
564 | break;
565 | default:
566 | break;
567 | }
568 | }
569 |
570 | - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
571 | {
572 | BOOL isRestrictedAction = (action == @selector(paste:) || action == @selector(cut:));
573 | if (isRestrictedAction && self.dropDownMode != IQDropDownModeTextField) {
574 | return NO;
575 | }
576 |
577 | return [super canPerformAction:action withSender:sender];
578 | }
579 |
580 | #pragma mark - Getter
581 |
582 | - (nonnull UIPickerView *) pickerView {
583 | if (!_pickerView)
584 | {
585 | _pickerView = [[UIPickerView alloc] init];
586 | [_pickerView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight)];
587 | [_pickerView setShowsSelectionIndicator:YES];
588 | [_pickerView setDelegate:self];
589 | [_pickerView setDataSource:self];
590 | }
591 | return _pickerView;
592 | }
593 |
594 | @end
595 |
--------------------------------------------------------------------------------
/IQDropDownTextField/IQDropDownTextFieldConstants.h:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextFieldConstants.h
3 | // IQDropDownTextField
4 | //
5 | // Created by Iftekhar on 12/19/23.
6 | //
7 |
8 | #ifndef IQDropDownTextFieldConstants_h
9 | #define IQDropDownTextFieldConstants_h
10 |
11 | #import
12 |
13 | /**
14 | Drop Down Mode settings.
15 |
16 | `IQDropDownModeTextPicker`
17 | Show pickerView with provided text data.
18 |
19 | `IQDropDownModeTimePicker`
20 | Show UIDatePicker to pick time.
21 |
22 | `IQDropDownModeDatePicker`
23 | Show UIDatePicker to pick date.
24 | */
25 | typedef NS_ENUM(NSInteger, IQDropDownMode) {
26 | IQDropDownModeTextPicker,
27 | IQDropDownModeTimePicker,
28 | IQDropDownModeDatePicker,
29 | IQDropDownModeDateTimePicker,
30 | IQDropDownModeTextField
31 | };
32 |
33 | /**
34 | `IQProposedSelectionAbove`
35 | pickerView find the nearest items above the deselected item that can be selected and then selecting that row.
36 |
37 | `IQProposedSelectionBelow`
38 | pickerView find the nearest items below the deselected item that can be selected and then selecting that row.
39 |
40 | `IQProposedSelectionBoth`
41 | pickerView find the nearest items that can be selected above or below the deselected item and then selecting that row.
42 | */
43 | typedef NS_ENUM(NSInteger, IQProposedSelection) {
44 | IQProposedSelectionBoth,
45 | IQProposedSelectionAbove,
46 | IQProposedSelectionBelow
47 | };
48 |
49 |
50 | #endif /* IQDropDownTextFieldConstants_h */
51 |
--------------------------------------------------------------------------------
/IQDropDownTextField/PrivacyInfo.xcprivacy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPrivacyCollectedDataTypes
6 |
7 | NSPrivacyTracking
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/IQDropDownTextFieldSwift.podspec.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "IQDropDownTextFieldSwift",
3 | "version": "4.0.5",
4 | "source": {
5 | "git": "https://github.com/hackiftekhar/IQDropDownTextField.git",
6 | "tag": "4.0.5"
7 | },
8 | "summary": "TextField with DropDown support using UIPickerView",
9 | "homepage": "https://github.com/hackiftekhar/IQDropDownTextField",
10 | "license": "MIT",
11 | "authors": {
12 | "Iftekhar Qurashi": "hack.iftekhar@gmail.com"
13 | },
14 | "platforms": {
15 | "ios": "11.0"
16 | },
17 | "source_files": [
18 | "IQDropDownTextFieldSwift/*.{swift}"
19 | ],
20 | "resource_bundles": {"IQDropDownTextFieldSwift": "IQDropDownTextFieldSwift/PrivacyInfo.xcprivacy"},
21 | "swift_versions": [
22 | "5.5",
23 | "5.6",
24 | "5.7",
25 | "5.8",
26 | "5.9",
27 | "6.0"
28 | ],
29 | "frameworks": [
30 | "UIKit",
31 | "Foundation",
32 | "CoreGraphics"
33 | ],
34 | "requires_arc": true
35 | }
36 |
--------------------------------------------------------------------------------
/IQDropDownTextFieldSwift/IQDropDownTextField+Date.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextField+Date.swift
3 | // https://github.com/hackiftekhar/IQDropDownTextField
4 | // Copyright (c) 2020-21 Iftekhar Qurashi.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | import UIKit
25 |
26 | @MainActor
27 | extension IQDropDownTextField {
28 |
29 | // MARK: - UIDatePicker delegate
30 |
31 | @objc internal func dateChanged(_ picker: UIDatePicker) {
32 | let selectedItem: String = dateFormatter.string(from: picker.date)
33 | privateSetSelectedItems(selectedItems: [selectedItem], animated: false, shouldNotifyDelegate: true)
34 | }
35 |
36 | @objc internal func timeChanged(_ picker: UIDatePicker) {
37 | let selectedItem: String = timeFormatter.string(from: picker.date)
38 | privateSetSelectedItems(selectedItems: [selectedItem], animated: false, shouldNotifyDelegate: true)
39 | }
40 |
41 | @objc internal func dateTimeChanged(_ picker: UIDatePicker) {
42 | let selectedItem: String = dateTimeFormatter.string(from: picker.date)
43 | privateSetSelectedItems(selectedItems: [selectedItem], animated: false, shouldNotifyDelegate: true)
44 | }
45 |
46 | @objc open var datePickerMode: UIDatePicker.Mode {
47 | get {
48 | return datePicker.datePickerMode
49 | }
50 | set {
51 | if dropDownMode == .date {
52 | datePicker.datePickerMode = newValue
53 | switch newValue {
54 | case .countDownTimer:
55 | dateFormatter.dateStyle = .none
56 | dateFormatter.timeStyle = .none
57 | case .date:
58 | dateFormatter.dateStyle = .short
59 | dateFormatter.timeStyle = .none
60 | case .time:
61 | timeFormatter.dateStyle = .none
62 | timeFormatter.timeStyle = .short
63 | case .dateAndTime:
64 | dateTimeFormatter.dateStyle = .short
65 | dateTimeFormatter.timeStyle = .short
66 | @unknown default:
67 | break
68 | }
69 | }
70 | }
71 | }
72 |
73 | @objc open var date: Date? {
74 | get {
75 | switch dropDownMode {
76 | case .date:
77 | if isOptionalDropDown {
78 | return (super.text?.isEmpty ?? true) ? nil : datePicker.date
79 | } else {
80 | return datePicker.date
81 | }
82 | case .time:
83 | if isOptionalDropDown {
84 | return (super.text?.isEmpty ?? true) ? nil : timePicker.date
85 | } else {
86 | return timePicker.date
87 | }
88 | case .dateTime:
89 | if isOptionalDropDown {
90 | return (super.text?.isEmpty ?? true) ? nil : dateTimePicker.date
91 | } else {
92 | return dateTimePicker.date
93 | }
94 | case .list, .textField, .multiList:
95 | return nil
96 | }
97 | }
98 | set {
99 | setDate(date: newValue, animated: false)
100 | }
101 | }
102 |
103 | @objc open func setDate(date: Date?, animated: Bool) {
104 | switch dropDownMode {
105 | case .date:
106 | let selectedItem: String?
107 | if let date = date {
108 | selectedItem = dateFormatter.string(from: date)
109 | } else {
110 | selectedItem = nil
111 | }
112 | privateSetSelectedItems(selectedItems: [selectedItem], animated: animated, shouldNotifyDelegate: false)
113 | case .time:
114 | let selectedItem: String?
115 | if let date = date {
116 | selectedItem = timeFormatter.string(from: date)
117 | } else {
118 | selectedItem = nil
119 | }
120 |
121 | privateSetSelectedItems(selectedItems: [selectedItem], animated: animated, shouldNotifyDelegate: false)
122 | case .dateTime:
123 | let selectedItem: String?
124 | if let date = date {
125 | selectedItem = dateTimeFormatter.string(from: date)
126 | } else {
127 | selectedItem = nil
128 | }
129 |
130 | privateSetSelectedItems(selectedItems: [selectedItem], animated: animated, shouldNotifyDelegate: false)
131 | default:
132 | break
133 | }
134 | }
135 |
136 | public var dateComponents: DateComponents {
137 | let components: Set = [.second, .minute, .hour, .day, .month, .year, .era]
138 | return Calendar.current.dateComponents(components, from: date ?? Date())
139 | }
140 |
141 | public var year: Int { return dateComponents.year ?? 0 }
142 | public var month: Int { return dateComponents.month ?? 0 }
143 | public var day: Int { return dateComponents.day ?? 0 }
144 | public var hour: Int { return dateComponents.hour ?? 0 }
145 | public var minute: Int { return dateComponents.minute ?? 0 }
146 | public var second: Int { return dateComponents.second ?? 0 }
147 | }
148 |
--------------------------------------------------------------------------------
/IQDropDownTextFieldSwift/IQDropDownTextField+Menu.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextField+Menu.swift
3 | // https://github.com/hackiftekhar/IQDropDownTextField
4 | // Copyright (c) 2020-21 Iftekhar Qurashi.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | import UIKit
25 |
26 | @available(iOS 15.0, *)
27 | @MainActor
28 | extension IQDropDownTextField {
29 |
30 | @objc open var menuButton: UIButton {
31 | privateMenuButton
32 | }
33 |
34 | @objc open var showMenuButton: Bool {
35 | get {
36 | privateMenuButton.superview != nil
37 | }
38 | set {
39 | if newValue {
40 | datePicker.preferredDatePickerStyle = .inline
41 | dateTimePicker.preferredDatePickerStyle = .inline
42 |
43 | self.addSubview(privateMenuButton)
44 | NSLayoutConstraint.activate([
45 | privateMenuButton.leadingAnchor.constraint(equalTo: leadingAnchor),
46 | privateMenuButton.trailingAnchor.constraint(equalTo: trailingAnchor),
47 | privateMenuButton.topAnchor.constraint(equalTo: topAnchor),
48 | privateMenuButton.bottomAnchor.constraint(equalTo: bottomAnchor)
49 | ])
50 | reconfigureMenu()
51 | } else {
52 | datePicker.preferredDatePickerStyle = .wheels
53 | dateTimePicker.preferredDatePickerStyle = .wheels
54 |
55 | privateMenuButton.removeFromSuperview()
56 | }
57 | }
58 | }
59 |
60 | internal func initializeMenu() {
61 | privateMenuButton.setImage(UIImage(systemName: "chevron.up.chevron.down"), for: .normal)
62 | privateMenuButton.contentHorizontalAlignment = .trailing
63 | privateMenuButton.contentEdgeInsets = .init(top: 0, left: 5, bottom: 0, right: 5)
64 | privateMenuButton.translatesAutoresizingMaskIntoConstraints = false
65 | privateMenuButton.addTarget(self, action: #selector(menuActionTriggered), for: .menuActionTriggered)
66 | privateMenuButton.showsMenuAsPrimaryAction = true
67 | }
68 |
69 | internal func reconfigureMenu() {
70 |
71 | switch dropDownMode {
72 |
73 | case .list, .multiList:
74 | let deferredMenuElement = UIDeferredMenuElement.uncached({ completion in
75 |
76 | var actions: [UIMenuElement] = []
77 | if self.multiItemList.count <= 1 {
78 | let selectedItem = self.selectedItem
79 | actions = self.itemList.map { item in
80 | return UIAction(title: item, image: nil,
81 | state: item == selectedItem ? .on : .off) { (_) in
82 | self.privateSetSelectedItems(selectedItems: [item], animated: true,
83 | shouldNotifyDelegate: true)
84 | }
85 | }
86 | } else {
87 | var selectedItems = self.selectedItems
88 | for (index, itemList) in self.multiItemList.enumerated() {
89 | let selectedItem = selectedItems[index]
90 | let childrens: [UIMenuElement] = itemList.map { item in
91 | return UIAction(title: item, image: nil,
92 | state: item == selectedItem ? .on : .off) { (_) in
93 | selectedItems[index] = item
94 | self.privateSetSelectedItems(selectedItems: selectedItems, animated: true,
95 | shouldNotifyDelegate: true)
96 | }
97 | }
98 |
99 | let title: String
100 | if index < self.optionalItemTexts.count {
101 | title = self.optionalItemTexts[index] ?? self.optionalItemText ?? ""
102 | } else {
103 | title = self.optionalItemText ?? ""
104 | }
105 | let subMenu = UIMenu(title: title, children: childrens)
106 |
107 | actions.append(subMenu)
108 | }
109 | }
110 | completion(actions)
111 | })
112 | let deferredMenus = UIMenu(title: self.placeholder ?? "", children: [deferredMenuElement])
113 | privateMenuButton.menu = deferredMenus
114 | privateMenuButton.isHidden = false
115 | case .time, .date, .dateTime:
116 | privateMenuButton.isHidden = false
117 | privateMenuButton.addTarget(self, action: #selector(menuButtonTapped), for: .touchUpInside)
118 | privateMenuButton.menu = nil
119 | case .textField:
120 | privateMenuButton.isHidden = true
121 | privateMenuButton.menu = nil
122 | }
123 | }
124 |
125 | @objc private func menuActionTriggered() {
126 | containerViewController?.view.endEditing(true)
127 | }
128 |
129 | @objc private func menuButtonTapped() {
130 | guard let containerViewController = containerViewController else {
131 | return
132 | }
133 | let popoverViewController = UIViewController()
134 | popoverViewController.modalPresentationStyle = .popover
135 | popoverViewController.popoverPresentationController?.sourceView = privateMenuButton
136 | popoverViewController.popoverPresentationController?.sourceRect = privateMenuButton.bounds
137 | popoverViewController.popoverPresentationController?.delegate = self
138 | switch dropDownMode {
139 | case .list, .multiList, .textField:
140 | break
141 | case .time:
142 | popoverViewController.view = timePicker
143 | timePicker.sizeToFit()
144 | popoverViewController.preferredContentSize = timePicker.bounds.size
145 | containerViewController.present(popoverViewController, animated: true, completion: nil)
146 | case .date:
147 | popoverViewController.view = datePicker
148 | datePicker.sizeToFit()
149 | popoverViewController.preferredContentSize = datePicker.bounds.size
150 | containerViewController.present(popoverViewController, animated: true, completion: nil)
151 | case .dateTime:
152 | popoverViewController.view = dateTimePicker
153 | dateTimePicker.sizeToFit()
154 | popoverViewController.preferredContentSize = dateTimePicker.bounds.size
155 | containerViewController.present(popoverViewController, animated: true, completion: nil)
156 | }
157 | }
158 | }
159 |
160 | @MainActor
161 | extension IQDropDownTextField {
162 | private var containerViewController: UIViewController? {
163 | var next = self.next
164 |
165 | repeat {
166 | if let next = next as? UIViewController {
167 | return next
168 | } else {
169 | next = next?.next
170 | }
171 | } while next != nil
172 | return nil
173 | }
174 | }
175 |
176 | @MainActor
177 | extension IQDropDownTextField: UIPopoverPresentationControllerDelegate {
178 | public func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
179 | // Force popover style
180 | return .none
181 | }
182 |
183 | public func prepareForPopoverPresentation(_ popoverPresentationController: UIPopoverPresentationController) {
184 | containerViewController?.view.endEditing(true)
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/IQDropDownTextFieldSwift/IQDropDownTextField+Picker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextField+Picker.swift
3 | // https://github.com/hackiftekhar/IQDropDownTextField
4 | // Copyright (c) 2020-21 Iftekhar Qurashi.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | import UIKit
25 |
26 | // MARK: - UIPickerView data source
27 | @MainActor
28 | extension IQDropDownTextField: UIPickerViewDataSource {
29 |
30 | public func numberOfComponents(in pickerView: UIPickerView) -> Int {
31 | return multiItemList.count
32 | }
33 |
34 | public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
35 | let isOptionalDropDown: Bool
36 | if component < isOptionalDropDowns.count {
37 | isOptionalDropDown = isOptionalDropDowns[component]
38 | } else if let last = isOptionalDropDowns.last {
39 | isOptionalDropDown = last
40 | } else {
41 | isOptionalDropDown = true
42 | }
43 |
44 | return multiItemList[component].count + (isOptionalDropDown ? 1 : 0)
45 | }
46 | }
47 |
48 | // MARK: UIPickerView delegate
49 | @MainActor
50 | extension IQDropDownTextField: UIPickerViewDelegate {
51 |
52 | public func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
53 | if let heightsForComponents = heightsForComponents,
54 | component < heightsForComponents.count,
55 | 0 < heightsForComponents[component] {
56 | return heightsForComponents[component]
57 | }
58 |
59 | return 44
60 | }
61 |
62 | public func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
63 | if let widthsForComponents = widthsForComponents,
64 | component < widthsForComponents.count,
65 | 0 < widthsForComponents[component] {
66 | return widthsForComponents[component]
67 | }
68 |
69 | // else calculating it's size.
70 | let availableWidth = (pickerView.bounds.width - 20) - 2 * CGFloat(multiItemList.count - 1)
71 | return availableWidth / CGFloat(multiItemList.count)
72 | }
73 |
74 | // swiftlint:disable function_body_length
75 | public func pickerView(_ pickerView: UIPickerView,
76 | viewForRow row: Int, forComponent component: Int,
77 | reusing view: UIView?) -> UIView {
78 |
79 | let isOptionalDropDown: Bool
80 | if component < isOptionalDropDowns.count {
81 | isOptionalDropDown = isOptionalDropDowns[component]
82 | } else if let last = isOptionalDropDowns.last {
83 | isOptionalDropDown = last
84 | } else {
85 | isOptionalDropDown = true
86 | }
87 |
88 | let row = row - (isOptionalDropDown ? 1 : 0)
89 |
90 | if row == Self.optionalItemIndex {
91 |
92 | let labelText: UILabel
93 | if let label = view as? UILabel {
94 | labelText = label
95 | } else {
96 | labelText = UILabel()
97 | labelText.textAlignment = .center
98 | labelText.adjustsFontSizeToFitWidth = adjustsFontSizeToFitWidth
99 | labelText.backgroundColor = UIColor.clear
100 | labelText.backgroundColor = UIColor.clear
101 | }
102 |
103 | labelText.font = dropDownFont ?? UIFont.systemFont(ofSize: 18)
104 | labelText.textColor = dropDownTextColor ?? UIColor.black
105 |
106 | labelText.isEnabled = false
107 | labelText.text = optionalItemText
108 |
109 | return labelText
110 |
111 | } else {
112 |
113 | let viewToReturn: UIView?
114 |
115 | if component < multiItemListView.count,
116 | row < multiItemListView[component].count,
117 | let view = multiItemListView[component][row] {
118 |
119 | // Archiving and Unarchiving is necessary to copy UIView instance.
120 | let viewData: Data = NSKeyedArchiver.archivedData(withRootObject: view)
121 | viewToReturn = NSKeyedUnarchiver.unarchiveObject(with: viewData) as? UIView
122 | } else {
123 | viewToReturn = nil
124 | }
125 |
126 | if let viewToReturn = viewToReturn {
127 | return viewToReturn
128 | } else {
129 |
130 | let labelText: UILabel
131 | if let label = view as? UILabel {
132 | labelText = label
133 | } else {
134 | labelText = UILabel()
135 | labelText.textAlignment = .center
136 | labelText.adjustsFontSizeToFitWidth = adjustsFontSizeToFitWidth
137 | labelText.backgroundColor = UIColor.clear
138 | labelText.backgroundColor = UIColor.clear
139 | }
140 |
141 | labelText.font = dropDownFont ?? UIFont.systemFont(ofSize: 18)
142 | labelText.textColor = dropDownTextColor ?? UIColor.black
143 |
144 | let itemList = multiItemList[component]
145 | let text = itemList[row]
146 | labelText.text = text
147 | labelText.adjustsFontSizeToFitWidth = adjustsFontSizeToFitWidth
148 | labelText.numberOfLines = adjustsFontSizeToFitWidth ? 1 : 0
149 | let canSelect: Bool
150 | if let result = dataSource?.textField(textField: self, canSelectItem: text) {
151 | canSelect = result
152 | } else {
153 | canSelect = true
154 | }
155 | labelText.isEnabled = canSelect
156 |
157 | return labelText
158 | }
159 | }
160 | }
161 | // swiftlint:enable function_body_length
162 |
163 | // swiftlint:disable cyclomatic_complexity
164 | // swiftlint:disable function_body_length
165 | public func pickerView(_ pickerView: UIPickerView,
166 | didSelectRow row: Int, inComponent component: Int) {
167 |
168 | privatePickerSelectedRows[component] = row
169 |
170 | let isOptionalDropDown: Bool
171 | if component < isOptionalDropDowns.count {
172 | isOptionalDropDown = isOptionalDropDowns[component]
173 | } else if let last = isOptionalDropDowns.last {
174 | isOptionalDropDown = last
175 | } else {
176 | isOptionalDropDown = true
177 | }
178 |
179 | let row = row - (isOptionalDropDown ? 1 : 0)
180 |
181 | if row == Self.optionalItemIndex {
182 | var selectedItems = selectedItems
183 | selectedItems[component] = nil
184 | privateSetSelectedItems(selectedItems: selectedItems, animated: false, shouldNotifyDelegate: true)
185 | } else if 0 <= row {
186 |
187 | let itemList = multiItemList[component]
188 | let text: String = itemList[row]
189 |
190 | let canSelect: Bool
191 | if let result = dataSource?.textField(textField: self, canSelectItem: text) {
192 | canSelect = result
193 | } else {
194 | canSelect = true
195 | }
196 |
197 | if canSelect {
198 | var selectedItems = selectedItems
199 | selectedItems[component] = text
200 | privateSetSelectedItems(selectedItems: selectedItems, animated: false, shouldNotifyDelegate: true)
201 | } else {
202 |
203 | let proposedSelection: IQProposedSelection
204 | if let result = dataSource?.textField(textField: self, proposedSelectionModeForItem: text) {
205 | proposedSelection = result
206 | } else {
207 | proposedSelection = .both
208 | }
209 |
210 | var aboveIndex: Int = row-1
211 | var belowIndex: Int = row+1
212 |
213 | if proposedSelection == .above {
214 | belowIndex = itemList.count
215 | } else if proposedSelection == .below {
216 | aboveIndex = -1
217 | }
218 |
219 | while 0 <= aboveIndex || belowIndex < itemList.count {
220 | if 0 <= aboveIndex {
221 | let aboveText: String = itemList[aboveIndex]
222 |
223 | if let result = dataSource?.textField(textField: self, canSelectItem: aboveText), result {
224 | var selectedItems = selectedItems
225 | selectedItems[component] = aboveText
226 | privateSetSelectedItems(selectedItems: selectedItems,
227 | animated: true,
228 | shouldNotifyDelegate: true)
229 | return
230 | }
231 |
232 | aboveIndex -= 1
233 | }
234 |
235 | if belowIndex < itemList.count {
236 | let belowText: String = itemList[belowIndex]
237 |
238 | if let result = dataSource?.textField(textField: self, canSelectItem: belowText), result {
239 | var selectedItems = selectedItems
240 | selectedItems[component] = belowText
241 | privateSetSelectedItems(selectedItems: selectedItems,
242 | animated: true,
243 | shouldNotifyDelegate: true)
244 | return
245 | }
246 |
247 | belowIndex += 1
248 | }
249 | }
250 |
251 | var selectedRows = selectedRows
252 | selectedRows[component] = 0
253 | setSelectedRows(rows: selectedRows, animated: true)
254 | }
255 | }
256 | }
257 | // swiftlint:enable cyclomatic_complexity
258 | // swiftlint:enable function_body_length
259 | }
260 |
--------------------------------------------------------------------------------
/IQDropDownTextFieldSwift/IQDropDownTextField.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextField.swift
3 | // https://github.com/hackiftekhar/IQDropDownTextField
4 | // Copyright (c) 2020-21 Iftekhar Qurashi.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | import UIKit
25 |
26 | // swiftlint:disable file_length
27 | @MainActor
28 | open class IQDropDownTextField: UITextField {
29 |
30 | nonisolated public static let optionalItemIndex: Int = -1
31 |
32 | open lazy var pickerView: UIPickerView = {
33 | let view = UIPickerView()
34 | view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
35 | view.showsSelectionIndicator = true
36 | view.delegate = self
37 | view.dataSource = self
38 | return view
39 | }()
40 |
41 | open lazy var timePicker: UIDatePicker = {
42 | let view = UIDatePicker()
43 | view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
44 | view.datePickerMode = .time
45 | if #available(iOS 14.0, *) {
46 | view.preferredDatePickerStyle = .wheels
47 | }
48 | view.addTarget(self, action: #selector(timeChanged(_:)), for: .valueChanged)
49 | return view
50 | }()
51 |
52 | open lazy var dateTimePicker: UIDatePicker = {
53 | let view = UIDatePicker()
54 | view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
55 | view.datePickerMode = .dateAndTime
56 | if #available(iOS 13.4, *) {
57 | view.preferredDatePickerStyle = .wheels
58 | }
59 | view.addTarget(self, action: #selector(dateTimeChanged(_:)), for: .valueChanged)
60 | return view
61 | }()
62 |
63 | open lazy var datePicker: UIDatePicker = {
64 | let view = UIDatePicker()
65 | view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
66 | view.datePickerMode = .date
67 | if #available(iOS 13.4, *) {
68 | view.preferredDatePickerStyle = .wheels
69 | }
70 | view.addTarget(self, action: #selector(dateChanged(_:)), for: .valueChanged)
71 | return view
72 | }()
73 |
74 | private lazy var dismissToolbar: UIToolbar = {
75 | let view = UIToolbar()
76 | view.isTranslucent = true
77 | view.sizeToFit()
78 | let buttonFlexible: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
79 | target: nil, action: nil)
80 | let buttonDone: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self,
81 | action: #selector(resignFirstResponder))
82 | view.items = [buttonFlexible, buttonDone]
83 | return view
84 | }()
85 |
86 | internal let privateMenuButton: UIButton = UIButton()
87 |
88 | // Sets a custom font for the IQDropdownTextField items. Default is boldSystemFontOfSize:18.0.
89 | open var dropDownFont: UIFont?
90 |
91 | // Sets a custom color for the IQDropdownTextField items. Default is blackColor.
92 | open var dropDownTextColor: UIColor?
93 |
94 | // Width and height to adopt for each section.
95 | // If you don't want to specify a row width then use 0 to calculate row width automatically.
96 | open var widthsForComponents: [CGFloat]?
97 | open var heightsForComponents: [CGFloat]?
98 |
99 | open var dateFormatter: DateFormatter = DateFormatter() {
100 | didSet {
101 | datePicker.locale = dateFormatter.locale
102 | }
103 | }
104 |
105 | open var timeFormatter: DateFormatter = DateFormatter() {
106 | didSet {
107 | timePicker.locale = timeFormatter.locale
108 | }
109 | }
110 |
111 | open var dateTimeFormatter: DateFormatter = DateFormatter() {
112 | didSet {
113 | dateTimePicker.locale = dateTimeFormatter.locale
114 | }
115 | }
116 |
117 | weak open var dropDownDelegate: (any IQDropDownTextFieldDelegate)?
118 | weak open override var delegate: (any UITextFieldDelegate)? {
119 | didSet {
120 | dropDownDelegate = delegate as? (any IQDropDownTextFieldDelegate)
121 | }
122 | }
123 |
124 | open var dataSource: (any IQDropDownTextFieldDataSource)?
125 |
126 | open var dropDownMode: IQDropDownMode = .list {
127 | didSet {
128 | switch dropDownMode {
129 | case .list, .multiList:
130 | inputView = pickerView
131 | setSelectedRows(rows: selectedRows, animated: true)
132 | case .date:
133 |
134 | inputView = datePicker
135 | if !isOptionalDropDown {
136 | date = datePicker.date
137 | }
138 | case .time:
139 |
140 | inputView = timePicker
141 | if !isOptionalDropDown {
142 | date = timePicker.date
143 | }
144 | case .dateTime:
145 |
146 | inputView = dateTimePicker
147 | if !isOptionalDropDown {
148 | date = dateTimePicker.date
149 | }
150 | case .textField:
151 | inputView = nil
152 | }
153 | if #available(iOS 15.0, *) {
154 | reconfigureMenu()
155 | }
156 | }
157 | }
158 |
159 | private var privateOptionalItemText: String?
160 | private var privateOptionalItemTexts: [String?] = []
161 |
162 | private var privateIsOptionalDropDowns: [Bool] = []
163 |
164 | open var multiListSelectionFormatHandler: ((_ selectedItems: [String?], _ selectedIndexes: [Int]) -> String)? {
165 | didSet {
166 | if let handler = multiListSelectionFormatHandler {
167 | super.text = handler(selectedItems, selectedRows)
168 | } else {
169 | super.text = selectedItems.compactMap({ $0 }).joined(separator: ", ")
170 | }
171 | }
172 | }
173 |
174 | open var selectionFormatHandler: ((_ selectedItem: String?, _ selectedIndex: Int) -> String)? {
175 | didSet {
176 | if let handler = selectionFormatHandler {
177 | super.text = handler(selectedItem, selectedRow)
178 | } else {
179 | super.text = selectedItems.compactMap({ $0 }).joined(separator: ", ")
180 | }
181 | }
182 | }
183 |
184 | @available(*, deprecated, message: "use 'selectedItem' instead", renamed: "selectedItem")
185 | open override var text: String? {
186 | didSet {
187 | }
188 | }
189 |
190 | @available(*, deprecated, message: "use 'selectedItem' instead", renamed: "selectedItem")
191 | open override var attributedText: NSAttributedString? {
192 | didSet {
193 | }
194 | }
195 |
196 | open var multiItemList: [[String]] = [] {
197 | didSet {
198 | // Refreshing pickerView
199 | isOptionalDropDowns = privateIsOptionalDropDowns
200 | let selectedRows = selectedRows
201 | self.selectedRows = selectedRows
202 | }
203 | }
204 |
205 | open var multiItemListView: [[UIView?]] = [] {
206 | didSet {
207 | // Refreshing pickerView
208 | isOptionalDropDowns = privateIsOptionalDropDowns
209 | let selectedRows = selectedRows
210 | self.selectedRows = selectedRows
211 | }
212 | }
213 |
214 | open override var adjustsFontSizeToFitWidth: Bool {
215 | didSet {
216 | privateUpdateOptionsList()
217 | }
218 | }
219 |
220 | private var hasSetInitialIsOptional: Bool = false
221 |
222 | func dealloc() {
223 | pickerView.delegate = nil
224 | pickerView.dataSource = nil
225 | self.delegate = nil
226 | dataSource = nil
227 | privateOptionalItemText = nil
228 | }
229 |
230 | // MARK: - Initialization
231 |
232 | func initialize() {
233 | contentVerticalAlignment = .center
234 | contentHorizontalAlignment = .center
235 |
236 | // These will update the UI and other components,
237 | // all the validation added if awakeFromNib for textField is called after custom UIView awakeFromNib call
238 | do {
239 | let mode = dropDownMode
240 | dropDownMode = mode
241 |
242 | isOptionalDropDown = hasSetInitialIsOptional ? isOptionalDropDown : true
243 | }
244 |
245 | dateFormatter.dateStyle = .medium
246 | dateFormatter.timeStyle = .none
247 |
248 | timeFormatter.dateStyle = .none
249 | timeFormatter.timeStyle = .short
250 |
251 | dateTimeFormatter.dateStyle = .medium
252 | dateTimeFormatter.timeStyle = .short
253 |
254 | if #available(iOS 15.0, *) {
255 | initializeMenu()
256 | }
257 | }
258 |
259 | override init(frame: CGRect) {
260 | super.init(frame: frame)
261 | initialize()
262 | }
263 |
264 | required public init?(coder: NSCoder) {
265 | super.init(coder: coder)
266 | }
267 |
268 | open override func awakeFromNib() {
269 | super.awakeFromNib()
270 | initialize()
271 | }
272 |
273 | // MARK: - Overrides
274 | @discardableResult
275 | open override func becomeFirstResponder() -> Bool {
276 | let result = super.becomeFirstResponder()
277 |
278 | for (index, selectedRow) in privatePickerSelectedRows where 0 <= selectedRow {
279 | pickerView.selectRow(selectedRow, inComponent: index, animated: false)
280 | }
281 | return result
282 | }
283 |
284 | open override func caretRect(for position: UITextPosition) -> CGRect {
285 | if dropDownMode == .textField {
286 | return super.caretRect(for: position)
287 | } else {
288 | return .zero
289 | }
290 | }
291 |
292 | open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
293 | if action == #selector(cut(_:)) || action == #selector(paste(_:)) {
294 | return false
295 | } else {
296 | return super.canPerformAction(action, withSender: sender)
297 | }
298 | }
299 |
300 | // MARK: - Selected Row
301 |
302 | // Key represents section index and value represents selection
303 | internal var privatePickerSelectedRows: [Int: Int] = [:]
304 |
305 | // MARK: - Setters
306 | open func privateUpdateOptionsList() {
307 |
308 | switch dropDownMode {
309 | case .date:
310 | if !isOptionalDropDown {
311 | date = datePicker.date
312 | }
313 | case .time:
314 | if !isOptionalDropDown {
315 | date = timePicker.date
316 | }
317 | case .dateTime:
318 |
319 | if !isOptionalDropDown {
320 | date = dateTimePicker.date
321 | }
322 | case .list, .multiList:
323 | pickerView.reloadAllComponents()
324 | case .textField:
325 | break
326 | }
327 | }
328 | }
329 |
330 | // MARK: - Toolbar
331 |
332 | @MainActor
333 | extension IQDropDownTextField {
334 |
335 | @objc open var showDismissToolbar: Bool {
336 | get {
337 | return (inputAccessoryView == dismissToolbar)
338 | }
339 | set {
340 | inputAccessoryView = (newValue ? dismissToolbar : nil)
341 | }
342 | }
343 | }
344 |
345 | // MARK: - Optional drop down
346 |
347 | @MainActor
348 | extension IQDropDownTextField {
349 |
350 | @IBInspectable
351 | open var isOptionalDropDown: Bool {
352 | get { return privateIsOptionalDropDowns.first ?? true }
353 | set {
354 | isOptionalDropDowns = [newValue]
355 | }
356 | }
357 |
358 | @objc open var isOptionalDropDowns: [Bool] {
359 | get { return privateIsOptionalDropDowns }
360 | set {
361 | if !hasSetInitialIsOptional || privateIsOptionalDropDowns != newValue {
362 |
363 | let previousSelectedRows: [Int] = selectedRows
364 |
365 | privateIsOptionalDropDowns = newValue
366 | hasSetInitialIsOptional = true
367 |
368 | if dropDownMode == .list || dropDownMode == .multiList {
369 | pickerView.reloadAllComponents()
370 | selectedRows = previousSelectedRows
371 | }
372 | }
373 | }
374 | }
375 |
376 | @IBInspectable
377 | open var optionalItemText: String? {
378 | get {
379 | if let privateOptionalItemText = privateOptionalItemText, !privateOptionalItemText.isEmpty {
380 | return privateOptionalItemText
381 | } else {
382 | return NSLocalizedString("Select", comment: "")
383 | }
384 | }
385 | set {
386 | privateOptionalItemText = newValue
387 | privateUpdateOptionsList()
388 | }
389 | }
390 |
391 | public var optionalItemTexts: [String?] {
392 | get {
393 | return privateOptionalItemTexts
394 | }
395 | set {
396 | privateOptionalItemTexts = newValue
397 | privateUpdateOptionsList()
398 | }
399 | }
400 | }
401 |
402 | // MARK: - Item List
403 |
404 | @MainActor
405 | extension IQDropDownTextField {
406 |
407 | @objc open var itemList: [String] {
408 | get {
409 | multiItemList.first ?? []
410 | }
411 | set {
412 | multiItemList = [newValue]
413 | }
414 | }
415 |
416 | public var itemListView: [UIView?] {
417 | get {
418 | multiItemListView.first ?? []
419 | }
420 | set {
421 | multiItemListView = [newValue]
422 | }
423 | }
424 |
425 | }
426 |
427 | // MARK: - Selected Row
428 |
429 | @MainActor
430 | extension IQDropDownTextField {
431 |
432 | @objc open var selectedRow: Int {
433 | get {
434 | var pickerViewSelectedRow: Int = selectedRows.first /*It may return -1*/ ?? 0
435 | pickerViewSelectedRow = max(pickerViewSelectedRow, 0)
436 | return pickerViewSelectedRow - (isOptionalDropDown ? 1 : 0)
437 | }
438 | set {
439 | selectedRows = [newValue]
440 | }
441 | }
442 |
443 | @objc open var selectedRows: [Int] {
444 | get {
445 | var selection: [Int] = []
446 | for index in multiItemList.indices {
447 |
448 | let isOptionalDropDown: Bool
449 | if index < isOptionalDropDowns.count {
450 | isOptionalDropDown = isOptionalDropDowns[index]
451 | } else if let last = isOptionalDropDowns.last {
452 | isOptionalDropDown = last
453 | } else {
454 | isOptionalDropDown = true
455 | }
456 |
457 | var pickerViewSelectedRow: Int = privatePickerSelectedRows[index] ?? -1 /*It may return -1*/
458 | pickerViewSelectedRow = max(pickerViewSelectedRow, 0)
459 |
460 | let finalSelection = pickerViewSelectedRow - (isOptionalDropDown ? 1 : 0)
461 | selection.append(finalSelection)
462 | }
463 | return selection
464 | }
465 | set {
466 | setSelectedRows(rows: newValue, animated: false)
467 | }
468 | }
469 |
470 | @objc open func selectedRow(inSection section: Int) -> Int {
471 | privatePickerSelectedRows[section] ?? Self.optionalItemIndex
472 | }
473 |
474 | @objc open func setSelectedRow(row: Int, animated: Bool) {
475 | setSelectedRows(rows: [row], animated: animated)
476 | }
477 |
478 | @objc open func setSelectedRow(row: Int, inSection section: Int, animated: Bool) {
479 | var selectedRows = selectedRows
480 | selectedRows[section] = row
481 | setSelectedRows(rows: selectedRows, animated: animated)
482 | }
483 |
484 | @objc open func setSelectedRows(rows: [Int], animated: Bool) {
485 |
486 | var finalResults: [String?] = []
487 | for (index, row) in rows.enumerated() {
488 |
489 | let itemList: [String]
490 |
491 | if index < multiItemList.count {
492 | itemList = multiItemList[index]
493 | } else {
494 | itemList = []
495 | }
496 |
497 | let isOptionalDropDown: Bool
498 | if index < isOptionalDropDowns.count {
499 | isOptionalDropDown = isOptionalDropDowns[index]
500 | } else if let last = isOptionalDropDowns.last {
501 | isOptionalDropDown = last
502 | } else {
503 | isOptionalDropDown = true
504 | }
505 |
506 | if row == Self.optionalItemIndex {
507 |
508 | if !isOptionalDropDown, !itemList.isEmpty {
509 | finalResults.append(itemList[0])
510 | } else {
511 | finalResults.append(nil)
512 | }
513 | } else {
514 | if row < itemList.count {
515 | finalResults.append(itemList[row])
516 | } else {
517 | finalResults.append(nil)
518 | }
519 | }
520 |
521 | let pickerViewRow: Int = row + (isOptionalDropDown ? 1 : 0)
522 | privatePickerSelectedRows[index] = pickerViewRow
523 | if index < pickerView.numberOfComponents {
524 | pickerView.selectRow(pickerViewRow, inComponent: index, animated: animated)
525 | }
526 | }
527 |
528 | if let multiListSelectionFormatHandler = multiListSelectionFormatHandler {
529 | super.text = multiListSelectionFormatHandler(finalResults, rows)
530 | } else if let selectionFormatHandler = selectionFormatHandler,
531 | let selectedItem = finalResults.first,
532 | let selectedRow = rows.first {
533 | super.text = selectionFormatHandler(selectedItem, selectedRow)
534 | } else {
535 | super.text = finalResults.compactMap({ $0 }).joined(separator: ", ")
536 | }
537 | }
538 | }
539 |
540 | // MARK: - Selected Items
541 |
542 | @MainActor
543 | extension IQDropDownTextField {
544 |
545 | @objc open var selectedItem: String? {
546 | get {
547 | return selectedItems.first ?? nil
548 | }
549 | set {
550 | switch dropDownMode {
551 | case .multiList:
552 | if let newValue = newValue {
553 | selectedItems = [newValue]
554 | } else {
555 | selectedItems = multiItemList.map({ _ in nil }) // Resetting every section
556 | }
557 | case .list, .date, .time, .dateTime, .textField:
558 | selectedItems = [newValue]
559 | }
560 | }
561 | }
562 |
563 | public var selectedItems: [String?] {
564 | get {
565 | switch dropDownMode {
566 | case .list, .multiList:
567 | var finalSelection: [String?] = []
568 | for (index, selectedRow) in selectedRows.enumerated() {
569 | if 0 <= selectedRow, index < multiItemList.count {
570 | finalSelection.append(multiItemList[index][selectedRow])
571 | } else {
572 | finalSelection.append(nil)
573 | }
574 | }
575 | return finalSelection
576 | case .date:
577 | return (super.text?.isEmpty ?? true) ? [nil] : [dateFormatter.string(from: datePicker.date)]
578 | case .time:
579 | return (super.text?.isEmpty ?? true) ? [nil] : [timeFormatter.string(from: timePicker.date)]
580 | case .dateTime:
581 | return (super.text?.isEmpty ?? true) ? [nil] : [dateTimeFormatter.string(from: dateTimePicker.date)]
582 | case .textField:
583 | return [super.text]
584 | }
585 | }
586 |
587 | set {
588 | privateSetSelectedItems(selectedItems: newValue, animated: false, shouldNotifyDelegate: false)
589 | }
590 | }
591 |
592 | @objc open func setSelectedItem(selectedItem: String?, animated: Bool) {
593 | privateSetSelectedItems(selectedItems: [selectedItem], animated: animated, shouldNotifyDelegate: false)
594 | }
595 |
596 | public func setSelectedItems(selectedItems: [String?], animated: Bool) {
597 | privateSetSelectedItems(selectedItems: selectedItems, animated: animated, shouldNotifyDelegate: false)
598 | }
599 | }
600 |
601 | @MainActor
602 | internal extension IQDropDownTextField {
603 |
604 | // swiftlint:disable cyclomatic_complexity
605 | // swiftlint:disable function_body_length
606 | func privateSetSelectedItems(selectedItems: [String?],
607 | animated: Bool, shouldNotifyDelegate: Bool) {
608 | switch dropDownMode {
609 | case .list, .multiList:
610 |
611 | var finalIndexes: [Int] = []
612 | var finalSelection: [String?] = []
613 |
614 | for (index, selectedItem) in selectedItems.enumerated() {
615 |
616 | if let selectedItem = selectedItem,
617 | index < multiItemList.count,
618 | let index = multiItemList[index].firstIndex(of: selectedItem) {
619 | finalIndexes.append(index)
620 | finalSelection.append(selectedItem)
621 |
622 | } else {
623 |
624 | let isOptionalDropDown: Bool
625 | if index < isOptionalDropDowns.count {
626 | isOptionalDropDown = isOptionalDropDowns[index]
627 | } else if let last = isOptionalDropDowns.last {
628 | isOptionalDropDown = last
629 | } else {
630 | isOptionalDropDown = true
631 | }
632 |
633 | let selectedIndex = isOptionalDropDown ? Self.optionalItemIndex : 0
634 | finalIndexes.append(selectedIndex)
635 | finalSelection.append(nil)
636 | }
637 | }
638 |
639 | setSelectedRows(rows: finalIndexes, animated: animated)
640 |
641 | if shouldNotifyDelegate {
642 | if dropDownMode == .multiList {
643 | dropDownDelegate?.textField(textField: self, didSelectItems: finalSelection)
644 | } else if let selectedItem = finalSelection.first {
645 | dropDownDelegate?.textField(textField: self, didSelectItem: selectedItem)
646 | }
647 | }
648 |
649 | case .date:
650 |
651 | if let selectedItem = selectedItems.first,
652 | let selectedItem = selectedItem,
653 | let date = dateFormatter.date(from: selectedItem) {
654 | super.text = selectedItem
655 | datePicker.setDate(date, animated: animated)
656 |
657 | if shouldNotifyDelegate {
658 | dropDownDelegate?.textField(textField: self, didSelectDate: date)
659 | }
660 | } else if isOptionalDropDown,
661 | let selectedItem = selectedItems.first,
662 | selectedItem == nil || selectedItem?.isEmpty == true {
663 | super.text = ""
664 |
665 | datePicker.setDate(Date(), animated: animated)
666 |
667 | if shouldNotifyDelegate {
668 | dropDownDelegate?.textField(textField: self, didSelectDate: nil)
669 | }
670 | }
671 | case .time:
672 |
673 | if let selectedItem = selectedItems.first,
674 | let selectedItem = selectedItem,
675 | let time = timeFormatter.date(from: selectedItem) {
676 | let day: Date = Date(timeIntervalSinceReferenceDate: 0)
677 | let componentsForDay: Set = [.era, .year, .month, .day]
678 | let componentsForTime: Set = [.hour, .minute, .second]
679 | var componentsDay: DateComponents = Calendar.current.dateComponents(componentsForDay, from: day)
680 | let componentsTime: DateComponents = Calendar.current.dateComponents(componentsForTime, from: time)
681 | componentsDay.hour = componentsTime.hour
682 | componentsDay.minute = componentsTime.minute
683 | componentsDay.second = componentsTime.second
684 |
685 | if let date = Calendar.current.date(from: componentsDay) {
686 | super.text = selectedItem
687 | timePicker.setDate(date, animated: animated)
688 |
689 | if shouldNotifyDelegate {
690 | dropDownDelegate?.textField(textField: self, didSelectDate: date)
691 | }
692 | }
693 | } else if isOptionalDropDown,
694 | let selectedItem = selectedItems.first,
695 | selectedItem == nil || selectedItem?.isEmpty == true {
696 | super.text = ""
697 | timePicker.setDate(Date(), animated: animated)
698 |
699 | if shouldNotifyDelegate {
700 | dropDownDelegate?.textField(textField: self, didSelectDate: nil)
701 | }
702 | }
703 |
704 | case .dateTime:
705 |
706 | if let selectedItem = selectedItems.first,
707 | let selectedItem = selectedItem,
708 | let date: Date = dateTimeFormatter.date(from: selectedItem) {
709 | super.text = selectedItem
710 | dateTimePicker.setDate(date, animated: animated)
711 |
712 | if shouldNotifyDelegate {
713 | dropDownDelegate?.textField(textField: self, didSelectDate: date)
714 | }
715 | } else if isOptionalDropDown,
716 | let selectedItem = selectedItems.first,
717 | selectedItem == nil || selectedItem?.isEmpty == true {
718 |
719 | super.text = ""
720 | dateTimePicker.setDate(Date(), animated: animated)
721 |
722 | if shouldNotifyDelegate {
723 | dropDownDelegate?.textField(textField: self, didSelectDate: nil)
724 | }
725 | }
726 | case .textField:
727 | super.text = selectedItems.compactMap({ $0 }).joined(separator: ", ")
728 | }
729 | }
730 | // swiftlint:enable cyclomatic_complexity
731 | // swiftlint:enable function_body_length
732 | }
733 |
--------------------------------------------------------------------------------
/IQDropDownTextFieldSwift/IQDropDownTextFieldConstants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextFieldConstants.swift
3 | // https://github.com/hackiftekhar/IQDropDownTextField
4 | // Copyright (c) 2020-21 Iftekhar Qurashi.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | import Foundation
25 |
26 | public enum IQDropDownMode: Sendable {
27 | case list
28 | case multiList
29 | case time
30 | case date
31 | case dateTime
32 | case textField
33 | }
34 |
35 | public enum IQProposedSelection: Sendable {
36 | case both
37 | case above
38 | case below
39 | }
40 |
--------------------------------------------------------------------------------
/IQDropDownTextFieldSwift/IQDropDownTextFieldDataSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextFieldDataSource.swift
3 | // https://github.com/hackiftekhar/IQDropDownTextField
4 | // Copyright (c) 2020-21 Iftekhar Qurashi.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | import UIKit
25 |
26 | @MainActor
27 | public protocol IQDropDownTextFieldDataSource: AnyObject {
28 |
29 | // Check if an item can be selected by dropdown texField.
30 | func textField(textField: IQDropDownTextField, canSelectItem item: String) -> Bool
31 |
32 | // If canSelectItem return NO, then textField:proposedSelectionModeForItem: asked for proposed selection mode.
33 | // .above: pickerView find the nearest items above the deselected item
34 | // that can be selected and then selecting that row.
35 | // .below: pickerView find the nearest items below the deselected item
36 | // that can be selected and then selecting that row.
37 | // both.: pickerView find the nearest items that can be selected
38 | // above or below the deselected item and then selecting that row.
39 | func textField(textField: IQDropDownTextField, proposedSelectionModeForItem item: String) -> IQProposedSelection
40 | }
41 |
42 | @MainActor
43 | extension IQDropDownTextFieldDataSource {
44 |
45 | func textField(textField: IQDropDownTextField, didSelectDate date: Date?) { }
46 | func textField(textField: IQDropDownTextField, canSelectItem item: String) -> Bool { return true }
47 | func textField(textField: IQDropDownTextField, proposedSelectionModeForItem item: String) -> IQProposedSelection {
48 | return .both
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/IQDropDownTextFieldSwift/IQDropDownTextFieldDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IQDropDownTextFieldDelegate.swift
3 | // https://github.com/hackiftekhar/IQDropDownTextField
4 | // Copyright (c) 2020-21 Iftekhar Qurashi.
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 |
24 | import UIKit
25 |
26 | @MainActor
27 | public protocol IQDropDownTextFieldDelegate: UITextFieldDelegate {
28 |
29 | // Called when textField changes it's selected item. Supported for list mode
30 | func textField(textField: IQDropDownTextField, didSelectItem item: String?)
31 |
32 | // Called when textField changes it's selected item. Supported for multiList mode
33 | func textField(textField: IQDropDownTextField, didSelectItems items: [String?])
34 |
35 | // Called when textField changes it's selected item. Supported for time, date, dateTime mode
36 | func textField(textField: IQDropDownTextField, didSelectDate date: Date?)
37 | }
38 |
39 | @MainActor
40 | extension IQDropDownTextFieldDelegate {
41 |
42 | func textField(textField: IQDropDownTextField, didSelectItem item: String?) { }
43 |
44 | func textField(textField: IQDropDownTextField, didSelectItems items: [String?]) { }
45 |
46 | func textField(textField: IQDropDownTextField, didSelectDate date: Date?) { }
47 | }
48 |
--------------------------------------------------------------------------------
/IQDropDownTextFieldSwift/PrivacyInfo.xcprivacy:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSPrivacyCollectedDataTypes
6 |
7 | NSPrivacyTracking
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Images/date.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackiftekhar/IQDropDownTextField/099faf11b3b2a18f4852cb76b0b908a334fae05d/Images/date.png
--------------------------------------------------------------------------------
/Images/date_time.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackiftekhar/IQDropDownTextField/099faf11b3b2a18f4852cb76b0b908a334fae05d/Images/date_time.png
--------------------------------------------------------------------------------
/Images/large_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackiftekhar/IQDropDownTextField/099faf11b3b2a18f4852cb76b0b908a334fae05d/Images/large_text.png
--------------------------------------------------------------------------------
/Images/multi_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackiftekhar/IQDropDownTextField/099faf11b3b2a18f4852cb76b0b908a334fae05d/Images/multi_list.png
--------------------------------------------------------------------------------
/Images/simple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackiftekhar/IQDropDownTextField/099faf11b3b2a18f4852cb76b0b908a334fae05d/Images/simple.png
--------------------------------------------------------------------------------
/Images/time.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hackiftekhar/IQDropDownTextField/099faf11b3b2a18f4852cb76b0b908a334fae05d/Images/time.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Mohd Iftekhar Qurashi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | 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, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2013-2017 Iftekhar Qurashi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.5
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "IQDropDownTextFieldSwift",
8 | platforms: [
9 | .iOS(.v11)
10 | ],
11 | products: [
12 | .library(
13 | name: "IQDropDownTextFieldSwift",
14 | targets: ["IQDropDownTextFieldSwift"]
15 | )
16 | ],
17 | targets: [
18 | .target(
19 | name: "IQDropDownTextFieldSwift",
20 | path: "IQDropDownTextFieldSwift",
21 | resources: [
22 | .copy("PrivacyInfo.xcprivacy")
23 | ]
24 | )
25 | ]
26 | )
27 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | project 'Drop Down TextField.xcodeproj'
2 |
3 | platform :ios, '11.0'
4 | use_frameworks!
5 |
6 | target 'Drop Down TextField' do
7 | pod "IQDropDownTextField", :path => "."
8 | end
9 |
10 | target 'DropDownTextFieldSwift' do
11 | pod "IQDropDownTextFieldSwift", :path => "."
12 | pod "SwiftLint"
13 | end
14 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - IQDropDownTextField (4.0.2)
3 | - IQDropDownTextFieldSwift (4.0.2)
4 | - SwiftLint (0.53.0)
5 |
6 | DEPENDENCIES:
7 | - IQDropDownTextField (from `.`)
8 | - IQDropDownTextFieldSwift (from `.`)
9 | - SwiftLint
10 |
11 | SPEC REPOS:
12 | trunk:
13 | - SwiftLint
14 |
15 | EXTERNAL SOURCES:
16 | IQDropDownTextField:
17 | :path: "."
18 | IQDropDownTextFieldSwift:
19 | :path: "."
20 |
21 | SPEC CHECKSUMS:
22 | IQDropDownTextField: 763cdb0b0ebe15cf5f486a0d31fe281e07a0800d
23 | IQDropDownTextFieldSwift: f705be4c1955eb475b74335105f8b360a0001db8
24 | SwiftLint: 5ce4d6a8ff83f1b5fd5ad5dbf30965d35af65e44
25 |
26 | PODFILE CHECKSUM: c45ef6dbf4032cdb8f2212b9067a426ee589b296
27 |
28 | COCOAPODS: 1.13.0
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | IQDropDownTextField
2 | ===================
3 |
4 | TextField with DropDown support using UIPickerView
5 |
6 | []
7 | []
8 | []
9 | []
10 | []
11 | []
12 |
13 | ## Installing
14 |
15 | Install using [cocoapods](http://cocoapods.org). Add in your `Podfile`:
16 |
17 | ```
18 | pod 'IQDropDownTextField'
19 | ```
20 |
21 | Or below for Swift version
22 | ```
23 | pod 'IQDropDownTextFieldSwift'
24 | ```
25 |
26 | ## How to Use
27 |
28 | In IB (_story boards or xibs_) you can add `UITextField`'s and set the class as `IQDropDownTextField`
29 |
30 | ### Objective-C
31 |
32 | Nothing more easy than it!
33 |
34 | ```objective-c
35 |
36 | @implementation ViewController
37 |
38 | - (void)viewDidLoad
39 | {
40 | [super viewDidLoad];
41 |
42 | textFieldTextPicker.isOptionalDropDown = NO;
43 | [textFieldTextPicker setItemList:[NSArray arrayWithObjects:@"London",@"Johannesburg",@"Moscow",@"Mumbai",@"Tokyo",@"Sydney", nil]];
44 | }
45 | @end
46 |
47 | ```
48 |
49 | ### Swift
50 |
51 | It's very simple to setup your `IQDropDownTextField`. The sample below shows you how to:
52 |
53 | ```swift
54 | import IQDropDownTextFieldSwift
55 |
56 | class MyController : UIViewController {
57 | @IBOutlet var occupationTextField: IQDropDownTextField!
58 |
59 | override func viewDidLoad() {
60 | occupationTextField.isOptionalDropDown = false
61 | occupationTextField.itemList = ["programmer", "teacher", "engineer"]
62 | }
63 | }
64 | ```
65 |
66 | And that's all you need! =)
67 |
68 | ## Contributions
69 |
70 | Any contribution is more than welcome! You can contribute through pull requests and issues on GitHub.
71 |
72 | ## Author
73 |
74 | If you wish to contact me, email at: hack.iftekhar@gmail.com
75 |
76 | ## LICENSE
77 |
78 | Copyright (c) 2010-2015
79 |
80 | Permission is hereby granted, free of charge, to any person obtaining a copy
81 | of this software and associated documentation files (the "Software"), to deal
82 | in the Software without restriction, including without limitation the rights
83 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
84 | copies of the Software, and to permit persons to whom the Software is
85 | furnished to do so, subject to the following conditions:
86 |
87 | The above copyright notice and this permission notice shall be included in
88 | all copies or substantial portions of the Software.
89 |
90 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
91 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
92 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
93 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
94 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
95 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
96 | THE SOFTWARE.
97 |
--------------------------------------------------------------------------------