gpt4 book ai didi

ios - 如何在 SwiftUI 中实现自定义委托(delegate)

转载 作者:行者123 更新时间:2023-12-01 19:31:36 29 4
gpt4 key购买 nike

例如,我有一个 SwitUI ContentView。当您第一次制作项目时出现的那个。

import SwiftUI

struct ContentView: View {
var manager = TestManager()
var body: some View {
ZStack{
Color(.green)
.edgesIgnoringSafeArea(.all)
VStack {
Text("Test Text")

Button(action:{}) {
Text("Get number 2")
.font(.title)
.foregroundColor(.white)
.padding()
.overlay(RoundedRectangle(cornerRadius: 30)
.stroke(Color.white, lineWidth: 5))
}
}
}
}
}

我有一个将处理 Api 调用的 TestManager。我为具有两个功能的类做了一个委托(delegate)。
protocol TestManagerDelegate {
func didCorrectlyComplete(_ testName: TestManager, model: TestModel)
func didFailWithError(_ error: Error)
}

struct TestManager {

var delegate: TestManagerDelegate?
let urlString = "http://numbersapi.com/2/trivia?json"

func Get(){
if let url = URL(string: urlString){

let session = URLSession(configuration: .default)

let task = session.dataTask(with: url) { (data, response, error) in
if error != nil{
self.delegate?.didFailWithError(error!)
return
}

if let safeData = data{
if let parsedData = self.parseJson(safeData){
self.delegate?.didCorrectlyComplete(self, model: parsedData)
}
}
}
task.resume()
}
}

func parseJson(_ jsonData: Data) -> TestModel?{
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(TestModel.self, from: jsonData)
let mes = decodedData.message
let model = TestModel(message: mes)
return model

} catch {
delegate?.didFailWithError(error)
return nil
}
}

}

这是 testModel 数据类。只抓取返回的 Json 文本。
struct TestModel :Decodable{
let text: String
}

如何将 TestManager 连接到 View 并让 View 处理委托(delegate),就像我们在 Storyboard 中所做的那样?

最佳答案

关于TestModel

Decodable协议(protocol)(在您的上下文中)假设您使用通过 JSON 获得的所有属性创建模型结构。当请求 http://numbersapi.com/2/trivia?json你会得到类似的东西:

{
"text": "2 is the number of stars in a binary star system (a stellar system consisting of two stars orbiting around their center of mass).",
"number": 2,
"found": true,
"type": "trivia"
}

这意味着,您的模型应如下所示:

struct TestModel: Decodable {
let text: String
let number: Int
let found: Bool
let type: String
}

关于委托(delegate)

在 SwiftUI 中,这种方法是无法实现的。相反,开发人员需要调整 Combine 框架的特性:属性包装器 @ObservedObject , @Published , 和 ObservableObject协议(protocol)。
您想将逻辑放入某个结构中。坏消息,(目前) ObservableObjectAnyObject协议(protocol)(即 Class-Only Protocol )。你需要重写你的 TestManager作为类:

class TestManager: ObservableObject {
// ...
}

只有这样你才能在你的 CurrentView 中使用它。使用 @ObservedObject属性包装器:

struct ContentView: View {
@ObservedObject var manager = TestManager()
// ...
}

关于TestManager

您的逻辑现在排除了 delegate因此,您需要使用您的 TestModel将数据传递给您的 CustomView .您可以修改 TestManager通过使用 @Published 添加新属性属性包装器:

class TestManager: ObservableObject {
let urlString = "http://numbersapi.com/2/trivia?json"
// 1
@Published var model: TestModel?

func get(){
if let url = URL(string: urlString){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { [weak self] (data, response, error) in
// 2
DispatchQueue.main.async {
if let safeData = data {
if let parsedData = self?.parseJson(safeData) {
// 3
self?.model = parsedData
}
}
}
}
task.resume()
}
}

private func parseJson(_ jsonData: Data) -> TestModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(TestModel.self, from: jsonData)
return decodedData
} catch {
return nil
}
}
}
  • 为了能够“从外部”访问您的模型,在您的情况下为 ContentView .
  • 使用DispatchQueue.main.async{ }对于异步任务,因为 Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
  • 只需使用您解析的模型。

  • 然后在 ContentView使用您的 TestManager像这样:

    struct ContentView: View {
    @ObservedObject var manager = TestManager()
    var body: some View {
    ZStack{
    Color(.green)
    .edgesIgnoringSafeArea(.all)
    VStack {
    Text("Trivia is: \(self.manager.model?.text ?? "Unknown")")
    Button(action:{ self.manager.get() }) {
    Text("Get number 2")
    .font(.title)
    .foregroundColor(.white)
    .padding()
    .overlay(RoundedRectangle(cornerRadius: 30)
    .stroke(Color.white, lineWidth: 5))
    }
    }
    }
    }
    }

    关于 HTTP

    您使用链接 http://numbersapi.com/2/trivia?json这是 not allowed by Apple ,请使用 https相反,或添加 App Transport Security Settings带有 Allow Arbitrary Loads 的键参数设置为 YES into your Info.Plist .但是这样做 very carefully因为http链接根本不起作用。

    进一步的步骤

    您可以根据上面的描述自行实现错误处理。

    完整代码(复制粘贴):

    import SwiftUI

    struct ContentView: View {
    @ObservedObject var manager = TestManager()
    var body: some View {
    ZStack{
    Color(.green)
    .edgesIgnoringSafeArea(.all)
    VStack {
    Text("Trivia is: \(self.manager.model?.text ?? "Unknown")")
    Button(action:{ self.manager.get() }) {
    Text("Get number 2")
    .font(.title)
    .foregroundColor(.white)
    .padding()
    .overlay(RoundedRectangle(cornerRadius: 30)
    .stroke(Color.white, lineWidth: 5))
    }
    }
    }
    }
    }

    struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
    ContentView()
    }
    }

    class TestManager: ObservableObject {
    let urlString = "http://numbersapi.com/2/trivia?json"
    @Published var model: TestModel?

    func get(){
    if let url = URL(string: urlString){
    let session = URLSession(configuration: .default)
    let task = session.dataTask(with: url) { [weak self] (data, response, error) in
    DispatchQueue.main.async {
    if let safeData = data {
    if let parsedData = self?.parseJson(safeData) {
    self?.model = parsedData
    }
    }
    }
    }
    task.resume()
    }
    }

    private func parseJson(_ jsonData: Data) -> TestModel? {
    let decoder = JSONDecoder()
    do {
    let decodedData = try decoder.decode(TestModel.self, from: jsonData)
    return decodedData
    } catch {
    return nil
    }
    }
    }

    struct TestModel: Decodable {
    let text: String
    let number: Int
    let found: Bool
    let type: String
    }

    关于ios - 如何在 SwiftUI 中实现自定义委托(delegate),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62471807/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com