SwiftUI - 是否可以在显示后更改 ActionSheet 按钮文本?

SwiftUI - Is it possible to change an ActionSheet button text after it is displayed?

我想展示一个 ActionSheet 包含用户可以购买的 InApp 购买对象。

但我希望 sheet 包含此类对象的价格,例如:

Object 1 (.99)
Object 2 (.99)
...

但价格是异步的,因为它必须从商店中检索。

所以,我考虑过这样做:

  struct Package {
    enum Packtype:String {
    typealias RawValue = String

    case obj1 = "com.example.object1"
    case obj2 = "com.example.object2"
  }

  var productID:String = ""
  @State var namePriceString:String = ""

  init(productID:String) {
    self.productID = productID
  }
}

然后,当我创建操作 sheet 按钮时,我会这样做:

var obj1 = Package(productID: Package.Packtype.obj1.rawValue)
var obj2 = Package(productID: Package.Packtype.obj2.rawValue)

self.getPrices(packages:[obj1, obj2])


let obj1Button = ActionSheet.Button.default(Text(obj1.$namePriceString)) {
  // do something with obj1
}

let obj2Button = ActionSheet.Button.default(Text(obj2.$namePriceString)) {
  // do something with obj1
}

// build the actionsheet

稍后在代码中:

  func getPrices(packages:[Package]) {
    let productIDS = Set(packages.map {[=13=].productID})
        
    SwiftyStoreKit.retrieveProductsInfo(productIDS) { (answer) in
      if answer.invalidProductIDs.first != nil { return }
      
      let results = answer.retrievedProducts
      if results.count == 0 { return }
      
      for result in answer {
        if let package = packages.filter({ ([=13=].productID == result.productIdentifier) }).first {
          package.namePriceString = result.localizedTitle + "(" + "\(result.localizedPrice!)" + ")"
        }
      }
    }
  }

我在按钮创建行上指向 Text 时出错,说

Initializer 'init(_:)' requires that 'Binding' conform to 'StringProtocol'

简而言之,我需要这个:

  1. 我显示动作sheet。它的按钮不包含价格。
  2. 我检索价格
  3. 操作sheet 按钮随价格更新。

一个可能的解决方案是在 completion 处理程序中 return 价格,然后才显示操作 sheet:

struct ContentView: View {
    @State var showActionSheet = false
    @State var localizedPrices = [Package: String]()

    var body: some View {
        Button("Get prices") {
            getPrices(packages: Package.allCases, completion: {
                localizedPrices = [=10=]
                showActionSheet = true
            })
        }
        .actionSheet(isPresented: $showActionSheet) {
            let buttons = localizedPrices.map { package, localizedPrice in
                ActionSheet.Button.default(Text(localizedPrice), action: { buy(package: package) })
            }
            return ActionSheet(title: Text("Title"), message: Text("Message"), buttons: buttons + [.cancel()])
        }
    }
}
func getPrices(packages: [Package], completion: @escaping ([Package: String]) -> Void) {
    // simulates an asynchronous task, should be replaced with the actual implementation
    DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
        let localizedPrices = Dictionary(uniqueKeysWithValues: packages.map { ([=11=], "\(Int.random(in: 1 ..< 100))") })
        completion(localizedPrices)
    }
}

func buy(package: Package) {
    print("Buying \(package.rawValue)")
}
enum Package: String, CaseIterable {
    case obj1 = "com.example.object1"
    case obj2 = "com.example.object2"
}

这可以通过加载动画等进一步调整...