web-dev-qa-db-de.com

UIAlertController - Hinzufügen von benutzerdefinierten Ansichten zum Aktionsblatt

Ich versuche, das Aktionsblatt so zu erstellen, wie es in der Nachrichten-App unter iOS angezeigt wird, wenn wir versuchen, ein Bild wie im Screenshot anzuhängen.

Ich habe festgestellt, dass in UIAlertController keine benutzerdefinierten Ansichten angepasst werden können. Irgendwie kann ich genau das machen?

Mein Code sieht ziemlich normal aus.

    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)

enter image description here

40
CalZone

UIAlertController erweitert UIViewController, der über eine Ansichtseigenschaft verfügt. Sie können dieser Ansicht nach Herzenslust Unteransichten hinzufügen. Das einzige Problem ist die richtige Dimensionierung des Alarm-Controllers. Sie könnten so etwas tun, aber dies könnte beim nächsten Mal leicht scheitern Apple passt das Design von UIAlertController an.

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:{})
    }

Swift

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:{})

Ziel-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:^{}];

Abgesehen davon wäre es viel weniger schwierig, eine eigene Ansichts-Unterklasse zu erstellen, die ähnlich wie das UIAlertActionStyle-Layout von UIAlertController funktioniert. In der Tat sieht derselbe Code in iOS 8 und iOS 9 etwas anders aus.

iOS 8 enter image description here

iOS 9 enter image description here

iOS 10 enter image description here

82
Keller

Die sauberste Lösung, die ich bisher mit AutoLayout-Einschränkungen gefunden habe:

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)
}

Ausgabe:

enter image description here

16
Cesare

Ich habe eine Erweiterung für UIAlertController geschrieben (in Swift 4), die die Layoutprobleme mit Autolayout behebt. Es gibt sogar eine Ersatznachrichtenzeichenfolge, falls etwas nicht funktioniert (aufgrund zukünftiger Änderungen im UIAlertController) Layout).

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
    }
}

Und hier ist ein Anwendungsbeispiel:

// 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)

Welches wird so etwas zeigen: enter image description here

13
Zaggo

Für die Faulen die Swift 3.0 und iOS> = 9 optimierte Version von @ Kellers Antwort:

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:{})
4
palme

Ich habe versucht, die Einschränkungen von UIAlertController zu umgehen, aber egal wie ich es schaffte, es war nie gut genug. Wenn Sie immer noch Probleme damit haben, habe ich eine Bibliothek erstellt, die möglicherweise hilfreich ist. Hiermit können Sie benutzerdefinierte Blätter mit einer Reihe von integrierten Typen erstellen. Es kann auch erweitert und neu gestaltet werden.

3
Daniel Saidi