UIAlertController – aggiungi viste personalizzate al foglio delle azioni

Sto provando a creare il foglio delle azioni come mostrato nell’app dei messaggi su iOS quando proviamo ad albind un’immagine come nell’immagine.

Ho realizzato in UIAlertController nuovo, non possiamo adattare alcuna vista personalizzata. Qualunque modo posso fare esattamente questo?

Il mio codice sembra piuttosto standard.

let alertController = UIAlertController(title: "My AlertController", message: "tryna show some images here man", preferredStyle: UIAlertControllerStyle.ActionSheet) let okAction = UIAlertAction(title: "oks", style: .Default) { (action: UIAlertAction) -> Void in alertController.dismissViewControllerAnimated(true, completion: nil) } let cancelAction = UIAlertAction(title: "Screw it!", style: .Cancel) { (action: UIAlertAction) -> Void in alertController.dismissViewControllerAnimated(true, completion: nil) } alertController.addAction(okAction) alertController.addAction(cancelAction) self.presentViewController(alertController, animated: true, completion: nil) 

inserisci la descrizione dell'immagine qui

UIAlertController estende UIViewController, che ha una proprietà view. Puoi aggiungere sottoview a quella vista secondo il desiderio del tuo cuore. L’unico problema è dimensionare correttamente il controller degli avvisi. Potresti fare qualcosa di simile, ma questo potrebbe facilmente rompersi la volta successiva che Apple aggiusta il design di UIAlertController.

Swift 3

  let alertController = UIAlertController(title: "\n\n\n\n\n\n", message: nil, preferredStyle: UIAlertControllerStyle.actionSheet) let margin:CGFloat = 10.0 let rect = CGRect(x: margin, y: margin, width: alertController.view.bounds.size.width - margin * 4.0, height: 120) let customView = UIView(frame: rect) customView.backgroundColor = .green alertController.view.addSubview(customView) let somethingAction = UIAlertAction(title: "Something", style: .default, handler: {(alert: UIAlertAction!) in print("something")}) let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: {(alert: UIAlertAction!) in print("cancel")}) alertController.addAction(somethingAction) alertController.addAction(cancelAction) DispatchQueue.main.async { self.present(alertController, animated: true, completion:{}) } 

veloce

 let alertController = UIAlertController(title: "\n\n\n\n\n\n", message: nil, preferredStyle: UIAlertControllerStyle.actionSheet) let margin:CGFloat = 10.0 let rect = CGRect(x: margin, y: margin, width: alertController.view.bounds.size.width - margin * 4.0, height: 120) let customView = UIView(frame: rect) customView.backgroundColor = .green alertController.view.addSubview(customView) let somethingAction = UIAlertAction(title: "Something", style: .default, handler: {(alert: UIAlertAction!) in print("something")}) let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: {(alert: UIAlertAction!) in print("cancel")}) alertController.addAction(somethingAction) alertController.addAction(cancelAction) self.present(alertController, animated: true, completion:{}) 

Objective-C

  UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"\n\n\n\n\n\n" message:nil preferredStyle:UIAlertControllerStyleActionSheet]; CGFloat margin = 8.0F; UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(margin, margin, alertController.view.bounds.size.width - margin * 4.0F, 100.0F)]; customView.backgroundColor = [UIColor greenColor]; [alertController.view addSubview:customView]; UIAlertAction *somethingAction = [UIAlertAction actionWithTitle:@"Something" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {}]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {}]; [alertController addAction:somethingAction]; [alertController addAction:cancelAction]; [self presentViewController:alertController animated:YES completion:^{}]; 

Detto questo, un approccio molto meno hacky sarebbe quello di creare una propria sottoclass di viste che funzioni in modo simile al layout UIAlertActionStyle di UIAlertController. In effetti, lo stesso codice sembra leggermente diverso in iOS 8 e iOS 9.

iOS 8 inserisci la descrizione dell'immagine qui

iOS 9 inserisci la descrizione dell'immagine qui

iOS 10 inserisci la descrizione dell'immagine qui

Ho scritto un’estensione per UIAlertController (in Swift 4), che risolve i problemi di layout con l’autolayout. C’è anche una stringa di messaggi di riserva nel caso in cui qualcosa non funzioni (a causa di cambiamenti futuri nel layout di UIAlertController).

 import Foundation extension UIAlertController { /// Creates a `UIAlertController` with a custom `UIView` instead the message text. /// - Note: In case anything goes wrong during replacing the message string with the custom view, a fallback message will /// be used as normal message string. /// /// - Parameters: /// - title: The title text of the alert controller /// - customView: A `UIView` which will be displayed in place of the message string. /// - fallbackMessage: An optional fallback message string, which will be displayed in case something went wrong with inserting the custom view. /// - preferredStyle: The preferred style of the `UIAlertController`. convenience init(title: String?, customView: UIView, fallbackMessage: String?, preferredStyle: UIAlertControllerStyle) { let marker = "__CUSTOM_CONTENT_MARKER__" self.init(title: title, message: marker, preferredStyle: preferredStyle) // Try to find the message label in the alert controller's view hierarchie if let customContentPlaceholder = self.view.findLabel(withText: marker), let customContainer = customContentPlaceholder.superview { // The message label was found. Add the custom view over it and fix the autolayout... customContainer.addSubview(customView) customView.translatesAutoresizingMaskIntoConstraints = false customContainer.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[customView]-|", options: [], metrics: nil, views: ["customView": customView])) customContainer.addConstraint(NSLayoutConstraint(item: customContentPlaceholder, attribute: .top, relatedBy: .equal, toItem: customView, attribute: .top, multiplier: 1, constant: 0)) customContainer.addConstraint(NSLayoutConstraint(item: customContentPlaceholder, attribute: .height, relatedBy: .equal, toItem: customView, attribute: .height, multiplier: 1, constant: 0)) customContentPlaceholder.text = "" } else { // In case something fishy is going on, fall back to the standard behaviour and display a fallback message string self.message = fallbackMessage } } } private extension UIView { /// Searches a `UILabel` with the given text in the view's subviews hierarchy. /// /// - Parameter text: The label text to search /// - Returns: A `UILabel` in the view's subview hierarchy, containing the searched text or `nil` if no `UILabel` was found. func findLabel(withText text: String) -> UILabel? { if let label = self as? UILabel, label.text == text { return label } for subview in self.subviews { if let found = subview.findLabel(withText: text) { return found } } return nil } } 

Ed ecco un esempio di utilizzo:

 // Create a custom view for testing... let customView = UIView() customView.translatesAutoresizingMaskIntoConstraints = false customView.backgroundColor = .red // Set the custom view to a fixed height. In a real world application, you could use autolayouted content for height constraints customView.addConstraint(NSLayoutConstraint(item: customView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 100)) // Create the alert and show it let alert = UIAlertController(title: "Alert Title", customView: customView, fallbackMessage: "This should be a red rectangle", preferredStyle: .actionSheet) alert.addAction(UIAlertAction(title: "Yay!", style: .default, handler: nil)) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) self.present(alert, animated: true, completion: nil) 

Che mostrerà qualcosa di simile a questo: inserisci la descrizione dell'immagine qui

Per i più pigri, la versione ottimizzata di Swift 3.0 e iOS> = 9 della risposta di @ Keller:

 let alertController = UIAlertController(title: "\n\n\n\n\n\n", message: nil, preferredStyle: UIAlertControllerStyle.actionSheet) let margin:CGFloat = 10.0 let rect = CGRect(x: margin, y: margin, width: alertController.view.bounds.size.width - margin * 4.0, height: 120) let customView = UIView(frame: rect) customView.backgroundColor = .green alertController.view.addSubview(customView) let somethingAction = UIAlertAction(title: "Something", style: .default, handler: {(alert: UIAlertAction!) in print("something")}) let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: {(alert: UIAlertAction!) in print("cancel")}) alertController.addAction(somethingAction) alertController.addAction(cancelAction) self.present(alertController, animated: true, completion:{}) 

La soluzione più pulita che ho trovato finora usando i vincoli AutoLayout:

 internal func showPickerController() { let alertController = UIAlertController(title: "Translation Language", message: nil, preferredStyle: .actionSheet) let customView = UIView() alertController.view.addSubview(customView) customView.translatesAutoresizingMaskIntoConstraints = false customView.topAnchor.constraint(equalTo: alertController.view.topAnchor, constant: 45).isActive = true customView.rightAnchor.constraint(equalTo: alertController.view.rightAnchor, constant: -10).isActive = true customView.leftAnchor.constraint(equalTo: alertController.view.leftAnchor, constant: 10).isActive = true customView.heightAnchor.constraint(equalToConstant: 250).isActive = true alertController.view.translatesAutoresizingMaskIntoConstraints = false alertController.view.heightAnchor.constraint(equalToConstant: 430).isActive = true customView.backgroundColor = .green let selectAction = UIAlertAction(title: "Select", style: .default) { (action) in print("selection") } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) alertController.addAction(selectAction) alertController.addAction(cancelAction) self.present(alertController, animated: true, completion: nil) } 

Ho cercato di aggirare le limitazioni di UIAlertController , ma non importa quanto bene sia riuscito, non è mai stato abbastanza buono.

Ho quindi creato una libreria Swift che ti consente di creare fogli di azione personalizzati. Ha un supporto integrato per molti articoli diversi (anche la raccolta orizzontale che hai elencato) e può essere esteso e ridisegnato.

Forse potrebbe aiutarti a realizzare ciò che stai cercando di fare? Trovi la biblioteca su https://github.com/danielsaidi/Sheeeeeeeeet