UIAlertController - 将自定义视图添加到操作表
UIAlertController - add custom views to actionsheet
当我们尝试附加截图中的图像时,我正在尝试制作 iOS 消息应用程序中显示的操作表。
我意识到在新的 UIAlertController 中,我们无法适应任何自定义视图。我有什么办法可以做到这一点?
我的代码看起来很标准。
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)
UIAlertController 扩展了 UIViewController,它有一个视图 属性。您可以根据自己的意愿向该视图添加子视图。唯一的问题是正确调整警报控制器的大小。您可以这样做,但是下次 Apple 调整 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:{})
}
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:{})
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:^{}];
话虽这么说,但一种更简单的方法是制作您自己的视图子类,其工作方式类似于 UIAlertController 的 UIAlertActionStyle 布局。事实上,相同的代码在 iOS 8 和 iOS 9 中看起来略有不同。
iOS 8
iOS 9
iOS 10
对于懒人来说 Swift 3.0 和 iOS >= 9 @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:{})
我为 UIAlertController 写了一个扩展(在 Swift 4),它解决了自动布局的布局问题。甚至还有一个回退消息字符串,以防某些东西不起作用(由于 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: UIAlertController.Style) {
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
}
}
这是一个用法示例:
// 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)
这将显示如下内容:
我尝试解决 UIAlertController
的限制,但无论我如何解决,都不够好。如果您仍在为此苦苦挣扎,我创建的 a library 可能会对您有所帮助。它允许您创建具有一堆 built-in 类型的自定义工作表。它也可以扩展和重新设计。
迄今为止我发现的使用 AutoLayout 约束的最干净的解决方案:
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)
}
输出:
这是@Cesare 解决方案的Objective-C 版本
- (void) showPickerController {
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"Translation Language" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIView *customView = [[UIView alloc] init];
[alertController.view addSubview:customView];
customView.translatesAutoresizingMaskIntoConstraints = NO;
[customView.topAnchor constraintEqualToAnchor:alertController.view.topAnchor constant:45].active = YES;
[customView.rightAnchor constraintEqualToAnchor:alertController.view.rightAnchor constant:-10].active = YES;
[customView.leftAnchor constraintEqualToAnchor:alertController.view.leftAnchor constant:10].active = YES;
[customView.heightAnchor constraintEqualToConstant:250].active = YES;
alertController.view.translatesAutoresizingMaskIntoConstraints = NO;
[alertController.view.heightAnchor constraintEqualToConstant:430].active = YES;
customView.backgroundColor = [UIColor greenColor];
UIAlertAction* selectAction = [UIAlertAction actionWithTitle:@"Select" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
}];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
}];
[alertController addAction:selectAction];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}
当我们尝试附加截图中的图像时,我正在尝试制作 iOS 消息应用程序中显示的操作表。
我意识到在新的 UIAlertController 中,我们无法适应任何自定义视图。我有什么办法可以做到这一点?
我的代码看起来很标准。
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)
UIAlertController 扩展了 UIViewController,它有一个视图 属性。您可以根据自己的意愿向该视图添加子视图。唯一的问题是正确调整警报控制器的大小。您可以这样做,但是下次 Apple 调整 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:{})
}
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:{})
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:^{}];
话虽这么说,但一种更简单的方法是制作您自己的视图子类,其工作方式类似于 UIAlertController 的 UIAlertActionStyle 布局。事实上,相同的代码在 iOS 8 和 iOS 9 中看起来略有不同。
iOS 8
iOS 9
iOS 10
对于懒人来说 Swift 3.0 和 iOS >= 9 @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:{})
我为 UIAlertController 写了一个扩展(在 Swift 4),它解决了自动布局的布局问题。甚至还有一个回退消息字符串,以防某些东西不起作用(由于 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: UIAlertController.Style) {
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
}
}
这是一个用法示例:
// 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)
这将显示如下内容:
我尝试解决 UIAlertController
的限制,但无论我如何解决,都不够好。如果您仍在为此苦苦挣扎,我创建的 a library 可能会对您有所帮助。它允许您创建具有一堆 built-in 类型的自定义工作表。它也可以扩展和重新设计。
迄今为止我发现的使用 AutoLayout 约束的最干净的解决方案:
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)
}
输出:
这是@Cesare 解决方案的Objective-C 版本
- (void) showPickerController {
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"Translation Language" message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIView *customView = [[UIView alloc] init];
[alertController.view addSubview:customView];
customView.translatesAutoresizingMaskIntoConstraints = NO;
[customView.topAnchor constraintEqualToAnchor:alertController.view.topAnchor constant:45].active = YES;
[customView.rightAnchor constraintEqualToAnchor:alertController.view.rightAnchor constant:-10].active = YES;
[customView.leftAnchor constraintEqualToAnchor:alertController.view.leftAnchor constant:10].active = YES;
[customView.heightAnchor constraintEqualToConstant:250].active = YES;
alertController.view.translatesAutoresizingMaskIntoConstraints = NO;
[alertController.view.heightAnchor constraintEqualToConstant:430].active = YES;
customView.backgroundColor = [UIColor greenColor];
UIAlertAction* selectAction = [UIAlertAction actionWithTitle:@"Select" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
}];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
}];
[alertController addAction:selectAction];
[alertController addAction:cancelAction];
[self presentViewController:alertController animated:YES completion:nil];
}