【问题标题】:Integrating Facebook and Twitter in Xcode Scenes Swift iOS在 Xcode 场景 Swift iOS 中集成 Facebook 和 Twitter
【发布时间】:2017-08-20 06:21:45
【问题描述】:

我在 Xcode 中使用 sprite kit 和场景开发了一个游戏。现在我正在尝试集成将高分发布到 twitter 和 Facebook 的功能。我环顾四周,大多数人说使用 SLComposeServiceViewController 很好,直到我尝试展示它。因为我的应用程序真的只使用场景,所以它们从来没有成员函数“presentViewController(....)”。因此,我永远无法呈现它。有谁知道解决这个问题的方法吗?

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    let touch:UITouch = touches.first!
    let touchLocation = touch.location(in: self)
    let touchedNode = self.atPoint(touchLocation)

    if (touchedNode.name == "tryAgain") {
        let nextScene = Scene_LiveGame(size: self.scene!.size)
        nextScene.scaleMode = self.scaleMode
        self.view?.presentScene(nextScene, transition: SKTransition.fade(withDuration: 0.5))
    }
    else if (touchedNode.name == "share") {

        if SLComposeViewController.isAvailable(forServiceType: SLServiceTypeFacebook) {

        let fShare = SLComposeViewController(forServiceType: SLServiceTypeFacebook)



        self.presentViewController(fShare!, animated: true, completion: nil)
         //^This is where my problem is. Xcode is telling me that self has no member function presentViewController which I totally understand, because its a scene and thus doesn't share those functions. But every resource online has shown me this is the only way to do it   

        }

    }

【问题讨论】:

  • 请发布您正在使用的代码(相关部分)。但一般来说,您可以在适当的视图控制器中指定视图控制器相关的方法,并在您想要调用这些方法时从场景中发布通知。
  • else if 块中的代码应替换为可以发布通知的代码。此外,它应该被移动到一个适当的视图控制器,该控制器具有必需的成员方法并侦听所提到的通知。
  • 我不确定我是否遵循,我将代码移动到我制作的自定义视图控制器类中,但是我仍然无法从我的场景中调用它。
  • 您所说的视图控制器应该监听从场景发布的通知。如果您仍然卡住,请告诉我,等我回家后,我会为您写一个示例。
  • 我还是很卡。任何帮助都会很棒

标签: ios swift facebook twitter


【解决方案1】:

您收到此错误是因为您需要从另一个 UIViewController 呈现一个 UIViewController。所以

self.presentViewController(...)

将不起作用,因为 self (SKScene) 不是 UIViewController。要从 SKScene 呈现,您必须这样说

view?.window?.rootViewController?.presentViewController(fShare!, animated: true, completion: nil)

我建议您不要再使用这些 API。最好使用 UIActivityViewController 来满足您的共享需求。这样,您的应用中只需要一个分享按钮,您就可以分享到各种服务(电子邮件、Twitter、Facebook、iMessage、WhatsApp 等)。

创建一个新的 Swift 文件并添加此代码。

enum ShareMenu {

    static func open(text: String, image: UIImage?, appStoreURL: String?, from viewController: UIViewController?) {
        guard let viewController = viewController, let view = viewController.view else { return }

    // Activity items
    var activityItems = [Any]()

    // Text
    activityItems.append(text)

    // Image
    if let image = image {
        activityItems.append(image)
    }

    /// App url
    if let appStoreURL = appStoreURL {
        let items = ActivityControllerItems(appStoreURL: appStoreURL)
        activityItems.append(items)
    }

    // Activity controller
    let activityController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)

    // iPad settings
    if UIDevice.current.userInterfaceIdiom == .pad {
        activityController.modalPresentationStyle = .popover
        activityController.popoverPresentationController?.sourceView = view
        activityController.popoverPresentationController?.sourceRect = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
        activityController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.init(rawValue: 0)
    }

    // Excluded activity types
    activityController.excludedActivityTypes = [
        .airDrop,
        .print,
        .assignToContact,
        .addToReadingList,
    ]

    // Present
    DispatchQueue.main.async {
        viewController.present(activityController, animated: true)
    }

    // Completion handler
    activityController.completionWithItemsHandler = { (activity, success, items, error) in
        guard success else {
            if let error = error {
                print(error.localizedDescription)
            }
            return
        }

            // do something if needed
       }
   } 
}
// MARK: - Activity Controller Items

/**
 ActivityControllerItems
 */
private final class ActivityControllerItems: NSObject {

    // MARK: - Properties

    /// App name
    fileprivate let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String ?? "-"

    /// App store web url
    fileprivate let appStoreURL: String

    // MARK: - Init

    /// Init
    fileprivate init(appStoreURL: String) {
        self.appStoreURL = appStoreURL
        super.init()
    }
}

// MARK: - UIActivityItemSource

/// UIActivityItemSource
extension ActivityControllerItems: UIActivityItemSource {

    /// Getting data items

    /// Placeholder item
    func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
        return ""
    }

    /// Item for actity type
    func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType) -> Any? {
        return URL(string: appStoreURL) ?? appName
    }

    /// Provide info about data items

    /// Subject field for services such as email
    func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivityType?) -> String {
        return appName
    }
}

比按下分享按钮时你可以这样称呼它

 ShareMenu.open(
     text: "Can you beat my score?", 
     image: UIImage(...),  // set to nil if unused
     appStoreURL: "your iTunes app store URL",  // set to nil if unused
     from: view?.window?.rootViewController
 )

请记住,图像和 appStoreURL 不会随处显示,这取决于共享服务。

您还可以使用场景中的得分值并将其添加到文本中,例如

ShareMenu.open( 
     text: "Can you beat my score \(self.score)?",
     ...
)

希望对你有帮助

【讨论】:

  • 谢谢,我认为 UIActivityControllers 是共享的方式。您也只需要 1 个按钮。我也相信在某处读到过这些共享 API 最终会被弃用,但我在这里可能完全错了。
  • 我从未探索过 UIActivityControllers 但现在绝对会。谢谢,这解决了很多问题
  • 不客气。如您所见,使用起来非常简单。唯一棘手的一点是添加诸如 URL 之类的东西,但它还不错。如果你觉得我的回答对你有帮助,你能不能好好标记一下。编码愉快。
【解决方案2】:

我不会进入SLComposeViewController相关代码。除了 crashoverride777 提出的方法之外,我将只向您展示两种技术。所以第一种技术是使用通知,像这样:

游戏场景:

import SpriteKit

let kNotificationName = "myNotificationName"

class GameScene: SKScene {


    private func postNotification(named name:String){

        NotificationCenter.default.post(
            Notification(name: Notification.Name(rawValue: name),
                         object: self,
                         userInfo: ["key":"value"]))
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {


        self.postNotification(named: kNotificationName)

    }
}

在这里,您可以通过点击屏幕来发布通知。所需的视图控制器类可以监听此通知,如下所示:

import UIKit
import SpriteKit

class GameViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()


        NotificationCenter.default.addObserver(
            self,
            selector: #selector(self.handle(notification:)),
            name: NSNotification.Name(rawValue: kNotificationName),
            object: nil)

        if let view = self.view as! SKView? {
            // Load the SKScene from 'GameScene.sks'
            if let scene = GameScene(fileNamed: "GameScene") {
                // Set the scale mode to scale to fit the window
                scene.scaleMode = .aspectFill

                // Present the scene
                view.presentScene(scene)
            }
        }
    }

    func handle(notification:Notification){
        print("Notification : \(notification)")
    }
}

在这里,我们添加 self 作为该通知的观察者 - 意味着当通知发生时,将调用适当的处理方法(这是我们自定义的 handle(notification:) 方法。在该方法中,您应该调用您的代码:

if SLComposeViewController.isAvailable(forServiceType:   SLServiceTypeFacebook) {
     let fShare = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
     self.presentViewController(fShare!, animated: true,  completion: nil)
}

其实,我会再写一个委托的例子,让事情保持干净:)

【讨论】:

    【解决方案3】:

    正如我所说,这可以使用通知来完成,例如 this answer,或者您可以使用委托:

    首先您应该声明MyDelegate 协议,该协议定义了一个名为myMethod() 的方法。

     protocol MyDelegate:class {
    
            func myMethod()
        }
    

    该方法是每个类都必须实现的要求,如果它符合此协议。

    在我们的示例中,您可以将场景视为worker,将视图控制器视为boss。当场景完成其任务时,它会通知其老板(将职责委派给他)工作完成,以便老板决定下一步做什么。我的意思是,我可以说:“场景是一个老板,它将责任委托给他的员工,即视图控制器......”但你认为谁是老板并不重要......delegation pattern很重要.

    所以,视图控制器,应该符合这个协议,它会实现myMethod()(稍后会被场景调用):

    class GameViewController: UIViewController, MyDelegate {
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        //MARK: Conforming to MyDelegate protocol
    
        if let view = self.view as! SKView? {
            // Load the SKScene from 'GameScene.sks'
            if let scene = GameScene(fileNamed: "GameScene") {
                // Set the scale mode to scale to fit the window
                scene.scaleMode = .aspectFill
    
                scene.myDelegate = self
    
                // Present the scene
                view.presentScene(scene)
            }
        }
       }
    
    func myMethod(){
        print("Do your stuff here")
    }
    
    
     }
    

    这是来自GameScene 的代码,您在其中定义了我们用来与视图控制器通信的myDelegate 属性:

    import SpriteKit
    
    class GameScene: SKScene {
    
    
       weak var myDelegate:MyDelegate?
    
    
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    
    
        self.myDelegate?.myMethod()
    
        }
    }
    

    要了解何时选择委托而不是通知,反之亦然,请查看 this article(或者只是搜索 SO,有一些不错的帖子)。

    【讨论】:

    • 谢谢,这确实有道理。比它做的要多得多。
    猜你喜欢
    • 2014-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-26
    • 2013-04-09
    • 1970-01-01
    相关资源
    最近更新 更多