iAd 冻结 Sprite Kit 应用

Posted

技术标签:

【中文标题】iAd 冻结 Sprite Kit 应用【英文标题】:iAd freezes Sprite Kit app 【发布时间】:2014-09-13 04:30:32 【问题描述】:

我发现点击 iAd 可以在任何 Sprite Kit 游戏中冻结屏幕。这不仅仅是我的特定项目,因为库存的 Sprite Kit 示例项目也与 iAd 一起冻结。但这不会发生在模拟器中!我无法确定是因为模拟器运行的是 ios 8 而我的实际测试设备是 7.1,还是因为模拟器只是一个模拟器,所以它做的事情不同。

因此,如果您点击 iAd,然后单击 iAd 中的链接进入 safari(或此时手动切换到任何应用程序),然后切换回 Sprite Kit 应用程序,该应用程序将被冻结。 iAd 横幅仍然有效,它会按照应有的方式加载广告。但是应用程序的其余部分被冻结了。或者具体来说,它仍然接收触摸和东西(我可以从NSLogs 看到)但节点的渲染被冻结。如果您通过点击再次打开 iAd,然后关闭 iAd,则应用程序会以某种方式恢复并且再次正常工作。

如果您好奇,以下是我对现有 Sprite Kit 示例项目所做的唯一修改:

// in GameViewController.h
#import <iAd/iAd.h>

@interface GameViewController : UIViewController <ADBannerViewDelegate>
@end


// in GameViewController.m
@interface GameViewController() 
    ADBannerView*       iAdBanner;
    NSLayoutConstraint* centerAd;

@end

@implementation GameViewController
- (void)viewDidLoad

    [super viewDidLoad];

    // Configure iAd
    iAdBanner           = [[ADBannerView alloc] initWithFrame:CGRectZero];
    iAdBanner.alpha     = 0.0;
    iAdBanner.delegate  = self;
    centerAd            = [NSLayoutConstraint constraintWithItem:iAdBanner
                                                   attribute:NSLayoutAttributeCenterX
                                                   relatedBy:NSLayoutRelationEqual
                                                      toItem:self.view
                                                   attribute:NSLayoutAttributeCenterX
                                                  multiplier:1.0
                                                    constant:0.0];
    [self.view addSubview:iAdBanner];
    [self.view addConstraint:centerAd];

    // The rest of viewDidLoad is the stock code. I'm not pasting that in...


// iAd delegate methods

-(void) bannerViewDidLoadAd:(ADBannerView *)banner 
    // fade in iAd banner
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.5];
    [banner setAlpha:1.0];
    [UIView commitAnimations];


-(void) bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error 
    NSLog(@"iAd error: %@", error.localizedDescription);
    banner.alpha = 0.0;

另外我将部署目标设置为 7.1(并且显然在项目中包含 iAd 框架)。

如您所见,我没有对应用程序执行任何可能导致此崩溃的操作。我只需添加 iAd 横幅即可。所以问题不在我的代码中。这一定是苹果框架中的一个错误。

有人知道解决方法吗?我在互联网上找到了几个关于此的主题,但没有人能提出可行的解决方案。

编辑:

我发现我设备上的 iOS 7.1 在返回应用程序时调用了bannerViewActionDidFinish:。然而,模拟器中的 iOS 8 会调用该方法在离开应用程序之前(确切地说是在您离开应用程序时)。这不会直接影响上面发布的代码,因为它没有实现此方法。但这确实表明 iOS 7.1 和 iOS 8 的 iAd 实现方式有所不同。

【问题讨论】:

返回应用时检查视图/场景或其他节点的暂停属性 不。 Sprite Kit 术语中没有任何暂停。既不是场景或视图,也不是任何节点。返回后,我尝试将所有可能事物的paused 属性设置为NO,但没有成功。 iAd 没有 Sprite Kit 的概念,因此它不会以 Sprite Kit 方式暂停视图。 但如果你只是打开iAd,然后按顶部的X,它会返回应用程序,并正确取消暂停。仅当您在 iAd 打开时离开应用程序时才会发生冻结。然后当你回来时它无法恢复它。当您查看 iAd 时,iAd 会故意冻结屏幕以停止在后台呈现。它并没有完全将应用程序的暂停留给开发人员。我认为 Apple 试图确保 iAd 后面没有运行任何东西以节省资源。这会很好,但如果它没有无法恢复它暂停的内容。 我在问题的编辑部分所说的意思是 iAd 在离开 iOS 8 中的应用程序之前实际上将控制权返回给应用程序(取消暂停它)。在 iOS 7.x 中,iAd 只提供返回后控制回到应用程序。但是这种方式无法成功取消暂停应用程序。这绝对是 7.x 中 iAd 中的一个错误。 它不会暂停应用程序,应用程序会暂停(进入后台)。在后台模式下,只有应用程序的后台操作正在运行,除非您专门对其进行编程,否则您没有这些操作。我认为点击时应该关闭 iAd,但不是(可能需要手动完成?)或者 iAd 出于某种原因故意暂停视图控制器/视图。或者你进入后台的时候做,看viewcontroller对应的UIApplicationDelegate方法。 【参考方案1】:

--- EDIT 2 中的最终答案 ---

好吧,我之前确实读过这方面的内容,并且我确实尝试过,但我之前尝试过时似乎失败了。我又试了一次,现在可以了。

因此,您要做的是将视图控制器的canDisplayBannerAds 属性设置为YES,然后使用originalContentView 属性而不是普通的view 属性来设置您的SKView。或者,如果您想从场景内部访问您的视图,那就是:self.view.window.rootViewController.originalContentView。所以基本上任何时候你需要对你的视图做一些特定于SKView 的事情(比如转换到一个场景),而不能用常规的UIView 完成的事情然后使用scene.view.window.rootViewController.originalContentView 而不仅仅是scene.view。原因是当您在视图控制器中设置canDisplayBannerAds 属性时,iAd 以某种方式显式地将您的视图转换为UIView,因此您不能再在视图上使用SKView 方法。 originalContentView 是解决此问题的方法。

这完全解决了我的问题,应用程序现在恢复正常状态。

编辑:

不,它没有解决我的问题。 :(

现在在广告打开时切换到另一个应用并返回不会冻结该应用。好的。但是现在只需打开和关闭一个广告,它就会以同样的方式冻结。这毫无意义……

所以如果我没有在我的视图控制器中设置canDisplayBannerAds,那么当应用程序冻结时,离开应用程序并返回。但只是打开和关闭广告就可以了。但是如果我确实设置了canDisplayBannerAds,那就相反了。

这真的没有意义。我现在很迷茫……

编辑2(实际解决方案):

终于解决了!但这并不容易,该死的... 所以问题出在视图层次结构中。如果我将ADBannerView 添加为我的主视图的子视图,那么它会冻结应用程序。这很奇怪,因为这是在屏幕上添加横幅的建议方法。 如果我不手动将横幅添加为子视图,它仍然会出现在屏幕上!我猜 iAd 的实现方式是,一旦初始化,无论如何它都会在屏幕上显示自己。不过看起来有点不同。如果它不是任何视图的子视图,那么它会从底部带有“向上滑动”动画,将所有内容调整为位于其上方的较小框。我猜这是 iAd 横幅的默认行为。所以这样它不会冻结屏幕,它工作得很好!但由于我想对横幅进行更多控制(比如自己制作动画,而不是留给横幅本身),所以我做了以下操作:

在我的视图控制器中,我创建了一个SKView 的实例(不是像往常一样将self.view 转换为SKView,而是一个全新的实例)。然后我将其添加为self.view 的子视图。之后,我初始化ADBannerView,并将其添加为self.view 的子视图。请注意,self.view 没有直接显示任何内容,我将其用作“容器”来容纳另外两个。视图层次结构如下所示:

        self.view
        /        \
    SKView    ADBannerView

这样ADBannerView 不是我的 SKView 的直接子视图或超级视图(这是我的游戏使用的主视图),因此它不能搞砸,因为它没有该视图的概念,因为它们都是同一视图的子视图。此时我不知道您是否需要在视图控制器中设置canDisplayBannerAds。我已经设置好了,它可以工作。我没有尝试忽略它。

简而言之,我可以完全控制横幅视图以及我的游戏视图,并且它们不能相互混淆,因为它们不是子视图/超级视图关系。

这很难弄清楚,但你去吧。如果您对 iAd 和 Sprite Kit 也有问题,只需像这样组织您的视图。

【讨论】:

好答案。如果加一些代码sn-ps就更好了。 遗憾的是我已经没有这个项目了,我放弃了它,并删除了它:(所以我不能从中粘贴任何代码

以上是关于iAd 冻结 Sprite Kit 应用的主要内容,如果未能解决你的问题,请参考以下文章

Sprite Kit 中的顶部屏幕 iAd

iOS Sprite Kit 中 SKScene 中的插页式 iAd

iAD 视图在广告关闭时冻结

iAd 冻结游戏画面

Swift Sprite Kit 应用内购买

canDisplayBannerAds = YES 导致 Sprite Kit 应用程序崩溃