Chatterbox 0.2.0

Chatterbox 0.2.0

Omer Aslan维护。



 
依赖
IGListKit>= 0
SocketRocket>= 0
SnapKit~> 4.0
 

Chatterbox

iOS的组件式聊天库

要求

  • iOS 9+
  • Swift 3

为了创建聊天界面,应该实现WebSocket和数据提供者协议,否则将会显示致命错误。

所有聊天信息都在聊天线程中。

依赖

  • IGListKit
  • SocketRocket
  • SnapKit

如果你发现任何问题,请在这里的GitHub上创建一个issue,并随意发送改进和修复的pull request。你还可以通过电子邮件联系我们[email protected]

使用

创建聊天界面视图控制器

let chatThread = ChatThreadable()

let dataProvider = DataProvider(thread: chatThread)
let socketProvider = SocketProvider(thread: chatThread)

let vc = ChatViewController(dataProvider: dataProvider,
webSocketProvider: socketProvider)

vc.chatInputAccessoryView = FLChatInputAccessoryView()

所需协议应该打包在一起。

数据提供者

class DataProvider: BaseChatInterfaceDataProvider<ChatThreadable, ChatMessage> {
    override var canLoadMoreChatMessages: Bool {
        return false
    }

    init(thread: ChatThreadable?) {
        super.init(chatThread: thread)
    }

    override func loadChatMessages(previous: Bool) {
        loadChatMessagesWasCompleted(previous: previous, with: nil)
    }

    override func send(_ chatDraftMessage: ChatDraftMessage,
                       onCompletion completion: ((Error?) -> Void)?) {

    let chatMessage = ChatMessage()

    chatMessage.text = "Test chatbox"

    completion?(nil)

    //Insert chat message to chat thread
    load(chatMessage)
    }
}

Socket提供者

class SocketProvider: BaseChatInterfaceWebSocketProvider<ChatThreadable> {
    init(thread: ChatThreadable?) {
        super.init(chatThread: thread)
    }

    override var chatServerUrl: String? { return nil }
    override func setupConnection() {}
    override func openConnection() {}
    override func closeConnection() {}
}

数据控制器

class DataController: ChatMessageDataController {
    
    override func sizeForItem(at index: Int) -> CGSize {
        let width: CGFloat = UIScreen.main.bounds.width - 20.0
        
        return CGSize(width: width, height: 100.0)
    }
    
    override func cellForItem(at index: Int) -> UICollectionViewCell {
        guard let chatMessage = chatMessage else {
            return super.cellForItem(at: index)
        }
        
      guard let cell = collectionContext?.dequeueReusableCell(
            of: MyCell.self,
            for: self,
            at: index) as? MyCell else {
                return super.cellForItem(at: index)
        }
        
        switch chatMessage.type {
        case .text(let text):
            cell.messageView.textLabel.text = text
        case .attributedText(let attributedText):
            cell.messageView.textLabel.attributedText = attributedText
        default:
            break
        }

        return cell
    }

消息单元

class MessageCell: ChatMessageCell<MessageView> {
    
    override func setupLayout() {
        
        buildMessageView()
        
        contentView.addSubview(messageView)
        
        messageView.snp.makeConstraints { (make) in
            make.edges.equalToSuperview()
        }
        
        setupMessageLabelLayout()
    }
    
    func buildMessageView() {
        messageView = builder.build()
    }
    
    func setupMessageLabelLayout() {
        
        let textLabel = messageView.textLabel
        
        messageView.contentView.addSubview(textLabel)
        
        textLabel.setContentHuggingPriority(999, for: .vertical)
        textLabel.snp.remakeConstraints({ (make) in
            make.top.equalToSuperview().inset(10.0)
            make.leading.equalToSuperview().inset(10.0)
            make.trailing.equalToSuperview().inset(10.0)
        })
    }
    
    override class func calculateHeight(with chatMessage: ChatMessageRepresentable,
                                        constrainedTo width: CGFloat) -> CGFloat {
        var height: CGFloat = 75.0
        
        return height
    }
}

class MyCell: MessageCell {
    
    override func setupLayout() {
        super.setupLayout()
        
        backgroundColor = UIColor.purple
        layer.cornerRadius = 4.0
    }
}

class MessageView: UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private(set) lazy var contentView: UIView = UIView()
    
    private(set) lazy var textLabel: UILabel = {
        [unowned self] in
        
        let label = UILabel()
        
        label.numberOfLines = 0
        label.lineBreakMode = .byWordWrapping
        label.textColor = .black
        label.font = UIFont.systemFont(ofSize: 14.0)
        
        return label
        }()
}

extension MessageView: ChatMessageViewRepresentable {
    var view: UIView {
        return self
    }
    
    var contentBackgroundView: UIView? {
        return nil
    }
    
    var contentMessageView: UIView? {
        return nil
    }
    
    var contentFooterSupplementaryView: UIView? {
        return nil
    }
    
    var senderView: UIView? {
        return nil
    }
}

您可以通过扩展ChatThreadRepresentableChatMessageRepresentable协议来指定您自己的消息和线程对象。


警告

聊天界面与 数据提供者Socket 提供者 工作正常。因此,如果您想使用 REST API 代替 Socket,则需要遵循示例项目。否则,您可能会收到两次消息。

示例

有一个本地示例项目用于创建聊天界面。

要运行示例项目,请先克隆存储库,然后从 Example 目录运行 pod install

协议

ChatThreadRepresentable

ChatMessageRepresentable

安装

Chatterbox 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中

pod 'Chatterbox'

致谢

Chatterbox 由 Hipo 团队 提供。

许可证

Chatterbox 在 MIT 许可证下提供。有关更多信息,请参阅 LICENSE 文件。