如何正确地将部分中的项目与 RxDataSource swift 结合起来?

Posted

技术标签:

【中文标题】如何正确地将部分中的项目与 RxDataSource swift 结合起来?【英文标题】:How I can correctly combine items in section with RxDataSource swift? 【发布时间】:2019-10-01 00:57:17 【问题描述】:

我需要在一分钟内发送项目时将聊天消息合并到部分中。

视图模型

.....

.scan([MessageSectionModel]())  sectionModels, messageItem in
        var models = sectionModels

        if let lastSectionModel = sectionModels.last 
            switch lastSectionModel 
            case .incomingSection(var items):
                if messageItem.0.isIncoming 
                    items.append(messageItem.0)
                    models[models.count-1] = .incomingSection(items: items)
                 else 
                    models.append(.outcomingSection(items: [messageItem.0]))
                

            case .outcomingSection(var items):
                if messageItem.0.isIncoming 
                    models.append(.incomingSection(items: [messageItem.0]))
                 else 
                    items.append(messageItem.0)
                    models[models.count-1] = .outcomingSection(items: items)
                
            
            return models
        

        if messageItem.0.isIncoming 
            models.append(.incomingSection(items: [messageItem.0]))
         else 
            models.append(.outcomingSection(items: [messageItem.0]))
        
        return models
    

.....

视图控制器

....

@IBOutlet private weak var messagesTableView: UITableView!

private let disposeBag = DisposeBag()
private var dataSource: RxTableViewSectionedAnimatedDataSource<MessageSectionModel>!

private let messageHeaderReuseIdentifier = String(describing: MessageHeaderView.self)
private let messageFooterReuseIdentifier = String(describing: MessageFooterView.self)

dataSource = RxTableViewSectionedAnimatedDataSource<MessageSectionModel>(
        animationConfiguration: .init(insertAnimation: .none, reloadAnimation: .none, deleteAnimation: .none),
        configureCell:  dataSource, tableView, indexPath, item in

            switch dataSource.sectionModels[indexPath.section] 
            case .incomingSection:
                guard let cell = tableView.dequeueReusableCell(
                    withIdentifier: R.reuseIdentifier.incomingMessageTableViewCell,
                    for: indexPath
                ) else 
                    return UITableViewCell()
                

                let isFirst = indexPath.row == dataSource[indexPath.section].items.count - 1

                cell.bind(
                    messageText: item.text,
                    isFirstInSection: isFirst
                )

                return cell
            case .userSection:
                guard let cell = tableView.dequeueReusableCell(
                    withIdentifier: R.reuseIdentifier.outcomingMessageTableViewCell,
                    for: indexPath
                ) else 
                     return UITableViewCell()
                

                cell.bind(
                    messageText: item.text,
                    isFirstInSection: indexPath.row == dataSource[indexPath.section].items.count - 1
                )

                return cell
            
    )

....

消息项

....

 import Foundation
 import RxDataSources

 enum MessageSectionModel 
    case incomingSection(items: [MessageSectionItem])
    case outcomingSection(items: [MessageSectionItem])

 var lastMessageDate: Date 
    switch self 
    case .incomingSection(let items):
        return items.last?.sentDate ?? Date()
    case .outcomingSection(let items):
        return items.last?.sentDate ?? Date()
    
   
 

struct MessageSectionItem 
   let userId: String
   let id: String = UUID().uuidString
   let text: String
   let sentDate: Date
  let isIncoming: Bool


extension MessageSectionItem: IdentifiableType 
   var identity : String 
       return id
  


extension MessageSectionItem: Equatable 
   static func == (lhs: MessageSectionItem, rhs: MessageSectionItem) -> Bool 
     return lhs.identity == rhs.identity
   
  

extension MessageSectionModel: AnimatableSectionModelType 
   init(original: MessageSectionModel, items: [MessageSectionItem]) 
     switch original 
    case .incomingSection(let items):
        self = .incomingSection(items: items)
    case .outcomingSection(let items):
        self = .outcomingSection(items: items)
    
 

typealias Item = MessageSectionItem

var items: [MessageSectionItem] 
    switch self 
    case .incomingSection(let items):
        return items.map  $0 
    case .outcomingSection(let items):
        return items.map  $0 
    


var identity: Date 
    return lastMessageDate
 

....

我的表格视图被旋转,因为我获取消息被还原。我知道这是我的扫描错误,因为当我收到此代码时,我的单元格以正确的方式排序,但没有按部分组合。

   if let lastSectionModel = sectionModels.last 
         switch lastSectionModel 
         case .incomingSection(var items):
            if messageItem.0.isIncoming 
                items.append(messageItem.0)
                models[models.count-1] = .incomingSection(items: items)
             else 
                models.append(.outcomingSection(items: [messageItem.0]))
            

        case .outcomingSection(var items):
            if messageItem.0.isIncoming 
                models.append(.incomingSection(items: [messageItem.0]))
             else 
                items.append(messageItem.0)
                models[models.count-1] = .outcomingSection(items: items)
            
        
        return models

【问题讨论】:

【参考方案1】:

我认为您一次尝试做太多事情,而且顺序错误。将工作分解成更小的工作,每个工作都可以轻松测试/验证...另外,首先按时间对消息进行分组,然后将它们放在你的部分中。我最终得到了这个:

struct MessageItem 
    let userId: String
    let id: String = UUID().uuidString
    let text: String
    let sentDate: Date
    let isIncoming: Bool


struct MessageGroup 
    let userId: String
    var text: String 
        return parts.map  $0.text .joined(separator: "\n")
    
    let isIncoming: Bool

    struct Part 
        let id: String
        let text: String
        let sentDate: Date

        init(_ messageSectionItem: MessageItem) 
            id = messageSectionItem.id
            text = messageSectionItem.text
            sentDate = messageSectionItem.sentDate
        
    
    var parts: [Part]

    init(from item: MessageItem) 
        userId = item.userId
        isIncoming = item.isIncoming
        parts = [Part(item)]
    


enum MessageSectionModel 
    case incomingSection(items: [MessageGroup])
    case outcomingSection(items: [MessageGroup])


extension ObservableType where Element == MessageItem 
    func convertedToSectionModels() -> Observable<[MessageSectionModel]> 
        return
            scan(into: ([MessageGroup](), MessageGroup?.none), accumulator: groupByTime(messages:item:))
            .map(appendingLastGroup(messages:group:))
            .map(groupedByIncoming(messages:))
            .map(convertedToSectionModels(messages:))
    


func groupByTime(messages: inout ([MessageGroup], MessageGroup?), item: MessageItem) 
    if let group = messages.1 
        let lastPart = group.parts.last!
        if lastPart.sentDate.timeIntervalSince(item.sentDate) > -60 && group.userId == item.userId 
            messages.1!.parts.append(MessageGroup.Part(item))
        
        else 
            messages.0.append(group)
            messages.1 = MessageGroup(from: item)
        
    
    else 
        messages.1 = MessageGroup(from: item)
    


func appendingLastGroup(messages: [MessageGroup], group: MessageGroup?) -> [MessageGroup] 
    guard let group = group else  return messages 
    return messages + [group]


func groupedByIncoming(messages: [MessageGroup]) -> [[MessageGroup]] 
    return messages.reduce([[MessageGroup]]())  result, message in
        guard let last = result.last else 
            return [[message]]
        
        if last.last!.isIncoming == message.isIncoming 
            return Array(result.dropLast()) + [last + [message]]
        
        else 
            return result + [[message]]
        
    


func convertedToSectionModels(messages: [[MessageGroup]]) -> [MessageSectionModel] 
    messages.map  messages in
        if messages.first!.isIncoming 
            return .incomingSection(items: messages)
        
        else 
            return .outcomingSection(items: messages)
        
    

【讨论】:

以上是关于如何正确地将部分中的项目与 RxDataSource swift 结合起来?的主要内容,如果未能解决你的问题,请参考以下文章

如何正确地将 Kal 框架添加到我的 iPhone 项目中?

如何正确地将库与 cmake 链接?

CMake项目结构:如何正确地将库合并在一起并将它们包含在多个可执行文件中

如何正确地将 OData 与 ASP.net Core 集成

需要的建议:如何正确地将 React 连接到 MongoDB

如何正确地将“单元格项目”从 SwiftUI LazyVGrid 传递给 .sheet?