jjzjj

ios - 使用 iAd 时方向更改会导致 iOS 7 上的应用程序崩溃(无法识别的选择器发送到实例)

coder 2024-01-25 原文

集成 iAd 后,只要方向发生变化,我们的应用程序就会崩溃(即使我们将方向锁定为仅纵向)。我们正在使用 PhoneGap 3.2。这是 Xcode 中的输出:

2013-12-01 13:22:40.906 Wopple[4600:60b] -[CDViAd deviceOrientationChange:]: unrecognized selector sent to instance 0x166b07e0
2013-12-01 13:22:40.907 Wopple[4600:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CDViAd deviceOrientationChange:]: unrecognized selector sent to instance 0x166b07e0'
*** First throw call stack:
(0x2df75e83 0x382d66c7 0x2df797b7 0x2df780af 0x2dec6dc8 0x2df37e71 0x2deabab1 0x2e891ec5 0x306ffca5 0x306ff2e9 0x306fecfd 0x30764321 0x32bde76d 0x32bde357 0x2df40777 0x2df40713 0x2df3eedf 0x2dea9471 0x2dea9253 0x32bdd2eb 0x3075e845 0xe1e63 0x387cfab7)
libc++abi.dylib: terminating with uncaught exception of type NSException

这是我们的 iAd 代码:

//
//  CDViAd.m
//  Ad Plugin for PhoneGap
//
//  Created by shazron on 10-07-12.
//  Copyright 2010 Shazron Abdullah. All rights reserved.
//  Cordova v1.5.0 Support added 2012 @RandyMcMillan
//  Cordova v3.0.0 Support added 2013 @LimingXie

#import "CDViAd.h"
#import <Cordova/CDVDebug.h>


@interface CDViAd()

- (void) __prepare:(BOOL)atTop;
- (void) __showAd:(BOOL)show;

@end


@implementation CDViAd

@synthesize adView;
@synthesize bannerIsVisible, bannerIsInitialized, bannerAtTop, isLandscape;

#pragma mark -
#pragma mark Public Methods

- (CDVPlugin *)initWithWebView:(UIWebView *)theWebView {
  self = (CDViAd *)[super initWithWebView:theWebView];
  if (self) {
    // These notifications are required for re-placing the ad on orientation
    // changes. Start listening for notifications here since we need to
    // translate the Smart Banner constants according to the orientation.
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(deviceOrientationChange:)
               name:UIDeviceOrientationDidChangeNotification
             object:nil];
  }
  return self;
}

- (void) createBannerView:(CDVInvokedUrlCommand *)command
{
    CDVPluginResult *pluginResult;
    NSString *callbackId = command.callbackId;
    NSArray* arguments = command.arguments;

    NSUInteger argc = [arguments count];
    if (argc > 1) {
        return;
    }

    BOOL atTop = NO;
    NSString* atTopValue = [arguments objectAtIndex:0];
    if( atTopValue ) atTop = [atTopValue boolValue];
    [self __prepare:atTop];

    pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
    [self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId];
}

- (void) showAd:(CDVInvokedUrlCommand *)command
{
    CDVPluginResult *pluginResult;
    NSString *callbackId = command.callbackId;
    NSArray* arguments = command.arguments;

    NSUInteger argc = [arguments count];
    if (argc > 1) {
        return;
    }

    BOOL show = YES;
    NSString* showValue = [arguments objectAtIndex:0];
    if( showValue ) show = [showValue boolValue];
    [self __showAd:show];

    pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
    [self.commandDelegate sendPluginResult:pluginResult callbackId:callbackId];
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    Class adBannerViewClass = NSClassFromString(@"ADBannerView");
    if (adBannerViewClass && self.adView) {

        if( UIInterfaceOrientationIsLandscape( toInterfaceOrientation ) ) {
            self.isLandscape = YES;
            self.adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
        } else {
            self.adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
        }

        [self resizeViews];
    }
}

- (void) resizeViews
{
    Class adBannerViewClass = NSClassFromString(@"ADBannerView");
    if (adBannerViewClass && self.adView) {

        CGRect webViewFrame = [super webView].frame;
        CGRect superViewFrame = [[super webView] superview].frame;
        CGRect adViewFrame = self.adView.frame;

        BOOL adIsShowing = [[[super webView] superview].subviews containsObject:self.adView];
        if (adIsShowing) {
            if (self.bannerAtTop) {
                webViewFrame.origin.y = adViewFrame.size.height;
            } else {
                webViewFrame.origin.y = 0;
                CGRect adViewFrame = self.adView.frame;
                CGRect superViewFrame = [[super webView] superview].frame;
                adViewFrame.origin.y = (self.isLandscape ? superViewFrame.size.width : superViewFrame.size.height) - adViewFrame.size.height;
                self.adView.frame = adViewFrame;
            }

            webViewFrame.size.height = self.isLandscape? (superViewFrame.size.width - adViewFrame.size.height) : (superViewFrame.size.height - adViewFrame.size.height);
        } else {
            webViewFrame.size = self.isLandscape? CGSizeMake(superViewFrame.size.height, superViewFrame.size.width) : superViewFrame.size;
            webViewFrame.origin = CGPointZero;
        }

        //[UIView beginAnimations:@"blah" context:NULL];
        //[UIView setAnimationDuration:0.5];
        //[self.adView setAlpha:1.0];
        //[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        [super webView].frame = webViewFrame;

        //[UIView commitAnimations];
    }
}

#pragma mark -
#pragma mark Private Methods

- (void) __prepare:(BOOL)atTop
{
    NSLog(@"CDViAd Prepare Ad, bannerAtTop: %d", atTop);

    Class adBannerViewClass = NSClassFromString(@"ADBannerView");
    if (adBannerViewClass && !self.adView) {
        self.adView = [[ADBannerView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
        self.adView.requiredContentSizeIdentifiers = [NSSet setWithObjects: ADBannerContentSizeIdentifierPortrait, ADBannerContentSizeIdentifierLandscape, nil];        

        UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation];
        if( UIInterfaceOrientationIsLandscape( currentOrientation ) ) {
            self.isLandscape = YES;
            self.adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierLandscape;
        } else {
            self.isLandscape = NO;
            self.adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
        }

        self.adView.delegate = self;
        self.adView.backgroundColor = [UIColor blackColor];
        //[self.webView.superview addSubview:self.adView];

        self.webView.superview.backgroundColor = [UIColor blackColor];

        self.bannerAtTop = atTop;
        self.bannerIsVisible = NO;
        self.bannerIsInitialized = YES;

        [self resizeViews];
    }
}

- (void) __showAd:(BOOL)show
{
    NSLog(@"CDViAd Show Ad: %d", show);

    if (!self.bannerIsInitialized){
        [self __prepare:NO];
    }

    if (!(NSClassFromString(@"ADBannerView") && self.adView)) { // ad classes not available
        return;
    }

    if (show == self.bannerIsVisible) { // same state, nothing to do
        return;
    }

    if (show) {
        //[UIView beginAnimations:@"blah" context:NULL];
        //[UIView setAnimationDuration:0.5];
        //[self.adView setAlpha:1.0];
        //[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        [[[super webView] superview] addSubview:self.adView];
        [[[super webView] superview] bringSubviewToFront:self.adView];
        [self resizeViews];

        //[UIView commitAnimations];

        self.bannerIsVisible = YES;
    } else {
        //[UIView beginAnimations:@"blah" context:NULL];
        //[UIView setAnimationDuration:0.5];
        //[self.adView setAlpha:0.0];
        //[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

        [self.adView removeFromSuperview];
        [self resizeViews];

        //[UIView commitAnimations];

        self.bannerIsVisible = NO;
    }

}

#pragma mark -
#pragma ADBannerViewDelegate

- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
    NSLog(@"Banner view begining action");

    [self writeJavascript:@"cordova.fireDocumentEvent('onClickAd');"];
    if (!willLeave) {

    }
    return YES;
}

- (void)bannerViewActionDidFinish:(ADBannerView *)banner
{
    NSLog(@"Banner view finished action");
}

- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
    NSLog(@"Banner Ad loaded");

    Class adBannerViewClass = NSClassFromString(@"ADBannerView");
    if (adBannerViewClass) {
        if (!self.bannerIsVisible) {
            [self __showAd:YES];
        }

        [self writeJavascript:@"cordova.fireDocumentEvent('onReceiveAd');"];
    }
}

- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError*)error
{
    NSLog(@"Banner failed to load Ad");

    Class adBannerViewClass = NSClassFromString(@"ADBannerView");
    if (adBannerViewClass) {
        //if ( self.bannerIsVisible ) {
        //  [self __showAd:NO];
        //}

        NSString *jsString =
            @"cordova.fireDocumentEvent('onFailedToReceiveAd',"
            @"{ 'error': '%@' });";
        [self writeJavascript:[NSString stringWithFormat:jsString, [error description]]];
    }
}

- (void)dealloc {
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter]
        removeObserver:self
        name:UIDeviceOrientationDidChangeNotification
        object:nil];

    self.adView.delegate = nil;
    self.adView = nil;
}

@end

Rich Tolley 建议的编辑代码:

- (CDVPlugin *)initWithWebView:(UIWebView *)theWebView {
  self = (CDViAd *)[super initWithWebView:theWebView];
  if (self) {
    // These notifications are required for re-placing the ad on orientation
    // changes. Start listening for notifications here since we need to
    // translate the Smart Banner constants according to the orientation.
    /*[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(deviceOrientationChange:)
               name:UIDeviceOrientationDidChangeNotification
             object:nil];*/
  }
  return self;
}

- (void)dealloc {
    /*[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter]
        removeObserver:self
        name:UIDeviceOrientationDidChangeNotification
        object:nil];*/

    self.adView.delegate = nil;
    self.adView = nil;
}

最佳答案

您收到通知是因为它们是设备 方向更新 - 而不是界面 方向更新。即使 UI 没有改变方向,设备可以,所以你仍然可以获得更新。

崩溃是因为代码缺少通知方法:

- (void)deviceOrientationChange:(NSNotification *)notification.

查看 CDViAid.m 的源代码,它订阅了一个通知,但没有声明处理它的方法:

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]
    addObserver:self
       selector:@selector(deviceOrientationChange:)
           name:UIDeviceOrientationDidChangeNotification
         object:nil];

(在父类(super class)上也没有什么可以处理的)

在以下位置有一个错误报告:https://github.com/floatinghotpot/cordova-plugin-iad/issues/1 ,以及一个建议的解决方案,它似乎与 willRotateToInterfaceOrientation 重复。

您可以只删除从 -init-dealloc 添加和删除代码的通知 - 因为它没有调用方法,所以它不能做任何事情。

关于ios - 使用 iAd 时方向更改会导致 iOS 7 上的应用程序崩溃(无法识别的选择器发送到实例),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20317464/

有关ios - 使用 iAd 时方向更改会导致 iOS 7 上的应用程序崩溃(无法识别的选择器发送到实例)的更多相关文章

  1. ruby-on-rails - Ruby on Rails 迁移,将表更改为 MyISAM - 2

    如何正确创建Rails迁移,以便将表更改为MySQL中的MyISAM?目前是InnoDB。运行原始执行语句会更改表,但它不会更新db/schema.rb,因此当在测试环境中重新创建表时,它会返回到InnoDB并且我的全文搜索失败。我如何着手更改/添加迁移,以便将现有表修改为MyISAM并更新schema.rb,以便我的数据库和相应的测试数据库得到相应更新? 最佳答案 我没有找到执行此操作的好方法。您可以像有人建议的那样更改您的schema.rb,然后运行:rakedb:schema:load,但是,这将覆盖您的数据。我的做法是(假设

  2. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  3. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  4. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  5. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  6. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  7. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  8. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  9. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  10. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

随机推荐