【问题标题】:UIAlertController - add custom views to actionsheetUIAlertController - 将自定义视图添加到操作表
【发布时间】:2015-12-23 18:14:24
【问题描述】:

当我们尝试在屏幕截图中附加图像时,我正在尝试制作操作表,就像它在 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)

【问题讨论】:

    标签: swift ios9 uialertcontroller


    【解决方案1】:

    UIAlertController 扩展了 UIViewController,它有一个 view 属性。您可以根据自己的意愿向该视图添加子视图。唯一的麻烦是正确调整警报控制器的大小。你可以做这样的事情,但这很容易在下次 Apple 调整 UIAlertController 的设计时中断。

    斯威夫特 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:{})
        }
    

    斯威夫特

    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

    【讨论】:

    • 这太棒了,真的很有帮助,我仍然在边缘的绿色视图后面看到一条线。我想我会为此在绿色视图周围放置一个容器视图。 0 边距。
    • 谢谢,伙计。这太棒了。只是稍微调整了自定义视图的宽度,但一切都很好!干杯。
    • 我的 Objective C 答案仍然可以正常工作。 Swift 答案使用旧语法,但现在已针对 Swift 3.0 进行了更新,感谢@palme。
    • 这就是为什么我在回答中提到“一种更简单的方法是创建自己的视图子类,其工作方式类似于 UIAlertController 的 UIAlertActionStyle 布局。事实上,相同的代码在 iOS 8 中看起来略有不同和 iOS 9。”问题是将子视图添加到 UIAlertController,这是公认的答案。
    • 我相信 UIAlertController 的初始帧大小与 UIView 相同。在 iPhone 上,上面的代码可以正常工作,因为 alertController 占据了设备的整个宽度。在 iPad 上调整了 alertController 的大小。要使子视图自动调整大小,请设置调整大小掩码 customView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
    【解决方案2】:

    到目前为止我发现的最干净的解决方案是使用 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)
    }
    

    输出:

    【讨论】:

    • 这是最好的答案 - 不是被“挑选”的答案我很高兴有人正确地做到了这一点:-)
    • 关于该主题的最佳答案。想知道为什么这里不接受这个答案。
    • 如果对话框在某些情况下有标题,它会中断。根据标题长度和用户字体大小偏好,标题可以与自定义视图重叠。应计算顶部填充。没有太多ios经验,否则会发布解决方案。
    【解决方案3】:

    我为 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)
    

    这将显示如下内容:

    【讨论】:

    • 这很好用。只是想知道这是否会在审核过程中被拒绝,有什么想法吗?
    • 没有涉及私有API,所以我不知道为什么Apple应该拒绝它。
    • 只面临一个问题,customView 显示为灰色而不是红色。有什么想法吗??
    • 这看起来是一个不错的、干净的解决方案,但我认为人们强烈建议不要扩展 UIAlertController,因为 Apple 不希望你这样做,并且可能会在未来的更新中破坏你的“黑客”。这个解决方案可能仍然是这种情况吗?
    • 我试图尽可能优雅地处理任何失败。如果 Apple 进行任何结构性更改,最坏的结果应该是警报不会显示自定义视图,而是仅显示后备文本消息,这正是针对这种情况的 API 的一部分。
    【解决方案4】:

    对于懒人来说,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:{})
    

    【讨论】:

      【解决方案5】:

      我试图解决UIAlertController 的限制,但无论我如何管理,它都不够好。如果您仍在为此苦苦挣扎,我创建了a library,这可能会有所帮助。它使您可以使用一堆内置类型创建自定义工作表。它也可以扩展和重新设置样式。

      【讨论】:

        【解决方案6】:

        这是 @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];
        }
        

        【讨论】:

          猜你喜欢
          • 2018-05-17
          • 2018-04-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-16
          • 1970-01-01
          相关资源
          最近更新 更多