ios Swift:尝试从我的应用程序打开 safari 中的 URL 时出现“正在拍摄尚未呈现的视图的快照..”错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30685379/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 06:14:51  来源:igfitidea点击:

Swift: Getting 'Snapshotting a view that has not been rendered..' error when trying to open a URL in safari from my app

iosuitableviewswift

提问by mayankk2308

One of the specifications of my app is that on tapping a tableView cell, the user will be redirected to the website associated with the cell. Here is the code:

我的应用程序的规范之一是,在点击 tableView 单元格时,用户将被重定向到与该单元格关联的网站。这是代码:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    if let url = NSURL(string: appdelegate.studentInfo[indexPath.row].url) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
        UIApplication.sharedApplication().openURL(url)
    }
    else {
        let alert = UIAlertController(title: "Invalid URL", message: "Cannot open URL because it is invalid.", preferredStyle: UIAlertControllerStyle.Alert)
        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: nil))
        self.presentViewController(alert, animated: true, completion: nil)
    } 
}

On my first tap, the URL opens like it is supposed to. However, returning to the app from Safari and touching another cell results in the following error, although the app still works like it is supposed to:

在我第一次点击时,URL 会像预期的那样打开。但是,从 Safari 返回应用程序并触摸另一个单元格会导致以下错误,尽管该应用程序仍然可以正常工作:

Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates.

对尚未渲染的视图进行快照会生成空快照。确保您的视图在屏幕更新之前或快照之前至少渲染过一次。

Is there any way to avoid this error? Or is this a bug?

有什么办法可以避免这个错误吗?或者这是一个错误?

I have experimented with dispatch_async blocks but it did not solve the problem.

我已经尝试过 dispatch_async 块,但它没有解决问题。

回答by Saliom

It might not be the same problem as me, but I just solved the same warning in my logs.

这可能与我的问题不同,但我刚刚解决了日志中相同的警告。

I'm showing a UIAlertControlleras an actionSheet popover on an iPad, and I had exactly the same warning 8 times in a row every time I tried to show the alert controller.

我在UIAlertControlleriPad 上显示一个actionSheet 弹出框,每次我尝试显示警报控制器时,我都会连续 8 次收到完全相同的警告。

To make the warning disappear all I had to do was to layout the alert controller view as in the following code:

为了使警告消失,我所要做的就是按照以下代码布局警报控制器视图:

let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)

    ...            

alertController.view.layoutIfNeeded() //avoid Snapshotting error
self.presentViewController(alertController, animated: true, completion: nil)

I hope this helps you or any other person having the same warning.

我希望这可以帮助您或任何其他有相同警告的人。

回答by Patrick

Also using Objective-C, using the suggested [modeAlert.view layoutIfNeeded]reduced errors to one as above. Final error has been suppressed by changing last addAction from UIAlertActionStyleCancelto UIAlertActionStyleDefaultas below. Not a great workaround to what appears to be a bug.

同样使用Objective-C,使用建议的[modeAlert.view layoutIfNeeded]将错误减少到一个如上所述。通过将最后一个 addAction 从UIAlertActionStyleCancel更改UIAlertActionStyleDefault为如下,最终错误已被抑制。对于似乎是错误的内容,这不是一个很好的解决方法。

[modeAlert addAction:[UIAlertAction
                  actionWithTitle:NSLocalizedString(@"Cancel", @"")
                  style:UIAlertActionStyleDefault
                  handler:nil ]];

回答by Jerome

I think the last warning came from the Cancel button.

我认为最后一个警告来自“取消”按钮。

On iOS8 the cancel button is shown only when needed. If you run the app on iPhone it is visible. If you run the app on iPad the cancel button is not shown and the handler for the cancel action (style:UIAlertActionStyleCancel) is called when the user taps outside the popup.

在 iOS8 上,取消按钮仅在需要时显示。如果您在 iPhone 上运行该应用程序,它是可见的。如果您在 iPad 上运行该应用程序,则不会显示取消按钮,并且当用户点击弹出窗口外时会调用取消操作的处理程序 (style:UIAlertActionStyleCancel)。

the answer come from: amalicka's answer

答案来自:amalicka's answer

回答by Gleb Tarasov

To avoid copy/paste from Saliom's answeryou can make such subclass and use it instead of UIAlertController:

为了避免从Saliom 的答案中复制/粘贴,您可以创建这样的子类并使用它而不是UIAlertController

class AlertController: UIAlertController {

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.view.layoutIfNeeded()
    }

}

回答by RyanP

I was getting a similar debug warning when trying to present a QLPreviewController modally. I've read on other posts that it's an Xcode bug.

尝试以模态方式呈现 QLPreviewController 时,我收到了类似的调试警告。我在其他帖子中读到这是一个 Xcode 错误。

For me the error displayed when I ran my app on the simulator but not on an actual device. Must be an Xcode/Simulator issue. Hope that helps.

对我来说,当我在模拟器上而不是在实际设备上运行我的应用程序时显示的错误。必须是 Xcode/模拟器问题。希望有帮助。

回答by alex02rt

I was facing the similar problem (same warning XD).

我面临着类似的问题(同样的警告 XD)。

Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates.

对尚未渲染的视图进行快照会生成空快照。确保您的视图在屏幕更新之前或快照之前至少渲染过一次。


My Xcode version is 9.4.1, and everything was perfect until I add resignFirstResponder()in textFieldShouldReturn(_ textField: UITextField).


我的 Xcode 版本是9.4.1,一切都很完美,直到我添加resignFirstResponder()textFieldShouldReturn(_ textField: UITextField).



Here was my situation:

这是我的情况:

When I touched UITextFieldwith textin the UITableViewCell, It will show the warning.

当我触摸UITextField中的文本UITableViewCell,它会显示警告。

So I changed the way to trigger UITextField(Edit version). It never show the warning again.

所以我改变了触发方式UITextFieldEdit version)。它永远不会再次显示警告。

In conclusion, I still don't know why this happened, but what I can do is avoid the warning, hope It will help someone :D


总之,我仍然不知道为什么会发生这种情况,但我能做的就是避免警告,希望它会帮助某人:D




Edited code:

编辑的代码:

class TableViewCell: UITableViewCell {
    // add this in TableViewCell
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        answerTextField.isEnabled = selected
        if selected {
            answerTextField.becomeFirstResponder()
        } else {
            answerTextField.resignFirstResponder()
        }
    }
}



Original code:

原始代码:

class ViewController: UIViewController {

    private let tableView: UITableView = {
        let t = UITableView()
        t.separatorStyle = .none
        t.backgroundColor = .clear
        return t
    }()

    private let cellId = "Cell"

    private let data = [
        Model(title: "A", answer: "a"),
        Model(title: "B", answer: "b"),
        Model(title: "C", answer: nil),
        Model(title: "D", answer: nil)
    ]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(TableViewCell.self, forCellReuseIdentifier: cellId)
        view.addSubview(tableView)
        tableView.frame = view.frame
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 3
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        if let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? TableViewCell {
            cell.contentView.backgroundColor = indexPath.section % 2 == 0 ? .gray : .white
            cell.setData(data[indexPath.row])
            return cell
        }
        return UITableViewCell()
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }
}

class TableViewCell: UITableViewCell {

    private let titleLabel = UILabel()
    private let answerTextField = UITextField()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupViews()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupViews()
    }

    func setupViews() {
        setupTitleLabels()
    }

    func setData(_ data: Model) {
        titleLabel.text = data.title
        answerTextField.text = data.answer
    }

    private func setupTitleLabels() {
        answerTextField.delegate = self

        let sv = UIStackView(arrangedSubviews: [titleLabel, answerTextField])
        sv.axis = .vertical
        sv.spacing = 0
        sv.distribution = .fill

        contentView.addSubview(sv)
        sv.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        sv.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        sv.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
        sv.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
    }
}

extension TableViewCell: UITextFieldDelegate {
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }
}

struct Model {
    let title: String
    var answer: String?
    init(title: String, answer: String? = nil) {
        self.title = title
        self.answer = answer
    }
}

回答by Heath Borders

I'm triggering a UIAlertControllerStyleActionSheetUIAlertControllerafter the user clicks a UITableViewRowAction, and I got the same error message repeated 8 times after presenting the UIAlertController.

UIAlertControllerStyleActionSheetUIAlertController在用户单击a后触发 a UITableViewRowAction,并且在呈现UIAlertController.

On the iPad Pro Simulator for iOS 9.3, I tried Saliom's answer, and I went from eight log messages to one.

在适用于 iOS 9.3 的 iPad Pro Simulator 上,我尝试了Saliom 的回答,我从 8 条日志消息变成了 1 条日志消息。

As anorskdev suggested, I put my call to -[UIView layoutIfNeeded]after my -[UIViewController presentViewController:animated:completion:]call, and all the warnings disappeared:

正如anorskdev 建议的那样,我在通话-[UIView layoutIfNeeded]-[UIViewController presentViewController:animated:completion:]拨通了电话,所有警告都消失了:

- (NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{
  UITableViewRowAction *moreAction =
  [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault
                                     title:@"More"
                                   handler:^(UITableViewRowAction * _Nonnull action,
                                             NSIndexPath * _Nonnull moreIndexPath)
  {
    UIAlertController *alertController = 
    [UIAlertController alertControllerWithTitle:name 
                                        message:nil
                                 preferredStyle:UIAlertControllerStyleActionSheet];
    UIAlertAction *deleteAlertAction = 
    [UIAlertAction actionWithTitle:@"Delete"
                             style:UIAlertActionStyleDestructive
                           handler:deleteAction];
    [alertController addAction:deleteAlertAction];

    UIAlertAction *cancelAlertAction = 
    [UIAlertAction actionWithTitle:@"Cancel"
                             // totally ok to use the proper
                             // UIAlertActionStyleCancel
                             style:UIAlertActionStyleCancel
                           handler:cancelAction];
    [alertController addAction:cancelAlertAction];

    CGRect sourceRect = [self.tableView rectForRowAtIndexPath:moreIndexPath];

    // You must specify a sourceRect and sourceView
    // or a barButtonItem or presenting a
    // UIAlertControllerStyleActionSheet UIAlertController
    // will crash on iPad.
    alertController.popoverPresentationController.sourceRect = sourceRect;
    alertController.popoverPresentationController.sourceView = self.tableView;
    // first, present the alertController
    [self presentViewController:alertController
                       animated:YES
                     completion:nil];
    // then -layoutIfNeeded
    [alertController.view layoutIfNeeded];
  }
  return @[
          moreAction,
          ];
}

Notice that it wasn't necessary to use Patrick's solutionof using UIAlertActionStyleDefaultfor cancelAlertActionor nurider's solutionof eliminating it altogether on iPads.

请注意,没有必要在 iPad 上使用Patrick 的using UIAlertActionStyleDefaultfor解决方案cancelAlertActionnurider 的完全消除它的解决方案

回答by nurider

I had the same problem and found a simple solution that makes sense.

我遇到了同样的问题,并找到了一个有意义的简单解决方案。

If it's an iPad ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) then do NOTadd the UIAlertActionwith style UIAlertActionStyleCancelto the UIAlertController.

如果它是一台iPad( [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad),那么就不要添加UIAlertAction与风格UIAlertActionStyleCancelUIAlertController

I made this change and my errors went away. This solution makes sense since you don't need a cancel action for alerts with style UIAlertControllerStyleActionSheeton an iPad.

我做了这个改变,我的错误就消失了。此解决方案很有意义,因为您不需要UIAlertControllerStyleActionSheet对 iPad 上的样式警报执行取消操作。