From 931eca5e44fe056f088ec6f02c9efc206f13cec1 Mon Sep 17 00:00:00 2001 From: Dean Eigenmann Date: Sat, 19 Oct 2019 20:22:19 +0200 Subject: [PATCH 1/9] Update QuickStart.md --- Documentation/QuickStart.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/Documentation/QuickStart.md b/Documentation/QuickStart.md index 53afed5a..5d877594 100644 --- a/Documentation/QuickStart.md +++ b/Documentation/QuickStart.md @@ -15,17 +15,18 @@ public protocol MessageType { var kind: MessageKind { get } } ``` -First, each `MessageType` is required to have a `Sender` which contains two properties, `id` and `displayName`: +First, each `MessageType` is required to have a `SenderType` which contains two properties, `senderId` and `displayName`: ### Sender ```Swift -public struct Sender { +public protocol SenderType { - public let id: String - - public let displayName: String + var senderId: String { get } + + var displayName: String { get } } + ``` -**MessageKit** uses the `Sender` type to determine if a message was sent by the current user or to the current user. +**MessageKit** uses the `SenderType` type to determine if a message was sent by the current user or to the current user. Second, each message must have its own `messageId` which is a unique `String` identifier for the message. @@ -91,13 +92,20 @@ class ChatViewController: MessagesViewController { You must implement the following 3 methods to conform to `MessagesDataSource`: ```Swift + +public struct Sender: SenderType { + public let senderId: String + + public let displayName: String +} + // Some global variables for the sake of the example. Using globals is not recommended! let sender = Sender(id: "any_unique_id", displayName: "Steven") let messages: [MessageType] = [] extension ChatViewController: MessagesDataSource { - func currentSender() -> Sender { + func currentSender() -> SenderType { return Sender(id: "any_unique_id", displayName: "Steven") } From d887d8541b47f92a6b2aec011d8f92b467d86a14 Mon Sep 17 00:00:00 2001 From: Gwynant Jones Date: Wed, 20 Nov 2019 14:00:53 +0000 Subject: [PATCH 2/9] [Hotfix] - Fix notifications being received from child modal view controllers. We are receiving keyboard notifications from child modal view controllers, this is a fix to ensure that this doesn't happen. --- Sources/Controllers/MessagesViewController+Keyboard.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/Controllers/MessagesViewController+Keyboard.swift b/Sources/Controllers/MessagesViewController+Keyboard.swift index 510828ae..c3b96310 100644 --- a/Sources/Controllers/MessagesViewController+Keyboard.swift +++ b/Sources/Controllers/MessagesViewController+Keyboard.swift @@ -63,6 +63,11 @@ internal extension MessagesViewController { return } + guard self.presentedViewController == nil else { + // This is important to skip notifications from child modal controllers in iOS >= 13.0 + return + } + // Note that the check above does not exclude all notifications from an undocked keyboard, only the weird ones. // // We've tried following Apple's recommended approach of tracking UIKeyboardWillShow / UIKeyboardDidHide and ignoring frame From d36568e5ab070c558831e0c53ca6128642f1be0d Mon Sep 17 00:00:00 2001 From: hyouuu Date: Mon, 17 Feb 2020 00:31:52 -0800 Subject: [PATCH 3/9] Add scrollToLastItem --- Sources/Views/MessagesCollectionView.swift | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Sources/Views/MessagesCollectionView.swift b/Sources/Views/MessagesCollectionView.swift index 56cc313a..2658b832 100644 --- a/Sources/Views/MessagesCollectionView.swift +++ b/Sources/Views/MessagesCollectionView.swift @@ -100,8 +100,24 @@ open class MessagesCollectionView: UICollectionView { cell?.handleTapGesture(gesture) } + // NOTE: It's possible for small content size this wouldn't work - https://github.com/MessageKit/MessageKit/issues/725 + public func scrollToLastItem(at pos: UICollectionView.ScrollPosition = .centeredVertically, animated: Bool = true) { + guard numberOfSections > 0 else { return } + + let lastSection = numberOfSections - 1 + let lastItem = numberOfItems(inSection: lastSection) - 1 + + guard lastItem >= 0 else { return } + + let indexPath = IndexPath(row: lastItem, section: lastSection) + scrollToItem(at: indexPath, at: pos, animated: animated) + } + + // NOTE: This method seems to cause crash in certain cases - https://github.com/MessageKit/MessageKit/issues/725 + // Could try using `scrollToLastItem` above public func scrollToBottom(animated: Bool = false) { - performBatchUpdates(nil) { _ in + performBatchUpdates(nil) { [weak self] _ in + guard let self = self else { return } let collectionViewContentHeight = self.collectionViewLayout.collectionViewContentSize.height self.scrollRectToVisible(CGRect(0.0, collectionViewContentHeight - 1.0, 1.0, 1.0), animated: animated) } From a3cb5aeaa60a98c6ef7200ffaa349a2e53aa4c41 Mon Sep 17 00:00:00 2001 From: hyouuu Date: Mon, 17 Feb 2020 00:32:46 -0800 Subject: [PATCH 4/9] Add scrollsToLastItemOnKeyboardBeginsEditing flag --- Sources/Controllers/MessagesViewController.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/Controllers/MessagesViewController.swift b/Sources/Controllers/MessagesViewController.swift index bc4bc00e..982a2df5 100644 --- a/Sources/Controllers/MessagesViewController.swift +++ b/Sources/Controllers/MessagesViewController.swift @@ -36,10 +36,18 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource { /// The `InputBarAccessoryView` used as the `inputAccessoryView` in the view controller. open lazy var messageInputBar = InputBarAccessoryView() + /// A Boolean value that determines whether the `MessagesCollectionView` scrolls to the + /// last item whenever the `InputTextView` begins editing. + /// + /// The default value of this property is `false`. + /// NOTE: This calls scrollToLastItem where as the below flag calls scrollToBottome - check methods for differences + open var scrollsToLastItemOnKeyboardBeginsEditing: Bool = false + /// A Boolean value that determines whether the `MessagesCollectionView` scrolls to the /// bottom whenever the `InputTextView` begins editing. /// /// The default value of this property is `false`. + /// NOTE: This calls scrollToBottome where as the above flag calls scrollToLastItem - check methods for differences open var scrollsToBottomOnKeyboardBeginsEditing: Bool = false /// A Boolean value that determines whether the `MessagesCollectionView` From 4d0350e96c2952a7fb930d449ce56e05a5d8384d Mon Sep 17 00:00:00 2001 From: hyouuu Date: Mon, 17 Feb 2020 00:33:27 -0800 Subject: [PATCH 5/9] Check scrollsToLastItemOnKeyboardBeginsEditing and scroll on begin editing --- .../MessagesViewController+Keyboard.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/Controllers/MessagesViewController+Keyboard.swift b/Sources/Controllers/MessagesViewController+Keyboard.swift index 29b786a1..7ec0be14 100644 --- a/Sources/Controllers/MessagesViewController+Keyboard.swift +++ b/Sources/Controllers/MessagesViewController+Keyboard.swift @@ -45,9 +45,15 @@ internal extension MessagesViewController { @objc private func handleTextViewDidBeginEditing(_ notification: Notification) { - if scrollsToBottomOnKeyboardBeginsEditing { - guard let inputTextView = notification.object as? InputTextView, inputTextView === messageInputBar.inputTextView else { return } - messagesCollectionView.scrollToBottom(animated: true) + if scrollsToLastItemOnKeyboardBeginsEditing || scrollsToBottomOnKeyboardBeginsEditing { + guard let inputTextView = notification.object as? InputTextView, + inputTextView === messageInputBar.inputTextView else { return } + + if scrollsToLastItemOnKeyboardBeginsEditing { + messagesCollectionView.scrollToLastItem() + } else { + messagesCollectionView.scrollToBottom(animated: true) + } } } From d1233de07132dee05fd948f74a37e59bff462839 Mon Sep 17 00:00:00 2001 From: hyouuu Date: Mon, 17 Feb 2020 01:27:07 -0800 Subject: [PATCH 6/9] default pos to bottom --- Sources/Views/MessagesCollectionView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Views/MessagesCollectionView.swift b/Sources/Views/MessagesCollectionView.swift index 2658b832..ce960897 100644 --- a/Sources/Views/MessagesCollectionView.swift +++ b/Sources/Views/MessagesCollectionView.swift @@ -101,7 +101,7 @@ open class MessagesCollectionView: UICollectionView { } // NOTE: It's possible for small content size this wouldn't work - https://github.com/MessageKit/MessageKit/issues/725 - public func scrollToLastItem(at pos: UICollectionView.ScrollPosition = .centeredVertically, animated: Bool = true) { + public func scrollToLastItem(at pos: UICollectionView.ScrollPosition = .bottom, animated: Bool = true) { guard numberOfSections > 0 else { return } let lastSection = numberOfSections - 1 From 378e60fcf2c73ec96d528cb53549a2d516e9d2c6 Mon Sep 17 00:00:00 2001 From: Shawn Gong Date: Mon, 17 Feb 2020 19:18:21 -0800 Subject: [PATCH 7/9] Fix tests --- Cartfile.resolved | 4 ++-- .../MessagesDisplayDelegateTests.swift | 8 ++++---- Tests/ViewsTests/AvatarViewTests.swift | 12 +++++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 7acd3164..87c9aee3 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,3 @@ -github "Quick/Nimble" "v8.0.1" -github "Quick/Quick" "v2.0.0" +github "Quick/Nimble" "v8.0.5" +github "Quick/Quick" "v2.2.0" github "nathantannar4/InputBarAccessoryView" "4.3.0" diff --git a/Tests/ProtocolsTests/MessagesDisplayDelegateTests.swift b/Tests/ProtocolsTests/MessagesDisplayDelegateTests.swift index 6cbfb632..717d154e 100644 --- a/Tests/ProtocolsTests/MessagesDisplayDelegateTests.swift +++ b/Tests/ProtocolsTests/MessagesDisplayDelegateTests.swift @@ -71,7 +71,7 @@ class MessagesDisplayDelegateTests: XCTestCase { at: IndexPath(item: 0, section: 0), in: sut.messagesCollectionView) - XCTAssertEqual(backgroundColor, .white) + XCTAssertEqual(backgroundColor, .backgroundColor) } func testBackgroundColorForMessageWithEmoji_returnsClearForDefault() { @@ -143,7 +143,7 @@ class TextMessageDisplayDelegateTests: XCTestCase { at: IndexPath(item: 0, section: 0), in: sut.messagesCollectionView) - XCTAssertEqual(textColor, .white) + XCTAssertEqual(textColor, .backgroundColor) } func testTextColorFromYou_returnsDarkTextForDefault() { @@ -151,7 +151,7 @@ class TextMessageDisplayDelegateTests: XCTestCase { at: IndexPath(item: 0, section: 0), in: sut.messagesCollectionView) - XCTAssertEqual(textColor, .darkText) + XCTAssertEqual(textColor, .labelColor) } func testTextColorWithoutDataSource_returnsDarkTextForDefault() { @@ -161,7 +161,7 @@ class TextMessageDisplayDelegateTests: XCTestCase { at: IndexPath(item: 0, section: 0), in: sut.messagesCollectionView) - XCTAssertEqual(textColor, .darkText) + XCTAssertEqual(textColor, .labelColor) } func testEnableDetectors_returnsEmptyForDefault() { diff --git a/Tests/ViewsTests/AvatarViewTests.swift b/Tests/ViewsTests/AvatarViewTests.swift index e919f141..602632c7 100644 --- a/Tests/ViewsTests/AvatarViewTests.swift +++ b/Tests/ViewsTests/AvatarViewTests.swift @@ -42,7 +42,9 @@ class AvatarViewTests: XCTestCase { func testNoParams() { XCTAssertEqual(avatarView.layer.cornerRadius, 15.0) - XCTAssertEqual(avatarView.backgroundColor, UIColor.gray) + // For certain dynamic colors, need to compare cgColor in XCTest + // https://stackoverflow.com/questions/58065340/how-to-compare-two-uidynamicprovidercolor + XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.grayColor.cgColor) } func testWithImage() { @@ -50,7 +52,7 @@ class AvatarViewTests: XCTestCase { avatarView.set(avatar: avatar) XCTAssertEqual(avatar.initials, "?") XCTAssertEqual(avatarView.layer.cornerRadius, 15.0) - XCTAssertEqual(avatarView.backgroundColor, UIColor.gray) + XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.grayColor.cgColor) } func testInitialsOnly() { @@ -59,13 +61,13 @@ class AvatarViewTests: XCTestCase { XCTAssertEqual(avatarView.initials, avatar.initials) XCTAssertEqual(avatar.initials, "DL") XCTAssertEqual(avatarView.layer.cornerRadius, 15.0) - XCTAssertEqual(avatarView.backgroundColor, UIColor.gray) + XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.grayColor.cgColor) } func testSetBackground() { - XCTAssertEqual(avatarView.backgroundColor, UIColor.gray) + XCTAssertEqual(avatarView.backgroundColor!.cgColor, UIColor.grayColor.cgColor) avatarView.backgroundColor = UIColor.red - XCTAssertEqual(avatarView.backgroundColor, UIColor.red) + XCTAssertEqual(avatarView.backgroundColor!, UIColor.red) } func testGetImage() { From 3dfd26e934c2ea4364401558b703d36448b91f86 Mon Sep 17 00:00:00 2001 From: hyouuu Date: Tue, 18 Feb 2020 14:06:57 -0800 Subject: [PATCH 8/9] typo --- Sources/Controllers/MessagesViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Controllers/MessagesViewController.swift b/Sources/Controllers/MessagesViewController.swift index 982a2df5..e5122f16 100644 --- a/Sources/Controllers/MessagesViewController.swift +++ b/Sources/Controllers/MessagesViewController.swift @@ -40,7 +40,7 @@ UICollectionViewDelegateFlowLayout, UICollectionViewDataSource { /// last item whenever the `InputTextView` begins editing. /// /// The default value of this property is `false`. - /// NOTE: This calls scrollToLastItem where as the below flag calls scrollToBottome - check methods for differences + /// NOTE: This calls scrollToLastItem where as the below flag calls scrollToBottom - check methods for differences open var scrollsToLastItemOnKeyboardBeginsEditing: Bool = false /// A Boolean value that determines whether the `MessagesCollectionView` scrolls to the From d4f8a466e05b9e55e5a8ae62e9976ebd26197d5d Mon Sep 17 00:00:00 2001 From: hyouuu Date: Tue, 18 Feb 2020 14:17:44 -0800 Subject: [PATCH 9/9] rename to lastItemIndex --- Sources/Views/MessagesCollectionView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Views/MessagesCollectionView.swift b/Sources/Views/MessagesCollectionView.swift index ce960897..05515be2 100644 --- a/Sources/Views/MessagesCollectionView.swift +++ b/Sources/Views/MessagesCollectionView.swift @@ -105,11 +105,11 @@ open class MessagesCollectionView: UICollectionView { guard numberOfSections > 0 else { return } let lastSection = numberOfSections - 1 - let lastItem = numberOfItems(inSection: lastSection) - 1 + let lastItemIndex = numberOfItems(inSection: lastSection) - 1 - guard lastItem >= 0 else { return } + guard lastItemIndex >= 0 else { return } - let indexPath = IndexPath(row: lastItem, section: lastSection) + let indexPath = IndexPath(row: lastItemIndex, section: lastSection) scrollToItem(at: indexPath, at: pos, animated: animated) }