【问题标题】:SwiftUI Returning different types of objects from within an arraySwiftUI从数组中返回不同类型的对象
【发布时间】:2020-05-15 16:34:15
【问题描述】:

我有一个包含不同类型消息的数组:

let chatMessagesArray = [
    TextMessage(direction: "Incoming", timestamp: 1589463871, senderID: "Bob", destinationID: "Sally", message: "I was sent this message", deliveryStatus: "Delivered", deliveryTimestamp: 1589463871),
    TextMessage(direction: "Outbound", timestamp: 1589467569, senderID: "Sally", destinationID: "Bob", message: "I sent this message", deliveryStatus: "Delivered", deliveryTimestamp: 1589467622),
    ImageMessage(direction: "Incoming", timestamp: 1589485467, senderID: "Bob", destinationID: "Sally", imageURL: "http://Image.Full.URL", thumbnailURL: "https://thumbnail.image.url", deliveryStatus: "Delivered", deliveryTimestamp:1589485467),
] as [Any] <-- Is Any the best choice?

每种类型的消息都有自己的视图:

struct TextMessageView: View {
    var currentMessage: TextMessage
    var body: some View {
        HStack(alignment: .bottom, spacing: 15, content: {
            if(currentMessage.direction == "Incoming"){
                Text(currentMessage.message)
                .padding()
                    .background(Color.init(red: 0.8, green: 0.8, blue: 0.8))
                //.background(Color.gray)
                .cornerRadius(30)
            } else {
                Text(currentMessage.message)
                .foregroundColor(Color.white)
                .padding()
                .background(Color.green)
                .cornerRadius(30)

            }

        })
    }
}

在我的列表视图中,我希望能够根据消息类型调用正确的视图TextMessage || ImageMessage || ...

List{
    ForEach(chatMessagesArray){chatMessageRow in
        if(chatMessageRow  === TextMessage){ <-- What do I put here?
            TextMessageView(currentMessage: chatMessageRow)
        } else if(chatMessageRow === ImageMessage){
        ImageMessageView(currentMessage: chatMessageRow)
    }
}

阅读消息类型和呈现正确视图的最佳方式是什么? 有没有更好的方法来存储和检索数据? 谢谢

【问题讨论】:

    标签: swift list swiftui


    【解决方案1】:

    我认为最好的解决方案是使用枚举。像这样。

    enum Message {
       case Text(TextMessage)
       case Image(ImageMessage)
    
       var textMessage: TextMessage? {
           guard case let .text(message) = self else { return nil }
           return message
       }
    
       var imageMessage: ImageMessage? {
           guard case let .image(message) = self else { return nil }
           return message
       }
    }
    
    let chatMessageArray:[Message] = [.text(TextMessage(...)), .image(ImageMessage(...)), ...]
    
    List{
        ForEach(Array(chatMessagesArray.enumerated()), id: \.offset) { _, message in
            self.view(for: message)
        }
    }
    
    func view(for message: Message) -> some View {
        return Group {
            if message.textMessage != nil {
                TextMessageView(textMessage: message.textMessage!)
            } else {
                ImageMessageView(imageMessage: message.imageMessage!)
            }
        }
    }
    

    【讨论】:

    • 我在 ForEach 中放了什么?
    • @Mathew Jenkinson 我已经通过实施更新了我的答案。
    • 所以这是最好的主意,最终我最终将其用作重写的基础。感谢您的帮助。
    【解决方案2】:

    这里是在 ForEach 混合模型容器中迭代的可能方法(基于协议)的简化演示。使用 Xcode 11.4 / iOS 13.4 测试

    // demo of usage
    struct DemoMixedModel: View {
        let messages: [Message] = [TextMessage(), ImageMessage()]
        var body: some View {
            List {
                ForEach(messages, id: \.id) { message in
                    message.buildView()
                }
            }
        }
    }
    
    // base protocol with view builder
    protocol Message {
        var id: String { get }
        func buildView() -> AnyView
    }
    
    // models
    struct TextMessage: Message {
        var id: String = "1"          // just demo
        var text: String = "Demo"     // just demo
    
        func buildView() -> AnyView {
            AnyView(TextMView(model: self))
        }
    }
    
    struct ImageMessage: Message {
        var id: String = "2"             // just demo
        var imageName: String = "cloud"  // just demo
    
        func buildView() -> AnyView {
            AnyView(ImageMView(model: self))
        }
    }
    
    // views
    struct TextMView: View {
        let model: TextMessage
    
        var body: some View {
            Text(model.text)
        }
    }
    
    struct ImageMView: View {
        let model: ImageMessage
    
        var body: some View {
            Image(systemName: model.imageName)
        }
    }
    

    【讨论】:

    • 是的,这也是一个好方法,但我建议您尽可能避免使用AnyView。使用AnyView 会降低性能:通过隐藏视图的结构方式,我们迫使 SwiftUI 在视图层次结构发生变化时做更多的工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多