Today Widget Extension 中经常出现“无法加载”

Posted

技术标签:

【中文标题】Today Widget Extension 中经常出现“无法加载”【英文标题】:Too often "Unable to load" in Today Widget Extension 【发布时间】:2015-01-28 14:55:39 【问题描述】:

我正在为我的应用制作一个 Today 小部件。我的小部件包含一个带有 10 个单元格的 UITableView。 (每个单元格的高度为 50pt。)功能很简单。如果我触摸单元格上的按钮,从 sqlite 重新加载 DB 并将它们显示在单元格上。它在模拟器和 iPhone 4s、5、5s、6 上运行良好,除了 iPhone6+。我确实删除了小部件并再次添加了 10 多次,但这对我没有帮助。我确实检查了内存和僵尸。但这稳定在~10M以下并且没有泄漏。我该如何解决我的问题?

【问题讨论】:

您需要更详细地描述您遇到的问题,例如更详细地描述您的代码并给出确切的错误消息。 【参考方案1】:

好的,伙计们,你们不喜欢我之前的回答。我会重试的。

考虑以下条件:

+-------------------+
| Table View        |
|+-----------------+|
|| Cell            ||
||+---------------+||
||| UILabel       |||
||+---------------+||
|+-----------------+|
+-------------------+

首先表格的逻辑宽度是:

在 iPhone 5 上为 320pt(纵向) 在 iPhone 6+ 上为 414pt

内容比例因子为:

0.5 适用于 iPhone 5 0.33 适用于 iPhone 6+

1pt x 1pt 的实际像素为:

对于 iPhone 5,(1/0.5)^2 = 4 适用于 iPhone 6+ (1/0.33)^2 = 9

那么,如果UILabel 适合整个设备宽度并且它的高度是 44(Apple 的 HIG 的最小可点击高度),那么实际像素是:

320 * 44 * 4 = iPhone 5 的 56,320 设备像素 414 * 44 * 9 = iPhone 6+ 的 163,944 个设备像素

因此,iPhone6+ 需要比 iPhone5 多 3 倍的缓冲区来绘制 UILabel。

但是,当小部件的内存使用量超过 10MB(经过无数次实验估计)时,就会发生内存错误。同样的限制适用于这两种设备。 Apple 未记录此限制。

请记住,在开发 ios8 扩展程序时,您只能使用 1% 的设备内存,以防止杀死后台应用程序。这也是目前支持照片编辑扩展的应用很少的主要原因。

无论如何,需要 UI 的扩展在 iPhone 6+ 上很容易崩溃,因为每个 UI 元素所需的内存量取决于大小和内容比例。

自定义绘图会导致相同的问题,因为它需要缓冲区来绘制以优化动画和渲染。并且缓冲区的分辨率和大小在 iPhone6+ 上要大得多。

此外,通知中心本身也存在错误(泄漏),即使只是 Hello World 小部件(带有 Xcode 模板)每次显示和隐藏都会不断增加内存消耗。当最终达到 10MB 时,它将崩溃并重新加载。这就是小部件有时闪烁的原因。如果某个小部件连续崩溃 3 次,iOS 会永久禁用该小部件并显示“无法加载”。

那么,在如此恶劣的条件下,我们能做些什么呢,我为这个问题制定了一些规则。

    不要使用实现[drawRect:] 的自定义视图,除非它非常小。 尽可能缩小视图(具有自己的内容,尤其是标签)。 不要使用背景图片。

点击空白区域选择单元格

对于 Widget 来说这是一个很常见的问题,它与内存错误有关,所以我也在写这个:

小部件层次结构中的所有视图都倾向于具有透明背景(UIClearColor),这意味着使单元格可点击非常困难。因为当用户触摸空白区域时,不会对小部件进行整个命中测试。 (自定义命中测试只有在至少有一个superview不透明的情况下才可以应用)有一些解决方案:

    使 UILabel 的宽度适合 Cell:不要,这会在 iPhone6+ 或更宽、更高分辨率的设备上消耗更多内存。 在自定义单元格上实现空[drawRect:]。不要,这是轻敲透明控件的非常简单的解决方案,但是它需要绘图缓冲区。请记住,每个设备的缓冲区大小会有所不同。

我可以让它在没有额外内存消耗的情况下工作的唯一解决方案是将小部件背景颜色设置为黑色,alpha 为 0.01。 (它使命中测试有效)

+-------------------+
| Table View        |- backgorund-color: (0, 0, 0, 0.01)
|+-----------------+|
|| Cell            ||
||+---------+      ||
||| UILabel +------++- make it small as possible as you can
||+---------+      ||
|+-----------------+|
+-------------------+

请记住,只有背景颜色的容器视图不会占用缓冲内存。

【讨论】:

【参考方案2】:

我喜欢jeeeyul answer's,但很多时候扩展程序会因为 self.preferredContentSize 设置不正确而崩溃(无法加载)。

我已经通过在 TodayViewController 类中放置一个变量“currentHeight”来解决它:

var currentHeight : CGFloat = 0.0
@IBOutlet var containerView: UIView! (the storyboard today extension viewcontroller view)

override func viewDidAppear(animated: Bool) 
        super.viewDidAppear(animated)
        ... (load custom xib inside my containerView)
        self.currentHeight = containerView.frame.size.height
        self.preferredContentSize = CGSizeMake(self.view.frame.size.width,currentHeight)
    

func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)) 
        // Perform any setup necessary in order to update the view.

        // If an error is encountered, use NCUpdateResult.Failed
        // If there's no update required, use NCUpdateResult.NoData
        // If there's an update, use NCUpdateResult.NewData
        ... (do whatever you want in order to update)
        self.preferredContentSize = CGSizeMake(self.view.frame.size.width,currentHeight)
        completionHandler(NCUpdateResult.NewData)
        ...
    

【讨论】:

【参考方案3】:

编辑扩展方案, 选择运行操作, 在信息部分, 将可执行文件设置为“启动时询问”

这似乎解决了问题,并且扩展程序每次都运行,不知道它是如何工作的,但这让我的工作顺利进行。

【讨论】:

【参考方案4】:

iPhone 6+ 的屏幕非常大,所以小部件的宽度也很大。 而且widget的最大高度比iPhone5大很多。

因此,

由于分辨率的原因,iPhone6+ 上的小部件使用的视频内存是 iPhone5 的 3~4 倍。加上一些动画,它会增加很多。

但是,由于在显示小部件时应用程序保持活动状态,iOS 允许小部件使用少量内存。 (没有记录实际限制)。

在我的实验中,使用实现drawRect 100% 的自定义视图会杀死 iPhone6+ 上的小部件,即使它什么也不做。 (空实现)

我认为这是错误。我的另一个推论是整个矩形将被缓存以用于核心动画,它会消耗内存。然后 iOS 将其杀死。

真正的问题是 iPhone6+ 与 iPhone5 的 RAM 容量相同。

很简单:

iPhone6+ 上的小部件需要更多内存。 但是,小部件内存限制与 iPhone5 相同,实际上,它更糟糕,因为在后台运行的应用程序需要比 iPhone 5 更多的内存。以及其他小部件。

我可以确定这是苹果的硬件设计失误。

最可悲的是,iPhone6+ 的用户将持续多年。 并且由于 iPhone 6+ 的用户,开发者无法提供丰富的小部件。

【讨论】:

我认为他的评论是准确的。我不知道他为什么得到负面评价。覆盖 contentScaleFactor,然后将其设置为 2.0 或更低。默认的 3.0 会消耗大量内存,导致内存问题。

以上是关于Today Widget Extension 中经常出现“无法加载”的主要内容,如果未能解决你的问题,请参考以下文章

Today Widget Extension 中经常出现“无法加载”

Today Extension Widget 内容更新

Dose NSTimer 可以在 Today Extension (Widget) 中使用吗?

Today Widget Extension 动画在 iOS 10 上不起作用

在 iOS 8 Today Extension 中从 Storyboard 加载特定的 ViewController

在我的应用程序中从我的 Today Extension(小部件)打开 Safari