How to create a view controller that is used as a picker? It shows up from the bottom of a screen over the current context, and covers just a part of the screen. After a value is picked, it is passed back to a view controller that presented the picker in iOS.
Try this in Objective C:
UIAlertController *alert = [UIAlertController alertControllerWithTitle:#"Success" message:#"Thank you for coming here" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:#"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action){
//define your custom action here or data passing here
[self dismissViewControllerAnimated:YES completion:nil];
}];
[alert addAction:okAction];
[self presentViewController:alert animated:YES completion:nil];
Try following useful library :
https://github.com/madjid/MMPickerView
Code snippet
NSArray *strings = #[#"This", #"is", #"just", #"an array", #"of strings."];
[MMPickerView showPickerViewInView:self.view
withStrings:strings
withOptions:nil
completion:^(NSString *selectedString) {
//selectedString is the return value which you can use as you wish
self.label.text = selectedString;
}];
Add a UIViewController and Design as You Want.. Give that View Controller a Stroyboard ID...
add the following code in picker view controller Class
var mainVC : YourSendingClassName!
when user didSelectRowAtIndexPath..
call self.dismiss(true , completion {
self.mainVC.userPickedValue(theValueYouWannaPass)
})
now in your sender class if you wanna use a button create an Action from it... according to your requirement the button should be at bottom..
add the following code in Button Action
let sliderViewController = storyboard?.instantiateViewController(withIdentifier: "YourStoryBoard ID") as! YourPickerClass
sliderViewController.modalPresentationStyle = .popover
//set width and height as you need
sliderViewController.preferredContentSize = CGSize(width: 253, height: 160)
sliderViewController.mainVC = self
let popoverMenuViewController = sliderViewController.popoverPresentationController
popoverMenuViewController?.permittedArrowDirections = .down
popoverMenuViewController?.delegate = self
popoverMenuViewController?.sourceView = sender
popoverMenuViewController?.sourceRect = sender.frame
present(sliderViewController, animated: true, completion: nil)
after the action create a function
func userPickedValue(value : YourDataype) {
// Do something with received value
}
Related
I am trying to show UIAlertController with text field. When it launches keyfoard automatically shows up as textfield get the focus automatically. How can I show alert with textfield without keyboard (it should show up only when user clicks on textfield).
Here is my code
UIAlertController* alert = [UIAlertController alertControllerWithTitle:#"Collect Input" message:#"input message"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:#"Submit" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
//use alert.textFields[0].text
}];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:#"Cancel" handler:^(UIAlertAction * action) {
//cancel action
}];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
// A block for configuring the text field prior to displaying the alert
//[textField resignFirstResponder];
}];
[alert addAction:defaultAction];
[alert addAction:cancelAction];
[self presentViewController:alert animated:NO completion:nil];
There are a few solutions.
You can get a reference to the text field in the addTextFieldWithConfigurationHandler callback and store it in a local variable. Then in the completion handler of presentViewController you can call resignFirstResponder on the text field. But this solution is far from ideal because the keyboard will appear and then immediately be dismissed.
Better yet is to set the text field's delegate and implement the shouldBeginEditing delegate method. Add an instance variable to act as a flag. The first time shouldBeginEditing is called, not the flag isn't set, set it, and return NO. Then each time after, check the flag and return YES.
Here's the implementation for option 2:
Indicate that your class conforms to the UITextFieldDelegate protocol in the .m file:
#interface YourClassHere () <UITextFieldDelegate>
#end
Add an instance variable for the flag:
BOOL showKeyboard = NO;
Update your alert setup code to set the text field's delegate:
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.delegate = self;
}];
Implement the text field delegate method:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
if (showKeyboard) {
return YES;
} else {
showKeyboard = YES;
return NO;
}
}
This prevents the initial display of the keyboard but allows it any time after that.
UIAlertController is crashing with this error message:
Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x7fb9107674d0>)
also with a warning is thrown trying to capture the textFields objectAtIndex.
Any ideas ?
Warning.. Capturing 'controller' strongly in this block is likely to lead to a retail cycle.
I also tried to create a #property (weak) reference the warning goes away but the app still crashes with this:
-(void)viewWillAppear:(BOOL)animated
{
// self.controller = [UIAlertController alloc];
UIAlertController* controller = [UIAlertController alertControllerWithTitle:#"Add Alergy To List" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
[controller addTextFieldWithConfigurationHandler:^(UITextField * nametextField) {
_nameTextField.text = [controller.textFields objectAtIndex:0].text;
}];
UIAlertAction *save = [UIAlertAction actionWithTitle:#"Save Data" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
[self save:nil];
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:#"Cancel Action" style:(UIAlertActionStyleCancel) handler:nil];
[controller addAction:save];
[controller addAction:cancel];
[self presentViewController:controller animated:YES completion:nil];
}
__weak __typeof(UIAlertController) weakController = controller;
__weak __typeof(UITextField) weakTextField = _nameTextField;
[controller addTextFieldWithConfigurationHandler:^(UITextField *nametextField){
// nameTextField is the textField being returned in the block
// if you want to use _nameTextField you need to make that weak too
nameTextField.text = [weakController.textFields objectAtIndex:0].text;
// or
weakTextField.text = [weakController.textFields objectAtIndex:0].text;
}];
Add
__weak __typeof(UIAlertController) weakController = controller;
before your addTextFieldWithConfigurationhandler:.
Then you should replace
_nameTextField.text = [controller.textFields objectAtIndex:0].text;
with
nameTextField.text = [weakController.textFields objectAtIndex:0].text;
Without the _.
_ is for internal access of properties. Here you have to update the field given in parameter
The fact is that you are in a block and if you want to get text from controller fields, you have to make a weak reference to it. The weak controller will prevent increase retain count.
You are trying to load alertcontroller before the viewcontroller is allocated this warning is because you are adding UIAlertController directly inside viewWillAppear
This method is called before the view controller's view is about to be added to a view hierarchy
Just give some time for view to get added to the view hierarchy.to do that use dispatch_after or some other delay functionality.
Since you are getting textfield input inside a block you can't use strong declaration,So you have to declare __weak typeof(UIAlertController) *weakcontroller = controller; before entering the block.Check apple developer site here for more understanding.
Overall modified code is given bekow:
-(void)viewWillAppear:(BOOL)animated{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIAlertController* controller = [UIAlertController alertControllerWithTitle:#"Add Alergy To List" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
__weak typeof(UIAlertController) *weakcontroller = controller;
[controller addTextFieldWithConfigurationHandler:^(UITextField * nametextField) {
nametextField.text = [weakcontroller.textFields objectAtIndex:0].text;
}];
UIAlertAction *save = [UIAlertAction actionWithTitle:#"Save Data" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
[self save:nil];
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:#"Cancel Action" style:(UIAlertActionStyleCancel) handler:nil];
[controller addAction:save];
[controller addAction:cancel];
[self presentViewController:controller animated:YES completion:nil];
});
}
It will work without any issues.
HeyI want to do that This my button and in button there is textfield I want to do that when I pressed on button the action sheet picker become appear and give 4 to 5 list of string whatever I select it will apear on textfield which is in button. please help me
Start by adding a target for your button. In Objective-C, that would be like this:
[myButton addTarget:self
action:#selector(buttonPressed:)
forControlEvents:UIControlEventTouchUpInside];
Then create the method buttonPressed. An example of that would be:
- (void)buttonPressed:(id)sender {
if ([sender isEqual:self.myButton]) {
//This is where you can create the UIAlertController
}
}
Then, to create the UIAlertController:
UIAlertController *myAlertController = [UIAlertController alertControllerWithTitle:#"Title"
message:#"Message"
preferredStyle:UIAlertControllerStyleActionSheet];
Then you create actions for what each button you want to have appear on the action sheet. You need to have a title for the button and an action for them, though the action block can be empty.
UIAlertAction *action1 = [UIAlertAction actionWithTitle:#"Action 1"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
//Whatever you want to have happen when the button is pressed
}];
[myAlertController addAction:action1];
//repeat for all subsequent actions...
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString(#"Cancel", nil)
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
// It's good practice to give the user an option to do nothing, but not necessary
}];
[myAlertController addAction:cancelAction];
Lastly, you present the UIAlertController:
[self presentViewController:myAlertController
animated:YES
completion:^{
}];
NOTE:
If you're building for iPad and using an Action Sheet style for the UIAlertController, then you will need to set a source for the UIAlertController to present from. This can be done like this:
if ([sender isKindOfClass:[UIView class]]) {
if ([myAlertController.popoverPresentationController respondsToSelector:#selector(setSourceView:)]) { // Check for availability of this method
myAlertController.popoverPresentationController.sourceView = self.myButton;
} else {
myAlertController.popoverPresentationController.sourceRect = self.myButton.frame;
}
}
Here's the problem up front: I have a UIAlertController that has a textfield. I want to save the content of that textfield as an NSString when the user touches a "Confirm" button in the alert. When the Confirm action block is executed, however, the alert is nil (presumably already dismissed and deallocated at that point), and thus so is its textfield, meaning I cannot save the textfield's text.
I am using a series of UIAlertControllers to allow a user to create a passcode for my app, such that any time the app comes to the foreground, the user is prompted for the code before the app can be used.
I created a category of UIAlertController with several convenience methods that return preconfigured alerts that I need to use. Here's one of them:
+ (UIAlertController*)passcodeCreationAlertWithConfirmBehavior:(void(^)())confirmBlock andCancelBehavior:(void(^)())cancelBlock {
UIAlertController *passcodeCreationAlert = [UIAlertController alertControllerWithTitle:#"Enter a passcode"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[passcodeCreationAlert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.keyboardType = UIKeyboardTypeNumberPad;
}];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (cancelBlock) {
cancelBlock();
}
}];
UIAlertAction* confirmAction = [UIAlertAction actionWithTitle:#"Confirm"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (confirmBlock) {
confirmBlock();
}
}];
[passcodeCreationAlert addAction:cancelAction];
[passcodeCreationAlert addAction:confirmAction];
passcodeCreationAlert.preferredAction = confirmAction;
confirmAction.enabled = NO;
return passcodeCreationAlert;
}
This method returns a UIAlertController that allows the user to enter their desired passcode into a textfield. When I call this method in my view controller, I pass blocks as parameters which are used as the UIAlertAction handlers:
- (void)presentCreatePasscodeAlert {
UIAlertController *alert = [UIAlertController passcodeCreationAlertWithConfirmBehavior:^{
firstPasscode = alert.textFields[0].text;
[self presentConfirmPasscodeAlert];
} andCancelBehavior:^{
[self presentEnablePasscodeAlert];
}];
alert.textFields[0].delegate = self;
[self presentViewController:alert animated:YES completion:nil];
}
To reiterate the problem now that there is more context: When the action block is entered at the line:
firstPasscode = alert.textFields[0].text;
the alert is nil, and so is its textfield, meaning I cannot save the textfield's text.
In a separate project, however, I tried getting the same functionality without using the category and custom convenience methods, and that works as desired:
- (void) createPassword {
UIAlertController *createPasswordAlert = [UIAlertController alertControllerWithTitle:#"Enter a password"
message:nil
preferredStyle:UIAlertControllerStyleAlert];
__weak ViewController *weakSelf = self;
[createPasswordAlert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.delegate = weakSelf;
textField.keyboardType = UIKeyboardTypeNumberPad;
}];
UIAlertAction* confirmAction = [UIAlertAction actionWithTitle:#"Confirm"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
self.password = createPasswordAlert.textFields[0].text;
[self confirmPassword];
}];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:#"Cancel"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self promptPasswordCreation];
}];
[createPasswordAlert addAction:confirmAction];
[createPasswordAlert addAction:cancelAction];
confirmAction.enabled = NO;
[self presentViewController:createPasswordAlert animated:YES completion:nil];
}
Sure enough, in the above code the alert exists when the Confirm block is entered, and I can save the text just fine.
Have I done something screwy by passing blocks as parameters to my convenience methods?
One of your problems is that when you create the block and send it into the initializer you are referencing a nil object inside the block. Then the block is saved with that nil reference and passed as a block to your handler.
UIAlertController *alert = [UIAlertController passcodeCreationAlertWithConfirmBehavior:^{
firstPasscode = alert.textFields[0].text;
[self presentConfirmPasscodeAlert];
} andCancelBehavior:^{
[self presentEnablePasscodeAlert];
}];
In there alert is not initialized yet when you create that block and send it to be saved. An option to fix that would be to use a standard initializer and then categorize a method to add the block functions that you want. Another option might be to try to add the __block modifier to the alert object, although I don't think that will help.
Inside of a forin loop, I need to present an UIAlertController and wait for user confirmation before presenting the next one. I presented them inside a forin loop, but only the first one appears(after confirmation, the others don't show up).Any help would be greatly appreciated.
You can use the UIAlertController delegate when a button is pressed you show the next alert.
Make a global alert index:
NSUInteger alertIndex = 0;
Make a global NSArray with your alert details in a NSDictionary eg:
self.alerts = #[#{#"title":#"Alert", #"message":#"Message 1"}, #{#"title":#"Alert", #"message":#"Message 2"}];
Call your first alert with an index, eg:
...title:self.alerts[alertIndex][#"title"]...
and
...message:self.alerts[alertIndex][#"message"]...
In your alert controller delegate's didClickButtonAtIndex:
alertIndex++;
// call your next alert here, explained above.
I need to do something similar to display hints to users throughout my app.
Just managed to cobble this together and it works quite well and is easy to adapt and add to ...
/////
// Alert Properties in #interface
////
#property NSOperationQueue *queue;
#property NSMutableArray* operationArray;
#property int operationCounter;
#property int operationTotal;
/////
// Alert Properties End
////
/////////
// Multi Alert Code Start
////////
- (void) alertCodeWithTitleString: (NSString*) titlestring AndMessageString:(NSString*)messagestring {
NSOperation *tempoperation = [NSBlockOperation blockOperationWithBlock: ^(void) {
NSString* tutTitleString = titlestring;
NSString* tutMessageString = messagestring;
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController * alert= [UIAlertController
alertControllerWithTitle:tutTitleString
message:tutMessageString
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* dontshow = [UIAlertAction
actionWithTitle:#"Don't Show This Again"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alert dismissViewControllerAnimated:YES completion:nil];
self.operationCounter++;
[self alertOperationCallMethod2];
}];
UIAlertAction* done = [UIAlertAction
actionWithTitle:#"Close"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action)
{
[alert dismissViewControllerAnimated:YES completion:nil];
self.operationCounter++;
[self alertOperationCallMethod2];
}];
[alert addAction:done];
[alert addAction:dontshow];
[self presentViewController:alert animated:YES completion:nil];
});
} ];
self.operationTotal++;
[self.operationArray addObject:tempoperation];
}
-(void) alertOperationCallMethod1 {
self.operationCounter = 0;
self.operationTotal = 0;
self.queue = [[NSOperationQueue alloc] init];
self.operationArray = [[NSMutableArray alloc] init];
[self alertCodeWithTitleString:#"Title1" AndMessageString:#"Message1"];
[self alertCodeWithTitleString:#"Title2" AndMessageString:#"Message2"];
[self alertCodeWithTitleString:#"Title3" AndMessageString:#"Message3"];
// Just keep adding method calls here to add alerts
[self alertOperationCallMethod2];
}
-(void) alertOperationCallMethod2 {
if (self.operationCounter<self.operationTotal) {
[self.queue addOperation:self.operationArray[self.operationCounter]];
}
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//something here
[self alertOperationCallMethod1];
}
/////////
// Multi Alert Code End
////////
----
All you need to do is add another method call in alertOperationCallMethod1
Multiple versions of alertCodeWith..... method should allow you to customise your alerts to suit.
Hope this helps someone : )
This UIViewController extension does what you want:
The alerts queue up, fifo. I.e. latter alers wait and don't show until
user respond to former alerts.