【问题标题】:How do I present a UIViewController from SKScene?如何呈现来自 SKScene 的 UIViewController?
【发布时间】:2013-10-26 15:37:55
【问题描述】:

我通过 Sprite Kit 进入 iOS,我认为这是不明智的。

我的目标是在游戏结束时显示“分享”按钮。点击分享按钮应该会显示一个 SLComposeViewController (Twitter Share)。场景的内容不应该改变。

指示“游戏结束”的游戏逻辑存在于 SpriteMyScene.m 中,它是 SKScene 的子类。

我可以通过这种方式在 Game Over 上显示分享按钮:

-(void)update:(CFTimeInterval)currentTime {

if (gameOver){
  UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
                [button addTarget:self
                           action:@selector(sendToController)
                forControlEvents:UIControlEventTouchDown];
                [button setTitle:@"Show View" forState:UIControlStateNormal];
                button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
                [self.view addSubview:button]; 
    }
}

- (void)sendToController
{
    NSLog(@"ok");
    SpriteViewController *viewController = [SpriteViewController alloc];
    [viewController openTweetSheet];
}

我卡住的地方是试图让 showTweetButton 方法工作。我的 SpriteViewController.m 看起来像这样:

- (void)openTweetSheet
    {
     SLComposeViewController *tweetSheet = [SLComposeViewController
                                           composeViewControllerForServiceType:
                                           SLServiceTypeTwitter];

    // Sets the completion handler.  Note that we don't know which thread the
    // block will be called on, so we need to ensure that any required UI
    // updates occur on the main queue
    tweetSheet.completionHandler = ^(SLComposeViewControllerResult result) {
        switch(result) {
                //  This means the user cancelled without sending the Tweet
            case SLComposeViewControllerResultCancelled:
                break;
                //  This means the user hit 'Send'
            case SLComposeViewControllerResultDone:
                break;
        }
    };

    //  Set the initial body of the Tweet
    [tweetSheet setInitialText:@"just setting up my twttr"];

    //  Adds an image to the Tweet.  For demo purposes, assume we have an
    //  image named 'larry.png' that we wish to attach
    if (![tweetSheet addImage:[UIImage imageNamed:@"larry.png"]]) {
        NSLog(@"Unable to add the image!");
    }

    //  Add an URL to the Tweet.  You can add multiple URLs.
    if (![tweetSheet addURL:[NSURL URLWithString:@"http://twitter.com/"]]){
        NSLog(@"Unable to add the URL!");
    }

    //  Presents the Tweet Sheet to the user
    [self presentViewController:tweetSheet animated:NO completion:^{
        NSLog(@"Tweet sheet has been presented.");
    }];
}

我总是在日志中得到类似的东西:

-[UIView presentScene:]: unrecognized selector sent to instance 0x13e63d00 2013-10-17 18:40:01.611 Fix[33409:a0b] * 终止应用 由于未捕获的异常“NSInvalidArgumentException”,原因: '-[UIView presentScene:]: 无法识别的选择器发送到实例 0x13e63d00'

【问题讨论】:

  • presentScene: message send 错误消息提到的内容在哪里?上面的代码似乎与错误没有任何联系。您将 presentScene: 发送到常规 UIView(不是 SKView)。
  • 我的理解是问题所在:[self presentViewController:tweetSheet animated:NO completion:^{ NSLog(@"Tweet sheet has been present."); }];但这不是在 UIViewController 中完成的吗?我不明白 UIView 部分,因为 SpriteMyScene.m 是 SKScene 的子类,而 SpriteViewController.m 是 UIViewController 的子类

标签: ios objective-c uiview uiviewcontroller sprite-kit


【解决方案1】:

您正在创建一个新的视图控制器,但从未展示它:

SpriteViewController *viewController = [SpriteViewController alloc];

我假设 SpriteViewController 是呈现您的 SpriteMyScene 的内容,而您希望将控制权交还给正在呈现的 SpriteViewController

您需要在 SpriteMyScene 子类中保留对 SpriteViewController 的引用,然后在调用 openTweetSheet 时访问该引用。

在 SpriteMyScene.h 中

@class SpriteViewController;

@interface SpriteMyScene : SKScene

@property (nonatomic, weak) SpriteViewController *spriteViewController;

@end

在 SpriteViewController.m 中

// somewhere you initialize your SpriteMyScene object, I'm going to call it myScene

myScene.spriteViewController = self;

在 SpriteMyScene.m 中

#import "SpriteViewController.h"

- (void)sendToController
{
    NSLog(@"ok");
    // use the already-created spriteViewController
    [_spriteViewController openTweetSheet];
}

【讨论】:

  • 这对我不起作用——Xcode 不喜欢 "myScene.spriteViewController = self;" (在 SKScene 类型的对象上找不到),现在它不再调用控制器中的 openTweetSheet 方法。
  • 您需要确保您的 myScene 对象显式类型为 SpriteMyScene 对象,而不是通用 SKScene 对象。
  • 知道了,谢谢。这里的部分问题是我实际上并没有从 SpriteViewController 呈现场景。相反,我首先呈现“欢迎菜单”场景,然后执行 self.view presentScene:SpriteMyScene。一旦我跳过欢迎菜单并从 SpriteViewController 呈现 SpriteMyScene,我就可以让它工作(这并不理想,因为我仍然想从欢迎场景开始,但这是一个单独的问题)。
  • 是的,您也可以在欢迎菜单场景中添加对视图控制器的引用,并且在创建 SpriteMyScene 时,您可以在呈现场景时传递该引用。
【解决方案2】:

你可以使用

UIViewController *vc = self.view.window.rootViewController;

此代码将使您能够访问根视图控制器,因此您可以像平常一样对视图控制器执行任何操作。

但是,您需要添加按钮吗?在这种情况下,使用精灵并向其添加事件更适合您。只需致电:

UIViewController *vc = self.view.window.rootViewController;
[vc openTweetSheet];

还有

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    NSArray *nodes = [self nodesAtPoint:[touch locationInNode:self]];
    for (SKNode *node in nodes) {
        if ([node.name isEqualToString:@"OpenTweet"]) {
            UIViewController *vc = self.view.window.rootViewController;
            [vc openTweetSheet];
        }
    }
}

【讨论】:

  • 好答案,你也可以这样做,如果 ([_tweetButton containsPoint:location]) 让你的按钮点击
  • 我挖掘了这种方法,但 [vc openTweetSheet] 说“'UIViewController' 没有可见的@interface 声明选择器'openTweetSheet'。”问题是它是 SpriteViewController 的子类吗?还是我需要分配它?我正在做 #import "SpriteViewController.h" 并且我有 - (void)openTweetSheet; 就在 SpriteViewController.h 中的 "@end" 之前。跨度>
【解决方案3】:

如果你想从你的场景中打开另一个 UIViewController,你必须首先在最初创建这个场景的主视图控制器中创建一个委托,以便你的场景可以通知​​它的 ViewController 一个打开的推文动作。您将需要以下步骤:

  1. 在主视图控制器(我们场景的视图控制器)中定义一个委托,以处理打开推文操作。
  2. 在主视图控制器中实现委托方法
  3. 在您的场景中添加一个委托属性,以便它可以保留其 ViewController 的委托方法实现的句柄。在创建场景期间将此委托设置为主视图控制器
  4. 检测场景中的事件以调用主 ViewController 的委托方法
  5. 在委托方法实现中将控制权传递给 TweetSheetViewController

示例如下:

@protocol ViewControllerDelegate <NSObject>
-(void) openTweetSheet;
@end

扩展此 ViewController 以在其 .h 文件中支持此协议

@interface ViewController : UIViewController<ViewControllerDelegate>

@end

然后在.m文件中实现协议中的方法

-(void) openTweetSheet{
    TweetSheetViewController *ctrl = [[TweetSheetViewController alloc] initWithNibName:@"TweetSheetViewController" bundle:nil];

    [self presentViewController:ctrl animated:YES completion:nil];
}

在您的场景头类中,添加一个委托属性

@interface MyScene : SKScene {

}
@property (nonatomic, weak) id <ViewControllerDelegate> delegate;

@end

在 ViewController 中,在呈现场景之前,在 viewDidLoad 方法中设置其委托:

// Create and configure the scene.
MyScene * scene = [MyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;

[scene setDelegate:self];
// Present the scene.
[skView presentScene:scene];

现在您的场景可以将消息传递回它的 ViewController 并且 ViewController 可以打开另一个 ViewController。在您的场景类中,确定将触发打开 TweetSheetViewController 的操作

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */

    for (UITouch *touch in touches) {
        CGPoint location = [touch locationInNode:self];
        SKAction *fadeOut = [SKAction fadeOutWithDuration:0.5];
        SKAction *fadeIn = [SKAction fadeInWithDuration:1.0];
        SKAction *sequence = [SKAction sequence:@[fadeOut,fadeIn]];

        SKNode *node = [self nodeAtPoint:location];
        if ([[node name]  isEqual: @"openTweet"]) {
            NSLog(@"help");
            [node runAction:sequence];
            [delegate openTweetSheet];
        }

}

希望对您有所帮助。

【讨论】:

  • 对我不起作用.. [场景 setDelegate:self]; xcode 不接受。可能是什么原因?
【解决方案4】:

在您希望它发生的场景中编写 SlComposeViewController 方法。例如:

@interface GameOverScene: SKScene

...initwithsize bla bla bla
...

添加这些方法:

-(void)OpenTweetShet{

    if ([SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter]) {

    _composeTwitter = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
    [_composeTwitter setInitialText:@"TWEET"];

然后调用:

self.view.window.rootViewController **to present the SLComposeViewController**

    [self.view.window.rootViewController presentViewController:_composeTwitter animated:YES completion:nil];


}

[_composeTwitter setCompletionHandler:^(SLComposeViewControllerResult result){

NSString *output =[[NSString alloc]init];

switch (result) {
    case SLComposeViewControllerResultCancelled:
        output = @"Post cancelled";
        break;
    case SLComposeViewControllerResultDone:
        output = @"Post Succesfull";
        break;
    default:
        break;      
    }

这是在发送/取消帖子后显示 UIAlert 的选项:

UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Twitter" message:output delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles: nil];
    [alert show];     
    }];
}
@end

【讨论】:

  • 另外我使用了一个自定义的 SKButton 类并设置它的动作来展示 slcomposeviewcontroller
【解决方案5】:

这对我有用:self.view.window.rootViewController

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSArray *nodes = [self nodesAtPoint:[touch locationInNode:self]];
for (SKNode *node in nodes) {
    if ([node.name isEqualToString:@"OpenTweet"]) {
        UIViewController *vc = self.view.window.rootViewController;
        [self.view.window.rootViewController openTweetSheet];
    }
}
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-20
相关资源
最近更新 更多