SKView.presentScene 如果已显示另一个 SKView,则过渡无效

SKView.presentScene with transition not working if have shown another SKView

更新 - 查看我自己的回答,这是一个具有非常间接结果的保留周期。

我有一个非常奇怪的错误,显然是在不同的 UIView 中显示 SKView 的某种副作用。这似乎是旧 iOS9 错误 .

的表现

背景

Touchgram 是一个 iMessage 应用程序扩展,可让您创建和播放 互动 消息。它就像 PowerPoint 和游戏编辑器之间的混合体,位于消息之上。界面内置于UIKit和SpriteKit(用于播放)。

Touchgram 的引擎创建 SKScene 对象,并可以在多页消息中使用或不使用转换来呈现它们。

编辑界面以树视图开始并显示详细编辑器屏幕以允许编辑触摸检测、页面更改动作、播放声音等内容

我们的一些细节编辑屏幕也显示 SKView。具体来说,Touch 编辑器有一种模式,其中显示 SKView 以记录要匹配的 边界区域 手势。

文本元素编辑器 显示 SKView 作为样式如何影响一小段文本的预览。它在页面的主要 背景 上呈现文本元素,可以是纯色或图像。

问题

所以,有效的是,启动应用程序(实际上是 iMessage 中的应用程序扩展)并播放保存的消息 - 一切运行良好,有场景转换。

然而,对于一个编辑屏幕,如果你只是进入那个屏幕,甚至没有在它嵌入的SKView上绘制任何东西,它就会中断 回放。 (这是上面提到的文本元素屏幕)。

当您随后播放 任何 具有转换的消息时,它们会卡住,并且前一场景的节点仍在显示。

我已将其缩小到错误屏幕上那个 SKView 的文字 存在。如果我所做的唯一更改是删除该 SKView,则不会出现该问题。 (现在)甚至没有绑定到 SKView 的插座,也没有与之交互的代码。

另一个屏幕(触摸编辑器)上有一个 SKView,不会导致这种副作用。

我花了几天时间缩小范围,才意识到这是副作用。直到我阅读上面链接的 SO post,我才意识到这个错误的明显不一致是因为一些消息在场景之间有转换,而另一些则没有。

经过一年多的核心引擎工作,这阻碍了产品的首次发布。

好屏-触控编辑器

显示视图嵌套的 xib 部分:

    <objects>
        <view opaque="NO" contentMode="scaleToFill" id="Mia-xp-RE9">
            <rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
            <subviews>
...
                <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rlf-kc-zvh" userLabel="RecordingOverlay">
                    <rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
                    <subviews>
...
                        <skView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="AnC-CG-Aqq" userLabel="RecordingArea">
                            <rect key="frame" x="57" y="191" width="300" height="543"/>
                            <constraints>
                                <constraint firstAttribute="width" secondItem="AnC-CG-Aqq" secondAttribute="height" multiplier="414:750" priority="950" id="zMg-Iv-5nu"/>
                            </constraints>
                        </skView>
                    </subviews>
...
                </view>
            </subviews>
...
        </view>

问题屏幕 - 文本编辑器

显示视图嵌套的 xib 部分:

    <objects>
        <view opaque="NO" contentMode="scaleToFill" id="upw-ro-x1m">
            <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
            <subviews>
                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KTl-Xs-TIo" userLabel="MainContentView">
                    <rect key="frame" x="0.0" y="44" width="375" height="734"/>
                    <subviews>
...
                        <skView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Wch-3x-CLz" userLabel="preview">
                            <rect key="frame" x="91" y="307.33333333333326" width="193" height="349.66666666666674"/>
                            <constraints>
                                <constraint firstAttribute="width" secondItem="Wch-3x-CLz" secondAttribute="height" multiplier="414:750" priority="950" id="jeK-un-SXq"/>
                            </constraints>
                        </skView>
...
                    </subviews>
...
                </view>
            </subviews>
...
        </view>

值得探索的事物

正在尝试找出这些屏幕之间的差异以及可能触发此 UIKit/SpriteKit 行为的原因。这是可见的头脑风暴——基本上是在没有事先理论的情况下寻找工作屏幕与问题屏幕之间不同之处的副作用。这些大多是感觉不太可能产生影响的东西,但我只是想找出 差异。

  1. 正在工作 - adjacent/overlapping 滚动视图。工作的屏幕有一个 scrollView 和另一个根据不同的模式可见。
  2. 问题屏幕有一个 UIViewController 子类(绑定时,SKView 被绑定到该父级的插座)。
  3. NO 问题屏幕是一个 SKSceneDelegate 并在 viewDidLoad 中指定为委托,但注意到它作为委托被清除的方式是通过一个可选的这可能会导致它没有被清除。
  4. NO 其他 VC 委托混入。问题屏幕实现委托协议,用于第 3 方 RichText 字体选择以及作为 UIText
  5. NO 嵌套 skView - 工作屏幕现在比问题屏幕有更深的嵌套。将 skView 元素向上移动成为“MainContentView”的兄弟元素没有区别。
  6. 解除分配 - 在工作和问题屏幕中添加一个 deinit 以确认在返回主树视图后,在播放之前将两者都删除。 终于找到了不同之处! - 问题屏幕从不调用 deinit。

无关紧要

可能没有效果的东西。

  1. 绑定与否- 在调试开始时,问题屏幕上的 SKView 绑定到插座。解绑后问题依旧。
  2. 可见 - 在这两种情况下,SKView 开始时都是隐藏的,然后通过代码显示。不管有没有显示都会出现这个问题
  3. 在 SKView 中显示一些东西 - 它未绑定到插座并且没有代码与之交互,只是 xib 上 SKView 的存在 导致了副作用。

因此,正如我在曲折的问题中所阐明的那样。

如何诊断

添加 deinit 进行日志记录让我将两个屏幕之间的差异归零 - 一个被清理而另一个没有。

原因

viewDidLoad 问题屏幕上有这个


// this line, in a method called from viewWillAppear. This is calling a simple drawing kit created by PaintCode
self.thicknessSwatch.drawer = { self.hasThickness ? LinePickerKit.drawEdgeThickness() : LinePickerKit.drawNoEdge() }

回想起来非常明显的修复:

self.thicknessSwatch.drawer = {[weak self] in (self?.hasThickness ?? false) ? LinePickerKit.drawEdgeThickness() : LinePickerKit.drawNoEdge() }

即:我的闭包在 UIViewController 上有一个保留周期。那保留了 SKView,即使它没有被使用,没有绑定到 outlet,只从 xib 加载。

令人困惑的行为是,浮动的半实时 SKView 的存在对大多数 SpriteKit 行为没有影响直到是一个 presentScene(transition)