在Xcode场景中整合Facebook和Twitter Swift iOS

时间:2017-03-27 13:44:47

标签: ios swift facebook twitter

我使用精灵套件和场景在Xcode中开发了一款游戏。现在我正在尝试整合功能,将高分发布到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   

        }

    }

3 个答案:

答案 0 :(得分:2)

您收到此错误是因为您需要从另一个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)?",
     ...
)

希望这有帮助

答案 1 :(得分:1)

我不会进入SLComposeViewController相关代码。除了crashoverride777提出的建议之外,我将向您展示两种技术。所以第一种技术就是使用通知,如下所示:

<强> GameScene:

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)
}

实际上,我会为委托编写另一个例子,以保持清洁:)

答案 2 :(得分:0)

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

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

 protocol MyDelegate:class {

        func myMethod()
    }

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

在我们的示例中,您可以将场景视为 worker ,将视图控制器视为 boss 。当场景完成任务时,它会通知其老板(代表对他的责任)关于完成工作,以便老板可以决定下一步是什么。我的意思是,我可以说:“场景是老板,它将责任委托给他的员工,视图控制员......”但是你认为谁是老板并不重要...... {{3}事情。

因此,视图控制器应符合此协议,并且它将实现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()

    }
}

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