I know how to add any Custom UI inside UIAlertView by using accessoryView like UITableView but I am now curious that if we still have option to add Custom UI inside an UIAlertController, what I am wanting to have is a UITableViewController inside an UIAlertController with clear understanding.
Courtesy of StackOverflow users I was able to do this task.
Here is my code:
UIViewController *controller = [[UIViewController alloc]init];
UITableView *alertTableView;
CGRect rect;
if (array.count < 4) {
rect = CGRectMake(0, 0, 272, 100);
[controller setPreferredContentSize:rect.size];
}
else if (array.count < 6){
rect = CGRectMake(0, 0, 272, 150);
[controller setPreferredContentSize:rect.size];
}
else if (array.count < 8){
rect = CGRectMake(0, 0, 272, 200);
[controller setPreferredContentSize:rect.size];
}
else {
rect = CGRectMake(0, 0, 272, 250);
[controller setPreferredContentSize:rect.size];
}
alertTableView = [[UITableView alloc]initWithFrame:rect];
alertTableView.delegate = self;
alertTableView.dataSource = self;
alertTableView.tableFooterView = [[UIView alloc]initWithFrame:CGRectZero];
[alertTableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
[alertTableView setTag:kAlertTableViewTag];
[controller.view addSubview:alertTableView];
[controller.view bringSubviewToFront:alertTableView];
[controller.view setUserInteractionEnabled:YES];
[alertTableView setUserInteractionEnabled:YES];
[alertTableView setAllowsSelection:YES];
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Title" message:#"Message" preferredStyle:UIAlertControllerStyleAlert];
[alertController setValue:controller forKey:#"contentViewController"];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
}];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
Here's #Syed Ali Salman's answer in simplified form in Swift:
let alertController = UIAlertController(title: "The Title",
message: "Here's a message.",
preferredStyle: .Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel)
{ (action) in
// ...
}
alertController.addAction(cancelAction)
let okAction = UIAlertAction(title: "OK", style: .Default)
{ (action) in
// ...
}
alertController.addAction(okAction)
let tableViewController = UITableViewController()
tableViewController.preferredContentSize = CGSize(width: 272, height: 176) // 4 default cell heights.
alertController.setValue(tableViewController, forKey: "contentViewController")
yourTopViewController().presentViewController(alertController, animated: true)
{
// ...
}
UIViewController *tempViewController = [[UIViewController alloc] init];
tempViewController.view.backgroundColor = [UIColor redColor];
[alertController setValue:tempViewController forKey:#"contentViewController"];
That piece of code will show a red view on the alert view,Now you can easily put a UITableView inside the UIViewController.Happy UIAlertController customizing ;)
Here is a Swift 5 Sample Code:
//MARK: - Properties
private var alertController = UIAlertController()
private var tblView = UITableView()
//MARK: - TableViewAlert
private func setupTableViewAlert() {
let alertVC = UIViewController.init()
let rect = CGRect(x: 0.0, y: 0.0, width: 300.0, height: 300.0)
alertVC.preferredContentSize = rect.size
tblView = UITableView(frame: rect)
tblView.delegate = self;
tblView.dataSource = self;
tblView.tableFooterView = UIView(frame: .zero)
tblView.separatorStyle = .singleLine
alertVC.view.addSubview(tblView)
alertVC.view.bringSubviewToFront(tblView)
alertVC.view.isUserInteractionEnabled = true
tblView.isUserInteractionEnabled = true
tblView.allowsSelection = true
self.alertController = UIAlertController(title: "Select City", message: nil, preferredStyle: .alert)
//this is the main part
//add local alert content over global one
alertController.setValue(alertVC, forKey: "contentViewController")
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
extension SignupViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell.init(style: .value1, reuseIdentifier: "cell")
cell.textLabel?.text = "Cell \(indexPath.row + 1)"
cell.textLabel?.textAlignment = .center
cell.detailTextLabel?.textColor = .black
return cell
}}
enter image description here
Related
In following code popover on iPad is not centred for second alert (cities) when iPad is rotated. It works fine for first alert (countries) though. I've printed the values and it's same for both alerts. It seems that despite having correct value for coordinates it doesn't present it in centre on device rotation for second alert.
Why it doesn't show in centre for second alert? How to fix it?
class MyVC: UIViewController {
var popoverController:UIPopoverPresentationController?
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
self.tableView.reloadData()
if self.popoverController != nil {
popoverController?.sourceView = self.view
print("viewWillTransition():width: x: \(UIScreen.main.bounds.size.width*0.5), y: \(UIScreen.main.bounds.size.height*0.5)")
popoverController?.sourceRect = CGRect(x: UIScreen.main.bounds.size.width*0.5, y: UIScreen.main.bounds.size.height*0.5, width: 0, height: 0)
popoverController?.permittedArrowDirections = []
}
}
#IBAction func countryButtonClicked(_ sender: UIBarButtonItem) {
displayCountries()
}
func displayCountries() {
let alert = UIAlertController(title: "", message: "", preferredStyle: .actionSheet)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
let titleAttributedText = NSMutableAttributedString(
string: "Select Country",
attributes: [
NSAttributedString.Key.paragraphStyle: paragraphStyle
]
)
alert.setValue(titleAttributedText, forKey: "attributedMessage")
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: countryHandler))
for list in Countries {
alert.addAction(UIAlertAction(title: list, style: .default, handler: countryHandler))
}
// For iPad
if let poController = alert.popoverPresentationController {
popoverController = poController
popoverController?.sourceView = self.view
print("displayCountries():width: x: \(UIScreen.main.bounds.size.width*0.5), y: \(UIScreen.main.bounds.size.height*0.5)")
popoverController?.sourceRect = CGRect(x: UIScreen.main.bounds.size.width*0.5, y: UIScreen.main.bounds.size.height*0.5, width: 0, height: 0)
popoverController?.permittedArrowDirections = []
}
alert.view.addSubview(UIView())
present(alert, animated: false, completion: nil)
}
#IBAction func cityButtonClicked(_ sender: Any) {
showCities()
}
func showCities(){
let alert = UIAlertController(title: "Select City", message: nil, preferredStyle: .actionSheet)
for listItem in Cities {
alert.addAction(UIAlertAction(title: listItem.title, style: .default, handler: cityHandler))
}
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: cityHandler))
if let popoverController = alert.popoverPresentationController {
popoverController.sourceView = self.view
print("showCities():width: x: \(UIScreen.main.bounds.size.width*0.5), y: \(UIScreen.main.bounds.size.height*0.5)")
popoverController.sourceRect = CGRect(x: UIScreen.main.bounds.size.width*0.5, y: UIScreen.main.bounds.size.height*0.5, width: 0, height: 0)
popoverController.permittedArrowDirections = []
}
alert.view.addSubview(UIView())
present(alert, animated: false, completion: nil)
}
}
popoverController must be also assigned in second alert:
if let popoverController = alert.popoverPresentationController {
self.popoverController = popoverController
...
i have an AlertController with UITextView.
when UITexView become first responder the alter doesn't move up with the keyboard.
this is my code:
#IBAction func showAlert(sender: AnyObject) {
let alertController = UIAlertController(title: "Hello, I'm alert! \n\n\n\n\n\n\n", message: "", preferredStyle: .alert)
let rect = CGRect(x: 15, y: 15, width: 240, height: 150)//CGRectMake(15, 50, 240, 150.0)
let textView = UITextView(frame: rect)
textView.font = UIFont(name: "Helvetica", size: 15)
textView.textColor = UIColor.lightGray
textView.backgroundColor = UIColor.white
textView.layer.borderColor = UIColor.lightGray.cgColor
textView.layer.borderWidth = 1.0
textView.text = "Enter message here"
textView.delegate = self
alertController.view.addSubview(textView)
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let action = UIAlertAction(title: "Ok", style: .default, handler: { action in
let msg = (textView.textColor == UIColor.lightGray) ? "" : textView.text
print(msg!)
})
alertController.addAction(cancel)
alertController.addAction(action)
self.present(alertController, animated: true, completion: {
textView.becomeFirstResponder()
})
}
and this is my result:
there is a solution?
thanks in advance
just addTextField and then remove it
alert.addTextField { field in
field.translatesAutoresizingMaskIntoConstraints = false
field.heightAnchor.constraint(equalToConstant: 0).isActive = true
}
let inCntrlr = alert.childViewControllers[0].view!
inCntrlr.removeFromSuperview()
and then you could add your own views. here is a
result
After presenting alert controller. Open keyboard for TextView and move alert controller up.
I hope this will suite for you.
self.present(alertController, animated: true, completion: {
textView.becomeFirstResponder()
UIView.animate(withDuration: 0.5, animations: {
alertController.view.frame.origin.y = 100
})
})
UIAlertController by default will slide up when they keyboard is shown. The problem here is that you have added a subview to the alert controller. From the UIAlertController docs:
The UIAlertController class is intended to be used as-is and does not
support subclassing. The view hierarchy for this class is private and
must not be modified.
Adding your own subview to the alert goes against what the docs say and is likely what is causing your problem. If you need an alert with a text view in it, your best bet is to create your own view and manage it yourself.
I created this drop-in replacement, also customizable for any special needs of course. It's pretty simple and 'just works'!
https://gist.github.com/unixb0y/42a1ae0fb707bdb5e1e484bafd33d44a
It is a subclass of UIAlertController with a UITextView inside.
When it is initialized, it observes keyboard changes and adjusts its view.frame.origin.y accordingly.
var keyboardHeight: CGFloat = 100 {
didSet {
let height = UIScreen.main.bounds.height
let menu = self.view.frame.height
let keyb = self.keyboardHeight
self.view.frame.origin.y = height-menu-keyb-20
}
}
...
...
...
#objc func keyboardChange(sender: Notification) {
guard let userInfo = sender.userInfo else { return }
let endFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect
keyboardHeight = endFrame?.height ?? 100
}
It's like. You open keyboard and change alert position.
var alertController = UIAlertController(title: nil, message: nil, preferredStyle: UIAlertController.Style.alert)
var alertControllerPositon:CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
#IBAction func showAlert(sender: AnyObject) {
let alertController = UIAlertController(title: "Hello, I'm alert! \n\n\n\n\n\n\n", message: "", preferredStyle: .alert)
let rect = CGRect(x: 15, y: 15, width: 240, height: 150)//CGRectMake(15, 50, 240, 150.0)
let textView = UITextView(frame: rect)
textView.font = UIFont(name: "Helvetica", size: 15)
textView.textColor = UIColor.lightGray
textView.backgroundColor = UIColor.white
textView.layer.borderColor = UIColor.lightGray.cgColor
textView.layer.borderWidth = 1.0
textView.text = "Enter message here"
textView.delegate = self
alertController.view.addSubview(textView)
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
let action = UIAlertAction(title: "Ok", style: .default, handler: { action in
let msg = (textView.textColor == UIColor.lightGray) ? "" : textView.text
print(msg!)
})
alertController.addAction(cancel)
alertController.addAction(action)
self.present(alertController, animated: true, completion: {
textView.becomeFirstResponder()
})
alertControllerPositon = alertController.view.frame.origin.y
}
#objc func keyboardWillShow(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
alertControllerPositon = alertController.view.frame.origin.y
let result = self.view.frame.height - (alertController.view.frame.height + alertController.view.frame.origin.y) - 20
self.alertController.view.frame.origin.y -= (keyboardSize.height - result)
}
}
#objc func keyboardWillHide(notification: NSNotification) {
if self.alertController.view.frame.origin.y != alertControllerPositon {
self.alertController.view.frame.origin.y = alertControllerPositon
}
}
I am very new to swift coding so I want to know that is there any way to show tableview in alertcontroller using swift.
var alrController = UIAlertController(title: "\n\n\n\n\n\n", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)
let margin:CGFloat = 8.0
let rect = CGRectMake(margin, margin, alrController.view.bounds.size.width - margin * 4.0, 100.0)
var tableView = UITableView(frame: rect)
tableView.delegate = self
tableView.dataSource = self
tableView.backgroundColor = UIColor.greenColor()
alrController.view.addSubview(tableView)
let somethingAction = UIAlertAction(title: "Something", style: UIAlertActionStyle.Default, handler: {(alert: UIAlertAction!) in println("something")})
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: {(alert: UIAlertAction!) in println("cancel")})
alrController.addAction(somethingAction)
alrController.addAction(cancelAction)
self.presentViewController(alrController, animated: true, completion:{})
private var alertController = UIAlertController()
private var tblView = UITableView()
private func setupCitySelectionAlert() {
let alertVC = UIViewController.init()
let rect = CGRect(x: 0.0, y: 0.0, width: 300.0, height: 300.0)
alertVC.preferredContentSize = rect.size
tblView = UITableView(frame: rect)
tblView.delegate = self;
tblView.dataSource = self;
tblView.tableFooterView = UIView(frame: .zero)
tblView.separatorStyle = .singleLine
alertVC.view.addSubview(tblView)
alertVC.view.bringSubviewToFront(tblView)
alertVC.view.isUserInteractionEnabled = true
tblView.isUserInteractionEnabled = true
tblView.allowsSelection = true
self.alertController = UIAlertController(title: "Title", message: nil, preferredStyle: .alert)
alertController.setValue(alertVC, forKey: "contentViewController")
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
I am working on a project which I had already released with iOS-7. But now as the action sheet is making problem so I am now implementing UIAlertController. Following is the code which I am using for showing UIAlertController with UIPicker.
alertController = [UIAlertController alertControllerWithTitle:#"" message:#"" preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *alertAction = [UIAlertAction actionWithTitle:#"" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:alertAction];
[alertController addAction:alertAction];
[alertController addAction:alertAction];
[alertController addAction:alertAction];
[pickerToolbar setFrame:CGRectMake(0, 0, self.view.frame.size.width-20, 44)];
[alertController.view addSubview:pickerToolbar];
[alertController.view addSubview:picker];
[alertController.view setBounds:CGRectMake(0, 0, self.view.frame.size.width, 485)];
[self presentViewController:alertController animated:YES completion:nil];
But I am not able to add datepicker in it. It is not working same as normal picker and the app hang. Please advice for any sample code for adding UIDatePicker in UIAlertController. Thanks in advance
Adding UIDatePicker to action sheet was discouraged by Apple all along. Since iOS 7, Apple had introduced the use of inline date picker (see how it is done in the Calendar app).
If you managed to hack a workaround using UIAlertController, it may probably break again in future iOS releases.
For iOS9+ Swift code here.
add target to your date button or other view.
dateBtn.addTarget(self,action: #selector(YourViewController.dateSelect(_:)),forControlEvents: UIControlEvents.TouchDown) //my date button
func dateSelect(sender:UIButton) {
if (UI_USER_INTERFACE_IDIOM() == .Phone)
{
//init date picker
self.datePicker = UIDatePicker(frame: CGRectMake(0, 0, self.view.frame.size.width,260))
self.datePicker.datePickerMode = UIDatePickerMode.Date
//add target
self.datePicker.addTarget(self, action: #selector(YourViewController.dateSelected(_:)), forControlEvents: UIControlEvents.ValueChanged)
//add to actionsheetview
if(UIDevice.currentDevice().systemVersion >= "8.0")
{
let alertController = UIAlertController(title: "Date Selection", message:" " , preferredStyle: UIAlertControllerStyle.ActionSheet)
alertController.view.addSubview(self.datePicker)//add subview
let cancelAction = UIAlertAction(title: "Done", style: .Cancel) { (action) in
self.dateSelected(self.datePicker)
self.tableView.reloadData()
}
//add button to action sheet
alertController.addAction(cancelAction)
let height:NSLayoutConstraint = NSLayoutConstraint(item: alertController.view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 300)
alertController.view.addConstraint(height);
self.presentViewController(alertController, animated: true, completion: nil)
}
}
}
//selected date func
func dateSelected(datePicker:UIDatePicker)
{
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.ShortStyle
let currentDate = datePicker.date
let day = currentDate.day()
let month = currentDate.month()
let year = currentDate.year()
let date = "\(day)/\(month)/\(year)"
print(date)
}
this works for me
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"\n\n\n\n\n\n\n\n\n\n\n" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIDatePicker *picker = [[UIDatePicker alloc] init];
[picker setDatePickerMode:UIDatePickerModeDate];
[alertController.view addSubview:picker];
[alertController addAction:({
UIAlertAction *action = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSLog(#"OK");
NSLog(#"%#",picker.date);
}];
action;
})];
UIPopoverPresentationController *popoverController = alertController.popoverPresentationController;
popoverController.sourceView = sender;
popoverController.sourceRect = [sender bounds];
[self presentViewController:alertController animated:YES completion:nil];
A clean way to do it in Swift 2:
let vc = UIAlertController(title: "Pickup time", message: nil, preferredStyle: .Alert)
vc.addTextFieldWithConfigurationHandler({ (textfield) -> Void in
let datepicker = UIDatePicker()
// add delegate ... here
textfield.inputView = datepicker
})
vc.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
vc.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil))
presentViewController(vc, animated: true, completion: nil)
#idris code in swift3
//Function Start
func dateSelect() {
//init date picker
let datePicker = UIDatePicker(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 260))
datePicker.datePickerMode = UIDatePickerMode.date
//add target
datePicker.addTarget(self, action: #selector(dateSelected(datePicker:)), for: UIControlEvents.valueChanged)
//add to actionsheetview
let alertController = UIAlertController(title: "Date Selection", message:" " , preferredStyle: UIAlertControllerStyle.actionSheet)
alertController.view.addSubview(datePicker)//add subview
let cancelAction = UIAlertAction(title: "Done", style: .cancel) { (action) in
self.dateSelected(datePicker: datePicker)
}
//add button to action sheet
alertController.addAction(cancelAction)
let height:NSLayoutConstraint = NSLayoutConstraint(item: alertController.view, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 300)
alertController.view.addConstraint(height);
self.present(alertController, animated: true, completion: nil)
}
//selected date func
#objc func dateSelected(datePicker:UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = DateFormatter.Style.short
let currentDate = datePicker.date
print(currentDate)
}
Here is my code that is tested on both iOS 7 and iOS 8
- (id)initWithDatePicker:(NSString*)title parentView:(UIView*)parentView {
self = [super init];
if (self) {
datePickerView = [[UIDatePicker alloc] init];
datePickerView.datePickerMode = UIDatePickerModeDateAndTime;
if (IS_IOS8_AND_UP) {
alertViewController = [UIAlertController
alertControllerWithTitle:EMPTY_STRING
message:title
preferredStyle:UIAlertControllerStyleActionSheet];
UIView* aboveBlurLayer = alertViewController.view.subviews[0];
[aboveBlurLayer addSubview:datePickerView];
[aboveBlurLayer setWidth:SCREEN_WIDTH - 16];
[datePickerView setWidth:SCREEN_WIDTH - 16];
[alertViewController.view setWidth:SCREEN_WIDTH - 16];
[alertViewController.view setBackgroundColor:[UIColor whiteColor]];
UIAlertAction* alertAction =
[UIAlertAction actionWithTitle:EMPTY_STRING
style:UIAlertActionStyleDefault
handler:nil];
[alertViewController addAction:alertAction];
[alertViewController addAction:alertAction];
[alertViewController addAction:alertAction];
[alertViewController addAction:alertAction];
[datePickerView setBackgroundColor:[UIColor whiteColor]];
[aboveBlurLayer addSubview:datePickerView];
} else {
actionSheet = [[UIActionSheet alloc] initWithTitle:title
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
[actionSheet addSubview:datePickerView];
}
[self addToolBar];
isDatePicker = YES;
parent = parentView;
}
return self;
}
On Tool bar I have two buttons Done and Cancel On Done i send back a call via delegate with selected date and on cancel I dismiss. This code is for both iOS 7 and iOS 8
It can be done with a few hacks.
Here's what I've used so far with very little maintenance.
let alertController = UIAlertController(title: alertTitle, message: nil, preferredStyle: .actionSheet)
let picker = UIDatePicker()
picker.translatesAutoresizingMaskIntoConstraints = false
alertController.view.addSubview(picker)
alertController.view.heightAnchor.constraint(equalToConstant: 350).isActive = true
picker.leadingAnchor.constraint(equalTo: alertController.view.leadingAnchor).isActive = true
picker.trailingAnchor.constraint(equalTo: alertController.view.trailingAnchor).isActive = true
picker.topAnchor.constraint(equalTo: alertController.view.topAnchor, constant: 10).isActive = true
/// Add your actions here....
I am trying to add a date picker to an alert view. I can add the animation to the screen based on a button click and I see a black box but no date picker.
Here is what I have so far....
- (IBAction)showDatePicker {
CGRect frame = CGRectMake(200, self.view.frame.size.height, self.view.frame.size.width, 0); //CGRectMake(225, 145, 260, 125);
UIPickerView *datePicker = [[UIPickerView alloc] initWithFrame:frame];
UIAlertView *showDateAlert = [[UIAlertView alloc]
initWithTitle:#"Enter the Code Date"
message:#"Sample message"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Update", nil];
[self.view addSubview:datePicker];
[UIView beginAnimations:#"slideIn" context:nil];
[datePicker setCenter:CGPointMake(datePicker.frame.origin.x, self.view.frame.size.height - datePicker.frame.size.height/2)];
[UIView commitAnimations];
}
I found an example that appears to be working BUT I don't see any dialog or buttons in my alert box, just the date picker itself. Am I missing something here?
- (IBAction)showDatePicker {
CGRect frame = CGRectMake(225, self.view.frame.size.height, self.view.frame.size.width, 125);
UIDatePicker *datePicker = [[UIDatePicker alloc] initWithFrame:frame];
datePicker = [[UIDatePicker alloc] init];
datePicker.datePickerMode = UIDatePickerModeDate;
[datePicker setDate:[NSDate date]];
UIAlertView *alert;
alert = [[UIAlertView alloc]
initWithTitle:#"Enter the Code Date"
message:#"Sample message"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Update", nil];
alert.delegate = self;
[alert addSubview:datePicker];
[alert show];
}
In Swift:
let myDatePicker: UIDatePicker = UIDatePicker()
// setting properties of the datePicker
myDatePicker.timeZone = NSTimeZone.localTimeZone()
myDatePicker.frame = CGRectMake(0, 15, 270, 200)
let alertController = UIAlertController(title: "\n\n\n\n\n\n\n\n", message: nil, preferredStyle: UIAlertControllerStyle.Alert)
alertController.view.addSubview(myDatePicker)
let somethingAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
alertController.addAction(somethingAction)
alertController.addAction(cancelAction)
self.presentViewController(alertController, animated: true, completion:{})
I agree with the commenters who have been saying this is not the way to accomplish this, for a couple reasons. First, no-one likes having alert-views constantly popping up. Second, and not applicable to your situation, It might eventually cause an app to be rejected. Apple has changed the wording in the UIAlertView class reference referring to its view hierarchy as private; And we all know how apple feels about you mucking about in what they consider private.
From UIAlertView class reference:
The UIAlertView class is intended to be used as-is and does not
support subclassing. The view hierarchy for this class is private and
must not be modified.
But since you say this is a private app, here goes. A UIAlertView is just a UIView subclass. So all of the rules of frames and bounds still apply. Here's a basic sample of how to adjust the frame of the picker and bounds of the alert to squeeze in the date picker.
// Create alert
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Hello" message:#"message" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
// Show alert (required for sizes to be available)
[alert show];
// Create date picker (could / should be an ivar)
UIDatePicker *picker = [[UIDatePicker alloc] initWithFrame:CGRectMake(10, alert.bounds.size.height, 320, 216)];
// Add picker to alert
[alert addSubview:picker];
// Adjust the alerts bounds
alert.bounds = CGRectMake(0, 0, 320 + 20, alert.bounds.size.height + 216 + 20);
EDIT
As has been noted by both comments and new answers to many of the 'how do I add some view to a UIAlertView' questions, this method no longer works in iOS7 and above. This should really be taken as evidence that this is a fundamentally bad idea.
Also note the usage of setValue:forKey: in the generally circulated "solution" to the "problem" of not being able to modify Apple's private view hierarchy. setValue:forKey: is one of those methods that draws the interest of the private API scanners. Go search for accessoryView in the UIAlertView documentation and headers. It's not in the docs. The only related item in the headers is an ivar named _accessoryView which is marked as #private.
Again for an in-house or private app I suppose it's okay, just okay though.
You're not going to like this answer, but my suggestion is: don't do that! UIAlertView is not intended for you to add your own subviews; examples that you find of people doing that are examples of wrong behavior. Create your own view containing the picker, text, buttons, etc. that you want, and present it somehow. For example you could present it modally via its own view controller, e.g. with presentModalViewController:animated: or presentViewController:animated:completion:. If you're on an iPad, you could use a popover (it can be modal if that's important).
This is what worked for me (using iOS7). Call this before [alertView show]:
[alertView setValue:customContentView forKey:#"accessoryView"];
Example:
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"TEST" message:#"subview" delegate:nil cancelButtonTitle:#"NO" otherButtonTitles:#"YES", nil];
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
v.backgroundColor = [UIColor yellowColor];
[av setValue:v forKey:#"accessoryView"];
[av show];
Source: iOS 7 UIDatePicker in UIAlertView customisation
This is working for me in ios7 and above.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Select Date" message:#"" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
UIDatePicker *picker = [[UIDatePicker alloc] initWithFrame:CGRectMake(10, alert.bounds.size.height, 320, 216)];
[alert addSubview:picker];
alert.bounds = CGRectMake(0, 0, 320 + 20, alert.bounds.size.height + 216 + 20);
[alert setValue:picker forKey:#"accessoryView"];
[alert show];
Swift 5 version of #Matheus Domingos answer
let myDatePicker: UIDatePicker = UIDatePicker()
myDatePicker.timeZone = NSTimeZone.local
myDatePicker.frame = CGRect(x: 0, y: 15, width: 270, height: 200)
let alertController = UIAlertController(title: "\n\n\n\n\n\n\n\n", message: nil, preferredStyle: UIAlertController.Style.alert)
alertController.view.addSubview(myDatePicker)
let somethingAction = UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: nil)
alertController.addAction(somethingAction)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion:{})
Swift 5.2
let myDatePicker: UIDatePicker = UIDatePicker()
myDatePicker.timeZone = NSTimeZone.local
myDatePicker.frame = CGRect(x: 0, y: 15, width: 270, height: 200)
let alertController = UIAlertController(title: "\n\n\n\n\n\n\n\n", message: nil, preferredStyle: UIAlertController.Style.alert)
alertController.view.addSubview(myDatePicker)
let somethingAction = UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: nil)
alertController.addAction(somethingAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: {})
Heres my UIViewController Extension Solution.
typealias DatePickerHandler = (_ success: Bool, _ date:Date) -> Void
typealias TimePickerHandler = (_ success: Bool, _ date:Date) -> Void
extension UIViewController{
func showDatePicker(completionHandler: #escaping DatePickerHandler){
let vc = UIViewController()
vc.preferredContentSize = CGSize(width: 250,height: 300)
let pickerView = UIDatePicker(frame: CGRect(x: 0, y: 0, width: vc.view.bounds.width, height: 300))
pickerView.datePickerMode = UIDatePicker.Mode.date
if #available(iOS 13.4, *) {
pickerView.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions
}
vc.view.addSubview(pickerView)
NSLayoutConstraint.activate([
pickerView.leadingAnchor.constraint(equalTo: vc.view.leadingAnchor,constant: 8),
pickerView.trailingAnchor.constraint(equalTo: vc.view.trailingAnchor,constant: 8),
pickerView.topAnchor.constraint(equalTo: vc.view.topAnchor,constant: 8),
pickerView.bottomAnchor.constraint(equalTo: vc.view.bottomAnchor,constant: 8),
])
let editRadiusAlert = UIAlertController(title: "Select Date", message: "", preferredStyle: UIAlertController.Style.actionSheet)
editRadiusAlert.setValue(vc, forKey: "contentViewController")
editRadiusAlert.addAction(UIAlertAction(title: "Select", style: .default, handler: {action in
completionHandler(true,pickerView.date)
}))
editRadiusAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: {action in
completionHandler(false,Date())
}))
self.present(editRadiusAlert, animated: true)
}
func showTimePicker(completionHandler: #escaping TimePickerHandler){
let vc = UIViewController()
vc.preferredContentSize = CGSize(width: 250,height: 300)
let pickerView = UIDatePicker(frame: CGRect(x: 0, y: 0, width: vc.view.bounds.width, height: 300))
pickerView.datePickerMode = UIDatePicker.Mode.time
if #available(iOS 13.4, *) {
pickerView.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions
}
vc.view.addSubview(pickerView)
NSLayoutConstraint.activate([
pickerView.leadingAnchor.constraint(equalTo: vc.view.leadingAnchor,constant: 8),
pickerView.trailingAnchor.constraint(equalTo: vc.view.trailingAnchor,constant: 8),
pickerView.topAnchor.constraint(equalTo: vc.view.topAnchor,constant: 8),
pickerView.bottomAnchor.constraint(equalTo: vc.view.bottomAnchor,constant: 8),
])
let editRadiusAlert = UIAlertController(title: "Select Time", message: "", preferredStyle: UIAlertController.Style.actionSheet)
editRadiusAlert.setValue(vc, forKey: "contentViewController")
editRadiusAlert.addAction(UIAlertAction(title: "Select", style: .default, handler: {action in
completionHandler(true,pickerView.date)
}))
editRadiusAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: {action in
completionHandler(false,Date())
}))
self.present(editRadiusAlert, animated: true)
}
}
Here how to used it in UIViewController
showDatePicker { (isSuccess, date) in
if(isSuccess){
}
}
Happy Coding!