Integración de Facebook y Twitter en Xcode Scenes Swift iOS

He desarrollado un juego en Xcode usando el kit de sprite y escenas. Ahora estoy tratando de integrar la funcionalidad para publicar puntuaciones más altas en Twitter y Facebook. He mirado a mi alnetworkingedor, y la mayoría de la gente dice que use SLComposeServiceViewController, que está bien, hasta que intente presentarlo. Porque mi aplicación realmente solo usa escenas, nunca tienen la function miembro "presentViewController (…)". Por lo tanto, no puedo presentarlo nunca. Alguien sabe de alguna manera en torno a esto?

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

No entraré en el código relacionado de SLComposeViewController . Solo le mostraré dos técnicas aparte de lo que propuso crashoverride777. Entonces, la primera técnica estaría usando notifications, como esta:

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

Aquí, publica una notificación tocando la pantalla. Una class controllera de vista deseada puede escuchar esta notificación, así:

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

Aquí, agregamos a uno mismo como un observador para esta notificación, significa que cuando ocurre la notificación, se llamará a un método de manejo apropiado (y ese es nuestro método personalizado de handle(notification:) . En ese método, debe llamar a su código:

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

En realidad, escribiré otro ejemplo para la delegación, para mantener las cosas limpias 🙂

Su error se debe a que debe presentar un UIViewController desde otro UIViewController. Asi que

 self.presentViewController(...) 

no funcionará porque self (SKScene) no es un UIViewController. Para presentar desde un SKScene, tendría que decir esto

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

Recomendaría que ya no uses esas API. Es mejor usar un UIActivityViewController para sus necesidades de uso compartido. De esta manera, solo necesita un button de compartir en su aplicación y puede compartir todo tipo de services (correo electrónico, Twitter, Facebook, iMessage, WhatsApp, etc.).

Cree un nuevo file Swift y añada este código.

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

Cuando se presiona el button de compartir, puede llamarlo así

  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 ) 

Tenga en count que la image y appStoreURL no aparecerán en todas partes, depende del service compartido.

También puede usar su valor de puntaje de su escena y agregarlo al text, por ejemplo

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

Espero que esto ayude

Como dije, esto se puede hacer usando notifications, como en esta respuesta , o puedes ir con la delegación:

Primero debe declarar el protocolo MyDelegate que define un método llamado myMethod() .

  protocol MyDelegate:class { func myMethod() } 

Ese método es un requisito que todas las classs deben implementar si se ajusta a este protocl.

En nuestro ejemplo, puede ver la escena como un trabajador y un controller de vista como un jefe . Cuando la escena termina su tarea, notifica a su jefe (le delega responsabilidades) sobre la finalización del trabajo, para que el jefe pueda decidir qué es lo próximo. Quiero decir, podría decir: "La escena es un jefe, y delega responsabilidades a su empleado, el controller de vista …" Pero realmente no importa a quién consideras como jefe … El patrón de la delegación importa.

Por lo tanto, el controller de vista, debe cumplir con este protocolo, e implementará el myMethod() (que será llamado por la escena más adelante):

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

Y aquí hay un código de GameScene en el que define la propiedad myDelegate que utilizamos para comunicarse con nuestro controller de vista:

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

Para saber cuándo elegir la delegación a través de las notifications y viceversa, eche un vistazo a este artículo (o simplemente busque SO, hay buenas publicaciones sobre eso).