我使用精灵套件和场景在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
}
}
答案 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,有一些不错的帖子)。