- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我已经在我的游戏的商店场景中实现了应用程序购买,并且在从商店场景切换到另一个场景时遇到了问题,它似乎使游戏崩溃并给我这个错误
Thread 1: EXC_BAD_ACCESS (code=1, address=0x840f8010)
或者它给了我其他错误的多个版本,例如:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x3f8)
它有时也会给我一个错误,像这样更改子类中的行
当我注释掉子类中搜索应用程序购买信息或将手机置于飞行模式的代码时,它工作正常并且问题消失了。
我有一个 SKnode 的子类,它获取有关可购买商品的信息,然后通过使用 SKLabels 和 sprite 节点显示它,以显示商店中购买的图片,如下所示:
class InAppPurchaseItems: SKNode, SKProductsRequestDelegate {
var shopItemNode = SKSpriteNode()
var itemPriceBackground = SKSpriteNode()
var shopItemLabel = SKLabelNode()
var shopItemTitleLabel = SKLabelNode()
var pressableNode = SKSpriteNode()
var itemPriceLabel = SKLabelNode()
var title: String = ""
var information: String = ""
var image: String = ""
var price:String = "X"
func createAppPurchaseItem(ID: String, purchaseImage: String, purchaseTitle:String) {
title = purchaseTitle
image = purchaseImage
createTheNode()
//let product = SKProduct()
let productID: NSSet = NSSet(objects: ID) //"RedShield.Astrum.Purchase", "DoubleCoin.Astrum.Purchase")
let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
request.delegate = self as? SKProductsRequestDelegate
request.start()
}
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
print("product request...")
let myProduct = response.products
for product in myProduct {
print("product added")
if product.productIdentifier == "RedShield.Astrum.Purchase" {
price = priceStringForProduct(item: product)!
information = product.localizedDescription
} else if product.productIdentifier == "DoubleCoin.Astrum.Purchase" {
price = priceStringForProduct(item: product)!
information = product.localizedDescription
} else if product.productIdentifier == "1500Stars.Astrum.Purchase" {
price = priceStringForProduct(item: product)!
shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirOne")
} else if product.productIdentifier == "7500Stars.Astrum.Purchase" {
price = priceStringForProduct(item: product)!
shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirTwo")
} else if product.productIdentifier == "14000Stars.Astrum.Purchase" {
price = priceStringForProduct(item: product)!
shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirThree")
} else if product.productIdentifier == "28000Stars.Astrum.Purchase" {
price = priceStringForProduct(item: product)!
shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirFour")
} else if product.productIdentifier == "65000Stars.Astrum.Purchase" {
price = priceStringForProduct(item: product)!
shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirFive")
} else if product.productIdentifier == "128000Stars.Astrum.Purchase" {
price = priceStringForProduct(item: product)!
shopItemNode.texture = SKTexture(imageNamed: "BuyStarsTeirSix")
}
createShopLabels()
}
}
func priceStringForProduct(item: SKProduct) -> String? {
let price = item.price
if price == 0 {
return "GET" //or whatever you like
} else {
let numberFormatter = NumberFormatter()
let locale = item.priceLocale
numberFormatter.numberStyle = .currency
numberFormatter.locale = locale
return numberFormatter.string(from: price)
}
}
func createTheNode() {
let tex:SKTexture = SKTexture(imageNamed: image)
shopItemNode = SKSpriteNode(texture: tex, color: SKColor.black, size: CGSize(width: 85, height: 85)) //frame.maxX / 20, height: frame.maxY / 20))
shopItemNode.zPosition = -10
shopItemNode.position = CGPoint(x: 0, y: 35)
self.addChild(shopItemNode)
self.name = "ShopItem"
self.zPosition = -11
shopItemTitleLabel = SKLabelNode(fontNamed: "Avenir-Black")
shopItemTitleLabel.fontColor = UIColor.black;
shopItemTitleLabel.fontSize = 15 //self.frame.maxY/30
shopItemTitleLabel.position = CGPoint (x: 0, y: -30)
shopItemTitleLabel.text = "\(title)"
shopItemTitleLabel.zPosition = -9
self.addChild(shopItemTitleLabel)
itemPriceBackground = SKSpriteNode(texture: SKTexture(imageNamed: "PriceShopBackground"), color: .clear, size: CGSize(width: 80, height: 30)) //SKSpriteNode(color: SKColor.black, size: CGSize(width: 65, height: 20))
//itemPriceBackground.alpha = 0.4
itemPriceBackground.zPosition = -10
itemPriceBackground.position = CGPoint(x: 0, y: -54)
addChild(itemPriceBackground)
pressableNode = SKSpriteNode(texture: nil, color: .clear, size: CGSize(width: 100, height: 140))
pressableNode.zPosition = -7
pressableNode.position = CGPoint(x: 0, y: 0)
shopItemSprites.append(pressableNode)
addChild(pressableNode)
}
func createShopLabels() {
shopItemLabel = SKLabelNode(fontNamed: "Avenir-Black")
shopItemLabel.fontColor = UIColor.white;
shopItemLabel.fontSize = 15 //self.frame.maxY/30
shopItemLabel.position = CGPoint (x: 0, y: -60)
shopItemLabel.text = "\(price)"
shopItemLabel.zPosition = -9
addChild(shopItemLabel)
}
}
然后使用以下代码将它们显示在商店场景中:
let ShopItem = InAppPurchaseItems()
ShopItem.createAppPurchaseItem(ID: "DoubleCoin.Astrum.Purchase", purchaseImage: "2StarCoin", purchaseTitle: "+2 In Game Pickups")
ShopItem.position = CGPoint(x: self.frame.midX / 1.6, y: self.frame.midY * 0.8)
ShopItem.zPosition = 100
ShopItem.name = "Shp0"
moveableArea.addChild(ShopItem)
商店的主要类
商店主类也有用于购买产品的应用内购买代码,也可以像在子类中一样搜索产品信息,如下所示
class ShopItemMenu: SKScene, SKProductsRequestDelegate, SKPaymentTransactionObserver {
//Purchase Variables
var listOfProducts = [SKProduct]()
var p = SKProduct()
override func didMoveToView(to view: SKView) {
let ShopItem = InAppPurchaseItems()
ShopItem.createAppPurchaseItem(ID: "DoubleCoin.Astrum.Purchase", purchaseImage: "2StarCoin", purchaseTitle: "+2 In Game Pickups")
ShopItem.position = CGPoint(x: self.frame.midX / 1.6, y: self.frame.midY * 0.8)
ShopItem.zPosition = 100
ShopItem.name = "Shp0"
moveableArea.addChild(ShopItem)
}
//This function allows for a product to be bought buy the user and starts the proccess for purchasing
func appPurchaseBuying(appPurchaseID:String) {
for product in listOfProducts {
let prodID = product.productIdentifier
if(prodID == appPurchaseID) {
p = product
buyProduct()
}
}
}
//This Function restores all previously purchased Items (use this for the restor button.
func restorePurchasesOfItems() {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
//This function checks if they can make payments and then loads the product ids from a harcoded set. (use this to start when the scene starts)
func checkCanMakePayment() {
if (SKPaymentQueue.canMakePayments()) {
print("can make payments...")
let productID: NSSet = NSSet(objects: "RedShield.Astrum.Purchase", "DoubleCoin.Astrum.Purchase")
let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
request.delegate = self
request.start()
} else {
let alert = UIAlertController(title: "In-App Purchases Not Enabled", message: "Please enable In App Purchases in Settings", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Settings", style: UIAlertActionStyle.default, handler: { alertAction in
alert.dismiss(animated: true, completion: nil)
let url: NSURL? = NSURL(string: UIApplicationOpenSettingsURLString)
if url != nil
{
UIApplication.shared.openURL(url! as URL)
}
}))
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { alertAction in
alert.dismiss(animated: true, completion: nil)
}))
if let vc = self.scene?.view?.window?.rootViewController {
vc.present(alert, animated: true, completion: nil)
}
}
}
//This allows the user to buy the product with a product idetifier given by the variable "p"
func buyProduct() {
print("buying " + p.productIdentifier)
let pay = SKPayment(product: p)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(pay as SKPayment)
}
//This Function gets all the avaliable products from apple and puts them into the product Array called listOfProducts
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
/* print("product request...")
let myProduct = response.products
for product in myProduct {
print("product added")
if product.productIdentifier == "RedShield.Astrum.Purchase" {
shieldPurchasePrice = priceStringForProduct(item: product)!
} else if product.productIdentifier == "DoubleCoin.Astrum.Purchase" {
DoubleCoinPurchasePrice = priceStringForProduct(item: product)!
}
/*print(product.productIdentifier)
print(product.localizedTitle)
print(product.localizedDescription)
print(product.price)
*/
listOfProducts.append(product)
}*/
}
func priceStringForProduct(item: SKProduct) -> String? {
let price = item.price
if price == 0 {
return "GET" //or whatever you like
} else {
let numberFormatter = NumberFormatter()
let locale = item.priceLocale
numberFormatter.numberStyle = .currency
numberFormatter.locale = locale
return numberFormatter.string(from: price)
}
}
//This Function restores all the already purchased products so that things can be restored such as shield
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
print("restoring all...")
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case "RedShield.Astrum.Purchase":
isRedShieldPurchaseOn = true
let defaults = UserDefaults.standard
defaults.set(isRedShieldPurchaseOn, forKey: "shieldPurchase")
print("finished restoring this purchase")
case "DoubleCoin.Astrum.Purchase":
isCoinPurchaseOn = true
let defaults = UserDefaults.standard
defaults.set(isCoinPurchaseOn, forKey: "doubleCoinPurchase")
print("finished restoring this purchase")
default:
print("IAP not found")
}
}
alert(title: "Restored", msg: "Purchases were restored")
}
//This Function is run when the user makes a purchase and checks the state of the purchase to make sure it works
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print("adding payment...")
for transaction: AnyObject in transactions {
let trans = transaction as! SKPaymentTransaction
print(trans.error)
switch trans.transactionState {
case .purchased:
print("buying ok, Unlocking purchase...")
print(p.productIdentifier)
let prodID = p.productIdentifier
switch prodID {
case "RedShield.Astrum.Purchase":
isRedShieldPurchaseOn = true
let defaults = UserDefaults.standard
defaults.set(isRedShieldPurchaseOn, forKey: "shieldPurchase")
print("unlocked Purchase")
case "DoubleCoin.Astrum.Purchase":
isCoinPurchaseOn = true
let defaults = UserDefaults.standard
defaults.set(isCoinPurchaseOn, forKey: "doubleCoinPurchase")
print("unlocked Purchase")
case "SOME IN APP PURCHASE ID HERE":
print("unlocked Purchase")
default:
print("IAP Not found")
}
queue.finishTransaction(trans)
case .failed:
print("error with payment...")
queue.finishTransaction(trans)
default:
print("Default")
}
}
}
我是否正在以正确的方式执行此操作,或者是否有更好的方法来执行此操作以及如何解决我遇到的崩溃问题?
编辑
编辑 2
编辑 3
编辑 4
import Foundation
import SpriteKit
import StoreKit
class PurchaseService {
static let session = PurchaseService()
var products = [SKProduct]()
var p = SKProduct()
//This function allows for a product to be bought buy the user and starts the proccess for purchasing
func appPurchaseBuying(appPurchaseID:String) {
for product in products {
let prodID = product.productIdentifier
if(prodID == appPurchaseID) {
p = product
buyProduct()
}
}
}
//This Function restores all previously purchased Items (use this for the restor button.
func restorePurchasesOfItems() {
//SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
//This function checks if they can make payments and then loads the product ids from a harcoded set. (use this to start when the scene starts)
func checkCanMakePayment() {
if (SKPaymentQueue.canMakePayments()) {
print("can make payments...")
let productID: NSSet = NSSet(objects: "RedShield.Astrum.Purchase", "DoubleCoin.Astrum.Purchase")
let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>)
//request.delegate = self
request.start()
} else {
let alert = UIAlertController(title: "In-App Purchases Not Enabled", message: "Please enable In App Purchases in Settings", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Settings", style: UIAlertActionStyle.default, handler: { alertAction in
alert.dismiss(animated: true, completion: nil)
let url: NSURL? = NSURL(string: UIApplicationOpenSettingsURLString)
if url != nil
{
UIApplication.shared.openURL(url! as URL)
}
}))
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: { alertAction in
alert.dismiss(animated: true, completion: nil)
}))
}
}
//This allows the user to buy the product with a product idetifier given by the variable "p"
func buyProduct() {
print("buying " + p.productIdentifier)
let pay = SKPayment(product: p)
//SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(pay as SKPayment)
}
//This Function gets all the avaliable products from apple and puts them into the product Array called listOfProducts
public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
print("product request...")
let myProduct = response.products
for product in myProduct {
print("product added")
products.append(product)
}
}
func priceStringForProduct(item: SKProduct) -> String? {
let price = item.price
if price == 0 {
return "GET" //or whatever you like
} else {
let numberFormatter = NumberFormatter()
let locale = item.priceLocale
numberFormatter.numberStyle = .currency
numberFormatter.locale = locale
return numberFormatter.string(from: price)
}
}
//This Function restores all the already purchased products so that things can be restored such as shield
public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
print("restoring all...")
for transaction in queue.transactions {
let t: SKPaymentTransaction = transaction
let prodID = t.payment.productIdentifier as String
switch prodID {
case "RedShield.Astrum.Purchase":
isRedShieldPurchaseOn = true
let defaults = UserDefaults.standard
defaults.set(isRedShieldPurchaseOn, forKey: "shieldPurchase")
print("finished restoring this purchase")
case "DoubleCoin.Astrum.Purchase":
isCoinPurchaseOn = true
let defaults = UserDefaults.standard
defaults.set(isCoinPurchaseOn, forKey: "doubleCoinPurchase")
print("finished restoring this purchase")
default:
print("IAP not found")
}
}
//alert(title: "Restored", msg: "Purchases were restored")
}
//This Function is run when the user makes a purchase and checks the state of the purchase to make sure it works
public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print("adding payment...")
for transaction: AnyObject in transactions {
let trans = transaction as! SKPaymentTransaction
print(trans.error)
switch trans.transactionState {
case .purchased:
print("buying ok, Unlocking purchase...")
print(p.productIdentifier)
let prodID = p.productIdentifier
switch prodID {
case "RedShield.Astrum.Purchase":
isRedShieldPurchaseOn = true
let defaults = UserDefaults.standard
defaults.set(isRedShieldPurchaseOn, forKey: "shieldPurchase")
print("unlocked Purchase")
case "DoubleCoin.Astrum.Purchase":
isCoinPurchaseOn = true
let defaults = UserDefaults.standard
defaults.set(isCoinPurchaseOn, forKey: "doubleCoinPurchase")
print("unlocked Purchase")
case "SOME IN APP PURCHASE ID HERE":
print("unlocked Purchase")
default:
print("IAP Not found")
}
queue.finishTransaction(trans)
case .failed:
print("error with payment...")
queue.finishTransaction(trans)
default:
print("Default")
}
}
}
}
最佳答案
在您的游戏场景中拥有所有 StoreKit 代码会使您更难隔离您遇到的问题。我建议您创建一个新的 swift 文件,我们将其称为 PurchaseService,并使用如下静态实例:
class PurchaseService {
static let session = PurchaseService()
var products = [SKProduct]()
// code
}
您可以在此处实现所有与采购相关的逻辑。我通常使用 getPurchases 函数从商店加载可用的购买,并从 AppDelegate.swift 文件的应用程序函数调用它。这确保您的购买很早就加载并且在您需要它们的第一时间就准备好(因为您创建了一个静态实例,您可以在需要通过 PurchaseService.session 进行购买时随时引用它...)
要获取价格,您可以使用一个函数来遍历您的产品变量并检查产品 ID:
func price(for productID:String)->Double{
if products.count>0 {
for product in products {
if product.productIdentifier == productID {
return product.price.doubleValue
}
}
}
}
如果您遵守 SKProductRequestDelegate 协议(protocol),则无需有条件地将 self 转换为它:
// unnecessary: request.delegate = self as? SKProductsRequestDelegate
request.delegate = self
想知道您是否公开了 productRequest 方法,因为在请求返回时,SKProductResponse 对象自身不再可用。
关于您项目中的 Objective-C 代码:我看到您可能正在使用 Firebase(我从您的控制台消息推断)并且它有一些 Objective-C 的零碎部分。
关于ios - 更改场景时,应用内购买会导致应用崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47129841/
IO 设备如何知道属于它的内存中的值在memory mapped IO 中发生了变化? ? 例如,假设内存地址 0 专用于保存 VGA 设备的背景颜色。当我们更改 memory[0] 中的值时,VGA
我目前正在开发一个使用Facebook sdk登录(通过FBLoginView)的iOS应用。 一切正常,除了那些拥有较旧版本的facebook的人。 当他们按下“使用Facebook登录”按钮时,他
假设我有: this - is an - example - with some - dashesNSRange将使用`rangeOfString:@“-”拾取“-”的第一个实例,但是如果我只想要最后
Card.io SDK提供以下详细信息: 卡号,有效期,月份,年份,CVV和邮政编码。 如何从此SDK获取国家名称。 - (void)userDidProvideCreditCardInfo:(Car
iOS 应用程序如何从网络服务下载图片并在安装过程中将它们安装到用户的 iOS 设备上?可能吗? 最佳答案 您无法控制应用在用户设备上的安装,因此无法在安装过程中下载其他数据。 只需在安装后首次启动应
我曾经开发过一款企业版 iOS 产品,我们公司曾将其出售给大型企业,供他们的员工使用。 该应用程序通过 AppStore 提供,企业用户获得了公司特定的配置文件(包含应用程序配置文件)以启用他们有权使
我正在尝试将 Card.io SDK 集成到我的 iOS 应用程序中。我想为 CardIO ui 做一个简单的本地化,如更改取消按钮标题或“在此保留信用卡”提示文本。 我在 github 上找到了这个
我正在使用 CardIOView 和 CardIOViewDelegate 类,没有可以设置为 YES 的 BOOL 来扫描 collectCardholderName。我可以看到它在 CardIOP
我有一个集成了通话工具包的 voip 应用程序。每次我从我的 voip 应用程序调用时,都会在 native 电话应用程序中创建一个新的最近通话记录。我在 voip 应用程序中也有自定义联系人(电话应
iOS 应用程序如何知道应用程序打开时屏幕上是否已经有键盘?应用程序运行后,它可以接收键盘显示/隐藏通知。但是,如果应用程序在分屏模式下作为辅助应用程序打开,而主应用程序已经显示键盘,则辅助应用程序不
我在模拟器中收到以下错误: ImageIO: CGImageReadSessionGetCachedImageBlockData *** CGImageReadSessionGetCachedIm
如 Apple 文档所示,可以通过 EAAccessory Framework 与经过认证的配件(由 Apple 认证)进行通信。但是我有点困惑,因为一些帖子告诉我它也可以通过 CoreBluetoo
尽管现在的调试器已经很不错了,但有时找出应用程序中正在发生的事情的最好方法仍然是古老的 NSLog。当您连接到计算机时,这样做很容易; Xcode 会帮助弹出日志查看器面板,然后就可以了。当您不在办公
在我的 iOS 应用程序中,我定义了一些兴趣点。其中一些有一个 Kontakt.io 信标的名称,它绑定(bind)到一个特定的 PoI(我的意思是通常贴在信标标签上的名称)。现在我想在附近发现信标,
我正在为警报提示创建一个 trigger.io 插件。尝试从警报提示返回数据。这是我的代码: // Prompt + (void)show_prompt:(ForgeTask*)task{
您好,我是 Apple iOS 的新手。我阅读并搜索了很多关于推送通知的文章,但我没有发现任何关于 APNS 从 io4 到 ios 6 的新更新的信息。任何人都可以向我提供 APNS 如何在 ios
UITabBar 的高度似乎在 iOS 7 和 8/9/10/11 之间发生了变化。我发布这个问题是为了让其他人轻松找到答案。 那么:在 iPhone 和 iPad 上的 iOS 8/9/10/11
我想我可以针对不同的 iOS 版本使用不同的 Storyboard。 由于 UI 的差异,我将创建下一个 Storyboard: Main_iPhone.storyboard Main_iPad.st
我正在写一些东西,我将使用设备的 iTunes 库中的一部分音轨来覆盖 2 个视频的组合,例如: AVMutableComposition* mixComposition = [[AVMutableC
我创建了一个简单的 iOS 程序,可以顺利编译并在 iPad 模拟器上运行良好。当我告诉 XCode 4 使用我连接的 iPad 设备时,无法编译相同的程序。问题似乎是当我尝试使用附加的 iPad 时
我是一名优秀的程序员,十分优秀!